From a5d8ceb0dd00bbcaeb0a22b1844d26ccc1faf52c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 17 Jun 2026 05:11:15 +0200 Subject: [PATCH] Vulkan: Additional smaller optimizations --- .../HW/Latte/Core/LatteCommandProcessor.cpp | 41 ++++++++++++++----- src/Cafe/HW/Latte/Core/LatteConst.h | 13 +++--- .../Latte/Renderer/Vulkan/VKRPipelineInfo.cpp | 19 +++------ .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 25 +---------- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 26 ++++++++---- .../Renderer/Vulkan/VulkanRendererCore.cpp | 35 +++------------- 6 files changed, 69 insertions(+), 90 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp index 3a1c1cf6..77f9c734 100644 --- a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp +++ b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp @@ -311,6 +311,7 @@ void LatteCP_itSetRegistersGeneric_handleSpecialRanges(uint32 registerStartIndex template LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords) { + nWords--; // subtract the register offset field uint32 registerOffset = LatteReadCMD(); uint32 registerIndex = TRegisterBase + registerOffset; uint32 registerStartIndex = registerIndex; @@ -324,7 +325,7 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords) // state shadowing enabled uint32* __restrict shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex; sint32 indexCounter = 0; - while (--nWords) + while (nWords--) { uint32 dataWord = LatteReadCMD(); MPTR regShadowAddr = shadowAddrs[indexCounter]; @@ -337,10 +338,19 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords) else { // state shadowing disabled - while (--nWords) + if (nWords == 1) // common case { *outputReg = LatteReadCMD(); - outputReg++; + } + else + { + sint32 i = 0; + while (i < nWords) + { + outputReg[i] = cmd[i]; + i++; + } + cmd += nWords; } } // some register writes trigger special behavior @@ -352,6 +362,7 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords) template bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCallback cbRegRange) { + nWords--; const uint32 registerOffset = LatteReadCMD(); const uint32 registerIndex = TRegisterBase + registerOffset; const uint32 registerStartIndex = registerIndex; @@ -365,7 +376,7 @@ bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCal // state shadowing enabled uint32* shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex; sint32 indexCounter = 0; - while (--nWords) + while (nWords--) { uint32 dataWord = LatteReadCMD(); MPTR regShadowAddr = shadowAddrs[indexCounter]; @@ -379,13 +390,23 @@ bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCal else { // state shadowing disabled - sint32 indexCounter = 0; - while (--nWords) + if (nWords == 1) // common case { - uint32 dataWord = LatteReadCMD(); - hasRegChange |= (*outputReg != dataWord); - *outputReg = dataWord; - outputReg++; + uint32 v = LatteReadCMD(); + hasRegChange |= (*outputReg != v); + *outputReg = v; + } + else + { + sint32 i = 0; + while (i < nWords) + { + uint32 v = cmd[i]; + hasRegChange |= (outputReg[i] != v); + outputReg[i] = v; + i++; + } + cmd += nWords; } } // some register writes trigger special behavior diff --git a/src/Cafe/HW/Latte/Core/LatteConst.h b/src/Cafe/HW/Latte/Core/LatteConst.h index ebe741e9..07711464 100644 --- a/src/Cafe/HW/Latte/Core/LatteConst.h +++ b/src/Cafe/HW/Latte/Core/LatteConst.h @@ -109,16 +109,13 @@ namespace LatteConst { enum class ShaderType : uint32 { - Reserved = 0, // shaders for drawing - FirstRender = 1, - Vertex = 1, - Pixel = 2, - Geometry = 3, - LastRender = 3, + Vertex = 0, + Pixel = 1, + Geometry = 2, // compute shader - Compute = 4, - TotalCount = 5 + Compute = 3, + TotalCount = 4 }; enum class VertexFetchNFA diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp index c2c3234e..fbd4292b 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp @@ -58,20 +58,13 @@ PipelineInfo::~PipelineInfo() } // delete descriptor sets - while (!pixel_ds_cache.empty()) + for (auto& it : ds_cache) { - VkDescriptorSetInfo* dsInfo = pixel_ds_cache.begin()->second; - delete dsInfo; - } - while (!geometry_ds_cache.empty()) - { - VkDescriptorSetInfo* dsInfo = geometry_ds_cache.begin()->second; - delete dsInfo; - } - while (!vertex_ds_cache.empty()) - { - VkDescriptorSetInfo* dsInfo = vertex_ds_cache.begin()->second; - delete dsInfo; + while (!it.empty()) + { + VkDescriptorSetInfo* dsInfo = it.begin()->second; + delete dsInfo; + } } // disassociate from shaders diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index ddff392f..136cc76b 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -3374,29 +3374,8 @@ VkDescriptorSetInfo::~VkDescriptorSetInfo() for (auto& it : list_referencedViews) it->RemoveDescriptorSetReference(this); // unregister - switch (shaderType) - { - case LatteConst::ShaderType::Vertex: - { - auto r = pipeline_info->vertex_ds_cache.erase(stateHash); - cemu_assert_debug(r == 1); - break; - } - case LatteConst::ShaderType::Pixel: - { - auto r = pipeline_info->pixel_ds_cache.erase(stateHash); - cemu_assert_debug(r == 1); - break; - } - case LatteConst::ShaderType::Geometry: - { - auto r = pipeline_info->geometry_ds_cache.erase(stateHash); - cemu_assert_debug(r == 1); - break; - } - default: - UNREACHABLE; - } + auto r = pipeline_info->GetDescriptorSetCache(shaderType).erase(stateHash); + cemu_assert_debug(r == 1); // update global stats performanceMonitor.vk.numDescriptorSamplerTextures.decrement(statsNumSamplerTextures); performanceMonitor.vk.numDescriptorDynUniformBuffers.decrement(statsNumDynUniformBuffers); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 47041972..94c94f67 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -55,12 +55,17 @@ private: namespace VulkanRendererConst { - static const inline int SHADER_STAGE_INDEX_VERTEX = 0; - static const inline int SHADER_STAGE_INDEX_FRAGMENT = 1; - static const inline int SHADER_STAGE_INDEX_GEOMETRY = 2; - static const inline int SHADER_STAGE_INDEX_COUNT = 3; + static const inline int SHADER_STAGE_INDEX_VERTEX = static_cast(LatteConst::ShaderType::Vertex); + static const inline int SHADER_STAGE_INDEX_FRAGMENT = static_cast(LatteConst::ShaderType::Pixel); + static const inline int SHADER_STAGE_INDEX_GEOMETRY = static_cast(LatteConst::ShaderType::Geometry); + static const inline int SHADER_STAGE_INDEX_COUNT = 4; }; +// the order doesnt really matter but the types should cover range 0-2 since we use them as an array index +static_assert(static_cast(LatteConst::ShaderType::Vertex) == 0); +static_assert(static_cast(LatteConst::ShaderType::Pixel) == 1); +static_assert(static_cast(LatteConst::ShaderType::Geometry) == 2); + class PipelineInfo { public: @@ -82,11 +87,18 @@ public: return k; } }; + using DescriptorSetCache = ska::flat_hash_map>; + + FORCEINLINE DescriptorSetCache& GetDescriptorSetCache(LatteConst::ShaderType shaderType) + { + cemu_assert_debug(shaderType == LatteConst::ShaderType::Vertex || shaderType == LatteConst::ShaderType::Pixel || shaderType == LatteConst::ShaderType::Geometry); + return ds_cache[static_cast(shaderType)]; + } // std::unordered_map 3.16% (total CPU time) - // robin_hood::unordered_flat_map vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; ~1.80% - // ska::bytell_hash_map> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; -> 1.91% - ska::flat_hash_map> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; // 1.71% + // robin_hood::unordered_flat_map descriptor set cache; ~1.80% + // ska::bytell_hash_map> descriptor set cache; -> 1.91% + DescriptorSetCache ds_cache[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT]; // 1.71% VKRObjectPipeline* m_vkrObjPipeline; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index a29caeb4..f9b1f714 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -590,31 +590,27 @@ uint64 VulkanRenderer::GetDescriptorSetStateHash(LatteDecompilerShader* shader) VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* pipeline_info, LatteDecompilerShader* shader) { const uint64 stateHash = GetDescriptorSetStateHash(shader); + cemu_assert_debug(shader->shaderType == LatteConst::ShaderType::Vertex || shader->shaderType == LatteConst::ShaderType::Pixel || shader->shaderType == LatteConst::ShaderType::Geometry); + auto& ds_cache = pipeline_info->GetDescriptorSetCache(shader->shaderType); + const auto it = ds_cache.find(stateHash); + if (it != ds_cache.cend()) + return it->second; VkDescriptorSetLayout descriptor_set_layout; switch (shader->shaderType) { case LatteConst::ShaderType::Vertex: { - const auto it = pipeline_info->vertex_ds_cache.find(stateHash); - if (it != pipeline_info->vertex_ds_cache.cend()) - return it->second; descriptor_set_layout = pipeline_info->m_vkrObjPipeline->m_vertexDSL; break; } case LatteConst::ShaderType::Pixel: { - const auto it = pipeline_info->pixel_ds_cache.find(stateHash); - if (it != pipeline_info->pixel_ds_cache.cend()) - return it->second; descriptor_set_layout = pipeline_info->m_vkrObjPipeline->m_pixelDSL; break; } case LatteConst::ShaderType::Geometry: { - const auto it = pipeline_info->geometry_ds_cache.find(stateHash); - if (it != pipeline_info->geometry_ds_cache.cend()) - return it->second; descriptor_set_layout = pipeline_info->m_vkrObjPipeline->m_geometryDSL; break; } @@ -1006,26 +1002,7 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* if (!descriptorWrites.empty()) vkUpdateDescriptorSets(m_logicalDevice, (uint32)descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - pipeline_info->vertex_ds_cache[stateHash] = dsInfo; - break; - } - case LatteConst::ShaderType::Pixel: - { - pipeline_info->pixel_ds_cache[stateHash] = dsInfo; - break; - } - case LatteConst::ShaderType::Geometry: - { - pipeline_info->geometry_ds_cache[stateHash] = dsInfo; - break; - } - default: - UNREACHABLE; - } + ds_cache[stateHash] = dsInfo; return dsInfo; }