/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 2.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Rbo state query tests. *//*--------------------------------------------------------------------*/ #include "es2fShaderStateQueryTests.hpp" #include "glsStateQueryUtil.hpp" #include "es2fApiCase.hpp" #include "gluRenderContext.hpp" #include "gluShaderProgram.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "deRandom.hpp" #include "deMath.h" #include "deString.h" using namespace glw; // GLint and other GL types using deqp::gls::StateQueryUtil::StateQueryMemoryWriteGuard; namespace deqp { namespace gles2 { namespace Functional { namespace { static const char* commonTestVertSource = "void main (void)\n" "{\n" " gl_Position = vec4(0.0);\n" "}\n"; static const char* commonTestFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n"; static const char* brokenShader = "broken, this should not compile!\n" "\n"; // rounds x.1 to x+1 template <typename T> T roundGLfloatToNearestIntegerUp (GLfloat val) { return (T)(ceil(val)); } // rounds x.9 to x template <typename T> T roundGLfloatToNearestIntegerDown (GLfloat val) { return (T)(floor(val)); } bool checkIntEquals (tcu::TestContext& testCtx, GLint got, GLint expected) { using tcu::TestLog; if (got != expected) { testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << expected << "; got " << got << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value"); return false; } return true; } void checkPointerEquals (tcu::TestContext& testCtx, const void* got, const void* expected) { using tcu::TestLog; if (got != expected) { testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << expected << "; got " << got << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value"); } } void verifyShaderParam (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint shader, GLenum pname, GLenum reference) { StateQueryMemoryWriteGuard<GLint> state; gl.glGetShaderiv(shader, pname, &state); if (state.verifyValidity(testCtx)) checkIntEquals(testCtx, state, reference); } bool verifyProgramParam (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLenum pname, GLenum reference) { StateQueryMemoryWriteGuard<GLint> state; gl.glGetProgramiv(program, pname, &state); if (state.verifyValidity(testCtx)) return checkIntEquals(testCtx, state, reference); return false; } void verifyCurrentVertexAttribf (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[4]> attribValue; gl.glGetVertexAttribfv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue); attribValue.verifyValidity(testCtx); if (attribValue[0] != x || attribValue[1] != y || attribValue[2] != z || attribValue[3] != w) { testCtx.getLog() << TestLog::Message << "// ERROR: Expected [" << x << "," << y << "," << z << "," << w << "];" << "got [" << attribValue[0] << "," << attribValue[1] << "," << attribValue[2] << "," << attribValue[3] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid attribute value"); } } void verifyCurrentVertexAttribConversion (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLint[4]> attribValue; gl.glGetVertexAttribiv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue); attribValue.verifyValidity(testCtx); const GLint referenceAsGLintMin[] = { roundGLfloatToNearestIntegerDown<GLint>(x), roundGLfloatToNearestIntegerDown<GLint>(y), roundGLfloatToNearestIntegerDown<GLint>(z), roundGLfloatToNearestIntegerDown<GLint>(w) }; const GLint referenceAsGLintMax[] = { roundGLfloatToNearestIntegerUp<GLint>(x), roundGLfloatToNearestIntegerUp<GLint>(y), roundGLfloatToNearestIntegerUp<GLint>(z), roundGLfloatToNearestIntegerUp<GLint>(w) }; if (attribValue[0] < referenceAsGLintMin[0] || attribValue[0] > referenceAsGLintMax[0] || attribValue[1] < referenceAsGLintMin[1] || attribValue[1] > referenceAsGLintMax[1] || attribValue[2] < referenceAsGLintMin[2] || attribValue[2] > referenceAsGLintMax[2] || attribValue[3] < referenceAsGLintMin[3] || attribValue[3] > referenceAsGLintMax[3]) { testCtx.getLog() << TestLog::Message << "// ERROR: expected in range " << "[" << referenceAsGLintMin[0] << " " << referenceAsGLintMax[0] << "], " << "[" << referenceAsGLintMin[1] << " " << referenceAsGLintMax[1] << "], " << "[" << referenceAsGLintMin[2] << " " << referenceAsGLintMax[2] << "], " << "[" << referenceAsGLintMin[3] << " " << referenceAsGLintMax[3] << "]" << "; got " << attribValue[0] << ", " << attribValue[1] << ", " << attribValue[2] << ", " << attribValue[3] << " " << "; Input=" << x << "; " << y << "; " << z << "; " << w << " " << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid attribute value"); } } void verifyVertexAttrib (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLenum pname, GLenum reference) { StateQueryMemoryWriteGuard<GLint> state; gl.glGetVertexAttribiv(index, pname, &state); if (state.verifyValidity(testCtx)) checkIntEquals(testCtx, state, reference); } void verifyUniformValue1f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[1]> state; gl.glGetUniformfv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << "]; got [" << state[0] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue2f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x, float y) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[2]> state; gl.glGetUniformfv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x || state[1] != y) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << ", " << y << "]; got [" << state[0] << ", " << state[1] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue3f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x, float y, float z) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[3]> state; gl.glGetUniformfv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x || state[1] != y || state[2] != z) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << ", " << y << ", " << z << "]; got [" << state[0] << ", " << state[1] << ", " << state[2] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue4f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x, float y, float z, float w) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[4]> state; gl.glGetUniformfv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x || state[1] != y || state[2] != z || state[3] != w) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << ", " << y << ", " << z << ", " << w << "]; got [" << state[0] << ", " << state[1] << ", " << state[2] << ", " << state[3] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue1i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLint[1]> state; gl.glGetUniformiv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << "]; got [" << state[0] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue2i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x, GLint y) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLint[2]> state; gl.glGetUniformiv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x || state[1] != y) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << ", " << y << "]; got [" << state[0] << ", " << state[1] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue3i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x, GLint y, GLint z) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLint[3]> state; gl.glGetUniformiv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x || state[1] != y || state[2] != z) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << ", " << y << ", " << z << "]; got [" << state[0] << ", " << state[1] << ", " << state[2] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } void verifyUniformValue4i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x, GLint y, GLint z, GLint w) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLint[4]> state; gl.glGetUniformiv(program, location, state); if (!state.verifyValidity(testCtx)) return; if (state[0] != x || state[1] != y || state[2] != z || state[3] != w) { testCtx.getLog() << TestLog::Message << "// ERROR: expected [" << x << ", " << y << ", " << z << ", " << w << "]; got [" << state[0] << ", " << state[1] << ", " << state[2] << ", " << state[3] << "]" << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } template <int Count> void verifyUniformValues (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, const GLfloat* values) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[Count]> state; gl.glGetUniformfv(program, location, state); if (!state.verifyValidity(testCtx)) return; for (int ndx = 0; ndx < Count; ++ndx) { if (values[ndx] != state[ndx]) { testCtx.getLog() << TestLog::Message << "// ERROR: at index " << ndx << " expected " << values[ndx] << "; got " << state[ndx] << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } } template <int N> void verifyUniformMatrixValues (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, const GLfloat* values, bool transpose) { using tcu::TestLog; StateQueryMemoryWriteGuard<GLfloat[N*N]> state; gl.glGetUniformfv(program, location, state); if (!state.verifyValidity(testCtx)) return; for (int y = 0; y < N; ++y) for (int x = 0; x < N; ++x) { const int refIndex = y*N + x; const int stateIndex = transpose ? (x*N + y) : (y*N + x); if (values[refIndex] != state[stateIndex]) { testCtx.getLog() << TestLog::Message << "// ERROR: at index [" << y << "][" << x << "] expected " << values[refIndex] << "; got " << state[stateIndex] << TestLog::EndMessage; if (testCtx.getTestResult() == QP_TEST_RESULT_PASS) testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid uniform value"); } } } void requireShaderCompiler (tcu::TestContext& testCtx, glu::CallLogWrapper& gl) { StateQueryMemoryWriteGuard<GLboolean> state; gl.glGetBooleanv(GL_SHADER_COMPILER, &state); if (!state.verifyValidity(testCtx) || state != GL_TRUE) throw tcu::NotSupportedError("Test requires SHADER_COMPILER = TRUE"); } class ShaderTypeCase : public ApiCase { public: ShaderTypeCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { const GLenum shaderTypes[] = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER}; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(shaderTypes); ++ndx) { const GLuint shader = glCreateShader(shaderTypes[ndx]); verifyShaderParam(m_testCtx, *this, shader, GL_SHADER_TYPE, shaderTypes[ndx]); glDeleteShader(shader); } } }; class ShaderCompileStatusCase : public ApiCase { public: ShaderCompileStatusCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { requireShaderCompiler(m_testCtx, *this); GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_FALSE); verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_FALSE); glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE); verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_TRUE); glDeleteShader(shaderVert); glDeleteShader(shaderFrag); expectError(GL_NO_ERROR); } }; class ShaderInfoLogCase : public ApiCase { public: ShaderInfoLogCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { requireShaderCompiler(m_testCtx, *this); using tcu::TestLog; // INFO_LOG_LENGTH is 0 by default and it includes null-terminator const GLuint shader = glCreateShader(GL_VERTEX_SHADER); verifyShaderParam(m_testCtx, *this, shader, GL_INFO_LOG_LENGTH, 0); glShaderSource(shader, 1, &brokenShader, DE_NULL); glCompileShader(shader); expectError(GL_NO_ERROR); // check the log length StateQueryMemoryWriteGuard<GLint> logLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); if (!logLength.verifyValidity(m_testCtx)) { glDeleteShader(shader); return; } if (logLength == 0) { glDeleteShader(shader); return; } // check normal case { char buffer[2048] = {'x'}; // non-zero initialization GLint written = 0; // written does not include null terminator glGetShaderInfoLog(shader, DE_LENGTH_OF_ARRAY(buffer), &written, buffer); // check lengths are consistent if (logLength <= DE_LENGTH_OF_ARRAY(buffer)) { if (written != logLength-1) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length " << logLength-1 << "; got " << written << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length"); } } // check null-terminator, either at end of buffer or at buffer[written] const char* terminator = &buffer[DE_LENGTH_OF_ARRAY(buffer) - 1]; if (logLength < DE_LENGTH_OF_ARRAY(buffer)) terminator = &buffer[written]; if (*terminator != '\0') { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator, got " << (int)*terminator << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log terminator"); } } // check with too small buffer { char buffer[2048] = {'x'}; // non-zero initialization // check string always ends with \0, even with small buffers GLint written = 0; glGetShaderInfoLog(shader, 1, &written, buffer); if (written != 0) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length 0; got " << written << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length"); } if (buffer[0] != '\0') { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator, got " << (int)buffer[0] << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log terminator"); } } glDeleteShader(shader); expectError(GL_NO_ERROR); } }; class ShaderSourceCase : public ApiCase { public: ShaderSourceCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { requireShaderCompiler(m_testCtx, *this); using tcu::TestLog; // SHADER_SOURCE_LENGTH does include 0-terminator const GLuint shader = glCreateShader(GL_VERTEX_SHADER); verifyShaderParam(m_testCtx, *this, shader, GL_SHADER_SOURCE_LENGTH, 0); // check the SHADER_SOURCE_LENGTH { glShaderSource(shader, 1, &brokenShader, DE_NULL); expectError(GL_NO_ERROR); StateQueryMemoryWriteGuard<GLint> sourceLength; glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLength); sourceLength.verifyValidity(m_testCtx); const GLint referenceLength = (GLint)std::string(brokenShader).length() + 1; // including the null terminator if (sourceLength != referenceLength) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length " << referenceLength << "; got " << sourceLength << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length"); } } // check the concat source SHADER_SOURCE_LENGTH { const char* shaders[] = {brokenShader, brokenShader}; glShaderSource(shader, 2, shaders, DE_NULL); expectError(GL_NO_ERROR); StateQueryMemoryWriteGuard<GLint> sourceLength; glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLength); sourceLength.verifyValidity(m_testCtx); const GLint referenceLength = 2 * (GLint)std::string(brokenShader).length() + 1; // including the null terminator if (sourceLength != referenceLength) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length " << referenceLength << "; got " << sourceLength << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length"); } } // check the string length { char buffer[2048] = {'x'}; DE_ASSERT(DE_LENGTH_OF_ARRAY(buffer) > 2 * (int)std::string(brokenShader).length()); GLint written = 0; // not inluding null-terminator glGetShaderSource(shader, DE_LENGTH_OF_ARRAY(buffer), &written, buffer); const GLint referenceLength = 2 * (GLint)std::string(brokenShader).length(); if (written != referenceLength) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected write length " << referenceLength << "; got " << written << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length"); } // check null pointer at else { if (buffer[referenceLength] != '\0') { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator at " << referenceLength << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "did not get a null terminator"); } } } // check with small buffer { char buffer[2048] = {'x'}; GLint written = 0; glGetShaderSource(shader, 1, &written, buffer); if (written != 0) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected write length 0; got " << written << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length"); } if (buffer[0] != '\0') { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator; got=" << int(buffer[0]) << ", char=" << buffer[0] << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid terminator"); } } glDeleteShader(shader); expectError(GL_NO_ERROR); } }; class DeleteStatusCase : public ApiCase { public: DeleteStatusCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE); verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_TRUE); GLuint shaderProg = glCreateProgram(); glAttachShader(shaderProg, shaderVert); glAttachShader(shaderProg, shaderFrag); glLinkProgram(shaderProg); expectError(GL_NO_ERROR); verifyProgramParam (m_testCtx, *this, shaderProg, GL_LINK_STATUS, GL_TRUE); verifyShaderParam (m_testCtx, *this, shaderVert, GL_DELETE_STATUS, GL_FALSE); verifyShaderParam (m_testCtx, *this, shaderFrag, GL_DELETE_STATUS, GL_FALSE); verifyProgramParam (m_testCtx, *this, shaderProg, GL_DELETE_STATUS, GL_FALSE); expectError(GL_NO_ERROR); glUseProgram(shaderProg); glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(shaderProg); expectError(GL_NO_ERROR); verifyShaderParam (m_testCtx, *this, shaderVert, GL_DELETE_STATUS, GL_TRUE); verifyShaderParam (m_testCtx, *this, shaderFrag, GL_DELETE_STATUS, GL_TRUE); verifyProgramParam (m_testCtx, *this, shaderProg, GL_DELETE_STATUS, GL_TRUE); expectError(GL_NO_ERROR); glUseProgram(0); expectError(GL_NO_ERROR); } }; class CurrentVertexAttribInitialCase : public ApiCase { public: CurrentVertexAttribInitialCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; int attribute_count = 16; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count); // initial for (int index = 0; index < attribute_count; ++index) { StateQueryMemoryWriteGuard<GLfloat[4]> attribValue; glGetVertexAttribfv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue); attribValue.verifyValidity(m_testCtx); if (attribValue[0] != 0.0f || attribValue[1] != 0.0f || attribValue[2] != 0.0f || attribValue[3] != 1.0f) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected [0, 0, 0, 1];" << "got [" << attribValue[0] << "," << attribValue[1] << "," << attribValue[2] << "," << attribValue[3] << "]" << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid attribute value"); } } } }; class CurrentVertexAttribFloatCase : public ApiCase { public: CurrentVertexAttribFloatCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; de::Random rnd(0xabcdef); int attribute_count = 16; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count); // test write float/read float for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = rnd.getFloat(-64000, 64000); const GLfloat z = rnd.getFloat(-64000, 64000); const GLfloat w = rnd.getFloat(-64000, 64000); glVertexAttrib4f(index, x, y, z, w); verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w); } for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = rnd.getFloat(-64000, 64000); const GLfloat z = rnd.getFloat(-64000, 64000); const GLfloat w = 1.0f; glVertexAttrib3f(index, x, y, z); verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w); } for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = rnd.getFloat(-64000, 64000); const GLfloat z = 0.0f; const GLfloat w = 1.0f; glVertexAttrib2f(index, x, y); verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w); } for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = 0.0f; const GLfloat z = 0.0f; const GLfloat w = 1.0f; glVertexAttrib1f(index, x); verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w); } } }; class CurrentVertexAttribConversionCase : public ApiCase { public: CurrentVertexAttribConversionCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; de::Random rnd(0xabcdef); int attribute_count = 16; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count); // test write float/read float for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = rnd.getFloat(-64000, 64000); const GLfloat z = rnd.getFloat(-64000, 64000); const GLfloat w = rnd.getFloat(-64000, 64000); glVertexAttrib4f(index, x, y, z, w); verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w); } for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = rnd.getFloat(-64000, 64000); const GLfloat z = rnd.getFloat(-64000, 64000); const GLfloat w = 1.0f; glVertexAttrib3f(index, x, y, z); verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w); } for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = rnd.getFloat(-64000, 64000); const GLfloat z = 0.0f; const GLfloat w = 1.0f; glVertexAttrib2f(index, x, y); verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w); } for (int index = 0; index < attribute_count; ++index) { const GLfloat x = rnd.getFloat(-64000, 64000); const GLfloat y = 0.0f; const GLfloat z = 0.0f; const GLfloat w = 1.0f; glVertexAttrib1f(index, x); verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w); } } }; class ProgramInfoLogCase : public ApiCase { public: ProgramInfoLogCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &brokenShader, DE_NULL); glShaderSource(shaderFrag, 1, &brokenShader, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); GLuint program = glCreateProgram(); glAttachShader(program, shaderVert); glAttachShader(program, shaderFrag); glLinkProgram(program); // check INFO_LOG_LENGTH == GetProgramInfoLog len { char buffer[2048] = {'x'}; GLint written = 0; glGetProgramInfoLog(program, DE_LENGTH_OF_ARRAY(buffer), &written, buffer); StateQueryMemoryWriteGuard<GLint> logLength; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); logLength.verifyValidity(m_testCtx); if (logLength != 0 && written+1 != logLength) // INFO_LOG_LENGTH contains 0-terminator { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected INFO_LOG_LENGTH " << written+1 << "; got " << logLength << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length"); } } // check GetProgramInfoLog works with too small buffer { char buffer[2048] = {'x'}; GLint written = 0; glGetProgramInfoLog(program, 1, &written, buffer); if (written != 0) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected write length 0; got " << written << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length"); } } glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } }; class ProgramValidateStatusCase : public ApiCase { public: ProgramValidateStatusCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { // test validate ok { GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); GLuint program = glCreateProgram(); glAttachShader(program, shaderVert); glAttachShader(program, shaderFrag); glLinkProgram(program); expectError(GL_NO_ERROR); verifyShaderParam (m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE); verifyShaderParam (m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_TRUE); verifyProgramParam (m_testCtx, *this, program, GL_LINK_STATUS, GL_TRUE); glValidateProgram(program); verifyProgramParam(m_testCtx, *this, program, GL_VALIDATE_STATUS, GL_TRUE); glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } // test with broken shader { GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &brokenShader, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); GLuint program = glCreateProgram(); glAttachShader(program, shaderVert); glAttachShader(program, shaderFrag); glLinkProgram(program); expectError(GL_NO_ERROR); verifyShaderParam (m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE); verifyShaderParam (m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_FALSE); verifyProgramParam (m_testCtx, *this, program, GL_LINK_STATUS, GL_FALSE); glValidateProgram(program); verifyProgramParam(m_testCtx, *this, program, GL_VALIDATE_STATUS, GL_FALSE); glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } } }; class ProgramAttachedShadersCase : public ApiCase { public: ProgramAttachedShadersCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); // check ATTACHED_SHADERS GLuint program = glCreateProgram(); verifyProgramParam(m_testCtx, *this, program, GL_ATTACHED_SHADERS, 0); expectError(GL_NO_ERROR); glAttachShader(program, shaderVert); verifyProgramParam(m_testCtx, *this, program, GL_ATTACHED_SHADERS, 1); expectError(GL_NO_ERROR); glAttachShader(program, shaderFrag); verifyProgramParam(m_testCtx, *this, program, GL_ATTACHED_SHADERS, 2); expectError(GL_NO_ERROR); // check GetAttachedShaders { GLuint shaders[2] = {0, 0}; GLint count = 0; glGetAttachedShaders(program, DE_LENGTH_OF_ARRAY(shaders), &count, shaders); if (count != 2) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 2; got " << count << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count"); } // shaders are the attached shaders? if (!((shaders[0] == shaderVert && shaders[1] == shaderFrag) || (shaders[0] == shaderFrag && shaders[1] == shaderVert))) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected {" << shaderVert << ", " << shaderFrag << "}; got {" << shaders[0] << ", " << shaders[1] << "}" << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count"); } } // check GetAttachedShaders with too small buffer { GLuint shaders[2] = {0, 0}; GLint count = 0; glGetAttachedShaders(program, 0, &count, shaders); if (count != 0) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 0; got " << count << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count"); } count = 0; glGetAttachedShaders(program, 1, &count, shaders); if (count != 1) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 1; got " << count << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count"); } } glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } }; class ProgramActiveUniformNameCase : public ApiCase { public: ProgramActiveUniformNameCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; static const char* testVertSource = "uniform highp float uniformNameWithLength23;\n" "uniform highp vec2 uniformVec2;\n" "uniform highp mat4 uniformMat4;\n" "void main (void)\n" "{\n" " gl_Position = vec4(0.0) + vec4(uniformNameWithLength23) + vec4(uniformVec2.x) + vec4(uniformMat4[2][3]);\n" "}\n\0"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n\0"; GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &testVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); GLuint program = glCreateProgram(); glAttachShader(program, shaderVert); glAttachShader(program, shaderFrag); glLinkProgram(program); expectError(GL_NO_ERROR); verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_UNIFORMS, 3); verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_UNIFORM_MAX_LENGTH, (GLint)std::string("uniformNameWithLength23").length() + 1); // including a null terminator expectError(GL_NO_ERROR); const char* uniformNames[] = { "uniformNameWithLength23", "uniformVec2", "uniformMat4" }; // check names for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uniformNames); ++ndx) { char buffer[2048] = {'x'}; char* bufferEnd = (buffer + 1); GLint written = 0; // null terminator not included GLint size = 0; GLenum type = 0; glGetActiveUniform(program, ndx, DE_LENGTH_OF_ARRAY(buffer), &written, &size, &type, buffer); if (written < DE_LENGTH_OF_ARRAY(buffer)) bufferEnd = &buffer[written]; // find matching uniform { const std::string uniformName(buffer, bufferEnd); bool found = false; for (int uniformNdx = 0; uniformNdx < DE_LENGTH_OF_ARRAY(uniformNames); ++uniformNdx) { if (uniformName == uniformNames[uniformNdx]) { found = true; break; } } if (!found) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Got unknown uniform name: " << uniformName << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform name"); } } // and with too small buffer written = 0; glGetActiveUniform(program, ndx, 1, &written, &size, &type, buffer); if (written != 0) { m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 0 got " << written << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform name length"); } } glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } }; class ProgramUniformCase : public ApiCase { public: ProgramUniformCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { const struct UniformType { const char* declaration; const char* postDeclaration; const char* precision; const char* layout; const char* getter; GLenum type; GLint size; GLint isRowMajor; } uniformTypes[] = { { "float", "", "highp", "", "uniformValue", GL_FLOAT, 1, GL_FALSE }, { "float", "[2]", "highp", "", "uniformValue[1]", GL_FLOAT, 2, GL_FALSE }, { "vec2", "", "highp", "", "uniformValue.x", GL_FLOAT_VEC2, 1, GL_FALSE }, { "vec3", "", "highp", "", "uniformValue.x", GL_FLOAT_VEC3, 1, GL_FALSE }, { "vec4", "", "highp", "", "uniformValue.x", GL_FLOAT_VEC4, 1, GL_FALSE }, { "int", "", "highp", "", "float(uniformValue)", GL_INT, 1, GL_FALSE }, { "ivec2", "", "highp", "", "float(uniformValue.x)", GL_INT_VEC2, 1, GL_FALSE }, { "ivec3", "", "highp", "", "float(uniformValue.x)", GL_INT_VEC3, 1, GL_FALSE }, { "ivec4", "", "highp", "", "float(uniformValue.x)", GL_INT_VEC4, 1, GL_FALSE }, { "bool", "", "", "", "float(uniformValue)", GL_BOOL, 1, GL_FALSE }, { "bvec2", "", "", "", "float(uniformValue.x)", GL_BOOL_VEC2, 1, GL_FALSE }, { "bvec3", "", "", "", "float(uniformValue.x)", GL_BOOL_VEC3, 1, GL_FALSE }, { "bvec4", "", "", "", "float(uniformValue.x)", GL_BOOL_VEC4, 1, GL_FALSE }, { "mat2", "", "highp", "", "float(uniformValue[0][0])", GL_FLOAT_MAT2, 1, GL_FALSE }, { "mat3", "", "highp", "", "float(uniformValue[0][0])", GL_FLOAT_MAT3, 1, GL_FALSE }, { "mat4", "", "highp", "", "float(uniformValue[0][0])", GL_FLOAT_MAT4, 1, GL_FALSE }, { "sampler2D", "", "highp", "", "float(texture2D(uniformValue, vec2(0.0, 0.0)).r)", GL_SAMPLER_2D, 1, GL_FALSE }, { "samplerCube", "", "highp", "", "float(textureCube(uniformValue, vec3(0.0, 0.0, 0.0)).r)", GL_SAMPLER_CUBE, 1, GL_FALSE }, }; static const char* vertSource = "void main (void)\n" "{\n" " gl_Position = vec4(0.0);\n" "}\n\0"; GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); GLuint program = glCreateProgram(); glAttachShader(program, shaderVert); glAttachShader(program, shaderFrag); glShaderSource(shaderVert, 1, &vertSource, DE_NULL); glCompileShader(shaderVert); expectError(GL_NO_ERROR); for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uniformTypes); ++ndx) { tcu::ScopedLogSection(m_log, uniformTypes[ndx].declaration, std::string("Verify type of ") + uniformTypes[ndx].declaration + " variable" + uniformTypes[ndx].postDeclaration ); // gen fragment shader std::ostringstream frag; frag << uniformTypes[ndx].layout << "uniform " << uniformTypes[ndx].precision << " " << uniformTypes[ndx].declaration << " uniformValue" << uniformTypes[ndx].postDeclaration << ";\n"; frag << "void main (void)\n"; frag << "{\n"; frag << " gl_FragColor = vec4(" << uniformTypes[ndx].getter << ");\n"; frag << "}\n"; { std::string fragmentSource = frag.str(); const char* fragmentSourceCStr = fragmentSource.c_str(); glShaderSource(shaderFrag, 1, &fragmentSourceCStr, DE_NULL); } // compile & link glCompileShader(shaderFrag); glLinkProgram(program); // test if (verifyProgramParam(m_testCtx, *this, program, GL_LINK_STATUS, GL_TRUE)) { const GLint index = 0; // first and only active uniform char buffer[] = "not written to"; // not written to GLint written = 0; GLint size = 0; GLenum type = 0; glGetActiveUniform(program, index, 0, &written, &size, &type, buffer); checkIntEquals(m_testCtx, type, uniformTypes[ndx].type); checkIntEquals(m_testCtx, size, uniformTypes[ndx].size); } } glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } }; class ActiveAttributesCase : public ApiCase { public: ActiveAttributesCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { using tcu::TestLog; static const char* testVertSource = "attribute highp vec2 longInputAttributeName;\n" "attribute highp vec2 shortName;\n" "void main (void)\n" "{\n" " gl_Position = longInputAttributeName.yxxy + shortName.xyxy;\n" "}\n\0"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n\0"; GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER); GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shaderVert, 1, &testVertSource, DE_NULL); glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL); glCompileShader(shaderVert); glCompileShader(shaderFrag); expectError(GL_NO_ERROR); GLuint program = glCreateProgram(); glAttachShader(program, shaderVert); glAttachShader(program, shaderFrag); glLinkProgram(program); expectError(GL_NO_ERROR); verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_ATTRIBUTES, 2); verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, (GLint)std::string("longInputAttributeName").length() + 1); // does include null-terminator // check names for (int attributeNdx = 0; attributeNdx < 2; ++attributeNdx) { char buffer[2048] = {'x'}; GLint written = 0; GLint size = 0; GLenum type = 0; glGetActiveAttrib(program, attributeNdx, DE_LENGTH_OF_ARRAY(buffer), &written, &size, &type, buffer); expectError(GL_NO_ERROR); if (deStringBeginsWith(buffer, "longInputAttributeName")) { checkIntEquals(m_testCtx, written, (GLint)std::string("longInputAttributeName").length()); // does NOT include null-terminator } else if (deStringBeginsWith(buffer, "shortName")) { checkIntEquals(m_testCtx, written, (GLint)std::string("shortName").length()); // does NOT include null-terminator } else { m_testCtx.getLog() << TestLog::Message << "// ERROR: Got unexpected attribute name." << TestLog::EndMessage; if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected name"); } } // and with too short buffer { char buffer[2048] = {'x'}; GLint written = 0; GLint size = 0; GLenum type = 0; glGetActiveAttrib(program, 0, 1, &written, &size, &type, buffer); expectError(GL_NO_ERROR); checkIntEquals(m_testCtx, written, 0); } glDeleteShader(shaderVert); glDeleteShader(shaderFrag); glDeleteProgram(program); expectError(GL_NO_ERROR); } }; struct PointerData { GLint size; GLenum type; GLint stride; GLboolean normalized; void* pointer; }; class VertexAttributeSizeCase : public ApiCase { public: VertexAttributeSizeCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { GLfloat vertexData[4] = {0.0f}; // never accessed // test VertexAttribPointer const PointerData pointers[] = { // size test { 4, GL_FLOAT, 0, GL_FALSE, vertexData }, { 3, GL_FLOAT, 0, GL_FALSE, vertexData }, { 2, GL_FLOAT, 0, GL_FALSE, vertexData }, { 1, GL_FLOAT, 0, GL_FALSE, vertexData }, { 4, GL_SHORT, 0, GL_FALSE, vertexData }, { 3, GL_SHORT, 0, GL_FALSE, vertexData }, { 2, GL_SHORT, 0, GL_FALSE, vertexData }, { 1, GL_SHORT, 0, GL_FALSE, vertexData }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx) { glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer); expectError(GL_NO_ERROR); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_SIZE, pointers[ndx].size); } } }; class VertexAttributeTypeCase : public ApiCase { public: VertexAttributeTypeCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { GLfloat vertexData[4] = {0.0f}; // never accessed const PointerData pointers[] = { { 1, GL_BYTE, 0, GL_FALSE, vertexData }, { 1, GL_UNSIGNED_BYTE, 0, GL_FALSE, vertexData }, { 1, GL_SHORT, 0, GL_FALSE, vertexData }, { 1, GL_UNSIGNED_SHORT, 0, GL_FALSE, vertexData }, { 1, GL_FIXED, 0, GL_FALSE, vertexData }, { 1, GL_FLOAT, 0, GL_FALSE, vertexData }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx) { glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer); expectError(GL_NO_ERROR); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_TYPE, pointers[ndx].type); } } }; class VertexAttributeStrideCase : public ApiCase { public: VertexAttributeStrideCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { GLfloat vertexData[4] = {0.0f}; // never accessed struct StridePointerData { GLint size; GLenum type; GLint stride; void* pointer; }; // test VertexAttribPointer { const StridePointerData pointers[] = { { 1, GL_FLOAT, 0, vertexData }, { 1, GL_FLOAT, 1, vertexData }, { 1, GL_FLOAT, 4, vertexData }, { 1, GL_SHORT, 0, vertexData }, { 1, GL_SHORT, 1, vertexData }, { 1, GL_SHORT, 4, vertexData }, { 1, GL_FIXED, 0, vertexData }, { 1, GL_FIXED, 1, vertexData }, { 1, GL_FIXED, 4, vertexData }, { 1, GL_BYTE, 0, vertexData }, { 1, GL_UNSIGNED_SHORT, 1, vertexData }, { 1, GL_UNSIGNED_SHORT, 4, vertexData }, { 4, GL_UNSIGNED_BYTE, 0, vertexData }, { 4, GL_UNSIGNED_BYTE, 1, vertexData }, { 4, GL_UNSIGNED_BYTE, 4, vertexData }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx) { glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, GL_FALSE, pointers[ndx].stride, pointers[ndx].pointer); expectError(GL_NO_ERROR); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, pointers[ndx].stride); } } } }; class VertexAttributeNormalizedCase : public ApiCase { public: VertexAttributeNormalizedCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { GLfloat vertexData[4] = {0.0f}; // never accessed // test VertexAttribPointer { const PointerData pointers[] = { { 1, GL_BYTE, 0, GL_FALSE, vertexData }, { 1, GL_SHORT, 0, GL_FALSE, vertexData }, { 1, GL_UNSIGNED_BYTE, 0, GL_FALSE, vertexData }, { 1, GL_UNSIGNED_SHORT, 0, GL_FALSE, vertexData }, { 1, GL_BYTE, 0, GL_TRUE, vertexData }, { 1, GL_SHORT, 0, GL_TRUE, vertexData }, { 1, GL_UNSIGNED_BYTE, 0, GL_TRUE, vertexData }, { 1, GL_UNSIGNED_SHORT, 0, GL_TRUE, vertexData }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx) { glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer); expectError(GL_NO_ERROR); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, pointers[ndx].normalized); } } } }; class VertexAttributeEnabledCase : public ApiCase { public: VertexAttributeEnabledCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { // VERTEX_ATTRIB_ARRAY_ENABLED verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_FALSE); glEnableVertexAttribArray(0); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_TRUE); glDisableVertexAttribArray(0); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_FALSE); } }; class VertexAttributeBufferBindingCase : public ApiCase { public: VertexAttributeBufferBindingCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { // initial verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, 0); GLuint bufferID; glGenBuffers(1, &bufferID); glBindBuffer(GL_ARRAY_BUFFER, bufferID); expectError(GL_NO_ERROR); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); expectError(GL_NO_ERROR); verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, bufferID); glDeleteBuffers(1, &bufferID); expectError(GL_NO_ERROR); } }; class VertexAttributePointerCase : public ApiCase { public: VertexAttributePointerCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { StateQueryMemoryWriteGuard<GLvoid*> initialState; glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &initialState); initialState.verifyValidity(m_testCtx); checkPointerEquals(m_testCtx, initialState, 0); GLfloat vertexData[4] = {0.0f}; // never accessed const PointerData pointers[] = { { 1, GL_BYTE, 0, GL_FALSE, &vertexData[2] }, { 1, GL_SHORT, 0, GL_FALSE, &vertexData[1] }, { 1, GL_FIXED, 0, GL_FALSE, &vertexData[2] }, { 1, GL_FIXED, 0, GL_FALSE, &vertexData[1] }, { 1, GL_FLOAT, 0, GL_FALSE, &vertexData[0] }, { 1, GL_FLOAT, 0, GL_FALSE, &vertexData[3] }, { 1, GL_FLOAT, 0, GL_FALSE, &vertexData[2] }, { 1, GL_UNSIGNED_SHORT, 0, GL_FALSE, &vertexData[0] }, { 4, GL_UNSIGNED_SHORT, 0, GL_FALSE, &vertexData[1] }, { 4, GL_UNSIGNED_SHORT, 0, GL_FALSE, &vertexData[2] }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx) { glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer); expectError(GL_NO_ERROR); StateQueryMemoryWriteGuard<GLvoid*> state; glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &state); state.verifyValidity(m_testCtx); checkPointerEquals(m_testCtx, state, pointers[ndx].pointer); } } }; class UniformValueFloatCase : public ApiCase { public: UniformValueFloatCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { static const char* testVertSource = "uniform highp float floatUniform;\n" "uniform highp vec2 float2Uniform;\n" "uniform highp vec3 float3Uniform;\n" "uniform highp vec4 float4Uniform;\n" "void main (void)\n" "{\n" " gl_Position = vec4(floatUniform + float2Uniform.x + float3Uniform.x + float4Uniform.x);\n" "}\n"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(testVertSource, testFragSource)); if (!program.isOk()) { m_log << program; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader"); return; } glUseProgram(program.getProgram()); expectError(GL_NO_ERROR); GLint location; location = glGetUniformLocation(program.getProgram(), "floatUniform"); glUniform1f(location, 1.0f); verifyUniformValue1f(m_testCtx, *this, program.getProgram(), location, 1.0f); location = glGetUniformLocation(program.getProgram(), "float2Uniform"); glUniform2f(location, 1.0f, 2.0f); verifyUniformValue2f(m_testCtx, *this, program.getProgram(), location, 1.0f, 2.0f); location = glGetUniformLocation(program.getProgram(), "float3Uniform"); glUniform3f(location, 1.0f, 2.0f, 3.0f); verifyUniformValue3f(m_testCtx, *this, program.getProgram(), location, 1.0f, 2.0f, 3.0f); location = glGetUniformLocation(program.getProgram(), "float4Uniform"); glUniform4f(location, 1.0f, 2.0f, 3.0f, 4.0f); verifyUniformValue4f(m_testCtx, *this, program.getProgram(), location, 1.0f, 2.0f, 3.0f, 4.0f); glUseProgram(0); expectError(GL_NO_ERROR); } }; class UniformValueIntCase : public ApiCase { public: UniformValueIntCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { static const char* testVertSource = "uniform highp int intUniform;\n" "uniform highp ivec2 int2Uniform;\n" "uniform highp ivec3 int3Uniform;\n" "uniform highp ivec4 int4Uniform;\n" "void main (void)\n" "{\n" " gl_Position = vec4(float(intUniform + int2Uniform.x + int3Uniform.x + int4Uniform.x));\n" "}\n"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(testVertSource, testFragSource)); if (!program.isOk()) { m_log << program; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader"); return; } glUseProgram(program.getProgram()); expectError(GL_NO_ERROR); GLint location; location = glGetUniformLocation(program.getProgram(), "intUniform"); glUniform1i(location, 1); verifyUniformValue1i(m_testCtx, *this, program.getProgram(), location, 1); location = glGetUniformLocation(program.getProgram(), "int2Uniform"); glUniform2i(location, 1, 2); verifyUniformValue2i(m_testCtx, *this, program.getProgram(), location, 1, 2); location = glGetUniformLocation(program.getProgram(), "int3Uniform"); glUniform3i(location, 1, 2, 3); verifyUniformValue3i(m_testCtx, *this, program.getProgram(), location, 1, 2, 3); location = glGetUniformLocation(program.getProgram(), "int4Uniform"); glUniform4i(location, 1, 2, 3, 4); verifyUniformValue4i(m_testCtx, *this, program.getProgram(), location, 1, 2, 3, 4); glUseProgram(0); expectError(GL_NO_ERROR); } }; class UniformValueBooleanCase : public ApiCase { public: UniformValueBooleanCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { static const char* testVertSource = "uniform bool boolUniform;\n" "uniform bvec2 bool2Uniform;\n" "uniform bvec3 bool3Uniform;\n" "uniform bvec4 bool4Uniform;\n" "void main (void)\n" "{\n" " gl_Position = vec4(float(boolUniform) + float(bool2Uniform.x) + float(bool3Uniform.x) + float(bool4Uniform.x));\n" "}\n"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(testVertSource, testFragSource)); if (!program.isOk()) { m_log << program; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader"); return; } glUseProgram(program.getProgram()); expectError(GL_NO_ERROR); GLint location; // int conversion location = glGetUniformLocation(program.getProgram(), "boolUniform"); glUniform1i(location, 1); verifyUniformValue1i(m_testCtx, *this, program.getProgram(), location, 1); location = glGetUniformLocation(program.getProgram(), "bool2Uniform"); glUniform2i(location, 1, 2); verifyUniformValue2i(m_testCtx, *this, program.getProgram(), location, 1, 1); location = glGetUniformLocation(program.getProgram(), "bool3Uniform"); glUniform3i(location, 0, 1, 2); verifyUniformValue3i(m_testCtx, *this, program.getProgram(), location, 0, 1, 1); location = glGetUniformLocation(program.getProgram(), "bool4Uniform"); glUniform4i(location, 1, 0, 1, -1); verifyUniformValue4i(m_testCtx, *this, program.getProgram(), location, 1, 0, 1, 1); // float conversion location = glGetUniformLocation(program.getProgram(), "boolUniform"); glUniform1f(location, 1.0f); verifyUniformValue1i(m_testCtx, *this, program.getProgram(), location, 1); location = glGetUniformLocation(program.getProgram(), "bool2Uniform"); glUniform2f(location, 1.0f, 0.1f); verifyUniformValue2i(m_testCtx, *this, program.getProgram(), location, 1, 1); location = glGetUniformLocation(program.getProgram(), "bool3Uniform"); glUniform3f(location, 0.0f, 0.1f, -0.1f); verifyUniformValue3i(m_testCtx, *this, program.getProgram(), location, 0, 1, 1); location = glGetUniformLocation(program.getProgram(), "bool4Uniform"); glUniform4f(location, 1.0f, 0.0f, 0.1f, -0.9f); verifyUniformValue4i(m_testCtx, *this, program.getProgram(), location, 1, 0, 1, 1); glUseProgram(0); expectError(GL_NO_ERROR); } }; class UniformValueSamplerCase : public ApiCase { public: UniformValueSamplerCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { static const char* testVertSource = "void main (void)\n" "{\n" " gl_Position = vec4(0.0);\n" "}\n"; static const char* testFragSource = "uniform highp sampler2D uniformSampler;\n" "void main (void)\n" "{\n" " gl_FragColor = vec4(texture2D(uniformSampler, vec2(0.0, 0.0)).x);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(testVertSource, testFragSource)); if (!program.isOk()) { m_log << program; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader"); return; } glUseProgram(program.getProgram()); expectError(GL_NO_ERROR); GLint location; location = glGetUniformLocation(program.getProgram(), "uniformSampler"); glUniform1i(location, 1); verifyUniformValue1i(m_testCtx, *this, program.getProgram(), location, 1); glUseProgram(0); expectError(GL_NO_ERROR); } }; class UniformValueArrayCase : public ApiCase { public: UniformValueArrayCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { static const char* testVertSource = "uniform highp float arrayUniform[5];" "uniform highp vec2 array2Uniform[5];" "uniform highp vec3 array3Uniform[5];" "uniform highp vec4 array4Uniform[5];" "void main (void)\n" "{\n" " gl_Position = \n" " + vec4(arrayUniform[0] + arrayUniform[1] + arrayUniform[2] + arrayUniform[3] + arrayUniform[4])\n" " + vec4(array2Uniform[0].x + array2Uniform[1].x + array2Uniform[2].x + array2Uniform[3].x + array2Uniform[4].x)\n" " + vec4(array3Uniform[0].x + array3Uniform[1].x + array3Uniform[2].x + array3Uniform[3].x + array3Uniform[4].x)\n" " + vec4(array4Uniform[0].x + array4Uniform[1].x + array4Uniform[2].x + array4Uniform[3].x + array4Uniform[4].x);\n" "}\n"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(testVertSource, testFragSource)); if (!program.isOk()) { m_log << program; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader"); return; } glUseProgram(program.getProgram()); expectError(GL_NO_ERROR); GLint location; float uniformValue[5 * 4] = { -1.0f, 0.1f, 4.0f, 800.0f, 13.0f, 55.0f, 12.0f, 91.0f, -55.1f, 1.1f, 98.0f, 19.0f, 41.0f, 65.0f, 4.0f, 12.2f, 95.0f, 77.0f, 32.0f, 48.0f }; location = glGetUniformLocation(program.getProgram(), "arrayUniform"); glUniform1fv(location, 5, uniformValue); expectError(GL_NO_ERROR); verifyUniformValue1f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "arrayUniform[0]"), uniformValue[0]); verifyUniformValue1f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "arrayUniform[1]"), uniformValue[1]); verifyUniformValue1f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "arrayUniform[2]"), uniformValue[2]); verifyUniformValue1f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "arrayUniform[3]"), uniformValue[3]); verifyUniformValue1f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "arrayUniform[4]"), uniformValue[4]); expectError(GL_NO_ERROR); location = glGetUniformLocation(program.getProgram(),"array2Uniform"); glUniform2fv(location, 5, uniformValue); expectError(GL_NO_ERROR); verifyUniformValue2f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array2Uniform[0]"), uniformValue[2 * 0], uniformValue[(2 * 0) + 1]); verifyUniformValue2f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array2Uniform[1]"), uniformValue[2 * 1], uniformValue[(2 * 1) + 1]); verifyUniformValue2f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array2Uniform[2]"), uniformValue[2 * 2], uniformValue[(2 * 2) + 1]); verifyUniformValue2f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array2Uniform[3]"), uniformValue[2 * 3], uniformValue[(2 * 3) + 1]); verifyUniformValue2f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array2Uniform[4]"), uniformValue[2 * 4], uniformValue[(2 * 4) + 1]); expectError(GL_NO_ERROR); location = glGetUniformLocation(program.getProgram(),"array3Uniform"); glUniform3fv(location, 5, uniformValue); expectError(GL_NO_ERROR); verifyUniformValue3f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array3Uniform[0]"), uniformValue[3 * 0], uniformValue[(3 * 0) + 1], uniformValue[(3 * 0) + 2]); verifyUniformValue3f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array3Uniform[1]"), uniformValue[3 * 1], uniformValue[(3 * 1) + 1], uniformValue[(3 * 1) + 2]); verifyUniformValue3f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array3Uniform[2]"), uniformValue[3 * 2], uniformValue[(3 * 2) + 1], uniformValue[(3 * 2) + 2]); verifyUniformValue3f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array3Uniform[3]"), uniformValue[3 * 3], uniformValue[(3 * 3) + 1], uniformValue[(3 * 3) + 2]); verifyUniformValue3f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array3Uniform[4]"), uniformValue[3 * 4], uniformValue[(3 * 4) + 1], uniformValue[(3 * 4) + 2]); expectError(GL_NO_ERROR); location = glGetUniformLocation(program.getProgram(),"array4Uniform"); glUniform4fv(location, 5, uniformValue); expectError(GL_NO_ERROR); verifyUniformValue4f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array4Uniform[0]"), uniformValue[4 * 0], uniformValue[(4 * 0) + 1], uniformValue[(4 * 0) + 2], uniformValue[(4 * 0) + 3]); verifyUniformValue4f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array4Uniform[1]"), uniformValue[4 * 1], uniformValue[(4 * 1) + 1], uniformValue[(4 * 1) + 2], uniformValue[(4 * 1) + 3]); verifyUniformValue4f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array4Uniform[2]"), uniformValue[4 * 2], uniformValue[(4 * 2) + 1], uniformValue[(4 * 2) + 2], uniformValue[(4 * 2) + 3]); verifyUniformValue4f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array4Uniform[3]"), uniformValue[4 * 3], uniformValue[(4 * 3) + 1], uniformValue[(4 * 3) + 2], uniformValue[(4 * 3) + 3]); verifyUniformValue4f(m_testCtx, *this, program.getProgram(), glGetUniformLocation(program.getProgram(), "array4Uniform[4]"), uniformValue[4 * 4], uniformValue[(4 * 4) + 1], uniformValue[(4 * 4) + 2], uniformValue[(4 * 4) + 3]); expectError(GL_NO_ERROR); glUseProgram(0); expectError(GL_NO_ERROR); } }; class UniformValueMatrixCase : public ApiCase { public: UniformValueMatrixCase (Context& context, const char* name, const char* description) : ApiCase(context, name, description) { } void test (void) { static const char* testVertSource = "uniform highp mat2 mat2Uniform;" "uniform highp mat3 mat3Uniform;" "uniform highp mat4 mat4Uniform;" "void main (void)\n" "{\n" " gl_Position = vec4(mat2Uniform[0][0] + mat3Uniform[0][0] + mat4Uniform[0][0]);\n" "}\n"; static const char* testFragSource = "void main (void)\n" "{\n" " gl_FragColor = vec4(0.0);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(testVertSource, testFragSource)); if (!program.isOk()) { m_log << program; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader"); return; } glUseProgram(program.getProgram()); expectError(GL_NO_ERROR); GLint location; float matrixValues[4 * 4] = { -1.0f, 0.1f, 4.0f, 800.0f, 13.0f, 55.0f, 12.0f, 91.0f, -55.1f, 1.1f, 98.0f, 19.0f, 41.0f, 65.0f, 4.0f, 12.2f, }; // the values of the matrix are returned in column major order but they can be given in either order location = glGetUniformLocation(program.getProgram(), "mat2Uniform"); glUniformMatrix2fv(location, 1, GL_FALSE, matrixValues); verifyUniformMatrixValues<2>(m_testCtx, *this, program.getProgram(), location, matrixValues, false); location = glGetUniformLocation(program.getProgram(), "mat3Uniform"); glUniformMatrix3fv(location, 1, GL_FALSE, matrixValues); verifyUniformMatrixValues<3>(m_testCtx, *this, program.getProgram(), location, matrixValues, false); location = glGetUniformLocation(program.getProgram(), "mat4Uniform"); glUniformMatrix4fv(location, 1, GL_FALSE, matrixValues); verifyUniformMatrixValues<4>(m_testCtx, *this, program.getProgram(), location, matrixValues, false); glUseProgram(0); expectError(GL_NO_ERROR); } }; class PrecisionFormatCase : public ApiCase { public: struct RequiredFormat { int negativeRange; int positiveRange; int precision; }; PrecisionFormatCase (Context& context, const char* name, const char* description, glw::GLenum shaderType, glw::GLenum precisionType) : ApiCase (context, name, description) , m_shaderType (shaderType) , m_precisionType (precisionType) { } private: void test (void) { const RequiredFormat expected = getRequiredFormat(); bool error = false; gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean> shaderCompiler; gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[2]> range; gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint> precision; // requires SHADER_COMPILER = true glGetBooleanv(GL_SHADER_COMPILER, &shaderCompiler); expectError(GL_NO_ERROR); if (!shaderCompiler.verifyValidity(m_testCtx)) return; if (shaderCompiler != GL_TRUE) throw tcu::NotSupportedError("SHADER_COMPILER = TRUE required"); // query values glGetShaderPrecisionFormat(m_shaderType, m_precisionType, range, &precision); expectError(GL_NO_ERROR); if (!range.verifyValidity(m_testCtx)) return; if (!precision.verifyValidity(m_testCtx)) return; m_log << tcu::TestLog::Message << "range[0] = " << range[0] << "\n" << "range[1] = " << range[1] << "\n" << "precision = " << precision << tcu::TestLog::EndMessage; // special case for highp and fragment shader if (m_shaderType == GL_FRAGMENT_SHADER && (m_precisionType == GL_HIGH_FLOAT || m_precisionType == GL_HIGH_INT)) { // not supported is a valid return value if (range[0] == 0 && range[1] == 0 && precision == 0) return; } // verify the returned values if (range[0] < expected.negativeRange) { m_log << tcu::TestLog::Message << "// ERROR: Invalid range[0], expected greater or equal to " << expected.negativeRange << tcu::TestLog::EndMessage; error = true; } if (range[1] < expected.positiveRange) { m_log << tcu::TestLog::Message << "// ERROR: Invalid range[1], expected greater or equal to " << expected.positiveRange << tcu::TestLog::EndMessage; error = true; } if (precision < expected.precision) { m_log << tcu::TestLog::Message << "// ERROR: Invalid precision, expected greater or equal to " << expected.precision << tcu::TestLog::EndMessage; error = true; } if (error) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid precision/range"); } RequiredFormat getRequiredFormat (void) const { // Precisions for different types. // For example highp float: range: (-2^62, 2^62) => min = -2^62 + e, max = 2^62 - e const RequiredFormat requirements[] = { { 0, 0, 8 }, //!< lowp float { 13, 13, 10 }, //!< mediump float { 61, 61, 16 }, //!< highp float { 7, 7, 0 }, //!< lowp int { 9, 9, 0 }, //!< mediump int { 15, 15, 0 }, //!< highp int }; const int ndx = (int)m_precisionType - (int)GL_LOW_FLOAT; DE_ASSERT(ndx >= 0); DE_ASSERT(ndx < DE_LENGTH_OF_ARRAY(requirements)); return requirements[ndx]; } const glw::GLenum m_shaderType; const glw::GLenum m_precisionType; }; } // anonymous ShaderStateQueryTests::ShaderStateQueryTests (Context& context) : TestCaseGroup(context, "shader", "Shader State Query tests") { } void ShaderStateQueryTests::init (void) { // shader addChild(new ShaderTypeCase (m_context, "shader_type", "SHADER_TYPE")); addChild(new ShaderCompileStatusCase (m_context, "shader_compile_status", "COMPILE_STATUS")); addChild(new ShaderInfoLogCase (m_context, "shader_info_log_length", "INFO_LOG_LENGTH")); addChild(new ShaderSourceCase (m_context, "shader_source_length", "SHADER_SOURCE_LENGTH")); // shader and program addChild(new DeleteStatusCase (m_context, "delete_status", "DELETE_STATUS")); // vertex-attrib addChild(new CurrentVertexAttribInitialCase (m_context, "current_vertex_attrib_initial", "CURRENT_VERTEX_ATTRIB")); addChild(new CurrentVertexAttribFloatCase (m_context, "current_vertex_attrib_float", "CURRENT_VERTEX_ATTRIB")); addChild(new CurrentVertexAttribConversionCase (m_context, "current_vertex_attrib_float_to_int", "CURRENT_VERTEX_ATTRIB")); // program addChild(new ProgramInfoLogCase (m_context, "program_info_log_length", "INFO_LOG_LENGTH")); addChild(new ProgramValidateStatusCase (m_context, "program_validate_status", "VALIDATE_STATUS")); addChild(new ProgramAttachedShadersCase (m_context, "program_attached_shaders", "ATTACHED_SHADERS")); addChild(new ProgramActiveUniformNameCase (m_context, "program_active_uniform_name", "ACTIVE_UNIFORMS and ACTIVE_UNIFORM_MAX_LENGTH")); addChild(new ProgramUniformCase (m_context, "program_active_uniform_types", "UNIFORM_TYPE and UNIFORM_SIZE")); // attribute related addChild(new ActiveAttributesCase (m_context, "active_attributes", "ACTIVE_ATTRIBUTES and ACTIVE_ATTRIBUTE_MAX_LENGTH")); addChild(new VertexAttributeSizeCase (m_context, "vertex_attrib_size", "VERTEX_ATTRIB_ARRAY_SIZE")); addChild(new VertexAttributeTypeCase (m_context, "vertex_attrib_type", "VERTEX_ATTRIB_ARRAY_TYPE")); addChild(new VertexAttributeStrideCase (m_context, "vertex_attrib_stride", "VERTEX_ATTRIB_ARRAY_STRIDE")); addChild(new VertexAttributeNormalizedCase (m_context, "vertex_attrib_normalized", "VERTEX_ATTRIB_ARRAY_NORMALIZED")); addChild(new VertexAttributeEnabledCase (m_context, "vertex_attrib_array_enabled", "VERTEX_ATTRIB_ARRAY_ENABLED")); addChild(new VertexAttributeBufferBindingCase (m_context, "vertex_attrib_array_buffer_binding", "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING")); addChild(new VertexAttributePointerCase (m_context, "vertex_attrib_pointerv", "GetVertexAttribPointerv")); // uniform values addChild(new UniformValueFloatCase (m_context, "uniform_value_float", "GetUniform*")); addChild(new UniformValueIntCase (m_context, "uniform_value_int", "GetUniform*")); addChild(new UniformValueBooleanCase (m_context, "uniform_value_boolean", "GetUniform*")); addChild(new UniformValueSamplerCase (m_context, "uniform_value_sampler", "GetUniform*")); addChild(new UniformValueArrayCase (m_context, "uniform_value_array", "GetUniform*")); addChild(new UniformValueMatrixCase (m_context, "uniform_value_matrix", "GetUniform*")); // precision format query addChild(new PrecisionFormatCase (m_context, "precision_vertex_lowp_float", "GetShaderPrecisionFormat", GL_VERTEX_SHADER, GL_LOW_FLOAT)); addChild(new PrecisionFormatCase (m_context, "precision_vertex_mediump_float", "GetShaderPrecisionFormat", GL_VERTEX_SHADER, GL_MEDIUM_FLOAT)); addChild(new PrecisionFormatCase (m_context, "precision_vertex_highp_float", "GetShaderPrecisionFormat", GL_VERTEX_SHADER, GL_HIGH_FLOAT)); addChild(new PrecisionFormatCase (m_context, "precision_vertex_lowp_int", "GetShaderPrecisionFormat", GL_VERTEX_SHADER, GL_LOW_INT)); addChild(new PrecisionFormatCase (m_context, "precision_vertex_mediump_int", "GetShaderPrecisionFormat", GL_VERTEX_SHADER, GL_MEDIUM_INT)); addChild(new PrecisionFormatCase (m_context, "precision_vertex_highp_int", "GetShaderPrecisionFormat", GL_VERTEX_SHADER, GL_HIGH_INT)); addChild(new PrecisionFormatCase (m_context, "precision_fragment_lowp_float", "GetShaderPrecisionFormat", GL_FRAGMENT_SHADER, GL_LOW_FLOAT)); addChild(new PrecisionFormatCase (m_context, "precision_fragment_mediump_float", "GetShaderPrecisionFormat", GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT)); addChild(new PrecisionFormatCase (m_context, "precision_fragment_highp_float", "GetShaderPrecisionFormat", GL_FRAGMENT_SHADER, GL_HIGH_FLOAT)); addChild(new PrecisionFormatCase (m_context, "precision_fragment_lowp_int", "GetShaderPrecisionFormat", GL_FRAGMENT_SHADER, GL_LOW_INT)); addChild(new PrecisionFormatCase (m_context, "precision_fragment_mediump_int", "GetShaderPrecisionFormat", GL_FRAGMENT_SHADER, GL_MEDIUM_INT)); addChild(new PrecisionFormatCase (m_context, "precision_fragment_highp_int", "GetShaderPrecisionFormat", GL_FRAGMENT_SHADER, GL_HIGH_INT)); } } // Functional } // gles2 } // deqp