Add test of GL shader debugging

This commit is contained in:
baldurk
2025-12-02 12:17:36 +00:00
parent 910f7ab65f
commit e85651545f
6 changed files with 852 additions and 2 deletions
+1
View File
@@ -283,6 +283,7 @@
<ClCompile Include="gl\gl_resource_lifetimes.cpp" />
<ClCompile Include="gl\gl_runtime_bind_prog_to_pipe.cpp" />
<ClCompile Include="gl\gl_separable_geometry_shader.cpp" />
<ClCompile Include="gl\gl_shader_debug_zoo.cpp" />
<ClCompile Include="gl\gl_shader_editing.cpp" />
<ClCompile Include="gl\gl_shader_isa.cpp" />
<ClCompile Include="gl\gl_simple_triangle.cpp" />
+3
View File
@@ -739,6 +739,9 @@
<ClCompile Include="d3d11\d3d11_pixel_history.cpp">
<Filter>D3D11\demos</Filter>
</ClCompile>
<ClCompile Include="gl\gl_shader_debug_zoo.cpp">
<Filter>OpenGL\demos</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="D3D11">
+675
View File
@@ -0,0 +1,675 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2025 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "gl_test.h"
RD_TEST(GL_Shader_Debug_Zoo, OpenGLGraphicsTest)
{
static constexpr const char *Description =
"Tests shader debugging on GL shaders, focussing on GL-specific functionality that is not "
"available or implemented separately from Vulkan.";
std::string vertex = R"EOSHADER(
#ifndef LOCATION
#define LOCATION layout
#endif
out gl_PerVertex
{
vec4 gl_Position;
};
LOCATION(location = 0) in vec3 inPosition;
LOCATION(location = 1) in vec4 inColor;
LOCATION(location = 2) in vec2 inUV;
LOCATION(location = 1) out vec4 v2fColor;
LOCATION(location = 2) out vec2 v2fUV;
#ifdef MIXED
// no location interpolators
in float a2v_a;
out float v2f_a;
#endif
#ifdef BLOCK
LOCATION(location = 3) in float a2v_a;
LOCATION(location = 4) in int a2v_b;
LOCATION(location = 3) out v2fBlock
{
float a;
flat int b;
} v2f;
#endif
#ifdef WEIRD_V2F
LOCATION(location = 3) in float a2v_a;
LOCATION(location = 4) in int a2v_b;
LOCATION(location = 3) out mat3 v2fMat;
LOCATION(location = 6) out float v2fArr[2];
#endif
#ifdef MULTI
LOCATION(location = 8) flat out int v2fInstData;
#endif
void main()
{
gl_Position = vec4(inPosition.xyz, 1);
v2fColor = inColor;
v2fUV = inUV;
#ifdef BLOCK
v2f.a = a2v_a;
v2f.b = a2v_b;
#endif
#ifdef MIXED
v2f_a = a2v_a;
#endif
#ifdef WEIRD_V2F
v2fMat = mat3(a2v_a * inPosition.xyz, (a2v_a + 1) * inPosition.xyz, (a2v_a + 2) * inPosition.xyz);
v2fArr[0] = sqrt(a2v_a);
v2fArr[1] = cos(a2v_a);
#endif
#ifdef MULTI
v2fInstData = a2v_b + gl_VertexID/3;
if(gl_InstanceID != 1 && a2v_a > 5.0)
gl_Position.y += 10.0f;
#endif
}
)EOSHADER";
std::string pixel = R"EOSHADER(
#ifndef LOCATION
#define LOCATION layout
#endif
LOCATION(location = 1) in vec4 v2fColor;
LOCATION(location = 2) in vec2 v2fUV;
LOCATION(location = 0) out vec4 outColor;
#ifdef MIXED
// no location interpolators
in float v2f_a;
#endif
#ifdef BLOCK
LOCATION(location = 3) in v2fBlock
{
float a;
flat int b;
} v2f;
#endif
#ifdef WEIRD_V2F
LOCATION(location = 3) in mat3 v2fMat;
LOCATION(location = 6) in float v2fArr[2];
#endif
#ifdef MULTI
LOCATION(location = 8) flat in int v2fInstData;
#endif
void main()
{
outColor = v2fColor + v2fUV.xyxy;
#ifdef BLOCK
outColor.r += v2f.a;
outColor.g += float(v2f.b);
#endif
#ifdef MIXED
outColor.r += v2f_a;
#endif
#ifdef WEIRD_V2F
outColor.rgb += v2fMat * outColor.rgb;
outColor.g += v2fArr[0] * v2fArr[1];
#endif
#ifdef MULTI
if(v2fInstData != 51) discard;
#endif
}
)EOSHADER";
std::string bindingZooVertex = R"EOSHADER(
#version 460 core
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec4 inColor;
layout(location = 2) in vec2 inUV;
layout(location = 1) out vec4 v2fColor;
layout(location = 2) out vec2 v2fUV;
void main()
{
gl_Position = vec4(inPosition.xyz, 1);
v2fColor = inColor;
v2fUV = inUV;
}
)EOSHADER";
std::string bindingZooPixel = R"EOSHADER(
#version 460 core
// have uniforms unused - our glslang recompilation will likely assign these locations but drivers likely won't.
uniform int unused;
// ensure alphabetical order does not match declaration order, also to test that we don't depend on that
// by accident
uniform vec4 zzzTest;
uniform mat4 matrix_test;
uniform float array_test[4];
struct TestStruct
{
float a;
int b;
};
uniform TestStruct struct_test;
layout(binding = 0, std140) uniform ubo_test
{
vec4 data;
} ubo;
layout(binding = 0, std430) buffer ssbo_test
{
vec4 data;
} ssbo;
layout(binding = 0) uniform sampler2D tex2d_test;
layout(binding = 1) uniform samplerBuffer texBuf_test;
layout(location = 1) in vec4 v2fColor;
layout(location = 2) in vec2 v2fUV;
layout(location = 0) out vec4 outColor;
void main()
{
vec4 col = vec4(0, 0, 0, 0);
col += matrix_test * zzzTest;
col.r += array_test[0];
col.g += array_test[1];
col.b += array_test[2];
col.a += array_test[3];
col += float(struct_test.b) * struct_test.a;
col += ubo.data;
col += ssbo.data;
col += dFdx(v2fUV).xyxy;
col += texture(tex2d_test, v2fUV);
col += texelFetch(texBuf_test, int(v2fUV.x*10));
outColor = col;
}
)EOSHADER";
void Prepare(int argc, char **argv)
{
OpenGLGraphicsTest::Prepare(argc, argv);
if(!Avail.empty())
return;
if(!SpvCompilationSupported())
Avail = InternalSpvCompiler() ? "Internal SPIR-V compiler did not initialise"
: "Couldn't find 'glslc' or 'glslangValidator' in PATH - "
"required for SPIR-V compilation";
}
int main()
{
// initialise, create window, create context, etc
if(!Init())
return 3;
GLuint vao = MakeVAO();
glBindVertexArray(vao);
const DefaultA2V tri[9] = {
{Vec3f(-1.0f, 1.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
{Vec3f(1.0f, 1.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(-1.0f, -1.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},
{Vec3f(-1.0f, 1.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)},
{Vec3f(1.0f, 1.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(-1.0f, -1.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)},
{Vec3f(-1.0f, 1.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
{Vec3f(1.0f, 1.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(-1.0f, -1.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},
};
GLuint vb = MakeBuffer();
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferStorage(GL_ARRAY_BUFFER, sizeof(tri), tri, 0);
ConfigureDefaultVAO();
struct ExtraA2V
{
float a;
int b;
};
const ExtraA2V tri2[6] = {
{2.5f, 50}, {3.5f, 7}, {4.5f, 8}, {5.5f, 40}, {6.5f, 50}, {7.5f, 60},
};
GLuint vb2 = MakeBuffer();
glBindBuffer(GL_ARRAY_BUFFER, vb2);
glBufferStorage(GL_ARRAY_BUFFER, sizeof(tri2), tri2, 0);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(ExtraA2V), (void *)(0));
glVertexAttribDivisor(3, 1);
glVertexAttribIPointer(4, 1, GL_INT, sizeof(ExtraA2V), (void *)(sizeof(float)));
glVertexAttribDivisor(4, 1);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
std::string vs = "#version 460 core\n";
vs += vertex;
std::string ps = "#version 460 core\n";
ps += pixel;
GLuint spirvprogram = MakeProgram();
GLuint spirvStrippedProgram = MakeProgram();
{
GLuint vsShad = glCreateShader(GL_VERTEX_SHADER);
GLuint psShad = glCreateShader(GL_FRAGMENT_SHADER);
std::vector<uint32_t> vsSPIRV =
CompileShaderToSpv(vs, SPIRVTarget::opengl, ShaderLang::glsl, ShaderStage::vert, "main");
std::vector<uint32_t> fsSPIRV =
CompileShaderToSpv(ps, SPIRVTarget::opengl, ShaderLang::glsl, ShaderStage::frag, "main");
glShaderBinary(1, &vsShad, GL_SHADER_BINARY_FORMAT_SPIR_V, vsSPIRV.data(),
(GLsizei)vsSPIRV.size() * 4);
glShaderBinary(1, &psShad, GL_SHADER_BINARY_FORMAT_SPIR_V, fsSPIRV.data(),
(GLsizei)fsSPIRV.size() * 4);
std::string entry_point = "main";
glSpecializeShaderARB(vsShad, (const GLchar *)entry_point.c_str(), 0, nullptr, nullptr);
glSpecializeShaderARB(psShad, (const GLchar *)entry_point.c_str(), 0, nullptr, nullptr);
glAttachShader(spirvprogram, vsShad);
glAttachShader(spirvprogram, psShad);
glLinkProgram(spirvprogram);
glDetachShader(spirvprogram, vsShad);
glDetachShader(spirvprogram, psShad);
glDeleteShader(vsShad);
glDeleteShader(psShad);
// strip any OpName/OpMemberName
for(size_t i = 5; i < vsSPIRV.size();)
{
uint16_t WordCount = vsSPIRV[i] >> 16;
uint16_t opcode = vsSPIRV[i] & 0xffff;
// OpName / OpMemberName
if(opcode == 5 || opcode == 6)
{
vsSPIRV.erase(vsSPIRV.begin() + i, vsSPIRV.begin() + i + WordCount);
continue;
}
i += WordCount;
}
for(size_t i = 5; i < fsSPIRV.size();)
{
uint16_t WordCount = fsSPIRV[i] >> 16;
uint16_t opcode = fsSPIRV[i] & 0xffff;
// OpName / OpMemberName
if(opcode == 5 || opcode == 6)
{
fsSPIRV.erase(fsSPIRV.begin() + i, fsSPIRV.begin() + i + WordCount);
continue;
}
i += WordCount;
}
vsShad = glCreateShader(GL_VERTEX_SHADER);
psShad = glCreateShader(GL_FRAGMENT_SHADER);
glShaderBinary(1, &vsShad, GL_SHADER_BINARY_FORMAT_SPIR_V, vsSPIRV.data(),
(GLsizei)vsSPIRV.size() * 4);
glShaderBinary(1, &psShad, GL_SHADER_BINARY_FORMAT_SPIR_V, fsSPIRV.data(),
(GLsizei)fsSPIRV.size() * 4);
glSpecializeShaderARB(vsShad, (const GLchar *)entry_point.c_str(), 0, nullptr, nullptr);
glSpecializeShaderARB(psShad, (const GLchar *)entry_point.c_str(), 0, nullptr, nullptr);
glAttachShader(spirvStrippedProgram, vsShad);
glAttachShader(spirvStrippedProgram, psShad);
glLinkProgram(spirvStrippedProgram);
glDetachShader(spirvStrippedProgram, vsShad);
glDetachShader(spirvStrippedProgram, psShad);
glDeleteShader(vsShad);
glDeleteShader(psShad);
}
GLuint simpleModernGLSLProgram = MakeProgram(vs, ps);
vs = "#version 460 core\n";
vs += "#define LOCATION(x)\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define LOCATION(x)\n";
ps += pixel;
GLuint noLocationGLSLProgram = MakeProgram(vs, ps, "", [](GLuint prog) {
glBindAttribLocation(prog, 0, "inPosition");
glBindAttribLocation(prog, 1, "inColor");
glBindAttribLocation(prog, 2, "inUV");
});
vs = "#version 460 core\n";
vs += "#define BLOCK 1\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define BLOCK 1\n";
ps += pixel;
GLuint blockLocationGLSLProgram = MakeProgram(vs, ps);
vs = "#version 460 core\n";
vs += "#define BLOCK 1\n";
vs += "#define LOCATION(x)\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define BLOCK 1\n";
ps += "#define LOCATION(x)\n";
ps += pixel;
GLuint blockNoLocationGLSLProgram = MakeProgram(vs, ps, "", [](GLuint prog) {
glBindAttribLocation(prog, 0, "inPosition");
glBindAttribLocation(prog, 1, "inColor");
glBindAttribLocation(prog, 2, "inUV");
glBindAttribLocation(prog, 3, "a2v_a");
glBindAttribLocation(prog, 4, "a2v_b");
});
vs = "#version 460 core\n";
vs += "#define MIXED 1\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define MIXED 1\n";
ps += pixel;
GLuint mixedLocationGLSLProgram =
MakeProgram(vs, ps, "", [](GLuint prog) { glBindAttribLocation(prog, 3, "a2v_a"); });
vs = "#version 460 core\n";
vs += "#define WEIRD_V2F 1\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define WEIRD_V2F 1\n";
ps += pixel;
GLuint weirdLocationGLSLProgram = MakeProgram(vs, ps);
GLuint pipeline = MakePipeline();
GLuint pipelineVert = MakePipelineProgram(GL_VERTEX_SHADER, vs);
GLuint pipelinePix = MakePipelineProgram(GL_FRAGMENT_SHADER, ps);
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, pipelineVert);
glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, pipelinePix);
vs = "#version 460 core\n";
vs += "#define WEIRD_V2F 1\n";
vs += "#define MULTI 1\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define WEIRD_V2F 1\n";
ps += "#define MULTI 1\n";
ps += pixel;
GLuint multiGLSLProgram = MakeProgram(vs, ps);
vs = "#version 460 core\n";
vs += "#define WEIRD_V2F 1\n";
vs += "#define LOCATION(x)\n";
vs += vertex;
ps = "#version 460 core\n";
ps += "#define WEIRD_V2F 1\n";
ps += "#define LOCATION(x)\n";
ps += pixel;
GLuint weirdNoLocationGLSLProgram = MakeProgram(vs, ps, "", [](GLuint prog) {
glBindAttribLocation(prog, 0, "inPosition");
glBindAttribLocation(prog, 1, "inColor");
glBindAttribLocation(prog, 2, "inUV");
glBindAttribLocation(prog, 3, "a2v_a");
glBindAttribLocation(prog, 4, "a2v_b");
});
GLuint bindingZooProgram = MakeProgram(bindingZooVertex, bindingZooPixel);
(void)bindingZooProgram;
glUseProgram(bindingZooProgram);
glUniform4f(glGetUniformLocation(bindingZooProgram, "zzzTest"), 0.2f, 0.3f, 0.4f, 0.5f);
float mat[16] = {};
for(int i = 0; i < 16; i++)
mat[i] = 0.6f + float(i) / 10.0f;
glUniformMatrix4fv(glGetUniformLocation(bindingZooProgram, "matrix_test"), 1, GL_FALSE, mat);
float arr[4] = {};
for(int i = 0; i < 4; i++)
arr[i] = 2.1f + float(i) / 10.0f;
glUniform1fv(glGetUniformLocation(bindingZooProgram, "array_test"), 4, arr);
glUniform1f(glGetUniformLocation(bindingZooProgram, "struct_test.a"), 9.9f);
glUniform1i(glGetUniformLocation(bindingZooProgram, "struct_test.b"), 99);
const size_t bindOffset = 16;
Vec4f cbufferdata[1024 + bindOffset];
for(int i = 0; i < bindOffset; i++)
cbufferdata[i] = Vec4f(-99.9f, -88.8f, -77.7f, -66.6f);
for(int i = 0; i < 1024; i++)
cbufferdata[bindOffset + i] =
Vec4f(float(i * 4 + 0), float(i * 4 + 1), float(i * 4 + 2), float(i * 4 + 3));
GLuint cb = MakeBuffer();
glBindBuffer(GL_UNIFORM_BUFFER, cb);
glBufferStorage(GL_UNIFORM_BUFFER, sizeof(cbufferdata), cbufferdata, GL_MAP_WRITE_BIT);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, cb, bindOffset * sizeof(Vec4f), 16 * sizeof(Vec4f));
GLuint ssbo = MakeBuffer();
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(cbufferdata), cbufferdata, GL_MAP_WRITE_BIT);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, ssbo, bindOffset * sizeof(Vec4f) * 2,
16 * sizeof(Vec4f));
GLuint tbuf_store = MakeBuffer();
glBindBuffer(GL_TEXTURE_BUFFER, tbuf_store);
glBufferStorage(GL_TEXTURE_BUFFER, sizeof(Vec4f) * 16,
cbufferdata + bindOffset * sizeof(Vec4f) * 3, 0);
GLuint tbuf = MakeTexture();
glBindTexture(GL_TEXTURE_BUFFER, tbuf);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, tbuf_store);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, tbuf);
Texture rgba8;
LoadXPM(SmileyTexture, rgba8);
glActiveTexture(GL_TEXTURE0);
GLuint smiley = MakeTexture();
glBindTexture(GL_TEXTURE_2D, smiley);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, rgba8.width, rgba8.height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgba8.width, rgba8.height, GL_RGBA, GL_UNSIGNED_BYTE,
rgba8.data.data());
// render offscreen to make picked values accurate
GLuint fbo = MakeFBO();
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Color render texture
GLuint colattach = MakeTexture();
glBindTexture(GL_TEXTURE_2D, colattach);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, screenWidth, screenHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colattach, 0);
while(Running())
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
float col[] = {0.0f, 0.0f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, col);
glBindVertexArray(vao);
int test = 0;
pushMarker("GLSL tests");
{
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(simpleModernGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(noLocationGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(blockLocationGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(blockNoLocationGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(mixedLocationGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(weirdLocationGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(multiGLSLProgram);
glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 3, 3, 3, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(multiGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 9);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(weirdNoLocationGLSLProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(bindingZooProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(0);
glBindProgramPipeline(pipeline);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindProgramPipeline(0);
}
popMarker();
pushMarker("SPIRV tests");
{
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(spirvprogram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glViewport(test * 4, screenHeight - 4, 4, 4);
test++;
glUseProgram(spirvStrippedProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
popMarker();
glBlitNamedFramebuffer(fbo, 0, 0, 0, screenWidth, screenHeight, 0, 0, screenWidth,
screenHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
Present();
}
return 0;
}
};
REGISTER_TEST();
+5 -1
View File
@@ -143,7 +143,8 @@ void OpenGLGraphicsTest::Shutdown()
delete mainWindow;
}
GLuint OpenGLGraphicsTest::MakeProgram(std::string vertSrc, std::string fragSrc, std::string geomSrc)
GLuint OpenGLGraphicsTest::MakeProgram(std::string vertSrc, std::string fragSrc,
std::string geomSrc, std::function<void(GLuint)> prelink)
{
GLuint vs = vertSrc.empty() ? 0 : glCreateShader(GL_VERTEX_SHADER);
GLuint fs = fragSrc.empty() ? 0 : glCreateShader(GL_FRAGMENT_SHADER);
@@ -232,6 +233,9 @@ GLuint OpenGLGraphicsTest::MakeProgram(std::string vertSrc, std::string fragSrc,
if(!vs || !fs)
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
if(prelink)
prelink(program);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &status);
+3 -1
View File
@@ -28,6 +28,7 @@
#include "3rdparty/glad/glad.h"
#include <functional>
#include <vector>
struct OpenGLGraphicsTest : public GraphicsTest
@@ -44,7 +45,8 @@ struct OpenGLGraphicsTest : public GraphicsTest
void PostInit();
GLuint MakeProgram(std::string vertSrc, std::string fragSrc, std::string geomSrc = "");
GLuint MakeProgram(std::string vertSrc, std::string fragSrc, std::string geomSrc = "",
std::function<void(GLuint)> prelink = {});
GLuint MakeProgram(std::string compSrc);
GLuint MakeProgram();
GLuint MakePipelineProgram(GLenum type, std::string src);
+165
View File
@@ -0,0 +1,165 @@
import renderdoc as rd
from typing import List
import rdtest
class GL_Shader_Debug_Zoo(rdtest.TestCase):
demos_test_name = 'GL_Shader_Debug_Zoo'
def check_capture(self):
if not self.controller.GetAPIProperties().shaderDebugging:
rdtest.log.success("Shader debugging not enabled, skipping test")
return
failed = False
x = -3
for child in ["GLSL tests", "SPIRV tests"]:
rdtest.log.begin_section(child)
section = self.find_action(child)
for test in range(len(section.children)):
action = section.children[test]
if not (action.flags & rd.ActionFlags.Drawcall):
continue
x += 4
self.controller.SetFrameEvent(action.eventId, False)
pipe: rd.PipeState = self.controller.GetPipelineState()
if not pipe.GetShaderReflection(rd.ShaderStage.Vertex).debugInfo.debuggable:
rdtest.log.print("Skipping undebuggable shader at {} in {}.".format(test, child))
return
if not pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo.debuggable:
rdtest.log.print("Skipping undebuggable shader at {} in {}.".format(test, child))
return
y = 1
inputs = rd.DebugPixelInputs()
inputs.primitive = 0
inputs.sample = 0
if action.numIndices > 3:
rdtest.log.print("prim 1")
inputs.primitive = 1
# Debug the shader
trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, inputs)
rdtest.log.print(f"debugging {x},{y}")
if trace.debugger is None:
failed = True
rdtest.log.error("Test {} in sub-section {} did not debug pixel".format(test, child))
self.controller.FreeTrace(trace)
continue
_, variables = self.process_trace(trace)
output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0)
debugged = self.evaluate_source_var(output, variables)
try:
self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4])
except rdtest.TestFailureException as ex:
failed = True
rdtest.log.error("Test {} in sub-section {} did not match pixel. {}".format(test, child, str(ex)))
continue
finally:
self.controller.FreeTrace(trace)
rdtest.log.success("Test {} pixel in sub-section {} matched as expected".format(test, child))
vtx = 1
inst = 0
idx = vtx
if action.flags & rd.ActionFlags.Instanced:
inst = 1
if action.flags & rd.ActionFlags.Indexed:
ib = pipe.GetIBuffer()
mesh = rd.MeshFormat()
mesh.indexResourceId = ib.resourceId
mesh.indexByteStride = ib.byteStride
mesh.indexByteOffset = ib.byteOffset + action.indexOffset * ib.byteStride
mesh.indexByteSize = ib.byteSize
mesh.baseVertex = action.baseVertex
indices = rdtest.fetch_indices(self.controller, action, mesh, 0, vtx, 1)
idx = indices[1]
postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, first_index=vtx, num_indices=1, instance=inst)
trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, 0)
if trace.debugger is None:
failed = True
rdtest.log.error("Test {} in sub-section {} did not debug vertex".format(test, child))
self.controller.FreeTrace(trace)
continue
_, variables = self.process_trace(trace)
outputs = 0
for var in trace.sourceVars:
var: rd.SourceVariableMapping
if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0:
name = var.name
if name not in postvs[0].keys():
name = name.replace(".", "Block.")
if name not in postvs[0].keys():
failed = True
rdtest.log.error("Don't have expected output for {}".format(name))
continue
expect = postvs[0][name]
value = self.evaluate_source_var(var, variables)
if len(expect) != value.columns:
failed = True
rdtest.log.error(
"Output {} at EID {} has different size ({} values) to expectation ({} values)"
.format(name, action.eventId, value.columns, len(expect)))
continue
compType = rd.VarTypeCompType(value.type)
if compType == rd.CompType.UInt:
debugged = list(value.value.u32v[0:value.columns])
elif compType == rd.CompType.SInt:
debugged = list(value.value.s32v[0:value.columns])
else:
debugged = list(value.value.f32v[0:value.columns])
if not rdtest.value_compare(expect, debugged):
failed = True
rdtest.log.error("Test {} in sub-section {} did not match vertex.\nExpected {} but got {}".format(test, child, expect, debugged))
break
is_eq, diff_amt = rdtest.value_compare_diff(expect, debugged, eps=5.0E-06)
if not is_eq:
failed = True
rdtest.log.error(
"Debugged value {} at EID {} vert {} (idx {}) instance {}: {} difference. {} doesn't exactly match postvs output {}".format(
name, action.eventId, vtx, idx, inst, diff_amt, debugged, expect))
outputs = outputs + 1
self.controller.FreeTrace(trace)
rdtest.log.success("Test {} vertex in sub-section {} matched as expected".format(test, child))
rdtest.log.end_section(child)
if failed:
raise rdtest.TestFailureException("Some tests were not as expected")
rdtest.log.success("All tests matched")