From 2aea58d056fd1fd97b881c2e8a25393789301e47 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 6 Mar 2021 02:11:17 +1000 Subject: [PATCH] HostDisplay: Add method to render screenshots at window size --- src/core/host_display.cpp | 37 ++++++ src/core/host_display.h | 7 ++ src/frontend-common/d3d11_host_display.cpp | 61 +++++++++- src/frontend-common/d3d11_host_display.h | 4 +- src/frontend-common/opengl_host_display.cpp | 53 +++++++- src/frontend-common/opengl_host_display.h | 11 +- src/frontend-common/vulkan_host_display.cpp | 128 +++++++++++++++++--- src/frontend-common/vulkan_host_display.h | 11 +- 8 files changed, 279 insertions(+), 33 deletions(-) diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index b7af94a29..141fcad5e 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -613,3 +613,40 @@ bool HostDisplay::WriteDisplayTextureToBuffer(std::vector* buffer, u32 resi return true; } + +bool HostDisplay::WriteScreenshotToFile(std::string filename, bool compress_on_thread /*= false*/) +{ + const u32 width = m_window_info.surface_width; + const u32 height = m_window_info.surface_height; + if (width == 0 || height == 0) + return false; + + std::vector pixels; + u32 pixels_stride; + HostDisplayPixelFormat pixels_format; + if (!RenderScreenshot(width, height, &pixels, &pixels_stride, &pixels_format)) + { + Log_ErrorPrintf("Failed to render %ux%u screenshot", width, height); + return false; + } + + auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb"); + if (!fp) + { + Log_ErrorPrintf("Can't open file '%s': errno %d", filename.c_str(), errno); + return false; + } + + const RenderAPI api = GetRenderAPI(); + const bool flip_y = (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES); + if (!compress_on_thread) + { + return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), true, flip_y, width, height, + std::move(pixels), pixels_stride, pixels_format); + } + + std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp), true, + flip_y, width, height, std::move(pixels), pixels_stride, pixels_format); + compress_thread.detach(); + return true; +} diff --git a/src/core/host_display.h b/src/core/host_display.h index 03601c4d6..1c683eb74 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -115,6 +115,10 @@ public: /// Returns false if the window was completely occluded. virtual bool Render() = 0; + /// Renders the display with postprocessing to the specified image. + virtual bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) = 0; + virtual void SetVSync(bool enabled) = 0; #ifdef WITH_IMGUI @@ -232,6 +236,9 @@ public: bool WriteDisplayTextureToBuffer(std::vector* buffer, u32 resize_width = 0, u32 resize_height = 0, bool clear_alpha = true); + /// Helper function to save screenshot to PNG. + bool WriteScreenshotToFile(std::string filename, bool compress_on_thread = false); + protected: ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast(m_cursor_texture); } ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture_handle != nullptr); } diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index b9a35d6ff..12faaf857 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -709,6 +709,59 @@ bool D3D11HostDisplay::Render() return true; } +bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) +{ + static constexpr DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; + static constexpr HostDisplayPixelFormat hdformat = HostDisplayPixelFormat::RGBA8; + + D3D11::Texture render_texture; + if (!render_texture.Create(m_device.Get(), width, height, 1, 1, format, D3D11_BIND_RENDER_TARGET) || + !m_readback_staging_texture.EnsureSize(m_context.Get(), width, height, format, false)) + { + return false; + } + + static constexpr std::array clear_color = {}; + m_context->ClearRenderTargetView(render_texture.GetD3DRTV(), clear_color.data()); + m_context->OMSetRenderTargets(1, render_texture.GetD3DRTVArray(), nullptr); + + if (HasDisplayTexture()) + { + const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height, 0); + + if (!m_post_processing_chain.IsEmpty()) + { + ApplyPostProcessingChain(render_texture.GetD3DRTV(), left, top, draw_width, draw_height, m_display_texture_handle, + m_display_texture_width, m_display_texture_height, 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, m_display_texture_handle, m_display_texture_width, + m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y, + m_display_texture_view_width, m_display_texture_view_height, m_display_linear_filtering); + } + } + + m_context->OMSetRenderTargets(0, nullptr, nullptr); + + m_readback_staging_texture.CopyFromTexture(m_context.Get(), render_texture, 0, 0, 0, 0, 0, width, height); + + if (!m_readback_staging_texture.Map(m_context.Get(), false)) + return false; + + const u32 stride = sizeof(u32) * width; + out_pixels->resize(width * height); + *out_stride = stride; + *out_format = hdformat; + + m_readback_staging_texture.ReadPixels(0, 0, width, height, stride, out_pixels->data()); + m_readback_staging_texture.Unmap(m_context.Get()); + return true; +} + void D3D11HostDisplay::RenderImGui() { ImGui::Render(); @@ -726,7 +779,8 @@ void D3D11HostDisplay::RenderDisplay() { ApplyPostProcessingChain(m_swap_chain_rtv.Get(), left, top, width, height, m_display_texture_handle, m_display_texture_width, m_display_texture_height, m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height); + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + GetWindowWidth(), GetWindowHeight()); return; } @@ -971,11 +1025,12 @@ bool D3D11HostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 ta void D3D11HostDisplay::ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x, - s32 texture_view_y, s32 texture_view_width, s32 texture_view_height) + s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, + u32 target_width, u32 target_height) { static constexpr std::array clear_color = {0.0f, 0.0f, 0.0f, 1.0f}; - if (!CheckPostProcessingRenderTargets(GetWindowWidth(), GetWindowHeight())) + if (!CheckPostProcessingRenderTargets(target_width, target_height)) { RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering); diff --git a/src/frontend-common/d3d11_host_display.h b/src/frontend-common/d3d11_host_display.h index 5ed518430..b90a89ea7 100644 --- a/src/frontend-common/d3d11_host_display.h +++ b/src/frontend-common/d3d11_host_display.h @@ -68,6 +68,8 @@ public: virtual void SetVSync(bool enabled) override; virtual bool Render() override; + virtual bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) override; static AdapterAndModeList StaticGetAdapterAndModeList(); @@ -107,7 +109,7 @@ protected: void ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, - s32 texture_view_height); + s32 texture_view_height, u32 target_width, u32 target_height); ComPtr m_device; ComPtr m_context; diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp index 4abc11023..2e2e7f7d1 100644 --- a/src/frontend-common/opengl_host_display.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -749,7 +749,6 @@ bool OpenGLHostDisplay::Render() return false; } - glDisable(GL_SCISSOR_TEST); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -765,6 +764,46 @@ bool OpenGLHostDisplay::Render() return true; } +bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) +{ + GL::Texture texture; + if (!texture.Create(width, height, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr) || !texture.CreateFramebuffer()) + return false; + + texture.BindFramebuffer(GL_FRAMEBUFFER); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (HasDisplayTexture()) + { + const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height, 0); + + if (!m_post_processing_chain.IsEmpty()) + { + ApplyPostProcessingChain(texture.GetGLFramebufferID(), left, height - top - draw_height, draw_width, draw_height, + m_display_texture_handle, m_display_texture_width, m_display_texture_height, + 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, height - top - draw_height, draw_width, draw_height, m_display_texture_handle, + m_display_texture_width, m_display_texture_height, m_display_texture_view_x, + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + m_display_linear_filtering); + } + } + + out_pixels->resize(width * height); + *out_stride = sizeof(u32) * width; + *out_format = HostDisplayPixelFormat::RGBA8; + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, out_pixels->data()); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return true; +} + void OpenGLHostDisplay::RenderImGui() { ImGui::Render(); @@ -783,7 +822,8 @@ void OpenGLHostDisplay::RenderDisplay() { ApplyPostProcessingChain(0, left, GetWindowHeight() - top - height, width, height, m_display_texture_handle, m_display_texture_width, m_display_texture_height, m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height); + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + GetWindowWidth(), GetWindowHeight()); return; } @@ -995,11 +1035,12 @@ bool OpenGLHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y, - s32 texture_view_width, s32 texture_view_height) + s32 texture_view_width, s32 texture_view_height, u32 target_width, + u32 target_height) { - if (!CheckPostProcessingRenderTargets(GetWindowWidth(), GetWindowHeight())) + if (!CheckPostProcessingRenderTargets(target_width, target_height)) { - RenderDisplay(final_left, GetWindowHeight() - final_top - final_height, final_width, final_height, texture_handle, + RenderDisplay(final_left, target_height - final_top - final_height, final_width, final_height, texture_handle, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering); return; @@ -1008,7 +1049,7 @@ void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_ // downsample/upsample - use same viewport for remainder m_post_processing_input_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER); glClear(GL_COLOR_BUFFER_BIT); - RenderDisplay(final_left, GetWindowHeight() - final_top - final_height, final_width, final_height, texture_handle, + RenderDisplay(final_left, target_height - final_top - final_height, final_width, final_height, texture_handle, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering); diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h index 0eef611a2..f95e18523 100644 --- a/src/frontend-common/opengl_host_display.h +++ b/src/frontend-common/opengl_host_display.h @@ -32,8 +32,10 @@ public: virtual bool HasRenderDevice() const override; virtual bool HasRenderSurface() const override; - virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device, bool threaded_presentation) override; - virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device, bool threaded_presentation) override; + virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device, + bool threaded_presentation) override; + virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device, + bool threaded_presentation) override; virtual void DestroyRenderDevice() override; virtual bool MakeRenderContextCurrent() override; @@ -65,6 +67,8 @@ public: virtual void SetVSync(bool enabled) override; virtual bool Render() override; + virtual bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) override; protected: const char* GetGLSLVersionString() const; @@ -99,7 +103,8 @@ protected: bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height); void ApplyPostProcessingChain(GLuint final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x, - s32 texture_view_y, s32 texture_view_width, s32 texture_view_height); + s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, u32 target_width, + u32 target_height); std::unique_ptr m_gl_context; diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index a617f2f5b..8d9529977 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -640,14 +640,107 @@ bool VulkanHostDisplay::Render() return true; } -void VulkanHostDisplay::BeginSwapChainRenderPass(VkFramebuffer framebuffer) +bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) +{ + // in theory we could do this without a swap chain, but postprocessing assumes it for now... + if (!m_swap_chain) + return false; + + const VkFormat format = m_swap_chain ? m_swap_chain->GetTextureFormat() : VK_FORMAT_R8G8B8A8_UNORM; + switch (format) + { + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SRGB: + *out_format = HostDisplayPixelFormat::RGBA8; + *out_stride = sizeof(u32) * width; + out_pixels->resize(width * height); + break; + + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SRGB: + *out_format = HostDisplayPixelFormat::BGRA8; + *out_stride = sizeof(u32) * width; + out_pixels->resize(width * height); + break; + + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: + *out_format = HostDisplayPixelFormat::RGBA5551; + *out_stride = sizeof(u16) * width; + out_pixels->resize(((width * height) + 1) / 2); + break; + + case VK_FORMAT_R5G6B5_UNORM_PACK16: + *out_format = HostDisplayPixelFormat::RGB565; + *out_stride = sizeof(u16) * width; + out_pixels->resize(((width * height) + 1) / 2); + break; + + default: + Log_ErrorPrintf("Unhandled swap chain pixel format %u", static_cast(format)); + break; + } + + Vulkan::Texture tex; + Vulkan::StagingTexture staging_tex; + if (!tex.Create(width, height, 1, 1, format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) || + !staging_tex.Create(Vulkan::StagingBuffer::Type::Readback, format, width, height)) + { + return false; + } + + const VkRenderPass rp = + m_swap_chain ? + m_swap_chain->GetClearRenderPass() : + g_vulkan_context->GetRenderPass(format, VK_FORMAT_UNDEFINED, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_CLEAR); + if (!rp) + return false; + + const VkFramebuffer fb = tex.CreateFramebuffer(rp); + if (!fb) + return false; + + tex.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + const auto [left, top, draw_width, draw_height] = + CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin); + + if (!m_post_processing_chain.IsEmpty()) + { + ApplyPostProcessingChain(fb, left, top, draw_width, draw_height, m_display_texture_handle, m_display_texture_width, + m_display_texture_height, 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, m_display_texture_handle, m_display_texture_width, + m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y, + m_display_texture_view_width, m_display_texture_view_height, m_display_linear_filtering); + } + + vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer()); + tex.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + staging_tex.CopyFromTexture(tex, 0, 0, 0, 0, 0, 0, width, height); + staging_tex.ReadTexels(0, 0, width, height, out_pixels->data(), *out_stride); + + // destroying these immediately should be safe since nothing's going to access them, and it's not part of the command + // stream + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), fb, nullptr); + staging_tex.Destroy(false); + tex.Destroy(false); + return true; +} + +void VulkanHostDisplay::BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height) { const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, m_swap_chain->GetClearRenderPass(), framebuffer, - {{0, 0}, {m_swap_chain->GetWidth(), m_swap_chain->GetHeight()}}, + {{0, 0}, {width, height}}, 1u, &clear_value}; vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE); @@ -657,7 +750,8 @@ void VulkanHostDisplay::RenderDisplay() { if (!HasDisplayTexture()) { - BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer()); + BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer(), m_swap_chain->GetWidth(), + m_swap_chain->GetHeight()); return; } @@ -665,13 +759,14 @@ void VulkanHostDisplay::RenderDisplay() if (!m_post_processing_chain.IsEmpty()) { - ApplyPostProcessingChain(left, top, width, height, m_display_texture_handle, m_display_texture_width, - m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y, - m_display_texture_view_width, m_display_texture_view_height); + ApplyPostProcessingChain(m_swap_chain->GetCurrentFramebuffer(), left, top, width, height, m_display_texture_handle, + m_display_texture_width, m_display_texture_height, m_display_texture_view_x, + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + m_swap_chain->GetWidth(), m_swap_chain->GetHeight()); return; } - BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer()); + BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer(), m_swap_chain->GetWidth(), m_swap_chain->GetHeight()); RenderDisplay(left, top, width, height, m_display_texture_handle, m_display_texture_width, m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, m_display_linear_filtering); @@ -946,14 +1041,15 @@ bool VulkanHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t return true; } -void VulkanHostDisplay::ApplyPostProcessingChain(s32 final_left, s32 final_top, s32 final_width, s32 final_height, - void* texture_handle, u32 texture_width, s32 texture_height, - s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, - s32 texture_view_height) +void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top, + s32 final_width, s32 final_height, void* texture_handle, + u32 texture_width, s32 texture_height, s32 texture_view_x, + s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, + u32 target_width, u32 target_height) { - if (!CheckPostProcessingRenderTargets(m_swap_chain->GetWidth(), m_swap_chain->GetHeight())) + if (!CheckPostProcessingRenderTargets(target_width, target_height)) { - BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer()); + BeginSwapChainRenderPass(target_fb, target_width, target_height); RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering); return; @@ -962,7 +1058,7 @@ void VulkanHostDisplay::ApplyPostProcessingChain(s32 final_left, s32 final_top, // downsample/upsample - use same viewport for remainder VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); m_post_processing_input_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - BeginSwapChainRenderPass(m_post_processing_input_framebuffer); + BeginSwapChainRenderPass(m_post_processing_input_framebuffer, target_width, target_height); RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering); vkCmdEndRenderPass(cmdbuffer); @@ -983,11 +1079,11 @@ void VulkanHostDisplay::ApplyPostProcessingChain(s32 final_left, s32 final_top, if (i != final_stage) { pps.output_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - BeginSwapChainRenderPass(pps.output_framebuffer); + BeginSwapChainRenderPass(pps.output_framebuffer, target_width, target_height); } else { - BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer()); + BeginSwapChainRenderPass(target_fb, target_width, target_height); } const bool use_push_constants = m_post_processing_chain.GetShaderStage(i).UsePushConstants(); diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h index 924fe8a3b..c394bf05e 100644 --- a/src/frontend-common/vulkan_host_display.h +++ b/src/frontend-common/vulkan_host_display.h @@ -64,6 +64,8 @@ public: virtual void SetVSync(bool enabled) override; virtual bool Render() override; + virtual bool RenderScreenshot(u32 width, u32 height, std::vector* out_pixels, u32* out_stride, + HostDisplayPixelFormat* out_format) override; static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi); @@ -89,9 +91,10 @@ protected: }; bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height); - void ApplyPostProcessingChain(s32 final_left, s32 final_top, s32 final_width, s32 final_height, void* texture_handle, - u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y, - s32 texture_view_width, s32 texture_view_height); + void ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top, s32 final_width, + s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height, + s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, + u32 target_width, u32 target_height); // Can be overridden by frontends. virtual VkRenderPass GetRenderPassForDisplay() const; @@ -103,7 +106,7 @@ protected: virtual void DestroyImGuiContext() override; virtual bool UpdateImGuiFontTexture() override; - void BeginSwapChainRenderPass(VkFramebuffer framebuffer); + void BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height); void RenderDisplay(); void RenderImGui(); void RenderSoftwareCursor();