graphics: compact shader keys and hash packed pipeline state

This commit is contained in:
nmzik
2026-09-29 03:40:03 +04:00
parent 5e4f6afc30
commit 0fbeac6d3f
4 changed files with 49 additions and 47 deletions
@@ -165,6 +165,32 @@ bool ValidateShaderSpirv(const char* label, uint64_t shader_hash,
} // namespace } // namespace
std::size_t PipelineCache::GraphicsPipelineKeyHash::operator()(const GraphicsPipelineKey& key) const {
std::size_t hash = 0;
PipelineKeyHash::Mix(hash, key.rendering.color_count);
for (uint32_t i = 0; i < key.rendering.color_count; i++) {
PipelineKeyHash::Mix(hash, static_cast<uint32_t>(key.rendering.color_formats[i]));
}
PipelineKeyHash::Mix(hash, static_cast<uint32_t>(key.rendering.depth_format));
PipelineKeyHash::Mix(hash, static_cast<uint32_t>(key.rendering.stencil_format));
for (const auto id: key.vertex_shader_ids) {
PipelineKeyHash::Mix(hash, id);
}
PipelineKeyHash::Mix(hash, key.ps_shader_id);
PipelineKeyHash::Mix(hash, key.vertex_input.binding_count);
for (uint32_t i = 0; i < key.vertex_input.binding_count; i++) {
PipelineKeyHash::Mix(hash, key.vertex_input.bindings[i].stride);
PipelineKeyHash::Mix(hash, key.vertex_input.bindings[i].instance);
}
PipelineKeyHash::Mix(hash, key.vertex_input.attribute_count);
for (uint32_t i = 0; i < key.vertex_input.attribute_count; i++) {
PipelineKeyHash::Mix(hash, key.vertex_input.attributes[i].offset);
PipelineKeyHash::Mix(hash, key.vertex_input.attributes[i].binding);
}
PipelineKeyHash::Mix(hash, XXH3_64bits(&key.static_params, sizeof(key.static_params)));
return hash;
}
struct PipelineCache::ProgramCache { struct PipelineCache::ProgramCache {
struct ProgramKey { struct ProgramKey {
ShaderType stage = ShaderType::Unknown; ShaderType stage = ShaderType::Unknown;
@@ -206,12 +232,12 @@ struct PipelineCache::ProgramCache {
PipelineKeyHash::Mix(hash, key.code_size); PipelineKeyHash::Mix(hash, key.code_size);
PipelineKeyHash::Mix(hash, key.static_state.size()); PipelineKeyHash::Mix(hash, key.static_state.size());
// Bucket same-shape static variants by source. ProgramKey equality performs the one // Bucket same-shape static variants by source. ProgramKey equality performs the one
// exact state comparison needed on a stable hit without hashing up to 429 words first. // exact state comparison needed on a stable hit without hashing the full state first.
return hash; return hash;
} }
}; };
static constexpr std::size_t MaxStaticKeyWords = 13 + ShaderVertexInputInfo::RES_MAX * 13; static constexpr std::size_t MaxStaticKeyWords = 32 + ShaderVertexInputInfo::RES_MAX * 6;
Permutation CompilePermutation(const char* stage_name, Permutation CompilePermutation(const char* stage_name,
const ShaderRecompiler::CompileOptions& options, const ShaderRecompiler::CompileOptions& options,
@@ -166,45 +166,10 @@ private:
hash ^= value + static_cast<std::size_t>(0x9e3779b97f4a7c15ull) + (hash << 6u) + hash ^= value + static_cast<std::size_t>(0x9e3779b97f4a7c15ull) + (hash << 6u) +
(hash >> 2u); (hash >> 2u);
} }
static void MixStaticParams(std::size_t& hash, const PipelineStaticParameters& params) {
const auto* bytes = reinterpret_cast<const uint8_t*>(&params);
for (std::size_t i = 0; i < sizeof(params); i++) {
Mix(hash, bytes[i]);
}
}
static void MixRendering(std::size_t& hash, const PipelineRenderingState& rendering) {
Mix(hash, rendering.color_count);
for (uint32_t i = 0; i < rendering.color_count; i++) {
Mix(hash, static_cast<uint32_t>(rendering.color_formats[i]));
}
Mix(hash, static_cast<uint32_t>(rendering.depth_format));
Mix(hash, static_cast<uint32_t>(rendering.stencil_format));
}
}; };
struct GraphicsPipelineKeyHash { struct GraphicsPipelineKeyHash {
std::size_t operator()(const GraphicsPipelineKey& key) const { std::size_t operator()(const GraphicsPipelineKey& key) const;
std::size_t hash = 0;
PipelineKeyHash::MixRendering(hash, key.rendering);
for (const auto id: key.vertex_shader_ids) {
PipelineKeyHash::Mix(hash, id);
}
PipelineKeyHash::Mix(hash, key.ps_shader_id);
PipelineKeyHash::Mix(hash, key.vertex_input.binding_count);
for (uint32_t i = 0; i < key.vertex_input.binding_count; i++) {
PipelineKeyHash::Mix(hash, key.vertex_input.bindings[i].stride);
PipelineKeyHash::Mix(hash, key.vertex_input.bindings[i].instance);
}
PipelineKeyHash::Mix(hash, key.vertex_input.attribute_count);
for (uint32_t i = 0; i < key.vertex_input.attribute_count; i++) {
PipelineKeyHash::Mix(hash, key.vertex_input.attributes[i].offset);
PipelineKeyHash::Mix(hash, key.vertex_input.attributes[i].binding);
}
PipelineKeyHash::MixStaticParams(hash, key.static_params);
return hash;
}
}; };
GraphicContext& m_graphics; GraphicContext& m_graphics;
+2 -9
View File
@@ -668,15 +668,8 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector<uint32_t
key.push_back(destination.registers_num); key.push_back(destination.registers_num);
key.push_back(destination.fetch_index); key.push_back(destination.fetch_index);
key.push_back(static_cast<uint32_t>(destination.attr_id)); key.push_back(static_cast<uint32_t>(destination.attr_id));
key.push_back(resource.Stride()); key.push_back(resource.fields[1] & 0xbfff0000u); // Stride and swizzle enable.
key.push_back(static_cast<uint32_t>(resource.SwizzleEnabled())); key.push_back(resource.fields[3] & 0x3087ffffu); // Channels, format, OOB, and add TID.
key.push_back(resource.DstSelX());
key.push_back(resource.DstSelY());
key.push_back(resource.DstSelZ());
key.push_back(resource.DstSelW());
key.push_back(resource.RawFormat());
key.push_back(resource.OutOfBounds());
key.push_back(static_cast<uint32_t>(resource.AddTid()));
} }
} }
+18
View File
@@ -10633,6 +10633,24 @@ void TestNewShaderRecompilerClipDisabledPosition() {
layout_b.resources[0].UpdateAddress48(4); layout_b.resources[0].UpdateAddress48(4);
Check(MakeStageStaticKey(layout_a) == MakeStageStaticKey(layout_b), Check(MakeStageStaticKey(layout_a) == MakeStageStaticKey(layout_b),
"pipeline-only vertex layout fragmented the shader module cache key"); "pipeline-only vertex layout fragmented the shader module cache key");
const auto descriptor_state = [](const ShaderBufferResource &resource) {
return std::array<uint32_t, 9>{
resource.Stride(), resource.SwizzleEnabled(), resource.DstSelX(),
resource.DstSelY(), resource.DstSelZ(), resource.DstSelW(),
resource.RawFormat(), resource.OutOfBounds(), resource.AddTid()};
};
const auto key = MakeStageStaticKey(layout_a);
const auto state = descriptor_state(layout_a.resources[0]);
for (uint32_t word = 0; word < 4; word++) {
for (uint32_t bit = 0; bit < 32; bit++) {
auto changed = layout_a;
changed.resources[0].fields[word] ^= 1u << bit;
Check((MakeStageStaticKey(changed) == key) ==
(descriptor_state(changed.resources[0]) == state),
"vertex key lost a descriptor field or included runtime-only bits");
}
}
} }
void TestNewShaderRecompilerAuxPositionExports() { void TestNewShaderRecompilerAuxPositionExports() {