From 354951f1d62e33bc94fcf650f993f42742a5e4f3 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 10 Dec 2022 19:11:08 +1000 Subject: [PATCH] GS: Add internal resolution screenshot option --- pcsx2-qt/Settings/GraphicsSettingsWidget.cpp | 10 ++ pcsx2-qt/Settings/GraphicsSettingsWidget.ui | 78 ++++++++++- pcsx2/Config.h | 20 +++ pcsx2/Frontend/FullscreenUI.cpp | 8 ++ pcsx2/GS.h | 3 +- pcsx2/GS/GS.cpp | 6 +- pcsx2/GS/GS.h | 3 +- pcsx2/GS/Renderers/Common/GSDevice.cpp | 6 - pcsx2/GS/Renderers/Common/GSDevice.h | 3 - pcsx2/GS/Renderers/Common/GSRenderer.cpp | 140 +++++++++++++------ pcsx2/GS/Renderers/Common/GSRenderer.h | 5 +- pcsx2/GS/Renderers/Metal/convert.metal | 3 +- pcsx2/MTGS.cpp | 7 +- pcsx2/Pcsx2Config.cpp | 8 ++ pcsx2/SaveState.cpp | 9 +- 15 files changed, 238 insertions(+), 71 deletions(-) diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index a19c65333b..5137b0852f 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -112,6 +112,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOverscan, "EmuCore/GS", "pcrtc_overscan", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCAntiBlur, "EmuCore/GS", "pcrtc_antiblur", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.DisableInterlaceOffset, "EmuCore/GS", "disable_interlace_offset", false); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.screenshotSize, "EmuCore/GS", "ScreenshotSize", static_cast(GSScreenshotSize::WindowResolution)); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.screenshotFormat, "EmuCore/GS", "ScreenshotFormat", static_cast(GSScreenshotFormat::PNG)); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.screenshotQuality, "EmuCore/GS", "ScreenshotQuality", 50); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cropLeft, "EmuCore/GS", "CropLeft", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cropTop, "EmuCore/GS", "CropTop", 0); @@ -356,6 +359,13 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* dialog->registerWidgetHelp(m_ui.interlacing, tr("Deinterlacing"), tr("Automatic (Default)"), tr("")); + dialog->registerWidgetHelp(m_ui.screenshotSize, tr("Screenshot Size"), tr("Screen Resolution"), + tr("Determines the resolution at which screenshots will be saved. Internal resolutions preserve more detail at the cost of file size.")); + dialog->registerWidgetHelp(m_ui.screenshotFormat, tr("Screenshot Format"), tr("PNG"), + tr("Selects the format which will be used to save screenshots. JPEG produces smaller files, but loses detail.")); + dialog->registerWidgetHelp(m_ui.screenshotQuality, tr("Screenshot Quality"), tr("50%"), + tr("Selects the quality at which screenshots will be compressed. Higher values preserve more detail for JPEG, and reduce file size for PNG.")); + dialog->registerWidgetHelp(m_ui.stretchY, tr("Stretch Height"), tr("100%"), tr("")); dialog->registerWidgetHelp(m_ui.fullscreenModes, tr("Fullscreen Mode"), tr("Borderless Fullscreen"), tr("")); diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui index ef2f998170..56cabe7b81 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -6,8 +6,8 @@ 0 0 - 850 - 500 + 861 + 501 @@ -227,14 +227,14 @@ - + Vertical Stretch: - + % @@ -247,14 +247,14 @@ - + Crop: - + @@ -326,7 +326,7 @@ - + @@ -389,6 +389,70 @@ + + + + Screenshot Size: + + + + + + + + + + Screen Resolution + + + + + Internal Resolution + + + + + Internal Resolution (Aspect Uncorrected) + + + + + + + + + PNG + + + + + JPEG + + + + + + + + Quality: + + + + + + + % + + + 1 + + + 100 + + + + + diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 5be511ec5f..2f85c7048a 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -204,6 +204,20 @@ enum class TexturePreloadingLevel : u8 Full, }; +enum class GSScreenshotSize : u8 +{ + WindowResolution, + InternalResolution, + InternalResolutionUncorrected, +}; + +enum class GSScreenshotFormat : u8 +{ + PNG, + JPEG, + Count, +}; + enum class GSDumpCompressionMethod : u8 { Uncompressed, @@ -606,8 +620,14 @@ struct Pcsx2Config int ShadeBoost_Brightness{50}; int ShadeBoost_Contrast{50}; int ShadeBoost_Saturation{50}; + int SaveN{0}; int SaveL{5000}; + + GSScreenshotSize ScreenshotSize{GSScreenshotSize::WindowResolution}; + GSScreenshotFormat ScreenshotFormat{GSScreenshotFormat::PNG}; + int ScreenshotQuality{50}; + std::string Adapter; GSOptions(); diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 20b431d845..1df8fb23f0 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -2812,6 +2812,8 @@ void FullscreenUI::DrawGraphicsSettingsPage() static constexpr const char* s_generic_options[] = {"Automatic (Default)", "Force Disabled", "Force Enabled"}; static constexpr const char* s_hw_download[] = {"Accurate (Recommended)", "Disable Readbacks (Synchronize GS Thread)", "Unsynchronized (Non-Deterministic)", "Disabled (Ignore Transfers)"}; + static constexpr const char* s_screenshot_sizes[] = {"Screen Resolution", "Internal Resolution", "Internal Resolution (Uncorrected)"}; + static constexpr const char* s_screenshot_formats[] = {"PNG", "JPEG"}; SettingsInterface* bsi = GetEditingSettingsInterface(); @@ -2838,6 +2840,12 @@ void FullscreenUI::DrawGraphicsSettingsPage() DrawIntListSetting(bsi, "Deinterlacing", "Selects the algorithm used to convert the PS2's interlaced output to progressive for display.", "EmuCore/GS", "deinterlace_mode", static_cast(GSInterlaceMode::Automatic), s_deinterlacing_options, std::size(s_deinterlacing_options)); + DrawIntListSetting(bsi, "Screenshot Size", "Determines the resolution at which screenshots will be saved.", "EmuCore/GS", + "ScreenshotSize", static_cast(GSScreenshotSize::WindowResolution), s_screenshot_sizes, std::size(s_screenshot_sizes)); + DrawIntListSetting(bsi, "Screenshot Format", "Selects the format which will be used to save screenshots.", "EmuCore/GS", + "ScreenshotFormat", static_cast(GSScreenshotFormat::PNG), s_screenshot_formats, std::size(s_screenshot_formats)); + DrawIntRangeSetting(bsi, "Screenshot Quality", "Selects the quality at which screenshots will be compressed.", "EmuCore/GS", + "ScreenshotQuality", 50, 1, 100, "%d%%"); DrawIntRangeSetting(bsi, "Vertical Stretch", "Increases or decreases the virtual picture size vertically.", "EmuCore/GS", "StretchY", 100, 10, 300, "%d%%"); DrawIntRectSetting(bsi, "Crop", "Crops the image, while respecting aspect ratio.", "EmuCore/GS", "CropLeft", 0, "CropTop", 0, diff --git a/pcsx2/GS.h b/pcsx2/GS.h index 4fa4412bc9..0d62618baf 100644 --- a/pcsx2/GS.h +++ b/pcsx2/GS.h @@ -417,7 +417,8 @@ public: void SwitchRenderer(GSRendererType renderer, bool display_message = true); void SetSoftwareRendering(bool software, bool display_message = true); void ToggleSoftwareRendering(); - bool SaveMemorySnapshot(u32 width, u32 height, std::vector* pixels); + bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels); void SetRunIdle(bool enabled); protected: diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 2a38c9d34f..42745e60fc 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -919,12 +919,14 @@ void GSRestoreAPIState() g_gs_device->RestoreAPIState(); } -bool GSSaveSnapshotToMemory(u32 width, u32 height, std::vector* pixels) +bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels) { if (!g_gs_renderer) return false; - return g_gs_renderer->SaveSnapshotToMemory(width, height, pixels); + return g_gs_renderer->SaveSnapshotToMemory(window_width, window_height, apply_aspect, crop_borders, + width, height, pixels); } std::string format(const char* fmt, ...) diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index 338143a8ae..0b6dc16bcc 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -91,7 +91,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config); void GSSwitchRenderer(GSRendererType new_renderer); void GSResetAPIState(); void GSRestoreAPIState(); -bool GSSaveSnapshotToMemory(u32 width, u32 height, std::vector* pixels); +bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels); class GSApp { diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index bf93117798..2276d0a5ec 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -309,12 +309,6 @@ void GSDevice::ClearCurrent() m_mad = nullptr; m_target_tmp = nullptr; m_cas = nullptr; - m_temp_snapshot = nullptr; -} - -void GSDevice::SetSnapshot() -{ - m_temp_snapshot = m_current; } void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index 01d297a7a4..76a4ab0509 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -740,7 +740,6 @@ protected: GSTexture* m_target_tmp = nullptr; GSTexture* m_current = nullptr; GSTexture* m_cas = nullptr; - GSTexture* m_temp_snapshot = nullptr; // No need to delete this, only ever points to m_current. struct { @@ -841,10 +840,8 @@ public: __fi FeatureSupport Features() const { return m_features; } __fi GSTexture* GetCurrent() const { return m_current; } - __fi GSTexture* GetSnapshot() const { return m_temp_snapshot; } void ClearCurrent(); - void SetSnapshot(); void Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c); void Interlace(const GSVector2i& ds, int field, int mode, float yoffset); void FXAA(); diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp index 3891734970..93faf83500 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp +++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp @@ -20,7 +20,9 @@ #include "HostDisplay.h" #include "PerformanceMetrics.h" #include "pcsx2/Config.h" +#include "IconsFontAwesome5.h" #include "common/FileSystem.h" +#include "common/Image.h" #include "common/Path.h" #include "common/StringUtil.h" #include "common/Timer.h" @@ -398,8 +400,6 @@ bool GSRenderer::Merge(int field) if (GSConfig.FXAA) g_gs_device->FXAA(); - g_gs_device->SetSnapshot(); - // Sharpens biinear at lower resolutions, almost nearest but with more uniform pixels. if (GSConfig.LinearPresent == GSPostBilinearMode::BilinearSharp && (g_host_display->GetWindowWidth() > fs.x || g_host_display->GetWindowHeight() > fs.y)) { @@ -533,9 +533,6 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const static GSVector4i CalculateDrawSrcRect(const GSTexture* src) { -#ifndef PCSX2_CORE - return GSVector4i(0, 0, src->GetWidth(), src->GetHeight()); -#else const float upscale = GSConfig.UpscaleMultiplier; const GSVector2i size(src->GetSize()); const int left = static_cast(static_cast(GSConfig.Crop[0]) * upscale); @@ -543,7 +540,37 @@ static GSVector4i CalculateDrawSrcRect(const GSTexture* src) const int right = size.x - static_cast(static_cast(GSConfig.Crop[2]) * upscale); const int bottom = size.y - static_cast(static_cast(GSConfig.Crop[3]) * upscale); return GSVector4i(left, top, right, bottom); -#endif +} + +static const char* GetScreenshotSuffix() +{ + static constexpr const char* suffixes[static_cast(GSScreenshotFormat::Count)] = { + "png", "jpg"}; + return suffixes[static_cast(GSConfig.ScreenshotFormat)]; +} + +static void CompressAndWriteScreenshot(std::string filename, u32 width, u32 height, std::vector pixels) +{ + Common::RGBA8Image image; + image.SetPixels(width, height, std::move(pixels)); + + std::string key(fmt::format("GSScreenshot_{}", filename)); + Host::AddIconOSDMessage(key, ICON_FA_CAMERA, fmt::format("Saving screenshot to '{}'.", Path::GetFileName(filename)), 60.0f); + + // maybe std::async would be better here.. but it's definitely worth threading, large screenshots take a while to compress. + std::thread compress_thread([key = std::move(key), filename = std::move(filename), image = std::move(image), quality = GSConfig.ScreenshotQuality]() { + if (image.SaveToFile(filename.c_str(), quality)) + { + Host::AddIconOSDMessage(std::move(key), ICON_FA_CAMERA, + fmt::format("Saved screenshot to '{}'.", Path::GetFileName(filename)), Host::OSD_INFO_DURATION); + } + else + { + Host::AddIconOSDMessage(std::move(key), ICON_FA_CAMERA, + fmt::format("Failed to save screenshot to '{}'.", Path::GetFileName(filename), Host::OSD_ERROR_DURATION)); + } + }); + compress_thread.detach(); } void GSRenderer::VSync(u32 field, bool registers_written) @@ -654,6 +681,9 @@ void GSRenderer::VSync(u32 field, bool registers_written) if (!m_snapshot.empty()) { + u32 screenshot_width, screenshot_height; + std::vector screenshot_pixels; + if (!m_dump && m_dump_frames > 0) { freezeData fd = {0, nullptr}; @@ -664,14 +694,14 @@ void GSRenderer::VSync(u32 field, bool registers_written) // keep the screenshot relatively small so we don't bloat the dump static constexpr u32 DUMP_SCREENSHOT_WIDTH = 640; static constexpr u32 DUMP_SCREENSHOT_HEIGHT = 480; - std::vector screenshot_pixels; - SaveSnapshotToMemory(DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, &screenshot_pixels); + SaveSnapshotToMemory(DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, true, false, + &screenshot_width, &screenshot_height, &screenshot_pixels); std::string_view compression_str; if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed) { m_dump = std::unique_ptr(new GSDumpUncompressed(m_snapshot, GetDumpSerial(), m_crc, - DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, + screenshot_width, screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs)); compression_str = "with no compression"; @@ -679,7 +709,7 @@ void GSRenderer::VSync(u32 field, bool registers_written) else if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::LZMA) { m_dump = std::unique_ptr(new GSDumpXz(m_snapshot, GetDumpSerial(), m_crc, - DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, + screenshot_width, screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs)); compression_str = "with LZMA compression"; @@ -687,7 +717,7 @@ void GSRenderer::VSync(u32 field, bool registers_written) else { m_dump = std::unique_ptr(new GSDumpZst(m_snapshot, GetDumpSerial(), m_crc, - DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, + screenshot_width, screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs)); compression_str = "with Zstandard compression"; @@ -700,18 +730,21 @@ void GSRenderer::VSync(u32 field, bool registers_written) Path::GetFileName(m_dump->GetPath())), Host::OSD_INFO_DURATION); } - if (GSTexture* t = g_gs_device->GetSnapshot()) + const bool internal_resolution = (GSConfig.ScreenshotSize >= GSScreenshotSize::InternalResolution); + const bool aspect_correct = (GSConfig.ScreenshotSize != GSScreenshotSize::InternalResolutionUncorrected); + + if (g_gs_device->GetCurrent() && SaveSnapshotToMemory( + internal_resolution ? 0 : g_host_display->GetWindowWidth(), + internal_resolution ? 0 : g_host_display->GetWindowHeight(), + aspect_correct, true, + &screenshot_width, &screenshot_height, &screenshot_pixels)) { - const std::string path(m_snapshot + ".png"); - if (t->Save(path)) - { - Host::AddKeyedOSDMessage("GSScreenshot", - fmt::format("Screenshot saved to '{}'.", Path::GetFileName(path)), Host::OSD_INFO_DURATION); - } - else - { - Host::AddFormattedOSDMessage(Host::OSD_ERROR_DURATION, "Failed to save screenshot to '%s'.", path.c_str()); - } + CompressAndWriteScreenshot(fmt::format("{}.{}", m_snapshot, GetScreenshotSuffix()), + screenshot_width, screenshot_height, std::move(screenshot_pixels)); + } + else + { + Host::AddIconOSDMessage("GSScreenshot", ICON_FA_CAMERA, "Failed to render/download screenshot.", Host::OSD_ERROR_DURATION); } m_snapshot = {}; @@ -945,42 +978,69 @@ void GSRenderer::PurgeTextureCache() { } -bool GSRenderer::SaveSnapshotToMemory(u32 width, u32 height, std::vector* pixels) +bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels) { GSTexture* const current = g_gs_device->GetCurrent(); if (!current) + { + *width = 0; + *height = 0; + pixels->clear(); return false; + } const GSVector4i src_rect(CalculateDrawSrcRect(current)); const GSVector4 src_uv(GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy()); - GSVector4 draw_rect(CalculateDrawDstRect(width, height, src_rect, current->GetSize(), - HostDisplay::Alignment::LeftOrTop, false, GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets))); - u32 draw_width = static_cast(draw_rect.z - draw_rect.x); - u32 draw_height = static_cast(draw_rect.w - draw_rect.y); - if (draw_width > width) + + const bool is_progressive = (GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)); + GSVector4 draw_rect; + if (window_width == 0 || window_height == 0) { - draw_width = width; - draw_rect.left = 0; - draw_rect.right = width; + if (apply_aspect) + { + // use internal resolution of the texture + const float aspect = GetCurrentAspectRatioFloat(is_progressive); + const int tex_width = current->GetWidth(); + const int tex_height = current->GetHeight(); + + // expand to the larger dimension + const float tex_aspect = static_cast(tex_width) / static_cast(tex_height); + if (tex_aspect >= aspect) + draw_rect = GSVector4(0.0f, 0.0f, static_cast(tex_width), static_cast(tex_width) / aspect); + else + draw_rect = GSVector4(0.0f, 0.0f, static_cast(tex_height) * aspect, static_cast(tex_height)); + } + else + { + // uncorrected aspect is only available at internal resolution + draw_rect = GSVector4(0.0f, 0.0f, static_cast(current->GetWidth()), static_cast(current->GetHeight())); + } } - if (draw_height > height) + else { - draw_height = height; - draw_rect.top = 0; - draw_rect.bottom = height; + draw_rect = CalculateDrawDstRect(window_width, window_height, src_rect, current->GetSize(), + HostDisplay::Alignment::LeftOrTop, false, is_progressive); } + const u32 draw_width = static_cast(draw_rect.z - draw_rect.x); + const u32 draw_height = static_cast(draw_rect.w - draw_rect.y); + const u32 image_width = crop_borders ? draw_width : std::max(draw_width, window_width); + const u32 image_height = crop_borders ? draw_height : std::max(draw_height, window_height); GSTexture::GSMap map; const bool result = g_gs_device->DownloadTextureConvert( current, src_uv, GSVector2i(draw_width, draw_height), GSTexture::Format::Color, - ShaderConvert::COPY, map, true); + ShaderConvert::TRANSPARENCY_FILTER, map, true); if (result) { - const u32 pad_x = (width - draw_width) / 2; - const u32 pad_y = (height - draw_height) / 2; - pixels->resize(width * height, 0); - StringUtil::StrideMemCpy(pixels->data() + pad_y * width + pad_x, width * sizeof(u32), + const u32 pad_x = (image_width - draw_width) / 2; + const u32 pad_y = (image_height - draw_height) / 2; + pixels->clear(); + pixels->resize(image_width * image_height, 0); + *width = image_width; + *height = image_height; + StringUtil::StrideMemCpy(pixels->data() + pad_y * image_width + pad_x, image_width * sizeof(u32), map.bits, map.pitch, draw_width * sizeof(u32), draw_height); g_gs_device->DownloadTextureComplete(); diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.h b/pcsx2/GS/Renderers/Common/GSRenderer.h index a5a346a2a2..d1d4f78714 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.h +++ b/pcsx2/GS/Renderers/Common/GSRenderer.h @@ -44,7 +44,7 @@ private: protected: GSVector2i m_real_size{0, 0}; - bool m_texture_shuffle; + bool m_texture_shuffle = false; virtual GSTexture* GetOutput(int i, int& y_offset) = 0; virtual GSTexture* GetFeedbackOutput() { return nullptr; } @@ -66,7 +66,8 @@ public: virtual void PurgePool() override; virtual void PurgeTextureCache(); - bool SaveSnapshotToMemory(u32 width, u32 height, std::vector* pixels); + bool SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels); void QueueSnapshot(const std::string& path, u32 gsdump_frames); void StopGSDump(); diff --git a/pcsx2/GS/Renderers/Metal/convert.metal b/pcsx2/GS/Renderers/Metal/convert.metal index a750f8f228..3402a2bf15 100644 --- a/pcsx2/GS/Renderers/Metal/convert.metal +++ b/pcsx2/GS/Renderers/Metal/convert.metal @@ -126,8 +126,7 @@ fragment float4 ps_hdr_resolve(float4 p [[position]], DirectReadTextureIn fragment float4 ps_filter_transparency(ConvertShaderData data [[stage_in]], ConvertPSRes res) { float4 c = res.sample(data.t); - c.a = dot(c.rgb, float3(0.299f, 0.587f, 0.114f)); - return c; + return float4(c.rgb, 1.0); } fragment uint ps_convert_float32_32bits(ConvertShaderData data [[stage_in]], ConvertPSDepthRes res) diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 3ed792d4b1..d48a6095b4 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -1029,11 +1029,12 @@ void SysMtgsThread::ToggleSoftwareRendering() SetSoftwareRendering(GSConfig.Renderer != GSRendererType::SW); } -bool SysMtgsThread::SaveMemorySnapshot(u32 width, u32 height, std::vector* pixels) +bool SysMtgsThread::SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels) { bool result = false; - RunOnGSThread([width, height, pixels, &result]() { - result = GSSaveSnapshotToMemory(width, height, pixels); + RunOnGSThread([window_width, window_height, apply_aspect, crop_borders, width, height, pixels, &result]() { + result = GSSaveSnapshotToMemory(window_width, window_height, apply_aspect, crop_borders, width, height, pixels); }); WaitGS(false, false, false); return result; diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 21ff9839c8..160c87b4c7 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -425,6 +425,11 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const OpEqu(ShadeBoost_Saturation) && OpEqu(SaveN) && OpEqu(SaveL) && + + OpEqu(ScreenshotSize) && + OpEqu(ScreenshotFormat) && + OpEqu(ScreenshotQuality) && + OpEqu(Adapter)); } @@ -468,6 +473,9 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap) SettingsWrapBitBool(SyncToHostRefreshRate); SettingsWrapEnumEx(AspectRatio, "AspectRatio", AspectRatioNames); SettingsWrapEnumEx(FMVAspectRatioSwitch, "FMVAspectRatioSwitch", FMVAspectRatioSwitchNames); + SettingsWrapIntEnumEx(ScreenshotSize, "ScreenshotSize"); + SettingsWrapIntEnumEx(ScreenshotFormat, "ScreenshotFormat"); + SettingsWrapEntry(ScreenshotQuality); SettingsWrapEntry(StretchY); SettingsWrapEntryEx(Crop[0], "CropLeft"); SettingsWrapEntryEx(Crop[1], "CropTop"); diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index 44e07435a8..fcc1b401c1 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -775,16 +775,17 @@ std::unique_ptr SaveState_SaveScreenshot() static constexpr u32 SCREENSHOT_WIDTH = 640; static constexpr u32 SCREENSHOT_HEIGHT = 480; - std::vector pixels(SCREENSHOT_WIDTH * SCREENSHOT_HEIGHT); - if (!GetMTGS().SaveMemorySnapshot(SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT, &pixels)) + u32 width, height; + std::vector pixels; + if (!GetMTGS().SaveMemorySnapshot(SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT, true, false, &width, &height, &pixels)) { // saving failed for some reason, device lost? return nullptr; } std::unique_ptr data = std::make_unique(); - data->width = SCREENSHOT_WIDTH; - data->height = SCREENSHOT_HEIGHT; + data->width = width; + data->height = height; data->pixels = std::move(pixels); return data; }