diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index bb6f189b0..5da2f140d 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -232,18 +232,20 @@ void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, float* apply_aspect_ratio ? (display_aspect_ratio / (static_cast(m_display_width) / static_cast(m_display_height))) : 1.0f; - const float display_width = g_settings.display_stretch_vertically ? - static_cast(m_display_width) : static_cast(m_display_width) * x_scale; - const float display_height = g_settings.display_stretch_vertically ? - static_cast(m_display_height) / x_scale : static_cast(m_display_height); - const float active_left = g_settings.display_stretch_vertically ? - static_cast(m_display_active_left) : static_cast(m_display_active_left) * x_scale; - const float active_top = g_settings.display_stretch_vertically ? - static_cast(m_display_active_top) / x_scale : static_cast(m_display_active_top); + const float display_width = g_settings.display_stretch_vertically ? static_cast(m_display_width) : + static_cast(m_display_width) * x_scale; + const float display_height = g_settings.display_stretch_vertically ? static_cast(m_display_height) / x_scale : + static_cast(m_display_height); + const float active_left = g_settings.display_stretch_vertically ? static_cast(m_display_active_left) : + static_cast(m_display_active_left) * x_scale; + const float active_top = g_settings.display_stretch_vertically ? static_cast(m_display_active_top) / x_scale : + static_cast(m_display_active_top); const float active_width = g_settings.display_stretch_vertically ? - static_cast(m_display_active_width) : static_cast(m_display_active_width) * x_scale; + static_cast(m_display_active_width) : + static_cast(m_display_active_width) * x_scale; const float active_height = g_settings.display_stretch_vertically ? - static_cast(m_display_active_height) / x_scale : static_cast(m_display_active_height); + static_cast(m_display_active_height) / x_scale : + static_cast(m_display_active_height); if (out_x_scale) *out_x_scale = x_scale; @@ -500,11 +502,13 @@ bool HostDisplay::WriteDisplayTextureToFile(std::string filename, bool full_reso const float ss_height_scale = static_cast(m_display_active_height) / static_cast(m_display_height); const float ss_aspect_ratio = m_display_aspect_ratio * ss_width_scale / ss_height_scale; resize_width = g_settings.display_stretch_vertically ? - m_display_texture_view_width : static_cast(static_cast(resize_height) * ss_aspect_ratio); + m_display_texture_view_width : + static_cast(static_cast(resize_height) * ss_aspect_ratio); resize_height = g_settings.display_stretch_vertically ? - static_cast(static_cast(resize_height) / - (m_display_aspect_ratio / (static_cast(m_display_width) / static_cast(m_display_height)))) : - resize_height; + static_cast(static_cast(resize_height) / + (m_display_aspect_ratio / + (static_cast(m_display_width) / static_cast(m_display_height)))) : + resize_height; } else { @@ -614,17 +618,65 @@ bool HostDisplay::WriteDisplayTextureToBuffer(std::vector* buffer, u32 resi return true; } -bool HostDisplay::WriteScreenshotToFile(std::string filename, bool compress_on_thread /*= false*/) +bool HostDisplay::WriteScreenshotToFile(std::string filename, bool internal_resolution /* = false */, + bool compress_on_thread /* = false */) { - const u32 width = m_window_info.surface_width; - const u32 height = m_window_info.surface_height; + u32 width = m_window_info.surface_width; + u32 height = m_window_info.surface_height; + auto [draw_left, draw_top, draw_width, draw_height] = CalculateDrawRect(width, height); + + if (internal_resolution && m_display_texture_view_width != 0 && m_display_texture_view_height != 0) + { + // If internal res, scale the computed draw rectangle to the internal res. + // We re-use the draw rect because it's already been AR corrected. + const float sar = + static_cast(m_display_texture_view_width) / static_cast(m_display_texture_view_height); + const float dar = static_cast(draw_width) / static_cast(draw_height); + if (sar >= dar) + { + // stretch height, preserve width + const float scale = static_cast(m_display_texture_view_width) / static_cast(draw_width); + width = m_display_texture_view_width; + height = static_cast(std::round(static_cast(draw_height) * scale)); + } + else + { + // stretch width, preserve height + const float scale = static_cast(m_display_texture_view_height) / static_cast(draw_height); + width = static_cast(std::round(static_cast(draw_width) * scale)); + height = m_display_texture_view_height; + } + + // DX11 won't go past 16K texture size. + constexpr u32 MAX_TEXTURE_SIZE = 16384; + if (width > MAX_TEXTURE_SIZE) + { + height = static_cast(static_cast(height) / + (static_cast(width) / static_cast(MAX_TEXTURE_SIZE))); + width = MAX_TEXTURE_SIZE; + } + if (height > MAX_TEXTURE_SIZE) + { + height = MAX_TEXTURE_SIZE; + width = static_cast(static_cast(width) / + (static_cast(height) / static_cast(MAX_TEXTURE_SIZE))); + } + + // Remove padding, it's not part of the framebuffer. + draw_left = 0; + draw_top = 0; + draw_width = static_cast(width); + draw_height = static_cast(height); + } if (width == 0 || height == 0) return false; std::vector pixels; u32 pixels_stride; GPUTexture::Format pixels_format; - if (!RenderScreenshot(width, height, &pixels, &pixels_stride, &pixels_format)) + if (!RenderScreenshot(width, height, + Common::Rectangle::FromExtents(draw_top, draw_left, draw_width, draw_height), &pixels, + &pixels_stride, &pixels_format)) { Log_ErrorPrintf("Failed to render %ux%u screenshot", width, height); return false; diff --git a/src/core/host_display.h b/src/core/host_display.h index f406ef4de..6332624b3 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -104,8 +104,8 @@ public: virtual bool Render(bool skip_present) = 0; /// Renders the display with postprocessing to the specified image. - virtual bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) = 0; + virtual bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, + std::vector* out_pixels, u32* out_stride, GPUTexture::Format* out_format) = 0; ALWAYS_INLINE bool IsVsyncEnabled() const { return m_vsync_enabled; } virtual void SetVSync(bool enabled) = 0; @@ -206,7 +206,7 @@ public: bool clear_alpha = true); /// Helper function to save screenshot to PNG. - bool WriteScreenshotToFile(std::string filename, bool compress_on_thread = false); + bool WriteScreenshotToFile(std::string filename, bool internal_resolution = false, bool compress_on_thread = false); protected: ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast(m_cursor_texture); } diff --git a/src/core/system.cpp b/src/core/system.cpp index e2a7141d2..59b2b8d7a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -2069,8 +2069,9 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 * std::vector screenshot_buffer; u32 screenshot_stride; GPUTexture::Format screenshot_format; - if (g_host_display->RenderScreenshot(screenshot_width, screenshot_height, &screenshot_buffer, &screenshot_stride, - &screenshot_format) && + if (g_host_display->RenderScreenshot(screenshot_width, screenshot_height, + Common::Rectangle::FromExtents(0, 0, screenshot_width, screenshot_height), + &screenshot_buffer, &screenshot_stride, &screenshot_format) && GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride, screenshot_format)) { @@ -3911,10 +3912,8 @@ bool System::SaveScreenshot(const char* filename /* = nullptr */, bool full_reso return false; } - const bool screenshot_saved = - g_settings.display_internal_resolution_screenshots ? - g_host_display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio, compress_on_thread) : - g_host_display->WriteScreenshotToFile(filename, compress_on_thread); + const bool screenshot_saved = g_host_display->WriteScreenshotToFile( + filename, g_settings.display_internal_resolution_screenshots, compress_on_thread); if (!screenshot_saved) { diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index 8d40248b5..e28e17212 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -705,8 +705,8 @@ bool D3D11HostDisplay::Render(bool skip_present) return true; } -bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) +bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, + std::vector* out_pixels, u32* out_stride, GPUTexture::Format* out_format) { static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8; @@ -720,20 +720,18 @@ bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* if (HasDisplayTexture()) { - const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height); - if (!m_post_processing_chain.IsEmpty()) { - ApplyPostProcessingChain(render_texture.GetD3DRTV(), left, top, draw_width, draw_height, - static_cast(m_display_texture), m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, - width, height); + ApplyPostProcessingChain(render_texture.GetD3DRTV(), draw_rect.left, draw_rect.top, draw_rect.GetWidth(), + draw_rect.GetHeight(), static_cast(m_display_texture), + m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, + m_display_texture_view_height, width, height); } else { - RenderDisplay(left, top, draw_width, draw_height, static_cast(m_display_texture), - m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, - m_display_texture_view_height, IsUsingLinearFiltering()); + RenderDisplay(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(), + static_cast(m_display_texture), m_display_texture_view_x, m_display_texture_view_y, + m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering()); } } diff --git a/src/frontend-common/d3d11_host_display.h b/src/frontend-common/d3d11_host_display.h index 32154e4ee..f6c4f725c 100644 --- a/src/frontend-common/d3d11_host_display.h +++ b/src/frontend-common/d3d11_host_display.h @@ -67,8 +67,8 @@ public: void SetVSync(bool enabled) override; bool Render(bool skip_present) override; - bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) override; + bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, std::vector* out_pixels, + u32* out_stride, GPUTexture::Format* out_format) override; static AdapterAndModeList StaticGetAdapterAndModeList(); diff --git a/src/frontend-common/d3d12_host_display.cpp b/src/frontend-common/d3d12_host_display.cpp index faafe2641..1e3df79ea 100644 --- a/src/frontend-common/d3d12_host_display.cpp +++ b/src/frontend-common/d3d12_host_display.cpp @@ -615,8 +615,8 @@ bool D3D12HostDisplay::Render(bool skip_present) return true; } -bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) +bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, + std::vector* out_pixels, u32* out_stride, GPUTexture::Format* out_format) { static constexpr DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8; @@ -630,14 +630,13 @@ bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* } ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height); if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty()) { - ApplyPostProcessingChain(cmdlist, &render_texture, left, top, width, height, - static_cast(m_display_texture), m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, - width, height); + ApplyPostProcessingChain(cmdlist, &render_texture, draw_rect.left, draw_rect.top, draw_rect.GetWidth(), + draw_rect.GetHeight(), static_cast(m_display_texture), + m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, + m_display_texture_view_height, width, height); } else { @@ -647,9 +646,9 @@ bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* if (HasDisplayTexture()) { - RenderDisplay(cmdlist, left, top, draw_width, draw_height, static_cast(m_display_texture), - m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, - m_display_texture_view_height, IsUsingLinearFiltering()); + RenderDisplay(cmdlist, draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(), + static_cast(m_display_texture), m_display_texture_view_x, m_display_texture_view_y, + m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering()); } } diff --git a/src/frontend-common/d3d12_host_display.h b/src/frontend-common/d3d12_host_display.h index 0aeb2f2c8..dfd49848d 100644 --- a/src/frontend-common/d3d12_host_display.h +++ b/src/frontend-common/d3d12_host_display.h @@ -66,8 +66,8 @@ public: void SetVSync(bool enabled) override; bool Render(bool skip_present) override; - bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) override; + bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, std::vector* out_pixels, + u32* out_stride, GPUTexture::Format* out_format) override; bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp index d9e120fe4..c058b56de 100644 --- a/src/frontend-common/opengl_host_display.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -680,8 +680,8 @@ bool OpenGLHostDisplay::Render(bool skip_present) return true; } -bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) +bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, + std::vector* out_pixels, u32* out_stride, GPUTexture::Format* out_format) { GL::Texture texture; if (!texture.Create(width, height, 1, 1, 1, GPUTexture::Format::RGBA8, nullptr, 0) || !texture.CreateFramebuffer()) @@ -692,14 +692,13 @@ bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector glDisable(GL_SCISSOR_TEST); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height); - if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty()) { - ApplyPostProcessingChain(texture.GetGLFramebufferID(), left, height - top - draw_height, draw_width, draw_height, - static_cast(m_display_texture), m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, - width, height); + ApplyPostProcessingChain(texture.GetGLFramebufferID(), draw_rect.left, + height - draw_rect.top - draw_rect.GetHeight(), draw_rect.GetWidth(), + draw_rect.GetHeight(), static_cast(m_display_texture), + m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, + m_display_texture_view_height, width, height); } else { @@ -708,9 +707,10 @@ bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector if (HasDisplayTexture()) { - RenderDisplay(left, height - top - draw_height, draw_width, draw_height, - static_cast(m_display_texture), m_display_texture_view_x, m_display_texture_view_y, - m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering()); + RenderDisplay(draw_rect.left, height - draw_rect.top - draw_rect.GetHeight(), draw_rect.GetWidth(), + draw_rect.GetHeight(), static_cast(m_display_texture), m_display_texture_view_x, + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + IsUsingLinearFiltering()); } } diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h index 3ae85976e..2cc21c120 100644 --- a/src/frontend-common/opengl_host_display.h +++ b/src/frontend-common/opengl_host_display.h @@ -55,8 +55,8 @@ public: void SetVSync(bool enabled) override; bool Render(bool skip_present) override; - bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) override; + bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, std::vector* out_pixels, + u32* out_stride, GPUTexture::Format* out_format) override; bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index 990f8704d..aa0eade9f 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -677,8 +677,8 @@ bool VulkanHostDisplay::Render(bool skip_present) return true; } -bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) +bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, + std::vector* out_pixels, u32* out_stride, GPUTexture::Format* out_format) { // in theory we could do this without a swap chain, but postprocessing assumes it for now... if (!m_swap_chain) @@ -746,20 +746,19 @@ bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector "VulkanHostDisplay::RenderScreenshot: %ux%u", width, height); tex.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height); - if (!m_post_processing_chain.IsEmpty()) { - ApplyPostProcessingChain(fb, left, top, draw_width, draw_height, static_cast(m_display_texture), - m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, - m_display_texture_view_height, width, height); + ApplyPostProcessingChain(fb, draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(), + static_cast(m_display_texture), m_display_texture_view_x, + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + width, height); } else { BeginSwapChainRenderPass(fb, width, height); - RenderDisplay(left, top, draw_width, draw_height, static_cast(m_display_texture), - m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, - m_display_texture_view_height, IsUsingLinearFiltering()); + RenderDisplay(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(), + static_cast(m_display_texture), m_display_texture_view_x, m_display_texture_view_y, + m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering()); } vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer()); diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h index 7106aa88c..1da6c500a 100644 --- a/src/frontend-common/vulkan_host_display.h +++ b/src/frontend-common/vulkan_host_display.h @@ -59,8 +59,8 @@ public: void SetVSync(bool enabled) override; bool Render(bool skip_present) override; - bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, - GPUTexture::Format* out_format) override; + bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle& draw_rect, std::vector* out_pixels, + u32* out_stride, GPUTexture::Format* out_format) override; bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override;