#include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include #include #include namespace { void Check(bool value, const char *text) { if (!value) { std::fprintf(stderr, "ResourceMaterializationTests: failed: %s\n", text); std::abort(); } } bool RejectSpecializationRead(void *userdata, uint64_t, uint32_t *) { ++*static_cast(userdata); return false; } Libs::Graphics::ShaderRecompiler::IR::Block & AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) { using namespace Libs::Graphics::ShaderRecompiler::IR; auto block = std::make_unique(); auto *result = block.get(); program.blocks.push_back(result); program.block_info.push_back({.id = 0}); program.block_storage.push_back(std::move(block)); return *result; } Libs::Graphics::ShaderRecompiler::IR::ResourcePlan SrtPlan(uint64_t address) { using namespace Libs::Graphics::ShaderRecompiler::IR; Program program; program.stage = Libs::Graphics::ShaderType::Compute; program.srt_plan_complete = true; program.resource_tracking_complete = true; auto &value_block = AddValueBlock(program); MemoryInfo memory; memory.kind = ResourceKind::ScalarAddress; memory.planning_only = true; program.memory_info.push_back(memory); const auto low = Value(static_cast(address)); const auto high = Value(static_cast(address >> 32u)); auto &handle = value_block.AppendNewInst(ValueOpcode::GetAddressResource, {low, high}); auto &raw = value_block.AppendNewInst( ValueOpcode::LoadAddressU32, {Value(&handle), Value(0u), Value(0u), Value(true)}); raw.SetFlags(MemoryFlags{.index = 0, .pc = 0x40}); program.srt_reads.push_back({Value(&raw), 0}); auto &srt = value_block.AppendNewInst(ValueOpcode::GetSrtResource); auto &flat = value_block.AppendNewInst(ValueOpcode::ReadConst, {Value(&srt), Value(0u)}); DescriptorSource source; source.dwords[0] = Value(&flat); source.dwords[1] = Value(0u); source.dword_count = 2; program.descriptor_sources.push_back(source); return ExtractResourcePlan(program); } Libs::Graphics::ShaderRecompiler::IR::ResourcePlan UnbasedFlatPlan() { using namespace Libs::Graphics::ShaderRecompiler::IR; Program program; program.stage = Libs::Graphics::ShaderType::Compute; program.srt_plan_complete = true; program.resource_tracking_complete = true; AddValueBlock(program); program.info.uses_dma = true; return ExtractResourcePlan(program); } Libs::Graphics::ShaderRecompiler::IR::ResourcePlan UserDataBufferPlan() { using namespace Libs::Graphics::ShaderRecompiler::IR; Program program; program.stage = Libs::Graphics::ShaderType::Compute; program.srt_plan_complete = true; program.resource_tracking_complete = true; auto &value_block = AddValueBlock(program); auto &user_data = value_block.AppendNewInst( ValueOpcode::GetUserData, {Value(static_cast(0))}); DescriptorSource source; source.dwords[0] = Value(&user_data); source.dwords[1] = Value(0u); source.dwords[2] = Value(0u); source.dwords[3] = Value(0u); source.dword_count = 4; program.descriptor_sources.push_back(source); program.info.buffers.push_back({.source = 0}); return ExtractResourcePlan(program); } Libs::Graphics::ShaderRecompiler::IR::ResourcePlan MixedSamplerPlan() { using namespace Libs::Graphics::ShaderRecompiler::IR; Program program; program.stage = Libs::Graphics::ShaderType::Compute; program.srt_plan_complete = true; program.resource_tracking_complete = true; AddValueBlock(program); const auto AddSource = [&program](uint32_t dword_count, uint32_t first) { DescriptorSource source; source.dword_count = dword_count; source.dwords[0] = Value(first); for (uint32_t i = 1; i < dword_count; i++) { source.dwords[i] = Value(0u); } program.descriptor_sources.push_back(source); return static_cast(program.descriptor_sources.size() - 1u); }; const auto image0 = AddSource(8, 0); const auto image1 = AddSource(8, 0); const auto sampler0 = AddSource(4, 0x11111111u); const auto sampler1 = AddSource(4, 0x22222222u); program.info.images.push_back( {.source = image0, .resource_class = ImageResourceClass::Sampled, .numeric_class = Libs::Graphics::Prospero::TextureNumericClass::Float, .dimension = Libs::Graphics::ShaderRecompiler::Decoder::ImageDimension::Dim2D}); program.info.images.push_back( {.source = image1, .resource_class = ImageResourceClass::Sampled, .numeric_class = Libs::Graphics::Prospero::TextureNumericClass::Float, .dimension = Libs::Graphics::ShaderRecompiler::Decoder::ImageDimension::Dim2D, .conversion_format = Libs::Graphics::Prospero::BufferFormat::k8_8_8_8UNorm}); program.info.samplers.push_back({.source = sampler0}); program.info.samplers.push_back({.source = sampler1}); program.info.sampled_pairs.push_back({.image = 0, .sampler = 0}); program.info.sampled_pairs.push_back({.image = 0, .sampler = 1}); program.info.sampled_pairs.push_back({.image = 1, .sampler = 1}); return ExtractResourcePlan(program); } void TestMappedSrtUsesDirectReaderByDefault() { using namespace Libs::Graphics::ShaderRecompiler::IR; const uint32_t dword = 0x12345678; auto plan = SrtPlan(reinterpret_cast(&dword)); uint32_t specialization_reads = 0; const SrtRuntime runtime{.userdata = &specialization_reads, .read_specialization_memory = RejectSpecializationRead}; ResourceSnapshot snapshot; ResourceSpecialization specialization; Check(MaterializeResources(plan, runtime, snapshot, specialization), "mapped SRT stage materialization failed"); Check(specialization_reads == 0, "ordinary SRT read used the specialization reader"); Check(snapshot.flattened_srt.size() == 1 && snapshot.flattened_srt[0] == dword, "cache rematerialization did not use the direct reader by default"); } void TestUnbasedFlatCacheHitMaterializes() { using namespace Libs::Graphics::ShaderRecompiler::IR; auto plan = UnbasedFlatPlan(); ResourceSnapshot snapshot; ResourceSpecialization specialization; Check(MaterializeResources(plan, {}, snapshot, specialization), "unbased FLAT stage materialization failed"); Check(snapshot.buffers.empty() && snapshot.images.empty(), "unbased FLAT plan produced unexpected descriptors"); } void TestFailedMaterializationPreservesPriorStage() { using namespace Libs::Graphics::ShaderRecompiler::IR; auto plan = UserDataBufferPlan(); ResourceSnapshot snapshot; snapshot.user_data.push_back(0xfeedbeefu); ResourceSpecialization specialization; specialization.buffers.push_back({.packed_stride = 7}); Check(!MaterializeResources(plan, {}, snapshot, specialization), "missing runtime user data did not reject the cached stage"); Check(snapshot.user_data == std::vector{0xfeedbeefu} && specialization.buffers.size() == 1 && specialization.buffers[0].packed_stride == 7, "failed cache materialization changed its destinations"); } void TestMixedSamplerDuplicatesTheCorrectSnapshot() { using namespace Libs::Graphics::ShaderRecompiler::IR; auto plan = MixedSamplerPlan(); ResourceSnapshot snapshot; ResourceSpecialization specialization; Check(MaterializeResources(plan, {}, snapshot, specialization), "mixed sampler materialization failed"); Check(snapshot.samplers.size() == 3, "mixed sampler materialization appended unrelated samplers"); Check(snapshot.samplers[2] == snapshot.samplers[1] && snapshot.samplers[2] != snapshot.samplers[0], "point sampler variant duplicated the wrong runtime descriptor"); } } // namespace namespace Common { int DbgExitHandler(const char *, int, std::string_view) { std::abort(); } int DbgExitHandler(const char *, int, fmt::text_style, std::string_view) { std::abort(); } int DbgExitIfHandler(const char *, const char *, int) { return 1; } void DbgExit(int) { std::abort(); } } // namespace Common int main() { TestMappedSrtUsesDirectReaderByDefault(); TestUnbasedFlatCacheHitMaterializes(); TestFailedMaterializationPreservesPriorStage(); TestMixedSamplerDuplicatesTheCorrectSnapshot(); std::puts("ResourceMaterializationTests: all cases passed"); return 0; } // Keep this focused standalone target self-contained by amalgamating its small // typed-IR implementation set. #include "graphics/shader/recompiler/ir/Block.cpp" #include "graphics/shader/recompiler/ir/Program.cpp" #include "graphics/shader/recompiler/ir/Type.cpp" #include "graphics/shader/recompiler/ir/Value.cpp" #include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"