#include "Recompiler.hpp" #include #include #include #include #include #include #include #include #include namespace { void requireInvariantPositions(const ShaderRecompiler::RecompileResult& result, const char* what) { constexpr std::uint32_t Variable = ~0u; const auto& words = result.spirv.Words(); std::set> positions; std::set> invariant; for (std::size_t at = 5; at < words.size() && (words[at] >> 16u) != 0; at += words[at] >> 16u) { const auto op = static_cast(words[at] & 0xffffu); const auto count = words[at] >> 16u; if (op == spv::OpMemberDecorate && count >= 4) { if (words[at + 3] == spv::DecorationBuiltIn && count == 5 && words[at + 4] == spv::BuiltInPosition) positions.emplace(words[at + 1], words[at + 2]); if (words[at + 3] == spv::DecorationInvariant) invariant.emplace(words[at + 1], words[at + 2]); } if (op == spv::OpDecorate && count >= 3) { if (words[at + 2] == spv::DecorationBuiltIn && count == 4 && words[at + 3] == spv::BuiltInPosition) positions.emplace(words[at + 1], Variable); if (words[at + 2] == spv::DecorationInvariant) invariant.emplace(words[at + 1], Variable); } } if (positions.empty()) throw std::runtime_error(std::string(what) + ": the module declares no position output"); for (const auto& position : positions) { if (!invariant.contains(position)) throw std::runtime_error(std::string(what) + ": the position output is not Invariant"); } } constexpr std::array PositionExport{0xf80008cfu, 0u, 0xbf810000u}; ShaderRecompiler::RecompileRequest baseRequest(ShaderRecompiler::ShaderStage stage) { using namespace ShaderRecompiler; RecompileRequest request{}; request.shader = {stage, 0x10000u, PositionExport, 0, {}}; request.context.waveSize = 64; request.context.vertex = ShaderVertexStageInfo{}; request.target.vulkanVersion = 0x00401000u; request.target.spirvVersion = 0x00010300u; request.target.subgroupSize = 64; request.layout.pushConstantSizeBytes = 128; request.useCache = false; return request; } void verifyVertex() { using namespace ShaderRecompiler; requireInvariantPositions(Recompile(baseRequest(ShaderStage::Vertex)), "vertex shader"); } void verifyMesh() { using namespace ShaderRecompiler; static constexpr std::array capabilities{spv::CapabilityMeshShadingEXT}; static constexpr std::array extensions{"SPV_EXT_mesh_shader"}; static constexpr std::array hiddenUserWords{}; auto request = baseRequest(ShaderStage::Mesh); request.context.userData = hiddenUserWords; request.target.spirvVersion = 0x00010400u; request.target.supportedCapabilities = capabilities; request.target.supportedExtensions = extensions; request.target.maxWorkgroupSize = {1024u, 1024u, 64u}; request.target.maxWorkgroupInvocations = 1024u; request.target.maxWorkgroupSharedMemoryBytes = 32768u; request.target.mesh = MeshTargetLimits{{128u, 1u, 1u}, 128u, 32768u, 256u, 256u, 128u, 32768u, 1u, 1u}; request.graphics = GraphicsCompileContext{0u, {}, MeshConfiguration{4u, 1u, 3u, 3u, 1u, 64u, 128u, 0u, 4u}, std::nullopt, {}}; requireInvariantPositions(Recompile(request), "mesh shader"); } } // namespace int main() { try { verifyVertex(); verifyMesh(); std::cout << "Vertex and mesh position outputs are Invariant\n"; return 0; } catch (const std::exception& error) { const std::string message(error.what()); std::cerr << message.substr(0, message.find("RecompileRequest:")) << '\n'; return 1; } }