void TestRayTracingDispatchDetection() { namespace D = ShaderRecompiler::Decoder; const auto check_decoded = [](std::span code, bool expected, const char *message) { D::Program decoded; D::DecodeProgram(code, decoded); Check(decoded.has_bvh == expected, message); if (expected) { const auto &last = decoded.instructions.back(); Check(last.family == D::Family::MIMG && (last.opcode_id == 0xe6u || last.opcode_id == 0xe7u), "decoder continued past the first BVH instruction"); const auto result = ShaderRecompiler::TranslateProgram( code, MakeCompileOptions(ShaderType::Compute)); Check(result.skip_dispatch && result.program.blocks.empty() && result.cfg_dump.empty(), "BVH compute shader reached IR translation"); } }; const auto check = [&](std::initializer_list code, bool expected, const char *message) { check_decoded(std::span{code.begin(), code.size()}, expected, message); }; check({0xf1989f01u, 0x00081014u, EncodeSopp(0x01)}, true, "captured contiguous BVH instruction did not skip dispatch"); check({0xf1989f07u, 0x00071001u, 0x3d3e3c15u, 0x38095851u, 0x00003a39u, EncodeSopp(0x01)}, true, "captured NSA BVH instruction did not skip dispatch"); check({0xf19c9f01u, 0x00081014u, EncodeSopp(0x01)}, true, "RDNA2 BVH64 instruction did not skip dispatch"); check({0xbe8e210eu, 0xf1989f01u, 0x00081014u, EncodeSopp(0x01)}, true, "unsupported SWAPPC before BVH prevented dispatch detection"); check({0xf1989f01u, 0x00081014u, 0xc0000000u, EncodeSopp(0x01)}, true, "unknown instruction after BVH prevented dispatch skipping"); for (uint32_t opcode : {0xe6u, 0xe7u}) { for (uint32_t nsa = 0; nsa < 4; ++nsa) { for (uint32_t slc = 0; slc < 2; ++slc) { std::array code{ EncodeMimg0(opcode, 0xf, true) | (nsa << 1u) | (slc << 25u), EncodeMimg1(0, 0, 0, 0)}; code[2 + nsa] = EncodeSopp(0x01); check_decoded(std::span(code).first(3 + nsa), true, "BVH cache flags or NSA length prevented dispatch detection"); } } } for (uint32_t opcode : {0x66u, 0x67u, 0xe4u, 0xe5u, 0xe8u, 0xe9u}) { check({EncodeMimg0(opcode, 0xf), EncodeMimg1(0, 0, 0, 0), EncodeSopp(0x01)}, false, "non-BVH image opcode was incorrectly skipped"); } check({EncodeSMovB32(0, 255), 0xf1989f01u, EncodeSopp(0x01)}, false, "scalar literal was mistaken for a BVH instruction"); check({EncodeMimg0(0x20, 0xf) | 6u, EncodeMimg1(0, 0, 0, 0), 0xf1989f01u, 0xf19c9f01u, 0u, EncodeSopp(0x01)}, false, "NSA address words were mistaken for BVH instructions"); check({EncodeMimg0(0x20, 0xf), 0xf1989f01u, EncodeSopp(0x01)}, false, "MIMG operand word was mistaken for a BVH instruction"); check({EncodeSopp(0x01), 0xf1989f01u, 0x00081014u}, false, "data after the shader end was mistaken for a BVH instruction"); check({EncodeMimg0(0x20, 0xf), EncodeMimg1(0, 0, 0, 0), EncodeSopp(0x01)}, false, "ordinary image shader was incorrectly skipped"); // An end instruction on one branch does not end another branch's code. check({EncodeSopp(0x04, 1), EncodeSopp(0x01), 0xf1989f01u, 0x00081014u, EncodeSopp(0x01)}, true, "BVH at a branch target after an early end was not detected"); const std::array bvh_code{0xf1989f01u, 0x00081014u, 0xf19c9f01u, 0x00081014u, EncodeSopp(0x01)}; const std::array ordinary_code{EncodeSMovB32(0, 255), 0xf1989f01u, EncodeSopp(0x01)}; D::Program reused; D::DecodeProgram(bvh_code, reused); Check(reused.has_bvh && reused.instructions.size() == 1, "decoder did not stop at the first BVH instruction"); D::DecodeProgram(ordinary_code, reused); Check(!reused.has_bvh, "decoder retained stale BVH metadata"); for (const auto stage : {ShaderType::Compute, ShaderType::Vertex, ShaderType::Pixel}) { const auto options = MakeCompileOptions(stage); Check(!ShaderRecompiler::TranslateProgram(ordinary_code, options).skip_dispatch, "BVH-shaped literal marked an ordinary shader for skipping"); const auto compiled = RecompileForTest(ordinary_code, options); Check(!compiled.spirv.empty(), "ordinary shader did not compile"); CheckSpirvBinaryValidates(compiled.spirv); } }