Vulkan: Additional smaller optimizations

This commit is contained in:
Exzap 2026-06-17 05:11:15 +02:00
parent 6d050e68ea
commit a5d8ceb0dd
6 changed files with 69 additions and 90 deletions

View File

@ -311,6 +311,7 @@ void LatteCP_itSetRegistersGeneric_handleSpecialRanges(uint32 registerStartIndex
template<uint32 TRegisterBase> template<uint32 TRegisterBase>
LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords) LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords)
{ {
nWords--; // subtract the register offset field
uint32 registerOffset = LatteReadCMD(); uint32 registerOffset = LatteReadCMD();
uint32 registerIndex = TRegisterBase + registerOffset; uint32 registerIndex = TRegisterBase + registerOffset;
uint32 registerStartIndex = registerIndex; uint32 registerStartIndex = registerIndex;
@ -324,7 +325,7 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords)
// state shadowing enabled // state shadowing enabled
uint32* __restrict shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex; uint32* __restrict shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex;
sint32 indexCounter = 0; sint32 indexCounter = 0;
while (--nWords) while (nWords--)
{ {
uint32 dataWord = LatteReadCMD(); uint32 dataWord = LatteReadCMD();
MPTR regShadowAddr = shadowAddrs[indexCounter]; MPTR regShadowAddr = shadowAddrs[indexCounter];
@ -337,10 +338,19 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords)
else else
{ {
// state shadowing disabled // state shadowing disabled
while (--nWords) if (nWords == 1) // common case
{ {
*outputReg = LatteReadCMD(); *outputReg = LatteReadCMD();
outputReg++; }
else
{
sint32 i = 0;
while (i < nWords)
{
outputReg[i] = cmd[i];
i++;
}
cmd += nWords;
} }
} }
// some register writes trigger special behavior // some register writes trigger special behavior
@ -352,6 +362,7 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords)
template<uint32 TRegisterBase, typename TRegRangeCallback> template<uint32 TRegisterBase, typename TRegRangeCallback>
bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCallback cbRegRange) bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCallback cbRegRange)
{ {
nWords--;
const uint32 registerOffset = LatteReadCMD(); const uint32 registerOffset = LatteReadCMD();
const uint32 registerIndex = TRegisterBase + registerOffset; const uint32 registerIndex = TRegisterBase + registerOffset;
const uint32 registerStartIndex = registerIndex; const uint32 registerStartIndex = registerIndex;
@ -365,7 +376,7 @@ bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCal
// state shadowing enabled // state shadowing enabled
uint32* shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex; uint32* shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex;
sint32 indexCounter = 0; sint32 indexCounter = 0;
while (--nWords) while (nWords--)
{ {
uint32 dataWord = LatteReadCMD(); uint32 dataWord = LatteReadCMD();
MPTR regShadowAddr = shadowAddrs[indexCounter]; MPTR regShadowAddr = shadowAddrs[indexCounter];
@ -379,13 +390,23 @@ bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCal
else else
{ {
// state shadowing disabled // state shadowing disabled
sint32 indexCounter = 0; if (nWords == 1) // common case
while (--nWords)
{ {
uint32 dataWord = LatteReadCMD(); uint32 v = LatteReadCMD();
hasRegChange |= (*outputReg != dataWord); hasRegChange |= (*outputReg != v);
*outputReg = dataWord; *outputReg = v;
outputReg++; }
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 // some register writes trigger special behavior

View File

@ -109,16 +109,13 @@ namespace LatteConst
{ {
enum class ShaderType : uint32 enum class ShaderType : uint32
{ {
Reserved = 0,
// shaders for drawing // shaders for drawing
FirstRender = 1, Vertex = 0,
Vertex = 1, Pixel = 1,
Pixel = 2, Geometry = 2,
Geometry = 3,
LastRender = 3,
// compute shader // compute shader
Compute = 4, Compute = 3,
TotalCount = 5 TotalCount = 4
}; };
enum class VertexFetchNFA enum class VertexFetchNFA

View File

@ -58,20 +58,13 @@ PipelineInfo::~PipelineInfo()
} }
// delete descriptor sets // delete descriptor sets
while (!pixel_ds_cache.empty()) for (auto& it : ds_cache)
{ {
VkDescriptorSetInfo* dsInfo = pixel_ds_cache.begin()->second; while (!it.empty())
delete dsInfo; {
} VkDescriptorSetInfo* dsInfo = it.begin()->second;
while (!geometry_ds_cache.empty()) delete dsInfo;
{ }
VkDescriptorSetInfo* dsInfo = geometry_ds_cache.begin()->second;
delete dsInfo;
}
while (!vertex_ds_cache.empty())
{
VkDescriptorSetInfo* dsInfo = vertex_ds_cache.begin()->second;
delete dsInfo;
} }
// disassociate from shaders // disassociate from shaders

View File

@ -3374,29 +3374,8 @@ VkDescriptorSetInfo::~VkDescriptorSetInfo()
for (auto& it : list_referencedViews) for (auto& it : list_referencedViews)
it->RemoveDescriptorSetReference(this); it->RemoveDescriptorSetReference(this);
// unregister // unregister
switch (shaderType) auto r = pipeline_info->GetDescriptorSetCache(shaderType).erase(stateHash);
{ cemu_assert_debug(r == 1);
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;
}
// update global stats // update global stats
performanceMonitor.vk.numDescriptorSamplerTextures.decrement(statsNumSamplerTextures); performanceMonitor.vk.numDescriptorSamplerTextures.decrement(statsNumSamplerTextures);
performanceMonitor.vk.numDescriptorDynUniformBuffers.decrement(statsNumDynUniformBuffers); performanceMonitor.vk.numDescriptorDynUniformBuffers.decrement(statsNumDynUniformBuffers);

View File

@ -55,12 +55,17 @@ private:
namespace VulkanRendererConst namespace VulkanRendererConst
{ {
static const inline int SHADER_STAGE_INDEX_VERTEX = 0; static const inline int SHADER_STAGE_INDEX_VERTEX = static_cast<int>(LatteConst::ShaderType::Vertex);
static const inline int SHADER_STAGE_INDEX_FRAGMENT = 1; static const inline int SHADER_STAGE_INDEX_FRAGMENT = static_cast<int>(LatteConst::ShaderType::Pixel);
static const inline int SHADER_STAGE_INDEX_GEOMETRY = 2; static const inline int SHADER_STAGE_INDEX_GEOMETRY = static_cast<int>(LatteConst::ShaderType::Geometry);
static const inline int SHADER_STAGE_INDEX_COUNT = 3; 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<int>(LatteConst::ShaderType::Vertex) == 0);
static_assert(static_cast<int>(LatteConst::ShaderType::Pixel) == 1);
static_assert(static_cast<int>(LatteConst::ShaderType::Geometry) == 2);
class PipelineInfo class PipelineInfo
{ {
public: public:
@ -82,11 +87,18 @@ public:
return k; return k;
} }
}; };
using DescriptorSetCache = ska::flat_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>>;
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<size_t>(shaderType)];
}
// std::unordered_map<uint64, VkDescriptorSetInfo*> 3.16% (total CPU time) // std::unordered_map<uint64, VkDescriptorSetInfo*> 3.16% (total CPU time)
// robin_hood::unordered_flat_map<uint64, VkDescriptorSetInfo*> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; ~1.80% // robin_hood::unordered_flat_map<uint64, VkDescriptorSetInfo*> descriptor set cache; ~1.80%
// ska::bytell_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; -> 1.91% // ska::bytell_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>> descriptor set cache; -> 1.91%
ska::flat_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; // 1.71% DescriptorSetCache ds_cache[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT]; // 1.71%
VKRObjectPipeline* m_vkrObjPipeline; VKRObjectPipeline* m_vkrObjPipeline;

View File

@ -590,31 +590,27 @@ uint64 VulkanRenderer::GetDescriptorSetStateHash(LatteDecompilerShader* shader)
VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* pipeline_info, LatteDecompilerShader* shader) VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* pipeline_info, LatteDecompilerShader* shader)
{ {
const uint64 stateHash = GetDescriptorSetStateHash(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; VkDescriptorSetLayout descriptor_set_layout;
switch (shader->shaderType) switch (shader->shaderType)
{ {
case LatteConst::ShaderType::Vertex: 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; descriptor_set_layout = pipeline_info->m_vkrObjPipeline->m_vertexDSL;
break; break;
} }
case LatteConst::ShaderType::Pixel: 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; descriptor_set_layout = pipeline_info->m_vkrObjPipeline->m_pixelDSL;
break; break;
} }
case LatteConst::ShaderType::Geometry: 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; descriptor_set_layout = pipeline_info->m_vkrObjPipeline->m_geometryDSL;
break; break;
} }
@ -1006,26 +1002,7 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo*
if (!descriptorWrites.empty()) if (!descriptorWrites.empty())
vkUpdateDescriptorSets(m_logicalDevice, (uint32)descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); vkUpdateDescriptorSets(m_logicalDevice, (uint32)descriptorWrites.size(), descriptorWrites.data(), 0, nullptr);
switch (shader->shaderType) ds_cache[stateHash] = dsInfo;
{
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;
}
return dsInfo; return dsInfo;
} }