From 64eea6b32d52bdd27075d0b5dec2f2152cebf893 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 1 Oct 2026 21:55:43 +0400 Subject: [PATCH] Reuse vertex shaders across equivalent vertex buffer layouts --- .../host_gpu/renderer/pipeline/shaders.cpp | 3 - src/graphics/shader/shader.cpp | 7 +- tests/shaderCfgTests.cpp | 74 +++++++++++-------- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp index df0d2644f..05fa894d7 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp @@ -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); } diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 7445d08e0..ba5bf46b5 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -381,6 +381,8 @@ static void ShaderApplyAttribSemantics(ShaderVertexInputInfo& info, r.fields[1] = sharp[1]; r.fields[2] = sharp[2]; r.fields[3] = sharp[3]; + EXIT_NOT_IMPLEMENTED(r.AddTid()); + EXIT_NOT_IMPLEMENTED(r.SwizzleEnabled()); if (format != Prospero::VertexAttribFormat::kInvalid) { const auto format_raw = static_cast(format); const auto buffer_format = format_raw >> 2u; @@ -630,12 +632,9 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector(destination.attr_id)); - key.push_back(resource.fields[1] & 0xbfff0000u); // Stride and swizzle enable. - key.push_back(resource.fields[3] & 0x3087ffffu); // Channels, format, OOB, and add TID. + key.push_back(resource.fields[3] & 0x7ffffu); // Channels and format used by embedded fetch. } } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index b48a27f8c..fc7dcdfa6 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -10200,6 +10200,39 @@ void TestEmbeddedFetchPreservesSharedScalarLoad() { result.resources.buffers[0].dwords.begin()), "embedded fetch discarded a scalar-load component used by another buffer read"); 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(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() { @@ -10251,7 +10284,7 @@ void TestEmbeddedVertexFormatSwizzle() { Decoder::Program decoded; Decoder::DecodeProgram(code, decoded); const uint32_t attribute = static_cast(test.attribute_format) << 5u; - const uint32_t buffer[] = {0x10000000u, 16u << 16u, 1u, + uint32_t buffer[] = {0x10000000u, 16u << 16u, 1u, (static_cast(test.format) << 12u) | test.swizzle}; HW::VertexShaderInfo regs{}; regs.es_regs.data_addr = reinterpret_cast(code); @@ -10270,6 +10303,16 @@ void TestEmbeddedVertexFormatSwizzle() { ShaderMapUserData(regs.es_regs.data_addr, mapped); ShaderVertexInputInfo 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; fetch.loads.push_back({.pc = 0, .attrib_id = 0, .components = test.components}); Frontend::TranslateOptions options{}; @@ -10695,35 +10738,6 @@ void TestNewShaderRecompilerClipDisabledPosition() { shifted.clip_space.offset[0] += 1.0f; Check(MakeStageStaticKey(clipped) != MakeStageStaticKey(shifted), "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{ - 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() {