mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
HostDisplay: Add method to render screenshots at window size
This commit is contained in:
parent
757bef7b6d
commit
2aea58d056
|
@ -613,3 +613,40 @@ bool HostDisplay::WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resi
|
||||||
|
|
||||||
return true;
|
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<u32> 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;
|
||||||
|
}
|
||||||
|
|
|
@ -115,6 +115,10 @@ public:
|
||||||
/// Returns false if the window was completely occluded.
|
/// Returns false if the window was completely occluded.
|
||||||
virtual bool Render() = 0;
|
virtual bool Render() = 0;
|
||||||
|
|
||||||
|
/// Renders the display with postprocessing to the specified image.
|
||||||
|
virtual bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
|
||||||
|
HostDisplayPixelFormat* out_format) = 0;
|
||||||
|
|
||||||
virtual void SetVSync(bool enabled) = 0;
|
virtual void SetVSync(bool enabled) = 0;
|
||||||
|
|
||||||
#ifdef WITH_IMGUI
|
#ifdef WITH_IMGUI
|
||||||
|
@ -232,6 +236,9 @@ public:
|
||||||
bool WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resize_width = 0, u32 resize_height = 0,
|
bool WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resize_width = 0, u32 resize_height = 0,
|
||||||
bool clear_alpha = true);
|
bool clear_alpha = true);
|
||||||
|
|
||||||
|
/// Helper function to save screenshot to PNG.
|
||||||
|
bool WriteScreenshotToFile(std::string filename, bool compress_on_thread = false);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
|
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
|
||||||
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture_handle != nullptr); }
|
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture_handle != nullptr); }
|
||||||
|
|
|
@ -709,6 +709,59 @@ bool D3D11HostDisplay::Render()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* 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<float, 4> 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<u32>(0, 0, width, height, stride, out_pixels->data());
|
||||||
|
m_readback_staging_texture.Unmap(m_context.Get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void D3D11HostDisplay::RenderImGui()
|
void D3D11HostDisplay::RenderImGui()
|
||||||
{
|
{
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
@ -726,7 +779,8 @@ void D3D11HostDisplay::RenderDisplay()
|
||||||
{
|
{
|
||||||
ApplyPostProcessingChain(m_swap_chain_rtv.Get(), left, top, width, height, m_display_texture_handle,
|
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_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;
|
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,
|
void D3D11HostDisplay::ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top,
|
||||||
s32 final_width, s32 final_height, void* texture_handle,
|
s32 final_width, s32 final_height, void* texture_handle,
|
||||||
u32 texture_width, s32 texture_height, s32 texture_view_x,
|
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<float, 4> clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
|
static constexpr std::array<float, 4> 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,
|
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);
|
texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering);
|
||||||
|
|
|
@ -68,6 +68,8 @@ public:
|
||||||
virtual void SetVSync(bool enabled) override;
|
virtual void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
virtual bool Render() override;
|
virtual bool Render() override;
|
||||||
|
virtual bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
|
||||||
|
HostDisplayPixelFormat* out_format) override;
|
||||||
|
|
||||||
static AdapterAndModeList StaticGetAdapterAndModeList();
|
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ protected:
|
||||||
void ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top, s32 final_width,
|
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 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_x, s32 texture_view_y, s32 texture_view_width,
|
||||||
s32 texture_view_height);
|
s32 texture_view_height, u32 target_width, u32 target_height);
|
||||||
|
|
||||||
ComPtr<ID3D11Device> m_device;
|
ComPtr<ID3D11Device> m_device;
|
||||||
ComPtr<ID3D11DeviceContext> m_context;
|
ComPtr<ID3D11DeviceContext> m_context;
|
||||||
|
|
|
@ -749,7 +749,6 @@ bool OpenGLHostDisplay::Render()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
glDisable(GL_SCISSOR_TEST);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
@ -765,6 +764,46 @@ bool OpenGLHostDisplay::Render()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* 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()
|
void OpenGLHostDisplay::RenderImGui()
|
||||||
{
|
{
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
@ -783,7 +822,8 @@ void OpenGLHostDisplay::RenderDisplay()
|
||||||
{
|
{
|
||||||
ApplyPostProcessingChain(0, left, GetWindowHeight() - top - height, width, height, m_display_texture_handle,
|
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_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;
|
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,
|
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 final_height, void* texture_handle, u32 texture_width,
|
||||||
s32 texture_height, s32 texture_view_x, s32 texture_view_y,
|
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_width, texture_height, texture_view_x, texture_view_y, texture_view_width,
|
||||||
texture_view_height, m_display_linear_filtering);
|
texture_view_height, m_display_linear_filtering);
|
||||||
return;
|
return;
|
||||||
|
@ -1008,7 +1049,7 @@ void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_
|
||||||
// downsample/upsample - use same viewport for remainder
|
// downsample/upsample - use same viewport for remainder
|
||||||
m_post_processing_input_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
m_post_processing_input_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
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,
|
texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height,
|
||||||
m_display_linear_filtering);
|
m_display_linear_filtering);
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,10 @@ public:
|
||||||
virtual bool HasRenderDevice() const override;
|
virtual bool HasRenderDevice() const override;
|
||||||
virtual bool HasRenderSurface() 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 CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device,
|
||||||
virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device, bool threaded_presentation) override;
|
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 void DestroyRenderDevice() override;
|
||||||
|
|
||||||
virtual bool MakeRenderContextCurrent() override;
|
virtual bool MakeRenderContextCurrent() override;
|
||||||
|
@ -65,6 +67,8 @@ public:
|
||||||
virtual void SetVSync(bool enabled) override;
|
virtual void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
virtual bool Render() override;
|
virtual bool Render() override;
|
||||||
|
virtual bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
|
||||||
|
HostDisplayPixelFormat* out_format) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char* GetGLSLVersionString() const;
|
const char* GetGLSLVersionString() const;
|
||||||
|
@ -99,7 +103,8 @@ protected:
|
||||||
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
|
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 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,
|
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<GL::Context> m_gl_context;
|
std::unique_ptr<GL::Context> m_gl_context;
|
||||||
|
|
||||||
|
|
|
@ -640,14 +640,107 @@ bool VulkanHostDisplay::Render()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanHostDisplay::BeginSwapChainRenderPass(VkFramebuffer framebuffer)
|
bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* 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<unsigned>(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 VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||||
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
nullptr,
|
nullptr,
|
||||||
m_swap_chain->GetClearRenderPass(),
|
m_swap_chain->GetClearRenderPass(),
|
||||||
framebuffer,
|
framebuffer,
|
||||||
{{0, 0}, {m_swap_chain->GetWidth(), m_swap_chain->GetHeight()}},
|
{{0, 0}, {width, height}},
|
||||||
1u,
|
1u,
|
||||||
&clear_value};
|
&clear_value};
|
||||||
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
@ -657,7 +750,8 @@ void VulkanHostDisplay::RenderDisplay()
|
||||||
{
|
{
|
||||||
if (!HasDisplayTexture())
|
if (!HasDisplayTexture())
|
||||||
{
|
{
|
||||||
BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer());
|
BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer(), m_swap_chain->GetWidth(),
|
||||||
|
m_swap_chain->GetHeight());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,13 +759,14 @@ void VulkanHostDisplay::RenderDisplay()
|
||||||
|
|
||||||
if (!m_post_processing_chain.IsEmpty())
|
if (!m_post_processing_chain.IsEmpty())
|
||||||
{
|
{
|
||||||
ApplyPostProcessingChain(left, top, width, height, m_display_texture_handle, m_display_texture_width,
|
ApplyPostProcessingChain(m_swap_chain->GetCurrentFramebuffer(), left, top, width, height, m_display_texture_handle,
|
||||||
m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
|
m_display_texture_width, m_display_texture_height, m_display_texture_view_x,
|
||||||
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,
|
||||||
|
m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
|
||||||
return;
|
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,
|
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_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||||
m_display_texture_view_height, m_display_linear_filtering);
|
m_display_texture_view_height, m_display_linear_filtering);
|
||||||
|
@ -946,14 +1041,15 @@ bool VulkanHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanHostDisplay::ApplyPostProcessingChain(s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top,
|
||||||
void* texture_handle, u32 texture_width, s32 texture_height,
|
s32 final_width, s32 final_height, void* texture_handle,
|
||||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
|
u32 texture_width, s32 texture_height, s32 texture_view_x,
|
||||||
s32 texture_view_height)
|
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,
|
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);
|
texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering);
|
||||||
return;
|
return;
|
||||||
|
@ -962,7 +1058,7 @@ void VulkanHostDisplay::ApplyPostProcessingChain(s32 final_left, s32 final_top,
|
||||||
// downsample/upsample - use same viewport for remainder
|
// downsample/upsample - use same viewport for remainder
|
||||||
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||||
m_post_processing_input_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
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,
|
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);
|
texture_view_x, texture_view_y, texture_view_width, texture_view_height, m_display_linear_filtering);
|
||||||
vkCmdEndRenderPass(cmdbuffer);
|
vkCmdEndRenderPass(cmdbuffer);
|
||||||
|
@ -983,11 +1079,11 @@ void VulkanHostDisplay::ApplyPostProcessingChain(s32 final_left, s32 final_top,
|
||||||
if (i != final_stage)
|
if (i != final_stage)
|
||||||
{
|
{
|
||||||
pps.output_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
pps.output_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
BeginSwapChainRenderPass(pps.output_framebuffer);
|
BeginSwapChainRenderPass(pps.output_framebuffer, target_width, target_height);
|
||||||
}
|
}
|
||||||
else
|
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();
|
const bool use_push_constants = m_post_processing_chain.GetShaderStage(i).UsePushConstants();
|
||||||
|
|
|
@ -64,6 +64,8 @@ public:
|
||||||
virtual void SetVSync(bool enabled) override;
|
virtual void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
virtual bool Render() override;
|
virtual bool Render() override;
|
||||||
|
virtual bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
|
||||||
|
HostDisplayPixelFormat* out_format) override;
|
||||||
|
|
||||||
static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi);
|
static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi);
|
||||||
|
|
||||||
|
@ -89,9 +91,10 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
|
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,
|
void ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top, s32 final_width,
|
||||||
u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y,
|
s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height,
|
||||||
s32 texture_view_width, s32 texture_view_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.
|
// Can be overridden by frontends.
|
||||||
virtual VkRenderPass GetRenderPassForDisplay() const;
|
virtual VkRenderPass GetRenderPassForDisplay() const;
|
||||||
|
@ -103,7 +106,7 @@ protected:
|
||||||
virtual void DestroyImGuiContext() override;
|
virtual void DestroyImGuiContext() override;
|
||||||
virtual bool UpdateImGuiFontTexture() override;
|
virtual bool UpdateImGuiFontTexture() override;
|
||||||
|
|
||||||
void BeginSwapChainRenderPass(VkFramebuffer framebuffer);
|
void BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height);
|
||||||
void RenderDisplay();
|
void RenderDisplay();
|
||||||
void RenderImGui();
|
void RenderImGui();
|
||||||
void RenderSoftwareCursor();
|
void RenderSoftwareCursor();
|
||||||
|
|
Loading…
Reference in a new issue