diff --git a/renderdoc/data/glsl_shaders.cpp b/renderdoc/data/glsl_shaders.cpp index af955dab9..daea6f63e 100644 --- a/renderdoc/data/glsl_shaders.cpp +++ b/renderdoc/data/glsl_shaders.cpp @@ -161,3 +161,1424 @@ std::string GenerateGLSLShader(const std::string &shader, ShaderType type, int v return ret; } + +#if ENABLED(ENABLE_UNIT_TESTS) + +#include "3rdparty/catch/catch.hpp" +#include "driver/shaders/spirv/spirv_reflect.h" + +// define tests of GLSL reflection that can be re-used by both GL emulated and SPIR-V reflection +void TestGLSLReflection(ShaderType testType, ReflectionMaker compile) +{ +#define REQUIRE_ARRAY_SIZE(size, min) \ + REQUIRE(size >= min); \ + CHECK(size == min); + + if(testType == ShaderType::eShaderGLSL || testType == ShaderType::eShaderGLSPIRV) + { + // test GL only features + + SECTION("GL global uniforms") + { + std::string source = R"( +#version 450 core + +layout(location = 100) uniform vec3 global_var[5]; +layout(location = 200) uniform mat3x2 global_var2[3]; + +void main() { + gl_FragDepth = global_var[4].y + global_var2[2][2][0]; +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.constantBlocks.size(), 1); + { + CHECK(refl.constantBlocks[0].name == "$Globals"); + { + const ConstantBlock &cblock = refl.constantBlocks[0]; + INFO("UBO: " << cblock.name.c_str()); + + CHECK(cblock.bindPoint == 0); + CHECK(!cblock.bufferBacked); + + REQUIRE_ARRAY_SIZE(cblock.variables.size(), 2); + { + CHECK(cblock.variables[0].name == "global_var"); + { + const ShaderConstant &member = cblock.variables[0]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 3); + CHECK(member.type.descriptor.elements == 5); + } + + CHECK(cblock.variables[1].name == "global_var2"); + { + const ShaderConstant &member = cblock.variables[1]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 2); + CHECK(member.type.descriptor.columns == 3); + CHECK(member.type.descriptor.elements == 3); + CHECK(member.type.descriptor.rowMajorStorage == false); + } + } + } + } + + REQUIRE_ARRAY_SIZE(mapping.constantBlocks.size(), 1); + { + // $Globals + CHECK(mapping.constantBlocks[1].used); + } + }; + + SECTION("GL atomic counters") + { + std::string source = R"( +#version 450 core + +layout(binding = 0) uniform atomic_uint atom; + +void main() { + gl_FragDepth = float(atomicCounter(atom)); +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.readWriteResources.size(), 1); + { + CHECK(refl.readWriteResources[0].name == "atom"); + { + const ShaderResource &res = refl.readWriteResources[0]; + INFO("read-write resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 0); + CHECK(res.resType == TextureType::Buffer); + CHECK(res.variableType.members.empty()); + CHECK(res.variableType.descriptor.type == VarType::UInt); + CHECK(res.variableType.descriptor.rows == 1); + CHECK(res.variableType.descriptor.columns == 1); + } + } + + REQUIRE_ARRAY_SIZE(mapping.readWriteResources.size(), 1); + { + // atom + CHECK(mapping.readWriteResources[0].bindset == 0); + CHECK(mapping.readWriteResources[0].bind == 0); + CHECK(mapping.readWriteResources[0].arraySize == 1); + CHECK(mapping.readWriteResources[0].used); + } + }; + } + else if(testType == ShaderType::eShaderVulkan) + { + // test Vulkan only features + + SECTION("Vulkan separate sampler objects") + { + std::string source = R"( +#version 450 core + +layout (set=1, binding=2) uniform sampler S; +layout (set=2, binding=4) uniform texture2D T; +layout (set=2, binding=5) uniform sampler2D ST; + +void main() { + gl_FragDepth = textureLod(ST, gl_FragCoord.xy, gl_FragCoord.z).z + + textureLod(sampler2D(T, S), gl_FragCoord.xy, gl_FragCoord.z).z; +} +)"; + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.samplers.size(), 1); + { + CHECK(refl.samplers[0].name == "S"); + { + const ShaderSampler &samp = refl.samplers[0]; + INFO("read-only resource: " << samp.name.c_str()); + + CHECK(samp.bindPoint == 0); + } + } + + REQUIRE_ARRAY_SIZE(mapping.samplers.size(), 1); + { + // S + CHECK(mapping.samplers[0].bindset == 1); + CHECK(mapping.samplers[0].bind == 2); + CHECK(mapping.samplers[0].arraySize == 1); + CHECK(mapping.samplers[0].used); + } + + REQUIRE_ARRAY_SIZE(refl.readOnlyResources.size(), 2); + { + CHECK(refl.readOnlyResources[0].name == "T"); + { + const ShaderResource &res = refl.readOnlyResources[0]; + INFO("read-only resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 0); + CHECK(res.resType == TextureType::Texture2D); + CHECK(res.variableType.members.empty()); + CHECK(res.variableType.descriptor.type == VarType::Float); + } + + CHECK(refl.readOnlyResources[1].name == "ST"); + { + const ShaderResource &res = refl.readOnlyResources[1]; + INFO("read-only resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 1); + CHECK(res.resType == TextureType::Texture2D); + CHECK(res.variableType.members.empty()); + CHECK(res.variableType.descriptor.type == VarType::Float); + } + } + + REQUIRE_ARRAY_SIZE(mapping.readOnlyResources.size(), 2); + { + // T + CHECK(mapping.readOnlyResources[0].bindset == 2); + CHECK(mapping.readOnlyResources[0].bind == 4); + CHECK(mapping.readOnlyResources[0].arraySize == 1); + CHECK(mapping.readOnlyResources[0].used); + + // ST + CHECK(mapping.readOnlyResources[1].bindset == 2); + CHECK(mapping.readOnlyResources[1].bind == 5); + CHECK(mapping.readOnlyResources[1].arraySize == 1); + CHECK(mapping.readOnlyResources[1].used); + } + }; + + SECTION("Vulkan specialization constants") + { + std::string source = R"( +#version 450 core + +layout(constant_id = 17) const int foo = 12; +layout(constant_id = 19) const float bar = 0.5f; + +void main() { + gl_FragDepth = float(foo) + bar; +} +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.constantBlocks.size(), 1); + { + CHECK(refl.constantBlocks[0].name == "Specialization Constants"); + { + const ConstantBlock &cblock = refl.constantBlocks[0]; + INFO("UBO: " << cblock.name.c_str()); + + CHECK(cblock.bindPoint == 0); + CHECK(!cblock.bufferBacked); + CHECK(cblock.byteSize == 0); + + REQUIRE_ARRAY_SIZE(cblock.variables.size(), 2); + { + CHECK(cblock.variables[0].name == "foo"); + { + const ShaderConstant &member = cblock.variables[0]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::SInt); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + CHECK(member.type.descriptor.name == "int"); + CHECK(member.defaultValue == 12); + } + + CHECK(cblock.variables[1].name == "bar"); + { + const ShaderConstant &member = cblock.variables[1]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + CHECK(member.type.descriptor.name == "float"); + } + } + } + } + + REQUIRE_ARRAY_SIZE(mapping.constantBlocks.size(), 1); + { + // spec constants + CHECK(mapping.constantBlocks[0].bindset == SpecializationConstantBindSet); + CHECK(mapping.constantBlocks[0].bind == 0); + CHECK(mapping.constantBlocks[0].arraySize == 1); + CHECK(mapping.constantBlocks[0].used); + } + }; + + SECTION("Vulkan push constants") + { + std::string source = R"( +#version 450 core + +layout(push_constant) uniform push +{ + int a; + float b; + uvec2 c; +} push_data; + +void main() { + gl_FragDepth = push_data.b; +} +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.constantBlocks.size(), 1); + { + CHECK(refl.constantBlocks[0].name == "push_data"); + { + const ConstantBlock &cblock = refl.constantBlocks[0]; + INFO("UBO: " << cblock.name.c_str()); + + CHECK(cblock.bindPoint == 0); + CHECK(!cblock.bufferBacked); + CHECK(cblock.byteSize == 16); + + REQUIRE_ARRAY_SIZE(cblock.variables.size(), 3); + { + CHECK(cblock.variables[0].name == "a"); + { + const ShaderConstant &member = cblock.variables[0]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 0); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::SInt); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + CHECK(member.type.descriptor.name == "int"); + } + + CHECK(cblock.variables[1].name == "b"); + { + const ShaderConstant &member = cblock.variables[1]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 4); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + } + + CHECK(cblock.variables[2].name == "c"); + { + const ShaderConstant &member = cblock.variables[2]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 8); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::UInt); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 2); + } + } + } + } + + REQUIRE_ARRAY_SIZE(mapping.constantBlocks.size(), 1); + { + // push_data + CHECK(mapping.constantBlocks[0].bindset == PushConstantBindSet); + CHECK(mapping.constantBlocks[0].bind == 0); + CHECK(mapping.constantBlocks[0].arraySize == 1); + CHECK(mapping.constantBlocks[0].used); + } + }; + } + else + { + RDCFATAL("Unexpected test type"); + } + + SECTION("Input and output signatures") + { + std::string source = R"( +#version 450 core + +layout(location = 3) in vec2 a_input; +layout(location = 6) flat in uvec3 z_input; + +layout(location = 0) out vec4 a_output; +layout(location = 1) out vec3 z_output; +layout(location = 2) out int b_output; + +void main() { + a_output = vec4(a_input.y + gl_FragCoord.x, 0, 0, 1); + z_output = vec3(a_output.xy, a_output.z); + b_output = int(z_input.x); + gl_FragDepth = float(z_output.y); +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.inputSignature.size(), 3); + { + CHECK(refl.inputSignature[0].varName == "gl_FragCoord"); + { + const SigParameter &sig = refl.inputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Position); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + + CHECK(refl.inputSignature[1].varName == "a_input"); + { + const SigParameter &sig = refl.inputSignature[1]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 3); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + + CHECK(refl.inputSignature[2].varName == "z_input"); + { + const SigParameter &sig = refl.inputSignature[2]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 6); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::UInt); + CHECK(sig.compCount == 3); + CHECK(sig.regChannelMask == 0x7); + CHECK(sig.channelUsedMask == 0x7); + } + } + + REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 4); + { + CHECK(refl.outputSignature[0].varName == "a_output"); + { + const SigParameter &sig = refl.outputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::ColorOutput); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + + CHECK(refl.outputSignature[1].varName == "z_output"); + { + const SigParameter &sig = refl.outputSignature[1]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 1); + CHECK(sig.systemValue == ShaderBuiltin::ColorOutput); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 3); + CHECK(sig.regChannelMask == 0x7); + CHECK(sig.channelUsedMask == 0x7); + } + + CHECK(refl.outputSignature[2].varName == "b_output"); + { + const SigParameter &sig = refl.outputSignature[2]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 2); + CHECK(sig.systemValue == ShaderBuiltin::ColorOutput); + CHECK(sig.compType == CompType::SInt); + CHECK(sig.compCount == 1); + CHECK(sig.regChannelMask == 0x1); + CHECK(sig.channelUsedMask == 0x1); + } + + CHECK(refl.outputSignature[3].varName == "gl_FragDepth"); + { + const SigParameter &sig = refl.outputSignature[3]; + INFO("signature element: " << sig.varName.c_str()); + + // when not running with a driver we default to just using the index instead of looking up + // the location of outputs, so this will be wrong + // CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::DepthOutput); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 1); + CHECK(sig.regChannelMask == 0x1); + CHECK(sig.channelUsedMask == 0x1); + } + } + + REQUIRE_ARRAY_SIZE(mapping.inputAttributes.size(), 16); + for(size_t i = 0; i < mapping.inputAttributes.size(); i++) + { + if(i == 3) + CHECK((mapping.inputAttributes[i] == -1 || mapping.inputAttributes[i] == 1)); + else if(i == 6) + CHECK((mapping.inputAttributes[i] == -1 || mapping.inputAttributes[i] == 2)); + else + CHECK(mapping.inputAttributes[i] == -1); + } + }; + + SECTION("constant buffers") + { + std::string source = R"( +#version 450 core + +struct glstruct +{ + float a; + int b; + mat2x2 c; +}; + +layout(binding = 8, std140) uniform ubo_block { + float ubo_a; + layout(column_major) mat4x3 ubo_b; + layout(row_major) mat4x3 ubo_c; + ivec2 ubo_d; + vec2 ubo_e[3]; + glstruct ubo_f; + layout(offset = 256) vec4 ubo_z; +} ubo_root; + +void main() { + gl_FragDepth = ubo_root.ubo_a; +} +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.constantBlocks.size(), 1); + { + // blocks get different reflected names in SPIR-V + const rdcstr ubo_name = testType == ShaderType::eShaderGLSL ? "ubo_block" : "ubo_root"; + + CHECK(refl.constantBlocks[0].name == ubo_name); + { + const ConstantBlock &cblock = refl.constantBlocks[0]; + INFO("UBO: " << cblock.name.c_str()); + + CHECK(cblock.bindPoint == 0); + CHECK(cblock.bufferBacked); + CHECK(cblock.byteSize == 272); + + // GLSL reflects out a root structure + if(testType == ShaderType::eShaderGLSL) + { + REQUIRE_ARRAY_SIZE(cblock.variables.size(), 1); + + CHECK(cblock.variables[0].name == ubo_name); + } + + const rdcarray &ubo_root = testType == ShaderType::eShaderGLSL + ? cblock.variables[0].type.members + : cblock.variables; + + REQUIRE_ARRAY_SIZE(ubo_root.size(), 7); + { + CHECK(ubo_root[0].name == "ubo_a"); + { + const ShaderConstant &member = ubo_root[0]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 0); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + CHECK(member.type.descriptor.name == "float"); + } + + CHECK(ubo_root[1].name == "ubo_b"); + { + const ShaderConstant &member = ubo_root[1]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 16); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 3); + CHECK(member.type.descriptor.columns == 4); + CHECK(member.type.descriptor.rowMajorStorage == false); + } + + CHECK(ubo_root[2].name == "ubo_c"); + { + const ShaderConstant &member = ubo_root[2]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 80); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 3); + CHECK(member.type.descriptor.columns == 4); + CHECK(member.type.descriptor.rowMajorStorage == true); + } + + CHECK(ubo_root[3].name == "ubo_d"); + { + const ShaderConstant &member = ubo_root[3]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 128); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::SInt); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 2); + } + + CHECK(ubo_root[4].name == "ubo_e"); + { + const ShaderConstant &member = ubo_root[4]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 144); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 2); + CHECK(member.type.descriptor.elements == 3); + CHECK(member.type.descriptor.arrayByteStride == 16); + } + + CHECK(ubo_root[5].name == "ubo_f"); + { + const ShaderConstant &member = ubo_root[5]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 192); + // this doesn't reflect in native introspection, so we skip it + // CHECK(member.type.descriptor.elements == 3); + + REQUIRE_ARRAY_SIZE(member.type.members.size(), 3); + { + CHECK(member.type.members[0].name == "a"); + { + const ShaderConstant &submember = member.type.members[0]; + INFO("UBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 0); + CHECK(submember.type.members.empty()); + CHECK(submember.type.descriptor.type == VarType::Float); + CHECK(submember.type.descriptor.rows == 1); + CHECK(submember.type.descriptor.columns == 1); + CHECK(submember.type.descriptor.name == "float"); + } + + CHECK(member.type.members[1].name == "b"); + { + const ShaderConstant &submember = member.type.members[1]; + INFO("UBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 4); + CHECK(submember.type.members.empty()); + CHECK(submember.type.descriptor.type == VarType::SInt); + CHECK(submember.type.descriptor.rows == 1); + CHECK(submember.type.descriptor.columns == 1); + CHECK(submember.type.descriptor.name == "int"); + } + + CHECK(member.type.members[2].name == "c"); + { + const ShaderConstant &submember = member.type.members[2]; + INFO("UBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 16); + CHECK(submember.type.members.empty()); + CHECK(submember.type.descriptor.type == VarType::Float); + CHECK(submember.type.descriptor.rows == 2); + CHECK(submember.type.descriptor.columns == 2); + CHECK(submember.type.descriptor.rowMajorStorage == false); + } + } + } + + CHECK(ubo_root[6].name == "ubo_z"); + { + const ShaderConstant &member = ubo_root[6]; + INFO("UBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 256); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 4); + } + } + } + } + + REQUIRE_ARRAY_SIZE(mapping.constantBlocks.size(), 1); + { + // ubo + CHECK(mapping.constantBlocks[0].bindset == 0); + CHECK(mapping.constantBlocks[0].bind == 8); + CHECK(mapping.constantBlocks[0].arraySize == 1); + CHECK(mapping.constantBlocks[0].used); + } + }; + + SECTION("Textures") + { + std::string source = R"( +#version 450 core + +layout(binding = 3) uniform sampler2D tex2D; +layout(binding = 5) uniform isampler3D tex3D; +layout(binding = 7) uniform samplerBuffer texBuf; + +void main() { + gl_FragDepth = textureLod(tex2D, gl_FragCoord.xy, gl_FragCoord.z).z + + float(texelFetch(tex3D, ivec3(gl_FragCoord.xyz), 0).y) + + texelFetch(texBuf, int(gl_FragCoord.x)).x; +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.readOnlyResources.size(), 3); + { + CHECK(refl.readOnlyResources[0].name == "tex2D"); + { + const ShaderResource &res = refl.readOnlyResources[0]; + INFO("read-only resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 0); + CHECK(res.resType == TextureType::Texture2D); + CHECK(res.variableType.members.empty()); + CHECK(res.variableType.descriptor.type == VarType::Float); + } + + CHECK(refl.readOnlyResources[1].name == "tex3D"); + { + const ShaderResource &res = refl.readOnlyResources[1]; + INFO("read-only resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 1); + CHECK(res.resType == TextureType::Texture3D); + CHECK(res.variableType.members.empty()); + CHECK(res.variableType.descriptor.type == VarType::SInt); + } + + CHECK(refl.readOnlyResources[2].name == "texBuf"); + { + const ShaderResource &res = refl.readOnlyResources[2]; + INFO("read-only resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 2); + CHECK(res.resType == TextureType::Buffer); + CHECK(res.variableType.members.empty()); + CHECK(res.variableType.descriptor.type == VarType::Float); + } + } + + REQUIRE_ARRAY_SIZE(mapping.readOnlyResources.size(), 3); + { + // tex2d + CHECK(mapping.readOnlyResources[0].bindset == 0); + CHECK(mapping.readOnlyResources[0].bind == 3); + CHECK(mapping.readOnlyResources[0].arraySize == 1); + CHECK(mapping.readOnlyResources[0].used); + + // tex3d + CHECK(mapping.readOnlyResources[1].bindset == 0); + CHECK(mapping.readOnlyResources[1].bind == 5); + CHECK(mapping.readOnlyResources[1].arraySize == 1); + CHECK(mapping.readOnlyResources[1].used); + + // texBuf + CHECK(mapping.readOnlyResources[2].bindset == 0); + CHECK(mapping.readOnlyResources[2].bind == 7); + CHECK(mapping.readOnlyResources[2].arraySize == 1); + CHECK(mapping.readOnlyResources[2].used); + } + }; + + SECTION("SSBOs") + { + std::string source = R"( +#version 450 core + +struct glstruct +{ + float a; + int b; + mat2x2 c; +}; + +layout(binding = 2, std430) buffer ssbo +{ + uint ssbo_a[10]; + glstruct ssbo_b[3]; + float ssbo_c; +} ssbo_root; + +struct nested +{ + glstruct first; + glstruct second; +}; + +layout(binding = 5, std430) buffer ssbo2 +{ + nested n[]; +} ssbo_root2; + +void main() { + ssbo_root.ssbo_a[5] = 4; + ssbo_root.ssbo_b[1].b = 6; + ssbo_root2.n[5].second.b = 4; + gl_FragDepth = ssbo_root.ssbo_c + ssbo_root2.n[0].first.a; +} + +)"; + +#define REQUIRE_ARRAY_SIZE(size, min) \ + REQUIRE(size >= min); \ + CHECK(size == min); + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Fragment, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.readWriteResources.size(), 2); + { + // blocks get different reflected names in SPIR-V + const rdcstr ssbo_name = testType == ShaderType::eShaderGLSL ? "ssbo" : "ssbo_root"; + + CHECK(refl.readWriteResources[0].name == ssbo_name); + { + const ShaderResource &res = refl.readWriteResources[0]; + INFO("read-write resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 0); + CHECK(res.resType == TextureType::Buffer); + + REQUIRE_ARRAY_SIZE(res.variableType.members.size(), 3); + { + CHECK(res.variableType.members[0].name == "ssbo_a"); + { + const ShaderConstant &member = res.variableType.members[0]; + INFO("SSBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 0); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::UInt); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + CHECK(member.type.descriptor.elements == 10); + CHECK(member.type.descriptor.arrayByteStride == 4); + CHECK(member.type.descriptor.name == "uint"); + } + + CHECK(res.variableType.members[1].name == "ssbo_b"); + { + const ShaderConstant &member = res.variableType.members[1]; + INFO("SSBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 40); + // this doesn't reflect in native introspection, so we skip it + // CHECK(member.type.descriptor.elements == 3); + CHECK(member.type.descriptor.arrayByteStride == 24); + + REQUIRE_ARRAY_SIZE(member.type.members.size(), 3); + { + CHECK(member.type.members[0].name == "a"); + { + const ShaderConstant &submember = member.type.members[0]; + INFO("SSBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 0); + CHECK(submember.type.members.empty()); + CHECK(submember.type.descriptor.type == VarType::Float); + CHECK(submember.type.descriptor.rows == 1); + CHECK(submember.type.descriptor.columns == 1); + CHECK(submember.type.descriptor.name == "float"); + } + + CHECK(member.type.members[1].name == "b"); + { + const ShaderConstant &submember = member.type.members[1]; + INFO("SSBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 4); + CHECK(submember.type.members.empty()); + CHECK(submember.type.descriptor.type == VarType::SInt); + CHECK(submember.type.descriptor.rows == 1); + CHECK(submember.type.descriptor.columns == 1); + CHECK(submember.type.descriptor.name == "int"); + } + + CHECK(member.type.members[2].name == "c"); + { + const ShaderConstant &submember = member.type.members[2]; + INFO("SSBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 8); + CHECK(submember.type.members.empty()); + CHECK(submember.type.descriptor.type == VarType::Float); + CHECK(submember.type.descriptor.rows == 2); + CHECK(submember.type.descriptor.columns == 2); + CHECK(submember.type.descriptor.rowMajorStorage == false); + } + } + } + + CHECK(res.variableType.members[2].name == "ssbo_c"); + { + const ShaderConstant &member = res.variableType.members[2]; + INFO("SSBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 112); + CHECK(member.type.members.empty()); + CHECK(member.type.descriptor.type == VarType::Float); + CHECK(member.type.descriptor.rows == 1); + CHECK(member.type.descriptor.columns == 1); + CHECK(member.type.descriptor.name == "float"); + } + } + } + + CHECK(refl.readWriteResources[1].name == (ssbo_name + "2")); + { + const ShaderResource &res = refl.readWriteResources[1]; + INFO("read-write resource: " << res.name.c_str()); + + CHECK(res.bindPoint == 1); + CHECK(res.resType == TextureType::Buffer); + + REQUIRE_ARRAY_SIZE(res.variableType.members.size(), 1); + { + CHECK(res.variableType.members[0].name == "n"); + { + const ShaderConstant &member = res.variableType.members[0]; + INFO("SSBO member: " << member.name.c_str()); + + CHECK(member.byteOffset == 0); + CHECK(member.type.descriptor.arrayByteStride == 48); + + REQUIRE_ARRAY_SIZE(member.type.members.size(), 2); + { + CHECK(member.type.members[0].name == "first"); + { + const ShaderConstant &submember = member.type.members[0]; + INFO("SSBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 0); + + REQUIRE_ARRAY_SIZE(submember.type.members.size(), 3); + { + CHECK(submember.type.members[0].name == "a"); + { + const ShaderConstant &subsubmember = submember.type.members[0]; + INFO("SSBO subsubmember: " << subsubmember.name.c_str()); + + CHECK(subsubmember.byteOffset == 0); + CHECK(subsubmember.type.members.empty()); + CHECK(subsubmember.type.descriptor.type == VarType::Float); + CHECK(subsubmember.type.descriptor.rows == 1); + CHECK(subsubmember.type.descriptor.columns == 1); + CHECK(subsubmember.type.descriptor.name == "float"); + } + + CHECK(submember.type.members[1].name == "b"); + { + const ShaderConstant &subsubmember = submember.type.members[1]; + INFO("SSBO subsubmember: " << subsubmember.name.c_str()); + + CHECK(subsubmember.byteOffset == 4); + CHECK(subsubmember.type.members.empty()); + CHECK(subsubmember.type.descriptor.type == VarType::SInt); + CHECK(subsubmember.type.descriptor.rows == 1); + CHECK(subsubmember.type.descriptor.columns == 1); + CHECK(subsubmember.type.descriptor.name == "int"); + } + + CHECK(submember.type.members[2].name == "c"); + { + const ShaderConstant &subsubmember = submember.type.members[2]; + INFO("SSBO subsubmember: " << subsubmember.name.c_str()); + + CHECK(subsubmember.byteOffset == 8); + CHECK(subsubmember.type.members.empty()); + CHECK(subsubmember.type.descriptor.type == VarType::Float); + CHECK(subsubmember.type.descriptor.rows == 2); + CHECK(subsubmember.type.descriptor.columns == 2); + CHECK(subsubmember.type.descriptor.rowMajorStorage == false); + } + } + } + + CHECK(member.type.members[1].name == "second"); + { + const ShaderConstant &submember = member.type.members[1]; + INFO("SSBO submember: " << submember.name.c_str()); + + CHECK(submember.byteOffset == 24); + + REQUIRE_ARRAY_SIZE(submember.type.members.size(), 3); + { + CHECK(submember.type.members[0].name == "a"); + { + const ShaderConstant &subsubmember = submember.type.members[0]; + INFO("SSBO subsubmember: " << subsubmember.name.c_str()); + + CHECK(subsubmember.byteOffset == 0); + CHECK(subsubmember.type.members.empty()); + CHECK(subsubmember.type.descriptor.type == VarType::Float); + CHECK(subsubmember.type.descriptor.rows == 1); + CHECK(subsubmember.type.descriptor.columns == 1); + CHECK(subsubmember.type.descriptor.name == "float"); + } + + CHECK(submember.type.members[1].name == "b"); + { + const ShaderConstant &subsubmember = submember.type.members[1]; + INFO("SSBO subsubmember: " << subsubmember.name.c_str()); + + CHECK(subsubmember.byteOffset == 4); + CHECK(subsubmember.type.members.empty()); + CHECK(subsubmember.type.descriptor.type == VarType::SInt); + CHECK(subsubmember.type.descriptor.rows == 1); + CHECK(subsubmember.type.descriptor.columns == 1); + CHECK(subsubmember.type.descriptor.name == "int"); + } + + CHECK(submember.type.members[2].name == "c"); + { + const ShaderConstant &subsubmember = submember.type.members[2]; + INFO("SSBO subsubmember: " << subsubmember.name.c_str()); + + CHECK(subsubmember.byteOffset == 8); + CHECK(subsubmember.type.members.empty()); + CHECK(subsubmember.type.descriptor.type == VarType::Float); + CHECK(subsubmember.type.descriptor.rows == 2); + CHECK(subsubmember.type.descriptor.columns == 2); + CHECK(subsubmember.type.descriptor.rowMajorStorage == false); + } + } + } + } + } + } + } + } + + REQUIRE_ARRAY_SIZE(mapping.readWriteResources.size(), 2); + { + // ssbo + CHECK(mapping.readWriteResources[0].bindset == 0); + CHECK(mapping.readWriteResources[0].bind == 2); + CHECK(mapping.readWriteResources[0].arraySize == 1); + CHECK(mapping.readWriteResources[0].used); + + // ssbo2 + CHECK(mapping.readWriteResources[1].bindset == 0); + CHECK(mapping.readWriteResources[1].bind == 5); + CHECK(mapping.readWriteResources[1].arraySize == 1); + CHECK(mapping.readWriteResources[1].used); + } + }; + + SECTION("vertex shader fixed function outputs") + { + std::string source = R"( +#version 450 core + +void main() { + gl_Position = vec4(0, 1, 0, 1); +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Vertex, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 1); + { + CHECK(refl.outputSignature[0].varName.contains("gl_Position")); + { + const SigParameter &sig = refl.outputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Position); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + } + + std::string source2 = R"( +#version 450 core + +void main() { + gl_Position = vec4(0, 1, 0, 1); + gl_PointSize = 1.5f; +} + +)"; + + refl = ShaderReflection(); + mapping = ShaderBindpointMapping(); + compile(ShaderStage::Vertex, source2, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 2); + { + CHECK(refl.outputSignature[0].varName.contains("gl_Position")); + { + const SigParameter &sig = refl.outputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Position); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + + CHECK(refl.outputSignature[1].varName.contains("gl_PointSize")); + { + const SigParameter &sig = refl.outputSignature[1]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::PointSize); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 1); + CHECK(sig.regChannelMask == 0x1); + CHECK(sig.channelUsedMask == 0x1); + } + } + }; + + SECTION("matrix and array outputs") + { + std::string source = R"( +#version 450 core + +layout(location = 0) out vec3 outarr[3]; +layout(location = 6) out mat2 outmat; +layout(location = 9) out mat2 outmatarr[2]; + +void main() +{ + gl_Position = vec4(0, 0, 0, 1); + outarr[0] = gl_Position.xyz; + outarr[1] = gl_Position.xyz; + outarr[2] = gl_Position.xyz; + outmat = mat2(0, 0, 0, 0); + outmatarr[0] = mat2(0, 0, 0, 0); + outmatarr[1] = mat2(0, 0, 0, 0); +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Vertex, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 10); + { + CHECK(refl.outputSignature[0].varName.contains("gl_Position")); + { + const SigParameter &sig = refl.outputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Position); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + + CHECK(refl.outputSignature[1].varName == "outarr[0]"); + { + const SigParameter &sig = refl.outputSignature[1]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.arrayIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 3); + CHECK(sig.regChannelMask == 0x7); + CHECK(sig.channelUsedMask == 0x7); + } + + CHECK(refl.outputSignature[2].varName == "outarr[1]"); + { + const SigParameter &sig = refl.outputSignature[2]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 1); + CHECK(sig.arrayIndex == 1); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 3); + CHECK(sig.regChannelMask == 0x7); + CHECK(sig.channelUsedMask == 0x7); + } + + CHECK(refl.outputSignature[3].varName == "outarr[2]"); + { + const SigParameter &sig = refl.outputSignature[3]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 2); + CHECK(sig.arrayIndex == 2); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 3); + CHECK(sig.regChannelMask == 0x7); + CHECK(sig.channelUsedMask == 0x7); + } + + CHECK(refl.outputSignature[4].varName == "outmat:row0"); + { + const SigParameter &sig = refl.outputSignature[4]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 6); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + + CHECK(refl.outputSignature[5].varName == "outmat:row1"); + { + const SigParameter &sig = refl.outputSignature[5]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 7); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + + CHECK(refl.outputSignature[6].varName == "outmatarr[0]:row0"); + { + const SigParameter &sig = refl.outputSignature[6]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 9); + CHECK(sig.arrayIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + + CHECK(refl.outputSignature[7].varName == "outmatarr[0]:row1"); + { + const SigParameter &sig = refl.outputSignature[7]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 10); + CHECK(sig.arrayIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + + CHECK(refl.outputSignature[8].varName == "outmatarr[1]:row0"); + { + const SigParameter &sig = refl.outputSignature[8]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 11); + CHECK(sig.arrayIndex == 1); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + + CHECK(refl.outputSignature[9].varName == "outmatarr[1]:row1"); + { + const SigParameter &sig = refl.outputSignature[9]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 12); + CHECK(sig.arrayIndex == 1); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + } + }; + + SECTION("shader input/output blocks") + { + std::string source = R"( +#version 450 core + +layout(triangles) in; +layout(triangle_strip, max_vertices = 4) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +layout(location = 0) in block +{ + vec2 Texcoord; +} In[]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(location = 0) out block +{ + vec2 Texcoord; +} Out; + +void main() +{ + for(int i = 0; i < gl_in.length(); ++i) + { + gl_Position = gl_in[i].gl_Position; + Out.Texcoord = In[i].Texcoord; + EmitVertex(); + } + EndPrimitive(); +} + +)"; + + ShaderReflection refl; + ShaderBindpointMapping mapping; + compile(ShaderStage::Geometry, source, "main", refl, mapping); + + REQUIRE_ARRAY_SIZE(refl.inputSignature.size(), 2); + { + // blocks get different reflected names in SPIR-V + const rdcstr gl_in_name = testType == ShaderType::eShaderGLSL ? "gl_PerVertex" : "gl_in"; + const rdcstr block_name = testType == ShaderType::eShaderGLSL ? "block" : "In"; + + CHECK(refl.inputSignature[0].varName == (gl_in_name + ".gl_Position")); + { + const SigParameter &sig = refl.inputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Position); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + + CHECK(refl.inputSignature[1].varName == (block_name + ".Texcoord")); + { + const SigParameter &sig = refl.inputSignature[1]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + } + + REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 2); + { + const rdcstr block_name = testType == ShaderType::eShaderGLSL ? "block" : "Out"; + + CHECK(refl.outputSignature[0].varName.contains("gl_Position")); + { + const SigParameter &sig = refl.outputSignature[0]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Position); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 4); + CHECK(sig.regChannelMask == 0xf); + CHECK(sig.channelUsedMask == 0xf); + } + + CHECK(refl.outputSignature[1].varName == (block_name + ".Texcoord")); + { + const SigParameter &sig = refl.outputSignature[1]; + INFO("signature element: " << sig.varName.c_str()); + + CHECK(sig.regIndex == 0); + CHECK(sig.systemValue == ShaderBuiltin::Undefined); + CHECK(sig.compType == CompType::Float); + CHECK(sig.compCount == 2); + CHECK(sig.regChannelMask == 0x3); + CHECK(sig.channelUsedMask == 0x3); + } + } + }; +} + +#endif \ No newline at end of file diff --git a/renderdoc/data/glsl_shaders.h b/renderdoc/data/glsl_shaders.h index 3cdf908c0..de5df4298 100644 --- a/renderdoc/data/glsl_shaders.h +++ b/renderdoc/data/glsl_shaders.h @@ -30,8 +30,18 @@ enum ShaderType eShaderGLSPIRV, }; +#include #include #include std::string GenerateGLSLShader(const std::string &shader, ShaderType type, int version, const std::string &defines = ""); + +// for unit tests +struct ShaderReflection; +struct ShaderBindpointMapping; +enum class ShaderStage : uint32_t; +using ReflectionMaker = + std::function; +void TestGLSLReflection(ShaderType testType, ReflectionMaker compile); \ No newline at end of file diff --git a/renderdoc/driver/gl/wrappers/gl_emulated.cpp b/renderdoc/driver/gl/wrappers/gl_emulated.cpp index 670fcc33c..37f82ce36 100644 --- a/renderdoc/driver/gl/wrappers/gl_emulated.cpp +++ b/renderdoc/driver/gl/wrappers/gl_emulated.cpp @@ -3317,6 +3317,7 @@ void GLDispatchTable::DriverForEmulation(WrappedOpenGL *driver) #include "../gl_shader_refl.h" #include "3rdparty/catch/catch.hpp" +#include "data/glsl_shaders.h" #include "strings/string_utils.h" GLint APIENTRY _testStub_GetUniformLocation(GLuint program, const GLchar *name) @@ -3430,11 +3431,14 @@ GLint APIENTRY _testStub_AttribLocation(GLuint program, const GLchar *name) } void MakeOfflineShaderReflection(ShaderStage stage, const std::string &source, - ShaderReflection &refl, ShaderBindpointMapping &mapping) + const std::string &entryPoint, ShaderReflection &refl, + ShaderBindpointMapping &mapping) { InitSPIRVCompiler(); RenderDoc::Inst().RegisterShutdownFunction(&ShutdownSPIRVCompiler); + RDCASSERT(entryPoint == "main"); + // as a hack, create a local 'driver' and just populate m_Programs with what we want. GLDummyPlatform dummy; WrappedOpenGL driver(dummy); @@ -3487,11 +3491,14 @@ void MakeOfflineShaderReflection(ShaderStage stage, const std::string &source, // // Note that we can't fill out ShaderBindpointMapping easily on the actual driver void MakeOnlineShaderReflection(ShaderStage stage, const std::string &source, - ShaderReflection &refl, ShaderBindpointMapping &mapping) + const std::string &entryPoint, ShaderReflection &refl, + ShaderBindpointMapping &mapping) { ReplayStatus status = ReplayStatus::UnknownError; IReplayDriver *driver = NULL; + RDCASSERT(entryPoint == "main"); + std::map replays = RenderDoc::Inst().GetReplayDrivers(); if(replays.find(RDCDriver::OpenGL) != replays.end()) @@ -3524,859 +3531,9 @@ void MakeOnlineShaderReflection(ShaderStage stage, const std::string &source, driver->Shutdown(); } -TEST_CASE("Validate ARB_program_interface_query emulation", "[opengl][glslang]") +TEST_CASE("Validate ARB_program_interface_query emulation", "[opengl][glslang][reflection]") { - SECTION("Single shader deep dive") - { - std::string source = R"( -#version 450 core - -struct glstruct -{ - float a; - int b; - mat2x2 c; -}; - -layout(binding = 8, std140) uniform ubo_block { - float ubo_a; - layout(column_major) mat4x3 ubo_b; - layout(row_major) mat4x3 ubo_c; - ivec2 ubo_d; - vec2 ubo_e[3]; - glstruct ubo_f; - layout(offset = 256) vec4 ubo_z; -} ubo_root; - -layout(binding = 2, std430) buffer ssbo -{ - uint ssbo_a[10]; - glstruct ssbo_b[3]; - float ssbo_c; -} ssbo_root; - -layout(binding = 0) uniform atomic_uint atom; - -layout(location = 3) in vec2 a_input; -layout(location = 6) flat in uvec3 z_input; - -uniform vec3 global_var[5]; -uniform mat3x2 global_var2[3]; - -layout(binding = 3) uniform sampler2D tex2D; -layout(binding = 5) uniform isampler3D tex3D; - -layout(location = 0) out vec4 a_output; -layout(location = 1) out vec3 z_output; -layout(location = 2) out int b_output; - -void main() { - float a = ubo_root.ubo_a + global_var2[2][0][1]; - a_output = vec4(sin(float(a) + gl_FragCoord.x), 0, 0, 1); - z_output = textureLod(tex2D, a_output.xy, a_output.z).xyz + a_input.xyx + global_var[4]; - ssbo_root.ssbo_a[5] = 4 + atomicCounter(atom) + z_input.y; - b_output = ssbo_root.ssbo_b[2].b + texelFetch(tex3D, ivec3(z_input), 0).x; - gl_FragDepth = z_output.y; -} - -)"; - -#define REQUIRE_ARRAY_SIZE(size, min) \ - REQUIRE(size >= min); \ - CHECK(size == min); - - ShaderReflection refl; - ShaderBindpointMapping mapping; - MakeOfflineShaderReflection(ShaderStage::Fragment, source, refl, mapping); - - REQUIRE_ARRAY_SIZE(refl.inputSignature.size(), 3); - { - CHECK(refl.inputSignature[0].varName == "gl_FragCoord"); - { - const SigParameter &sig = refl.inputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Position); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - - CHECK(refl.inputSignature[1].varName == "a_input"); - { - const SigParameter &sig = refl.inputSignature[1]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 3); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 2); - CHECK(sig.regChannelMask == 0x3); - CHECK(sig.channelUsedMask == 0x3); - } - - CHECK(refl.inputSignature[2].varName == "z_input"); - { - const SigParameter &sig = refl.inputSignature[2]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 6); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::UInt); - CHECK(sig.compCount == 3); - CHECK(sig.regChannelMask == 0x7); - CHECK(sig.channelUsedMask == 0x7); - } - } - - REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 4); - { - CHECK(refl.outputSignature[0].varName == "a_output"); - { - const SigParameter &sig = refl.outputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::ColorOutput); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - - CHECK(refl.outputSignature[1].varName == "z_output"); - { - const SigParameter &sig = refl.outputSignature[1]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 1); - CHECK(sig.systemValue == ShaderBuiltin::ColorOutput); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 3); - CHECK(sig.regChannelMask == 0x7); - CHECK(sig.channelUsedMask == 0x7); - } - - CHECK(refl.outputSignature[2].varName == "b_output"); - { - const SigParameter &sig = refl.outputSignature[2]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 2); - CHECK(sig.systemValue == ShaderBuiltin::ColorOutput); - CHECK(sig.compType == CompType::SInt); - CHECK(sig.compCount == 1); - CHECK(sig.regChannelMask == 0x1); - CHECK(sig.channelUsedMask == 0x1); - } - - CHECK(refl.outputSignature[3].varName == "gl_FragDepth"); - { - const SigParameter &sig = refl.outputSignature[3]; - INFO("signature element: " << sig.varName.c_str()); - - // when not running with a driver we default to just using the index instead of looking up - // the location of outputs, so this will be wrong - // CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::DepthOutput); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 1); - CHECK(sig.regChannelMask == 0x1); - CHECK(sig.channelUsedMask == 0x1); - } - } - - REQUIRE_ARRAY_SIZE(refl.readOnlyResources.size(), 2); - { - CHECK(refl.readOnlyResources[0].name == "tex2D"); - { - const ShaderResource &res = refl.readOnlyResources[0]; - INFO("read-only resource: " << res.name.c_str()); - - CHECK(res.bindPoint == 0); - CHECK(res.resType == TextureType::Texture2D); - CHECK(res.variableType.members.empty()); - CHECK(res.variableType.descriptor.type == VarType::Float); - CHECK(res.variableType.descriptor.rows == 1); - CHECK(res.variableType.descriptor.columns == 4); - CHECK(res.variableType.descriptor.name == "sampler2D"); - } - - CHECK(refl.readOnlyResources[1].name == "tex3D"); - { - const ShaderResource &res = refl.readOnlyResources[1]; - INFO("read-only resource: " << res.name.c_str()); - - CHECK(res.bindPoint == 1); - CHECK(res.resType == TextureType::Texture3D); - CHECK(res.variableType.members.empty()); - CHECK(res.variableType.descriptor.type == VarType::SInt); - CHECK(res.variableType.descriptor.rows == 1); - CHECK(res.variableType.descriptor.columns == 4); - CHECK(res.variableType.descriptor.name == "isampler3D"); - } - } - - REQUIRE_ARRAY_SIZE(refl.readWriteResources.size(), 2); - { - CHECK(refl.readWriteResources[0].name == "atom"); - { - const ShaderResource &res = refl.readWriteResources[0]; - INFO("read-write resource: " << res.name.c_str()); - - CHECK(res.bindPoint == 0); - CHECK(res.resType == TextureType::Buffer); - CHECK(res.variableType.members.empty()); - CHECK(res.variableType.descriptor.type == VarType::UInt); - CHECK(res.variableType.descriptor.rows == 1); - CHECK(res.variableType.descriptor.columns == 1); - CHECK(res.variableType.descriptor.name == "atomic_uint"); - } - - CHECK(refl.readWriteResources[1].name == "ssbo"); - { - const ShaderResource &res = refl.readWriteResources[1]; - INFO("read-write resource: " << res.name.c_str()); - - CHECK(res.bindPoint == 1); - CHECK(res.resType == TextureType::Buffer); - CHECK(res.variableType.descriptor.type == VarType::UInt); - CHECK(res.variableType.descriptor.rows == 0); - CHECK(res.variableType.descriptor.columns == 0); - CHECK(res.variableType.descriptor.name == "buffer"); - - REQUIRE_ARRAY_SIZE(res.variableType.members.size(), 3); - { - CHECK(res.variableType.members[0].name == "ssbo_a"); - { - const ShaderConstant &member = res.variableType.members[0]; - INFO("SSBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 0); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::UInt); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 1); - CHECK(member.type.descriptor.elements == 10); - CHECK(member.type.descriptor.arrayByteStride == 4); - CHECK(member.type.descriptor.name == "uint"); - } - - CHECK(res.variableType.members[1].name == "ssbo_b"); - { - const ShaderConstant &member = res.variableType.members[1]; - INFO("SSBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 40); - // this doesn't reflect in native introspection, so we skip it - // CHECK(member.type.descriptor.elements == 3); - CHECK(member.type.descriptor.name == "struct"); - CHECK(member.type.descriptor.arrayByteStride == 24); - - REQUIRE_ARRAY_SIZE(member.type.members.size(), 3); - { - CHECK(member.type.members[0].name == "a"); - { - const ShaderConstant &submember = member.type.members[0]; - INFO("SSBO submember: " << submember.name.c_str()); - - CHECK(submember.byteOffset == 0); - CHECK(submember.type.members.empty()); - CHECK(submember.type.descriptor.type == VarType::Float); - CHECK(submember.type.descriptor.rows == 1); - CHECK(submember.type.descriptor.columns == 1); - CHECK(submember.type.descriptor.name == "float"); - } - - CHECK(member.type.members[1].name == "b"); - { - const ShaderConstant &submember = member.type.members[1]; - INFO("SSBO submember: " << submember.name.c_str()); - - CHECK(submember.byteOffset == 4); - CHECK(submember.type.members.empty()); - CHECK(submember.type.descriptor.type == VarType::SInt); - CHECK(submember.type.descriptor.rows == 1); - CHECK(submember.type.descriptor.columns == 1); - CHECK(submember.type.descriptor.name == "int"); - } - - CHECK(member.type.members[2].name == "c"); - { - const ShaderConstant &submember = member.type.members[2]; - INFO("SSBO submember: " << submember.name.c_str()); - - CHECK(submember.byteOffset == 8); - CHECK(submember.type.members.empty()); - CHECK(submember.type.descriptor.type == VarType::Float); - CHECK(submember.type.descriptor.rows == 2); - CHECK(submember.type.descriptor.columns == 2); - CHECK(submember.type.descriptor.rowMajorStorage == false); - CHECK(submember.type.descriptor.name == "mat2"); - } - } - } - - CHECK(res.variableType.members[2].name == "ssbo_c"); - { - const ShaderConstant &member = res.variableType.members[2]; - INFO("SSBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 112); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 1); - CHECK(member.type.descriptor.name == "float"); - } - } - } - } - - REQUIRE_ARRAY_SIZE(refl.constantBlocks.size(), 2); - { - CHECK(refl.constantBlocks[0].name == "ubo_block"); - { - const ConstantBlock &cblock = refl.constantBlocks[0]; - INFO("UBO: " << cblock.name.c_str()); - - CHECK(cblock.bindPoint == 0); - CHECK(cblock.bufferBacked); - CHECK(cblock.byteSize == 272); - - REQUIRE_ARRAY_SIZE(cblock.variables.size(), 1); - - CHECK(cblock.variables[0].name == "ubo_block"); - const ShaderConstant &ubo_root = cblock.variables[0]; - - CHECK(ubo_root.byteOffset == 0); - CHECK(ubo_root.type.descriptor.name == "struct"); - - REQUIRE_ARRAY_SIZE(ubo_root.type.members.size(), 7); - { - CHECK(ubo_root.type.members[0].name == "ubo_a"); - { - const ShaderConstant &member = ubo_root.type.members[0]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 0); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 1); - CHECK(member.type.descriptor.name == "float"); - } - - CHECK(ubo_root.type.members[1].name == "ubo_b"); - { - const ShaderConstant &member = ubo_root.type.members[1]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 16); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 3); - CHECK(member.type.descriptor.columns == 4); - CHECK(member.type.descriptor.rowMajorStorage == false); - CHECK(member.type.descriptor.name == "mat4x3"); - } - - CHECK(ubo_root.type.members[2].name == "ubo_c"); - { - const ShaderConstant &member = ubo_root.type.members[2]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 80); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 3); - CHECK(member.type.descriptor.columns == 4); - CHECK(member.type.descriptor.rowMajorStorage == true); - CHECK(member.type.descriptor.name == "mat4x3"); - } - - CHECK(ubo_root.type.members[3].name == "ubo_d"); - { - const ShaderConstant &member = ubo_root.type.members[3]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 128); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::SInt); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 2); - CHECK(member.type.descriptor.name == "ivec2"); - } - - CHECK(ubo_root.type.members[4].name == "ubo_e"); - { - const ShaderConstant &member = ubo_root.type.members[4]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 144); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 2); - CHECK(member.type.descriptor.elements == 3); - CHECK(member.type.descriptor.arrayByteStride == 16); - CHECK(member.type.descriptor.name == "vec2"); - } - - CHECK(ubo_root.type.members[5].name == "ubo_f"); - { - const ShaderConstant &member = ubo_root.type.members[5]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 192); - // this doesn't reflect in native introspection, so we skip it - // CHECK(member.type.descriptor.elements == 3); - CHECK(member.type.descriptor.name == "struct"); - - REQUIRE_ARRAY_SIZE(member.type.members.size(), 3); - { - CHECK(member.type.members[0].name == "a"); - { - const ShaderConstant &submember = member.type.members[0]; - INFO("UBO submember: " << submember.name.c_str()); - - CHECK(submember.byteOffset == 0); - CHECK(submember.type.members.empty()); - CHECK(submember.type.descriptor.type == VarType::Float); - CHECK(submember.type.descriptor.rows == 1); - CHECK(submember.type.descriptor.columns == 1); - CHECK(submember.type.descriptor.name == "float"); - } - - CHECK(member.type.members[1].name == "b"); - { - const ShaderConstant &submember = member.type.members[1]; - INFO("UBO submember: " << submember.name.c_str()); - - CHECK(submember.byteOffset == 4); - CHECK(submember.type.members.empty()); - CHECK(submember.type.descriptor.type == VarType::SInt); - CHECK(submember.type.descriptor.rows == 1); - CHECK(submember.type.descriptor.columns == 1); - CHECK(submember.type.descriptor.name == "int"); - } - - CHECK(member.type.members[2].name == "c"); - { - const ShaderConstant &submember = member.type.members[2]; - INFO("UBO submember: " << submember.name.c_str()); - - CHECK(submember.byteOffset == 16); - CHECK(submember.type.members.empty()); - CHECK(submember.type.descriptor.type == VarType::Float); - CHECK(submember.type.descriptor.rows == 2); - CHECK(submember.type.descriptor.columns == 2); - CHECK(submember.type.descriptor.rowMajorStorage == false); - CHECK(submember.type.descriptor.name == "mat2"); - } - } - } - - CHECK(ubo_root.type.members[6].name == "ubo_z"); - { - const ShaderConstant &member = ubo_root.type.members[6]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.byteOffset == 256); - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 4); - CHECK(member.type.descriptor.name == "vec4"); - } - } - } - - CHECK(refl.constantBlocks[1].name == "$Globals"); - { - const ConstantBlock &cblock = refl.constantBlocks[1]; - INFO("UBO: " << cblock.name.c_str()); - - CHECK(cblock.bindPoint == 1); - CHECK(!cblock.bufferBacked); - - REQUIRE_ARRAY_SIZE(cblock.variables.size(), 2); - { - CHECK(cblock.variables[0].name == "global_var"); - { - const ShaderConstant &member = cblock.variables[0]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 1); - CHECK(member.type.descriptor.columns == 3); - CHECK(member.type.descriptor.elements == 5); - CHECK(member.type.descriptor.name == "vec3"); - } - - CHECK(cblock.variables[1].name == "global_var2"); - { - const ShaderConstant &member = cblock.variables[1]; - INFO("UBO member: " << member.name.c_str()); - - CHECK(member.type.members.empty()); - CHECK(member.type.descriptor.type == VarType::Float); - CHECK(member.type.descriptor.rows == 2); - CHECK(member.type.descriptor.columns == 3); - CHECK(member.type.descriptor.elements == 3); - CHECK(member.type.descriptor.rowMajorStorage == false); - CHECK(member.type.descriptor.name == "mat3x2"); - } - } - } - } - - REQUIRE(refl.samplers.empty()); - REQUIRE(refl.interfaces.empty()); - - REQUIRE_ARRAY_SIZE(mapping.inputAttributes.size(), 16); - for(size_t i = 0; i < mapping.inputAttributes.size(); i++) - { - CHECK(mapping.inputAttributes[i] == -1); - } - - REQUIRE_ARRAY_SIZE(mapping.readOnlyResources.size(), 2); - { - // tex2d - CHECK(mapping.readOnlyResources[0].bindset == 0); - CHECK(mapping.readOnlyResources[0].bind == 3); - CHECK(mapping.readOnlyResources[0].arraySize == 1); - CHECK(mapping.readOnlyResources[0].used); - - // tex3d - CHECK(mapping.readOnlyResources[1].bindset == 0); - CHECK(mapping.readOnlyResources[1].bind == 5); - CHECK(mapping.readOnlyResources[1].arraySize == 1); - CHECK(mapping.readOnlyResources[1].used); - } - - REQUIRE_ARRAY_SIZE(mapping.readWriteResources.size(), 2); - { - // atom - CHECK(mapping.readWriteResources[0].bindset == 0); - CHECK(mapping.readWriteResources[0].bind == 0); - CHECK(mapping.readWriteResources[0].arraySize == 1); - CHECK(mapping.readWriteResources[0].used); - - // ssbo - CHECK(mapping.readWriteResources[1].bindset == 0); - CHECK(mapping.readWriteResources[1].bind == 2); - CHECK(mapping.readWriteResources[1].arraySize == 1); - CHECK(mapping.readWriteResources[1].used); - } - - REQUIRE_ARRAY_SIZE(mapping.constantBlocks.size(), 2); - { - // ubo - CHECK(mapping.constantBlocks[0].bindset == 0); - CHECK(mapping.constantBlocks[0].bind == 8); - CHECK(mapping.constantBlocks[0].arraySize == 1); - CHECK(mapping.constantBlocks[0].used); - - // $Globals - CHECK(mapping.constantBlocks[1].bindset == -1); - CHECK(mapping.constantBlocks[1].bind == -1); - CHECK(mapping.constantBlocks[1].arraySize == 1); - CHECK(mapping.constantBlocks[1].used); - } - - REQUIRE(mapping.samplers.empty()); - }; - - SECTION("vertex shader fixed function outputs") - { - std::string source = R"( -#version 150 core - -void main() { - gl_Position = vec4(0, 1, 0, 1); -} - -)"; - - ShaderReflection refl; - ShaderBindpointMapping mapping; - MakeOfflineShaderReflection(ShaderStage::Vertex, source, refl, mapping); - - REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 1); - { - CHECK(refl.outputSignature[0].varName == "gl_Position"); - { - const SigParameter &sig = refl.outputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Position); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - } - - std::string source2 = R"( -#version 150 core - -void main() { - gl_Position = vec4(0, 1, 0, 1); - gl_PointSize = 1.5f; -} - -)"; - - refl = ShaderReflection(); - mapping = ShaderBindpointMapping(); - MakeOfflineShaderReflection(ShaderStage::Vertex, source2, refl, mapping); - - REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 2); - { - CHECK(refl.outputSignature[0].varName == "gl_Position"); - { - const SigParameter &sig = refl.outputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Position); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - - CHECK(refl.outputSignature[1].varName == "gl_PointSize"); - { - const SigParameter &sig = refl.outputSignature[1]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::PointSize); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 1); - CHECK(sig.regChannelMask == 0x1); - CHECK(sig.channelUsedMask == 0x1); - } - } - }; - - SECTION("shader input/output blocks") - { - std::string source = R"( -#version 420 core - -layout(triangles) in; -layout(triangle_strip, max_vertices = 4) out; - -in gl_PerVertex -{ - vec4 gl_Position; -} gl_in[]; - -in block -{ - vec2 Texcoord; -} In[]; - -out gl_PerVertex -{ - vec4 gl_Position; -}; - -out block -{ - vec2 Texcoord; -} Out; - -void main() -{ - for(int i = 0; i < gl_in.length(); ++i) - { - gl_Position = gl_in[i].gl_Position; - Out.Texcoord = In[i].Texcoord; - EmitVertex(); - } - EndPrimitive(); -} - -)"; - - ShaderReflection refl; - ShaderBindpointMapping mapping; - MakeOfflineShaderReflection(ShaderStage::Geometry, source, refl, mapping); - - REQUIRE_ARRAY_SIZE(refl.inputSignature.size(), 2); - { - CHECK(refl.inputSignature[0].varName == "gl_PerVertex.gl_Position"); - { - const SigParameter &sig = refl.inputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Position); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - - CHECK(refl.inputSignature[1].varName == "block.Texcoord"); - { - const SigParameter &sig = refl.inputSignature[1]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 2); - CHECK(sig.regChannelMask == 0x3); - CHECK(sig.channelUsedMask == 0x3); - } - } - - REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 2); - { - CHECK(refl.outputSignature[0].varName == "gl_Position"); - { - const SigParameter &sig = refl.outputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Position); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - - CHECK(refl.outputSignature[1].varName == "block.Texcoord"); - { - const SigParameter &sig = refl.outputSignature[1]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 2); - CHECK(sig.regChannelMask == 0x3); - CHECK(sig.channelUsedMask == 0x3); - } - } - }; - - SECTION("matrix and array outputs") - { - std::string source = R"( -#version 150 core - -out vec3 outarr[3]; -out mat2 outmat; - -void main() -{ - gl_Position = vec4(0, 0, 0, 1); - outarr[0] = gl_Position.xyz; - outarr[1] = gl_Position.xyz; - outarr[2] = gl_Position.xyz; - outmat = mat2(0, 0, 0, 0); -} - -)"; - - ShaderReflection refl; - ShaderBindpointMapping mapping; - MakeOfflineShaderReflection(ShaderStage::Vertex, source, refl, mapping); - - REQUIRE_ARRAY_SIZE(refl.outputSignature.size(), 6); - { - CHECK(refl.outputSignature[0].varName == "gl_Position"); - { - const SigParameter &sig = refl.outputSignature[0]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Position); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 4); - CHECK(sig.regChannelMask == 0xf); - CHECK(sig.channelUsedMask == 0xf); - } - - CHECK(refl.outputSignature[1].varName == "outarr[0]"); - { - const SigParameter &sig = refl.outputSignature[1]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 0); - CHECK(sig.arrayIndex == 0); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 3); - CHECK(sig.regChannelMask == 0x7); - CHECK(sig.channelUsedMask == 0x7); - } - - CHECK(refl.outputSignature[2].varName == "outarr[1]"); - { - const SigParameter &sig = refl.outputSignature[2]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 1); - CHECK(sig.arrayIndex == 1); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 3); - CHECK(sig.regChannelMask == 0x7); - CHECK(sig.channelUsedMask == 0x7); - } - - CHECK(refl.outputSignature[3].varName == "outarr[2]"); - { - const SigParameter &sig = refl.outputSignature[3]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 2); - CHECK(sig.arrayIndex == 2); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 3); - CHECK(sig.regChannelMask == 0x7); - CHECK(sig.channelUsedMask == 0x7); - } - - CHECK(refl.outputSignature[4].varName == "outmat:row0"); - { - const SigParameter &sig = refl.outputSignature[4]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 3); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 2); - CHECK(sig.regChannelMask == 0x3); - CHECK(sig.channelUsedMask == 0x3); - } - - CHECK(refl.outputSignature[5].varName == "outmat:row1"); - { - const SigParameter &sig = refl.outputSignature[5]; - INFO("signature element: " << sig.varName.c_str()); - - CHECK(sig.regIndex == 4); - CHECK(sig.systemValue == ShaderBuiltin::Undefined); - CHECK(sig.compType == CompType::Float); - CHECK(sig.compCount == 2); - CHECK(sig.regChannelMask == 0x3); - CHECK(sig.channelUsedMask == 0x3); - } - } - }; + TestGLSLReflection(ShaderType::eShaderGLSL, MakeOfflineShaderReflection); SECTION("shader stage references") { diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index 4d68085ff..5285428fe 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -184,3 +184,53 @@ void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData &patch } } } + +#if ENABLED(ENABLE_UNIT_TESTS) + +#include "3rdparty/catch/catch.hpp" +#include "data/glsl_shaders.h" +#include "glslang_compile.h" + +TEST_CASE("Validate SPIR-V reflection", "[spirv][reflection]") +{ + ShaderType type = ShaderType::eShaderVulkan; + auto compiler = [&type](ShaderStage stage, const std::string &source, const std::string &entryPoint, + ShaderReflection &refl, ShaderBindpointMapping &mapping) { + + InitSPIRVCompiler(); + RenderDoc::Inst().RegisterShutdownFunction(&ShutdownSPIRVCompiler); + + std::vector spirv; + SPIRVCompilationSettings settings(type == ShaderType::eShaderVulkan + ? SPIRVSourceLanguage::VulkanGLSL + : SPIRVSourceLanguage::OpenGLGLSL, + SPIRVShaderStage(stage)); + std::string errors = CompileSPIRV(settings, {source}, spirv); + + INFO("SPIR-V compile output: " << errors); + + REQUIRE(!spirv.empty()); + + SPVModule spv; + ParseSPIRV(spirv.data(), spirv.size(), spv); + + SPIRVPatchData patchData; + spv.MakeReflection(type == ShaderType::eShaderVulkan ? GraphicsAPI::Vulkan : GraphicsAPI::OpenGL, + stage, entryPoint, refl, mapping, patchData); + }; + + // test both Vulkan and GL SPIR-V reflection + SECTION("Vulkan GLSL reflection") + { + type = ShaderType::eShaderVulkan; + TestGLSLReflection(type, compiler); + }; + + SECTION("OpenGL GLSL reflection") + { + type = ShaderType::eShaderGLSPIRV; + TestGLSLReflection(type, compiler); + }; +} + +#endif \ No newline at end of file