From d6230bbad83d1961e31c88cc2da4f2a6eb1edc35 Mon Sep 17 00:00:00 2001 From: Filoppi Date: Mon, 18 Dec 2023 00:52:31 +0200 Subject: [PATCH 1/5] Video: Change the frame dumper to actually use the raw emulation output resolution, avoiding any scaling if possible. This should make comparisons much more reliable as pixels wouldn't be smushed together or stretched. --- .../Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp | 5 ++--- Source/Core/VideoCommon/Present.cpp | 11 +++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp index 9f86ea5ed177..34b6fde739ab 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp @@ -339,9 +339,8 @@ void AdvancedWidget::AddDescriptions() QT_TR_NOOP("Loads graphics mods from User/Load/GraphicsMods/.

If " "unsure, leave this unchecked."); static const char TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION[] = QT_TR_NOOP( - "Creates frame dumps and screenshots at the internal resolution of the renderer, rather than " - "the size of the window it is displayed within.

If the aspect ratio is widescreen, " - "the output image will be scaled horizontally to preserve the vertical resolution.

" + "Creates frame dumps and screenshots at the raw internal resolution of the renderer," + "rather than using the size it is displayed within the window.

" "If unsure, leave this unchecked."); #if defined(HAVE_FFMPEG) static const char TR_USE_FFV1_DESCRIPTION[] = diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 0cc19d6e1470..322685084dd7 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -214,14 +214,17 @@ void Presenter::ProcessFrameDumping(u64 ticks) const MathUtil::Rectangle target_rect; if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) { + // This is already scaled by "VIDEO_ENCODER_LCM" target_rect = GetTargetRectangle(); } else { - int width, height; - std::tie(width, height) = - CalculateOutputDimensions(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); - target_rect = MathUtil::Rectangle(0, 0, width, height); + target_rect = m_xfb_rect; + ASSERT(target_rect.top == 0 && target_rect.left == 0); + // Scale positively to make sure the least amount of information is lost. + // TODO: this should be added as black padding on the edges by the frame dumper + target_rect.right += VIDEO_ENCODER_LCM - (target_rect.GetWidth() % VIDEO_ENCODER_LCM); + target_rect.bottom += VIDEO_ENCODER_LCM - (target_rect.GetHeight() % VIDEO_ENCODER_LCM); } g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, From 1f34adf216ae4638bf6d420e95513b9e7c56b94f Mon Sep 17 00:00:00 2001 From: Filoppi Date: Fri, 23 Feb 2024 04:09:49 +0200 Subject: [PATCH 2/5] Video: move all padding added for frame dumping to a single function, which also avoids the output window from being resized randomly to be a multiple of 4 --- Source/Core/VideoCommon/Present.cpp | 51 ++++++++++++----------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 322685084dd7..d338821dd841 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -213,19 +213,29 @@ void Presenter::ProcessFrameDumping(u64 ticks) const { MathUtil::Rectangle target_rect; if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) - { - // This is already scaled by "VIDEO_ENCODER_LCM" target_rect = GetTargetRectangle(); - } else - { target_rect = m_xfb_rect; - ASSERT(target_rect.top == 0 && target_rect.left == 0); - // Scale positively to make sure the least amount of information is lost. - // TODO: this should be added as black padding on the edges by the frame dumper - target_rect.right += VIDEO_ENCODER_LCM - (target_rect.GetWidth() % VIDEO_ENCODER_LCM); - target_rect.bottom += VIDEO_ENCODER_LCM - (target_rect.GetHeight() % VIDEO_ENCODER_LCM); - } + + int width = target_rect.GetWidth(); + int height = target_rect.GetHeight(); + + // Ensure divisibility by "VIDEO_ENCODER_LCM" to make it compatible with all the video + // encoders. Note that this is theoretically only necessary when recording videos and not + // screenshots. + // We always scale positively to make sure the least amount of information is lost. + // + // TODO: this should be added as black padding on the edges by the frame dumper. + if ((width % VIDEO_ENCODER_LCM) != 0) + width += VIDEO_ENCODER_LCM - (width % VIDEO_ENCODER_LCM); + if ((height % VIDEO_ENCODER_LCM) != 0) + height += VIDEO_ENCODER_LCM - (height % VIDEO_ENCODER_LCM); + + // Remove any black borders, there would be no point in including them in the recording + target_rect.left = 0; + target_rect.top = 0; + target_rect.right = width; + target_rect.bottom = height; g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, m_frame_count); @@ -607,18 +617,7 @@ void Presenter::UpdateDrawRectangle() int int_draw_width; int int_draw_height; - if (g_frame_dumper->IsFrameDumping()) - { - // ensure divisibility by "VIDEO_ENCODER_LCM" to make it compatible with all the video encoders. - // Note that this is theoretically only necessary when recording videos and not screenshots. - draw_width = - std::ceil(draw_width) - static_cast(std::ceil(draw_width)) % VIDEO_ENCODER_LCM; - draw_height = - std::ceil(draw_height) - static_cast(std::ceil(draw_height)) % VIDEO_ENCODER_LCM; - int_draw_width = static_cast(draw_width); - int_draw_height = static_cast(draw_height); - } - else if (g_ActiveConfig.aspect_mode != AspectMode::Raw || !m_xfb_entry) + if (g_ActiveConfig.aspect_mode != AspectMode::Raw || !m_xfb_entry) { // Find the best integer resolution: the closest aspect ratio with the least black bars. // This should have no influence if "AspectMode::Stretch" is active. @@ -703,14 +702,6 @@ std::tuple Presenter::CalculateOutputDimensions(int width, int height, height = static_cast(std::ceil(scaled_height)); } - if (g_frame_dumper->IsFrameDumping()) - { - // UpdateDrawRectangle() makes sure that the rendered image is divisible by "VIDEO_ENCODER_LCM" - // for video encoders, so do that here too to match it - width -= width % VIDEO_ENCODER_LCM; - height -= height % VIDEO_ENCODER_LCM; - } - return std::make_tuple(width, height); } From 72db62e17856265adefaaff6c7827989a2b362a4 Mon Sep 17 00:00:00 2001 From: Filoppi Date: Thu, 22 Feb 2024 02:11:31 +0200 Subject: [PATCH 3/5] Video: split frame dumping settings into 3 resolution dumping modes also polish aspect ratio related code for clarity --- Source/Core/Core/Config/GraphicsSettings.cpp | 5 +- Source/Core/Core/Config/GraphicsSettings.h | 3 +- .../Config/Graphics/AdvancedWidget.cpp | 40 ++++++++++----- .../Config/Graphics/AdvancedWidget.h | 2 +- Source/Core/VideoCommon/Present.cpp | 51 +++++++++++++++---- Source/Core/VideoCommon/Present.h | 3 ++ Source/Core/VideoCommon/VideoConfig.cpp | 2 +- Source/Core/VideoCommon/VideoConfig.h | 13 ++++- 8 files changed, 90 insertions(+), 29 deletions(-) diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 4d18bd17aca8..2dfae2f8b3e5 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -70,8 +70,9 @@ const Info GFX_DUMP_PIXEL_FORMAT{{System::GFX, "Settings", "DumpPix const Info GFX_DUMP_ENCODER{{System::GFX, "Settings", "DumpEncoder"}, ""}; const Info GFX_DUMP_PATH{{System::GFX, "Settings", "DumpPath"}, ""}; const Info GFX_BITRATE_KBPS{{System::GFX, "Settings", "BitrateKbps"}, 25000}; -const Info GFX_INTERNAL_RESOLUTION_FRAME_DUMPS{ - {System::GFX, "Settings", "InternalResolutionFrameDumps"}, false}; +const Info GFX_FRAME_DUMPS_RESOLUTION_TYPE{ + {System::GFX, "Settings", "FrameDumpsResolutionType"}, + FrameDumpResolutionType::XFB_ASPECT_RATIO_CORRECTED_RESOLUTION}; const Info GFX_PNG_COMPRESSION_LEVEL{{System::GFX, "Settings", "PNGCompressionLevel"}, 6}; const Info GFX_ENABLE_GPU_TEXTURE_DECODING{ {System::GFX, "Settings", "EnableGPUTextureDecoding"}, false}; diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index d641ce4aaa65..bccc5ae293b5 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -14,6 +14,7 @@ enum class TextureFilteringMode : int; enum class OutputResamplingMode : int; enum class ColorCorrectionRegion : int; enum class TriState : int; +enum class FrameDumpResolutionType : int; namespace Config { @@ -67,7 +68,7 @@ extern const Info GFX_DUMP_PIXEL_FORMAT; extern const Info GFX_DUMP_ENCODER; extern const Info GFX_DUMP_PATH; extern const Info GFX_BITRATE_KBPS; -extern const Info GFX_INTERNAL_RESOLUTION_FRAME_DUMPS; +extern const Info GFX_FRAME_DUMPS_RESOLUTION_TYPE; extern const Info GFX_PNG_COMPRESSION_LEVEL; extern const Info GFX_ENABLE_GPU_TEXTURE_DECODING; extern const Info GFX_ENABLE_PIXEL_LIGHTING; diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp index 34b6fde739ab..a74cf2acb061 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp @@ -135,21 +135,24 @@ void AdvancedWidget::CreateWidgets() auto* dump_layout = new QGridLayout(); dump_box->setLayout(dump_layout); - m_use_fullres_framedumps = new ConfigBool(tr("Dump at Internal Resolution"), - Config::GFX_INTERNAL_RESOLUTION_FRAME_DUMPS); + m_frame_dumps_resolution_type = + new ConfigChoice({tr("Window Resolution"), tr("Aspect Ratio Corrected Internal Resolution"), + tr("Raw Internal Resolution")}, + Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE); m_dump_use_ffv1 = new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1); m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, 1000); m_png_compression_level = new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL); - dump_layout->addWidget(m_use_fullres_framedumps, 0, 0); + dump_layout->addWidget(new QLabel(tr("Resolution Type:")), 0, 0); + dump_layout->addWidget(m_frame_dumps_resolution_type, 0, 1); #if defined(HAVE_FFMPEG) - dump_layout->addWidget(m_dump_use_ffv1, 0, 1); - dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 1, 0); - dump_layout->addWidget(m_dump_bitrate, 1, 1); + dump_layout->addWidget(m_dump_use_ffv1, 1, 0); + dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 2, 0); + dump_layout->addWidget(m_dump_bitrate, 2, 1); #endif - dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 2, 0); + dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 3, 0); m_png_compression_level->SetTitle(tr("PNG Compression Level")); - dump_layout->addWidget(m_png_compression_level, 2, 1); + dump_layout->addWidget(m_png_compression_level, 3, 1); // Misc. auto* misc_box = new QGroupBox(tr("Misc")); @@ -338,10 +341,21 @@ void AdvancedWidget::AddDescriptions() static const char TR_LOAD_GRAPHICS_MODS_DESCRIPTION[] = QT_TR_NOOP("Loads graphics mods from User/Load/GraphicsMods/.

If " "unsure, leave this unchecked."); - static const char TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION[] = QT_TR_NOOP( - "Creates frame dumps and screenshots at the raw internal resolution of the renderer," - "rather than using the size it is displayed within the window.

" - "If unsure, leave this unchecked."); + static const char TR_FRAME_DUMPS_RESOLUTION_TYPE_DESCRIPTION[] = QT_TR_NOOP( + "Selects how frame dumps (videos) and screenshots are going to be captured.
If the game " + "or window resolution change during a recording, multiple video files might be created.
" + "Note that color correction and cropping are always ignored by the captures." + "

Window Resolution: Uses the output window resolution (without black bars)." + "
This is a simple dumping option that will capture the image more or less as you see it." + "
Aspect Ratio Corrected Internal Resolution: " + "Uses the Internal Resolution (XFB size), and corrects it by the target aspect ratio.
" + "This option will consistently dump at the specified Internal Resolution " + "regardless of how the image is displayed during recording." + "
Raw Internal Resolution: Uses the Internal Resolution (XFB size) " + "without correcting it with the target aspect ratio.
" + "This will provide a clean dump without any aspect ratio correction so users have as raw as " + "possible input for external editing software.

If unsure, leave " + "this at \"Aspect Ratio Corrected Internal Resolution\"."); #if defined(HAVE_FFMPEG) static const char TR_USE_FFV1_DESCRIPTION[] = QT_TR_NOOP("Encodes frame dumps using the FFV1 codec.

If " @@ -432,7 +446,7 @@ void AdvancedWidget::AddDescriptions() m_dump_xfb_target->SetDescription(tr(TR_DUMP_XFB_DESCRIPTION)); m_disable_vram_copies->SetDescription(tr(TR_DISABLE_VRAM_COPIES_DESCRIPTION)); m_enable_graphics_mods->SetDescription(tr(TR_LOAD_GRAPHICS_MODS_DESCRIPTION)); - m_use_fullres_framedumps->SetDescription(tr(TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION)); + m_frame_dumps_resolution_type->SetDescription(tr(TR_FRAME_DUMPS_RESOLUTION_TYPE_DESCRIPTION)); #ifdef HAVE_FFMPEG m_dump_use_ffv1->SetDescription(tr(TR_USE_FFV1_DESCRIPTION)); #endif diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h index 3d89b04ef0a1..6cd0e18fdb67 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h @@ -60,7 +60,7 @@ class AdvancedWidget final : public QWidget // Frame dumping ConfigBool* m_dump_use_ffv1; - ConfigBool* m_use_fullres_framedumps; + ConfigChoice* m_frame_dumps_resolution_type; ConfigInteger* m_dump_bitrate; ConfigInteger* m_png_compression_level; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index d338821dd841..3a026bedd511 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -212,23 +212,49 @@ void Presenter::ProcessFrameDumping(u64 ticks) const if (g_frame_dumper->IsFrameDumping() && m_xfb_entry) { MathUtil::Rectangle target_rect; - if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) - target_rect = GetTargetRectangle(); - else + switch (g_ActiveConfig.frame_dumps_resolution_type) + { + default: + case FrameDumpResolutionType::WINDOW_RESOLUTION: + { + if (!g_gfx->IsHeadless()) + { + target_rect = GetTargetRectangle(); + break; + } + [[fallthrough]]; + } + case FrameDumpResolutionType::XFB_ASPECT_RATIO_CORRECTED_RESOLUTION: + { + target_rect = m_xfb_rect; + const bool allow_stretch = false; + auto [float_width, float_height] = + ScaleToDisplayAspectRatio(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight(), allow_stretch); + const float draw_aspect_ratio = CalculateDrawAspectRatio(allow_stretch); + auto [int_width, int_height] = + FindClosestIntegerResolution(float_width, float_height, draw_aspect_ratio); + target_rect = MathUtil::Rectangle(0, 0, int_width, int_height); + break; + } + case FrameDumpResolutionType::XFB_RAW_RESOLUTION: + { target_rect = m_xfb_rect; + break; + } + } int width = target_rect.GetWidth(); int height = target_rect.GetHeight(); - // Ensure divisibility by "VIDEO_ENCODER_LCM" to make it compatible with all the video - // encoders. Note that this is theoretically only necessary when recording videos and not + // Ensure divisibility by "VIDEO_ENCODER_LCM" and a min of 1 to make it compatible with all the + // video encoders. Note that this is theoretically only necessary when recording videos and not // screenshots. // We always scale positively to make sure the least amount of information is lost. // // TODO: this should be added as black padding on the edges by the frame dumper. - if ((width % VIDEO_ENCODER_LCM) != 0) + if ((width % VIDEO_ENCODER_LCM) != 0 || width == 0) width += VIDEO_ENCODER_LCM - (width % VIDEO_ENCODER_LCM); - if ((height % VIDEO_ENCODER_LCM) != 0) + if ((height % VIDEO_ENCODER_LCM) != 0 || height == 0) height += VIDEO_ENCODER_LCM - (height % VIDEO_ENCODER_LCM); // Remove any black borders, there would be no point in including them in the recording @@ -237,6 +263,8 @@ void Presenter::ProcessFrameDumping(u64 ticks) const target_rect.right = width; target_rect.bottom = height; + // TODO: any scaling done by this won't be gamma corrected, + // we should either apply post processing as well, or port its gamma correction code g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, m_frame_count); } @@ -360,7 +388,8 @@ float Presenter::CalculateDrawAspectRatio(bool allow_stretch) const if (aspect_mode == AspectMode::Stretch) return (static_cast(m_backbuffer_width) / static_cast(m_backbuffer_height)); - auto& vi = Core::System::GetInstance().GetVideoInterface(); + // The actual aspect ratio of the XFB texture is irrelevant, the VI one is the one that matters + const auto& vi = Core::System::GetInstance().GetVideoInterface(); const float source_aspect_ratio = vi.GetAspectRatio(); // This will scale up the source ~4:3 resolution to its equivalent ~16:9 resolution @@ -551,7 +580,7 @@ void Presenter::UpdateDrawRectangle() // Don't know if there is a better place for this code so there isn't a 1 frame delay if (g_ActiveConfig.bWidescreenHack) { - auto& vi = Core::System::GetInstance().GetVideoInterface(); + const auto& vi = Core::System::GetInstance().GetVideoInterface(); float source_aspect_ratio = vi.GetAspectRatio(); // If the game is meant to be in widescreen (or forced to), // scale the source aspect ratio to it. @@ -595,9 +624,10 @@ void Presenter::UpdateDrawRectangle() // Crop the picture to a standard aspect ratio. (if enabled) auto [crop_width, crop_height] = ApplyStandardAspectCrop(draw_width, draw_height); + const float crop_aspect_ratio = crop_width / crop_height; // scale the picture to fit the rendering window - if (win_aspect_ratio >= crop_width / crop_height) + if (win_aspect_ratio >= crop_aspect_ratio) { // the window is flatter than the picture draw_width *= win_height / crop_height; @@ -668,6 +698,7 @@ std::tuple Presenter::ScaleToDisplayAspectRatio(const int width, c std::tuple Presenter::CalculateOutputDimensions(int width, int height, bool allow_stretch) const { + // Protect against zero width and height, a minimum of 1 will do width = std::max(width, 1); height = std::max(height, 1); diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 9723e1aa1968..3f8f43a687ce 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -107,10 +107,13 @@ class Presenter void OnBackBufferSizeChanged(); + // Scales a raw XFB resolution to the target (display) aspect ratio, + // also accounting for crop and other minor adjustments std::tuple CalculateOutputDimensions(int width, int height, bool allow_stretch = true) const; std::tuple ApplyStandardAspectCrop(float width, float height, bool allow_stretch = true) const; + // Scales a raw XFB resolution to the target (display) aspect ratio std::tuple ScaleToDisplayAspectRatio(int width, int height, bool allow_stretch = true) const; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index e11776d3568f..0266cb9c19b6 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -128,7 +128,7 @@ void VideoConfig::Refresh() sDumpEncoder = Config::Get(Config::GFX_DUMP_ENCODER); sDumpPath = Config::Get(Config::GFX_DUMP_PATH); iBitrateKbps = Config::Get(Config::GFX_BITRATE_KBPS); - bInternalResolutionFrameDumps = Config::Get(Config::GFX_INTERNAL_RESOLUTION_FRAME_DUMPS); + frame_dumps_resolution_type = Config::Get(Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE); bEnableGPUTextureDecoding = Config::Get(Config::GFX_ENABLE_GPU_TEXTURE_DECODING); bPreferVSForLinePointExpansion = Config::Get(Config::GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION); bEnablePixelLighting = Config::Get(Config::GFX_ENABLE_PIXEL_LIGHTING); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index d7ab34f1420e..baf8fb4e8b25 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -80,6 +80,16 @@ enum class TriState : int Auto }; +enum class FrameDumpResolutionType : int +{ + // Window resolution (not including potential back buffer black borders) + WINDOW_RESOLUTION, + // The aspect ratio corrected XFB resolution (XFB pixels might not have been square) + XFB_ASPECT_RATIO_CORRECTED_RESOLUTION, + // The raw unscaled XFB resolution (based on "internal resolution" scale) + XFB_RAW_RESOLUTION, +}; + // Bitmask containing information about which configuration has changed for the backend. enum ConfigChangeBits : u32 { @@ -189,7 +199,8 @@ struct VideoConfig final std::string sDumpEncoder; std::string sDumpFormat; std::string sDumpPath; - bool bInternalResolutionFrameDumps = false; + FrameDumpResolutionType frame_dumps_resolution_type = + FrameDumpResolutionType::XFB_ASPECT_RATIO_CORRECTED_RESOLUTION; bool bBorderlessFullscreen = false; bool bEnableGPUTextureDecoding = false; bool bPreferVSForLinePointExpansion = false; From 66592f79f2230bf5589bccb1f51da72150bf89c9 Mon Sep 17 00:00:00 2001 From: Filoppi Date: Sun, 3 Mar 2024 15:10:23 +0200 Subject: [PATCH 4/5] Video: remove enforced resolution least common multiple of 4 when dumping screenshots and not videos (only videos encoders have this limit). NOTE: this will likely trigger FIFOCI differences. --- Source/Core/VideoCommon/FrameDumper.cpp | 10 ++++++++++ Source/Core/VideoCommon/FrameDumper.h | 1 + Source/Core/VideoCommon/Present.cpp | 15 +++++++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index db4ef5205eb2..30f6a40e10c0 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -18,6 +18,9 @@ #include "VideoCommon/Present.h" #include "VideoCommon/VideoConfig.h" +// The video encoder needs the image to be a multiple of x samples. +static constexpr int VIDEO_ENCODER_LCM = 4; + static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) { return Common::ConvertRGBAToRGBAndSavePNG(file_name, frame.data, frame.width, frame.height, @@ -354,6 +357,13 @@ bool FrameDumper::IsFrameDumping() const return false; } +int FrameDumper::GetRequiredResolutionLeastCommonMultiple() const +{ + if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) + return VIDEO_ENCODER_LCM; + return 1; +} + void FrameDumper::DoState(PointerWrap& p) { #ifdef HAVE_FFMPEG diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index 859a4140e341..abf3bac38a58 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -33,6 +33,7 @@ class FrameDumper void SaveScreenshot(std::string filename); bool IsFrameDumping() const; + int GetRequiredResolutionLeastCommonMultiple() const; void DoState(PointerWrap& p); diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 3a026bedd511..7de27f504987 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -25,9 +25,6 @@ std::unique_ptr g_presenter; -// The video encoder needs the image to be a multiple of x samples. -static constexpr int VIDEO_ENCODER_LCM = 4; - namespace VideoCommon { // Stretches the native/internal analog resolution aspect ratio from ~4:3 to ~16:9 @@ -246,16 +243,18 @@ void Presenter::ProcessFrameDumping(u64 ticks) const int width = target_rect.GetWidth(); int height = target_rect.GetHeight(); - // Ensure divisibility by "VIDEO_ENCODER_LCM" and a min of 1 to make it compatible with all the + const int resolution_lcm = g_frame_dumper->GetRequiredResolutionLeastCommonMultiple(); + + // Ensure divisibility by the dumper LCM and a min of 1 to make it compatible with all the // video encoders. Note that this is theoretically only necessary when recording videos and not // screenshots. // We always scale positively to make sure the least amount of information is lost. // // TODO: this should be added as black padding on the edges by the frame dumper. - if ((width % VIDEO_ENCODER_LCM) != 0 || width == 0) - width += VIDEO_ENCODER_LCM - (width % VIDEO_ENCODER_LCM); - if ((height % VIDEO_ENCODER_LCM) != 0 || height == 0) - height += VIDEO_ENCODER_LCM - (height % VIDEO_ENCODER_LCM); + if ((width % resolution_lcm) != 0 || width == 0) + width += resolution_lcm - (width % resolution_lcm); + if ((height % resolution_lcm) != 0 || height == 0) + height += resolution_lcm - (height % resolution_lcm); // Remove any black borders, there would be no point in including them in the recording target_rect.left = 0; From 2f13be5a2d05b41ba91a2abfa4af2c79a85bb34f Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 13 Apr 2024 03:20:06 +0200 Subject: [PATCH 5/5] VideoConfig: Adjust FrameDumpResolutionType enum class to style guidelines --- Source/Core/Core/Config/GraphicsSettings.cpp | 2 +- Source/Core/VideoCommon/Present.cpp | 6 +++--- Source/Core/VideoCommon/VideoConfig.h | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 2dfae2f8b3e5..4dfe819beb55 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -72,7 +72,7 @@ const Info GFX_DUMP_PATH{{System::GFX, "Settings", "DumpPath"}, ""} const Info GFX_BITRATE_KBPS{{System::GFX, "Settings", "BitrateKbps"}, 25000}; const Info GFX_FRAME_DUMPS_RESOLUTION_TYPE{ {System::GFX, "Settings", "FrameDumpsResolutionType"}, - FrameDumpResolutionType::XFB_ASPECT_RATIO_CORRECTED_RESOLUTION}; + FrameDumpResolutionType::XFBAspectRatioCorrectedResolution}; const Info GFX_PNG_COMPRESSION_LEVEL{{System::GFX, "Settings", "PNGCompressionLevel"}, 6}; const Info GFX_ENABLE_GPU_TEXTURE_DECODING{ {System::GFX, "Settings", "EnableGPUTextureDecoding"}, false}; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 7de27f504987..d6794a5d545a 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -212,7 +212,7 @@ void Presenter::ProcessFrameDumping(u64 ticks) const switch (g_ActiveConfig.frame_dumps_resolution_type) { default: - case FrameDumpResolutionType::WINDOW_RESOLUTION: + case FrameDumpResolutionType::WindowResolution: { if (!g_gfx->IsHeadless()) { @@ -221,7 +221,7 @@ void Presenter::ProcessFrameDumping(u64 ticks) const } [[fallthrough]]; } - case FrameDumpResolutionType::XFB_ASPECT_RATIO_CORRECTED_RESOLUTION: + case FrameDumpResolutionType::XFBAspectRatioCorrectedResolution: { target_rect = m_xfb_rect; const bool allow_stretch = false; @@ -233,7 +233,7 @@ void Presenter::ProcessFrameDumping(u64 ticks) const target_rect = MathUtil::Rectangle(0, 0, int_width, int_height); break; } - case FrameDumpResolutionType::XFB_RAW_RESOLUTION: + case FrameDumpResolutionType::XFBRawResolution: { target_rect = m_xfb_rect; break; diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index baf8fb4e8b25..1e5f03d0e5e8 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -83,11 +83,11 @@ enum class TriState : int enum class FrameDumpResolutionType : int { // Window resolution (not including potential back buffer black borders) - WINDOW_RESOLUTION, + WindowResolution, // The aspect ratio corrected XFB resolution (XFB pixels might not have been square) - XFB_ASPECT_RATIO_CORRECTED_RESOLUTION, + XFBAspectRatioCorrectedResolution, // The raw unscaled XFB resolution (based on "internal resolution" scale) - XFB_RAW_RESOLUTION, + XFBRawResolution, }; // Bitmask containing information about which configuration has changed for the backend. @@ -200,7 +200,7 @@ struct VideoConfig final std::string sDumpFormat; std::string sDumpPath; FrameDumpResolutionType frame_dumps_resolution_type = - FrameDumpResolutionType::XFB_ASPECT_RATIO_CORRECTED_RESOLUTION; + FrameDumpResolutionType::XFBAspectRatioCorrectedResolution; bool bBorderlessFullscreen = false; bool bEnableGPUTextureDecoding = false; bool bPreferVSForLinePointExpansion = false;