mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (MG_Impl/GLImpl, MG_State): implement glGetActiveUniformsiv (UBO reflection query)
Completes the uniform-block reflection chain: glGetUniformIndices, glGetActiveUniformName and glGetActiveUniformBlockiv were already implemented; glGetActiveUniformsiv was the last stub. Supports all 8 GL 3.3 Core pnames: * GL_UNIFORM_TYPE / SIZE / NAME_LENGTH / BLOCK_INDEX / OFFSET / ARRAY_STRIDE come straight from glslang's TObjectReflection (the same reflection the existing uniform queries use). * GL_UNIFORM_IS_ROW_MAJOR from the member's TType layout qualifier, guarded by isMatrix() so a scalar in a layout(row_major) block does not wrongly report 1. * GL_UNIFORM_MATRIX_STRIDE is derived: glslang exposes no matrix stride, so it is computed from the std140 rule (each column/row vector rounded up to a vec4), which matches the std140 layout MobileGL's SPIR-V path emits. Evaluates to 16 for every GL 3.3 float matrix. The -1-vs-0 distinction is handled explicitly: OFFSET / ARRAY_STRIDE / MATRIX_STRIDE / BLOCK_INDEX return -1 for a default-block uniform (glslang gives arrayStride 0 there, so it is gated on block membership), while ARRAY_STRIDE / MATRIX_STRIDE return 0 for a non-array / non-matrix member that IS in a block. Errors: GL_INVALID_VALUE for uniformCount<0, any index >= active uniform count, or a never-generated program name; GL_INVALID_OPERATION for a live shader name; GL_INVALID_ENUM for an unaccepted pname (e.g. the GL 4.2 GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX). All validation runs before any write, so params is untouched on error. There is no "not linked" error -- an unlinked program has zero active uniforms, so any index raises GL_INVALID_VALUE. Also fix GetActiveUniformArraySize, which returned glslang's TObjectReflection.size verbatim: that field only carries the element count for a non-block array and reports 1 for a block array member, so GL_UNIFORM_SIZE (and glGetActiveUniform's size out-param, and glGetProgramResourceiv's GL_ARRAY_SIZE) wrongly reported 1 for an array inside a UBO. Take the count from the TType instead, which is authoritative for both cases. Covered by 3 ProgramTest cases (std140 block with scalar/array/mat4 + a default-block sampler, a row_major variant, and the six error cases) that link real shaders and assert every pname value.
This commit is contained in:
@@ -265,7 +265,7 @@ DECLARE_GL_FUNCTION_HEAD(void, ClearBufferfv, GLenum buffer, GLint drawbuffer, c
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, ClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferfi, buffer, drawbuffer, depth, stencil)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferfi, buffer, drawbuffer, depth, stencil)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size)
|
DECLARE_GL_FUNCTION_HEAD(void, CopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, GLuint* uniformIndices) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetUniformIndices, program, uniformCount, uniformNames, uniformIndices)
|
DECLARE_GL_FUNCTION_HEAD(void, GetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, GLuint* uniformIndices) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetUniformIndices, program, uniformCount, uniformNames, uniformIndices)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(GLuint, GetUniformBlockIndex, GLuint program, const GLchar* uniformBlockName) DECLARE_GL_FUNCTION_END(GLuint, GetUniformBlockIndex, program, uniformBlockName)
|
DECLARE_GL_FUNCTION_HEAD(GLuint, GetUniformBlockIndex, GLuint program, const GLchar* uniformBlockName) DECLARE_GL_FUNCTION_END(GLuint, GetUniformBlockIndex, program, uniformBlockName)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformBlockiv, program, uniformBlockIndex, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformBlockiv, program, uniformBlockIndex, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformBlockName, program, uniformBlockIndex, bufSize, length, uniformBlockName)
|
DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformBlockName, program, uniformBlockIndex, bufSize, length, uniformBlockName)
|
||||||
|
|||||||
@@ -424,6 +424,101 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetActiveUniformsiv_State(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname,
|
||||||
|
GLint* params) {
|
||||||
|
if (uniformCount < 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"uniformCount " + std::to_string(uniformCount) + " is less than 0."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Program-name resolution with the correct two-error split: a live shader name is
|
||||||
|
// GL_INVALID_OPERATION, a never-generated name is GL_INVALID_VALUE. glGetActiveUniformsiv has
|
||||||
|
// no "not linked" error, so unlike TryToGetLinkedProgramForInterfaceQuery there is no
|
||||||
|
// link-status check here; an unlinked program simply has zero active uniforms (handled below).
|
||||||
|
if (!MG_State::pGLContext->ValidateProgramName(program)) {
|
||||||
|
const ErrorCode error = MG_State::pGLContext->ValidateShaderName(program) ? ErrorCode::InvalidOperation
|
||||||
|
: ErrorCode::InvalidValue;
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
error, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
std::to_string(program) + " is not a program object."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& programObject = MG_State::pGLContext->GetProgramObject(program);
|
||||||
|
if (!programObject) return;
|
||||||
|
|
||||||
|
switch (pname) {
|
||||||
|
case GL_UNIFORM_TYPE:
|
||||||
|
case GL_UNIFORM_SIZE:
|
||||||
|
case GL_UNIFORM_NAME_LENGTH:
|
||||||
|
case GL_UNIFORM_BLOCK_INDEX:
|
||||||
|
case GL_UNIFORM_OFFSET:
|
||||||
|
case GL_UNIFORM_ARRAY_STRIDE:
|
||||||
|
case GL_UNIFORM_MATRIX_STRIDE:
|
||||||
|
case GL_UNIFORM_IS_ROW_MAJOR:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"pname " + std::to_string(pname) + " is not an accepted value."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniformCount == 0) return;
|
||||||
|
if (uniformIndices == nullptr || params == nullptr) return;
|
||||||
|
|
||||||
|
// Every index must be < the number of active uniforms, checked before any write so params is
|
||||||
|
// left untouched on error. GetUniformCount() is 0 for an unlinked program, which is also the
|
||||||
|
// spec-mandated GL_INVALID_VALUE path for querying an unlinked program (no separate error).
|
||||||
|
const Uint activeUniforms = programObject->GetUniformCount();
|
||||||
|
for (GLsizei i = 0; i < uniformCount; ++i) {
|
||||||
|
if (uniformIndices[i] >= activeUniforms) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"uniformIndices[" + std::to_string(i) +
|
||||||
|
"] = " + std::to_string(uniformIndices[i]) +
|
||||||
|
" is greater than or equal to the number of active uniforms."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GLsizei i = 0; i < uniformCount; ++i) {
|
||||||
|
const Uint idx = uniformIndices[i];
|
||||||
|
switch (pname) {
|
||||||
|
case GL_UNIFORM_TYPE:
|
||||||
|
params[i] = static_cast<GLint>(programObject->GetActiveUniformType(idx));
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_SIZE:
|
||||||
|
params[i] = programObject->GetActiveUniformArraySize(idx);
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_NAME_LENGTH:
|
||||||
|
params[i] = static_cast<GLint>(programObject->GetActiveUniformName(idx).length() + 1);
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_BLOCK_INDEX:
|
||||||
|
params[i] = programObject->GetActiveUniformBlockIndex(idx);
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_OFFSET:
|
||||||
|
params[i] = programObject->GetActiveUniformOffset(idx);
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_ARRAY_STRIDE:
|
||||||
|
params[i] = programObject->GetActiveUniformArrayStride(idx);
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_MATRIX_STRIDE:
|
||||||
|
params[i] = programObject->GetActiveUniformMatrixStride(idx);
|
||||||
|
break;
|
||||||
|
case GL_UNIFORM_IS_ROW_MAJOR:
|
||||||
|
params[i] = programObject->GetActiveUniformIsRowMajor(idx);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GetAttachedShaders_State(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) {
|
void GetAttachedShaders_State(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) {
|
||||||
if (maxCount < 0) {
|
if (maxCount < 0) {
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
@@ -1503,6 +1598,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
GetUniformIndices_State(program, uniformCount, uniformNames, uniformIndices);
|
GetUniformIndices_State(program, uniformCount, uniformNames, uniformIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname,
|
||||||
|
GLint* params) {
|
||||||
|
GetActiveUniformsiv_State(program, uniformCount, uniformIndices, pname, params);
|
||||||
|
}
|
||||||
|
|
||||||
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) {
|
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) {
|
||||||
GetAttachedShaders_State(program, maxCount, count, shaders);
|
GetAttachedShaders_State(program, maxCount, count, shaders);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
GLchar* uniformName);
|
GLchar* uniformName);
|
||||||
void GetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames,
|
void GetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames,
|
||||||
GLuint* uniformIndices);
|
GLuint* uniformIndices);
|
||||||
|
void GetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname,
|
||||||
|
GLint* params);
|
||||||
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
|
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
|
||||||
GLint GetAttribLocation(GLuint program, const GLchar* name);
|
GLint GetAttribLocation(GLuint program, const GLchar* name);
|
||||||
void GetProgramiv(GLuint program, GLenum pname, GLint* params);
|
void GetProgramiv(GLuint program, GLenum pname, GLint* params);
|
||||||
|
|||||||
@@ -75,9 +75,17 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return uniform.glDefineType;
|
return uniform.glDefineType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Number of active array elements (GL_UNIFORM_SIZE / GL_ARRAY_SIZE); 1 for a non-array.
|
||||||
|
// glslang's TObjectReflection.size only carries the element count for a NON-block array; for
|
||||||
|
// a block array member it reports 1, so take the count from the TType, which is authoritative
|
||||||
|
// for both. GL 3.3 core uniforms are always sized.
|
||||||
GLint GetActiveUniformArraySize(Uint index) const {
|
GLint GetActiveUniformArraySize(Uint index) const {
|
||||||
auto& uniform = m_program->getUniform(static_cast<Int>(index));
|
const auto& uniform = m_program->getUniform(static_cast<Int>(index));
|
||||||
return uniform.size;
|
const glslang::TType* type = uniform.getType();
|
||||||
|
if (type != nullptr && type->isSizedArray()) {
|
||||||
|
return type->getOuterArraySize();
|
||||||
|
}
|
||||||
|
return uniform.size < 1 ? 1 : uniform.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
Int GetActiveUniformBlockIndex(Uint index) const {
|
Int GetActiveUniformBlockIndex(Uint index) const {
|
||||||
@@ -85,6 +93,63 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return uniform.index;
|
return uniform.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL_UNIFORM_OFFSET: byte offset within the owning named block. glslang already reports -1
|
||||||
|
// for a default-block uniform, which is exactly the spec value there.
|
||||||
|
GLint GetActiveUniformOffset(Uint index) const {
|
||||||
|
return m_program->getUniform(static_cast<Int>(index)).offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GL_UNIFORM_ARRAY_STRIDE: byte stride of an array member in a named block; 0 for a non-array
|
||||||
|
// block member; -1 for a default-block uniform. glslang yields arrayStride==0 for the
|
||||||
|
// default-block case, so gate on block membership to return the spec-mandated -1.
|
||||||
|
GLint GetActiveUniformArrayStride(Uint index) const {
|
||||||
|
const auto& uniform = m_program->getUniform(static_cast<Int>(index));
|
||||||
|
return (uniform.index < 0) ? -1 : uniform.arrayStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GL_UNIFORM_IS_ROW_MAJOR: 1 only for a row-major matrix in a named block, else 0. The
|
||||||
|
// isMatrix() guard is required -- glslang stamps a block-level layout(row_major) onto
|
||||||
|
// non-matrix members too, so a float/vec in a row_major block would otherwise report 1.
|
||||||
|
// For the glslang build here a block-level layout(row_major) is also resolved onto each
|
||||||
|
// matrix member's own qualifier (verified by GetActiveUniformsivRowMajorBlock), so the member
|
||||||
|
// check suffices; the getUniformBlock() fallback is defensive for a config that instead leaves
|
||||||
|
// an inheriting member's layoutMatrix == ElmNone.
|
||||||
|
GLint GetActiveUniformIsRowMajor(Uint index) const {
|
||||||
|
const auto& uniform = m_program->getUniform(static_cast<Int>(index));
|
||||||
|
if (uniform.index < 0) return 0;
|
||||||
|
const glslang::TType* type = uniform.getType();
|
||||||
|
if (type == nullptr || !type->isMatrix()) return 0;
|
||||||
|
glslang::TLayoutMatrix layoutMatrix = type->getQualifier().layoutMatrix;
|
||||||
|
if (layoutMatrix == glslang::ElmNone) {
|
||||||
|
layoutMatrix = m_program->getUniformBlock(uniform.index).getType()->getQualifier().layoutMatrix;
|
||||||
|
}
|
||||||
|
return (layoutMatrix == glslang::ElmRowMajor) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GL_UNIFORM_MATRIX_STRIDE: byte stride between columns (col-major) / rows (row-major) of a
|
||||||
|
// matrix in a named block; 0 for a non-matrix block member; -1 for a default-block uniform.
|
||||||
|
// glslang exposes no matrix stride, so it is derived from the std140 rule -- each column/row
|
||||||
|
// vector's base alignment rounded up to a vec4 (16 B). MobileGL's SPIR-V path lays every UBO
|
||||||
|
// out as std140 (packed/shared are coerced), so this matches the offsets glslang reports. For
|
||||||
|
// every GL 3.3 float matrix this evaluates to 16, independent of majorness.
|
||||||
|
GLint GetActiveUniformMatrixStride(Uint index) const {
|
||||||
|
const auto& uniform = m_program->getUniform(static_cast<Int>(index));
|
||||||
|
if (uniform.index < 0) return -1;
|
||||||
|
const glslang::TType* type = uniform.getType();
|
||||||
|
if (type == nullptr || !type->isMatrix()) return 0;
|
||||||
|
glslang::TLayoutMatrix layoutMatrix = type->getQualifier().layoutMatrix;
|
||||||
|
if (layoutMatrix == glslang::ElmNone) {
|
||||||
|
layoutMatrix = m_program->getUniformBlock(uniform.index).getType()->getQualifier().layoutMatrix;
|
||||||
|
}
|
||||||
|
const bool rowMajor = (layoutMatrix == glslang::ElmRowMajor);
|
||||||
|
const int strideVectorComponents = rowMajor ? type->getMatrixCols() : type->getMatrixRows();
|
||||||
|
constexpr int scalarSize = 4; // GL 3.3 core uniform matrices are float
|
||||||
|
const int vectorAlignment = (strideVectorComponents <= 1) ? scalarSize
|
||||||
|
: (strideVectorComponents == 2) ? 2 * scalarSize
|
||||||
|
: 4 * scalarSize;
|
||||||
|
return (vectorAlignment + 15) & ~15; // std140 round-up to a vec4
|
||||||
|
}
|
||||||
|
|
||||||
const glslang::TType* GetUniformTType(Uint location) const {
|
const glslang::TType* GetUniformTType(Uint location) const {
|
||||||
auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]);
|
auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]);
|
||||||
return uniform.getType();
|
return uniform.getType();
|
||||||
|
|||||||
@@ -1602,3 +1602,186 @@ TEST_F(ProgramTest, CompileShaderWithSamplerAsVarName) {
|
|||||||
spvcSession.Compile(&result);
|
spvcSession.Compile(&result);
|
||||||
printf("decomp from fragSpirv:\n%s\n\n", result);
|
printf("decomp from fragSpirv:\n%s\n\n", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Links a VS+FS pair whose fragment shader carries a std140 uniform block (scalar + array + mat4)
|
||||||
|
// plus a default-block sampler, and returns the linked program. matrixLayout lets a test flip the
|
||||||
|
// block to row_major.
|
||||||
|
GLuint LinkUboReflectionProgram(const char* matrixLayout) {
|
||||||
|
char infoLog[1024] = "";
|
||||||
|
const char* vsSrc = R"(#version 330 core
|
||||||
|
void main() { gl_Position = vec4(0.0); }
|
||||||
|
)";
|
||||||
|
std::string fsSrc = std::string("#version 330 core\n") +
|
||||||
|
"layout(std140" + matrixLayout + ") uniform Block {\n" +
|
||||||
|
" float uScalar;\n" +
|
||||||
|
" vec4 uArray[3];\n" +
|
||||||
|
" mat4 uMatrix;\n" +
|
||||||
|
"};\n" +
|
||||||
|
"uniform sampler2D uTex;\n" +
|
||||||
|
"out vec4 fragColor;\n" +
|
||||||
|
"void main() {\n" +
|
||||||
|
" fragColor = texture(uTex, uArray[0].xy) * uScalar * uMatrix[0];\n" +
|
||||||
|
"}\n";
|
||||||
|
const char* fsPtr = fsSrc.c_str();
|
||||||
|
|
||||||
|
GLuint vs = CreateShader(GL_VERTEX_SHADER);
|
||||||
|
ShaderSource(vs, 1, &vsSrc, nullptr);
|
||||||
|
CompileShader(vs);
|
||||||
|
GLint vsStatus = GL_FALSE;
|
||||||
|
GetShaderiv(vs, GL_COMPILE_STATUS, &vsStatus);
|
||||||
|
GetShaderInfoLog(vs, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
EXPECT_EQ(vsStatus, GL_TRUE) << infoLog;
|
||||||
|
|
||||||
|
GLuint fs = CreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
ShaderSource(fs, 1, &fsPtr, nullptr);
|
||||||
|
CompileShader(fs);
|
||||||
|
GLint fsStatus = GL_FALSE;
|
||||||
|
GetShaderiv(fs, GL_COMPILE_STATUS, &fsStatus);
|
||||||
|
GetShaderInfoLog(fs, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
EXPECT_EQ(fsStatus, GL_TRUE) << infoLog;
|
||||||
|
|
||||||
|
GLuint program = CreateProgram();
|
||||||
|
AttachShader(program, vs);
|
||||||
|
AttachShader(program, fs);
|
||||||
|
LinkProgram(program);
|
||||||
|
GLint linkStatus = GL_FALSE;
|
||||||
|
GetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||||
|
GetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
EXPECT_EQ(linkStatus, GL_TRUE) << infoLog;
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint UniformIndexByName(GLuint program, const char* name) {
|
||||||
|
GLuint index = GL_INVALID_INDEX;
|
||||||
|
GetUniformIndices(program, 1, &name, &index);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint QueryUniformiv(GLuint program, GLuint uniformIndex, GLenum pname) {
|
||||||
|
GLint value = -12345; // sentinel that is not a legal answer for any queried pname
|
||||||
|
GetActiveUniformsiv(program, 1, &uniformIndex, pname, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_F(ProgramTest, GetActiveUniformsivStd140Block) {
|
||||||
|
GLuint program = LinkUboReflectionProgram(/*matrixLayout=*/"");
|
||||||
|
|
||||||
|
GLint activeUniforms = 0;
|
||||||
|
GetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms);
|
||||||
|
EXPECT_EQ(activeUniforms, 4);
|
||||||
|
|
||||||
|
const GLuint s = UniformIndexByName(program, "uScalar");
|
||||||
|
const GLuint a = UniformIndexByName(program, "uArray");
|
||||||
|
const GLuint m = UniformIndexByName(program, "uMatrix");
|
||||||
|
const GLuint t = UniformIndexByName(program, "uTex");
|
||||||
|
ASSERT_NE(s, GL_INVALID_INDEX);
|
||||||
|
ASSERT_NE(a, GL_INVALID_INDEX);
|
||||||
|
ASSERT_NE(m, GL_INVALID_INDEX);
|
||||||
|
ASSERT_NE(t, GL_INVALID_INDEX);
|
||||||
|
|
||||||
|
// Types and sizes.
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_TYPE), GL_FLOAT);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, a, GL_UNIFORM_TYPE), GL_FLOAT_VEC4);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_TYPE), GL_FLOAT_MAT4);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, t, GL_UNIFORM_TYPE), GL_SAMPLER_2D);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_SIZE), 1);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, a, GL_UNIFORM_SIZE), 3);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_SIZE), 1);
|
||||||
|
|
||||||
|
// Block membership: -1 for the default-block sampler.
|
||||||
|
EXPECT_GE(QueryUniformiv(program, s, GL_UNIFORM_BLOCK_INDEX), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, t, GL_UNIFORM_BLOCK_INDEX), -1);
|
||||||
|
|
||||||
|
// std140 offsets.
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_OFFSET), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, a, GL_UNIFORM_OFFSET), 16);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_OFFSET), 64);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, t, GL_UNIFORM_OFFSET), -1);
|
||||||
|
|
||||||
|
// ARRAY_STRIDE: 16 for the array, 0 for non-array block members, -1 for the default block.
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, a, GL_UNIFORM_ARRAY_STRIDE), 16);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_ARRAY_STRIDE), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_ARRAY_STRIDE), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, t, GL_UNIFORM_ARRAY_STRIDE), -1);
|
||||||
|
|
||||||
|
// MATRIX_STRIDE: 16 for the matrix, 0 for non-matrix block members, -1 for the default block.
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_MATRIX_STRIDE), 16);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_MATRIX_STRIDE), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, a, GL_UNIFORM_MATRIX_STRIDE), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, t, GL_UNIFORM_MATRIX_STRIDE), -1);
|
||||||
|
|
||||||
|
// Column-major block: nothing is row-major.
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_IS_ROW_MAJOR), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_IS_ROW_MAJOR), 0);
|
||||||
|
|
||||||
|
// NAME_LENGTH includes the terminator and matches glGetActiveUniform's reported name.
|
||||||
|
char nameBuf[64] = "";
|
||||||
|
GLsizei nameLen = 0;
|
||||||
|
GLint size = 0;
|
||||||
|
GLenum type = 0;
|
||||||
|
GetActiveUniform(program, m, sizeof(nameBuf), &nameLen, &size, &type, nameBuf);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_NAME_LENGTH),
|
||||||
|
static_cast<GLint>(std::strlen(nameBuf) + 1));
|
||||||
|
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A block-level layout(row_major) with no per-member qualifier: only the matrix is row-major, and
|
||||||
|
// only via the block-inheritance fallback (member layoutMatrix == ElmNone). A naive per-member check
|
||||||
|
// returns 0 here.
|
||||||
|
TEST_F(ProgramTest, GetActiveUniformsivRowMajorBlock) {
|
||||||
|
GLuint program = LinkUboReflectionProgram(/*matrixLayout=*/", row_major");
|
||||||
|
|
||||||
|
const GLuint s = UniformIndexByName(program, "uScalar");
|
||||||
|
const GLuint a = UniformIndexByName(program, "uArray");
|
||||||
|
const GLuint m = UniformIndexByName(program, "uMatrix");
|
||||||
|
ASSERT_NE(m, GL_INVALID_INDEX);
|
||||||
|
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_IS_ROW_MAJOR), 1);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, s, GL_UNIFORM_IS_ROW_MAJOR), 0); // non-matrix, isMatrix() guard
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, a, GL_UNIFORM_IS_ROW_MAJOR), 0);
|
||||||
|
EXPECT_EQ(QueryUniformiv(program, m, GL_UNIFORM_MATRIX_STRIDE), 16); // unchanged by majorness
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProgramTest, GetActiveUniformsivErrors) {
|
||||||
|
GLuint program = LinkUboReflectionProgram(/*matrixLayout=*/"");
|
||||||
|
GLint activeUniforms = 0;
|
||||||
|
GetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms);
|
||||||
|
ASSERT_GT(activeUniforms, 0);
|
||||||
|
|
||||||
|
GLuint validIndex = 0;
|
||||||
|
GLint params[4] = {-999, -999, -999, -999};
|
||||||
|
|
||||||
|
// E1: negative count -> GL_INVALID_VALUE, params untouched.
|
||||||
|
GetActiveUniformsiv(program, -1, &validIndex, GL_UNIFORM_TYPE, params);
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||||
|
EXPECT_EQ(params[0], -999);
|
||||||
|
|
||||||
|
// E2: index == ACTIVE_UNIFORMS -> GL_INVALID_VALUE, params untouched.
|
||||||
|
GLuint outOfRange = static_cast<GLuint>(activeUniforms);
|
||||||
|
GetActiveUniformsiv(program, 1, &outOfRange, GL_UNIFORM_TYPE, params);
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||||
|
EXPECT_EQ(params[0], -999);
|
||||||
|
|
||||||
|
// E3: GL 4.2 token -> GL_INVALID_ENUM here.
|
||||||
|
GetActiveUniformsiv(program, 1, &validIndex, GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX, params);
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_ENUM);
|
||||||
|
EXPECT_EQ(params[0], -999);
|
||||||
|
|
||||||
|
// E4a: a live shader name -> GL_INVALID_OPERATION.
|
||||||
|
GLuint shader = CreateShader(GL_VERTEX_SHADER);
|
||||||
|
GetActiveUniformsiv(shader, 1, &validIndex, GL_UNIFORM_TYPE, params);
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||||
|
|
||||||
|
// E4b: a never-generated name -> GL_INVALID_VALUE.
|
||||||
|
GetActiveUniformsiv(9999u, 1, &validIndex, GL_UNIFORM_TYPE, params);
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||||
|
|
||||||
|
// E6: zero count on a linked program is a valid no-op.
|
||||||
|
GetActiveUniformsiv(program, 0, &validIndex, GL_UNIFORM_TYPE, params);
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
EXPECT_EQ(params[0], -999);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user