Vulkan: Additional smaller optimizations

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

View File

@ -311,6 +311,7 @@ void LatteCP_itSetRegistersGeneric_handleSpecialRanges(uint32 registerStartIndex
template<uint32 TRegisterBase>
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<uint32 TRegisterBase, typename TRegRangeCallback>
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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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<int>(LatteConst::ShaderType::Vertex);
static const inline int SHADER_STAGE_INDEX_FRAGMENT = static_cast<int>(LatteConst::ShaderType::Pixel);
static const inline int SHADER_STAGE_INDEX_GEOMETRY = static_cast<int>(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<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
{
public:
@ -82,11 +87,18 @@ public:
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)
// robin_hood::unordered_flat_map<uint64, VkDescriptorSetInfo*> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; ~1.80%
// ska::bytell_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; -> 1.91%
ska::flat_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>> vertex_ds_cache, pixel_ds_cache, geometry_ds_cache; // 1.71%
// robin_hood::unordered_flat_map<uint64, VkDescriptorSetInfo*> descriptor set cache; ~1.80%
// ska::bytell_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>> descriptor set cache; -> 1.91%
DescriptorSetCache ds_cache[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT]; // 1.71%
VKRObjectPipeline* m_vkrObjPipeline;

View File

@ -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;
}