mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Test] (MG_IntegrationTest): cover a storage block's default binding where another resource competes for it
This commit is contained in:
@@ -42,10 +42,10 @@
|
||||
namespace MGITest {
|
||||
namespace {
|
||||
|
||||
// The eight vertex shaders of the conformance sweep, verbatim in shape. Each reads three
|
||||
// vec4 positions out of a storage block on binding 0 and emits them as a triangle that
|
||||
// covers the whole viewport.
|
||||
constexpr const char* kFormVS[8] = {
|
||||
// The eight vertex shaders of the conformance sweep, verbatim in shape, plus a ninth that
|
||||
// is not from the sweep (see form 8). Each reads three vec4 positions out of a storage
|
||||
// block on binding 0 and emits them as a triangle that covers the whole viewport.
|
||||
constexpr const char* kFormVS[9] = {
|
||||
// 0 - instance name, no binding qualifier, sized array member
|
||||
R"(#version 430 core
|
||||
layout(std430) buffer Buffer {
|
||||
@@ -127,6 +127,38 @@ void main() {
|
||||
case 2: gl_Position = g_buffer.position2[gl_VertexID - 2]; break;
|
||||
}
|
||||
}
|
||||
)",
|
||||
// 8 - NOT from the conformance sweep. An unqualified storage block with a UNIFORM
|
||||
// BLOCK beside it, which is what makes the block's DEFAULT binding observable at all.
|
||||
//
|
||||
// GL 4.3 core 7.8 gives a storage block with no layout(binding = N) a buffer binding
|
||||
// of zero. Forms 0, 1, 3, 4 and 5 above are all unqualified and all pass, but they
|
||||
// cannot prove that rule holds: they are the only resource in their shader, so the
|
||||
// binding glslang's IO mapper invents for them happens to BE zero and the right answer
|
||||
// arrives for the wrong reason.
|
||||
//
|
||||
// Every shader here is parsed as a Vulkan client, so that mapper allocates out of ONE
|
||||
// flat space shared by samplers, images, uniform blocks, storage blocks and the
|
||||
// synthesized global-uniform block (iomapper.cpp resolveBinding takes the `ent.newSet`
|
||||
// branch, and every resource resolves to set 0), and then writes the result back into
|
||||
// the type's qualifier - so the reflection cannot tell an invented binding from a
|
||||
// declared one. Put anything live next to the block and it is pushed off zero, the
|
||||
// draw reads a binding point nothing was ever bound to, and the triangle collapses
|
||||
// with no GL error anywhere. That is
|
||||
// KHR-GL43.compute_shader.resource-ubo's whole failure, in a vertex stage.
|
||||
//
|
||||
// The uniform block is REBOUND explicitly with glUniformBlockBinding, exactly as that
|
||||
// conformance case does. That keeps this case about the storage block's default and
|
||||
// not about the uniform block's - the rebinding path has always worked, and the
|
||||
// uniform-block default is a separate (still open) question.
|
||||
R"(#version 430 core
|
||||
layout(std140) uniform ScaleBlock {
|
||||
vec4 factor;
|
||||
} g_scale;
|
||||
layout(std430) buffer Buffer {
|
||||
vec4 position[3];
|
||||
} g_input_buffer;
|
||||
void main() { gl_Position = g_input_buffer.position[gl_VertexID] * g_scale.factor; }
|
||||
)",
|
||||
};
|
||||
|
||||
@@ -197,6 +229,26 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
const unsigned int program = CompileProgram(kFormVS[form], kFormFS, &error);
|
||||
ASSERT_NE(program, 0u) << "form " << form << " did not build: " << error;
|
||||
|
||||
// Form 8 alone declares a uniform block, and it exists only to occupy a slot the
|
||||
// storage block must not be pushed onto. Bound to a buffer of ones so it scales
|
||||
// the positions by exactly 1 - the block's contribution to the IMAGE is nothing,
|
||||
// and its contribution to the TEST is that it is there at all.
|
||||
GLuint uniformBuffer = 0;
|
||||
if (form == 8) {
|
||||
const float ones[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
glGenBuffers(1, &uniformBuffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(ones), ones, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniformBuffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
const GLuint blockIndex = glGetUniformBlockIndex(program, "ScaleBlock");
|
||||
ASSERT_NE(blockIndex, GL_INVALID_INDEX) << "form 8: the uniform block is not active";
|
||||
// Explicit, so this case cannot fail on the uniform block's own default
|
||||
// binding - which is a separate question from the storage block's.
|
||||
glUniformBlockBinding(program, blockIndex, 0);
|
||||
ASSERT_EQ(FirstGLError(), 0u) << "form 8: uniform block setup errored";
|
||||
}
|
||||
|
||||
GLuint vao = 0;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
@@ -221,6 +273,7 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteProgram(program);
|
||||
glDeleteBuffers(1, &buffer);
|
||||
if (uniformBuffer != 0) glDeleteBuffers(1, &uniformBuffer);
|
||||
gl.EndFrame();
|
||||
}
|
||||
};
|
||||
@@ -241,6 +294,10 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
MGL_SSBO_FORM_CASE(3, GlobalLayoutDefaultsThenAnInstanceNamedBlock)
|
||||
MGL_SSBO_FORM_CASE(4, BlockInstanceArrayOfOne)
|
||||
MGL_SSBO_FORM_CASE(5, BlockInstanceArrayOfOneWithSharedLayout)
|
||||
// The form that makes the DEFAULT binding observable rather than accidental: forms 0/1/3/4/5
|
||||
// are unqualified too, but nothing competes with them for glslang's flat slot 0, so they
|
||||
// would keep passing even with the default wrong. See the comment on kFormVS[8].
|
||||
MGL_SSBO_FORM_CASE(8, NoBindingQualifierBesideAUniformBlock)
|
||||
// ---- the two forms that do not work yet ----
|
||||
//
|
||||
// Both carry an UNSIZED array that is not the block's sole trailing member, and both fail
|
||||
|
||||
Reference in New Issue
Block a user