mirror of https://github.com/PCSX2/pcsx2.git
GS: Refactor screenshots/GS dumping triggers
i.e. make it not rubbish and a massive race condition. Also adds GS dump saving to Qt.
This commit is contained in:
parent
7bd7cdd867
commit
8c270288de
|
@ -611,6 +611,22 @@ void EmuThread::runOnCPUThread(const std::function<void()>& func)
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmuThread::queueSnapshot(quint32 gsdump_frames)
|
||||||
|
{
|
||||||
|
if (!isOnEmuThread())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "queueSnapshot", Qt::QueuedConnection, Q_ARG(quint32, gsdump_frames));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VMManager::HasValidVM())
|
||||||
|
return;
|
||||||
|
|
||||||
|
GetMTGS().RunOnGSThread([gsdump_frames]() {
|
||||||
|
GSQueueSnapshot(std::string(), gsdump_frames);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void EmuThread::updateDisplay()
|
void EmuThread::updateDisplay()
|
||||||
{
|
{
|
||||||
pxAssertRel(!isOnEmuThread(), "Not on emu thread");
|
pxAssertRel(!isOnEmuThread(), "Not on emu thread");
|
||||||
|
@ -893,13 +909,6 @@ SysMtgsThread& GetMTGS()
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
BEGIN_HOTKEY_LIST(g_host_hotkeys)
|
BEGIN_HOTKEY_LIST(g_host_hotkeys)
|
||||||
DEFINE_HOTKEY("Screenshot", "General", "Save Screenshot", [](bool pressed) {
|
|
||||||
if (!pressed)
|
|
||||||
{
|
|
||||||
Host::AddOSDMessage("Saved Screenshot.", 10.0f);
|
|
||||||
GSmakeSnapshot(EmuFolders::Snapshots.ToString().char_str());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool pressed) {
|
DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool pressed) {
|
||||||
if (!pressed)
|
if (!pressed)
|
||||||
{
|
{
|
||||||
|
|
|
@ -79,6 +79,7 @@ public Q_SLOTS:
|
||||||
void enumerateInputDevices();
|
void enumerateInputDevices();
|
||||||
void enumerateVibrationMotors();
|
void enumerateVibrationMotors();
|
||||||
void runOnCPUThread(const std::function<void()>& func);
|
void runOnCPUThread(const std::function<void()>& func);
|
||||||
|
void queueSnapshot(quint32 gsdump_frames);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
DisplayWidget* onCreateDisplayRequested(bool fullscreen, bool render_to_main);
|
DisplayWidget* onCreateDisplayRequested(bool fullscreen, bool render_to_main);
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
#include "Settings/InterfaceSettingsWidget.h"
|
#include "Settings/InterfaceSettingsWidget.h"
|
||||||
#include "SettingWidgetBinder.h"
|
#include "SettingWidgetBinder.h"
|
||||||
|
|
||||||
extern u32 GSmakeSnapshot(char* path);
|
|
||||||
|
|
||||||
static constexpr char DISC_IMAGE_FILTER[] =
|
static constexpr char DISC_IMAGE_FILTER[] =
|
||||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.elf *.irx *.m3u *.gs *.gs.xz);;"
|
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.elf *.irx *.m3u *.gs *.gs.xz);;"
|
||||||
"Single-Track Raw Images (*.bin *.iso);;"
|
"Single-Track Raw Images (*.bin *.iso);;"
|
||||||
|
@ -212,6 +210,8 @@ void MainWindow::connectSignals()
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableIOPConsoleLogging, "Logging", "EnableIOPConsole", true);
|
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableIOPConsoleLogging, "Logging", "EnableIOPConsole", true);
|
||||||
connect(m_ui.actionEnableIOPConsoleLogging, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged);
|
connect(m_ui.actionEnableIOPConsoleLogging, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged);
|
||||||
|
|
||||||
|
connect(m_ui.actionSaveGSDump, &QAction::triggered, this, &MainWindow::onSaveGSDumpActionTriggered);
|
||||||
|
|
||||||
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
|
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
|
||||||
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
|
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
|
||||||
connect(m_game_list_widget, &GameListWidget::refreshComplete, this, &MainWindow::onGameListRefreshComplete);
|
connect(m_game_list_widget, &GameListWidget::refreshComplete, this, &MainWindow::onGameListRefreshComplete);
|
||||||
|
@ -514,8 +514,12 @@ void MainWindow::setIconThemeFromSettings()
|
||||||
|
|
||||||
void MainWindow::onScreenshotActionTriggered()
|
void MainWindow::onScreenshotActionTriggered()
|
||||||
{
|
{
|
||||||
Host::AddOSDMessage("Saved Screenshot.", 10.0f);
|
g_emu_thread->queueSnapshot(0);
|
||||||
GSmakeSnapshot(EmuFolders::Snapshots.ToString().char_str());
|
}
|
||||||
|
|
||||||
|
void MainWindow::onSaveGSDumpActionTriggered()
|
||||||
|
{
|
||||||
|
g_emu_thread->queueSnapshot(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::saveStateToConfig()
|
void MainWindow::saveStateToConfig()
|
||||||
|
|
|
@ -134,6 +134,7 @@ private Q_SLOTS:
|
||||||
void onThemeChangedFromSettings();
|
void onThemeChangedFromSettings();
|
||||||
void onLoggingOptionChanged();
|
void onLoggingOptionChanged();
|
||||||
void onScreenshotActionTriggered();
|
void onScreenshotActionTriggered();
|
||||||
|
void onSaveGSDumpActionTriggered();
|
||||||
|
|
||||||
void onVMStarting();
|
void onVMStarting();
|
||||||
void onVMStarted();
|
void onVMStarted();
|
||||||
|
|
|
@ -136,8 +136,9 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionToggleSoftwareRendering"/>
|
|
||||||
<addaction name="menuDebugSwitchRenderer"/>
|
<addaction name="menuDebugSwitchRenderer"/>
|
||||||
|
<addaction name="actionToggleSoftwareRendering"/>
|
||||||
|
<addaction name="actionSaveGSDump"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionReloadPatches"/>
|
<addaction name="actionReloadPatches"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
@ -561,12 +562,13 @@
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionDEV9Settings">
|
<action name="actionDEV9Settings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="dashboard-line">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Network && HDD</string>
|
<string>&Network && HDD</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="dashboard-line"/>
|
|
||||||
</property>
|
|
||||||
</action>
|
</action>
|
||||||
<action name="actionViewToolbar">
|
<action name="actionViewToolbar">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
|
@ -738,6 +740,11 @@
|
||||||
<string>Enable IOP Console Logging</string>
|
<string>Enable IOP Console Logging</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSaveGSDump">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save Single Frame GS Dump</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="resources/resources.qrc"/>
|
<include location="resources/resources.qrc"/>
|
||||||
|
|
|
@ -208,6 +208,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideTextureBarriers, "EmuCore/GS", "OverrideTextureBarriers", -1, -1);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideTextureBarriers, "EmuCore/GS", "OverrideTextureBarriers", -1, -1);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideGeometryShader, "EmuCore/GS", "OverrideGeometryShaders", -1, -1);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideGeometryShader, "EmuCore/GS", "OverrideGeometryShaders", -1, -1);
|
||||||
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gsDumpCompression, "EmuCore/GS", "GSDumpCompression", static_cast<int>(GSDumpCompressionMethod::Uncompressed));
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "EmuCore/GS", "DisableFramebufferFetch", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "EmuCore/GS", "DisableFramebufferFetch", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "EmuCore/GS", "DisableDualSourceBlend", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "EmuCore/GS", "DisableDualSourceBlend", false);
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
<string>Auto Standard (4:3/3:2 Progressive)</string>
|
<string>Auto Standard (4:3/3:2 Progressive)</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Standard (4:3)</string>
|
<string>Standard (4:3)</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
<string>Off (Default)</string>
|
<string>Off (Default)</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Auto Standard (4:3/3:2 Progressive)</string>
|
<string>Auto Standard (4:3/3:2 Progressive)</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1146,7 +1146,7 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="3" column="0" colspan="2">
|
||||||
<layout class="QGridLayout" name="gridLayout_7">
|
<layout class="QGridLayout" name="gridLayout_7">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="useBlitSwapChain">
|
<widget class="QCheckBox" name="useBlitSwapChain">
|
||||||
|
@ -1178,6 +1178,27 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_34">
|
||||||
|
<property name="text">
|
||||||
|
<string>GS Dump Compression:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="gsDumpCompression">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Uncompressed</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>LZMA (XZ)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -192,6 +192,12 @@ enum class TexturePreloadingLevel : u8
|
||||||
Full,
|
Full,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GSDumpCompressionMethod : u8
|
||||||
|
{
|
||||||
|
Uncompressed,
|
||||||
|
LZMA
|
||||||
|
};
|
||||||
|
|
||||||
// Template function for casting enumerations to their underlying type
|
// Template function for casting enumerations to their underlying type
|
||||||
template <typename Enumeration>
|
template <typename Enumeration>
|
||||||
typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
|
typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
|
||||||
|
@ -519,6 +525,7 @@ struct Pcsx2Config
|
||||||
CRCHackLevel CRCHack{CRCHackLevel::Automatic};
|
CRCHackLevel CRCHack{CRCHackLevel::Automatic};
|
||||||
BiFiltering TextureFiltering{BiFiltering::PS2};
|
BiFiltering TextureFiltering{BiFiltering::PS2};
|
||||||
TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Off};
|
TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Off};
|
||||||
|
GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::Uncompressed};
|
||||||
int Dithering{2};
|
int Dithering{2};
|
||||||
int MaxAnisotropy{0};
|
int MaxAnisotropy{0};
|
||||||
int SWExtraThreads{2};
|
int SWExtraThreads{2};
|
||||||
|
|
|
@ -480,35 +480,6 @@ void GSvsync(u32 field, bool registers_written)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GSmakeSnapshot(char* path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::string s{path};
|
|
||||||
|
|
||||||
if (!s.empty())
|
|
||||||
{
|
|
||||||
// Allows for providing a complete path
|
|
||||||
std::string extension = s.substr(s.size() - 4, 4);
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::transform(extension.begin(), extension.end(), extension.begin(), (char(_cdecl*)(int))tolower);
|
|
||||||
#else
|
|
||||||
std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
|
|
||||||
#endif
|
|
||||||
if (extension == ".png")
|
|
||||||
return g_gs_renderer->MakeSnapshot(s);
|
|
||||||
else if (s[s.length() - 1] != DIRECTORY_SEPARATOR)
|
|
||||||
s = s + DIRECTORY_SEPARATOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_gs_renderer->MakeSnapshot(s + "gs");
|
|
||||||
}
|
|
||||||
catch (GSRecoverableError)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GSfreeze(FreezeAction mode, freezeData* data)
|
int GSfreeze(FreezeAction mode, freezeData* data)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -533,6 +504,18 @@ int GSfreeze(FreezeAction mode, freezeData* data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSQueueSnapshot(const std::string& path, u32 gsdump_frames)
|
||||||
|
{
|
||||||
|
if (g_gs_renderer)
|
||||||
|
g_gs_renderer->QueueSnapshot(path, gsdump_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSStopGSDump()
|
||||||
|
{
|
||||||
|
if (g_gs_renderer)
|
||||||
|
g_gs_renderer->StopGSDump();
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
#ifndef PCSX2_CORE
|
||||||
|
|
||||||
void GSkeyEvent(const HostKeyEvent& e)
|
void GSkeyEvent(const HostKeyEvent& e)
|
||||||
|
@ -577,9 +560,7 @@ int GStest()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
static void pt(const char* str)
|
||||||
|
|
||||||
void pt(const char* str)
|
|
||||||
{
|
{
|
||||||
struct tm* current;
|
struct tm* current;
|
||||||
time_t now;
|
time_t now;
|
||||||
|
@ -623,6 +604,7 @@ void GSendRecording()
|
||||||
g_gs_renderer->EndCapture();
|
g_gs_renderer->EndCapture();
|
||||||
pt(" - Capture ended\n");
|
pt(" - Capture ended\n");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void GSsetGameCRC(u32 crc, int options)
|
void GSsetGameCRC(u32 crc, int options)
|
||||||
{
|
{
|
||||||
|
@ -1293,6 +1275,9 @@ void GSApp::Init()
|
||||||
m_gs_tv_shaders.push_back(GSSetting(3, "Triangular filter", ""));
|
m_gs_tv_shaders.push_back(GSSetting(3, "Triangular filter", ""));
|
||||||
m_gs_tv_shaders.push_back(GSSetting(4, "Wave filter", ""));
|
m_gs_tv_shaders.push_back(GSSetting(4, "Wave filter", ""));
|
||||||
|
|
||||||
|
m_gs_dump_compression.push_back(GSSetting(static_cast<u32>(GSDumpCompressionMethod::Uncompressed), "Uncompressed", ""));
|
||||||
|
m_gs_dump_compression.push_back(GSSetting(static_cast<u32>(GSDumpCompressionMethod::LZMA), "LZMA (XZ)", ""));
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Avoid to clutter the ini file with useless options
|
// Avoid to clutter the ini file with useless options
|
||||||
#if defined(ENABLE_VULKAN) || defined(_WIN32)
|
#if defined(ENABLE_VULKAN) || defined(_WIN32)
|
||||||
|
@ -1333,6 +1318,7 @@ void GSApp::Init()
|
||||||
m_default_configuration["filter"] = std::to_string(static_cast<s8>(BiFiltering::PS2));
|
m_default_configuration["filter"] = std::to_string(static_cast<s8>(BiFiltering::PS2));
|
||||||
m_default_configuration["FMVSoftwareRendererSwitch"] = "0";
|
m_default_configuration["FMVSoftwareRendererSwitch"] = "0";
|
||||||
m_default_configuration["fxaa"] = "0";
|
m_default_configuration["fxaa"] = "0";
|
||||||
|
m_default_configuration["GSDumpCompression"] = "0";
|
||||||
m_default_configuration["HWDisableReadbacks"] = "0";
|
m_default_configuration["HWDisableReadbacks"] = "0";
|
||||||
m_default_configuration["pcrtc_offsets"] = "0";
|
m_default_configuration["pcrtc_offsets"] = "0";
|
||||||
m_default_configuration["IntegerScaling"] = "0";
|
m_default_configuration["IntegerScaling"] = "0";
|
||||||
|
@ -1581,8 +1567,32 @@ static void HotkeyAdjustZoom(double delta)
|
||||||
GetMTGS().RunOnGSThread([new_zoom]() { GSConfig.Zoom = new_zoom; });
|
GetMTGS().RunOnGSThread([new_zoom]() { GSConfig.Zoom = new_zoom; });
|
||||||
}
|
}
|
||||||
|
|
||||||
BEGIN_HOTKEY_LIST(g_gs_hotkeys){
|
BEGIN_HOTKEY_LIST(g_gs_hotkeys)
|
||||||
"ToggleSoftwareRendering", "Graphics", "Toggle Software Rendering", [](bool pressed) {
|
{"Screenshot", "Graphics", "Save Screenshot", [](bool pressed) {
|
||||||
|
if (!pressed)
|
||||||
|
{
|
||||||
|
GetMTGS().RunOnGSThread([]() {
|
||||||
|
GSQueueSnapshot(std::string(), 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"GSDumpSingleFrame", "Graphics", "Save Single Frame GS Dump", [](bool pressed) {
|
||||||
|
if (!pressed)
|
||||||
|
{
|
||||||
|
GetMTGS().RunOnGSThread([]() {
|
||||||
|
GSQueueSnapshot(std::string(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"GSDumpMultiFrame", "Graphics", "Save Multi Frame GS Dump", [](bool pressed) {
|
||||||
|
GetMTGS().RunOnGSThread([pressed]() {
|
||||||
|
if (pressed)
|
||||||
|
GSQueueSnapshot(std::string(), std::numeric_limits<u32>::max());
|
||||||
|
else
|
||||||
|
GSStopGSDump();
|
||||||
|
});
|
||||||
|
}},
|
||||||
|
{"ToggleSoftwareRendering", "Graphics", "Toggle Software Rendering", [](bool pressed) {
|
||||||
if (!pressed)
|
if (!pressed)
|
||||||
GetMTGS().ToggleSoftwareRendering();
|
GetMTGS().ToggleSoftwareRendering();
|
||||||
}},
|
}},
|
||||||
|
|
|
@ -66,15 +66,16 @@ void GSgifTransfer1(u8* mem, u32 addr);
|
||||||
void GSgifTransfer2(u8* mem, u32 size);
|
void GSgifTransfer2(u8* mem, u32 size);
|
||||||
void GSgifTransfer3(u8* mem, u32 size);
|
void GSgifTransfer3(u8* mem, u32 size);
|
||||||
void GSvsync(u32 field, bool registers_written);
|
void GSvsync(u32 field, bool registers_written);
|
||||||
u32 GSmakeSnapshot(char* path);
|
|
||||||
int GSfreeze(FreezeAction mode, freezeData* data);
|
int GSfreeze(FreezeAction mode, freezeData* data);
|
||||||
|
void GSQueueSnapshot(const std::string& path, u32 gsdump_frames = 0);
|
||||||
|
void GSStopGSDump();
|
||||||
#ifndef PCSX2_CORE
|
#ifndef PCSX2_CORE
|
||||||
void GSkeyEvent(const HostKeyEvent& e);
|
void GSkeyEvent(const HostKeyEvent& e);
|
||||||
void GSconfigure();
|
void GSconfigure();
|
||||||
int GStest();
|
int GStest();
|
||||||
#endif
|
|
||||||
bool GSsetupRecording(std::string& filename);
|
bool GSsetupRecording(std::string& filename);
|
||||||
void GSendRecording();
|
void GSendRecording();
|
||||||
|
#endif
|
||||||
void GSsetGameCRC(u32 crc, int options);
|
void GSsetGameCRC(u32 crc, int options);
|
||||||
void GSsetFrameSkip(int frameskip);
|
void GSsetFrameSkip(int frameskip);
|
||||||
|
|
||||||
|
@ -139,6 +140,7 @@ public:
|
||||||
std::vector<GSSetting> m_gs_crc_level;
|
std::vector<GSSetting> m_gs_crc_level;
|
||||||
std::vector<GSSetting> m_gs_acc_blend_level;
|
std::vector<GSSetting> m_gs_acc_blend_level;
|
||||||
std::vector<GSSetting> m_gs_tv_shaders;
|
std::vector<GSSetting> m_gs_tv_shaders;
|
||||||
|
std::vector<GSSetting> m_gs_dump_compression;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GSError
|
struct GSError
|
||||||
|
|
|
@ -17,14 +17,17 @@
|
||||||
#include "GSDump.h"
|
#include "GSDump.h"
|
||||||
#include "GSExtra.h"
|
#include "GSExtra.h"
|
||||||
#include "GSState.h"
|
#include "GSState.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/FileSystem.h"
|
||||||
|
|
||||||
GSDumpBase::GSDumpBase(const std::string& fn)
|
GSDumpBase::GSDumpBase(std::string fn)
|
||||||
: m_frames(0)
|
: m_filename(std::move(fn))
|
||||||
|
, m_frames(0)
|
||||||
, m_extra_frames(2)
|
, m_extra_frames(2)
|
||||||
{
|
{
|
||||||
m_gs = px_fopen(fn, "wb");
|
m_gs = FileSystem::OpenCFile(m_filename.c_str(), "wb");
|
||||||
if (!m_gs)
|
if (!m_gs)
|
||||||
fprintf(stderr, "GSDump: Error failed to open %s\n", fn.c_str());
|
Console.Error("GSDump: Error failed to open %s", m_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
GSDumpBase::~GSDumpBase()
|
GSDumpBase::~GSDumpBase()
|
||||||
|
|
|
@ -56,9 +56,10 @@ struct GSDumpHeader
|
||||||
|
|
||||||
class GSDumpBase
|
class GSDumpBase
|
||||||
{
|
{
|
||||||
|
FILE* m_gs;
|
||||||
|
std::string m_filename;
|
||||||
int m_frames;
|
int m_frames;
|
||||||
int m_extra_frames;
|
int m_extra_frames;
|
||||||
FILE* m_gs;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void AddHeader(const std::string& serial, u32 crc,
|
void AddHeader(const std::string& serial, u32 crc,
|
||||||
|
@ -70,9 +71,11 @@ protected:
|
||||||
virtual void AppendRawData(u8 c) = 0;
|
virtual void AppendRawData(u8 c) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSDumpBase(const std::string& fn);
|
GSDumpBase(std::string fn);
|
||||||
virtual ~GSDumpBase();
|
virtual ~GSDumpBase();
|
||||||
|
|
||||||
|
__fi const std::string& GetPath() const { return m_filename; }
|
||||||
|
|
||||||
void ReadFIFO(u32 size);
|
void ReadFIFO(u32 size);
|
||||||
void Transfer(int index, const u8* mem, size_t size);
|
void Transfer(int index, const u8* mem, size_t size);
|
||||||
bool VSync(int field, bool last, const GSPrivRegSet* regs);
|
bool VSync(int field, bool last, const GSPrivRegSet* regs);
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
#include "pcsx2/Config.h"
|
#include "pcsx2/Config.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
|
#include "common/Path.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
#ifndef PCSX2_CORE
|
||||||
#include "gui/AppCoreThread.h"
|
#include "gui/AppCoreThread.h"
|
||||||
|
@ -54,17 +56,9 @@ static std::string GetDumpSerial()
|
||||||
|
|
||||||
std::unique_ptr<GSRenderer> g_gs_renderer;
|
std::unique_ptr<GSRenderer> g_gs_renderer;
|
||||||
|
|
||||||
GSRenderer::GSRenderer()
|
GSRenderer::GSRenderer() = default;
|
||||||
: m_shift_key(false)
|
|
||||||
, m_control_key(false)
|
|
||||||
, m_texture_shuffle(false)
|
|
||||||
, m_real_size(0, 0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GSRenderer::~GSRenderer()
|
GSRenderer::~GSRenderer() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSRenderer::Destroy()
|
void GSRenderer::Destroy()
|
||||||
{
|
{
|
||||||
|
@ -541,10 +535,14 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
g_gs_device->RestoreAPIState();
|
g_gs_device->RestoreAPIState();
|
||||||
|
|
||||||
// snapshot
|
// snapshot
|
||||||
|
// wx is dumb and call this from the UI thread...
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
std::unique_lock snapshot_lock(m_snapshot_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!m_snapshot.empty())
|
if (!m_snapshot.empty())
|
||||||
{
|
{
|
||||||
if (!m_dump && m_shift_key)
|
if (!m_dump && m_dump_frames > 0)
|
||||||
{
|
{
|
||||||
freezeData fd = {0, nullptr};
|
freezeData fd = {0, nullptr};
|
||||||
Freeze(&fd, true);
|
Freeze(&fd, true);
|
||||||
|
@ -557,12 +555,14 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
std::vector<u32> screenshot_pixels;
|
std::vector<u32> screenshot_pixels;
|
||||||
SaveSnapshotToMemory(DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, &screenshot_pixels);
|
SaveSnapshotToMemory(DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, &screenshot_pixels);
|
||||||
|
|
||||||
if (m_control_key)
|
std::string_view compression_str;
|
||||||
|
if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed)
|
||||||
{
|
{
|
||||||
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, GetDumpSerial(), m_crc,
|
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, GetDumpSerial(), m_crc,
|
||||||
DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT,
|
DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT,
|
||||||
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||||
fd, m_regs));
|
fd, m_regs));
|
||||||
|
compression_str = "with no compression";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -570,26 +570,49 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT,
|
DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT,
|
||||||
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||||
fd, m_regs));
|
fd, m_regs));
|
||||||
|
compression_str = "with LZMA compression";
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] fd.data;
|
delete[] fd.data;
|
||||||
|
|
||||||
|
Host::AddKeyedOSDMessage("GSDump", fmt::format("Saving {0} GS dump {1} to '{2}'",
|
||||||
|
(m_dump_frames == 1) ? "single frame" : "multi-frame", compression_str,
|
||||||
|
FileSystem::GetFileNameFromPath(m_dump->GetPath())), 10.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GSTexture* t = g_gs_device->GetCurrent())
|
if (GSTexture* t = g_gs_device->GetCurrent())
|
||||||
{
|
{
|
||||||
t->Save(m_snapshot + ".png");
|
const std::string path(m_snapshot + ".png");
|
||||||
|
const std::string_view filename(FileSystem::GetFileNameFromPath(path));
|
||||||
|
if (t->Save(path))
|
||||||
|
{
|
||||||
|
Host::AddKeyedOSDMessage("GSScreenshot",
|
||||||
|
fmt::format("Screenshot saved to '{}'.", FileSystem::GetFileNameFromPath(path)), 10.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Host::AddFormattedOSDMessage(10.0f, "Failed to save screenshot to '%s'.", path.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_snapshot.clear();
|
m_snapshot = {};
|
||||||
}
|
}
|
||||||
else if (m_dump)
|
else if (m_dump)
|
||||||
{
|
{
|
||||||
if (m_dump->VSync(field, !m_control_key, m_regs))
|
const bool last = (m_dump_frames == 0);
|
||||||
|
if (m_dump->VSync(field, last, m_regs))
|
||||||
|
{
|
||||||
|
Host::AddKeyedOSDMessage("GSDump", fmt::format("Saved GS dump to '{}'.", FileSystem::GetFileNameFromPath(m_dump->GetPath())), 10.0f);
|
||||||
m_dump.reset();
|
m_dump.reset();
|
||||||
|
}
|
||||||
|
else if (!last)
|
||||||
|
{
|
||||||
|
m_dump_frames--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
// capture
|
// capture
|
||||||
|
|
||||||
if (m_capture.IsCapturing())
|
if (m_capture.IsCapturing())
|
||||||
{
|
{
|
||||||
if (GSTexture* current = g_gs_device->GetCurrent())
|
if (GSTexture* current = g_gs_device->GetCurrent())
|
||||||
|
@ -610,55 +633,74 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSRenderer::MakeSnapshot(const std::string& path)
|
void GSRenderer::QueueSnapshot(const std::string& path, u32 gsdump_frames)
|
||||||
{
|
{
|
||||||
if (m_snapshot.empty())
|
if (!m_snapshot.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Allows for providing a complete path
|
||||||
|
if (path.size() > 4 && StringUtil::compareNoCase(path.substr(path.size() - 4, 4), ".png"))
|
||||||
{
|
{
|
||||||
// Allows for providing a complete path
|
m_snapshot = path.substr(0, path.size() - 4);
|
||||||
if (path.substr(path.size() - 4, 4) == ".png")
|
}
|
||||||
m_snapshot = path.substr(0, path.size() - 4);
|
else
|
||||||
else
|
{
|
||||||
|
time_t cur_time = time(nullptr);
|
||||||
|
static time_t prev_snap;
|
||||||
|
// The variable 'n' is used for labelling the screenshots when multiple screenshots are taken in
|
||||||
|
// a single second, we'll start using this variable for naming when a second screenshot request is detected
|
||||||
|
// at the same time as the first one. Hence, we're initially setting this counter to 2 to imply that
|
||||||
|
// the captured image is the 2nd image captured at this specific time.
|
||||||
|
static int n = 2;
|
||||||
|
char local_time[16];
|
||||||
|
|
||||||
|
if (strftime(local_time, sizeof(local_time), "%Y%m%d%H%M%S", localtime(&cur_time)))
|
||||||
{
|
{
|
||||||
time_t cur_time = time(nullptr);
|
if (cur_time == prev_snap)
|
||||||
static time_t prev_snap;
|
m_snapshot = fmt::format("gs_{0}_({1})", local_time, n++);
|
||||||
// The variable 'n' is used for labelling the screenshots when multiple screenshots are taken in
|
else
|
||||||
// a single second, we'll start using this variable for naming when a second screenshot request is detected
|
|
||||||
// at the same time as the first one. Hence, we're initially setting this counter to 2 to imply that
|
|
||||||
// the captured image is the 2nd image captured at this specific time.
|
|
||||||
static int n = 2;
|
|
||||||
char local_time[16];
|
|
||||||
|
|
||||||
if (strftime(local_time, sizeof(local_time), "%Y%m%d%H%M%S", localtime(&cur_time)))
|
|
||||||
{
|
{
|
||||||
if (cur_time == prev_snap)
|
n = 2;
|
||||||
m_snapshot = format("%s_%s_(%d)", path.c_str(), local_time, n++);
|
m_snapshot = fmt::format("gs_{}", local_time);
|
||||||
else
|
|
||||||
{
|
|
||||||
n = 2;
|
|
||||||
m_snapshot = format("%s_%s", path.c_str(), local_time);
|
|
||||||
}
|
|
||||||
prev_snap = cur_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
// append the game serial and title
|
|
||||||
if (std::string name(GetDumpName()); !name.empty())
|
|
||||||
{
|
|
||||||
FileSystem::SanitizeFileName(name);
|
|
||||||
m_snapshot += format("_%s", name.c_str());
|
|
||||||
}
|
|
||||||
if (std::string serial(GetDumpSerial()); !serial.empty())
|
|
||||||
{
|
|
||||||
FileSystem::SanitizeFileName(serial);
|
|
||||||
m_snapshot += format("_%s", serial.c_str());
|
|
||||||
}
|
}
|
||||||
|
prev_snap = cur_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append the game serial and title
|
||||||
|
if (std::string name(GetDumpName()); !name.empty())
|
||||||
|
{
|
||||||
|
FileSystem::SanitizeFileName(name);
|
||||||
|
m_snapshot += '_';
|
||||||
|
m_snapshot += name;
|
||||||
|
}
|
||||||
|
if (std::string serial(GetDumpSerial()); !serial.empty())
|
||||||
|
{
|
||||||
|
FileSystem::SanitizeFileName(serial);
|
||||||
|
m_snapshot += '_';
|
||||||
|
m_snapshot += serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend snapshots directory
|
||||||
|
m_snapshot = Path::CombineStdString(EmuFolders::Snapshots, m_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
// this is really gross, but wx we get the snapshot request after shift...
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
m_dump_frames = gsdump_frames;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSRenderer::StopGSDump()
|
||||||
|
{
|
||||||
|
m_snapshot = {};
|
||||||
|
m_dump_frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
|
||||||
bool GSRenderer::BeginCapture(std::string& filename)
|
bool GSRenderer::BeginCapture(std::string& filename)
|
||||||
{
|
{
|
||||||
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), GetCurrentAspectRatioFloat(GetVideoMode() == GSVideoMode::SDTV_480P), filename);
|
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), GetCurrentAspectRatioFloat(GetVideoMode() == GSVideoMode::SDTV_480P), filename);
|
||||||
|
@ -671,7 +713,6 @@ void GSRenderer::EndCapture()
|
||||||
|
|
||||||
void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
||||||
{
|
{
|
||||||
#if !defined(PCSX2_CORE)
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
m_shift_key = !!(::GetAsyncKeyState(VK_SHIFT) & 0x8000);
|
m_shift_key = !!(::GetAsyncKeyState(VK_SHIFT) & 0x8000);
|
||||||
m_control_key = !!(::GetAsyncKeyState(VK_CONTROL) & 0x8000);
|
m_control_key = !!(::GetAsyncKeyState(VK_CONTROL) & 0x8000);
|
||||||
|
@ -696,6 +737,19 @@ void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (m_dump_frames == 0)
|
||||||
|
{
|
||||||
|
// start dumping
|
||||||
|
if (m_shift_key)
|
||||||
|
m_dump_frames = m_control_key ? std::numeric_limits<u32>::max() : 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// stop dumping
|
||||||
|
if (m_dump && !m_control_key)
|
||||||
|
m_dump_frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.type == HostKeyEvent::Type::KeyPressed)
|
if (e.type == HostKeyEvent::Type::KeyPressed)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -732,9 +786,10 @@ void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // PCSX2_CORE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // PCSX2_CORE
|
||||||
|
|
||||||
void GSRenderer::PurgePool()
|
void GSRenderer::PurgePool()
|
||||||
{
|
{
|
||||||
g_gs_device->PurgePool();
|
g_gs_device->PurgePool();
|
||||||
|
|
|
@ -19,21 +19,29 @@
|
||||||
#include "GS/GSCapture.h"
|
#include "GS/GSCapture.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct HostKeyEvent;
|
struct HostKeyEvent;
|
||||||
|
|
||||||
class GSRenderer : public GSState
|
class GSRenderer : public GSState
|
||||||
{
|
{
|
||||||
GSCapture m_capture;
|
private:
|
||||||
std::string m_snapshot;
|
|
||||||
|
|
||||||
bool Merge(int field);
|
bool Merge(int field);
|
||||||
|
|
||||||
bool m_shift_key;
|
#ifndef PCSX2_CORE
|
||||||
bool m_control_key;
|
GSCapture m_capture;
|
||||||
|
std::mutex m_snapshot_mutex;
|
||||||
|
bool m_shift_key = false;
|
||||||
|
bool m_control_key = false;
|
||||||
|
#endif
|
||||||
|
std::string m_snapshot;
|
||||||
|
u32 m_dump_frames = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
GSVector2i m_real_size{0, 0};
|
||||||
bool m_texture_shuffle;
|
bool m_texture_shuffle;
|
||||||
GSVector2i m_real_size;
|
|
||||||
|
|
||||||
virtual GSTexture* GetOutput(int i, int& y_offset) = 0;
|
virtual GSTexture* GetOutput(int i, int& y_offset) = 0;
|
||||||
virtual GSTexture* GetFeedbackOutput() { return nullptr; }
|
virtual GSTexture* GetFeedbackOutput() { return nullptr; }
|
||||||
|
@ -45,20 +53,24 @@ public:
|
||||||
virtual void Destroy();
|
virtual void Destroy();
|
||||||
|
|
||||||
virtual void VSync(u32 field, bool registers_written);
|
virtual void VSync(u32 field, bool registers_written);
|
||||||
virtual bool MakeSnapshot(const std::string& path);
|
|
||||||
virtual void KeyEvent(const HostKeyEvent& e);
|
|
||||||
virtual bool CanUpscale() { return false; }
|
virtual bool CanUpscale() { return false; }
|
||||||
virtual int GetUpscaleMultiplier() { return 1; }
|
virtual int GetUpscaleMultiplier() { return 1; }
|
||||||
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
|
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
|
||||||
GSVector2i GetInternalResolution();
|
GSVector2i GetInternalResolution();
|
||||||
|
|
||||||
virtual bool BeginCapture(std::string& filename);
|
|
||||||
virtual void EndCapture();
|
|
||||||
|
|
||||||
virtual void PurgePool() override;
|
virtual void PurgePool() override;
|
||||||
virtual void PurgeTextureCache();
|
virtual void PurgeTextureCache();
|
||||||
|
|
||||||
bool SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels);
|
bool SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels);
|
||||||
|
|
||||||
|
void QueueSnapshot(const std::string& path, u32 gsdump_frames);
|
||||||
|
void StopGSDump();
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
bool BeginCapture(std::string& filename);
|
||||||
|
void EndCapture();
|
||||||
|
void KeyEvent(const HostKeyEvent& e);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<GSRenderer> g_gs_renderer;
|
extern std::unique_ptr<GSRenderer> g_gs_renderer;
|
|
@ -586,6 +586,7 @@ DebugTab::DebugTab(wxWindow* parent)
|
||||||
m_ui.addComboBoxAndLabel(ogl_grid, "Geometry Shader:", "OverrideGeometryShaders", &theApp.m_gs_generic_list, IDC_GEOMETRY_SHADER_OVERRIDE, vk_ogl_hw_prereq);
|
m_ui.addComboBoxAndLabel(ogl_grid, "Geometry Shader:", "OverrideGeometryShaders", &theApp.m_gs_generic_list, IDC_GEOMETRY_SHADER_OVERRIDE, vk_ogl_hw_prereq);
|
||||||
m_ui.addComboBoxAndLabel(ogl_grid, "Image Load Store:", "override_GL_ARB_shader_image_load_store", &theApp.m_gs_generic_list, IDC_IMAGE_LOAD_STORE, ogl_hw_prereq);
|
m_ui.addComboBoxAndLabel(ogl_grid, "Image Load Store:", "override_GL_ARB_shader_image_load_store", &theApp.m_gs_generic_list, IDC_IMAGE_LOAD_STORE, ogl_hw_prereq);
|
||||||
m_ui.addComboBoxAndLabel(ogl_grid, "Sparse Texture:", "override_GL_ARB_sparse_texture", &theApp.m_gs_generic_list, IDC_SPARSE_TEXTURE, ogl_hw_prereq);
|
m_ui.addComboBoxAndLabel(ogl_grid, "Sparse Texture:", "override_GL_ARB_sparse_texture", &theApp.m_gs_generic_list, IDC_SPARSE_TEXTURE, ogl_hw_prereq);
|
||||||
|
m_ui.addComboBoxAndLabel(ogl_grid, "Dump Compression:", "GSDumpCompression", &theApp.m_gs_dump_compression, -1);
|
||||||
ogl_box->Add(ogl_grid);
|
ogl_box->Add(ogl_grid);
|
||||||
|
|
||||||
tab_box->Add(ogl_box.outer, wxSizerFlags().Expand());
|
tab_box->Add(ogl_box.outer, wxSizerFlags().Expand());
|
||||||
|
|
|
@ -391,6 +391,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
|
||||||
OpEqu(CRCHack) &&
|
OpEqu(CRCHack) &&
|
||||||
OpEqu(TextureFiltering) &&
|
OpEqu(TextureFiltering) &&
|
||||||
OpEqu(TexturePreloading) &&
|
OpEqu(TexturePreloading) &&
|
||||||
|
OpEqu(GSDumpCompression) &&
|
||||||
OpEqu(Dithering) &&
|
OpEqu(Dithering) &&
|
||||||
OpEqu(MaxAnisotropy) &&
|
OpEqu(MaxAnisotropy) &&
|
||||||
OpEqu(SWExtraThreads) &&
|
OpEqu(SWExtraThreads) &&
|
||||||
|
@ -575,6 +576,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
||||||
GSSettingIntEnumEx(CRCHack, "crc_hack_level");
|
GSSettingIntEnumEx(CRCHack, "crc_hack_level");
|
||||||
GSSettingIntEnumEx(TextureFiltering, "filter");
|
GSSettingIntEnumEx(TextureFiltering, "filter");
|
||||||
GSSettingIntEnumEx(TexturePreloading, "texture_preloading");
|
GSSettingIntEnumEx(TexturePreloading, "texture_preloading");
|
||||||
|
GSSettingIntEnumEx(GSDumpCompression, "GSDumpCompression");
|
||||||
GSSettingIntEx(Dithering, "dithering_ps2");
|
GSSettingIntEx(Dithering, "dithering_ps2");
|
||||||
GSSettingIntEx(MaxAnisotropy, "MaxAnisotropy");
|
GSSettingIntEx(MaxAnisotropy, "MaxAnisotropy");
|
||||||
GSSettingIntEx(SWExtraThreads, "extrathreads");
|
GSSettingIntEx(SWExtraThreads, "extrathreads");
|
||||||
|
|
|
@ -372,8 +372,7 @@ namespace Implementations
|
||||||
|
|
||||||
void Sys_TakeSnapshot()
|
void Sys_TakeSnapshot()
|
||||||
{
|
{
|
||||||
if (GSmakeSnapshot(g_Conf->Folders.Snapshots.ToUTF8().data()))
|
GSQueueSnapshot(std::string(), 0);
|
||||||
OSDlog(ConsoleColors::Color_Black, true, "Snapshot taken");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sys_RenderToggle()
|
void Sys_RenderToggle()
|
||||||
|
|
|
@ -1040,7 +1040,7 @@ void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent& even
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GSmakeSnapshot(g_Conf->Folders.Snapshots.ToString().char_str());
|
GSQueueSnapshot(std::string(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_As_Click(wxCommandEvent& event)
|
void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_As_Click(wxCommandEvent& event)
|
||||||
|
@ -1056,7 +1056,7 @@ void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_As_Click(wxCommandEvent& e
|
||||||
wxFileDialog fileDialog(this, _("Select a file"), g_Conf->Folders.Snapshots.ToAscii(), wxEmptyString, "PNG files (*.png)|*.png", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
wxFileDialog fileDialog(this, _("Select a file"), g_Conf->Folders.Snapshots.ToAscii(), wxEmptyString, "PNG files (*.png)|*.png", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
|
|
||||||
if (fileDialog.ShowModal() == wxID_OK)
|
if (fileDialog.ShowModal() == wxID_OK)
|
||||||
GSmakeSnapshot((char*)fileDialog.GetPath().char_str());
|
GSQueueSnapshot(StringUtil::wxStringToUTF8String(fileDialog.GetPath()), 0);
|
||||||
|
|
||||||
// Resume emulation
|
// Resume emulation
|
||||||
if (!wasPaused)
|
if (!wasPaused)
|
||||||
|
|
Loading…
Reference in New Issue