From cd3481fbc7c82e8a1a44bff7239928a8faabbb42 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 13 Nov 2016 15:38:32 +1000 Subject: [PATCH 01/10] Vulkan: Differentiate between descriptor set layouts and bind points This also moves the pipeline and descriptor set layouts used for texture conversion (texel buffers) to ObjectCache, and shares a binding location with the SSBO set. --- Source/Core/VideoBackends/Vulkan/Constants.h | 22 ++++-- .../Core/VideoBackends/Vulkan/ObjectCache.cpp | 41 ++++++++--- .../Core/VideoBackends/Vulkan/ObjectCache.h | 11 ++- .../Vulkan/PaletteTextureConverter.cpp | 69 +++---------------- .../Vulkan/PaletteTextureConverter.h | 4 -- .../VideoBackends/Vulkan/StateTracker.cpp | 29 ++++---- .../Core/VideoBackends/Vulkan/StateTracker.h | 9 ++- Source/Core/VideoBackends/Vulkan/Util.cpp | 30 ++++---- 8 files changed, 106 insertions(+), 109 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index a135c76180..ed38cf47d1 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -23,13 +23,23 @@ enum STAGING_BUFFER_TYPE STAGING_BUFFER_TYPE_READBACK }; -// Descriptor sets -enum DESCRIPTOR_SET +// Descriptor set layouts +enum DESCRIPTOR_SET_LAYOUT { - DESCRIPTOR_SET_UNIFORM_BUFFERS, - DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS, - DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS, - NUM_DESCRIPTOR_SETS + DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS, + DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS, + DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS, + DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS, + NUM_DESCRIPTOR_SET_LAYOUTS +}; + +// Descriptor set bind points +enum DESCRIPTOR_SET_BIND_POINT +{ + DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, + DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS, + DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER, + NUM_DESCRIPTOR_SET_BIND_POINTS }; // Uniform buffer bindings within the first descriptor set diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index 761402e478..56baa687ef 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -695,15 +695,21 @@ bool ObjectCache::CreateDescriptorSetLayouts() static const VkDescriptorSetLayoutBinding ssbo_set_bindings[] = { {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}}; - static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SETS] = { + static const VkDescriptorSetLayoutBinding texel_buffer_set_bindings[] = { + {0, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + }; + + static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = { {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(ubo_set_bindings)), ubo_set_bindings}, {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(sampler_set_bindings)), sampler_set_bindings}, {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, - static_cast(ArraySize(ssbo_set_bindings)), ssbo_set_bindings}}; + static_cast(ArraySize(ssbo_set_bindings)), ssbo_set_bindings}, + {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, + static_cast(ArraySize(texel_buffer_set_bindings)), texel_buffer_set_bindings}}; - for (size_t i = 0; i < NUM_DESCRIPTOR_SETS; i++) + for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++) { VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i], nullptr, &m_descriptor_set_layouts[i]); @@ -732,12 +738,16 @@ bool ObjectCache::CreatePipelineLayouts() // Descriptor sets for each pipeline layout VkDescriptorSetLayout standard_sets[] = { - m_descriptor_set_layouts[DESCRIPTOR_SET_UNIFORM_BUFFERS], - m_descriptor_set_layouts[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS]}; + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS], + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]}; VkDescriptorSetLayout bbox_sets[] = { - m_descriptor_set_layouts[DESCRIPTOR_SET_UNIFORM_BUFFERS], - m_descriptor_set_layouts[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS], - m_descriptor_set_layouts[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS]}; + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS], + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS], + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]}; + VkDescriptorSetLayout texture_conversion_sets[] = { + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS], + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS], + m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS]}; VkPushConstantRange push_constant_range = { VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE}; @@ -763,13 +773,23 @@ bool ObjectCache::CreatePipelineLayouts() standard_sets, 1, &push_constant_range}; + VkPipelineLayoutCreateInfo texture_conversion_info = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + nullptr, + 0, + static_cast(ArraySize(texture_conversion_sets)), + texture_conversion_sets, + 1, + &push_constant_range}; if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &standard_info, nullptr, &m_standard_pipeline_layout)) != VK_SUCCESS || (res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &bbox_info, nullptr, &m_bbox_pipeline_layout)) != VK_SUCCESS || (res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &push_constant_info, nullptr, - &m_push_constant_pipeline_layout))) + &m_push_constant_pipeline_layout)) != VK_SUCCESS || + (res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &texture_conversion_info, + nullptr, &m_texture_conversion_pipeline_layout)) != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: "); return false; @@ -787,6 +807,9 @@ void ObjectCache::DestroyPipelineLayouts() if (m_push_constant_pipeline_layout != VK_NULL_HANDLE) vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_push_constant_pipeline_layout, nullptr); + if (m_texture_conversion_pipeline_layout != VK_NULL_HANDLE) + vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_texture_conversion_pipeline_layout, + nullptr); } bool ObjectCache::CreateUtilityShaderVertexFormat() diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.h b/Source/Core/VideoBackends/Vulkan/ObjectCache.h index 26593d139d..76ed886ecd 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.h +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.h @@ -70,17 +70,23 @@ public: // - Same as standard, plus a single SSBO accessible from PS // - Push Constant // - Same as standard, plus 128 bytes of push constants, accessible from all stages. + // - Texture Conversion + // - Same as push constant, plus a single texel buffer accessible from PS. // // All three pipeline layouts use the same descriptor set layouts, but the final descriptor set // (SSBO) is only required when using the BBox Enabled pipeline layout. // - VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET set) const + VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT set) const { return m_descriptor_set_layouts[set]; } VkPipelineLayout GetStandardPipelineLayout() const { return m_standard_pipeline_layout; } VkPipelineLayout GetBBoxPipelineLayout() const { return m_bbox_pipeline_layout; } VkPipelineLayout GetPushConstantPipelineLayout() const { return m_push_constant_pipeline_layout; } + VkPipelineLayout GetTextureConversionPipelineLayout() const + { + return m_texture_conversion_pipeline_layout; + } // Shared utility shader resources VertexFormat* GetUtilityShaderVertexFormat() const { @@ -157,11 +163,12 @@ private: void DestroySharedShaders(); void DestroySamplers(); - std::array m_descriptor_set_layouts = {}; + std::array m_descriptor_set_layouts = {}; VkPipelineLayout m_standard_pipeline_layout = VK_NULL_HANDLE; VkPipelineLayout m_bbox_pipeline_layout = VK_NULL_HANDLE; VkPipelineLayout m_push_constant_pipeline_layout = VK_NULL_HANDLE; + VkPipelineLayout m_texture_conversion_pipeline_layout = VK_NULL_HANDLE; std::unique_ptr m_utility_shader_vertex_format; std::unique_ptr m_utility_shader_vertex_buffer; diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp index 3e992d4944..1234f5d5dd 100644 --- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp @@ -40,12 +40,6 @@ PaletteTextureConverter::~PaletteTextureConverter() if (m_palette_buffer_view != VK_NULL_HANDLE) vkDestroyBufferView(g_vulkan_context->GetDevice(), m_palette_buffer_view, nullptr); - - if (m_pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_pipeline_layout, nullptr); - - if (m_palette_set_layout != VK_NULL_HANDLE) - vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), m_palette_set_layout, nullptr); } bool PaletteTextureConverter::Initialize() @@ -56,9 +50,6 @@ bool PaletteTextureConverter::Initialize() if (!CompileShaders()) return false; - if (!CreateDescriptorLayout()) - return false; - return true; } @@ -84,16 +75,18 @@ void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer, // If any of these fail, execute a command buffer, and try again. if (!m_palette_stream_buffer->ReserveMemory(palette_size, g_vulkan_context->GetTexelBufferAlignment()) || - (texel_buffer_descriptor_set = - g_command_buffer_mgr->AllocateDescriptorSet(m_palette_set_layout)) == VK_NULL_HANDLE) + (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == + VK_NULL_HANDLE) { WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer"); Util::ExecuteCurrentCommandsAndRestoreState(false); if (!m_palette_stream_buffer->ReserveMemory(palette_size, g_vulkan_context->GetTexelBufferAlignment()) || - (texel_buffer_descriptor_set = - g_command_buffer_mgr->AllocateDescriptorSet(m_palette_set_layout)) == VK_NULL_HANDLE) + (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == + VK_NULL_HANDLE) { PanicAlert("Failed to allocate space for texture conversion"); return; @@ -120,8 +113,8 @@ void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); // Set up draw - UtilityShaderDraw draw(command_buffer, m_pipeline_layout, render_pass, - g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, + UtilityShaderDraw draw(command_buffer, g_object_cache->GetTextureConversionPipelineLayout(), + render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, m_shaders[format]); VkRect2D region = {{0, 0}, {width, height}}; @@ -139,7 +132,9 @@ void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer, draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetPointSampler()); // We have to bind the texel buffer descriptor set separately. - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1, + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + g_object_cache->GetTextureConversionPipelineLayout(), + DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER, 1, &texel_buffer_descriptor_set, 0, nullptr); // Draw @@ -274,46 +269,4 @@ bool PaletteTextureConverter::CompileShaders() m_shaders[GX_TL_RGB5A3] != VK_NULL_HANDLE); } -bool PaletteTextureConverter::CreateDescriptorLayout() -{ - static const VkDescriptorSetLayoutBinding set_bindings[] = { - {0, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, - }; - static const VkDescriptorSetLayoutCreateInfo set_info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, - static_cast(ArraySize(set_bindings)), set_bindings}; - - VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &set_info, nullptr, - &m_palette_set_layout); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateDescriptorSetLayout failed: "); - return false; - } - - VkDescriptorSetLayout sets[] = {m_palette_set_layout, g_object_cache->GetDescriptorSetLayout( - DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS)}; - - VkPushConstantRange push_constant_range = { - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE}; - - VkPipelineLayoutCreateInfo pipeline_layout_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - nullptr, - 0, - static_cast(ArraySize(sets)), - sets, - 1, - &push_constant_range}; - - res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info, nullptr, - &m_pipeline_layout); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: "); - return false; - } - - return true; -} - } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h index 2540b5affa..668e35eae6 100644 --- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h @@ -34,10 +34,6 @@ private: bool CreateBuffers(); bool CompileShaders(); - bool CreateDescriptorLayout(); - - VkDescriptorSetLayout m_palette_set_layout = VK_NULL_HANDLE; - VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; std::array m_shaders = {}; diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index 081036ed8e..ae45ce857f 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -76,7 +76,7 @@ bool StateTracker::Initialize() // BBox is disabled by default. m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout(); - m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS - 1; + m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; m_bbox_enabled = false; // Initialize all samplers to point by default @@ -545,16 +545,16 @@ void StateTracker::SetBBoxEnable(bool enable) if (enable) { m_pipeline_state.pipeline_layout = g_object_cache->GetBBoxPipelineLayout(); - m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS; + m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS; // The bbox buffer never changes, so we defer descriptor updates until it is enabled. - if (m_descriptor_sets[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS] == VK_NULL_HANDLE) + if (m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE) m_dirty_flags |= DIRTY_FLAG_PS_SSBO; } else { m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout(); - m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS - 1; + m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; } m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING; @@ -731,7 +731,8 @@ bool StateTracker::Bind(bool rebind_all /*= false*/) { vkCmdBindDescriptorSets( command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_state.pipeline_layout, - DESCRIPTOR_SET_UNIFORM_BUFFERS, 1, &m_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS], + DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1, + &m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS], NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data()); } @@ -921,10 +922,10 @@ bool StateTracker::UpdateDescriptorSet() u32 num_writes = 0; if (m_dirty_flags & (DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO) || - m_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS] == VK_NULL_HANDLE) + m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS] == VK_NULL_HANDLE) { VkDescriptorSetLayout layout = - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_UNIFORM_BUFFERS); + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS); VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout); if (set == VK_NULL_HANDLE) return false; @@ -943,15 +944,15 @@ bool StateTracker::UpdateDescriptorSet() nullptr}; } - m_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS] = set; + m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS] = set; m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING; } if (m_dirty_flags & DIRTY_FLAG_PS_SAMPLERS || - m_descriptor_sets[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS] == VK_NULL_HANDLE) + m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] == VK_NULL_HANDLE) { VkDescriptorSetLayout layout = - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS); + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS); VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout); if (set == VK_NULL_HANDLE) return false; @@ -974,16 +975,16 @@ bool StateTracker::UpdateDescriptorSet() } } - m_descriptor_sets[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS] = set; + m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] = set; m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING; } if (m_bbox_enabled && (m_dirty_flags & DIRTY_FLAG_PS_SSBO || - m_descriptor_sets[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS] == VK_NULL_HANDLE)) + m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)) { VkDescriptorSetLayout layout = - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS); + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS); VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout); if (set == VK_NULL_HANDLE) return false; @@ -999,7 +1000,7 @@ bool StateTracker::UpdateDescriptorSet() &m_bindings.ps_ssbo, nullptr}; - m_descriptor_sets[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS] = set; + m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] = set; m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING; } diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h index 126041e9bc..97bcdd2cc0 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.h +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h @@ -132,6 +132,13 @@ private: VkPrimitiveTopology primitive_topology; }; + // Number of descriptor sets for game draws. + enum + { + NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1, + NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1 + }; + enum DITRY_FLAG : u32 { DIRTY_FLAG_VS_UBO = (1 << 0), @@ -202,7 +209,7 @@ private: VkPipeline m_pipeline_object = VK_NULL_HANDLE; // shader bindings - std::array m_descriptor_sets = {}; + std::array m_descriptor_sets = {}; struct { std::array uniform_buffer_bindings = diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index e92c070fc8..f2b23a25ee 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -511,7 +511,7 @@ void UtilityShaderDraw::BindVertexBuffer() void UtilityShaderDraw::BindDescriptors() { // TODO: This method is a mess, clean it up - std::array bind_descriptor_sets = {}; + std::array bind_descriptor_sets = {}; std::array set_writes = {}; uint32_t num_set_writes = 0; @@ -523,7 +523,7 @@ void UtilityShaderDraw::BindDescriptors() if (m_vs_uniform_buffer.buffer != VK_NULL_HANDLE || m_ps_uniform_buffer.buffer != VK_NULL_HANDLE) { VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_UNIFORM_BUFFERS)); + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS)); if (set == VK_NULL_HANDLE) PanicAlert("Failed to allocate descriptor set for utility draw"); @@ -552,7 +552,7 @@ void UtilityShaderDraw::BindDescriptors() &dummy_uniform_buffer, nullptr}; - bind_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS] = set; + bind_descriptor_sets[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS] = set; } // PS samplers @@ -572,7 +572,7 @@ void UtilityShaderDraw::BindDescriptors() { // Allocate a new descriptor set VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS)); + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS)); if (set == VK_NULL_HANDLE) PanicAlert("Failed to allocate descriptor set for utility draw"); @@ -594,7 +594,7 @@ void UtilityShaderDraw::BindDescriptors() } } - bind_descriptor_sets[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS] = set; + bind_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] = set; } vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_set_writes, set_writes.data(), 0, @@ -605,24 +605,24 @@ void UtilityShaderDraw::BindDescriptors() { // UBO only vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_info.pipeline_layout, DESCRIPTOR_SET_UNIFORM_BUFFERS, 1, - &bind_descriptor_sets[0], NUM_UBO_DESCRIPTOR_SET_BINDINGS, - m_ubo_offsets.data()); + m_pipeline_info.pipeline_layout, + DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1, &bind_descriptor_sets[0], + NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_ubo_offsets.data()); } else if (bind_descriptor_sets[0] == VK_NULL_HANDLE && bind_descriptor_sets[1] != VK_NULL_HANDLE) { // Samplers only - vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_info.pipeline_layout, DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS, - 1, &bind_descriptor_sets[1], 0, nullptr); + vkCmdBindDescriptorSets( + m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_info.pipeline_layout, + DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS, 1, &bind_descriptor_sets[1], 0, nullptr); } else if (bind_descriptor_sets[0] != VK_NULL_HANDLE && bind_descriptor_sets[1] != VK_NULL_HANDLE) { // Both - vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_info.pipeline_layout, DESCRIPTOR_SET_UNIFORM_BUFFERS, 2, - bind_descriptor_sets.data(), NUM_UBO_DESCRIPTOR_SET_BINDINGS, - m_ubo_offsets.data()); + vkCmdBindDescriptorSets( + m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_info.pipeline_layout, + DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 2, bind_descriptor_sets.data(), + NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_ubo_offsets.data()); } } From 4bc0e14995343de05666e33fcd973a7d082403c2 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 13 Nov 2016 15:24:55 +1000 Subject: [PATCH 02/10] Vulkan: Use an enumeration to index pipeline layouts --- Source/Core/VideoBackends/Vulkan/Constants.h | 23 ++++++ .../Vulkan/FramebufferManager.cpp | 22 +++--- .../Core/VideoBackends/Vulkan/ObjectCache.cpp | 78 +++++++------------ .../Core/VideoBackends/Vulkan/ObjectCache.h | 33 ++------ .../Vulkan/PaletteTextureConverter.cpp | 8 +- .../Core/VideoBackends/Vulkan/RasterFont.cpp | 4 +- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 4 +- .../VideoBackends/Vulkan/StateTracker.cpp | 10 +-- .../VideoBackends/Vulkan/TextureCache.cpp | 20 ++--- .../VideoBackends/Vulkan/TextureEncoder.cpp | 6 +- 10 files changed, 96 insertions(+), 112 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index ed38cf47d1..229168c9e7 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -42,6 +42,29 @@ enum DESCRIPTOR_SET_BIND_POINT NUM_DESCRIPTOR_SET_BIND_POINTS }; +// We use four pipeline layouts: +// - Standard +// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS) +// - 8 combined image samplers (accessible from PS) +// - BBox Enabled +// - Same as standard, plus a single SSBO accessible from PS +// - Push Constant +// - Same as standard, plus 128 bytes of push constants, accessible from all stages. +// - Texture Decoding +// - Same as push constant, plus a single texel buffer accessible from PS. +// +// All four pipeline layout share the first two descriptor sets (uniform buffers, PS samplers). +// The third descriptor set (see bind points above) is used for storage or texel buffers. +// +enum PIPELINE_LAYOUT +{ + PIPELINE_LAYOUT_STANDARD, + PIPELINE_LAYOUT_BBOX, + PIPELINE_LAYOUT_PUSH_CONSTANT, + PIPELINE_LAYOUT_TEXTURE_CONVERSION, + NUM_PIPELINE_LAYOUTS +}; + // Uniform buffer bindings within the first descriptor set enum UNIFORM_BUFFER_DESCRIPTOR_SET_BINDING { diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 774afbf79d..61eb194e49 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -442,8 +442,8 @@ void FramebufferManager::ReinterpretPixelData(int convtype) VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), m_efb_load_render_pass, - g_object_cache->GetScreenQuadVertexShader(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_efb_load_render_pass, g_object_cache->GetScreenQuadVertexShader(), g_object_cache->GetScreenQuadGeometryShader(), pixel_shader); RasterizationState rs_state = Util::GetNoCullRasterizationState(); @@ -511,8 +511,8 @@ Texture2D* FramebufferManager::ResolveEFBDepthTexture(const VkRect2D& region) // Draw using resolve shader to write the minimum depth of all samples to the resolve texture. UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), m_depth_resolve_render_pass, - g_object_cache->GetScreenQuadVertexShader(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_depth_resolve_render_pass, g_object_cache->GetScreenQuadVertexShader(), g_object_cache->GetScreenQuadGeometryShader(), m_ps_depth_resolve); draw.BeginRenderPass(m_depth_resolve_framebuffer, region); draw.SetPSSampler(0, m_efb_depth_texture->GetView(), g_object_cache->GetPointSampler()); @@ -696,9 +696,9 @@ bool FramebufferManager::PopulateColorReadbackTexture() if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT) { UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), m_copy_color_render_pass, - g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, - m_copy_color_shader); + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_copy_color_render_pass, g_object_cache->GetScreenQuadVertexShader(), + VK_NULL_HANDLE, m_copy_color_shader); VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}}; draw.BeginRenderPass(m_color_copy_framebuffer, rect); @@ -777,9 +777,9 @@ bool FramebufferManager::PopulateDepthReadbackTexture() if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT) { UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), m_copy_depth_render_pass, - g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, - m_copy_depth_shader); + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_copy_depth_render_pass, g_object_cache->GetScreenQuadVertexShader(), + VK_NULL_HANDLE, m_copy_depth_shader); VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}}; draw.BeginRenderPass(m_depth_copy_framebuffer, rect); @@ -1171,7 +1171,7 @@ void FramebufferManager::DrawPokeVertices(const EFBPokeVertex* vertices, size_t // We don't use the utility shader in order to keep the vertices compact. PipelineInfo pipeline_info = {}; pipeline_info.vertex_format = m_poke_vertex_format.get(); - pipeline_info.pipeline_layout = g_object_cache->GetStandardPipelineLayout(); + pipeline_info.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); pipeline_info.vs = m_poke_vertex_shader; pipeline_info.gs = (m_efb_layers > 1) ? m_poke_geometry_shader : VK_NULL_HANDLE; pipeline_info.ps = m_poke_fragment_shader; diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index 56baa687ef..1d8357e21c 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -752,47 +752,32 @@ bool ObjectCache::CreatePipelineLayouts() VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE}; // Info for each pipeline layout - VkPipelineLayoutCreateInfo standard_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - nullptr, - 0, - static_cast(ArraySize(standard_sets)), - standard_sets, - 0, - nullptr}; - VkPipelineLayoutCreateInfo bbox_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - nullptr, - 0, - static_cast(ArraySize(bbox_sets)), - bbox_sets, - 0, - nullptr}; - VkPipelineLayoutCreateInfo push_constant_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - nullptr, - 0, - static_cast(ArraySize(standard_sets)), - standard_sets, - 1, - &push_constant_range}; - VkPipelineLayoutCreateInfo texture_conversion_info = { - VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - nullptr, - 0, - static_cast(ArraySize(texture_conversion_sets)), - texture_conversion_sets, - 1, - &push_constant_range}; + VkPipelineLayoutCreateInfo pipeline_layout_info[NUM_PIPELINE_LAYOUTS] = { + // Standard + {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, + static_cast(ArraySize(standard_sets)), standard_sets, 0, nullptr}, - if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &standard_info, nullptr, - &m_standard_pipeline_layout)) != VK_SUCCESS || - (res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &bbox_info, nullptr, - &m_bbox_pipeline_layout)) != VK_SUCCESS || - (res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &push_constant_info, nullptr, - &m_push_constant_pipeline_layout)) != VK_SUCCESS || - (res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &texture_conversion_info, - nullptr, &m_texture_conversion_pipeline_layout)) != VK_SUCCESS) + // BBox + {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, + static_cast(ArraySize(bbox_sets)), bbox_sets, 0, nullptr}, + + // Push Constant + {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, + static_cast(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range}, + + // Texture Conversion + {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, + static_cast(ArraySize(texture_conversion_sets)), texture_conversion_sets, 1, + &push_constant_range}}; + + for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++) { - LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: "); - return false; + if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i], + nullptr, &m_pipeline_layouts[i])) != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: "); + return false; + } } return true; @@ -800,16 +785,11 @@ bool ObjectCache::CreatePipelineLayouts() void ObjectCache::DestroyPipelineLayouts() { - if (m_standard_pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_standard_pipeline_layout, nullptr); - if (m_bbox_pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_bbox_pipeline_layout, nullptr); - if (m_push_constant_pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_push_constant_pipeline_layout, - nullptr); - if (m_texture_conversion_pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_texture_conversion_pipeline_layout, - nullptr); + for (VkPipelineLayout layout : m_pipeline_layouts) + { + if (layout != VK_NULL_HANDLE) + vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), layout, nullptr); + } } bool ObjectCache::CreateUtilityShaderVertexFormat() diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.h b/Source/Core/VideoBackends/Vulkan/ObjectCache.h index 76ed886ecd..546d1439a5 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.h +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.h @@ -62,30 +62,15 @@ public: ObjectCache(); ~ObjectCache(); - // We have four shared pipeline layouts: - // - Standard - // - Per-stage UBO (VS/GS/PS, VS constants accessible from PS) - // - 8 combined image samplers (accessible from PS) - // - BBox Enabled - // - Same as standard, plus a single SSBO accessible from PS - // - Push Constant - // - Same as standard, plus 128 bytes of push constants, accessible from all stages. - // - Texture Conversion - // - Same as push constant, plus a single texel buffer accessible from PS. - // - // All three pipeline layouts use the same descriptor set layouts, but the final descriptor set - // (SSBO) is only required when using the BBox Enabled pipeline layout. - // - VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT set) const + // Descriptor set layout accessor. Used for allocating descriptor sets. + VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT layout) const { - return m_descriptor_set_layouts[set]; + return m_descriptor_set_layouts[layout]; } - VkPipelineLayout GetStandardPipelineLayout() const { return m_standard_pipeline_layout; } - VkPipelineLayout GetBBoxPipelineLayout() const { return m_bbox_pipeline_layout; } - VkPipelineLayout GetPushConstantPipelineLayout() const { return m_push_constant_pipeline_layout; } - VkPipelineLayout GetTextureConversionPipelineLayout() const + // Pipeline layout accessor. Used to fill in required field in PipelineInfo. + VkPipelineLayout GetPipelineLayout(PIPELINE_LAYOUT layout) const { - return m_texture_conversion_pipeline_layout; + return m_pipeline_layouts[layout]; } // Shared utility shader resources VertexFormat* GetUtilityShaderVertexFormat() const @@ -164,11 +149,7 @@ private: void DestroySamplers(); std::array m_descriptor_set_layouts = {}; - - VkPipelineLayout m_standard_pipeline_layout = VK_NULL_HANDLE; - VkPipelineLayout m_bbox_pipeline_layout = VK_NULL_HANDLE; - VkPipelineLayout m_push_constant_pipeline_layout = VK_NULL_HANDLE; - VkPipelineLayout m_texture_conversion_pipeline_layout = VK_NULL_HANDLE; + std::array m_pipeline_layouts = {}; std::unique_ptr m_utility_shader_vertex_format; std::unique_ptr m_utility_shader_vertex_buffer; diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp index 1234f5d5dd..7db6286805 100644 --- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp @@ -113,9 +113,9 @@ void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); // Set up draw - UtilityShaderDraw draw(command_buffer, g_object_cache->GetTextureConversionPipelineLayout(), - render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, - m_shaders[format]); + UtilityShaderDraw draw( + command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), + render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, m_shaders[format]); VkRect2D region = {{0, 0}, {width, height}}; draw.BeginRenderPass(dst_framebuffer, region); @@ -133,7 +133,7 @@ void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer, // We have to bind the texel buffer descriptor set separately. vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - g_object_cache->GetTextureConversionPipelineLayout(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER, 1, &texel_buffer_descriptor_set, 0, nullptr); diff --git a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp index f67412e243..91f5566fdb 100644 --- a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp +++ b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp @@ -300,8 +300,8 @@ void RasterFont::PrintMultiLineText(VkRenderPass render_pass, const std::string& return; UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPushConstantPipelineLayout(), render_pass, - m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader); + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), + render_pass, m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader); UtilityShaderVertex* vertices = draw.ReserveVertices(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, text.length() * 6); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 2b8e0b63a6..291f3e07e8 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -448,7 +448,7 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha // No need to start a new render pass, but we do need to restore viewport state UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), FramebufferManager::GetInstance()->GetEFBLoadRenderPass(), g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader); @@ -879,7 +879,7 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r // Set up common data UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), render_pass, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), render_pass, g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, m_blit_fragment_shader); diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index ae45ce857f..ef2343564d 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -75,7 +75,7 @@ bool StateTracker::Initialize() m_pipeline_state.rasterization_state.depth_clamp = VK_TRUE; // BBox is disabled by default. - m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout(); + m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; m_bbox_enabled = false; @@ -164,8 +164,8 @@ bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid) // vertex loader that uses this format, since we need it to create a pipeline. pinfo.vertex_format = VertexFormat::GetOrCreateMatchingFormat(uid.vertex_decl); pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ? - g_object_cache->GetBBoxPipelineLayout() : - g_object_cache->GetStandardPipelineLayout(); + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) : + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); pinfo.vs = g_object_cache->GetVertexShaderForUid(uid.vs_uid); if (pinfo.vs == VK_NULL_HANDLE) { @@ -544,7 +544,7 @@ void StateTracker::SetBBoxEnable(bool enable) // Change the number of active descriptor sets, as well as the pipeline layout if (enable) { - m_pipeline_state.pipeline_layout = g_object_cache->GetBBoxPipelineLayout(); + m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX); m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS; // The bbox buffer never changes, so we defer descriptor updates until it is enabled. @@ -553,7 +553,7 @@ void StateTracker::SetBBoxEnable(bool enable) } else { - m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout(); + m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; } diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index b607a91fd0..139221c9c0 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -233,7 +233,7 @@ void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), GetRenderPassForTextureUpdate(dst_texture->GetTexture()), g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(), m_copy_shader); @@ -588,7 +588,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); UtilityShaderDraw draw( - command_buffer, g_object_cache->GetPushConstantPipelineLayout(), + command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), TextureCache::GetInstance()->GetRenderPassForTextureUpdate(m_texture.get()), g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(), is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader : @@ -880,10 +880,10 @@ void TextureCache::EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 d // 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->GetStandardPipelineLayout(), - m_texture_encoder->GetEncodingRenderPass(), - g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, - m_rgb_to_yuyv_shader); + UtilityShaderDraw draw( + command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_texture_encoder->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_encoder->GetEncodingTextureFramebuffer(), region); draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); @@ -953,10 +953,10 @@ void TextureCache::DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const v 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->GetStandardPipelineLayout(), - m_texture_encoder->GetEncodingRenderPass(), - g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, - m_yuyv_to_rgb_shader); + UtilityShaderDraw draw( + command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + m_texture_encoder->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)); diff --git a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp index 77a443a9e0..08948d97cb 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp @@ -89,9 +89,9 @@ void TextureEncoder::EncodeTextureToRam(VkImageView src_texture, u8* dest_ptr, u StateTracker::GetInstance()->EndRenderPass(); UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPushConstantPipelineLayout(), m_encoding_render_pass, - g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, - m_texture_encoding_shaders[format]); + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), + m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(), + VK_NULL_HANDLE, m_texture_encoding_shaders[format]); // Uniform - int4 of left,top,native_width,scale s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast(native_width), From e241ec666689ccf89949d4469fd60f505b63514a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 19 Nov 2016 22:22:04 +1000 Subject: [PATCH 03/10] Vulkan: Implement StagingTexture2D on top of StagingBuffer Greatly simplifies things, and we weren't using the linear texture implementation anyway. --- .../Vulkan/FramebufferManager.cpp | 6 +- .../VideoBackends/Vulkan/StagingBuffer.cpp | 55 ++- .../Core/VideoBackends/Vulkan/StagingBuffer.h | 7 +- .../VideoBackends/Vulkan/StagingTexture2D.cpp | 442 ++---------------- .../VideoBackends/Vulkan/StagingTexture2D.h | 91 +--- 5 files changed, 91 insertions(+), 510 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 61eb194e49..1f497416be 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -1007,10 +1007,8 @@ bool FramebufferManager::CreateReadbackTextures() VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - // We can't copy to/from color<->depth formats, so using a linear texture is not an option here. - // TODO: Investigate if vkCmdBlitImage can be used. The documentation isn't that clear. - m_depth_readback_texture = StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, - EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT); + m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, + EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT); if (!m_depth_copy_texture || !m_depth_readback_texture) { ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture"); diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp b/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp index f972492abb..6bd8170fc9 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp @@ -80,6 +80,9 @@ void StagingBuffer::InvalidateGPUCache(VkCommandBuffer command_buffer, VkPipelineStageFlagBits dest_pipeline_stage, VkDeviceSize offset, VkDeviceSize size) { + if (m_coherent) + return; + _assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE)); Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, dest_access_flags, offset, size, VK_PIPELINE_STAGE_HOST_BIT, dest_pipeline_stage); @@ -90,6 +93,9 @@ void StagingBuffer::PrepareForGPUWrite(VkCommandBuffer command_buffer, VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset, VkDeviceSize size) { + if (m_coherent) + return; + _assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE)); Util::BufferMemoryBarrier(command_buffer, m_buffer, 0, dst_access_flags, offset, size, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dst_pipeline_stage); @@ -99,6 +105,9 @@ void StagingBuffer::FlushGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBi VkPipelineStageFlagBits src_pipeline_stage, VkDeviceSize offset, VkDeviceSize size) { + if (m_coherent) + return; + _assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE)); Util::BufferMemoryBarrier(command_buffer, m_buffer, src_access_flags, VK_ACCESS_HOST_READ_BIT, offset, size, src_pipeline_stage, VK_PIPELINE_STAGE_HOST_BIT); @@ -136,8 +145,9 @@ void StagingBuffer::Write(VkDeviceSize offset, const void* data, size_t size, FlushCPUCache(offset, size); } -std::unique_ptr -StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage) +bool StagingBuffer::AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, + VkBufferUsageFlags usage, VkBuffer* out_buffer, + VkDeviceMemory* out_memory, bool* out_coherent) { VkBufferCreateInfo buffer_create_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType @@ -149,24 +159,22 @@ StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsage 0, // uint32_t queueFamilyIndexCount nullptr // const uint32_t* pQueueFamilyIndices }; - VkBuffer buffer; VkResult res = - vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer); + vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, out_buffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); - return nullptr; + return false; } VkMemoryRequirements requirements; - vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &requirements); + vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), *out_buffer, &requirements); - bool is_coherent; u32 type_index; if (type == STAGING_BUFFER_TYPE_UPLOAD) - type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, &is_coherent); + type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, out_coherent); else - type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, &is_coherent); + type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, out_coherent); VkMemoryAllocateInfo memory_allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType @@ -174,25 +182,36 @@ StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsage requirements.size, // VkDeviceSize allocationSize type_index // uint32_t memoryTypeIndex }; - VkDeviceMemory memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); + res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, out_memory); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - return nullptr; + vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr); + return false; } - res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0); + res = vkBindBufferMemory(g_vulkan_context->GetDevice(), *out_buffer, *out_memory, 0); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return nullptr; + vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr); + vkFreeMemory(g_vulkan_context->GetDevice(), *out_memory, nullptr); + return false; } - return std::make_unique(type, buffer, memory, size, is_coherent); + return true; +} + +std::unique_ptr StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, + VkBufferUsageFlags usage) +{ + VkBuffer buffer; + VkDeviceMemory memory; + bool coherent; + if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent)) + return nullptr; + + return std::make_unique(type, buffer, memory, size, coherent); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h index 22f99d1780..65cfb1c5c7 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h @@ -16,7 +16,7 @@ class StagingBuffer public: StagingBuffer(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size, bool coherent); - ~StagingBuffer(); + virtual ~StagingBuffer(); STAGING_BUFFER_TYPE GetType() const { return m_type; } VkDeviceSize GetSize() const { return m_size; } @@ -33,6 +33,7 @@ public: void FlushCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE); // Upload part 2: Prepare for device read from the GPU side + // Implicit when submitting the command buffer, so rarely needed. void InvalidateGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags, VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE); @@ -59,6 +60,10 @@ public: VkBufferUsageFlags usage); protected: + // Allocates the resources needed to create a staging buffer. + static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage, + VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent); + STAGING_BUFFER_TYPE m_type; VkBuffer m_buffer; VkDeviceMemory m_memory; diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp index 1f5be7563c..ee726fc8f2 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp +++ b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp @@ -14,16 +14,16 @@ namespace Vulkan { -StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, - u32 stride) - : m_type(type), m_width(width), m_height(height), m_format(format), - m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride) +StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, + VkDeviceSize size, bool coherent, u32 width, u32 height, + VkFormat format, u32 stride) + : StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height), + m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride) { } StagingTexture2D::~StagingTexture2D() { - _assert_(!m_map_pointer); } void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const @@ -96,283 +96,13 @@ void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const vo } } -std::unique_ptr StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width, - u32 height, VkFormat format) -{ -// TODO: Using a buffer here as opposed to a linear texture is faster on AMD. -// NVIDIA also seems faster with buffers over textures. -#if 0 - // Check for support for this format as a linear texture. - // Some drivers don't support this (e.g. adreno). - VkImageFormatProperties properties; - VkResult res = vkGetPhysicalDeviceImageFormatProperties( - g_object_cache->GetPhysicalDevice(), format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_LINEAR, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, &properties); - if (res == VK_SUCCESS && width <= properties.maxExtent.width && - height <= properties.maxExtent.height) - { - return StagingTexture2DLinear::Create(type, width, height, format); - } -#endif - - // Fall back to a buffer copy. - return StagingTexture2DBuffer::Create(type, width, height, format); -} - -StagingTexture2DLinear::StagingTexture2DLinear(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format, u32 stride, VkImage image, - VkDeviceMemory memory, VkDeviceSize size, - bool coherent) - : StagingTexture2D(type, width, height, format, stride), m_image(image), m_memory(memory), - m_size(size), m_layout(VK_IMAGE_LAYOUT_PREINITIALIZED), m_coherent(coherent) -{ -} - -StagingTexture2DLinear::~StagingTexture2DLinear() -{ - if (m_map_pointer) - Unmap(); - - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); - g_command_buffer_mgr->DeferImageDestruction(m_image); -} - -void StagingTexture2DLinear::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Prepare the buffer for copying. - // We don't care about the existing contents, so set to UNDEFINED. - VkImageMemoryBarrier before_transfer_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkAccessFlags srcAccessMask - VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask - VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout - VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex - VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex - m_image, // VkImage image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, - &before_transfer_barrier); - - // Issue the image copy, gpu -> host. - VkImageCopy copy_region = { - {src_aspect, level, layer, 1}, // VkImageSubresourceLayers srcSubresource - {static_cast(x), static_cast(y), 0}, // VkOffset3D srcOffset - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers dstSubresource - {0, 0, 0}, // VkOffset3D dstOffset - {width, height, 1} // VkExtent3D extent - }; - vkCmdCopyImage(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); - - // Ensure writes are visible to the host. - VkImageMemoryBarrier visible_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask - VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout - VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout - VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex - VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex - m_image, // VkImage image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, - 0, 0, nullptr, 0, nullptr, 1, &visible_barrier); - m_layout = VK_IMAGE_LAYOUT_GENERAL; - - // Invalidate memory range if currently mapped. - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } -} - -void StagingTexture2DLinear::CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Flush memory range if currently mapped. - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } - - // Ensure any writes to the image are visible to the GPU. - VkImageMemoryBarrier barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask - VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask - m_layout, // VkImageLayout oldLayout - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout - VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex - VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex - m_image, // VkImage image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, 0, nullptr, 0, nullptr, 1, &barrier); - - m_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - // Issue the image copy, host -> gpu. - VkImageCopy copy_region = { - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers srcSubresource - {0, 0, 0}, // VkOffset3D srcOffset - {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers dstSubresource - {static_cast(x), static_cast(y), 0}, // VkOffset3D dstOffset - {width, height, 1} // VkExtent3D extent - }; - vkCmdCopyImage(command_buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); -} - -bool StagingTexture2DLinear::Map(VkDeviceSize offset /* = 0 */, - VkDeviceSize size /* = VK_WHOLE_SIZE */) -{ - m_map_offset = offset; - if (size == VK_WHOLE_SIZE) - m_map_size = m_size - offset; - else - m_map_size = size; - - _assert_(!m_map_pointer); - _assert_(m_map_offset + m_map_size <= m_size); - - void* map_pointer; - VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0, - &map_pointer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkMapMemory failed: "); - return false; - } - - m_map_pointer = reinterpret_cast(map_pointer); - return true; -} - -void StagingTexture2DLinear::Unmap() -{ - _assert_(m_map_pointer); - - vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); - m_map_pointer = nullptr; - m_map_offset = 0; - m_map_size = 0; -} - -std::unique_ptr -StagingTexture2DLinear::Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format) -{ - VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - VkImageCreateInfo create_info = { - VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkImageCreateFlags flags - VK_IMAGE_TYPE_2D, // VkImageType imageType - format, // VkFormat format - {width, height, 1}, // VkExtent3D extent - 1, // uint32_t mipLevels - 1, // uint32_t arrayLayers - VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples - VK_IMAGE_TILING_LINEAR, // VkImageTiling tiling - usage, // VkImageUsageFlags usage - VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode - 0, // uint32_t queueFamilyIndexCount - nullptr, // const uint32_t* pQueueFamilyIndices - VK_IMAGE_LAYOUT_PREINITIALIZED // VkImageLayout initialLayout - }; - - VkImage image; - VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &create_info, nullptr, &image); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); - return nullptr; - } - - VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); - - bool is_coherent; - u32 memory_type_index; - if (type == STAGING_BUFFER_TYPE_READBACK) - { - memory_type_index = - g_vulkan_context->GetReadbackMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - else - { - memory_type_index = - g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - VkMemoryAllocateInfo memory_allocate_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - memory_requirements.size, // VkDeviceSize allocationSize - memory_type_index // uint32_t memoryTypeIndex - }; - VkDeviceMemory memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - return nullptr; - } - - res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return nullptr; - } - - // Assume tight packing. Is this correct? - u32 stride = width * Util::GetTexelSize(format); - return std::make_unique(type, width, height, format, stride, image, - memory, memory_requirements.size, is_coherent); -} - -StagingTexture2DBuffer::StagingTexture2DBuffer(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format, u32 stride, VkBuffer buffer, - VkDeviceMemory memory, VkDeviceSize size, - bool coherent) - : StagingTexture2D(type, width, height, format, stride), m_buffer(buffer), m_memory(memory), - m_size(size), m_coherent(coherent) -{ -} - -StagingTexture2DBuffer::~StagingTexture2DBuffer() -{ - if (m_map_pointer) - Unmap(); - - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); - g_command_buffer_mgr->DeferBufferDestruction(m_buffer); -} - -void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) +void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, + VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, + u32 height, u32 level, u32 layer) { // Issue the image->buffer copy. VkBufferImageCopy image_copy = { - 0, // VkDeviceSize bufferOffset + y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource @@ -382,42 +112,28 @@ void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkIma vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, &image_copy); - // Ensure the write has completed. - VkDeviceSize copy_size = m_row_stride * height; - Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_HOST_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_HOST_BIT); - - // If we're still mapped, invalidate the mapped range - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } + // Flush CPU and GPU caches if not coherent mapping. + VkDeviceSize buffer_flush_offset = y * m_row_stride; + VkDeviceSize buffer_flush_size = height * m_row_stride; + FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + buffer_flush_offset, buffer_flush_size); + InvalidateCPUCache(buffer_flush_offset, buffer_flush_size); } -void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) +void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image, + VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, + u32 height, u32 level, u32 layer) { - // If we're still mapped, flush the mapped range - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } + // Flush CPU and GPU caches if not coherent mapping. + VkDeviceSize buffer_flush_offset = y * m_row_stride; + VkDeviceSize buffer_flush_size = height * m_row_stride; + FlushCPUCache(buffer_flush_offset, buffer_flush_size); + InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + buffer_flush_offset, buffer_flush_size); - // Ensure writes are visible to GPU. - VkDeviceSize copy_size = m_row_stride * height; - Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_HOST_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT); - - // Issue the buffer->image copy + // Issue the buffer->image copy. VkBufferImageCopy image_copy = { - 0, // VkDeviceSize bufferOffset + y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource @@ -428,109 +144,21 @@ void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage &image_copy); } -bool StagingTexture2DBuffer::Map(VkDeviceSize offset /* = 0 */, - VkDeviceSize size /* = VK_WHOLE_SIZE */) -{ - m_map_offset = offset; - if (size == VK_WHOLE_SIZE) - m_map_size = m_size - offset; - else - m_map_size = size; - - _assert_(!m_map_pointer); - _assert_(m_map_offset + m_map_size <= m_size); - - void* map_pointer; - VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0, - &map_pointer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkMapMemory failed: "); - return false; - } - - m_map_pointer = reinterpret_cast(map_pointer); - return true; -} - -void StagingTexture2DBuffer::Unmap() -{ - _assert_(m_map_pointer); - - vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); - m_map_pointer = nullptr; - m_map_offset = 0; - m_map_size = 0; -} - -std::unique_ptr -StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format) +std::unique_ptr StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width, + u32 height, VkFormat format) { // Assume tight packing. - u32 row_stride = Util::GetTexelSize(format) * width; - u32 buffer_size = row_stride * height; + u32 stride = Util::GetTexelSize(format) * width; + u32 size = stride * height; VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - VkBufferCreateInfo buffer_create_info = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkBufferCreateFlags flags - buffer_size, // VkDeviceSize size - usage, // VkBufferUsageFlags usage - VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode - 0, // uint32_t queueFamilyIndexCount - nullptr // const uint32_t* pQueueFamilyIndices - }; + VkBuffer buffer; - VkResult res = - vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); - return nullptr; - } - - VkMemoryRequirements memory_requirements; - vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &memory_requirements); - - bool is_coherent; - u32 memory_type_index; - if (type == STAGING_BUFFER_TYPE_READBACK) - { - memory_type_index = - g_vulkan_context->GetReadbackMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - else - { - memory_type_index = - g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - - VkMemoryAllocateInfo memory_allocate_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - memory_requirements.size, // VkDeviceSize allocationSize - memory_type_index // uint32_t memoryTypeIndex - }; VkDeviceMemory memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); + bool coherent; + if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent)) return nullptr; - } - res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return nullptr; - } - - return std::make_unique(type, width, height, format, row_stride, buffer, - memory, buffer_size, is_coherent); + return std::make_unique(type, buffer, memory, size, coherent, width, height, + format, stride); } - } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h index 9dfd0c0e04..74dbb8cf07 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h +++ b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h @@ -9,29 +9,26 @@ #include "Common/CommonTypes.h" #include "VideoBackends/Vulkan/Constants.h" +#include "VideoBackends/Vulkan/StagingBuffer.h" namespace Vulkan { -class StagingTexture2D +class StagingTexture2D final : public StagingBuffer { public: - StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, u32 stride); - virtual ~StagingTexture2D(); + StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, + VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format, + u32 stride); + ~StagingTexture2D(); - STAGING_BUFFER_TYPE GetType() const { return m_type; } u32 GetWidth() const { return m_width; } u32 GetHeight() const { return m_height; } VkFormat GetFormat() const { return m_format; } u32 GetRowStride() const { return m_row_stride; } u32 GetTexelSize() const { return m_texel_size; } - bool IsMapped() const { return m_map_pointer != nullptr; } - const char* GetMapPointer() const { return m_map_pointer; } - char* GetMapPointer() { return m_map_pointer; } - VkDeviceSize GetMapOffset() const { return m_map_offset; } - VkDeviceSize GetMapSize() const { return m_map_size; } + // Requires Map() to be called first. const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; } char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; } - // Requires Map() to be called first. void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const; void WriteTexel(u32 x, u32 y, const void* data, size_t data_size); void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const; @@ -39,89 +36,23 @@ public: // Assumes that image is in TRANSFER_SRC layout. // Results are not ready until command_buffer has been executed. - virtual void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, u32 height, - u32 level, u32 layer) = 0; + void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, + u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); // Assumes that image is in TRANSFER_DST layout. // Buffer is not safe for re-use until after command_buffer has been executed. - virtual void CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, - u32 level, u32 layer) = 0; - virtual bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) = 0; - virtual void Unmap() = 0; + void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, + u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); // Creates the optimal format of image copy. static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format); protected: - STAGING_BUFFER_TYPE m_type; u32 m_width; u32 m_height; VkFormat m_format; u32 m_texel_size; u32 m_row_stride; - - char* m_map_pointer = nullptr; - VkDeviceSize m_map_offset = 0; - VkDeviceSize m_map_size = 0; -}; - -class StagingTexture2DLinear : public StagingTexture2D -{ -public: - StagingTexture2DLinear(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, - u32 stride, VkImage image, VkDeviceMemory memory, VkDeviceSize size, - bool coherent); - - ~StagingTexture2DLinear(); - - void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) override; - void Unmap() override; - - static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format); - -private: - VkImage m_image; - VkDeviceMemory m_memory; - VkDeviceSize m_size; - VkImageLayout m_layout; - bool m_coherent; -}; - -class StagingTexture2DBuffer : public StagingTexture2D -{ -public: - StagingTexture2DBuffer(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, - u32 stride, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size, - bool coherent); - - ~StagingTexture2DBuffer(); - - void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) override; - void Unmap() override; - - static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format); - -private: - VkBuffer m_buffer; - VkDeviceMemory m_memory; - VkDeviceSize m_size; - bool m_coherent; }; } From 804af42cccd18fde08c58f38238c67de952ec5e0 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 20 Nov 2016 00:02:06 +1000 Subject: [PATCH 04/10] Vulkan: Support binding texel buffers in UtilityShaderDraw --- .../VideoBackends/Vulkan/ShaderCompiler.cpp | 1 + Source/Core/VideoBackends/Vulkan/Util.cpp | 77 ++++++++++++++----- Source/Core/VideoBackends/Vulkan/Util.h | 4 + 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp index ded63ce3ed..2265a34364 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp @@ -52,6 +52,7 @@ static const char SHADER_HEADER[] = R"( #define UBO_BINDING(packing, x) layout(packing, set = 0, binding = (x - 1)) #define SAMPLER_BINDING(x) layout(set = 1, binding = x) #define SSBO_BINDING(x) layout(set = 2, binding = x) + #define TEXEL_BUFFER_BINDING(x) layout(set = 2, binding = x) #define VARYING_LOCATION(x) layout(location = x) #define FORCE_EARLY_Z layout(early_fragment_tests) in diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index f2b23a25ee..f85321bac5 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -364,6 +364,15 @@ void UtilityShaderDraw::SetPSSampler(size_t index, VkImageView view, VkSampler s m_ps_samplers[index].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } +void UtilityShaderDraw::SetPSTexelBuffer(VkBufferView view) +{ + // Should only be used with the texture conversion pipeline layout. + _assert_(m_pipeline_info.pipeline_layout == + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION)); + + m_ps_texel_buffer = view; +} + void UtilityShaderDraw::SetRasterizationState(const RasterizationState& state) { m_pipeline_info.rasterization_state.bits = state.bits; @@ -600,29 +609,59 @@ void UtilityShaderDraw::BindDescriptors() vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_set_writes, set_writes.data(), 0, nullptr); - // Bind only the sets we updated - if (bind_descriptor_sets[0] != VK_NULL_HANDLE && bind_descriptor_sets[1] == VK_NULL_HANDLE) + if (m_ps_texel_buffer != VK_NULL_HANDLE) { - // UBO only + // TODO: Handle case where this fails. + // This'll only be when we do over say, 1024 allocations per frame, which shouldn't happen. + // TODO: Execute the command buffer, reset render passes and then try again. + VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet( + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS)); + if (set == VK_NULL_HANDLE) + { + PanicAlert("Failed to allocate texel buffer descriptor set for utility draw"); + return; + } + + VkWriteDescriptorSet set_write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + nullptr, + set, + 0, + 0, + 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + nullptr, + nullptr, + &m_ps_texel_buffer}; + vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), 1, &set_write, 0, nullptr); + bind_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] = set; + } + + // Fast path when there are no gaps in the set bindings + u32 bind_point_index; + for (bind_point_index = 0; bind_point_index < NUM_DESCRIPTOR_SET_BIND_POINTS; bind_point_index++) + { + if (bind_descriptor_sets[bind_point_index] == VK_NULL_HANDLE) + break; + } + if (bind_point_index > 0) + { + // Bind the contiguous sets, any others after any gaps will be handled below vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_info.pipeline_layout, - DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1, &bind_descriptor_sets[0], - NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_ubo_offsets.data()); + m_pipeline_info.pipeline_layout, 0, bind_point_index, + &bind_descriptor_sets[0], NUM_UBO_DESCRIPTOR_SET_BINDINGS, + m_ubo_offsets.data()); } - else if (bind_descriptor_sets[0] == VK_NULL_HANDLE && bind_descriptor_sets[1] != VK_NULL_HANDLE) + + // Handle any remaining sets + for (u32 i = bind_point_index; i < NUM_DESCRIPTOR_SET_BIND_POINTS; i++) { - // Samplers only - vkCmdBindDescriptorSets( - m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_info.pipeline_layout, - DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS, 1, &bind_descriptor_sets[1], 0, nullptr); - } - else if (bind_descriptor_sets[0] != VK_NULL_HANDLE && bind_descriptor_sets[1] != VK_NULL_HANDLE) - { - // Both - vkCmdBindDescriptorSets( - m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_info.pipeline_layout, - DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 2, bind_descriptor_sets.data(), - NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_ubo_offsets.data()); + if (bind_descriptor_sets[i] == VK_NULL_HANDLE) + continue; + + // No need to worry about dynamic offsets here, since #0 will always be bound above. + vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_pipeline_info.pipeline_layout, i, 1, &bind_descriptor_sets[i], 0, + nullptr); } } diff --git a/Source/Core/VideoBackends/Vulkan/Util.h b/Source/Core/VideoBackends/Vulkan/Util.h index 6900cce08b..bb701716c2 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.h +++ b/Source/Core/VideoBackends/Vulkan/Util.h @@ -137,6 +137,8 @@ public: void SetPSSampler(size_t index, VkImageView view, VkSampler sampler); + void SetPSTexelBuffer(VkBufferView view); + void SetRasterizationState(const RasterizationState& state); void SetDepthStencilState(const DepthStencilState& state); void SetBlendState(const BlendState& state); @@ -182,6 +184,8 @@ private: std::array m_ps_samplers = {}; + VkBufferView m_ps_texel_buffer = VK_NULL_HANDLE; + PipelineInfo m_pipeline_info = {}; }; From add638538b24f5c7273323538ee4dab0f0271a16 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 19 Nov 2016 23:25:23 +1000 Subject: [PATCH 05/10] Vulkan: Combine PaletteTextureConverter and TextureEncoder classes --- .../Core/VideoBackends/Vulkan/CMakeLists.txt | 5 +- .../Vulkan/PaletteTextureConverter.cpp | 272 ---------- .../Vulkan/PaletteTextureConverter.h | 46 -- .../VideoBackends/Vulkan/TextureCache.cpp | 36 +- .../Core/VideoBackends/Vulkan/TextureCache.h | 7 +- .../VideoBackends/Vulkan/TextureConverter.cpp | 481 ++++++++++++++++++ .../{TextureEncoder.h => TextureConverter.h} | 37 +- .../VideoBackends/Vulkan/TextureEncoder.cpp | 237 --------- .../Core/VideoBackends/Vulkan/Vulkan.vcxproj | 8 +- 9 files changed, 527 insertions(+), 602 deletions(-) delete mode 100644 Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp delete mode 100644 Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h create mode 100644 Source/Core/VideoBackends/Vulkan/TextureConverter.cpp rename Source/Core/VideoBackends/Vulkan/{TextureEncoder.h => TextureConverter.h} (54%) delete mode 100644 Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index 82d471ba54..fc5ac99df9 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -3,7 +3,6 @@ set(SRCS CommandBufferManager.cpp FramebufferManager.cpp ObjectCache.cpp - PaletteTextureConverter.cpp PerfQuery.cpp RasterFont.cpp Renderer.cpp @@ -15,11 +14,11 @@ set(SRCS SwapChain.cpp Texture2D.cpp TextureCache.cpp - TextureEncoder.cpp + TextureConverter.cpp Util.cpp VertexFormat.cpp VertexManager.cpp - VulkanContext.cpp + VulkanContext.cpp VulkanLoader.cpp main.cpp ) diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp deleted file mode 100644 index 7db6286805..0000000000 --- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "VideoBackends/Vulkan/PaletteTextureConverter.h" - -#include -#include -#include -#include - -#include "Common/Assert.h" -#include "Common/CommonFuncs.h" -#include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" - -#include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/FramebufferManager.h" -#include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/Renderer.h" -#include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/StreamBuffer.h" -#include "VideoBackends/Vulkan/Texture2D.h" -#include "VideoBackends/Vulkan/Util.h" -#include "VideoBackends/Vulkan/VulkanContext.h" - -namespace Vulkan -{ -PaletteTextureConverter::PaletteTextureConverter() -{ -} - -PaletteTextureConverter::~PaletteTextureConverter() -{ - for (const auto& it : m_shaders) - { - if (it != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), it, nullptr); - } - - if (m_palette_buffer_view != VK_NULL_HANDLE) - vkDestroyBufferView(g_vulkan_context->GetDevice(), m_palette_buffer_view, nullptr); -} - -bool PaletteTextureConverter::Initialize() -{ - if (!CreateBuffers()) - return false; - - if (!CompileShaders()) - return false; - - return true; -} - -void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer, - VkRenderPass render_pass, - VkFramebuffer dst_framebuffer, Texture2D* src_texture, - u32 width, u32 height, void* palette, - TlutFormat format, u32 src_format) -{ - struct PSUniformBlock - { - float multiplier; - int texel_buffer_offset; - int pad[2]; - }; - - _assert_(static_cast(format) < NUM_PALETTE_CONVERSION_SHADERS); - - size_t palette_size = (src_format & 0xF) == GX_TF_I4 ? 32 : 512; - VkDescriptorSet texel_buffer_descriptor_set; - - // Allocate memory for the palette, and descriptor sets for the buffer. - // If any of these fail, execute a command buffer, and try again. - if (!m_palette_stream_buffer->ReserveMemory(palette_size, - g_vulkan_context->GetTexelBufferAlignment()) || - (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == - VK_NULL_HANDLE) - { - WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); - - if (!m_palette_stream_buffer->ReserveMemory(palette_size, - g_vulkan_context->GetTexelBufferAlignment()) || - (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == - VK_NULL_HANDLE) - { - PanicAlert("Failed to allocate space for texture conversion"); - return; - } - } - - // Fill descriptor set #2 (texel buffer) - u32 palette_offset = static_cast(m_palette_stream_buffer->GetCurrentOffset()); - VkWriteDescriptorSet texel_set_write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - texel_buffer_descriptor_set, - 0, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, - nullptr, - nullptr, - &m_palette_buffer_view}; - vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), 1, &texel_set_write, 0, nullptr); - - Util::BufferMemoryBarrier(command_buffer, m_palette_stream_buffer->GetBuffer(), - VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, palette_offset, - palette_size, VK_PIPELINE_STAGE_HOST_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - // Set up draw - UtilityShaderDraw draw( - command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), - render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, m_shaders[format]); - - VkRect2D region = {{0, 0}, {width, height}}; - draw.BeginRenderPass(dst_framebuffer, region); - - // Copy in palette - memcpy(m_palette_stream_buffer->GetCurrentHostPointer(), palette, palette_size); - m_palette_stream_buffer->CommitMemory(palette_size); - - // PS Uniforms/Samplers - PSUniformBlock uniforms = {}; - uniforms.multiplier = (src_format & 0xF) == GX_TF_I4 ? 15.0f : 255.0f; - uniforms.texel_buffer_offset = static_cast(palette_offset / sizeof(u16)); - draw.SetPushConstants(&uniforms, sizeof(uniforms)); - draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetPointSampler()); - - // We have to bind the texel buffer descriptor set separately. - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), - DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER, 1, - &texel_buffer_descriptor_set, 0, nullptr); - - // Draw - draw.SetViewportAndScissor(0, 0, width, height); - draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); - draw.EndRenderPass(); -} - -bool PaletteTextureConverter::CreateBuffers() -{ - // TODO: Check against maximum size - static const size_t BUFFER_SIZE = 1024 * 1024; - - m_palette_stream_buffer = - StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, BUFFER_SIZE, BUFFER_SIZE); - if (!m_palette_stream_buffer) - return false; - - // Create a view of the whole buffer, we'll offset our texel load into it - VkBufferViewCreateInfo view_info = { - VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkBufferViewCreateFlags flags - m_palette_stream_buffer->GetBuffer(), // VkBuffer buffer - VK_FORMAT_R16_UINT, // VkFormat format - 0, // VkDeviceSize offset - BUFFER_SIZE // VkDeviceSize range - }; - - VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, - &m_palette_buffer_view); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: "); - return false; - } - - return true; -} - -bool PaletteTextureConverter::CompileShaders() -{ - static const char PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE[] = R"( - layout(std140, push_constant) uniform PCBlock - { - float multiplier; - int texture_buffer_offset; - } PC; - - layout(set = 1, binding = 0) uniform sampler2DArray samp0; - layout(set = 0, binding = 0) uniform usamplerBuffer samp1; - - layout(location = 0) in vec3 f_uv0; - layout(location = 0) out vec4 ocol0; - - int Convert3To8(int v) - { - // Swizzle bits: 00000123 -> 12312312 - return (v << 5) | (v << 2) | (v >> 1); - } - int Convert4To8(int v) - { - // Swizzle bits: 00001234 -> 12341234 - return (v << 4) | v; - } - int Convert5To8(int v) - { - // Swizzle bits: 00012345 -> 12345123 - return (v << 3) | (v >> 2); - } - int Convert6To8(int v) - { - // Swizzle bits: 00123456 -> 12345612 - return (v << 2) | (v >> 4); - } - float4 DecodePixel_RGB5A3(int val) - { - int r,g,b,a; - if ((val&0x8000) > 0) - { - r=Convert5To8((val>>10) & 0x1f); - g=Convert5To8((val>>5 ) & 0x1f); - b=Convert5To8((val ) & 0x1f); - a=0xFF; - } - else - { - a=Convert3To8((val>>12) & 0x7); - r=Convert4To8((val>>8 ) & 0xf); - g=Convert4To8((val>>4 ) & 0xf); - b=Convert4To8((val ) & 0xf); - } - return float4(r, g, b, a) / 255.0; - } - float4 DecodePixel_RGB565(int val) - { - int r, g, b, a; - r = Convert5To8((val >> 11) & 0x1f); - g = Convert6To8((val >> 5) & 0x3f); - b = Convert5To8((val) & 0x1f); - a = 0xFF; - return float4(r, g, b, a) / 255.0; - } - float4 DecodePixel_IA8(int val) - { - int i = val & 0xFF; - int a = val >> 8; - return float4(i, i, i, a) / 255.0; - } - void main() - { - int src = int(round(texture(samp0, f_uv0).r * PC.multiplier)); - src = int(texelFetch(samp1, src + PC.texture_buffer_offset).r); - src = ((src << 8) & 0xFF00) | (src >> 8); - ocol0 = DECODE(src); - } - - )"; - - std::string palette_ia8_program = StringFromFormat("%s\n%s", "#define DECODE DecodePixel_IA8", - PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE); - std::string palette_rgb565_program = StringFromFormat( - "%s\n%s", "#define DECODE DecodePixel_RGB565", PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE); - std::string palette_rgb5a3_program = StringFromFormat( - "%s\n%s", "#define DECODE DecodePixel_RGB5A3", PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE); - - m_shaders[GX_TL_IA8] = Util::CompileAndCreateFragmentShader(palette_ia8_program); - m_shaders[GX_TL_RGB565] = Util::CompileAndCreateFragmentShader(palette_rgb565_program); - m_shaders[GX_TL_RGB5A3] = Util::CompileAndCreateFragmentShader(palette_rgb5a3_program); - - return (m_shaders[GX_TL_IA8] != VK_NULL_HANDLE && m_shaders[GX_TL_RGB565] != VK_NULL_HANDLE && - m_shaders[GX_TL_RGB5A3] != VK_NULL_HANDLE); -} - -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h deleted file mode 100644 index 668e35eae6..0000000000 --- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "Common/CommonTypes.h" -#include "VideoBackends/Vulkan/StreamBuffer.h" -#include "VideoCommon/TextureDecoder.h" - -namespace Vulkan -{ -class Texture2D; - -// Since this converter uses a uniform texel buffer, we can't use the general pipeline generators. - -class PaletteTextureConverter -{ -public: - PaletteTextureConverter(); - ~PaletteTextureConverter(); - - bool Initialize(); - - void ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass, - VkFramebuffer dst_framebuffer, Texture2D* src_texture, u32 width, u32 height, - void* palette, TlutFormat format, u32 src_format); - -private: - static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3; - - bool CreateBuffers(); - bool CompileShaders(); - - std::array m_shaders = {}; - - std::unique_ptr m_palette_stream_buffer; - VkBufferView m_palette_buffer_view = VK_NULL_HANDLE; - - std::unique_ptr m_uniform_buffer; -}; - -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 139221c9c0..3bb92fa3c2 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -17,13 +17,12 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/PaletteTextureConverter.h" #include "VideoBackends/Vulkan/Renderer.h" #include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" -#include "VideoBackends/Vulkan/TextureEncoder.h" +#include "VideoBackends/Vulkan/TextureConverter.h" #include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -66,17 +65,10 @@ bool TextureCache::Initialize() return false; } - m_texture_encoder = std::make_unique(); - if (!m_texture_encoder->Initialize()) + m_texture_converter = std::make_unique(); + if (!m_texture_converter->Initialize()) { - PanicAlert("Failed to initialize texture encoder."); - return false; - } - - m_palette_texture_converter = std::make_unique(); - if (!m_palette_texture_converter->Initialize()) - { - PanicAlert("Failed to initialize palette texture converter"); + PanicAlert("Failed to initialize texture converter"); return false; } @@ -112,7 +104,7 @@ void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer(); } - m_palette_texture_converter->ConvertTexture( + m_texture_converter->ConvertTexture( command_buffer, GetRenderPassForTextureUpdate(entry->GetTexture()), entry->GetFramebuffer(), unconverted->GetTexture(), entry->config.width, entry->config.height, palette, format, unconverted->format); @@ -156,9 +148,9 @@ void TextureCache::CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_ src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_texture_encoder->EncodeTextureToRam(src_texture->GetView(), dst, format, native_width, - bytes_per_row, num_blocks_y, memory_stride, src_format, - is_intensity, scale_by_half, src_rect); + m_texture_converter->EncodeTextureToMemory(src_texture->GetView(), dst, format, native_width, + bytes_per_row, num_blocks_y, memory_stride, src_format, + is_intensity, scale_by_half, src_rect); // Transition back to original state src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout); @@ -871,8 +863,8 @@ void TextureCache::EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 d src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Borrow framebuffer from EFB2RAM encoder. - Texture2D* encoding_texture = m_texture_encoder->GetEncodingTexture(); - StagingTexture2D* download_texture = m_texture_encoder->GetDownloadTexture(); + 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. @@ -882,10 +874,10 @@ void TextureCache::EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 d u32 output_width = dst_width / 2; UtilityShaderDraw draw( command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), - m_texture_encoder->GetEncodingRenderPass(), g_object_cache->GetPassthroughVertexShader(), + 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_encoder->GetEncodingTextureFramebuffer(), region); + 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(), @@ -942,7 +934,7 @@ void TextureCache::DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const v {src_width / 2, src_height, 1} // VkExtent3D imageExtent }; VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - Texture2D* intermediate_texture = m_texture_encoder->GetEncodingTexture(); + 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, @@ -955,7 +947,7 @@ void TextureCache::DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const v // 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_encoder->GetEncodingRenderPass(), g_object_cache->GetScreenQuadVertexShader(), + 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); diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index 5da9515991..74cd52f182 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -12,10 +12,9 @@ namespace Vulkan { -class PaletteTextureConverter; +class TextureConverter; class StateTracker; class Texture2D; -class TextureEncoder; class TextureCache : public TextureCacheBase { @@ -91,9 +90,7 @@ private: std::unique_ptr m_texture_upload_buffer; - std::unique_ptr m_texture_encoder; - - std::unique_ptr m_palette_texture_converter; + std::unique_ptr m_texture_converter; VkShaderModule m_copy_shader = VK_NULL_HANDLE; VkShaderModule m_efb_color_to_tex_shader = VK_NULL_HANDLE; diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp new file mode 100644 index 0000000000..902d3d118c --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -0,0 +1,481 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/Vulkan/TextureConverter.h" + +#include +#include +#include +#include + +#include "Common/Assert.h" +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" + +#include "VideoBackends/Vulkan/CommandBufferManager.h" +#include "VideoBackends/Vulkan/FramebufferManager.h" +#include "VideoBackends/Vulkan/ObjectCache.h" +#include "VideoBackends/Vulkan/Renderer.h" +#include "VideoBackends/Vulkan/StagingTexture2D.h" +#include "VideoBackends/Vulkan/StateTracker.h" +#include "VideoBackends/Vulkan/StreamBuffer.h" +#include "VideoBackends/Vulkan/Texture2D.h" +#include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VulkanContext.h" + +#include "VideoCommon/TextureConversionShader.h" +#include "VideoCommon/TextureDecoder.h" + +namespace Vulkan +{ +TextureConverter::TextureConverter() +{ +} + +TextureConverter::~TextureConverter() +{ + for (const auto& it : m_palette_conversion_shaders) + { + if (it != VK_NULL_HANDLE) + vkDestroyShaderModule(g_vulkan_context->GetDevice(), it, nullptr); + } + + if (m_palette_buffer_view != VK_NULL_HANDLE) + vkDestroyBufferView(g_vulkan_context->GetDevice(), m_palette_buffer_view, nullptr); + + if (m_encoding_render_pass != VK_NULL_HANDLE) + vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr); + + if (m_encoding_texture_framebuffer != VK_NULL_HANDLE) + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_texture_framebuffer, nullptr); + + for (VkShaderModule shader : m_texture_encoding_shaders) + { + if (shader != VK_NULL_HANDLE) + vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr); + } +} + +bool TextureConverter::Initialize() +{ + if (!CreateUniformBuffer()) + { + PanicAlert("Failed to create uniform buffer"); + return false; + } + + if (!CompilePaletteConversionShaders()) + { + PanicAlert("Failed to compile palette conversion shaders"); + return false; + } + + if (!CompileEncodingShaders()) + { + PanicAlert("Failed to compile texture encoding shaders"); + return false; + } + + if (!CreateEncodingRenderPass()) + { + PanicAlert("Failed to create encode render pass"); + return false; + } + + if (!CreateEncodingTexture()) + { + PanicAlert("Failed to create encoding texture"); + return false; + } + + if (!CreateDownloadTexture()) + { + PanicAlert("Failed to create download texture"); + return false; + } + + return true; +} + +void TextureConverter::ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass, + VkFramebuffer dst_framebuffer, Texture2D* src_texture, + u32 width, u32 height, void* palette, TlutFormat format, + u32 src_format) +{ + struct PSUniformBlock + { + float multiplier; + int texel_buffer_offset; + int pad[2]; + }; + + _assert_(static_cast(format) < NUM_PALETTE_CONVERSION_SHADERS); + + size_t palette_size = (src_format & 0xF) == GX_TF_I4 ? 32 : 512; + VkDescriptorSet texel_buffer_descriptor_set; + + // Allocate memory for the palette, and descriptor sets for the buffer. + // If any of these fail, execute a command buffer, and try again. + if (!m_palette_stream_buffer->ReserveMemory(palette_size, + g_vulkan_context->GetTexelBufferAlignment()) || + (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == + VK_NULL_HANDLE) + { + WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer"); + Util::ExecuteCurrentCommandsAndRestoreState(false); + + if (!m_palette_stream_buffer->ReserveMemory(palette_size, + g_vulkan_context->GetTexelBufferAlignment()) || + (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( + g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == + VK_NULL_HANDLE) + { + PanicAlert("Failed to allocate space for texture conversion"); + return; + } + } + + // Fill descriptor set #2 (texel buffer) + u32 palette_offset = static_cast(m_palette_stream_buffer->GetCurrentOffset()); + VkWriteDescriptorSet texel_set_write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + nullptr, + texel_buffer_descriptor_set, + 0, + 0, + 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + nullptr, + nullptr, + &m_palette_buffer_view}; + vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), 1, &texel_set_write, 0, nullptr); + + Util::BufferMemoryBarrier(command_buffer, m_palette_stream_buffer->GetBuffer(), + VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, palette_offset, + palette_size, VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + // Set up draw + UtilityShaderDraw draw(command_buffer, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), + render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, + m_palette_conversion_shaders[format]); + + VkRect2D region = {{0, 0}, {width, height}}; + draw.BeginRenderPass(dst_framebuffer, region); + + // Copy in palette + memcpy(m_palette_stream_buffer->GetCurrentHostPointer(), palette, palette_size); + m_palette_stream_buffer->CommitMemory(palette_size); + + // PS Uniforms/Samplers + PSUniformBlock uniforms = {}; + uniforms.multiplier = (src_format & 0xF) == GX_TF_I4 ? 15.0f : 255.0f; + uniforms.texel_buffer_offset = static_cast(palette_offset / sizeof(u16)); + draw.SetPushConstants(&uniforms, sizeof(uniforms)); + draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetPointSampler()); + + // We have to bind the texel buffer descriptor set separately. + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), + DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER, 1, + &texel_buffer_descriptor_set, 0, nullptr); + + // Draw + draw.SetViewportAndScissor(0, 0, width, height); + draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); + draw.EndRenderPass(); +} + +void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_ptr, u32 format, + u32 native_width, u32 bytes_per_row, u32 num_blocks_y, + u32 memory_stride, PEControl::PixelFormat src_format, + bool is_intensity, int scale_by_half, + const EFBRectangle& src_rect) +{ + if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE) + { + ERROR_LOG(VIDEO, "Missing encoding fragment shader for format %u", format); + return; + } + + // Can't do our own draw within a render pass. + StateTracker::GetInstance()->EndRenderPass(); + + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), + m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(), + VK_NULL_HANDLE, m_texture_encoding_shaders[format]); + + // Uniform - int4 of left,top,native_width,scale + s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast(native_width), + scale_by_half ? 2 : 1}; + draw.SetPushConstants(position_uniform, sizeof(position_uniform)); + + // Doesn't make sense to linear filter depth values + draw.SetPSSampler(0, src_texture, (scale_by_half && src_format != PEControl::Z24) ? + g_object_cache->GetLinearSampler() : + g_object_cache->GetPointSampler()); + + u32 render_width = bytes_per_row / sizeof(u32); + u32 render_height = num_blocks_y; + Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, render_width, + render_height); + + VkRect2D render_region = {{0, 0}, {render_width, render_height}}; + draw.BeginRenderPass(m_encoding_texture_framebuffer, render_region); + draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); + draw.EndRenderPass(); + + // Transition the image before copying + m_encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_download_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), + m_encoding_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, + render_width, render_height, 0, 0); + + // Block until the GPU has finished copying to the staging texture. + Util::ExecuteCurrentCommandsAndRestoreState(false, true); + + // Copy from staging texture to the final destination, adjusting pitch if necessary. + m_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, memory_stride); +} + +bool TextureConverter::CreateUniformBuffer() +{ + // TODO: Check against maximum size + static const size_t BUFFER_SIZE = 1024 * 1024; + + m_palette_stream_buffer = + StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, BUFFER_SIZE, BUFFER_SIZE); + if (!m_palette_stream_buffer) + return false; + + // Create a view of the whole buffer, we'll offset our texel load into it + VkBufferViewCreateInfo view_info = { + VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkBufferViewCreateFlags flags + m_palette_stream_buffer->GetBuffer(), // VkBuffer buffer + VK_FORMAT_R16_UINT, // VkFormat format + 0, // VkDeviceSize offset + BUFFER_SIZE // VkDeviceSize range + }; + + VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, + &m_palette_buffer_view); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: "); + return false; + } + + return true; +} + +bool TextureConverter::CompilePaletteConversionShaders() +{ + static const char PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE[] = R"( + layout(std140, push_constant) uniform PCBlock + { + float multiplier; + int texture_buffer_offset; + } PC; + + layout(set = 1, binding = 0) uniform sampler2DArray samp0; + layout(set = 0, binding = 0) uniform usamplerBuffer samp1; + + layout(location = 0) in vec3 f_uv0; + layout(location = 0) out vec4 ocol0; + + int Convert3To8(int v) + { + // Swizzle bits: 00000123 -> 12312312 + return (v << 5) | (v << 2) | (v >> 1); + } + int Convert4To8(int v) + { + // Swizzle bits: 00001234 -> 12341234 + return (v << 4) | v; + } + int Convert5To8(int v) + { + // Swizzle bits: 00012345 -> 12345123 + return (v << 3) | (v >> 2); + } + int Convert6To8(int v) + { + // Swizzle bits: 00123456 -> 12345612 + return (v << 2) | (v >> 4); + } + float4 DecodePixel_RGB5A3(int val) + { + int r,g,b,a; + if ((val&0x8000) > 0) + { + r=Convert5To8((val>>10) & 0x1f); + g=Convert5To8((val>>5 ) & 0x1f); + b=Convert5To8((val ) & 0x1f); + a=0xFF; + } + else + { + a=Convert3To8((val>>12) & 0x7); + r=Convert4To8((val>>8 ) & 0xf); + g=Convert4To8((val>>4 ) & 0xf); + b=Convert4To8((val ) & 0xf); + } + return float4(r, g, b, a) / 255.0; + } + float4 DecodePixel_RGB565(int val) + { + int r, g, b, a; + r = Convert5To8((val >> 11) & 0x1f); + g = Convert6To8((val >> 5) & 0x3f); + b = Convert5To8((val) & 0x1f); + a = 0xFF; + return float4(r, g, b, a) / 255.0; + } + float4 DecodePixel_IA8(int val) + { + int i = val & 0xFF; + int a = val >> 8; + return float4(i, i, i, a) / 255.0; + } + void main() + { + int src = int(round(texture(samp0, f_uv0).r * PC.multiplier)); + src = int(texelFetch(samp1, src + PC.texture_buffer_offset).r); + src = ((src << 8) & 0xFF00) | (src >> 8); + ocol0 = DECODE(src); + } + + )"; + + std::string palette_ia8_program = StringFromFormat("%s\n%s", "#define DECODE DecodePixel_IA8", + PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE); + std::string palette_rgb565_program = StringFromFormat( + "%s\n%s", "#define DECODE DecodePixel_RGB565", PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE); + std::string palette_rgb5a3_program = StringFromFormat( + "%s\n%s", "#define DECODE DecodePixel_RGB5A3", PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE); + + m_palette_conversion_shaders[GX_TL_IA8] = + Util::CompileAndCreateFragmentShader(palette_ia8_program); + m_palette_conversion_shaders[GX_TL_RGB565] = + Util::CompileAndCreateFragmentShader(palette_rgb565_program); + m_palette_conversion_shaders[GX_TL_RGB5A3] = + Util::CompileAndCreateFragmentShader(palette_rgb5a3_program); + + return m_palette_conversion_shaders[GX_TL_IA8] != VK_NULL_HANDLE && + m_palette_conversion_shaders[GX_TL_RGB565] != VK_NULL_HANDLE && + m_palette_conversion_shaders[GX_TL_RGB5A3] != VK_NULL_HANDLE; +} + +bool TextureConverter::CompileEncodingShaders() +{ + // Texture encoding shaders + static const u32 texture_encoding_shader_formats[] = { + GX_TF_I4, GX_TF_I8, GX_TF_IA4, GX_TF_IA8, GX_TF_RGB565, GX_TF_RGB5A3, GX_TF_RGBA8, + GX_CTF_R4, GX_CTF_RA4, GX_CTF_RA8, GX_CTF_A8, GX_CTF_R8, GX_CTF_G8, GX_CTF_B8, + GX_CTF_RG8, GX_CTF_GB8, GX_CTF_Z8H, GX_TF_Z8, GX_CTF_Z16R, GX_TF_Z16, GX_TF_Z24X8, + GX_CTF_Z4, GX_CTF_Z8M, GX_CTF_Z8L, GX_CTF_Z16L}; + for (u32 format : texture_encoding_shader_formats) + { + const char* shader_source = + TextureConversionShader::GenerateEncodingShader(format, APIType::Vulkan); + m_texture_encoding_shaders[format] = Util::CompileAndCreateFragmentShader(shader_source); + if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE) + return false; + } + + return true; +} + +bool TextureConverter::CreateEncodingRenderPass() +{ + VkAttachmentDescription attachments[] = { + {0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL}}; + + VkAttachmentReference color_attachment_references[] = { + {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; + + VkSubpassDescription subpass_descriptions[] = {{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, + color_attachment_references, nullptr, nullptr, 0, + nullptr}}; + + VkSubpassDependency dependancies[] = { + {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}}; + + VkRenderPassCreateInfo pass_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + nullptr, + 0, + static_cast(ArraySize(attachments)), + attachments, + static_cast(ArraySize(subpass_descriptions)), + subpass_descriptions, + static_cast(ArraySize(dependancies)), + dependancies}; + + VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr, + &m_encoding_render_pass); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateRenderPass (Encode) failed: "); + return false; + } + + return true; +} + +bool TextureConverter::CreateEncodingTexture() +{ + m_encoding_texture = Texture2D::Create( + ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT, + VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + if (!m_encoding_texture) + return false; + + VkImageView framebuffer_attachments[] = {m_encoding_texture->GetView()}; + VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + nullptr, + 0, + m_encoding_render_pass, + static_cast(ArraySize(framebuffer_attachments)), + framebuffer_attachments, + m_encoding_texture->GetWidth(), + m_encoding_texture->GetHeight(), + m_encoding_texture->GetLayers()}; + + VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, + &m_encoding_texture_framebuffer); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); + return false; + } + + return true; +} + +bool TextureConverter::CreateDownloadTexture() +{ + m_download_texture = + StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH, + ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT); + + if (!m_download_texture || !m_download_texture->Map()) + return false; + + return true; +} + +} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureEncoder.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h similarity index 54% rename from Source/Core/VideoBackends/Vulkan/TextureEncoder.h rename to Source/Core/VideoBackends/Vulkan/TextureConverter.h index c3fa68c752..3dcf4683ef 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureEncoder.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -8,8 +8,9 @@ #include #include "Common/CommonTypes.h" -#include "VideoBackends/Vulkan/VulkanLoader.h" +#include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoCommon/BPMemory.h" +#include "VideoCommon/TextureDecoder.h" #include "VideoCommon/VideoCommon.h" namespace Vulkan @@ -17,11 +18,11 @@ namespace Vulkan class StagingTexture2D; class Texture2D; -class TextureEncoder +class TextureConverter { public: - TextureEncoder(); - ~TextureEncoder(); + TextureConverter(); + ~TextureConverter(); VkRenderPass GetEncodingRenderPass() const { return m_encoding_render_pass; } Texture2D* GetEncodingTexture() const { return m_encoding_texture.get(); } @@ -29,26 +30,38 @@ public: StagingTexture2D* GetDownloadTexture() const { return m_download_texture.get(); } bool Initialize(); + void ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass, + VkFramebuffer dst_framebuffer, Texture2D* src_texture, u32 width, u32 height, + void* palette, TlutFormat format, u32 src_format); + // Uses an encoding shader to copy src_texture to dest_ptr. - // Assumes that no render pass is currently in progress. - // WARNING: Executes the current command buffer. - void EncodeTextureToRam(VkImageView src_texture, u8* dest_ptr, u32 format, u32 native_width, - u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, - PEControl::PixelFormat src_format, bool is_intensity, int scale_by_half, - const EFBRectangle& source); + // NOTE: Executes the current command buffer. + void EncodeTextureToMemory(VkImageView src_texture, u8* dest_ptr, u32 format, u32 native_width, + u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, + PEControl::PixelFormat src_format, bool is_intensity, + int scale_by_half, const EFBRectangle& source); private: - // From OGL. static const u32 NUM_TEXTURE_ENCODING_SHADERS = 64; static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4; static const u32 ENCODING_TEXTURE_HEIGHT = 1024; static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM; + static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3; - bool CompileShaders(); + bool CreateUniformBuffer(); + bool CompilePaletteConversionShaders(); + bool CompileEncodingShaders(); bool CreateEncodingRenderPass(); bool CreateEncodingTexture(); bool CreateDownloadTexture(); + std::array m_palette_conversion_shaders = {}; + + std::unique_ptr m_palette_stream_buffer; + VkBufferView m_palette_buffer_view = VK_NULL_HANDLE; + + std::unique_ptr m_uniform_buffer; + std::array m_texture_encoding_shaders = {}; VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE; diff --git a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp deleted file mode 100644 index 08948d97cb..0000000000 --- a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "VideoBackends/Vulkan/TextureEncoder.h" - -#include -#include - -#include "Common/CommonFuncs.h" -#include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" - -#include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/Renderer.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" -#include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/Texture2D.h" -#include "VideoBackends/Vulkan/Util.h" -#include "VideoBackends/Vulkan/VulkanContext.h" - -#include "VideoCommon/TextureConversionShader.h" -#include "VideoCommon/TextureDecoder.h" - -namespace Vulkan -{ -TextureEncoder::TextureEncoder() -{ -} - -TextureEncoder::~TextureEncoder() -{ - if (m_encoding_render_pass != VK_NULL_HANDLE) - vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr); - - if (m_encoding_texture_framebuffer != VK_NULL_HANDLE) - vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_texture_framebuffer, nullptr); - - for (VkShaderModule shader : m_texture_encoding_shaders) - { - if (shader != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr); - } -} - -bool TextureEncoder::Initialize() -{ - if (!CompileShaders()) - { - PanicAlert("Failed to compile shaders"); - return false; - } - - if (!CreateEncodingRenderPass()) - { - PanicAlert("Failed to create encode render pass"); - return false; - } - - if (!CreateEncodingTexture()) - { - PanicAlert("Failed to create encoding texture"); - return false; - } - - if (!CreateDownloadTexture()) - { - PanicAlert("Failed to create download texture"); - return false; - } - - return true; -} - -void TextureEncoder::EncodeTextureToRam(VkImageView src_texture, u8* dest_ptr, u32 format, - u32 native_width, u32 bytes_per_row, u32 num_blocks_y, - u32 memory_stride, PEControl::PixelFormat src_format, - bool is_intensity, int scale_by_half, - const EFBRectangle& src_rect) -{ - if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE) - { - ERROR_LOG(VIDEO, "Missing encoding fragment shader for format %u", format); - return; - } - - // Can't do our own draw within a render pass. - StateTracker::GetInstance()->EndRenderPass(); - - UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), - m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(), - VK_NULL_HANDLE, m_texture_encoding_shaders[format]); - - // Uniform - int4 of left,top,native_width,scale - s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast(native_width), - scale_by_half ? 2 : 1}; - draw.SetPushConstants(position_uniform, sizeof(position_uniform)); - - // Doesn't make sense to linear filter depth values - draw.SetPSSampler(0, src_texture, (scale_by_half && src_format != PEControl::Z24) ? - g_object_cache->GetLinearSampler() : - g_object_cache->GetPointSampler()); - - u32 render_width = bytes_per_row / sizeof(u32); - u32 render_height = num_blocks_y; - Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, render_width, - render_height); - - // TODO: We could use compute shaders here. - VkRect2D render_region = {{0, 0}, {render_width, render_height}}; - draw.BeginRenderPass(m_encoding_texture_framebuffer, render_region); - draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); - draw.EndRenderPass(); - - // Transition the image before copying - m_encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_download_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - m_encoding_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, - render_width, render_height, 0, 0); - - // Block until the GPU has finished copying to the staging texture. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Copy from staging texture to the final destination, adjusting pitch if necessary. - m_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, memory_stride); -} - -bool TextureEncoder::CompileShaders() -{ - // Texture encoding shaders - static const u32 texture_encoding_shader_formats[] = { - GX_TF_I4, GX_TF_I8, GX_TF_IA4, GX_TF_IA8, GX_TF_RGB565, GX_TF_RGB5A3, GX_TF_RGBA8, - GX_CTF_R4, GX_CTF_RA4, GX_CTF_RA8, GX_CTF_A8, GX_CTF_R8, GX_CTF_G8, GX_CTF_B8, - GX_CTF_RG8, GX_CTF_GB8, GX_CTF_Z8H, GX_TF_Z8, GX_CTF_Z16R, GX_TF_Z16, GX_TF_Z24X8, - GX_CTF_Z4, GX_CTF_Z8M, GX_CTF_Z8L, GX_CTF_Z16L}; - for (u32 format : texture_encoding_shader_formats) - { - const char* shader_source = - TextureConversionShader::GenerateEncodingShader(format, APIType::Vulkan); - m_texture_encoding_shaders[format] = Util::CompileAndCreateFragmentShader(shader_source); - if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE) - return false; - } - - return true; -} - -bool TextureEncoder::CreateEncodingRenderPass() -{ - VkAttachmentDescription attachments[] = { - {0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL}}; - - VkAttachmentReference color_attachment_references[] = { - {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; - - VkSubpassDescription subpass_descriptions[] = {{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, - color_attachment_references, nullptr, nullptr, 0, - nullptr}}; - - VkSubpassDependency dependancies[] = { - {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}}; - - VkRenderPassCreateInfo pass_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - nullptr, - 0, - static_cast(ArraySize(attachments)), - attachments, - static_cast(ArraySize(subpass_descriptions)), - subpass_descriptions, - static_cast(ArraySize(dependancies)), - dependancies}; - - VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr, - &m_encoding_render_pass); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateRenderPass (Encode) failed: "); - return false; - } - - return true; -} - -bool TextureEncoder::CreateEncodingTexture() -{ - // From OGL: Why do we create a 1024 height texture? - m_encoding_texture = Texture2D::Create( - ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT, - VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - if (!m_encoding_texture) - return false; - - VkImageView framebuffer_attachments[] = {m_encoding_texture->GetView()}; - VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - m_encoding_render_pass, - static_cast(ArraySize(framebuffer_attachments)), - framebuffer_attachments, - m_encoding_texture->GetWidth(), - m_encoding_texture->GetHeight(), - m_encoding_texture->GetLayers()}; - - VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &m_encoding_texture_framebuffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); - return false; - } - - return true; -} - -bool TextureEncoder::CreateDownloadTexture() -{ - m_download_texture = - StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH, - ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT); - - if (!m_download_texture || !m_download_texture->Map()) - return false; - - return true; -} - -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj index 64ac839164..c7bdada31b 100644 --- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj +++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj @@ -53,12 +53,11 @@ - + - @@ -78,11 +77,10 @@ - + - @@ -116,4 +114,4 @@ - + \ No newline at end of file From d67463e0a7570aeb00d2d10e21987fb639b78731 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 20 Nov 2016 00:08:24 +1000 Subject: [PATCH 06/10] Vulkan: Simplify palette texture conversion --- Source/Core/VideoBackends/Vulkan/Constants.h | 3 + .../VideoBackends/Vulkan/TextureCache.cpp | 21 +- .../VideoBackends/Vulkan/TextureConverter.cpp | 192 +++++++++--------- .../VideoBackends/Vulkan/TextureConverter.h | 43 ++-- 4 files changed, 120 insertions(+), 139 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index 229168c9e7..e5c774cfc5 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -107,6 +107,9 @@ constexpr size_t STAGING_TEXTURE_UPLOAD_THRESHOLD = 1024 * 1024 * 4; constexpr size_t INITIAL_UNIFORM_STREAM_BUFFER_SIZE = 16 * 1024 * 1024; constexpr size_t MAXIMUM_UNIFORM_STREAM_BUFFER_SIZE = 32 * 1024 * 1024; +// Texel buffer size for palette and texture decoding. +constexpr size_t TEXTURE_CONVERSION_TEXEL_BUFFER_SIZE = 8 * 1024 * 1024; + // Push constant buffer size for utility shaders constexpr u32 PUSH_CONSTANT_BUFFER_SIZE = 128; diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 3bb92fa3c2..801a967630 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -86,28 +86,9 @@ void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* { TCacheEntry* entry = static_cast(base_entry); TCacheEntry* unconverted = static_cast(base_unconverted); - _assert_(entry->config.rendertarget); - - // EFB copies can be used as paletted textures as well. For these, we can't assume them to be - // contain the correct data before the frame begins (when the init command buffer is executed), - // so we must convert them at the appropriate time, during the drawing command buffer. - VkCommandBuffer command_buffer; - if (unconverted->IsEfbCopy()) - { - command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - StateTracker::GetInstance()->EndRenderPass(); - StateTracker::GetInstance()->SetPendingRebind(); - } - else - { - // Use initialization command buffer and perform conversion before the drawing commands. - command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer(); - } m_texture_converter->ConvertTexture( - command_buffer, GetRenderPassForTextureUpdate(entry->GetTexture()), entry->GetFramebuffer(), - unconverted->GetTexture(), entry->config.width, entry->config.height, palette, format, - unconverted->format); + entry, unconverted, GetRenderPassForTextureUpdate(entry->GetTexture()), palette, format); // Render pass transitions to SHADER_READ_ONLY. entry->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index 902d3d118c..690c0decf2 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -42,16 +42,16 @@ TextureConverter::~TextureConverter() vkDestroyShaderModule(g_vulkan_context->GetDevice(), it, nullptr); } - if (m_palette_buffer_view != VK_NULL_HANDLE) - vkDestroyBufferView(g_vulkan_context->GetDevice(), m_palette_buffer_view, nullptr); + if (m_texel_buffer_view_r16_uint != VK_NULL_HANDLE) + vkDestroyBufferView(g_vulkan_context->GetDevice(), m_texel_buffer_view_r16_uint, nullptr); if (m_encoding_render_pass != VK_NULL_HANDLE) vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr); - if (m_encoding_texture_framebuffer != VK_NULL_HANDLE) - vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_texture_framebuffer, nullptr); + if (m_encoding_render_framebuffer != VK_NULL_HANDLE) + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr); - for (VkShaderModule shader : m_texture_encoding_shaders) + for (VkShaderModule shader : m_encoding_shaders) { if (shader != VK_NULL_HANDLE) vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr); @@ -60,7 +60,7 @@ TextureConverter::~TextureConverter() bool TextureConverter::Initialize() { - if (!CreateUniformBuffer()) + if (!CreateTexelBuffer()) { PanicAlert("Failed to create uniform buffer"); return false; @@ -90,7 +90,7 @@ bool TextureConverter::Initialize() return false; } - if (!CreateDownloadTexture()) + if (!CreateEncodingDownloadTexture()) { PanicAlert("Failed to create download texture"); return false; @@ -99,10 +99,10 @@ bool TextureConverter::Initialize() return true; } -void TextureConverter::ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass, - VkFramebuffer dst_framebuffer, Texture2D* src_texture, - u32 width, u32 height, void* palette, TlutFormat format, - u32 src_format) +void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, + TextureCache::TCacheEntry* src_entry, + VkRenderPass render_pass, const void* palette, + TlutFormat palette_format) { struct PSUniformBlock { @@ -111,80 +111,65 @@ void TextureConverter::ConvertTexture(VkCommandBuffer command_buffer, VkRenderPa int pad[2]; }; - _assert_(static_cast(format) < NUM_PALETTE_CONVERSION_SHADERS); + _assert_(static_cast(palette_format) < NUM_PALETTE_CONVERSION_SHADERS); + _assert_(dst_entry->config.rendertarget); - size_t palette_size = (src_format & 0xF) == GX_TF_I4 ? 32 : 512; - VkDescriptorSet texel_buffer_descriptor_set; + // We want to align to 2 bytes (R16) or the device's texel buffer alignment, whichever is greater. + VkDeviceSize texel_buffer_alignment = + std::min(g_vulkan_context->GetTexelBufferAlignment(), sizeof(u16)); + size_t palette_size = (src_entry->format & 0xF) == GX_TF_I4 ? 32 : 512; // Allocate memory for the palette, and descriptor sets for the buffer. // If any of these fail, execute a command buffer, and try again. - if (!m_palette_stream_buffer->ReserveMemory(palette_size, - g_vulkan_context->GetTexelBufferAlignment()) || - (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == - VK_NULL_HANDLE) + if (!m_texel_buffer->ReserveMemory(palette_size, texel_buffer_alignment)) { WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer"); Util::ExecuteCurrentCommandsAndRestoreState(false); - if (!m_palette_stream_buffer->ReserveMemory(palette_size, - g_vulkan_context->GetTexelBufferAlignment()) || - (texel_buffer_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS))) == - VK_NULL_HANDLE) + if (!m_texel_buffer->ReserveMemory(palette_size, texel_buffer_alignment)) { PanicAlert("Failed to allocate space for texture conversion"); return; } } - // Fill descriptor set #2 (texel buffer) - u32 palette_offset = static_cast(m_palette_stream_buffer->GetCurrentOffset()); - VkWriteDescriptorSet texel_set_write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - texel_buffer_descriptor_set, - 0, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, - nullptr, - nullptr, - &m_palette_buffer_view}; - vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), 1, &texel_set_write, 0, nullptr); + // Copy in palette to texel buffer. + u32 palette_offset = static_cast(m_texel_buffer->GetCurrentOffset()); + memcpy(m_texel_buffer->GetCurrentHostPointer(), palette, palette_size); + m_texel_buffer->CommitMemory(palette_size); - Util::BufferMemoryBarrier(command_buffer, m_palette_stream_buffer->GetBuffer(), - VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, palette_offset, - palette_size, VK_PIPELINE_STAGE_HOST_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + // EFB copies can be used as paletted textures as well. For these, we can't assume them to be + // contain the correct data before the frame begins (when the init command buffer is executed), + // so we must convert them at the appropriate time, during the drawing command buffer. + VkCommandBuffer command_buffer; + if (src_entry->IsEfbCopy()) + { + command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); + StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->SetPendingRebind(); + } + else + { + // Use initialization command buffer and perform conversion before the drawing commands. + command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer(); + } - // Set up draw + // Bind and draw to the destination. UtilityShaderDraw draw(command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, - m_palette_conversion_shaders[format]); + m_palette_conversion_shaders[palette_format]); - VkRect2D region = {{0, 0}, {width, height}}; - draw.BeginRenderPass(dst_framebuffer, region); + VkRect2D region = {{0, 0}, {dst_entry->config.width, dst_entry->config.height}}; + draw.BeginRenderPass(dst_entry->GetFramebuffer(), region); - // Copy in palette - memcpy(m_palette_stream_buffer->GetCurrentHostPointer(), palette, palette_size); - m_palette_stream_buffer->CommitMemory(palette_size); - - // PS Uniforms/Samplers PSUniformBlock uniforms = {}; - uniforms.multiplier = (src_format & 0xF) == GX_TF_I4 ? 15.0f : 255.0f; + uniforms.multiplier = (src_entry->format & 0xF) == GX_TF_I4 ? 15.0f : 255.0f; uniforms.texel_buffer_offset = static_cast(palette_offset / sizeof(u16)); draw.SetPushConstants(&uniforms, sizeof(uniforms)); - draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetPointSampler()); - - // We have to bind the texel buffer descriptor set separately. - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), - DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER, 1, - &texel_buffer_descriptor_set, 0, nullptr); - - // Draw - draw.SetViewportAndScissor(0, 0, width, height); + draw.SetPSSampler(0, src_entry->GetTexture()->GetView(), g_object_cache->GetPointSampler()); + draw.SetPSTexelBuffer(m_texel_buffer_view_r16_uint); + draw.SetViewportAndScissor(0, 0, dst_entry->config.width, dst_entry->config.height); draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); draw.EndRenderPass(); } @@ -195,7 +180,7 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p bool is_intensity, int scale_by_half, const EFBRectangle& src_rect) { - if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE) + if (m_encoding_shaders[format] == VK_NULL_HANDLE) { ERROR_LOG(VIDEO, "Missing encoding fragment shader for format %u", format); return; @@ -207,7 +192,7 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(), - VK_NULL_HANDLE, m_texture_encoding_shaders[format]); + VK_NULL_HANDLE, m_encoding_shaders[format]); // Uniform - int4 of left,top,native_width,scale s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast(native_width), @@ -225,53 +210,65 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p render_height); VkRect2D render_region = {{0, 0}, {render_width, render_height}}; - draw.BeginRenderPass(m_encoding_texture_framebuffer, render_region); + draw.BeginRenderPass(m_encoding_render_framebuffer, render_region); draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); draw.EndRenderPass(); // Transition the image before copying - m_encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_download_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - m_encoding_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, - render_width, render_height, 0, 0); + m_encoding_render_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_encoding_download_texture->CopyFromImage( + g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(), + VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0); // Block until the GPU has finished copying to the staging texture. Util::ExecuteCurrentCommandsAndRestoreState(false, true); // Copy from staging texture to the final destination, adjusting pitch if necessary. - m_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, memory_stride); + m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, + memory_stride); } -bool TextureConverter::CreateUniformBuffer() +bool TextureConverter::CreateTexelBuffer() { - // TODO: Check against maximum size - static const size_t BUFFER_SIZE = 1024 * 1024; + // Prefer an 8MB buffer if possible, but use less if the device doesn't support this. + // This buffer is potentially going to be addressed as R8s in the future, so we assume + // that one element is one byte. + m_texel_buffer_size = + std::min(TEXTURE_CONVERSION_TEXEL_BUFFER_SIZE, + static_cast(g_vulkan_context->GetDeviceLimits().maxTexelBufferElements)); - m_palette_stream_buffer = - StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, BUFFER_SIZE, BUFFER_SIZE); - if (!m_palette_stream_buffer) + m_texel_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, + m_texel_buffer_size, m_texel_buffer_size); + if (!m_texel_buffer) return false; + // Create views of the formats that we will be using. + m_texel_buffer_view_r16_uint = CreateTexelBufferView(VK_FORMAT_R16_UINT); + return m_texel_buffer_view_r16_uint != VK_NULL_HANDLE; +} + +VkBufferView TextureConverter::CreateTexelBufferView(VkFormat format) const +{ // Create a view of the whole buffer, we'll offset our texel load into it VkBufferViewCreateInfo view_info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType nullptr, // const void* pNext 0, // VkBufferViewCreateFlags flags - m_palette_stream_buffer->GetBuffer(), // VkBuffer buffer - VK_FORMAT_R16_UINT, // VkFormat format + m_texel_buffer->GetBuffer(), // VkBuffer buffer + format, // VkFormat format 0, // VkDeviceSize offset - BUFFER_SIZE // VkDeviceSize range + m_texel_buffer_size // VkDeviceSize range }; - VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, - &m_palette_buffer_view); + VkBufferView view; + VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: "); - return false; + return VK_NULL_HANDLE; } - return true; + return view; } bool TextureConverter::CompilePaletteConversionShaders() @@ -283,8 +280,8 @@ bool TextureConverter::CompilePaletteConversionShaders() int texture_buffer_offset; } PC; - layout(set = 1, binding = 0) uniform sampler2DArray samp0; - layout(set = 0, binding = 0) uniform usamplerBuffer samp1; + SAMPLER_BINDING(0) uniform sampler2DArray samp0; + TEXEL_BUFFER_BINDING(0) uniform usamplerBuffer samp1; layout(location = 0) in vec3 f_uv0; layout(location = 0) out vec4 ocol0; @@ -384,8 +381,8 @@ bool TextureConverter::CompileEncodingShaders() { const char* shader_source = TextureConversionShader::GenerateEncodingShader(format, APIType::Vulkan); - m_texture_encoding_shaders[format] = Util::CompileAndCreateFragmentShader(shader_source); - if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE) + m_encoding_shaders[format] = Util::CompileAndCreateFragmentShader(shader_source); + if (m_encoding_shaders[format] == VK_NULL_HANDLE) return false; } @@ -436,27 +433,27 @@ bool TextureConverter::CreateEncodingRenderPass() bool TextureConverter::CreateEncodingTexture() { - m_encoding_texture = Texture2D::Create( + m_encoding_render_texture = Texture2D::Create( ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - if (!m_encoding_texture) + if (!m_encoding_render_texture) return false; - VkImageView framebuffer_attachments[] = {m_encoding_texture->GetView()}; + VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()}; VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, m_encoding_render_pass, static_cast(ArraySize(framebuffer_attachments)), framebuffer_attachments, - m_encoding_texture->GetWidth(), - m_encoding_texture->GetHeight(), - m_encoding_texture->GetLayers()}; + m_encoding_render_texture->GetWidth(), + m_encoding_render_texture->GetHeight(), + m_encoding_render_texture->GetLayers()}; VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &m_encoding_texture_framebuffer); + &m_encoding_render_framebuffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); @@ -466,16 +463,13 @@ bool TextureConverter::CreateEncodingTexture() return true; } -bool TextureConverter::CreateDownloadTexture() +bool TextureConverter::CreateEncodingDownloadTexture() { - m_download_texture = + m_encoding_download_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT); - if (!m_download_texture || !m_download_texture->Map()) - return false; - - return true; + return m_encoding_download_texture && m_encoding_download_texture->Map(); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h index 3dcf4683ef..27dd2a7e33 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -9,6 +9,7 @@ #include "Common/CommonTypes.h" #include "VideoBackends/Vulkan/StreamBuffer.h" +#include "VideoBackends/Vulkan/TextureCache.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/VideoCommon.h" @@ -25,14 +26,14 @@ public: ~TextureConverter(); VkRenderPass GetEncodingRenderPass() const { return m_encoding_render_pass; } - Texture2D* GetEncodingTexture() const { return m_encoding_texture.get(); } - VkFramebuffer GetEncodingTextureFramebuffer() const { return m_encoding_texture_framebuffer; } - StagingTexture2D* GetDownloadTexture() const { return m_download_texture.get(); } + 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(); - void ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass, - VkFramebuffer dst_framebuffer, Texture2D* src_texture, u32 width, u32 height, - void* palette, TlutFormat format, u32 src_format); + // Applies palette to dst_entry, using indices from src_entry. + void ConvertTexture(TextureCache::TCacheEntry* dst_entry, TextureCache::TCacheEntry* src_entry, + VkRenderPass render_pass, const void* palette, TlutFormat palette_format); // Uses an encoding shader to copy src_texture to dest_ptr. // NOTE: Executes the current command buffer. @@ -48,28 +49,30 @@ private: static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM; static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3; - bool CreateUniformBuffer(); + bool CreateTexelBuffer(); + VkBufferView CreateTexelBufferView(VkFormat format) const; + bool CompilePaletteConversionShaders(); + bool CompileEncodingShaders(); bool CreateEncodingRenderPass(); bool CreateEncodingTexture(); - bool CreateDownloadTexture(); + bool CreateEncodingDownloadTexture(); + // Shared between conversion types + std::unique_ptr m_texel_buffer; + VkBufferView m_texel_buffer_view_r16_uint = VK_NULL_HANDLE; + size_t m_texel_buffer_size = 0; + + // Palette conversion - taking an indexed texture and applying palette std::array m_palette_conversion_shaders = {}; - std::unique_ptr m_palette_stream_buffer; - VkBufferView m_palette_buffer_view = VK_NULL_HANDLE; - - std::unique_ptr m_uniform_buffer; - - std::array m_texture_encoding_shaders = {}; - + // Texture encoding - RGBA8->GX format in memory + std::array m_encoding_shaders = {}; VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE; - - std::unique_ptr m_encoding_texture; - VkFramebuffer m_encoding_texture_framebuffer = VK_NULL_HANDLE; - - std::unique_ptr m_download_texture; + std::unique_ptr m_encoding_render_texture; + VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE; + std::unique_ptr m_encoding_download_texture; }; } // namespace Vulkan From 804cd0ff03ad6b940f6cd415b29a60263c8c9042 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 20 Nov 2016 00:43:50 +1000 Subject: [PATCH 07/10] 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 From 58978c14405ed2e9025987b5cd2496b04334abfd Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 20 Nov 2016 01:16:27 +1000 Subject: [PATCH 08/10] Vulkan: Faster path for decoding XFB data Using a texel buffer as the copy destination removes the need to copy to an intermediate texture first. --- .../Core/VideoBackends/Vulkan/TextureCache.h | 1 - .../VideoBackends/Vulkan/TextureConverter.cpp | 175 ++++++++++-------- .../VideoBackends/Vulkan/TextureConverter.h | 12 ++ 3 files changed, 111 insertions(+), 77 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index 327a9bcaf3..f59a66e063 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -48,7 +48,6 @@ public: static TextureCache* GetInstance(); - StreamBuffer* GetUploadBuffer() const { return m_texture_upload_buffer.get(); } TextureConverter* GetTextureConverter() const { return m_texture_converter.get(); } bool Initialize(); diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index 1a701611ea..51f668f583 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -44,6 +44,8 @@ TextureConverter::~TextureConverter() if (m_texel_buffer_view_r16_uint != VK_NULL_HANDLE) vkDestroyBufferView(g_vulkan_context->GetDevice(), m_texel_buffer_view_r16_uint, nullptr); + if (m_texel_buffer_view_rgba8_unorm != VK_NULL_HANDLE) + vkDestroyBufferView(g_vulkan_context->GetDevice(), m_texel_buffer_view_rgba8_unorm, nullptr); if (m_encoding_render_pass != VK_NULL_HANDLE) vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr); @@ -110,6 +112,48 @@ bool TextureConverter::Initialize() return true; } +bool TextureConverter::ReserveTexelBufferStorage(size_t size, size_t alignment) +{ + // Enforce the minimum alignment for texture buffers on the device. + size_t actual_alignment = + std::max(static_cast(g_vulkan_context->GetTexelBufferAlignment()), alignment); + if (m_texel_buffer->ReserveMemory(size, actual_alignment)) + return true; + + WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer"); + Util::ExecuteCurrentCommandsAndRestoreState(false); + + // This next call should never fail, since a command buffer is now in-flight and we can + // wait on the fence for the GPU to finish. If this returns false, it's probably because + // the device has been lost, which is fatal anyway. + if (!m_texel_buffer->ReserveMemory(size, actual_alignment)) + { + PanicAlert("Failed to allocate space for texture conversion"); + return false; + } + + return true; +} + +VkCommandBuffer +TextureConverter::GetCommandBufferForTextureConversion(const TextureCache::TCacheEntry* src_entry) +{ + // EFB copies can be used as paletted textures as well. For these, we can't assume them to be + // contain the correct data before the frame begins (when the init command buffer is executed), + // so we must convert them at the appropriate time, during the drawing command buffer. + if (src_entry->IsEfbCopy()) + { + StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->SetPendingRebind(); + return g_command_buffer_mgr->GetCurrentCommandBuffer(); + } + else + { + // Use initialization command buffer and perform conversion before the drawing commands. + return g_command_buffer_mgr->GetCurrentInitCommandBuffer(); + } +} + void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, TextureCache::TCacheEntry* src_entry, VkRenderPass render_pass, const void* palette, @@ -126,44 +170,16 @@ void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, _assert_(dst_entry->config.rendertarget); // We want to align to 2 bytes (R16) or the device's texel buffer alignment, whichever is greater. - VkDeviceSize texel_buffer_alignment = - std::min(g_vulkan_context->GetTexelBufferAlignment(), sizeof(u16)); size_t palette_size = (src_entry->format & 0xF) == GX_TF_I4 ? 32 : 512; - - // Allocate memory for the palette, and descriptor sets for the buffer. - // If any of these fail, execute a command buffer, and try again. - if (!m_texel_buffer->ReserveMemory(palette_size, texel_buffer_alignment)) - { - WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); - - if (!m_texel_buffer->ReserveMemory(palette_size, texel_buffer_alignment)) - { - PanicAlert("Failed to allocate space for texture conversion"); - return; - } - } + if (!ReserveTexelBufferStorage(palette_size, sizeof(u16))) + return; // Copy in palette to texel buffer. u32 palette_offset = static_cast(m_texel_buffer->GetCurrentOffset()); memcpy(m_texel_buffer->GetCurrentHostPointer(), palette, palette_size); m_texel_buffer->CommitMemory(palette_size); - // EFB copies can be used as paletted textures as well. For these, we can't assume them to be - // contain the correct data before the frame begins (when the init command buffer is executed), - // so we must convert them at the appropriate time, during the drawing command buffer. - VkCommandBuffer command_buffer; - if (src_entry->IsEfbCopy()) - { - command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - StateTracker::GetInstance()->EndRenderPass(); - StateTracker::GetInstance()->SetPendingRebind(); - } - else - { - // Use initialization command buffer and perform conversion before the drawing commands. - command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer(); - } + VkCommandBuffer command_buffer = GetCommandBufferForTextureConversion(src_entry); // Bind and draw to the destination. UtilityShaderDraw draw(command_buffer, @@ -290,57 +306,58 @@ void TextureConverter::DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* ds 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())) + // Pack each row without any padding in the texel buffer. + size_t upload_stride = src_width * sizeof(u16); + size_t upload_size = upload_stride * src_height; + + // Reserve space in the texel buffer for storing the raw image. + if (!ReserveTexelBufferStorage(upload_size, sizeof(u16))) + return; + + // Handle pitch differences here. + if (src_stride != upload_stride) { - // 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"); + const u8* src_row_ptr = reinterpret_cast(src_ptr); + u8* dst_row_ptr = m_texel_buffer->GetCurrentHostPointer(); + size_t copy_size = std::min(upload_stride, static_cast(src_stride)); + for (u32 row = 0; row < src_height; row++) + { + std::memcpy(dst_row_ptr, src_row_ptr, copy_size); + src_row_ptr += src_stride; + dst_row_ptr += upload_stride; + } + } + else + { + std::memcpy(m_texel_buffer->GetCurrentHostPointer(), src_ptr, upload_size); } - // 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); + VkDeviceSize texel_buffer_offset = m_texel_buffer->GetCurrentOffset(); + m_texel_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, + dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // We divide the offset by 4 here because we're fetching RGBA8 elements. + // The stride is in RGBA8 elements, so we divide by two because our data is two bytes per pixel. + struct PSUniformBlock + { + int buffer_offset; + int src_stride; + }; + PSUniformBlock push_constants = {static_cast(texel_buffer_offset / sizeof(u32)), + static_cast(src_width / 2)}; + // 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), + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION), 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.SetPSTexelBuffer(m_texel_buffer_view_rgba8_unorm); + draw.SetPushConstants(&push_constants, sizeof(push_constants)); draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); draw.EndRenderPass(); } @@ -361,7 +378,9 @@ bool TextureConverter::CreateTexelBuffer() // Create views of the formats that we will be using. m_texel_buffer_view_r16_uint = CreateTexelBufferView(VK_FORMAT_R16_UINT); - return m_texel_buffer_view_r16_uint != VK_NULL_HANDLE; + m_texel_buffer_view_rgba8_unorm = CreateTexelBufferView(VK_FORMAT_R8G8B8A8_UNORM); + return m_texel_buffer_view_r16_uint != VK_NULL_HANDLE && + m_texel_buffer_view_rgba8_unorm != VK_NULL_HANDLE; } VkBufferView TextureConverter::CreateTexelBufferView(VkFormat format) const @@ -614,17 +633,21 @@ bool TextureConverter::CompileYUYVConversionShaders() )"; static const char YUYV_TO_RGB_SHADER_SOURCE[] = R"( - SAMPLER_BINDING(0) uniform sampler2D source; + layout(std140, push_constant) uniform PCBlock + { + int buffer_offset; + int src_stride; + } PC; + + TEXEL_BUFFER_BINDING(0) uniform samplerBuffer 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; + int buffer_pos = PC.buffer_offset + uv.y * PC.src_stride + (uv.x / 2); + vec4 c0 = texelFetch(source, buffer_pos); float y = mix(c0.r, c0.b, (uv.x & 1) == 1); float yComp = 1.164 * (y - 0.0625); diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h index 4df1597470..f01fda1fb2 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -65,9 +65,21 @@ private: bool CompileYUYVConversionShaders(); + // Allocates storage in the texel command buffer of the specified size. + // If the buffer does not have enough space, executes the current command buffer and tries again. + // If this is done, g_command_buffer_mgr->GetCurrentCommandBuffer() will return a different value, + // so it always should be re-obtained after calling this method. + // Once the data copy is done, call m_texel_buffer->CommitMemory(size). + bool ReserveTexelBufferStorage(size_t size, size_t alignment); + + // Returns the command buffer that the texture conversion should occur in for the given texture. + // This can be the initialization/copy command buffer, or the drawing command buffer. + VkCommandBuffer GetCommandBufferForTextureConversion(const TextureCache::TCacheEntry* src_entry); + // Shared between conversion types std::unique_ptr m_texel_buffer; VkBufferView m_texel_buffer_view_r16_uint = VK_NULL_HANDLE; + VkBufferView m_texel_buffer_view_rgba8_unorm = VK_NULL_HANDLE; size_t m_texel_buffer_size = 0; // Palette conversion - taking an indexed texture and applying palette From a475792163d3383d94e3bd21e4d3c22077ef63ab Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 20 Nov 2016 02:51:53 +1000 Subject: [PATCH 09/10] Vulkan: Fix incorrect logic in readback preemption This could have been causing a large number of command buffer submissions per frame, depending on when the readbacks occured. --- Source/Core/VideoBackends/Vulkan/Constants.h | 3 +++ Source/Core/VideoBackends/Vulkan/StateTracker.cpp | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index e5c774cfc5..fd66dcc32b 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -113,6 +113,9 @@ constexpr size_t TEXTURE_CONVERSION_TEXEL_BUFFER_SIZE = 8 * 1024 * 1024; // Push constant buffer size for utility shaders constexpr u32 PUSH_CONSTANT_BUFFER_SIZE = 128; +// Minimum number of draw calls per command buffer when attempting to preempt a readback operation. +constexpr u32 MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK = 10; + // Rasterization state info union RasterizationState { BitField<0, 2, VkCullModeFlags> cull_mode; diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index ef2343564d..34ea98f41b 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -792,7 +792,12 @@ void StateTracker::OnEndFrame() u32 interval = static_cast(g_ActiveConfig.iCommandBufferExecuteInterval); for (u32 draw_counter : m_cpu_accesses_this_frame) { + // We don't want to waste executing command buffers for only a few draws, so set a minimum. + // Leave last_draw_counter as-is, so we get the correct number of draws between submissions. u32 draw_count = draw_counter - last_draw_counter; + if (draw_count < MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK) + continue; + if (draw_count <= interval) { u32 mid_point = draw_count / 2; @@ -807,6 +812,8 @@ void StateTracker::OnEndFrame() counter += interval; } } + + last_draw_counter = draw_counter; } } From 9736198c3b2552e51dd98445c0439dff759e08af Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 21 Nov 2016 01:59:19 +1000 Subject: [PATCH 10/10] Vulkan: Use explicit barriers instead of dependancies At least on NV, some of these don't seem to have the intended effect. One known instance of this is in texture conversion. --- .../Vulkan/FramebufferManager.cpp | 60 +++---- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 6 + .../VideoBackends/Vulkan/TextureCache.cpp | 158 ++++-------------- .../Core/VideoBackends/Vulkan/TextureCache.h | 4 +- .../VideoBackends/Vulkan/TextureConverter.cpp | 27 +-- 5 files changed, 77 insertions(+), 178 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index a707d8ad0e..e102be287a 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -183,24 +183,12 @@ bool FramebufferManager::CreateEFBRenderPass() VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; - // Ensure all reads have finished from the resolved texture before overwriting it. - VkSubpassDependency dependencies[] = { - {VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_DEPENDENCY_BY_REGION_BIT}, - {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}}; subpass_description.pDepthStencilAttachment = nullptr; pass_info.pAttachments = &resolve_attachment; pass_info.attachmentCount = 1; - pass_info.dependencyCount = static_cast(ArraySize(dependencies)); - pass_info.pDependencies = dependencies; res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr, &m_depth_resolve_render_pass); @@ -696,6 +684,9 @@ bool FramebufferManager::PopulateColorReadbackTexture() if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT) { + m_color_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), m_copy_color_render_pass, g_object_cache->GetScreenQuadVertexShader(), @@ -720,7 +711,6 @@ bool FramebufferManager::PopulateColorReadbackTexture() } // Use this as a source texture now. - m_color_copy_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); src_texture = m_color_copy_texture.get(); } @@ -777,6 +767,9 @@ bool FramebufferManager::PopulateDepthReadbackTexture() } if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT) { + m_depth_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), m_copy_depth_render_pass, g_object_cache->GetScreenQuadVertexShader(), @@ -801,7 +794,6 @@ bool FramebufferManager::PopulateDepthReadbackTexture() } // Use this as a source texture now. - m_depth_copy_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); src_texture = m_depth_copy_texture.get(); src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; } @@ -840,15 +832,15 @@ void FramebufferManager::InvalidatePeekCache() bool FramebufferManager::CreateReadbackRenderPasses() { VkAttachmentDescription copy_attachment = { - 0, // VkAttachmentDescriptionFlags flags - EFB_COLOR_TEXTURE_FORMAT, // VkFormat format - VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples - VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp - VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp - VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp - VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout initialLayout - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL // VkImageLayout finalLayout + 0, // VkAttachmentDescriptionFlags flags + EFB_COLOR_TEXTURE_FORMAT, // VkFormat format + VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples + VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp + VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp + VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp + VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout }; VkAttachmentReference copy_attachment_ref = { 0, // uint32_t attachment @@ -866,14 +858,6 @@ bool FramebufferManager::CreateReadbackRenderPasses() 0, // uint32_t preserveAttachmentCount nullptr // const uint32_t* pPreserveAttachments }; - VkSubpassDependency copy_dependency = { - 0, - VK_SUBPASS_EXTERNAL, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - VK_DEPENDENCY_BY_REGION_BIT}; VkRenderPassCreateInfo copy_pass = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType nullptr, // const void* pNext @@ -882,8 +866,8 @@ bool FramebufferManager::CreateReadbackRenderPasses() ©_attachment, // const VkAttachmentDescription* pAttachments 1, // uint32_t subpassCount ©_subpass, // const VkSubpassDescription* pSubpasses - 1, // uint32_t dependencyCount - ©_dependency // const VkSubpassDependency* pDependencies + 0, // uint32_t dependencyCount + nullptr // const VkSubpassDependency* pDependencies }; VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), ©_pass, nullptr, @@ -1023,12 +1007,6 @@ bool FramebufferManager::CreateReadbackTextures() return false; } - // Transition to TRANSFER_SRC, as this is expected by the render pass. - m_color_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_depth_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - return true; } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 291f3e07e8..fea355341a 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -613,6 +613,9 @@ void Renderer::DrawVirtualXFB(VkRenderPass render_pass, const TargetRectangle& t for (u32 i = 0; i < xfb_count; ++i) { const XFBSource* xfb_source = static_cast(xfb_sources[i]); + xfb_source->GetTexture()->GetTexture()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + TargetRectangle source_rect = xfb_source->sourceRc; TargetRectangle draw_rect; @@ -646,6 +649,9 @@ void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& targ for (u32 i = 0; i < xfb_count; ++i) { const XFBSource* xfb_source = static_cast(xfb_sources[i]); + xfb_source->GetTexture()->GetTexture()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + TargetRectangle source_rect = xfb_source->sourceRc; TargetRectangle draw_rect = target_rect; source_rect.right -= fb_stride - fb_width; diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index b13e2fe4ec..78892f9fb3 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -36,10 +36,8 @@ TextureCache::TextureCache() TextureCache::~TextureCache() { - if (m_initialize_render_pass != VK_NULL_HANDLE) - vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_initialize_render_pass, nullptr); - if (m_update_render_pass != VK_NULL_HANDLE) - vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_update_render_pass, nullptr); + if (m_render_pass != VK_NULL_HANDLE) + vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_render_pass, nullptr); TextureCache::DeleteShaders(); } @@ -87,11 +85,7 @@ void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* TCacheEntry* entry = static_cast(base_entry); TCacheEntry* unconverted = static_cast(base_unconverted); - m_texture_converter->ConvertTexture( - entry, unconverted, GetRenderPassForTextureUpdate(entry->GetTexture()), palette, format); - - // Render pass transitions to SHADER_READ_ONLY. - entry->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_texture_converter->ConvertTexture(entry, unconverted, m_render_pass, palette, format); } static bool IsDepthCopyFormat(PEControl::PixelFormat format) @@ -203,11 +197,10 @@ void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture, src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), - GetRenderPassForTextureUpdate(dst_texture->GetTexture()), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), m_render_pass, g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(), m_copy_shader); @@ -221,9 +214,6 @@ void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture, static_cast(src_texture->GetWidth()), static_cast(src_texture->GetHeight())); draw.EndRenderPass(); - - // Render pass transitions destination texture to SHADER_READ_ONLY. - dst_texture->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config) @@ -251,7 +241,7 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, - m_initialize_render_pass, + m_render_pass, static_cast(ArraySize(framebuffer_attachments)), framebuffer_attachments, texture->GetWidth(), @@ -281,17 +271,6 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry bool TextureCache::CreateRenderPasses() { - static constexpr VkAttachmentDescription initialize_attachment = { - 0, - TEXTURECACHE_TEXTURE_FORMAT, - VK_SAMPLE_COUNT_1_BIT, - VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_STORE, - VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_DONT_CARE, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; - static constexpr VkAttachmentDescription update_attachment = { 0, TEXTURECACHE_TEXTURE_FORMAT, @@ -300,8 +279,8 @@ bool TextureCache::CreateRenderPasses() VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; static constexpr VkAttachmentReference color_attachment_reference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; @@ -313,36 +292,6 @@ bool TextureCache::CreateRenderPasses() nullptr, nullptr, 0, nullptr}; - static constexpr VkSubpassDependency initialize_dependancies[] = { - {VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_DEPENDENCY_BY_REGION_BIT}, - {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}}; - - static constexpr VkSubpassDependency update_dependancies[] = { - {VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_DEPENDENCY_BY_REGION_BIT}, - {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}}; - - VkRenderPassCreateInfo initialize_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - nullptr, - 0, - 1, - &initialize_attachment, - 1, - &subpass_description, - static_cast(ArraySize(initialize_dependancies)), - initialize_dependancies}; - VkRenderPassCreateInfo update_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, @@ -350,44 +299,20 @@ bool TextureCache::CreateRenderPasses() &update_attachment, 1, &subpass_description, - static_cast(ArraySize(update_dependancies)), - update_dependancies}; + 0, + nullptr}; - VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &initialize_info, nullptr, - &m_initialize_render_pass); + VkResult res = + vkCreateRenderPass(g_vulkan_context->GetDevice(), &update_info, nullptr, &m_render_pass); if (res != VK_SUCCESS) { - LOG_VULKAN_ERROR(res, "vkCreateRenderPass (initialize) failed: "); - return false; - } - - res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &update_info, nullptr, - &m_update_render_pass); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateRenderPass (update) failed: "); + LOG_VULKAN_ERROR(res, "vkCreateRenderPass failed: "); return false; } return true; } -VkRenderPass TextureCache::GetRenderPassForTextureUpdate(const Texture2D* texture) const -{ - // EFB copies can be re-used as part of the texture pool. If this is the case, we need to insert - // a pipeline barrier to ensure that all reads from the texture expecting the old data have - // completed before overwriting the texture's contents. New textures will be in TRANSFER_DST - // due to the clear after creation. - - // These two render passes are compatible, so even though the framebuffer was created with - // the initialize render pass it's still allowed. - - if (texture->GetLayout() == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - return m_initialize_render_pass; - else - return m_update_render_pass; -} - TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_, std::unique_ptr texture, VkFramebuffer framebuffer) @@ -410,27 +335,23 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, width = std::max(1u, std::min(width, m_texture->GetWidth() >> level)); height = std::max(1u, std::min(height, m_texture->GetHeight() >> level)); - // We don't care about the existing contents of the texture, so we set the image layout to - // VK_IMAGE_LAYOUT_UNDEFINED here. However, if this texture is being re-used from the texture - // pool, it may still be in use. We assume that it's not, as non-efb-copy textures are only - // returned to the pool when the frame number is different, furthermore, we're doing this - // on the initialize command buffer, so a texture being re-used mid-frame would have undesirable - // effects regardless. - VkImageMemoryBarrier barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkAccessFlags srcAccessMask - VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask - VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout - VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex - VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex - m_texture->GetImage(), // VkImage image - {VK_IMAGE_ASPECT_COLOR_BIT, level, 1, 0, 1}, // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, - nullptr, 0, nullptr, 1, &barrier); + // We don't care about the existing contents of the texture, so we could the image layout to + // VK_IMAGE_LAYOUT_UNDEFINED here. However, under section 2.2.1, Queue Operation of the Vulkan + // specification, it states: + // + // Command buffer submissions to a single queue must always adhere to command order and + // API order, but otherwise may overlap or execute out of order. + // + // Therefore, if a previous frame's command buffer is still sampling from this texture, and we + // overwrite it without a pipeline barrier, a texture sample could occur in parallel with the + // texture upload/copy. I'm not sure if any drivers currently take advantage of this, but we + // should insert an explicit pipeline barrier just in case (done by TransitionToLayout). + // + // We transition to TRANSFER_DST, ready for the image copy, and leave the texture in this state. + // This is so that the remaining mip levels can be uploaded without barriers, and then when the + // texture is used, it can be transitioned to SHADER_READ_ONLY (see TCacheEntry::Bind). + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Does this texture data fit within the streaming buffer? u32 upload_width = width; @@ -513,16 +434,6 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width, height, level, 0); } - - // Transition to shader read only. - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkCmdPipelineBarrier(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, - nullptr, 0, nullptr, 1, &barrier); - m_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat src_format, @@ -559,11 +470,12 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler(); VkImageLayout original_layout = src_texture->GetLayout(); src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); UtilityShaderDraw draw( command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), - TextureCache::GetInstance()->GetRenderPassForTextureUpdate(m_texture.get()), - g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(), + TextureCache::GetInstance()->m_render_pass, g_object_cache->GetPassthroughVertexShader(), + g_object_cache->GetPassthroughGeometryShader(), is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader : TextureCache::GetInstance()->m_efb_color_to_tex_shader); @@ -587,7 +499,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat src_texture->TransitionToLayout(command_buffer, original_layout); // Render pass transitions texture to SHADER_READ_ONLY. - m_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source, @@ -607,6 +519,8 @@ void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* void TextureCache::TCacheEntry::Bind(unsigned int stage) { + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); } diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index f59a66e063..4b60d39272 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -68,7 +68,6 @@ public: private: bool CreateRenderPasses(); - VkRenderPass GetRenderPassForTextureUpdate(const Texture2D* texture) const; // Copies the contents of a texture using vkCmdCopyImage void CopyTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle& dst_rect, @@ -78,8 +77,7 @@ private: void ScaleTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle& dst_rect, Texture2D* src_texture, const MathUtil::Rectangle& src_rect); - VkRenderPass m_initialize_render_pass = VK_NULL_HANDLE; - VkRenderPass m_update_render_pass = VK_NULL_HANDLE; + VkRenderPass m_render_pass = VK_NULL_HANDLE; std::unique_ptr m_texture_upload_buffer; diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index 51f668f583..9c943e4b40 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -180,6 +180,10 @@ void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, m_texel_buffer->CommitMemory(palette_size); VkCommandBuffer command_buffer = GetCommandBufferForTextureConversion(src_entry); + src_entry->GetTexture()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + dst_entry->GetTexture()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Bind and draw to the destination. UtilityShaderDraw draw(command_buffer, @@ -216,6 +220,9 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p // Can't do our own draw within a render pass. StateTracker::GetInstance()->EndRenderPass(); + m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(), @@ -242,7 +249,8 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p draw.EndRenderPass(); // Transition the image before copying - m_encoding_render_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); m_encoding_download_texture->CopyFromImage( g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0); @@ -286,7 +294,8 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u draw.EndRenderPass(); // Render pass transitions to TRANSFER_SRC. - m_encoding_render_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + 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(), @@ -530,8 +539,8 @@ bool TextureConverter::CreateEncodingRenderPass() VkAttachmentDescription attachments[] = { {0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL}}; + VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; VkAttachmentReference color_attachment_references[] = { {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; @@ -540,12 +549,6 @@ bool TextureConverter::CreateEncodingRenderPass() color_attachment_references, nullptr, nullptr, 0, nullptr}}; - VkSubpassDependency dependancies[] = { - {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}}; - VkRenderPassCreateInfo pass_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, @@ -553,8 +556,8 @@ bool TextureConverter::CreateEncodingRenderPass() attachments, static_cast(ArraySize(subpass_descriptions)), subpass_descriptions, - static_cast(ArraySize(dependancies)), - dependancies}; + 0, + nullptr}; VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr, &m_encoding_render_pass);