Reuse vertex shaders across equivalent vertex buffer layouts

This commit is contained in:
nmzik
2026-10-01 21:55:43 +04:00
parent 706eec3ca9
commit 64eea6b32d
3 changed files with 47 additions and 37 deletions
@@ -330,9 +330,6 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p
} }
} }
EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].AddTid());
EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].SwizzleEnabled());
EXIT_IF(registers_num < 1 || registers_num > 4); EXIT_IF(registers_num < 1 || registers_num > 4);
} }
+3 -4
View File
@@ -381,6 +381,8 @@ static void ShaderApplyAttribSemantics(ShaderVertexInputInfo& info,
r.fields[1] = sharp[1]; r.fields[1] = sharp[1];
r.fields[2] = sharp[2]; r.fields[2] = sharp[2];
r.fields[3] = sharp[3]; r.fields[3] = sharp[3];
EXIT_NOT_IMPLEMENTED(r.AddTid());
EXIT_NOT_IMPLEMENTED(r.SwizzleEnabled());
if (format != Prospero::VertexAttribFormat::kInvalid) { if (format != Prospero::VertexAttribFormat::kInvalid) {
const auto format_raw = static_cast<uint32_t>(format); const auto format_raw = static_cast<uint32_t>(format);
const auto buffer_format = format_raw >> 2u; const auto buffer_format = format_raw >> 2u;
@@ -630,12 +632,9 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector<uint32_t
for (int i = 0; i < info.resources_num; i++) { for (int i = 0; i < info.resources_num; i++) {
const auto& resource = info.resources[i]; const auto& resource = info.resources[i];
const auto& destination = info.resources_dst[i]; const auto& destination = info.resources_dst[i];
key.push_back(destination.register_start);
key.push_back(destination.registers_num); key.push_back(destination.registers_num);
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.fields[1] & 0xbfff0000u); // Stride and swizzle enable. key.push_back(resource.fields[3] & 0x7ffffu); // Channels and format used by embedded fetch.
key.push_back(resource.fields[3] & 0x3087ffffu); // Channels, format, OOB, and add TID.
} }
} }
+44 -30
View File
@@ -10200,6 +10200,39 @@ void TestEmbeddedFetchPreservesSharedScalarLoad() {
result.resources.buffers[0].dwords.begin()), result.resources.buffers[0].dwords.begin()),
"embedded fetch discarded a scalar-load component used by another buffer read"); "embedded fetch discarded a scalar-load component used by another buffer read");
CheckSpirvBinaryValidates(result.spirv); CheckSpirvBinaryValidates(result.spirv);
const auto key = MakeStageStaticKey(input);
const auto compile = [&](const ShaderVertexInputInfo &vertex) {
auto variant_options = options;
variant_options.input_info.vertex = &vertex;
return RecompileForTest(code, variant_options).spirv;
};
auto layout = input;
layout.resources_dst[0].register_start = 32;
layout.resources_dst[0].fetch_index = 1;
layout.resources[0].UpdateAddress48(0x56780000u);
layout.resources[0].fields[1] = 52u << 16u;
layout.resources[0].fields[2] = 128;
layout.resources[0].fields[3] |= 3u << 28u;
layout.buffers_num = 1;
layout.buffers[0] = {.addr = 0x56780000u, .stride = 52, .num_records = 128,
.fetch_index = 1};
Check(compile(layout) == result.spirv,
"pipeline-only vertex layout changed the embedded-fetch shader");
Check(MakeStageStaticKey(layout) == key,
"pipeline-only vertex layout fragmented the shader module cache key");
auto formatted = input;
formatted.resources[0].fields[3] =
(static_cast<uint32_t>(Prospero::BufferFormat::k32_32_32UInt) << 12u) |
DstSel(4, 5, 6, 7);
Check(compile(formatted) != result.spirv && MakeStageStaticKey(formatted) != key,
"vertex input numeric type lost its shader module specialization");
auto swizzled = input;
swizzled.resources[0].fields[3] =
(swizzled.resources[0].fields[3] & ~0xfffu) | DstSel(5, 4, 6, 7);
Check(compile(swizzled) != result.spirv && MakeStageStaticKey(swizzled) != key,
"vertex input destination selectors lost their shader module specialization");
} }
void TestEmbeddedVertexFormatSwizzle() { void TestEmbeddedVertexFormatSwizzle() {
@@ -10251,7 +10284,7 @@ void TestEmbeddedVertexFormatSwizzle() {
Decoder::Program decoded; Decoder::Program decoded;
Decoder::DecodeProgram(code, decoded); Decoder::DecodeProgram(code, decoded);
const uint32_t attribute = static_cast<uint32_t>(test.attribute_format) << 5u; const uint32_t attribute = static_cast<uint32_t>(test.attribute_format) << 5u;
const uint32_t buffer[] = {0x10000000u, 16u << 16u, 1u, uint32_t buffer[] = {0x10000000u, 16u << 16u, 1u,
(static_cast<uint32_t>(test.format) << 12u) | test.swizzle}; (static_cast<uint32_t>(test.format) << 12u) | test.swizzle};
HW::VertexShaderInfo regs{}; HW::VertexShaderInfo regs{};
regs.es_regs.data_addr = reinterpret_cast<uint64_t>(code); regs.es_regs.data_addr = reinterpret_cast<uint64_t>(code);
@@ -10270,6 +10303,16 @@ void TestEmbeddedVertexFormatSwizzle() {
ShaderMapUserData(regs.es_regs.data_addr, mapped); ShaderMapUserData(regs.es_regs.data_addr, mapped);
ShaderVertexInputInfo input{}; ShaderVertexInputInfo input{};
PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input); PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input);
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
if (&test == cases) {
for (const auto [word, flag] : {std::pair{1u, 1u << 31u}, std::pair{3u, 1u << 23u}}) {
buffer[word] |= flag;
ExpectFatal([&] { PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input); },
"unsupported vertex fetch flags bypassed acquisition validation");
buffer[word] &= ~flag;
}
}
#endif
Frontend::EmbeddedFetchPlan fetch; Frontend::EmbeddedFetchPlan fetch;
fetch.loads.push_back({.pc = 0, .attrib_id = 0, .components = test.components}); fetch.loads.push_back({.pc = 0, .attrib_id = 0, .components = test.components});
Frontend::TranslateOptions options{}; Frontend::TranslateOptions options{};
@@ -10695,35 +10738,6 @@ void TestNewShaderRecompilerClipDisabledPosition() {
shifted.clip_space.offset[0] += 1.0f; shifted.clip_space.offset[0] += 1.0f;
Check(MakeStageStaticKey(clipped) != MakeStageStaticKey(shifted), Check(MakeStageStaticKey(clipped) != MakeStageStaticKey(shifted),
"clip-disabled viewport transform is absent from the shader cache key"); "clip-disabled viewport transform is absent from the shader cache key");
ShaderVertexInputInfo layout_a{};
layout_a.resources_num = 1;
layout_a.buffers_num = 1;
layout_a.buffers[0].stride = 16;
auto layout_b = layout_a;
layout_b.buffers[0].stride = 32;
layout_b.buffers[0].fetch_index = 1;
layout_b.resources[0].UpdateAddress48(4);
Check(MakeStageStaticKey(layout_a) == MakeStageStaticKey(layout_b),
"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() {