From 804cd0ff03ad6b940f6cd415b29a60263c8c9042 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 20 Nov 2016 00:43:50 +1000 Subject: [PATCH] Vulkan: Move XFB encoding/decoding to TextureConverter --- .../Vulkan/FramebufferManager.cpp | 7 +- .../VideoBackends/Vulkan/TextureCache.cpp | 170 +---------------- .../Core/VideoBackends/Vulkan/TextureCache.h | 12 +- .../VideoBackends/Vulkan/TextureConverter.cpp | 174 ++++++++++++++++++ .../VideoBackends/Vulkan/TextureConverter.h | 18 +- 5 files changed, 196 insertions(+), 185 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 1f497416be..a707d8ad0e 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -18,6 +18,7 @@ #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" +#include "VideoBackends/Vulkan/TextureConverter.h" #include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -1411,7 +1412,7 @@ void FramebufferManager::CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_heigh // The destination stride can differ from the copy region width, in which case the pixels // outside the copy region should not be written to. - TextureCache::GetInstance()->EncodeYUYVTextureToMemory( + TextureCache::GetInstance()->GetTextureConverter()->EncodeTextureToMemoryYUYV( xfb_ptr, static_cast(source_rc.GetWidth()), fb_stride, fb_height, src_texture, scaled_rc); @@ -1437,8 +1438,8 @@ void XFBSource::DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) // Guest memory -> GPU EFB Textures const u8* src_ptr = Memory::GetPointer(xfb_addr); _assert_(src_ptr); - TextureCache::GetInstance()->DecodeYUYVTextureFromMemory(m_texture.get(), src_ptr, fb_width, - fb_width * 2, fb_height); + TextureCache::GetInstance()->GetTextureConverter()->DecodeYUYVTextureFromMemory( + m_texture.get(), src_ptr, fb_width, fb_width * 2, fb_height); } void XFBSource::CopyEFB(float gamma) diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 801a967630..b13e2fe4ec 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -730,52 +730,6 @@ bool TextureCache::CompileShaders() } )"; - static const char RGB_TO_YUYV_SHADER_SOURCE[] = R"( - SAMPLER_BINDING(0) uniform sampler2DArray source; - layout(location = 0) in vec3 uv0; - layout(location = 0) out vec4 ocol0; - - const vec3 y_const = vec3(0.257,0.504,0.098); - const vec3 u_const = vec3(-0.148,-0.291,0.439); - const vec3 v_const = vec3(0.439,-0.368,-0.071); - const vec4 const3 = vec4(0.0625,0.5,0.0625,0.5); - - void main() - { - vec3 c0 = texture(source, vec3(uv0.xy - dFdx(uv0.xy) * 0.25, 0.0)).rgb; - vec3 c1 = texture(source, vec3(uv0.xy + dFdx(uv0.xy) * 0.25, 0.0)).rgb; - vec3 c01 = (c0 + c1) * 0.5; - ocol0 = vec4(dot(c1, y_const), - dot(c01,u_const), - dot(c0,y_const), - dot(c01, v_const)) + const3; - } - )"; - - static const char YUYV_TO_RGB_SHADER_SOURCE[] = R"( - SAMPLER_BINDING(0) uniform sampler2D source; - layout(location = 0) in vec3 uv0; - layout(location = 0) out vec4 ocol0; - - void main() - { - ivec2 uv = ivec2(gl_FragCoord.xy); - vec4 c0 = texelFetch(source, ivec2(uv.x / 2, uv.y), 0); - - // The texture used to stage the upload is in BGRA order. - c0 = c0.zyxw; - - float y = mix(c0.r, c0.b, (uv.x & 1) == 1); - float yComp = 1.164 * (y - 0.0625); - float uComp = c0.g - 0.5; - float vComp = c0.a - 0.5; - ocol0 = vec4(yComp + (1.596 * vComp), - yComp - (0.813 * vComp) - (0.391 * uComp), - yComp + (2.018 * uComp), - 1.0); - } - )"; - std::string header = g_object_cache->GetUtilityShaderHeader(); std::string source; @@ -791,14 +745,8 @@ bool TextureCache::CompileShaders() source = header + EFB_DEPTH_TO_TEX_SOURCE; m_efb_depth_to_tex_shader = Util::CompileAndCreateFragmentShader(source); - source = header + RGB_TO_YUYV_SHADER_SOURCE; - m_rgb_to_yuyv_shader = Util::CompileAndCreateFragmentShader(source); - source = header + YUYV_TO_RGB_SHADER_SOURCE; - m_yuyv_to_rgb_shader = Util::CompileAndCreateFragmentShader(source); - - return (m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE && - m_efb_depth_to_tex_shader != VK_NULL_HANDLE && m_rgb_to_yuyv_shader != VK_NULL_HANDLE && - m_yuyv_to_rgb_shader != VK_NULL_HANDLE); + return m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE && + m_efb_depth_to_tex_shader != VK_NULL_HANDLE; } void TextureCache::DeleteShaders() @@ -822,120 +770,6 @@ void TextureCache::DeleteShaders() vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr); m_efb_depth_to_tex_shader = VK_NULL_HANDLE; } - if (m_rgb_to_yuyv_shader != VK_NULL_HANDLE) - { - vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_rgb_to_yuyv_shader, nullptr); - m_rgb_to_yuyv_shader = VK_NULL_HANDLE; - } - if (m_yuyv_to_rgb_shader != VK_NULL_HANDLE) - { - vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_yuyv_to_rgb_shader, nullptr); - m_yuyv_to_rgb_shader = VK_NULL_HANDLE; - } -} - -void TextureCache::EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 dst_stride, - u32 dst_height, Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) -{ - StateTracker::GetInstance()->EndRenderPass(); - - VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - // Borrow framebuffer from EFB2RAM encoder. - Texture2D* encoding_texture = m_texture_converter->GetEncodingTexture(); - StagingTexture2D* download_texture = m_texture_converter->GetDownloadTexture(); - encoding_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Use fragment shader to convert RGBA to YUYV. - // Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in - // the order the guest is expecting and we don't have to swap it at readback time. The width - // is halved because we're using an RGBA8 texture, but the YUYV data is two bytes per pixel. - u32 output_width = dst_width / 2; - UtilityShaderDraw draw( - command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), - m_texture_converter->GetEncodingRenderPass(), g_object_cache->GetPassthroughVertexShader(), - VK_NULL_HANDLE, m_rgb_to_yuyv_shader); - VkRect2D region = {{0, 0}, {output_width, dst_height}}; - draw.BeginRenderPass(m_texture_converter->GetEncodingTextureFramebuffer(), region); - draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); - draw.DrawQuad(0, 0, static_cast(output_width), static_cast(dst_height), src_rect.left, - src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), - static_cast(src_texture->GetWidth()), - static_cast(src_texture->GetHeight())); - draw.EndRenderPass(); - - // Render pass transitions to TRANSFER_SRC. - encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - - // Copy from encoding texture to download buffer. - download_texture->CopyFromImage(command_buffer, encoding_texture->GetImage(), - VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width, dst_height, 0, 0); - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Finally, copy to guest memory. This may have a different stride. - download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride); -} - -void TextureCache::DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const void* src_ptr, - u32 src_width, u32 src_stride, u32 src_height) -{ - // Copies (and our decoding step) cannot be done inside a render pass. - StateTracker::GetInstance()->EndRenderPass(); - - // We share the upload buffer with normal textures here, since the XFB buffers aren't very large. - u32 upload_size = src_stride * src_height; - if (!m_texture_upload_buffer->ReserveMemory(upload_size, - g_vulkan_context->GetBufferImageGranularity())) - { - // Execute the command buffer first. - WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); - if (!m_texture_upload_buffer->ReserveMemory(upload_size, - g_vulkan_context->GetBufferImageGranularity())) - PanicAlert("Failed to allocate space in texture upload buffer"); - } - - // Assume that each source row is not padded. - _assert_(src_stride == (src_width * sizeof(u16))); - VkDeviceSize image_upload_buffer_offset = m_texture_upload_buffer->GetCurrentOffset(); - std::memcpy(m_texture_upload_buffer->GetCurrentHostPointer(), src_ptr, upload_size); - m_texture_upload_buffer->CommitMemory(upload_size); - - // Copy from the upload buffer to the intermediate texture. We borrow this from the encoder. - // The width is specified as half here because we have two pixels packed in each RGBA texel. - // In the future this could be skipped by reading the upload buffer as a uniform texel buffer. - VkBufferImageCopy image_copy = { - image_upload_buffer_offset, // VkDeviceSize bufferOffset - 0, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers imageSubresource - {0, 0, 0}, // VkOffset3D imageOffset - {src_width / 2, src_height, 1} // VkExtent3D imageExtent - }; - VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - Texture2D* intermediate_texture = m_texture_converter->GetEncodingTexture(); - intermediate_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdCopyBufferToImage(command_buffer, m_texture_upload_buffer->GetBuffer(), - intermediate_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &image_copy); - intermediate_texture->TransitionToLayout(command_buffer, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - dst_texture->GetTexture()->TransitionToLayout(command_buffer, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Convert from the YUYV data now in the intermediate texture to RGBA in the destination. - UtilityShaderDraw draw( - command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), - m_texture_converter->GetEncodingRenderPass(), g_object_cache->GetScreenQuadVertexShader(), - VK_NULL_HANDLE, m_yuyv_to_rgb_shader); - VkRect2D region = {{0, 0}, {src_width, src_height}}; - draw.BeginRenderPass(dst_texture->GetFramebuffer(), region); - draw.SetViewportAndScissor(0, 0, static_cast(src_width), static_cast(src_height)); - draw.SetPSSampler(0, intermediate_texture->GetView(), g_object_cache->GetPointSampler()); - draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); - draw.EndRenderPass(); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index 74cd52f182..327a9bcaf3 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -48,6 +48,8 @@ public: static TextureCache* GetInstance(); + StreamBuffer* GetUploadBuffer() const { return m_texture_upload_buffer.get(); } + TextureConverter* GetTextureConverter() const { return m_texture_converter.get(); } bool Initialize(); bool CompileShaders() override; @@ -65,14 +67,6 @@ public: void CopyRectangleFromTexture(TCacheEntry* dst_texture, const MathUtil::Rectangle& dst_rect, Texture2D* src_texture, const MathUtil::Rectangle& src_rect); - // Encodes texture to guest memory in XFB (YUYV) format. - void EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 dst_stride, u32 dst_height, - Texture2D* src_texture, const MathUtil::Rectangle& src_rect); - - // Decodes data from guest memory in XFB (YUYV) format to a RGBA format texture on the GPU. - void DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const void* src_ptr, u32 src_width, - u32 src_stride, u32 src_height); - private: bool CreateRenderPasses(); VkRenderPass GetRenderPassForTextureUpdate(const Texture2D* texture) const; @@ -95,8 +89,6 @@ private: VkShaderModule m_copy_shader = VK_NULL_HANDLE; VkShaderModule m_efb_color_to_tex_shader = VK_NULL_HANDLE; VkShaderModule m_efb_depth_to_tex_shader = VK_NULL_HANDLE; - VkShaderModule m_rgb_to_yuyv_shader = VK_NULL_HANDLE; - VkShaderModule m_yuyv_to_rgb_shader = VK_NULL_HANDLE; }; } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index 690c0decf2..1a701611ea 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -56,6 +56,11 @@ TextureConverter::~TextureConverter() if (shader != VK_NULL_HANDLE) vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr); } + + if (m_rgb_to_yuyv_shader != VK_NULL_HANDLE) + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_rgb_to_yuyv_shader, nullptr); + if (m_yuyv_to_rgb_shader != VK_NULL_HANDLE) + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_yuyv_to_rgb_shader, nullptr); } bool TextureConverter::Initialize() @@ -96,6 +101,12 @@ bool TextureConverter::Initialize() return false; } + if (!CompileYUYVConversionShaders()) + { + PanicAlert("Failed to compile YUYV conversion shaders"); + return false; + } + return true; } @@ -228,6 +239,112 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p memory_stride); } +void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride, + u32 dst_height, Texture2D* src_texture, + const MathUtil::Rectangle& src_rect) +{ + StateTracker::GetInstance()->EndRenderPass(); + + // Borrow framebuffer from EFB2RAM encoder. + VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); + src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_encoding_render_texture->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Use fragment shader to convert RGBA to YUYV. + // Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in + // the order the guest is expecting and we don't have to swap it at readback time. The width + // is halved because we're using an RGBA8 texture, but the YUYV data is two bytes per pixel. + u32 output_width = dst_width / 2; + UtilityShaderDraw draw(command_buffer, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_encoding_render_pass, g_object_cache->GetPassthroughVertexShader(), + VK_NULL_HANDLE, m_rgb_to_yuyv_shader); + VkRect2D region = {{0, 0}, {output_width, dst_height}}; + draw.BeginRenderPass(m_encoding_render_framebuffer, region); + draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); + draw.DrawQuad(0, 0, static_cast(output_width), static_cast(dst_height), src_rect.left, + src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), + static_cast(src_texture->GetWidth()), + static_cast(src_texture->GetHeight())); + draw.EndRenderPass(); + + // Render pass transitions to TRANSFER_SRC. + m_encoding_render_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // Copy from encoding texture to download buffer. + m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(), + VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width, + dst_height, 0, 0); + Util::ExecuteCurrentCommandsAndRestoreState(false, true); + + // Finally, copy to guest memory. This may have a different stride. + m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride); +} + +void TextureConverter::DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* dst_texture, + const void* src_ptr, u32 src_width, + u32 src_stride, u32 src_height) +{ + // Copies (and our decoding step) cannot be done inside a render pass. + StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->SetPendingRebind(); + + // We share the upload buffer with normal textures here, since the XFB buffers aren't very large. + u32 upload_size = src_stride * src_height; + StreamBuffer* texture_upload_buffer = TextureCache::GetInstance()->GetUploadBuffer(); + if (!texture_upload_buffer->ReserveMemory(upload_size, + g_vulkan_context->GetBufferImageGranularity())) + { + // Execute the command buffer first. + WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); + Util::ExecuteCurrentCommandsAndRestoreState(false); + if (!texture_upload_buffer->ReserveMemory(upload_size, + g_vulkan_context->GetBufferImageGranularity())) + PanicAlert("Failed to allocate space in texture upload buffer"); + } + + // Assume that each source row is not padded. + _assert_(src_stride == (src_width * sizeof(u16))); + VkDeviceSize image_upload_buffer_offset = texture_upload_buffer->GetCurrentOffset(); + std::memcpy(texture_upload_buffer->GetCurrentHostPointer(), src_ptr, upload_size); + texture_upload_buffer->CommitMemory(upload_size); + + // Copy from the upload buffer to the intermediate texture. We borrow this from the encoder. + // The width is specified as half here because we have two pixels packed in each RGBA texel. + // In the future this could be skipped by reading the upload buffer as a uniform texel buffer. + VkBufferImageCopy image_copy = { + image_upload_buffer_offset, // VkDeviceSize bufferOffset + 0, // uint32_t bufferRowLength + 0, // uint32_t bufferImageHeight + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers imageSubresource + {0, 0, 0}, // VkOffset3D imageOffset + {src_width / 2, src_height, 1} // VkExtent3D imageExtent + }; + VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); + m_encoding_render_texture->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdCopyBufferToImage(command_buffer, texture_upload_buffer->GetBuffer(), + m_encoding_render_texture->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + m_encoding_render_texture->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + dst_texture->GetTexture()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Convert from the YUYV data now in the intermediate texture to RGBA in the destination. + UtilityShaderDraw draw(command_buffer, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(), + VK_NULL_HANDLE, m_yuyv_to_rgb_shader); + VkRect2D region = {{0, 0}, {src_width, src_height}}; + draw.BeginRenderPass(dst_texture->GetFramebuffer(), region); + draw.SetViewportAndScissor(0, 0, static_cast(src_width), static_cast(src_height)); + draw.SetPSSampler(0, m_encoding_render_texture->GetView(), g_object_cache->GetPointSampler()); + draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); + draw.EndRenderPass(); +} + bool TextureConverter::CreateTexelBuffer() { // Prefer an 8MB buffer if possible, but use less if the device doesn't support this. @@ -472,4 +589,61 @@ bool TextureConverter::CreateEncodingDownloadTexture() return m_encoding_download_texture && m_encoding_download_texture->Map(); } +bool TextureConverter::CompileYUYVConversionShaders() +{ + static const char RGB_TO_YUYV_SHADER_SOURCE[] = R"( + SAMPLER_BINDING(0) uniform sampler2DArray source; + layout(location = 0) in vec3 uv0; + layout(location = 0) out vec4 ocol0; + + const vec3 y_const = vec3(0.257,0.504,0.098); + const vec3 u_const = vec3(-0.148,-0.291,0.439); + const vec3 v_const = vec3(0.439,-0.368,-0.071); + const vec4 const3 = vec4(0.0625,0.5,0.0625,0.5); + + void main() + { + vec3 c0 = texture(source, vec3(uv0.xy - dFdx(uv0.xy) * 0.25, 0.0)).rgb; + vec3 c1 = texture(source, vec3(uv0.xy + dFdx(uv0.xy) * 0.25, 0.0)).rgb; + vec3 c01 = (c0 + c1) * 0.5; + ocol0 = vec4(dot(c1, y_const), + dot(c01,u_const), + dot(c0,y_const), + dot(c01, v_const)) + const3; + } + )"; + + static const char YUYV_TO_RGB_SHADER_SOURCE[] = R"( + SAMPLER_BINDING(0) uniform sampler2D source; + layout(location = 0) in vec3 uv0; + layout(location = 0) out vec4 ocol0; + + void main() + { + ivec2 uv = ivec2(gl_FragCoord.xy); + vec4 c0 = texelFetch(source, ivec2(uv.x / 2, uv.y), 0); + + // The texture used to stage the upload is in BGRA order. + c0 = c0.zyxw; + + float y = mix(c0.r, c0.b, (uv.x & 1) == 1); + float yComp = 1.164 * (y - 0.0625); + float uComp = c0.g - 0.5; + float vComp = c0.a - 0.5; + ocol0 = vec4(yComp + (1.596 * vComp), + yComp - (0.813 * vComp) - (0.391 * uComp), + yComp + (2.018 * uComp), + 1.0); + } + )"; + + std::string header = g_object_cache->GetUtilityShaderHeader(); + std::string source = header + RGB_TO_YUYV_SHADER_SOURCE; + m_rgb_to_yuyv_shader = Util::CompileAndCreateFragmentShader(source); + source = header + YUYV_TO_RGB_SHADER_SOURCE; + m_yuyv_to_rgb_shader = Util::CompileAndCreateFragmentShader(source); + + return m_rgb_to_yuyv_shader != VK_NULL_HANDLE && m_yuyv_to_rgb_shader != VK_NULL_HANDLE; +} + } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h index 27dd2a7e33..4df1597470 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -25,10 +25,6 @@ public: TextureConverter(); ~TextureConverter(); - VkRenderPass GetEncodingRenderPass() const { return m_encoding_render_pass; } - Texture2D* GetEncodingTexture() const { return m_encoding_render_texture.get(); } - VkFramebuffer GetEncodingTextureFramebuffer() const { return m_encoding_render_framebuffer; } - StagingTexture2D* GetDownloadTexture() const { return m_encoding_download_texture.get(); } bool Initialize(); // Applies palette to dst_entry, using indices from src_entry. @@ -42,6 +38,14 @@ public: PEControl::PixelFormat src_format, bool is_intensity, int scale_by_half, const EFBRectangle& source); + // Encodes texture to guest memory in XFB (YUYV) format. + void EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride, u32 dst_height, + Texture2D* src_texture, const MathUtil::Rectangle& src_rect); + + // Decodes data from guest memory in XFB (YUYV) format to a RGBA format texture on the GPU. + void DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* dst_texture, const void* src_ptr, + u32 src_width, u32 src_stride, u32 src_height); + private: static const u32 NUM_TEXTURE_ENCODING_SHADERS = 64; static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4; @@ -59,6 +63,8 @@ private: bool CreateEncodingTexture(); bool CreateEncodingDownloadTexture(); + bool CompileYUYVConversionShaders(); + // Shared between conversion types std::unique_ptr m_texel_buffer; VkBufferView m_texel_buffer_view_r16_uint = VK_NULL_HANDLE; @@ -73,6 +79,10 @@ private: std::unique_ptr m_encoding_render_texture; VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE; std::unique_ptr m_encoding_download_texture; + + // XFB encoding/decoding shaders + VkShaderModule m_rgb_to_yuyv_shader = VK_NULL_HANDLE; + VkShaderModule m_yuyv_to_rgb_shader = VK_NULL_HANDLE; }; } // namespace Vulkan