Move GLSL reflection tests to common place for SPIR-V to use

This commit is contained in:
baldurk
2019-06-12 15:26:19 +01:00
parent 98c8178c11
commit 3b00566ace
4 changed files with 1492 additions and 854 deletions
File diff suppressed because it is too large Load Diff
+10
View File
@@ -30,8 +30,18 @@ enum ShaderType
eShaderGLSPIRV,
};
#include <functional>
#include <string>
#include <vector>
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(ShaderStage stage, const std::string &source, const std::string &entryPoint,
ShaderReflection &refl, ShaderBindpointMapping &mapping)>;
void TestGLSLReflection(ShaderType testType, ReflectionMaker compile);
+11 -854
View File
@@ -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<RDCDriver, std::string> 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")
{
@@ -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<uint32_t> 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