| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2014-2016 The Khronos Group Inc. |
| * |
| * 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 |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "es31cSeparateShaderObjsTests.hpp" |
| #include "deMath.h" |
| #include "deRandom.hpp" |
| #include "deString.h" |
| #include "deStringUtil.hpp" |
| #include "gluDrawUtil.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "glw.h" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "tcuSurface.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuVector.hpp" |
| |
| #include <map> |
| |
| /*************************************************************************/ |
| /* Test Plan for shared_shader_objects: |
| * Overview |
| * |
| * This is a conformance test for XXX_separate_shader_objects extension. The |
| * results of the tests are verified by checking the expected GL error, |
| * by checking the expected state with state query functions and by rendering |
| * test images and comparing specified pixels with expected values. Not all of |
| * the methods are applicable for all tests. Additional details such as |
| * expected error codes are included in test descriptions. |
| * |
| *New Tests |
| * |
| *-- Tests for invidual API functions |
| * |
| * |
| * CreateShaderProgramv tests |
| * |
| * Positive tests: |
| * |
| * * Test with valid parameters and verify that program and GL state are set |
| * accordingly to the code sequence defined in the extension spec. |
| * * Test with vertex and fragment shader types. |
| * * Test with few different count/strings parameters (count >= 1) |
| * |
| * Negative tests: |
| * |
| * * Test with invalid type. Expect INVALID_ENUM and zero return value. |
| * * Test with uncompilable / unlinkable source. Expect no errors. Program |
| * should be returned Program info log may contain information about link / |
| * compile failure. |
| * * Test with count < 0. Expect INVALID_VALUE and zero return value. |
| * |
| * |
| * UseProgramStages tests |
| * |
| * Positive tests: |
| * |
| * * Test with a program containing |
| * - vertex stage only |
| * - fragment stage only |
| * - both stages |
| * * Test with null program to reset a stage or stages. Expect no errors. |
| * * Test with a program that doesn't contain code for a stage defined in the |
| * stages bitfield. Expect no errors. |
| * * Test with a new program pipeline object that has not yet been used/bound. |
| * |
| * Negative tests: |
| * |
| * * Test with invalid stages bitfield (with unused bits). Expect INVALID_VALUE GL error. |
| * * Test with deleted/nonexistent pipeline. Expect INVALID_OPERATION GL error. |
| * * Test with program that isn't separable. Expect INVALID_OPERATION GL error. |
| * * Test with program that isn't linked succesfully. Expect INVALID_OPERATION |
| * GL error. |
| * * Test with deleted/nonexistent program. Expect INVALID_OPERATION error. |
| * |
| * |
| * ActiveShaderProgram tests |
| * |
| * Positive tests: |
| * |
| * * Test with a new program pipeline object that has not yet been used/bound. |
| * |
| * Negative tests: |
| * |
| * * Test with deleted/nonexistent program pipeline object. Expect INVALID_OPERATION and no |
| * changes to active program. |
| * * Test with nonexistent/deleted/unsuccesfully linked program. Expect |
| * INVALID_OPERATION GL error and no changes to active program. |
| * |
| * |
| * GenProgramPipelines tests |
| * |
| * Positive tests: |
| * |
| * * Test creating different amounts of program pipeline object names. |
| * Verify with IsProgramPipeline. |
| * |
| * |
| * BindProgramPipeline tests |
| * |
| * Positive tests: |
| * |
| * * Test binding existing program pipeline object. Verify with |
| * PROGRAM_PIPELINE_BINDING |
| * * Test binding zero program pipeline object. Verify |
| * PROGRAM_PIPELINE_BINDING is reset to 0 |
| * |
| * Negative tests: |
| * |
| * * Test binding deleted/nonexistent program pipeline object. Expect |
| * INVALID_OPERATION GL error and no changes to bound pipeline. |
| * |
| * |
| * DeleteProgramPipelines tests |
| * |
| * Positive tests: |
| * |
| * * Test deleting zero and unused names. Expect no errors (should be no-op) |
| * * Test deleting different amounts of existing pipeline names. Verify |
| * deletion with IsProgramPipeline. |
| * * Test deleting bound names. Expect pipeline binding revert to zero, verify |
| * with PROGRAM_PIPELINE_BINDING. |
| * |
| * |
| * IsProgramPipeline |
| * |
| * Positive tests: |
| * |
| * * Test with deleted/nonexistent program pipeline names. |
| * * Test with existing program pipeline names. |
| * |
| * |
| * ProgramParameteri / PROGRAM_SEPARABLE tests |
| * |
| * Positive tests: |
| * |
| * * Test setting TRUE and FALSE values for existing, unlinked programs. |
| * Verify with GetProgramParameteri |
| * |
| * Negative tests: |
| * |
| * * Test with nonexistent/deleted program. Expect INVALID_OPERATION GL error |
| * * Test with invalid value. Expect INVALID_VALUE GL error |
| * |
| * |
| * GetProgramPipelineiv tests |
| * |
| * Positive tests: |
| * |
| * * Test with new program pipeline object that has not yet been used/bound |
| * * Test ACTIVE_PROGRAM |
| * * Test VERTEX_SHADER |
| * * Test FRAGMENT_SHADER |
| * * Test VALIDATE_STATUS |
| * * Test INFO_LOG_LENGTH |
| * |
| * Negative tests: |
| * |
| * * Test with deleted/nonexistent pipeline. Expect INVALID_OPERATION GL error |
| * |
| * |
| * ValidateProgramPipeline tests: |
| * |
| * Positive tests: |
| * |
| * * Test with valid program pipeline. Expect VALIDATE_STATUS = TRUE |
| * * Test with invalid program pipeline Expect VALIDATE_STATUS = FALSE |
| * * Test with empty program pipeline (uninitialized, but bound). Expect VALIDATE_STATUS = FALSE. |
| * * Test that initial (unvalidated) VALIDATE_STATUS is FALSE |
| * * Test with a new program pipeline object that has not been used/bound yet |
| * |
| * Negative tests: |
| * |
| * * Test with deleted/nonexistent program pipeline object. Expect |
| * INVALID_OPERATION |
| * |
| * |
| * ProgramUniform* tests |
| * |
| * Positive tests: |
| * |
| * * Test all ProgramUniform* methods with few different parameters combinations |
| * * Setup pipeline with programs A and B. Update uniforms for A and verify |
| * that only A is affected. |
| * * Test with a program with all combinations of |
| * - program is/isn't part of a bound pipeline |
| * - program is/isn't made current with UseProgram |
| * - program is/isn't made active with ActiveShaderProgram |
| * in all cases, only the uniforms of the specified program should be |
| * updated. |
| * |
| * Negative tests: |
| * |
| * * Test with deleted/nonexistent program. Expect INVALID_VALUE GL error. |
| * * Test with unsuccesfully linked program. Expect INVALID_OPERATION GL error. |
| * |
| * |
| * GetProgramPipelineInfoLog tests |
| * |
| * Run ValidateProgramPipeline for valid / invalid program pipeline object |
| * before running the tests. NOTE: The spec doesn't require that the driver |
| * updates the pipeline info log. It may or may not contain information about |
| * validation. |
| * |
| * Positive tests |
| * |
| * * Test with NULL length. |
| * * Test with zero bufSize. Expect no errors |
| * * Test with varying bufSizes (where 0 < bufSize <= INFO_LOG_LENGTH). Except |
| * * length = (bufSize - 1) and zero-terminated string with matching length in |
| * infoLog. |
| * |
| * Negative tests |
| * |
| * * Test with deleted/nonexistent program pipeline object. Expect |
| * GL_INVALID_VALUE error (the error is still missing from the spec) |
| * |
| * |
| *-- Other tests |
| * |
| * |
| * UseProgram vs. BindProgramPipeline tests |
| * |
| * Positive tests: |
| * |
| * * Test that a program made active with UseProgram has precedence over |
| * program pipeline object bound with BindProgramPipeline. |
| * * Test that program(s) in bound program pipeline object will be used if |
| * there is no active program set with UseProgram |
| * * Test that a state without active program or without bound pipeline object |
| * generates no errors. |
| * |
| * |
| * Pipeline setup tests |
| * |
| * Positive tests: |
| * |
| * * Test that missing pipeline stages produces no errors: |
| * - no program set with UseProgramStages for vertex or frargment stages |
| * - no vertex or fragment code in a program set for the stage |
| * |
| * Negative tests: |
| * |
| * * Test that program with both vertex and fragment shaders cannot be attached |
| * just to vertex or fragment stage. Expect DrawArrays/Elements to generate |
| * INVALID_OPERATION and pipeline VALIDATE_STATUS set to FALSE. |
| * - Run with and without validating the pipeline with ValidateProgramPipeline |
| * |
| * |
| * Shader/program management tests |
| * |
| * Positive tests: |
| * |
| * * Test creating separable shader objects both by |
| * - Using the core functions combined with PROGRAM_SEPARABLE flag |
| * - CreateShaderProgram |
| * * Test that separable program can contain and links properly if there are |
| * - vertex stage |
| * - fragment stage |
| * - both stages |
| * * Test that active program isn't deleted immediately (deletion doesn't |
| * affect rendering state) |
| * * Test that program in current pipeline isn't deleted immediately |
| * * Test that attaching/detaching/recompiling a shader in active program or |
| * program in current pipeline doesn't affect the program link status or |
| * rendering state. |
| * * Test that succesfully re-linking active program or program in current |
| * pipeline affects the rendering state. |
| * |
| * Negative tests: |
| * |
| * aren't present. |
| * * Test that unsuccesfully re-linking active program or program in current |
| * pipeline sets LINK_STATUS=FALSE but doesn't affect the rendering state. |
| * * Test that unsuccesfully linked program cannot be made part of a program |
| * pipeline object. |
| * |
| * |
| * Uniform update tests |
| * |
| * Positive cases: |
| * |
| * with UseProgram. |
| * * Test that Uniform* functions update the uniforms of a program made active with |
| * ActiveShader program if no program has been made active with UseProgram. |
| * * Test that ProgramUniform* functions update the uniforms of a specified |
| * program regardless of active program (probably already covered with |
| * "ProgramUniform* tests") |
| * |
| * Negative cases: |
| * |
| * * Test that Uniform* functions set INVALID_OPERATION if there is no active |
| * program set with UseProgram nor ActiveShaderProgram |
| * |
| * |
| * Shader interface matching tests |
| * |
| * Positive tests: |
| * |
| * * Test that partially or completely mismatching shaders do not generate |
| * validation nor other GL errors (just undefined inputs) |
| * * Test that exactly matching shaders work. |
| * * Test that variables with matching layout qualifiers match and are defined |
| * even if the shaders don't match exactly. |
| * - Test with int, uint and float component types |
| * - Test with different vector sizes, where output vector size >= input |
| * vector size |
| * |
| * |
| * End Test Plan */ |
| /*************************************************************************/ |
| |
| namespace glcts |
| { |
| |
| using std::string; |
| using std::vector; |
| using tcu::TestLog; |
| |
| // A fragment shader to allow testing various scalar and vector |
| // uniforms as well as array [2] varieties. To keep the uniforms |
| // active they are compared against constants. |
| static const char *s_unifFragShaderSrc = |
| "precision highp float;\n" |
| "uniform ${SCALAR_TYPE} uVal0;\n" |
| "uniform ${VECTOR_TYPE}2 uVal1;\n" |
| "uniform ${VECTOR_TYPE}3 uVal2;\n" |
| "uniform ${VECTOR_TYPE}4 uVal3;\n" |
| "\n" |
| "uniform ${SCALAR_TYPE} uVal4[2];\n" |
| "uniform ${VECTOR_TYPE}2 uVal5[2];\n" |
| "uniform ${VECTOR_TYPE}3 uVal6[2];\n" |
| "uniform ${VECTOR_TYPE}4 uVal7[2];\n" |
| "\n" |
| "const ${SCALAR_TYPE} kVal0= 1${SFX};\n" |
| "const ${VECTOR_TYPE}2 kVal1 = ${VECTOR_TYPE}2(2${SFX}, 3${SFX});\n" |
| "const ${VECTOR_TYPE}3 kVal2 = ${VECTOR_TYPE}3(4${SFX}, 5${SFX}, 6${SFX});\n" |
| "const ${VECTOR_TYPE}4 kVal3 = ${VECTOR_TYPE}4(7${SFX}, 8${SFX}, 9${SFX}, 10${SFX});\n" |
| "\n" |
| "const ${SCALAR_TYPE} kArr4_0 = 11${SFX};\n" |
| "const ${SCALAR_TYPE} kArr4_1 = 12${SFX};\n" |
| "const ${VECTOR_TYPE}2 kArr5_0 = ${VECTOR_TYPE}2(13${SFX}, 14${SFX});\n" |
| "const ${VECTOR_TYPE}2 kArr5_1 = ${VECTOR_TYPE}2(15${SFX}, 16${SFX});\n" |
| "const ${VECTOR_TYPE}3 kArr6_0 = ${VECTOR_TYPE}3(17${SFX}, 18${SFX}, 19${SFX});\n" |
| "const ${VECTOR_TYPE}3 kArr6_1 = ${VECTOR_TYPE}3(20${SFX}, 21${SFX}, 22${SFX});\n" |
| "const ${VECTOR_TYPE}4 kArr7_0 = ${VECTOR_TYPE}4(23${SFX}, 24${SFX}, 25${SFX}, 26${SFX});\n" |
| "const ${VECTOR_TYPE}4 kArr7_1 = ${VECTOR_TYPE}4(27${SFX}, 28${SFX}, 29${SFX}, 30${SFX});\n" |
| "\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| "\n" |
| "void main() {\n" |
| " if ((uVal0 != kVal0) ||\n" |
| " (uVal1 != kVal1) ||\n" |
| " (uVal2 != kVal2) ||\n" |
| " (uVal3 != kVal3) ||\n" |
| " (uVal4[0] != kArr4_0) || (uVal4[1] != kArr4_1) ||\n" |
| " (uVal5[0] != kArr5_0) || (uVal5[1] != kArr5_1) ||\n" |
| " (uVal6[0] != kArr6_0) || (uVal6[1] != kArr6_1) ||\n" |
| " (uVal7[0] != kArr7_0) || (uVal7[1] != kArr7_1)) {\n" |
| " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| " } else {\n" |
| " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " }\n" |
| "}\n"; |
| |
| // A fragment shader to test uniforms of square matrices |
| static const char *s_unifFragSquareMatShaderSrc = "precision highp float;\n" |
| "uniform mat2 uValM2[2];\n" |
| "uniform mat3 uValM3[2];\n" |
| "uniform mat4 uValM4[2];\n" |
| "\n" |
| "const mat2 kMat2_0 = mat2(91.0, 92.0, 93.0, 94.0);\n" |
| "const mat2 kMat2_1 = mat2(95.0, 96.0, 97.0, 98.0);\n" |
| "const mat3 kMat3_0 = mat3(vec3( 99.0, 100.0, 101.0),\n" |
| " vec3(102.0, 103.0, 104.0),\n" |
| " vec3(105.0, 106.0, 107.0));\n" |
| "const mat3 kMat3_1 = mat3(vec3(108.0, 109.0, 110.0),\n" |
| " vec3(111.0, 112.0, 113.0),\n" |
| " vec3(114.0, 115.0, 116.0));\n" |
| "const mat4 kMat4_0 = mat4(vec4(117.0, 118.0, 119.0, 120.0),\n" |
| " vec4(121.0, 122.0, 123.0, 124.0),\n" |
| " vec4(125.0, 126.0, 127.0, 128.0),\n" |
| " vec4(129.0, 130.0, 131.0, 132.0));\n" |
| "const mat4 kMat4_1 = mat4(vec4(133.0, 134.0, 135.0, 136.0),\n" |
| " vec4(137.0, 138.0, 139.0, 140.0),\n" |
| " vec4(141.0, 142.0, 143.0, 144.0),\n" |
| " vec4(145.0, 146.0, 147.0, 148.0));\n" |
| "\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| "\n" |
| "void main() {\n" |
| " if ((uValM2[0] != kMat2_0) || (uValM2[1] != kMat2_1) ||\n" |
| " (uValM3[0] != kMat3_0) || (uValM3[1] != kMat3_1) ||\n" |
| " (uValM4[0] != kMat4_0) || (uValM4[1] != kMat4_1)) {\n" |
| " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| " } else {\n" |
| " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " }\n" |
| "}\n"; |
| |
| // A fragment shader to test uniforms of square matrices |
| static const char *s_unifFragNonSquareMatShaderSrc = |
| "precision highp float;\n" |
| "uniform mat2x3 uValM2x3[2];\n" |
| "uniform mat3x2 uValM3x2[2];\n" |
| "uniform mat2x4 uValM2x4[2];\n" |
| "uniform mat4x2 uValM4x2[2];\n" |
| "uniform mat3x4 uValM3x4[2];\n" |
| "uniform mat4x3 uValM4x3[2];\n" |
| "\n" |
| "const mat2x3 kMat2x3_0 = mat2x3(vec2(149.0, 150.0),\n" |
| " vec2(151.0, 152.0),\n" |
| " vec2(153.0, 154.0));\n" |
| "const mat2x3 kMat2x3_1 = mat2x3(vec2(155.0, 156.0),\n" |
| " vec2(157.0, 158.0),\n" |
| " vec2(159.0, 160.0));\n" |
| "const mat3x2 kMat3x2_0 = mat3x2(vec3(161.0, 162.0, 163.0),\n" |
| " vec3(164.0, 165.0, 166.0));\n" |
| "const mat3x2 kMat3x2_1 = mat3x2(vec3(167.0, 168.0, 169.0),\n" |
| " vec3(170.0, 171.0, 172.0));\n" |
| "const mat2x4 kMat2x4_0 = mat2x4(vec2(173.0, 174.0),\n" |
| " vec2(175.0, 176.0),\n" |
| " vec2(177.0, 178.0),\n" |
| " vec2(179.0, 180.0));\n" |
| "const mat2x4 kMat2x4_1 = mat2x4(vec2(181.0, 182.0),\n" |
| " vec2(183.0, 184.0),\n" |
| " vec2(185.0, 186.0),\n" |
| " vec2(187.0, 188.0));\n" |
| "const mat4x2 kMat4x2_0 = mat4x2(vec4(189.0, 190.0, 191.0, 192.0),\n" |
| " vec4(193.0, 194.0, 195.0, 196.0));\n" |
| "const mat4x2 kMat4x2_1 = mat4x2(vec4(197.0, 198.0, 199.0, 200.0),\n" |
| " vec4(201.0, 202.0, 203.0, 204.0));\n" |
| "const mat3x4 kMat3x4_0 = mat3x4(vec3(205.0, 206.0, 207.0),\n" |
| " vec3(208.0, 209.0, 210.0),\n" |
| " vec3(211.0, 212.0, 213.0),\n" |
| " vec3(214.0, 215.0, 216.0));\n" |
| "const mat3x4 kMat3x4_1 = mat3x4(vec3(217.0, 218.0, 219.0),\n" |
| " vec3(220.0, 221.0, 222.0),\n" |
| " vec3(223.0, 224.0, 225.0),\n" |
| " vec3(226.0, 227.0, 228.0));\n" |
| "const mat4x3 kMat4x3_0 = mat4x3(vec4(229.0, 230.0, 231.0, 232.0),\n" |
| " vec4(233.0, 234.0, 235.0, 236.0),\n" |
| " vec4(237.0, 238.0, 239.0, 240.0));\n" |
| "const mat4x3 kMat4x3_1 = mat4x3(vec4(241.0, 242.0, 243.0, 244.0),\n" |
| " vec4(245.0, 246.0, 247.0, 248.0),\n" |
| " vec4(249.0, 250.0, 251.0, 252.0));\n" |
| "\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| "\n" |
| "void main() {\n" |
| " if ((uValM2x3[0] != kMat2x3_0) || (uValM2x3[1] != kMat2x3_1) ||\n" |
| " (uValM3x2[0] != kMat3x2_0) || (uValM3x2[1] != kMat3x2_1) ||\n" |
| " (uValM2x4[0] != kMat2x4_0) || (uValM2x4[1] != kMat2x4_1) ||\n" |
| " (uValM4x2[0] != kMat4x2_0) || (uValM4x2[1] != kMat4x2_1) ||\n" |
| " (uValM3x4[0] != kMat3x4_0) || (uValM3x4[1] != kMat3x4_1) ||\n" |
| " (uValM4x3[0] != kMat4x3_0) || (uValM4x3[1] != kMat4x3_1)) {\n" |
| " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| " } else {\n" |
| " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " }\n" |
| "}\n"; |
| |
| static std::string generateBasicVertexSrc(glu::GLSLVersion glslVersion) |
| { |
| std::stringstream str; |
| |
| str << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| str << "in highp vec4 a_position;\n"; |
| if (glslVersion >= glu::GLSL_VERSION_410) |
| { |
| str << "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| str << "void main (void)\n" |
| "{\n" |
| " gl_Position = a_position;\n" |
| "}\n"; |
| |
| return str.str(); |
| } |
| |
| static std::string generateBasicFragmentSrc(glu::GLSLVersion glslVersion) |
| { |
| std::stringstream str; |
| |
| str << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| str << "uniform highp vec4 u_color;\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| "void main (void)\n" |
| "{\n" |
| " o_color = u_color;\n" |
| "}\n"; |
| |
| return str.str(); |
| } |
| |
| // Testcase for glCreateShaderProgramv |
| class CreateShadProgCase : public TestCase |
| { |
| public: |
| CreateShadProgCase(Context &context, const char *name, const char *description, glu::GLSLVersion glslVersion) |
| : TestCase(context, name, description) |
| , m_glslVersion(glslVersion) |
| { |
| } |
| |
| ~CreateShadProgCase(void) |
| { |
| } |
| |
| // Check program validity created with CreateShaderProgram |
| bool checkCSProg(const glw::Functions &gl, GLuint program, int expectedSep = GL_TRUE, int expectedLink = GL_TRUE) |
| { |
| int separable = GL_FALSE; |
| int linked = GL_FALSE; |
| if (program != 0) |
| { |
| gl.getProgramiv(program, GL_PROGRAM_SEPARABLE, &separable); |
| gl.getProgramiv(program, GL_LINK_STATUS, &linked); |
| } |
| |
| return (program != 0) && (separable == expectedSep) && (linked == expectedLink); |
| } |
| |
| IterateResult iterate(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| int i; |
| const char *srcStrings[10]; |
| glw::GLuint program; |
| glw::GLenum err; |
| |
| // CreateShaderProgramv verification |
| log << TestLog::Message << "Begin:CreateShadProgCase iterate" << TestLog::EndMessage; |
| |
| // vertex shader |
| i = 0; |
| srcStrings[i++] = glu::getGLSLVersionDeclaration(m_glslVersion); |
| srcStrings[i++] = "\n"; |
| if (m_glslVersion >= glu::GLSL_VERSION_410) |
| { |
| srcStrings[i++] = "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| srcStrings[i++] = "in vec4 a_position;\n"; |
| srcStrings[i++] = "void main ()\n"; |
| srcStrings[i++] = "{\n"; |
| srcStrings[i++] = " gl_Position = a_position;\n"; |
| srcStrings[i++] = "}\n"; |
| |
| program = gl.createShaderProgramv(GL_VERTEX_SHADER, i, srcStrings); |
| if (!checkCSProg(gl, program)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for vertex shader"); |
| } |
| |
| gl.deleteProgram(program); |
| |
| // Half as many strings |
| i = 0; |
| srcStrings[i++] = glu::getGLSLVersionDeclaration(m_glslVersion); |
| srcStrings[i++] = "\n"; |
| if (m_glslVersion >= glu::GLSL_VERSION_410) |
| { |
| srcStrings[i++] = "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| srcStrings[i++] = "in vec4 a_position;\n" |
| "void main ()\n"; |
| srcStrings[i++] = "{\n" |
| " gl_Position = a_position;\n"; |
| srcStrings[i++] = "}\n"; |
| |
| program = gl.createShaderProgramv(GL_VERTEX_SHADER, i, srcStrings); |
| if (!checkCSProg(gl, program)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for vertex shader"); |
| } |
| |
| gl.deleteProgram(program); |
| |
| // Fragment shader |
| i = 0; |
| srcStrings[i++] = glu::getGLSLVersionDeclaration(m_glslVersion); |
| srcStrings[i++] = "\nin highp vec4 u_color;\n"; |
| srcStrings[i++] = "layout(location = 0) out mediump vec4 o_color;\n"; |
| srcStrings[i++] = "void main ()\n"; |
| srcStrings[i++] = "{\n"; |
| srcStrings[i++] = " o_color = u_color;\n"; |
| srcStrings[i++] = "}\n"; |
| |
| program = gl.createShaderProgramv(GL_FRAGMENT_SHADER, i, srcStrings); |
| if (!checkCSProg(gl, program)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for fragment shader"); |
| } |
| |
| gl.deleteProgram(program); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShaderProgramv failed"); |
| |
| // Negative Cases |
| |
| // invalid type |
| program = gl.createShaderProgramv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, i, srcStrings); |
| err = gl.getError(); |
| if ((program != 0) || (err != GL_INVALID_ENUM)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed"); |
| } |
| |
| // Negative count |
| program = gl.createShaderProgramv(GL_FRAGMENT_SHADER, -1, srcStrings); |
| err = gl.getError(); |
| if ((program != 0) || (err != GL_INVALID_VALUE)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed"); |
| } |
| |
| // source compile error |
| i = 0; |
| srcStrings[i++] = glu::getGLSLVersionDeclaration(m_glslVersion); |
| srcStrings[i++] = "\nin highp vec4 u_color;\n"; |
| srcStrings[i++] = "layout(location = 0) out mediump vec4 o_color;\n"; |
| srcStrings[i++] = "void main ()\n"; |
| srcStrings[i++] = "{\n"; |
| srcStrings[i++] = " o_color = u_color;\n"; |
| |
| program = gl.createShaderProgramv(GL_FRAGMENT_SHADER, i, srcStrings); |
| // expect valid program and false for link status |
| if (!checkCSProg(gl, program, GL_FALSE, GL_FALSE)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for fragment shader"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShaderProgramv failed"); |
| gl.deleteProgram(program); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| private: |
| glu::GLSLVersion m_glslVersion; |
| }; |
| |
| // Testcase for glUseProgamStages |
| class UseProgStagesCase : public TestCase |
| { |
| public: |
| UseProgStagesCase(Context &context, const char *name, const char *description, glu::GLSLVersion glslVersion) |
| : TestCase(context, name, description) |
| , m_glslVersion(glslVersion) |
| { |
| } |
| |
| ~UseProgStagesCase(void) |
| { |
| } |
| |
| IterateResult iterate(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| glw::GLenum err; |
| glw::GLuint pipeline; |
| glw::GLuint progIdV, progIdF; |
| glw::GLuint programVtx, programFrag; |
| const char *shaderSrc[1]; |
| std::string vtx; |
| std::string frag; |
| glw::GLint linkStatus; |
| |
| vtx = generateBasicVertexSrc(m_glslVersion); |
| frag = generateBasicFragmentSrc(m_glslVersion); |
| |
| // UseProgramStages verification |
| log << TestLog::Message << "Begin:UseProgStagesCase iterate" << TestLog::EndMessage; |
| |
| gl.genProgramPipelines(1, &pipeline); |
| gl.bindProgramPipeline(pipeline); |
| |
| // Use Vertex Shader |
| shaderSrc[0] = vtx.c_str(); |
| programVtx = gl.createShaderProgramv(GL_VERTEX_SHADER, 1, shaderSrc); |
| |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programVtx); |
| gl.getProgramPipelineiv(pipeline, GL_VERTEX_SHADER, (glw::GLint *)&progIdV); |
| gl.getProgramPipelineiv(pipeline, GL_FRAGMENT_SHADER, (glw::GLint *)&progIdF); |
| if ((programVtx == 0) || (progIdV != programVtx) || (progIdF != 0)) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failed"); |
| |
| // Use Fragment Shader |
| shaderSrc[0] = frag.c_str(); |
| programFrag = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, shaderSrc); |
| |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programFrag); |
| gl.getProgramPipelineiv(pipeline, GL_FRAGMENT_SHADER, (glw::GLint *)&progIdF); |
| if ((programFrag == 0) || (progIdF != programFrag) || (progIdF == progIdV)) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| // Reset stages |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0); |
| gl.getProgramPipelineiv(pipeline, GL_VERTEX_SHADER, (glw::GLint *)&progIdV); |
| gl.getProgramPipelineiv(pipeline, GL_FRAGMENT_SHADER, (glw::GLint *)&progIdF); |
| if ((progIdV != 0) || (progIdF != 0)) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| // One program for both. |
| glu::ShaderProgram progVF(m_context.getRenderContext(), glu::makeVtxFragSources(vtx.c_str(), frag.c_str())); |
| |
| // Make separable and relink |
| gl.programParameteri(progVF.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE); |
| gl.linkProgram(progVF.getProgram()); |
| gl.getProgramiv(progVF.getProgram(), GL_LINK_STATUS, &linkStatus); |
| if (linkStatus != 1) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failed"); |
| |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, progVF.getProgram()); |
| gl.getProgramPipelineiv(pipeline, GL_VERTEX_SHADER, (glw::GLint *)&progIdV); |
| gl.getProgramPipelineiv(pipeline, GL_FRAGMENT_SHADER, (glw::GLint *)&progIdF); |
| if ((progIdV != progVF.getProgram()) || (progIdV != progIdF)) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failed"); |
| |
| // Use a fragment program with vertex bit |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programFrag); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failed"); |
| |
| // Unbound pipeline |
| gl.bindProgramPipeline(0); |
| gl.deleteProgramPipelines(1, &pipeline); |
| pipeline = 0; |
| gl.genProgramPipelines(1, &pipeline); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programVtx); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programFrag); |
| gl.getProgramPipelineiv(pipeline, GL_VERTEX_SHADER, (glw::GLint *)&progIdV); |
| gl.getProgramPipelineiv(pipeline, GL_FRAGMENT_SHADER, (glw::GLint *)&progIdF); |
| if ((progIdV != programVtx) || (progIdF != programFrag)) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failed"); |
| |
| // Negative Cases |
| |
| // Invalid stages |
| gl.useProgramStages(pipeline, GL_ALL_SHADER_BITS ^ (GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT), programVtx); |
| err = gl.getError(); |
| if (err != GL_INVALID_VALUE) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| // Program that is not separable |
| gl.programParameteri(progVF.getProgram(), GL_PROGRAM_SEPARABLE, GL_FALSE); |
| gl.linkProgram(progVF.getProgram()); |
| gl.getProgramiv(progVF.getProgram(), GL_LINK_STATUS, &linkStatus); |
| if (linkStatus != 1) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failed"); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, progVF.getProgram()); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| // Program that is not successfully linked |
| // remove the main keyword |
| std::string fragNoMain = frag; |
| unsigned int pos = (unsigned int)fragNoMain.find("main"); |
| fragNoMain.replace(pos, 4, "niaM"); |
| glu::ShaderProgram progNoLink(m_context.getRenderContext(), |
| glu::makeVtxFragSources(vtx.c_str(), fragNoMain.c_str())); |
| |
| gl.programParameteri(progNoLink.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE); |
| gl.linkProgram(progNoLink.getProgram()); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, progNoLink.getProgram()); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| // Invalid pipeline |
| gl.useProgramStages(pipeline + 1000, GL_VERTEX_SHADER_BIT, programVtx); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| // Invalid pipeline |
| gl.deleteProgramPipelines(1, &pipeline); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programVtx); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("UseProgramStages failed"); |
| } |
| |
| gl.deleteProgram(programVtx); |
| gl.deleteProgram(programFrag); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| private: |
| glu::GLSLVersion m_glslVersion; |
| }; |
| |
| // Testcase for pipeline api |
| class PipelineApiCase : public TestCase |
| { |
| public: |
| PipelineApiCase(Context &context, const char *name, const char *description, glu::GLSLVersion glslVersion) |
| : TestCase(context, name, description) |
| , m_glslVersion(glslVersion) |
| { |
| } |
| |
| ~PipelineApiCase(void) |
| { |
| } |
| |
| // Validate glGetProgramPipelineInfoLog |
| void checkProgInfoLog(const glw::Functions &gl, GLuint pipeline) |
| { |
| glw::GLint value; |
| glw::GLsizei bufSize; |
| glw::GLsizei length; |
| glw::GLenum err; |
| |
| gl.getProgramPipelineiv(pipeline, GL_INFO_LOG_LENGTH, &value); |
| std::vector<char> infoLogBuf(value + 1); |
| |
| bufSize = 0; |
| gl.getProgramPipelineInfoLog(pipeline, bufSize, &length, &infoLogBuf[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramPipelineInfoLog failed"); |
| |
| bufSize = value / 2; // read half the log |
| gl.getProgramPipelineInfoLog(pipeline, bufSize, &length, &infoLogBuf[0]); |
| if ((bufSize != 0) && (bufSize != length + 1)) |
| { |
| TCU_FAIL("GetProgramPipelineInfoLog failed"); |
| } |
| bufSize = value; |
| gl.getProgramPipelineInfoLog(pipeline, bufSize, &length, &infoLogBuf[0]); |
| if ((bufSize != 0) && (bufSize != length + 1)) |
| { |
| TCU_FAIL("GetProgramPipelineInfoLog failed"); |
| } |
| |
| // Negative case for GetProgramPipelineInfoLog |
| |
| gl.getProgramPipelineInfoLog(pipeline + 101, bufSize, &length, &infoLogBuf[0]); |
| err = gl.getError(); |
| if (err != GL_INVALID_VALUE) |
| { |
| TCU_FAIL("GetProgramPipelineInfoLog failed"); |
| } |
| } |
| |
| IterateResult iterate(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| glw::GLenum err; |
| const int maxpipelines = 10; |
| glw::GLuint pipelines[maxpipelines]; |
| std::string vtx; |
| std::string frag; |
| glw::GLint linkStatus; |
| glw::GLuint value; |
| |
| vtx = generateBasicVertexSrc(m_glslVersion); |
| frag = generateBasicFragmentSrc(m_glslVersion); |
| |
| // Pipeline API verification |
| log << TestLog::Message << "Begin:PipelineApiCase iterate" << TestLog::EndMessage; |
| |
| glu::ShaderProgram progVF(m_context.getRenderContext(), glu::makeVtxFragSources(vtx.c_str(), frag.c_str())); |
| |
| // Make separable and relink |
| gl.programParameteri(progVF.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE); |
| gl.linkProgram(progVF.getProgram()); |
| gl.getProgramiv(progVF.getProgram(), GL_LINK_STATUS, &linkStatus); |
| if (linkStatus != 1) |
| { |
| TCU_FAIL("LinkProgram failed"); |
| } |
| |
| gl.genProgramPipelines(1, pipelines); |
| |
| // ActiveShaderProgram |
| gl.activeShaderProgram(pipelines[0], progVF.getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveShaderProgram failed"); |
| |
| // Negative cases for ActiveShaderProgram |
| |
| // Nonexistent program |
| gl.activeShaderProgram(pipelines[0], progVF.getProgram() + 100); |
| err = gl.getError(); |
| if (err != GL_INVALID_VALUE) |
| { |
| TCU_FAIL("ActiveShaderProgram failed"); |
| } |
| gl.getProgramPipelineiv(pipelines[0], GL_ACTIVE_PROGRAM, (glw::GLint *)&value); |
| if (value != progVF.getProgram()) |
| { |
| TCU_FAIL("ActiveShaderProgram failed"); |
| } |
| |
| // Deleted pipeline |
| gl.deleteProgramPipelines(1, pipelines); |
| gl.activeShaderProgram(pipelines[0], progVF.getProgram()); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("ActiveShaderProgram failed"); |
| } |
| |
| // GenProgramPipeline |
| |
| gl.genProgramPipelines(2, &pipelines[0]); |
| gl.genProgramPipelines(3, &pipelines[2]); |
| gl.genProgramPipelines(5, &pipelines[5]); |
| |
| for (int i = 0; i < maxpipelines; i++) |
| { |
| gl.bindProgramPipeline(pipelines[i]); // has to be bound to be recognized |
| if (!gl.isProgramPipeline(pipelines[i])) |
| { |
| TCU_FAIL("GenProgramPipelines failed"); |
| } |
| } |
| gl.deleteProgramPipelines(maxpipelines, pipelines); |
| |
| // BindProgramPipeline |
| |
| gl.genProgramPipelines(2, pipelines); |
| gl.bindProgramPipeline(pipelines[0]); |
| gl.getIntegerv(GL_PROGRAM_PIPELINE_BINDING, (glw::GLint *)&value); |
| if (value != pipelines[0]) |
| { |
| TCU_FAIL("BindProgramPipeline failed"); |
| } |
| gl.bindProgramPipeline(pipelines[1]); |
| gl.getIntegerv(GL_PROGRAM_PIPELINE_BINDING, (glw::GLint *)&value); |
| if (value != pipelines[1]) |
| { |
| TCU_FAIL("BindProgramPipeline failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindProgramPipeline failed"); |
| |
| // Negative Case for BindProgramPipeline |
| |
| gl.bindProgramPipeline(pipelines[2]); // deleted pipeline |
| gl.getIntegerv(GL_PROGRAM_PIPELINE_BINDING, (glw::GLint *)&value); |
| err = gl.getError(); |
| if ((err != GL_INVALID_OPERATION) || (value != pipelines[1])) |
| { |
| TCU_FAIL("BindProgramPipeline failed"); |
| } |
| |
| // DeleteProgramPipelines |
| |
| gl.genProgramPipelines(8, &pipelines[2]); // back to 10 total |
| gl.deleteProgramPipelines(2, &pipelines[8]); |
| gl.deleteProgramPipelines(3, &pipelines[5]); |
| pipelines[9] = 0; |
| gl.deleteProgramPipelines(maxpipelines, pipelines); // 5 good, 4 deleted, 1 zero |
| gl.deleteProgramPipelines(0, pipelines); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteProgramPipelines failed"); |
| for (int i = 0; i < maxpipelines; i++) |
| { |
| if (gl.isProgramPipeline(pipelines[i])) |
| { |
| TCU_FAIL("DeleteProgramPipelines failed"); |
| } |
| } |
| gl.getIntegerv(GL_PROGRAM_PIPELINE_BINDING, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("DeleteProgramPipelines failed"); |
| } |
| |
| // IsProgramPipeline |
| |
| pipelines[1] = 0x1000; |
| pipelines[2] += 100; |
| for (int i = 0; i < 3; i++) |
| { |
| // 1 deleted and 2 bogus values |
| if (gl.isProgramPipeline(pipelines[i])) |
| { |
| TCU_FAIL("IsProgramPipeline failed"); |
| } |
| } |
| gl.genProgramPipelines(1, pipelines); |
| if (gl.isProgramPipeline(pipelines[0])) |
| { |
| TCU_FAIL("IsProgramPipeline failed"); |
| } |
| gl.deleteProgramPipelines(1, pipelines); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "IsProgramPipeline failed"); |
| |
| // ProgramParameteri PROGRAM_SEPARABLE |
| // NOTE: The query for PROGRAM_SEPARABLE must query latched |
| // state. In other words, the state of the binary after |
| // it was linked. So in the tests below, the queries |
| // should return the default state GL_FALSE since the |
| // program has no linked binary. |
| |
| glw::GLuint programSep = gl.createProgram(); |
| int separable; |
| gl.programParameteri(programSep, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| gl.getProgramiv(programSep, GL_PROGRAM_SEPARABLE, &separable); |
| if (separable != GL_FALSE) |
| { |
| TCU_FAIL("programParameteri PROGRAM_SEPARABLE failed"); |
| } |
| gl.programParameteri(programSep, GL_PROGRAM_SEPARABLE, GL_FALSE); |
| gl.getProgramiv(programSep, GL_PROGRAM_SEPARABLE, &separable); |
| if (separable != 0) |
| { |
| TCU_FAIL("programParameteri PROGRAM_SEPARABLE failed"); |
| } |
| |
| // Negative Case for ProgramParameteri PROGRAM_SEPARABLE |
| |
| gl.deleteProgram(programSep); |
| gl.programParameteri(programSep, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| err = gl.getError(); |
| if (err != GL_INVALID_VALUE) |
| { |
| TCU_FAIL("programParameteri PROGRAM_SEPARABLE failed"); |
| } |
| gl.programParameteri(progVF.getProgram(), GL_PROGRAM_SEPARABLE, 501); |
| err = gl.getError(); |
| if (err != GL_INVALID_VALUE) |
| { |
| TCU_FAIL("programParameteri PROGRAM_SEPARABLE failed"); |
| } |
| |
| // GetProgramPipelineiv |
| |
| gl.genProgramPipelines(1, pipelines); |
| gl.getProgramPipelineiv(pipelines[0], GL_ACTIVE_PROGRAM, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("GetProgramPipelineiv failed for ACTIVE_PROGRAM"); |
| } |
| gl.getProgramPipelineiv(pipelines[0], GL_VERTEX_SHADER, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("GetProgramPipelineiv failed for VERTEX_SHADER"); |
| } |
| gl.getProgramPipelineiv(pipelines[0], GL_FRAGMENT_SHADER, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("GetProgramPipelineiv failed for FRAGMENT_SHADER"); |
| } |
| gl.getProgramPipelineiv(pipelines[0], GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("GetProgramPipelineiv failed for VALIDATE_STATUS"); |
| } |
| gl.getProgramPipelineiv(pipelines[0], GL_INFO_LOG_LENGTH, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("GetProgramPipelineiv failed for INFO_LOG_LENGTH"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramPipelineiv failed"); |
| |
| // Negative Case for GetProgramPipelineiv |
| |
| gl.deleteProgramPipelines(1, pipelines); |
| gl.getProgramPipelineiv(pipelines[0], GL_ACTIVE_PROGRAM, (glw::GLint *)&value); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("GetProgramPipelineiv failed for ACTIVE_PROGRAM"); |
| } |
| |
| // ValidateProgramPipeline |
| |
| gl.genProgramPipelines(1, pipelines); // Unvalidated |
| gl.getProgramPipelineiv(pipelines[0], GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("ValidateProgramPipeline failed"); |
| } |
| |
| gl.validateProgramPipeline(pipelines[0]); // Not bound yet |
| gl.getProgramPipelineiv(pipelines[0], GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("ValidateProgramPipeline failed"); |
| } |
| |
| gl.bindProgramPipeline(pipelines[0]); |
| |
| gl.validateProgramPipeline(pipelines[0]); // Still empty program pipeline. |
| gl.getProgramPipelineiv(pipelines[0], GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("ValidateProgramPipeline failed with empty program pipeline"); |
| } |
| |
| gl.useProgramStages(pipelines[0], GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, progVF.getProgram()); |
| gl.validateProgramPipeline(pipelines[0]); |
| gl.getProgramPipelineiv(pipelines[0], GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 1) |
| { |
| TCU_FAIL("ValidateProgramPipeline failed"); |
| } |
| |
| // GetProgramPipelineInfoLog |
| checkProgInfoLog(gl, pipelines[0]); |
| |
| // ValidateProgramPipeline additional |
| // Relink the bound separable program as not separable |
| gl.programParameteri(progVF.getProgram(), GL_PROGRAM_SEPARABLE, GL_FALSE); |
| gl.linkProgram(progVF.getProgram()); |
| err = gl.getError(); |
| gl.validateProgramPipeline(pipelines[0]); |
| gl.getProgramPipelineiv(pipelines[0], GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("ValidateProgramPipeline failed"); |
| } |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ValidateProgramPipeline failed"); |
| |
| // GetProgramPipelineInfoLog |
| checkProgInfoLog(gl, pipelines[0]); |
| |
| // Negative Case for ValidateProgramPipeline |
| |
| gl.deleteProgramPipelines(1, pipelines); |
| gl.validateProgramPipeline(pipelines[0]); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("ValidateProgramPipeline failed"); |
| } |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| private: |
| glu::GLSLVersion m_glslVersion; |
| }; |
| |
| // Testcase for glProgramUniform |
| class ProgramUniformCase : public TestCase |
| { |
| public: |
| ProgramUniformCase(Context &context, const char *name, const char *description, glu::GLSLVersion glslVersion) |
| : TestCase(context, name, description) |
| , m_glslVersion(glslVersion) |
| { |
| } |
| |
| ~ProgramUniformCase(void) |
| { |
| } |
| |
| bool isDataTypeSquareMatrix(glu::DataType dtyp) |
| { |
| return (dtyp == glu::TYPE_FLOAT_MAT2) || (dtyp == glu::TYPE_FLOAT_MAT3) || (dtyp == glu::TYPE_FLOAT_MAT4); |
| } |
| |
| // outFragSrc will hold a fragment program that is DataType specific |
| void generateUniformFragSrc(std::string &outFragSrc, glu::GLSLVersion glslVersion, glu::DataType dType) |
| { |
| std::ostringstream fragSrc; |
| |
| fragSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| if (isDataTypeMatrix(dType) && isDataTypeSquareMatrix(dType)) |
| { |
| fragSrc << s_unifFragSquareMatShaderSrc; |
| } |
| else if (isDataTypeMatrix(dType) && !isDataTypeSquareMatrix(dType)) |
| { |
| fragSrc << s_unifFragNonSquareMatShaderSrc; |
| } |
| else |
| { |
| fragSrc << s_unifFragShaderSrc; |
| } |
| |
| std::map<std::string, std::string> params; |
| |
| if (dType == glu::TYPE_INT) |
| { |
| params.insert(std::pair<std::string, std::string>("SCALAR_TYPE", "int")); |
| params.insert(std::pair<std::string, std::string>("VECTOR_TYPE", "ivec")); |
| params.insert(std::pair<std::string, std::string>("SFX", "")); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| params.insert(std::pair<std::string, std::string>("SCALAR_TYPE", "uint")); |
| params.insert(std::pair<std::string, std::string>("VECTOR_TYPE", "uvec")); |
| params.insert(std::pair<std::string, std::string>("SFX", "u")); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| params.insert(std::pair<std::string, std::string>("SCALAR_TYPE", "float")); |
| params.insert(std::pair<std::string, std::string>("VECTOR_TYPE", "vec")); |
| params.insert(std::pair<std::string, std::string>("SFX", ".0")); |
| } |
| |
| tcu::StringTemplate fragTmpl(fragSrc.str().c_str()); |
| outFragSrc = fragTmpl.specialize(params); |
| } |
| |
| // Set the integer programUniforms |
| void progUniformi(const glw::Functions &gl, glw::GLuint prog, int arraySize, int *location, int *value) |
| { |
| gl.programUniform1i(prog, location[0], value[0]); |
| value += 1; |
| gl.programUniform2i(prog, location[1], value[0], value[1]); |
| value += 2; |
| gl.programUniform3i(prog, location[2], value[0], value[1], value[2]); |
| value += 3; |
| gl.programUniform4i(prog, location[3], value[0], value[1], value[2], value[3]); |
| value += 4; |
| |
| gl.programUniform1iv(prog, location[4], arraySize, value); |
| value += 1 * arraySize; |
| gl.programUniform2iv(prog, location[6], arraySize, value); |
| value += 2 * arraySize; |
| gl.programUniform3iv(prog, location[8], arraySize, value); |
| value += 3 * arraySize; |
| gl.programUniform4iv(prog, location[10], arraySize, value); |
| } |
| |
| // Set the unsigned integer programUniforms |
| void progUniformui(const glw::Functions &gl, glw::GLuint prog, int arraySize, int *location, unsigned int *value) |
| { |
| gl.programUniform1ui(prog, location[0], value[0]); |
| value += 1; |
| gl.programUniform2ui(prog, location[1], value[0], value[1]); |
| value += 2; |
| gl.programUniform3ui(prog, location[2], value[0], value[1], value[2]); |
| value += 3; |
| gl.programUniform4ui(prog, location[3], value[0], value[1], value[2], value[3]); |
| value += 4; |
| |
| gl.programUniform1uiv(prog, location[4], arraySize, value); |
| value += 1 * arraySize; |
| gl.programUniform2uiv(prog, location[6], arraySize, value); |
| value += 2 * arraySize; |
| gl.programUniform3uiv(prog, location[8], arraySize, value); |
| value += 3 * arraySize; |
| gl.programUniform4uiv(prog, location[10], arraySize, value); |
| } |
| |
| // Set the float programUniforms |
| void progUniformf(const glw::Functions &gl, glw::GLuint prog, int arraySize, int *location, float *value) |
| { |
| gl.programUniform1f(prog, location[0], value[0]); |
| value += 1; |
| gl.programUniform2f(prog, location[1], value[0], value[1]); |
| value += 2; |
| gl.programUniform3f(prog, location[2], value[0], value[1], value[2]); |
| value += 3; |
| gl.programUniform4f(prog, location[3], value[0], value[1], value[2], value[3]); |
| value += 4; |
| |
| gl.programUniform1fv(prog, location[4], arraySize, value); |
| value += 1 * arraySize; |
| gl.programUniform2fv(prog, location[6], arraySize, value); |
| value += 2 * arraySize; |
| gl.programUniform3fv(prog, location[8], arraySize, value); |
| value += 3 * arraySize; |
| gl.programUniform4fv(prog, location[10], arraySize, value); |
| } |
| |
| // Set the integer uniforms with conventional glUniformi |
| void activeUniformi(const glw::Functions &gl, int arraySize, int *location, int *value) |
| { |
| gl.uniform1i(location[0], value[0]); |
| value += 1; |
| gl.uniform2i(location[1], value[0], value[1]); |
| value += 2; |
| gl.uniform3i(location[2], value[0], value[1], value[2]); |
| value += 3; |
| gl.uniform4i(location[3], value[0], value[1], value[2], value[3]); |
| value += 4; |
| |
| gl.uniform1iv(location[4], arraySize, value); |
| value += 1 * arraySize; |
| gl.uniform2iv(location[6], arraySize, value); |
| value += 2 * arraySize; |
| gl.uniform3iv(location[8], arraySize, value); |
| value += 3 * arraySize; |
| gl.uniform4iv(location[10], arraySize, value); |
| } |
| |
| // Set the unsigned integer uniforms with conventional glUniformui |
| void activeUniformui(const glw::Functions &gl, int arraySize, int *location, unsigned int *value) |
| { |
| gl.uniform1ui(location[0], value[0]); |
| value += 1; |
| gl.uniform2ui(location[1], value[0], value[1]); |
| value += 2; |
| gl.uniform3ui(location[2], value[0], value[1], value[2]); |
| value += 3; |
| gl.uniform4ui(location[3], value[0], value[1], value[2], value[3]); |
| value += 4; |
| |
| gl.uniform1uiv(location[4], arraySize, value); |
| value += 1 * arraySize; |
| gl.uniform2uiv(location[6], arraySize, value); |
| value += 2 * arraySize; |
| gl.uniform3uiv(location[8], arraySize, value); |
| value += 3 * arraySize; |
| gl.uniform4uiv(location[10], arraySize, value); |
| } |
| |
| // Set the float uniforms with conventional glUniformui |
| void activeUniformf(const glw::Functions &gl, int arraySize, int *location, float *value) |
| { |
| gl.uniform1f(location[0], value[0]); |
| value += 1; |
| gl.uniform2f(location[1], value[0], value[1]); |
| value += 2; |
| gl.uniform3f(location[2], value[0], value[1], value[2]); |
| value += 3; |
| gl.uniform4f(location[3], value[0], value[1], value[2], value[3]); |
| value += 4; |
| |
| gl.uniform1fv(location[4], arraySize, value); |
| value += 1 * arraySize; |
| gl.uniform2fv(location[6], arraySize, value); |
| value += 2 * arraySize; |
| gl.uniform3fv(location[8], arraySize, value); |
| value += 3 * arraySize; |
| gl.uniform4fv(location[10], arraySize, value); |
| } |
| |
| // Call programUniform and verify for non-Matrix uniforms |
| // Two programs are verified independently and against each other |
| bool setAndCompareUniforms(glw::GLuint pipeline, glw::GLuint programA, glw::GLuint programB, glu::DataType dType, |
| int seed) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| // The fragment shader has defined uniforms of type: |
| // scalar, vec2, vec3, vec4, and then length 2 arrays of |
| // scalar, vec2, vec3, and vec4. |
| // 4 uniforms in array form and 4 not in arrays. |
| // We query a total of 12 uniform locations |
| const int nonarrayUnifCount = 4; |
| const int arrayUnifCount = 4; |
| const int arraySize = 2; |
| const int locationCount = nonarrayUnifCount + arraySize * arrayUnifCount; |
| // dwordCount represents the number of dwords to compare for each uniform location |
| // scalar, vec2, vec3, vec4, scalar[0], scalar[1], vec2[0], vec2[1], etc. |
| const int dwordCount[locationCount] = {1, 2, 3, 4, 1, 1, 2, 2, 3, 3, 4, 4}; |
| glw::GLint locationA[locationCount]; |
| glw::GLint locationB[locationCount]; |
| // The total amount of data the uniforms take up: 1+2+3+4 + 2*(1+2+3+4) |
| const int udataCount = 30; |
| unsigned int udata[udataCount]; // |
| int *data = (int *)&udata[0]; |
| float *fdata = (float *)&udata[0]; |
| int i, j, k; |
| std::string uniformBaseName("uVal"); |
| |
| // ProgramUniform API verification |
| log << TestLog::Message << "Begin:ProgramUniformCase iterate" << TestLog::EndMessage; |
| |
| // get uniform locations |
| // scalar and vec uniforms |
| for (i = 0; i < nonarrayUnifCount; i++) |
| { |
| string name = uniformBaseName + de::toString(i); |
| locationA[i] = gl.getUniformLocation(programA, name.c_str()); |
| locationB[i] = gl.getUniformLocation(programB, name.c_str()); |
| } |
| // uniform arrays |
| for (j = 0; j < arrayUnifCount; j++) |
| { |
| for (k = 0; k < arraySize; k++) |
| { |
| string name = uniformBaseName + de::toString(nonarrayUnifCount + j) + "[" + de::toString(k) + "]"; |
| locationA[i] = gl.getUniformLocation(programA, name.c_str()); |
| locationB[i] = gl.getUniformLocation(programB, name.c_str()); |
| i++; |
| } |
| } |
| |
| // seed data buffer with unique values |
| if (dType == glu::TYPE_FLOAT) |
| { |
| for (i = 0; i < udataCount; i++) |
| { |
| fdata[i] = (float)(seed + i); |
| } |
| } |
| else |
| { |
| for (i = 0; i < udataCount; i++) |
| { |
| data[i] = seed + i; |
| } |
| } |
| |
| // set uniforms in program A |
| if (dType == glu::TYPE_INT) |
| { |
| progUniformi(gl, programA, arraySize, locationA, data); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| progUniformui(gl, programA, arraySize, locationA, udata); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| progUniformf(gl, programA, arraySize, locationA, fdata); |
| } |
| |
| // get and compare uniforms |
| unsigned int *uValue = &udata[0]; |
| for (i = 0; i < nonarrayUnifCount + arraySize * arrayUnifCount; i++) |
| { |
| unsigned int retValA[4], retValB[4]; |
| |
| if (dType == glu::TYPE_INT) |
| { |
| gl.getUniformiv(programA, locationA[i], (int *)&retValA[0]); |
| gl.getUniformiv(programB, locationB[i], (int *)&retValB[0]); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| gl.getUniformuiv(programA, locationA[i], &retValA[0]); |
| gl.getUniformuiv(programB, locationB[i], &retValB[0]); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| gl.getUniformfv(programA, locationA[i], (float *)&retValA[0]); |
| gl.getUniformfv(programB, locationB[i], (float *)&retValB[0]); |
| } |
| |
| for (j = 0; j < dwordCount[i]; j++) |
| { |
| // Compare programA uniform to expected value and |
| // test to see if programB picked up the value. |
| if ((retValA[j] != *uValue++) || (retValA[j] == retValB[j])) |
| { |
| TCU_FAIL("ProgramUniformi failed"); |
| } |
| } |
| } |
| |
| // reseed data buffer, continuing to increment |
| if (dType == glu::TYPE_FLOAT) |
| { |
| fdata[0] = fdata[udataCount - 1] + 1.0f; |
| for (i = 1; i < udataCount; i++) |
| { |
| fdata[i] = fdata[i - 1] + 1.0f; |
| } |
| } |
| else |
| { |
| data[0] = data[udataCount - 1] + 1; |
| for (i = 1; i < udataCount; i++) |
| { |
| data[i] = data[i - 1] + 1; |
| } |
| } |
| |
| // set uniforms in program B |
| |
| if (dType == glu::TYPE_INT) |
| { |
| progUniformi(gl, programB, arraySize, locationB, data); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| progUniformui(gl, programB, arraySize, locationB, udata); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| progUniformf(gl, programB, arraySize, locationB, fdata); |
| } |
| |
| // get and compare uniforms |
| uValue = &udata[0]; |
| for (i = 0; i < nonarrayUnifCount + arraySize * arrayUnifCount; i++) |
| { |
| unsigned int retValA[4], retValB[4]; |
| |
| if (dType == glu::TYPE_INT) |
| { |
| gl.getUniformiv(programA, locationA[i], (int *)&retValA[0]); |
| gl.getUniformiv(programB, locationB[i], (int *)&retValB[0]); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| gl.getUniformuiv(programA, locationA[i], &retValA[0]); |
| gl.getUniformuiv(programB, locationB[i], &retValB[0]); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| gl.getUniformfv(programA, locationA[i], (float *)&retValA[0]); |
| gl.getUniformfv(programB, locationB[i], (float *)&retValB[0]); |
| } |
| |
| for (j = 0; j < dwordCount[i]; j++) |
| { |
| // Compare programB uniform to expected value and |
| // test to see if programA picked up the value. |
| if ((retValB[j] != *uValue++) || (retValA[j] == retValB[j])) |
| { |
| TCU_FAIL("ProgramUniformi failed"); |
| } |
| } |
| } |
| |
| // Test the conventional uniform interfaces on an ACTIVE_PROGRAM |
| glw::GLuint activeProgram = 0; |
| if (pipeline != 0) |
| { |
| gl.getProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, (int *)&activeProgram); |
| } |
| if ((activeProgram != 0) && ((activeProgram == programA) || (activeProgram == programB))) |
| { |
| glw::GLint *location; |
| |
| location = (activeProgram == programA) ? locationA : locationB; |
| |
| // reseed data buffer, continuing to increment |
| if (dType == glu::TYPE_FLOAT) |
| { |
| fdata[0] = fdata[udataCount - 1] + 1.0f; |
| for (i = 1; i < udataCount; i++) |
| { |
| fdata[i] = fdata[i - 1] + 1.0f; |
| } |
| } |
| else |
| { |
| data[0] = data[udataCount - 1] + 1; |
| for (i = 1; i < udataCount; i++) |
| { |
| data[i] = data[i - 1] + 1; |
| } |
| } |
| |
| // set uniforms using original glUniform* |
| |
| if (dType == glu::TYPE_INT) |
| { |
| activeUniformi(gl, arraySize, location, data); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| activeUniformui(gl, arraySize, location, udata); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| activeUniformf(gl, arraySize, location, fdata); |
| } |
| |
| // get and compare uniforms |
| uValue = &udata[0]; |
| for (i = 0; i < nonarrayUnifCount + arraySize * arrayUnifCount; i++) |
| { |
| unsigned int retVal[4]; |
| |
| if (dType == glu::TYPE_INT) |
| { |
| gl.getUniformiv(activeProgram, location[i], (int *)&retVal[0]); |
| } |
| else if (dType == glu::TYPE_UINT) |
| { |
| gl.getUniformuiv(activeProgram, location[i], &retVal[0]); |
| } |
| else if (dType == glu::TYPE_FLOAT) |
| { |
| gl.getUniformfv(activeProgram, location[i], (float *)&retVal[0]); |
| } |
| |
| for (j = 0; j < dwordCount[i]; j++) |
| { |
| // Compare activeProgram uniform to expected value |
| if ((retVal[j] != *uValue++)) |
| { |
| TCU_FAIL("ActiveShaderProgram failed"); |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // Call programUniform for Matrix uniforms |
| // Two programs are verified independently and against each other |
| bool setAndCompareMatrixUniforms(glw::GLuint pipeline, glw::GLuint programA, glw::GLuint programB, |
| glu::DataType dType, int seed) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| bool isSquareMat = isDataTypeSquareMatrix(dType); |
| // The matrix versions of the fragment shader have two element arrays |
| // of each uniform. |
| // There are 3 * 2 uniforms for the square matrix shader and |
| // 6 * 2 uniforms in the non-square matrix shader. |
| const int maxUniforms = 12; |
| int numUniforms; |
| const int arraySize = 2; |
| glw::GLint locationA[maxUniforms]; |
| glw::GLint locationB[maxUniforms]; |
| // These arrays represent the number of floats for each uniform location |
| // 2x2[0], 2x2[1], 3x3[0], 3x3[1], 4x4[0], 4x4[1] |
| const int floatCountSqu[maxUniforms] = {4, 4, 9, 9, 16, 16, 0, 0, 0, 0, 0, 0}; |
| // 2x3[0], 2x3[1], 2x4[0], 2x4[1], 3x2[0], 3x2[1], 3x4[0], 3x4[1], 4x2[0]... |
| const int floatCountNonSqu[maxUniforms] = {6, 6, 8, 8, 6, 6, 12, 12, 8, 8, 12, 12}; |
| const int *floatCount; |
| // Max data for the uniforms = 2*(2*3 + 3*2 + 2*4 + 4*2 + 3*4 + 4*3) |
| const int maxDataCount = 104; |
| float data[maxDataCount]; |
| int i, j, k; |
| std::string uniformBaseName("uValM"); |
| |
| // ProgramUniform API verification |
| log << TestLog::Message << "Begin:ProgramUniformCase for Matrix iterate" << TestLog::EndMessage; |
| |
| numUniforms = 0; |
| // get uniform locations |
| for (i = 2; i <= 4; i++) // matrix dimension m |
| { |
| for (j = 2; j <= 4; j++) // matrix dimension n |
| { |
| for (k = 0; k < arraySize; k++) |
| { |
| if ((i == j) && isSquareMat) |
| { |
| string name = uniformBaseName + de::toString(i) + "[" + de::toString(k) + "]"; |
| locationA[numUniforms] = gl.getUniformLocation(programA, name.c_str()); |
| locationB[numUniforms] = gl.getUniformLocation(programB, name.c_str()); |
| numUniforms++; |
| } |
| else if ((i != j) && !isSquareMat) |
| { |
| string name = |
| uniformBaseName + de::toString(i) + "x" + de::toString(j) + "[" + de::toString(k) + "]"; |
| locationA[numUniforms] = gl.getUniformLocation(programA, name.c_str()); |
| locationB[numUniforms] = gl.getUniformLocation(programB, name.c_str()); |
| numUniforms++; |
| } |
| } |
| } |
| } |
| DE_ASSERT((numUniforms == 6) || (numUniforms == 12)); |
| |
| // init the float data array |
| for (i = 0; i < maxDataCount; i++) |
| { |
| data[i] = (float)(seed + i); |
| } |
| |
| // Set the uniforms in programA |
| float *value = &data[0]; |
| if (isSquareMat) |
| { |
| floatCount = floatCountSqu; |
| gl.programUniformMatrix2fv(programA, locationA[0], arraySize, GL_FALSE, value); |
| value += 2 * 2 * arraySize; |
| gl.programUniformMatrix3fv(programA, locationA[2], arraySize, GL_FALSE, value); |
| value += 3 * 3 * arraySize; |
| gl.programUniformMatrix4fv(programA, locationA[4], arraySize, GL_FALSE, value); |
| } |
| else |
| { |
| floatCount = floatCountNonSqu; |
| gl.programUniformMatrix2x3fv(programA, locationA[0], arraySize, GL_FALSE, value); |
| value += 2 * 3 * arraySize; |
| gl.programUniformMatrix2x4fv(programA, locationA[2], arraySize, GL_FALSE, value); |
| value += 2 * 4 * arraySize; |
| gl.programUniformMatrix3x2fv(programA, locationA[4], arraySize, GL_FALSE, value); |
| value += 3 * 2 * arraySize; |
| gl.programUniformMatrix3x4fv(programA, locationA[6], arraySize, GL_FALSE, value); |
| value += 3 * 4 * arraySize; |
| gl.programUniformMatrix4x2fv(programA, locationA[8], arraySize, GL_FALSE, value); |
| value += 4 * 2 * arraySize; |
| gl.programUniformMatrix4x3fv(programA, locationA[10], arraySize, GL_FALSE, value); |
| } |
| |
| // get and compare the uniform data |
| value = &data[0]; |
| for (i = 0; i < numUniforms; i++) |
| { |
| float retValA[16], retValB[16]; |
| |
| gl.getUniformfv(programA, locationA[i], retValA); |
| gl.getUniformfv(programB, locationB[i], retValB); |
| |
| for (j = 0; j < floatCount[i]; j++) |
| { |
| // Compare programA uniform to expected value and |
| // test to see if programB picked up the value. |
| if ((retValA[j] != *value++) || (retValA[j] == retValB[j])) |
| { |
| TCU_FAIL("ProgramUniformi failed"); |
| } |
| } |
| } |
| |
| // reseed the float buffer |
| data[0] = data[maxDataCount - 1]; |
| for (i = 1; i < maxDataCount; i++) |
| { |
| data[i] = data[i - 1] + 1.0f; |
| } |
| |
| // set uniforms in program B |
| value = &data[0]; |
| if (isSquareMat) |
| { |
| floatCount = floatCountSqu; |
| gl.programUniformMatrix2fv(programB, locationB[0], arraySize, GL_FALSE, value); |
| value += 2 * 2 * arraySize; |
| gl.programUniformMatrix3fv(programB, locationB[2], arraySize, GL_FALSE, value); |
| value += 3 * 3 * arraySize; |
| gl.programUniformMatrix4fv(programB, locationB[4], arraySize, GL_FALSE, value); |
| } |
| else |
| { |
| floatCount = floatCountNonSqu; |
| gl.programUniformMatrix2x3fv(programB, locationB[0], arraySize, GL_FALSE, value); |
| value += 2 * 3 * arraySize; |
| gl.programUniformMatrix2x4fv(programB, locationB[2], arraySize, GL_FALSE, value); |
| value += 2 * 4 * arraySize; |
| gl.programUniformMatrix3x2fv(programB, locationB[4], arraySize, GL_FALSE, value); |
| value += 3 * 2 * arraySize; |
| gl.programUniformMatrix3x4fv(programB, locationB[6], arraySize, GL_FALSE, value); |
| value += 3 * 4 * arraySize; |
| gl.programUniformMatrix4x2fv(programB, locationB[8], arraySize, GL_FALSE, value); |
| value += 4 * 2 * arraySize; |
| gl.programUniformMatrix4x3fv(programB, locationB[10], arraySize, GL_FALSE, value); |
| } |
| |
| // get and compare the uniform data |
| value = &data[0]; |
| for (i = 0; i < numUniforms; i++) |
| { |
| float retValA[16], retValB[16]; |
| |
| gl.getUniformfv(programA, locationA[i], retValA); |
| gl.getUniformfv(programB, locationB[i], retValB); |
| |
| for (j = 0; j < floatCount[i]; j++) |
| { |
| // Compare programB uniform to expected value and |
| // test to see if programA picked up the value. |
| if ((retValB[j] != *value++) || (retValA[j] == retValB[j])) |
| { |
| TCU_FAIL("ProgramUniformi failed"); |
| } |
| } |
| } |
| |
| // Use the conventional uniform interfaces on an ACTIVE_PROGRAM |
| glw::GLuint activeProgram = 0; |
| if (pipeline != 0) |
| { |
| gl.getProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, (int *)&activeProgram); |
| } |
| if ((activeProgram != 0) && ((activeProgram == programA) || (activeProgram == programB))) |
| { |
| glw::GLint *location; |
| |
| location = (activeProgram == programA) ? locationA : locationB; |
| |
| // reseed the float buffer |
| data[0] = data[maxDataCount - 1]; |
| for (i = 1; i < maxDataCount; i++) |
| { |
| data[i] = data[i - 1] + 1.0f; |
| } |
| |
| // set uniforms with conventional uniform calls |
| value = &data[0]; |
| if (isSquareMat) |
| { |
| floatCount = floatCountSqu; |
| gl.uniformMatrix2fv(location[0], arraySize, GL_FALSE, value); |
| value += 2 * 2 * arraySize; |
| gl.uniformMatrix3fv(location[2], arraySize, GL_FALSE, value); |
| value += 3 * 3 * arraySize; |
| gl.uniformMatrix4fv(location[4], arraySize, GL_FALSE, value); |
| } |
| else |
| { |
| floatCount = floatCountNonSqu; |
| gl.uniformMatrix2x3fv(location[0], arraySize, GL_FALSE, value); |
| value += 2 * 3 * arraySize; |
| gl.uniformMatrix2x4fv(location[2], arraySize, GL_FALSE, value); |
| value += 2 * 4 * arraySize; |
| gl.uniformMatrix3x2fv(location[4], arraySize, GL_FALSE, value); |
| value += 3 * 2 * arraySize; |
| gl.uniformMatrix3x4fv(location[6], arraySize, GL_FALSE, value); |
| value += 3 * 4 * arraySize; |
| gl.uniformMatrix4x2fv(location[8], arraySize, GL_FALSE, value); |
| value += 4 * 2 * arraySize; |
| gl.uniformMatrix4x3fv(location[10], arraySize, GL_FALSE, value); |
| } |
| |
| // get and compare the uniform data |
| value = &data[0]; |
| for (i = 0; i < numUniforms; i++) |
| { |
| float retVal[16]; |
| |
| gl.getUniformfv(activeProgram, location[i], retVal); |
| |
| for (j = 0; j < floatCount[i]; j++) |
| { |
| // Compare activeshaderprogram uniform to expected value |
| if (retVal[j] != *value++) |
| { |
| TCU_FAIL("ActiveShaderProgram with glUniform failed"); |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| IterateResult iterate(void) |
| { |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| glu::DataType dType[5] = {glu::TYPE_INT, glu::TYPE_UINT, glu::TYPE_FLOAT, glu::TYPE_FLOAT_MAT2, |
| glu::TYPE_FLOAT_MAT2X3}; |
| |
| // Loop over the various data types, generate fragment programs, and test uniforms |
| // (MAT2 means stands for all square matrices, MAT2x3 stands for all non-square matrices) |
| for (int i = 0; i < 5; i++) |
| { |
| glw::GLuint programA, programB; |
| glw::GLuint pipeline = 0; |
| const char *shaderSrc[1]; |
| std::string fragSrc; |
| int seed = 1000 + (1000 * i); |
| |
| generateUniformFragSrc(fragSrc, m_glslVersion, dType[i]); |
| |
| size_t length = fragSrc.size(); |
| std::vector<char> shaderbuf(length + 1); |
| fragSrc.copy(&shaderbuf[0], length); |
| shaderbuf[length] = '\0'; |
| shaderSrc[0] = &shaderbuf[0]; |
| programA = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, shaderSrc); |
| programB = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, shaderSrc); |
| |
| if (isDataTypeMatrix(dType[i])) |
| { |
| // programs are unbound |
| setAndCompareMatrixUniforms(pipeline, programA, programB, dType[i], seed); |
| |
| // bind one program with useProgramStages |
| gl.genProgramPipelines(1, &pipeline); |
| gl.bindProgramPipeline(pipeline); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programA); |
| seed += 100; |
| setAndCompareMatrixUniforms(pipeline, programA, programB, dType[i], seed); |
| |
| // make an active program with activeShaderProgram |
| gl.activeShaderProgram(pipeline, programB); |
| seed += 100; |
| setAndCompareMatrixUniforms(pipeline, programA, programB, dType[i], seed); |
| } |
| else |
| { |
| // programs are unbound |
| setAndCompareUniforms(pipeline, programA, programB, dType[i], seed); |
| |
| // bind one program with useProgramStages |
| gl.genProgramPipelines(1, &pipeline); |
| gl.bindProgramPipeline(pipeline); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programA); |
| seed += 100; |
| setAndCompareUniforms(pipeline, programA, programB, dType[i], seed); |
| |
| // make an active program with activeShaderProgram |
| gl.activeShaderProgram(pipeline, programB); |
| seed += 100; |
| setAndCompareUniforms(pipeline, programA, programB, dType[i], seed); |
| } |
| |
| gl.deleteProgram(programA); |
| gl.deleteProgram(programB); |
| gl.deleteProgramPipelines(1, &pipeline); |
| } |
| |
| // Negative Cases |
| |
| // Program that is not successfully linked |
| glw::GLenum err; |
| std::string vtx; |
| std::string frag; |
| |
| vtx = generateBasicVertexSrc(m_glslVersion); |
| frag = generateBasicFragmentSrc(m_glslVersion); |
| |
| // remove the main keyword so it doesn't link |
| std::string fragNoMain = frag; |
| unsigned int pos = (unsigned int)fragNoMain.find("main"); |
| fragNoMain.replace(pos, 4, "niaM"); |
| glu::ShaderProgram progNoLink(m_context.getRenderContext(), |
| glu::makeVtxFragSources(vtx.c_str(), fragNoMain.c_str())); |
| gl.programParameteri(progNoLink.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE); |
| gl.linkProgram(progNoLink.getProgram()); |
| int unifLocation = gl.getUniformLocation(progNoLink.getProgram(), "u_color"); |
| gl.programUniform4f(progNoLink.getProgram(), unifLocation, 1.0, 1.0, 1.0, 1.0); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("ProgramUniformi failed"); |
| } |
| |
| // deleted program |
| gl.deleteProgram(progNoLink.getProgram()); |
| gl.programUniform4f(progNoLink.getProgram(), unifLocation, 1.0, 1.0, 1.0, 1.0); |
| err = gl.getError(); |
| if (err != GL_INVALID_VALUE) |
| { |
| TCU_FAIL("ProgramUniformi failed"); |
| } |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| private: |
| glu::GLSLVersion m_glslVersion; |
| }; |
| |
| // Testcase for state interactions |
| class StateInteractionCase : public TestCase |
| { |
| public: |
| StateInteractionCase(Context &context, const char *name, const char *description, glu::GLSLVersion glslVersion) |
| : TestCase(context, name, description) |
| , m_glslVersion(glslVersion) |
| { |
| } |
| |
| ~StateInteractionCase(void) |
| { |
| } |
| |
| // Log the program info log |
| void logProgramInfoLog(const glw::Functions &gl, glw::GLuint program) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| glw::GLint value = 0; |
| glw::GLsizei bufSize = 0; |
| glw::GLsizei length = 0; |
| |
| gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &value); |
| std::vector<char> infoLogBuf(value + 1); |
| |
| gl.getProgramInfoLog(program, bufSize, &length, &infoLogBuf[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog failed"); |
| |
| log << TestLog::Message << "Program Log:\n" << &infoLogBuf[0] << TestLog::EndMessage; |
| } |
| |
| // Check program validity created with CreateShaderProgram |
| bool checkCSProg(const glw::Functions &gl, GLuint program, int expectedLink = GL_TRUE) |
| { |
| int linked = GL_FALSE; |
| if (program != 0) |
| { |
| gl.getProgramiv(program, GL_LINK_STATUS, &linked); |
| |
| if (expectedLink && !linked) |
| { |
| logProgramInfoLog(gl, program); |
| } |
| } |
| |
| return (program != 0) && (linked == expectedLink); |
| } |
| |
| // Generate a vertex shader for variable input/output testing |
| void generateVarLinkVertexShaderSrc(std::string &outVtxSrc, glu::GLSLVersion glslVersion, int numOutputs) |
| { |
| std::ostringstream vtxSrc; |
| |
| vtxSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| if (glslVersion >= glu::GLSL_VERSION_410) |
| { |
| vtxSrc << "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| vtxSrc << "in highp vec4 a_position;\n"; |
| vtxSrc << "uniform highp vec4 u_color;\n"; |
| |
| switch (numOutputs) |
| { |
| // Note all these cases fall through |
| case 5: |
| vtxSrc << "layout(location = 3) out vec4 o_val5;\n"; |
| // Fallthrough |
| case 4: |
| vtxSrc << "flat out uvec4 val4;\n"; |
| // Fallthrough |
| case 3: |
| vtxSrc << "flat out ivec2 val3;\n"; |
| // Fallthrough |
| case 2: |
| vtxSrc << "out vec3 val2[2];\n"; |
| // Fallthrough |
| case 1: |
| vtxSrc << "out vec4 val1;\n"; |
| // Fallthrough |
| default: |
| vtxSrc << "out float val0;\n"; |
| } |
| |
| vtxSrc << "void main (void)\n"; |
| vtxSrc << "{\n"; |
| vtxSrc << " gl_Position = a_position;\n"; |
| |
| // The color uniform is passed in the last declared output variable |
| switch (numOutputs) |
| { |
| case 5: |
| vtxSrc << " o_val5 = u_color;\n"; |
| break; |
| case 4: |
| vtxSrc << " val4 = uvec4(u_color);\n"; |
| break; |
| case 3: |
| vtxSrc << " val3 = ivec2(u_color);\n"; |
| break; |
| case 2: |
| vtxSrc << " val2[0] = vec3(u_color);\n"; |
| break; |
| case 1: |
| vtxSrc << " val1 = u_color;\n"; |
| break; |
| default: |
| vtxSrc << " val0 = u_color.x;\n"; |
| break; |
| } |
| vtxSrc << "}\n"; |
| |
| outVtxSrc = vtxSrc.str(); |
| } |
| |
| // Generate a fragment shader for variable input/output testing |
| void generateVarLinkFragmentShaderSrc(std::string &outFragSrc, glu::GLSLVersion glslVersion, int numInputs) |
| { |
| std::ostringstream fragSrc; |
| |
| fragSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| fragSrc << "precision highp float;\n"; |
| fragSrc << "precision highp int;\n"; |
| |
| switch (numInputs) |
| { |
| // Note all these cases fall through |
| case 5: |
| fragSrc << "layout(location = 3) in vec4 i_val5;\n"; |
| // Fallthrough |
| case 4: |
| fragSrc << "flat in uvec4 val4;\n"; |
| // Fallthrough |
| case 3: |
| fragSrc << "flat in ivec2 val3;\n"; |
| // Fallthrough |
| case 2: |
| fragSrc << "in vec3 val2[2];\n"; |
| // Fallthrough |
| case 1: |
| fragSrc << "in vec4 val1;\n"; |
| // Fallthrough |
| default: |
| fragSrc << "in float val0;\n"; |
| } |
| |
| fragSrc << "layout(location = 0) out mediump vec4 o_color;\n"; |
| fragSrc << "void main (void)\n"; |
| fragSrc << "{\n"; |
| |
| switch (numInputs) |
| { |
| case 5: |
| fragSrc << " o_color = i_val5;\n"; |
| break; |
| case 4: |
| fragSrc << " o_color = vec4(val4);\n"; |
| break; |
| case 3: |
| fragSrc << " o_color = vec4(val3, 1.0, 1.0);\n"; |
| break; |
| case 2: |
| fragSrc << " o_color = vec4(val2[0], 1.0);\n"; |
| break; |
| case 1: |
| fragSrc << " o_color = vec4(val1);\n"; |
| break; |
| default: |
| fragSrc << " o_color = vec4(val0, val0, val0, 1.0);\n"; |
| break; |
| } |
| |
| fragSrc << "}\n"; |
| |
| outFragSrc = fragSrc.str(); |
| } |
| |
| // Verify the surface is filled with the expected color |
| bool checkSurface(tcu::Surface surface, tcu::RGBA expectedColor) |
| { |
| int numFailedPixels = 0; |
| for (int y = 0; y < surface.getHeight(); y++) |
| { |
| for (int x = 0; x < surface.getWidth(); x++) |
| { |
| if (surface.getPixel(x, y) != expectedColor) |
| numFailedPixels += 1; |
| } |
| } |
| |
| return (numFailedPixels == 0); |
| } |
| |
| IterateResult iterate(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| const tcu::RenderTarget &renderTarget = m_context.getRenderContext().getRenderTarget(); |
| int viewportW = de::min(16, renderTarget.getWidth()); |
| int viewportH = de::min(16, renderTarget.getHeight()); |
| tcu::Surface renderedFrame(viewportW, viewportH); |
| |
| glw::GLuint programA, programB; |
| glw::GLuint vao, vertexBuf, indexBuf; |
| std::string vtx; |
| std::string frag, frag2; |
| glw::GLuint pipeline; |
| const char *srcStrings[1]; |
| glw::GLenum err; |
| |
| log << TestLog::Message << "Begin:StateInteractionCase iterate" << TestLog::EndMessage; |
| |
| gl.viewport(0, 0, viewportW, viewportH); |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| |
| // Check the precedence of glUseProgram over glBindProgramPipeline |
| // The program bound with glUseProgram will draw green, the programs |
| // bound with glBindProgramPipeline will render blue. |
| vtx = generateBasicVertexSrc(m_glslVersion); |
| frag = generateBasicFragmentSrc(m_glslVersion); |
| |
| glu::ShaderProgram progVF(m_context.getRenderContext(), glu::makeVtxFragSources(vtx.c_str(), frag.c_str())); |
| |
| gl.useProgram(progVF.getProgram()); |
| // Ouput green in the fragment shader |
| gl.uniform4f(gl.getUniformLocation(progVF.getProgram(), "u_color"), 0.0f, 1.0f, 0.0f, 1.0f); |
| |
| // Create and bind a pipeline with a different fragment shader |
| gl.genProgramPipelines(1, &pipeline); |
| // Use a different uniform name in another fragment shader |
| frag2 = frag; |
| size_t pos = 0; |
| while ((pos = frag2.find("u_color", pos)) != std::string::npos) |
| { |
| frag2.replace(pos, 7, "u_clrPB"); |
| pos += 7; |
| } |
| |
| srcStrings[0] = vtx.c_str(); |
| programA = gl.createShaderProgramv(GL_VERTEX_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programA)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for vertex shader"); |
| } |
| srcStrings[0] = frag2.c_str(); |
| programB = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programB)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for fragment shader"); |
| } |
| // Program B outputs blue. |
| gl.programUniform4f(programB, gl.getUniformLocation(programB, "u_clrPB"), 0.0f, 0.0f, 1.0f, 1.0f); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programA); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programB); |
| gl.bindProgramPipeline(pipeline); |
| |
| static const uint16_t quadIndices[] = {0, 1, 2, 2, 1, 3}; |
| const float position[] = {-1.0f, -1.0f, +1.0f, 1.0f, -1.0f, +1.0f, 0.0f, 1.0f, |
| +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 1.0f}; |
| |
| // Draw a quad with glu::draw |
| glu::VertexArrayBinding posArray = glu::va::Float("a_position", 4, 4, 0, &position[0]); |
| glu::draw(m_context.getRenderContext(), progVF.getProgram(), 1, &posArray, |
| glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0])); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "StateInteraction glu::draw failure"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // useProgram takes precedence and the buffer should be green |
| if (!checkSurface(renderedFrame, tcu::RGBA::green())) |
| { |
| TCU_FAIL("StateInteraction failed; surface should be green"); |
| } |
| |
| // The position attribute locations may be different. |
| int posLoc = gl.getAttribLocation(progVF.getProgram(), "a_position"); |
| |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| gl.disableVertexAttribArray(posLoc); |
| |
| /* Set up a vertex array object */ |
| gl.genVertexArrays(1, &vao); |
| gl.bindVertexArray(vao); |
| |
| gl.genBuffers(1, &indexBuf); |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf); |
| gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quadIndices), quadIndices, GL_STATIC_DRAW); |
| |
| gl.genBuffers(1, &vertexBuf); |
| gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuf); |
| gl.bufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW); |
| |
| posLoc = gl.getAttribLocation(programA, "a_position"); |
| gl.vertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, 0); |
| gl.enableVertexAttribArray(posLoc); |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "VAO setup failure"); |
| |
| // bindProgramPipeline without a program installed by useProgram |
| // Rerender the quad. Don't use glu::draw because it takes the |
| // program as a parameter and sets state. |
| gl.useProgram(0); |
| gl.bindProgramPipeline(pipeline); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // bindProgramPipeline will render blue |
| if (!checkSurface(renderedFrame, tcu::RGBA::blue())) |
| { |
| TCU_FAIL("StateInteraction failed; surface should be blue"); |
| } |
| |
| // Test rendering with no program bound. Rendering is undefined |
| // but shouldn't produce an error. |
| gl.useProgram(0); |
| gl.bindProgramPipeline(0); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| // Render call with missing pipeline stages should not generate an error |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, 0); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programB); |
| gl.bindProgramPipeline(pipeline); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programA); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, 0); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| // Missing program for fragment shader |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, programA); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| // Separable program with both vertex and fragment shaders attached to only one stage |
| |
| gl.programParameteri(progVF.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE); |
| gl.linkProgram(progVF.getProgram()); |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, progVF.getProgram()); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("DrawElements failed"); |
| } |
| |
| gl.validateProgramPipeline(pipeline); |
| glw::GLint value; |
| gl.getProgramPipelineiv(pipeline, GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("Program pipeline validation failed"); |
| } |
| |
| // attached to just the fragment shader |
| // Call validateProgramPipeline before rendering this time |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, 0); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, progVF.getProgram()); |
| |
| gl.validateProgramPipeline(pipeline); |
| gl.getProgramPipelineiv(pipeline, GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| if (value != 0) |
| { |
| TCU_FAIL("Program pipeline validation failed"); |
| } |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| err = gl.getError(); |
| if (err != GL_INVALID_OPERATION) |
| { |
| TCU_FAIL("DrawElements failed"); |
| } |
| |
| // Program deletion |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programA); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programB); |
| |
| // Program B renders red this time |
| gl.programUniform4f(programB, gl.getUniformLocation(programB, "u_clrPB"), 1.0f, 0.0f, 0.0f, 1.0f); |
| |
| gl.deleteProgram(programB); |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // expect red |
| if (!checkSurface(renderedFrame, tcu::RGBA::red())) |
| { |
| TCU_FAIL("StateInteraction failed; surface should be red"); |
| } |
| |
| // Attach new shader |
| srcStrings[0] = frag2.c_str(); |
| programB = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programB)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for fragment shader"); |
| } |
| // Render green |
| gl.programUniform4f(programB, gl.getUniformLocation(programB, "u_clrPB"), 0.0f, 1.0f, 0.0f, 1.0f); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programB); |
| |
| // new shader |
| glw::GLuint vshader = gl.createShader(GL_FRAGMENT_SHADER); |
| srcStrings[0] = frag.c_str(); // First frag shader with u_color uniform |
| gl.shaderSource(vshader, 1, srcStrings, NULL); |
| gl.compileShader(vshader); |
| gl.getShaderiv(vshader, GL_COMPILE_STATUS, &value); |
| DE_ASSERT(value == GL_TRUE); |
| gl.attachShader(programB, vshader); |
| |
| // changing shader shouldn't affect link_status |
| gl.getProgramiv(programB, GL_LINK_STATUS, &value); |
| if (value != 1) |
| { |
| TCU_FAIL("Shader attachment shouldn't affect link status"); |
| } |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // expect green |
| if (!checkSurface(renderedFrame, tcu::RGBA::green())) |
| { |
| TCU_FAIL("StateInteraction failed; surface should be green"); |
| } |
| |
| // Negative Case: Unsuccessfully linked program should not affect current program |
| |
| // Render white |
| gl.programUniform4f(programB, gl.getUniformLocation(programB, "u_clrPB"), 1.0f, 1.0f, 1.0f, 1.0f); |
| std::string noMain = frag; |
| pos = noMain.find("main", 0); |
| noMain.replace(pos, 4, "niaM"); |
| |
| srcStrings[0] = noMain.c_str(); |
| gl.shaderSource(vshader, 1, srcStrings, NULL); |
| gl.compileShader(vshader); |
| gl.getShaderiv(vshader, GL_COMPILE_STATUS, &value); |
| gl.attachShader(programB, vshader); |
| gl.linkProgram(programB); |
| err = gl.getError(); |
| |
| // link_status should be false |
| gl.getProgramiv(programB, GL_LINK_STATUS, &value); |
| if (value != 0) |
| { |
| TCU_FAIL("StateInteraction failed; link failure"); |
| } |
| |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // expect white |
| if (!checkSurface(renderedFrame, tcu::RGBA::white())) |
| { |
| TCU_FAIL("StateInteraction failed; surface should be white"); |
| } |
| |
| gl.deleteProgram(programA); |
| gl.deleteProgram(programB); |
| |
| // Shader interface matching inputs/outputs |
| |
| int maxVars = 6; // generate code supports 6 variables |
| for (int numInputs = 0; numInputs < maxVars; numInputs++) |
| { |
| for (int numOutputs = 0; numOutputs < maxVars; numOutputs++) |
| { |
| |
| generateVarLinkVertexShaderSrc(vtx, m_glslVersion, numOutputs); |
| generateVarLinkFragmentShaderSrc(frag, m_glslVersion, numInputs); |
| |
| srcStrings[0] = vtx.c_str(); |
| programA = gl.createShaderProgramv(GL_VERTEX_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programA)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for vertex shader"); |
| } |
| |
| srcStrings[0] = frag.c_str(); |
| programB = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programB)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for fragment shader"); |
| } |
| |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programA); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programB); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages failure"); |
| |
| gl.validateProgramPipeline(pipeline); |
| gl.getProgramPipelineiv(pipeline, GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| |
| // Matched input and output variables should render |
| if (numInputs == numOutputs) |
| { |
| if (value != 1) |
| { |
| log << TestLog::Message << "Matched input and output variables should validate successfully.\n" |
| << "Vertex Shader:\n" |
| << vtx << "Fragment Shader:\n" |
| << frag << TestLog::EndMessage; |
| TCU_FAIL("StateInteraction failed"); |
| } |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| // white |
| gl.programUniform4f(programA, gl.getUniformLocation(programA, "u_color"), 1.0f, 1.0f, 1.0f, 1.0f); |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // expect white |
| if (!checkSurface(renderedFrame, tcu::RGBA::white())) |
| { |
| TCU_FAIL("StateInteraction failed; surface should be white"); |
| } |
| } |
| else |
| { |
| // Mismatched input and output variables |
| // For OpenGL ES contexts, this should cause a validation failure |
| // For OpenGL contexts, validation should succeed. |
| if (glu::isContextTypeES(m_context.getRenderContext().getType()) != (value == 0)) |
| { |
| log << TestLog::Message << "Mismatched input and output variables; validation should " |
| << (glu::isContextTypeES(m_context.getRenderContext().getType()) ? "fail.\n" : "succeed.\n") |
| << "Vertex Shader:\n" |
| << vtx << "Fragment Shader:\n" |
| << frag << TestLog::EndMessage; |
| TCU_FAIL("StateInteraction failed"); |
| } |
| } |
| |
| gl.deleteProgram(programA); |
| gl.deleteProgram(programB); |
| } |
| } |
| |
| gl.bindProgramPipeline(0); |
| gl.bindVertexArray(0); |
| gl.deleteProgramPipelines(1, &pipeline); |
| gl.deleteShader(vshader); |
| gl.deleteVertexArrays(1, &vao); |
| gl.deleteBuffers(1, &indexBuf); |
| gl.deleteBuffers(1, &vertexBuf); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| private: |
| glu::GLSLVersion m_glslVersion; |
| }; |
| |
| // Testcase for interface qualifiers matching |
| class InterfaceMatchingCase : public TestCase |
| { |
| public: |
| enum TestType |
| { |
| DEFAULT_PRECISION, |
| SET_DEFAULT_PRECISION, |
| SET_PRECISION |
| }; |
| |
| std::string getTestTypeName(TestType testType) |
| { |
| switch (testType) |
| { |
| case DEFAULT_PRECISION: |
| return "use predeclared precision"; |
| case SET_DEFAULT_PRECISION: |
| return "set default precision"; |
| case SET_PRECISION: |
| return "explicit precision"; |
| } |
| return ""; |
| } |
| |
| InterfaceMatchingCase(Context &context, const char *name, glu::GLSLVersion glslVersion) |
| : TestCase(context, name, "matching precision qualifiers between stages") |
| , m_glslVersion(glslVersion) |
| { |
| } |
| |
| ~InterfaceMatchingCase(void) |
| { |
| } |
| |
| string getDefaultFragmentPrecision() |
| { |
| return ""; |
| } |
| |
| // Generate a vertex shader for variable input/output precision testing |
| virtual void generateVarLinkVertexShaderSrc(std::string &outVtxSrc, glu::GLSLVersion glslVersion, |
| const string &precision, TestType testMode) = 0; |
| |
| // Generate a fragment shader for variable input/output precision testing |
| virtual void generateVarLinkFragmentShaderSrc(std::string &outFragSrc, glu::GLSLVersion glslVersion, |
| const string &precision, TestType testMode) = 0; |
| |
| // Verify the surface is filled with the expected color |
| bool checkSurface(tcu::Surface surface, tcu::RGBA expectedColor) |
| { |
| int numFailedPixels = 0; |
| for (int y = 0; y < surface.getHeight(); y++) |
| { |
| for (int x = 0; x < surface.getWidth(); x++) |
| { |
| if (surface.getPixel(x, y) != expectedColor) |
| numFailedPixels += 1; |
| } |
| } |
| return (numFailedPixels == 0); |
| } |
| |
| // Log the program info log |
| void logProgramInfoLog(const glw::Functions &gl, glw::GLuint program) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| glw::GLint value = 0; |
| glw::GLsizei bufSize = 0; |
| glw::GLsizei length = 0; |
| |
| gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &value); |
| std::vector<char> infoLogBuf(value + 1); |
| |
| gl.getProgramInfoLog(program, bufSize, &length, &infoLogBuf[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog failed"); |
| |
| log << TestLog::Message << "Program Log:\n" << &infoLogBuf[0] << TestLog::EndMessage; |
| } |
| |
| // Check program validity created with CreateShaderProgram |
| bool checkCSProg(const glw::Functions &gl, GLuint program, int expectedLink = GL_TRUE) |
| { |
| int linked = GL_FALSE; |
| if (program != 0) |
| { |
| gl.getProgramiv(program, GL_LINK_STATUS, &linked); |
| |
| if (expectedLink && !linked) |
| { |
| logProgramInfoLog(gl, program); |
| } |
| } |
| |
| return (program != 0) && (linked == expectedLink); |
| } |
| |
| IterateResult iterate(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| const tcu::RenderTarget &renderTarget = m_context.getRenderContext().getRenderTarget(); |
| int viewportW = de::min(16, renderTarget.getWidth()); |
| int viewportH = de::min(16, renderTarget.getHeight()); |
| tcu::Surface renderedFrame(viewportW, viewportH); |
| |
| glw::GLuint programA, programB; |
| glw::GLuint vao, vertexBuf, indexBuf; |
| std::string vtx; |
| std::string frag, frag2; |
| glw::GLuint pipeline; |
| const char *srcStrings[1]; |
| glw::GLuint value; |
| |
| gl.viewport(0, 0, viewportW, viewportH); |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| |
| static const uint16_t quadIndices[] = {0, 1, 2, 2, 1, 3}; |
| const float position[] = {-1.0f, -1.0f, +1.0f, 1.0f, -1.0f, +1.0f, 0.0f, 1.0f, |
| +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 1.0f}; |
| |
| /* Set up a vertex array object */ |
| gl.genVertexArrays(1, &vao); |
| gl.bindVertexArray(vao); |
| |
| gl.genBuffers(1, &indexBuf); |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf); |
| gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quadIndices), quadIndices, GL_STATIC_DRAW); |
| |
| gl.genBuffers(1, &vertexBuf); |
| gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuf); |
| gl.bufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW); |
| |
| /* Set up shader pipeline */ |
| gl.genProgramPipelines(1, &pipeline); |
| gl.bindProgramPipeline(pipeline); |
| |
| struct PrecisionTests |
| { |
| TestType testType; |
| std::string precision; |
| }; |
| |
| PrecisionTests vertexPrecisionTests[] = { |
| {DEFAULT_PRECISION, "highp"}, {SET_DEFAULT_PRECISION, "highp"}, {SET_DEFAULT_PRECISION, "mediump"}, |
| {SET_DEFAULT_PRECISION, "lowp"}, {SET_PRECISION, "highp"}, {SET_PRECISION, "mediump"}, |
| {SET_PRECISION, "lowp"}}; |
| |
| PrecisionTests fragmentPrecisionTests[] = {{DEFAULT_PRECISION, getDefaultFragmentPrecision()}, |
| {SET_DEFAULT_PRECISION, "highp"}, |
| {SET_DEFAULT_PRECISION, "mediump"}, |
| {SET_DEFAULT_PRECISION, "lowp"}, |
| {SET_PRECISION, "highp"}, |
| {SET_PRECISION, "mediump"}, |
| {SET_PRECISION, "lowp"}}; |
| |
| // Shader interface matching inputs/outputs precision |
| int maxTests = 7; |
| for (int vertexTestIteration = 0; vertexTestIteration < maxTests; vertexTestIteration++) |
| { |
| std::string vertexPrecision = vertexPrecisionTests[vertexTestIteration].precision; |
| TestType vertexTestType = vertexPrecisionTests[vertexTestIteration].testType; |
| for (int fragmentTestIteration = 0; fragmentTestIteration < maxTests; fragmentTestIteration++) |
| { |
| std::string fragmentPrecision = fragmentPrecisionTests[fragmentTestIteration].precision; |
| TestType fragmentTestType = fragmentPrecisionTests[fragmentTestIteration].testType; |
| if (fragmentPrecision.empty()) |
| continue; |
| |
| log << TestLog::Message << "vertex shader precision: " << vertexPrecision |
| << ", shader test mode: " << getTestTypeName(vertexTestType) << TestLog::EndMessage; |
| |
| log << TestLog::Message << "fragment shader precision: " << fragmentPrecision |
| << ", shader test mode: " << getTestTypeName(fragmentTestType) << TestLog::EndMessage; |
| |
| generateVarLinkVertexShaderSrc(vtx, m_glslVersion, vertexPrecision, vertexTestType); |
| generateVarLinkFragmentShaderSrc(frag, m_glslVersion, fragmentPrecision, fragmentTestType); |
| |
| srcStrings[0] = vtx.c_str(); |
| programA = gl.createShaderProgramv(GL_VERTEX_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programA)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for vertex shader"); |
| } |
| srcStrings[0] = frag.c_str(); |
| programB = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, srcStrings); |
| if (!checkCSProg(gl, programB)) |
| { |
| TCU_FAIL("CreateShaderProgramv failed for fragment shader"); |
| } |
| |
| gl.useProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programA); |
| gl.useProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programB); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "InterfaceMatching failure"); |
| |
| // Mismatched input and output qualifiers |
| // For OpenGL ES contexts, this should result in a validation failure. |
| // For OpenGL contexts, validation should succeed. |
| gl.validateProgramPipeline(pipeline); |
| gl.getProgramPipelineiv(pipeline, GL_VALIDATE_STATUS, (glw::GLint *)&value); |
| int precisionCompareResult = fragmentPrecision.compare(vertexPrecision); |
| if (glu::isContextTypeES(m_context.getRenderContext().getType()) && (precisionCompareResult != 0)) |
| { |
| // precision mismatch |
| if (value != GL_FALSE) |
| { |
| log.startShaderProgram( |
| false, "Precision mismatch, pipeline validation status GL_TRUE expected GL_FALSE"); |
| log.writeShader(QP_SHADER_TYPE_VERTEX, vtx.c_str(), true, ""); |
| log.writeShader(QP_SHADER_TYPE_FRAGMENT, frag.c_str(), true, ""); |
| log.endShaderProgram(); |
| TCU_FAIL("InterfaceMatchingCase failed"); |
| } |
| else |
| { |
| log << TestLog::Message << "Precision mismatch, Pipeline validation status GL_FALSE -> OK" |
| << TestLog::EndMessage; |
| } |
| } |
| else |
| { |
| if (value != GL_TRUE) |
| { |
| std::stringstream str; |
| str << "Precision " << (precisionCompareResult ? "mismatch" : "matches") |
| << ", pipeline validation status GL_FALSE expected GL_TRUE"; |
| |
| log.startShaderProgram(false, str.str().c_str()); |
| log.writeShader(QP_SHADER_TYPE_VERTEX, vtx.c_str(), true, ""); |
| log.writeShader(QP_SHADER_TYPE_FRAGMENT, frag.c_str(), true, ""); |
| log.endShaderProgram(); |
| TCU_FAIL("InterfaceMatchingCase failed"); |
| } |
| else |
| { |
| log << TestLog::Message << "Precision " << (precisionCompareResult ? "mismatch" : "matches") |
| << ", pipeline validation status GL_TRUE -> OK" << TestLog::EndMessage; |
| // precision matches |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| // white |
| int posLoc = gl.getAttribLocation(programA, "a_position"); |
| gl.vertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, 0); |
| gl.enableVertexAttribArray(posLoc); |
| gl.programUniform4f(programA, gl.getUniformLocation(programA, "u_color"), 1.0f, 1.0f, 1.0f, |
| 1.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "StateInteraction failure, set uniform value"); |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements failure"); |
| gl.disableVertexAttribArray(posLoc); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| |
| // expect white |
| if (!checkSurface(renderedFrame, tcu::RGBA::white())) |
| { |
| TCU_FAIL("InterfaceMatchingCase failed; surface should be white"); |
| } |
| } |
| } |
| |
| // validate non separable program |
| |
| glu::ShaderProgram progVF(m_context.getRenderContext(), |
| glu::makeVtxFragSources(vtx.c_str(), frag.c_str())); |
| |
| gl.useProgram(progVF.getProgram()); |
| gl.uniform4f(gl.getUniformLocation(progVF.getProgram(), "u_color"), 1.0f, 1.0f, 1.0f, 1.0f); |
| if (!progVF.getProgramInfo().linkOk) |
| { |
| log << progVF; |
| log << TestLog::Message << "Non separable program link status GL_FALSE expected GL_TRUE" |
| << TestLog::EndMessage; |
| TCU_FAIL("InterfaceMatchingCase failed, non separable program should link"); |
| } |
| |
| int posLoc = gl.getAttribLocation(progVF.getProgram(), "a_position"); |
| gl.vertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, 0); |
| gl.enableVertexAttribArray(posLoc); |
| gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, NULL); |
| gl.disableVertexAttribArray(posLoc); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "StateInteraction failure, non separable program draw call"); |
| glu::readPixels(m_context.getRenderContext(), 0, 0, renderedFrame.getAccess()); |
| // expect white |
| if (!checkSurface(renderedFrame, tcu::RGBA::white())) |
| { |
| TCU_FAIL("InterfaceMatchingCase failed, non separable program, unexpected color found"); |
| } |
| |
| gl.deleteProgram(programA); |
| gl.deleteProgram(programB); |
| gl.useProgram(0); |
| } |
| } |
| gl.bindVertexArray(0); |
| gl.deleteVertexArrays(1, &vao); |
| gl.deleteBuffers(1, &indexBuf); |
| gl.deleteBuffers(1, &vertexBuf); |
| gl.bindProgramPipeline(0); |
| gl.deleteProgramPipelines(1, &pipeline); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| protected: |
| glu::GLSLVersion m_glslVersion; |
| }; |
| |
| class InterfaceMatchingCaseFloat : public InterfaceMatchingCase |
| { |
| public: |
| InterfaceMatchingCaseFloat(Context &context, const char *name, glu::GLSLVersion glslVersion) |
| : InterfaceMatchingCase(context, name, glslVersion) |
| { |
| } |
| |
| void generateVarLinkVertexShaderSrc(std::string &outVtxSrc, glu::GLSLVersion glslVersion, const string &precision, |
| TestType testMode) |
| { |
| std::ostringstream vtxSrc; |
| vtxSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| if (glslVersion >= glu::GLSL_VERSION_410) |
| { |
| vtxSrc << "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| vtxSrc << "in highp vec4 a_position;\n"; |
| vtxSrc << "uniform highp vec4 u_color;\n"; |
| switch (testMode) |
| { |
| case SET_DEFAULT_PRECISION: |
| vtxSrc << "precision " << precision << " float;\n"; |
| // Fallthrough |
| case DEFAULT_PRECISION: |
| vtxSrc << "out float var;\n"; |
| break; |
| case SET_PRECISION: |
| vtxSrc << "out " << precision << " float var;\n"; |
| break; |
| } |
| vtxSrc << "void main (void)\n"; |
| vtxSrc << "{\n"; |
| vtxSrc << " gl_Position = a_position;\n"; |
| vtxSrc << " var = u_color.r;\n"; |
| vtxSrc << "}\n"; |
| outVtxSrc = vtxSrc.str(); |
| } |
| |
| void generateVarLinkFragmentShaderSrc(std::string &outFragSrc, glu::GLSLVersion glslVersion, |
| const string &precision, TestType testMode) |
| { |
| std::ostringstream fragSrc; |
| fragSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| switch (testMode) |
| { |
| case SET_DEFAULT_PRECISION: |
| fragSrc << "precision " << precision << " float;\n"; |
| // Fallthrough |
| case DEFAULT_PRECISION: |
| fragSrc << "in float var;\n"; |
| break; |
| case SET_PRECISION: |
| fragSrc << "in " << precision << " float var;\n"; |
| break; |
| } |
| fragSrc << "layout(location = 0) out mediump vec4 o_color;\n"; |
| fragSrc << "void main (void)\n"; |
| fragSrc << "{\n"; |
| fragSrc << " o_color = vec4(var);\n"; |
| fragSrc << "}\n"; |
| outFragSrc = fragSrc.str(); |
| } |
| }; |
| |
| class InterfaceMatchingCaseInt : public InterfaceMatchingCase |
| { |
| public: |
| InterfaceMatchingCaseInt(Context &context, const char *name, glu::GLSLVersion glslVersion) |
| : InterfaceMatchingCase(context, name, glslVersion) |
| { |
| } |
| |
| std::string getDefaultFragmentPrecision() |
| { |
| return "mediump"; |
| } |
| |
| void generateVarLinkVertexShaderSrc(std::string &outVtxSrc, glu::GLSLVersion glslVersion, const string &precision, |
| TestType testMode) |
| { |
| std::ostringstream vtxSrc; |
| vtxSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| if (glslVersion >= glu::GLSL_VERSION_410) |
| { |
| vtxSrc << "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| vtxSrc << "in highp vec4 a_position;\n"; |
| vtxSrc << "uniform highp vec4 u_color;\n"; |
| switch (testMode) |
| { |
| case SET_DEFAULT_PRECISION: |
| vtxSrc << "precision " << precision << " int;\n"; |
| // Fallthrough |
| case DEFAULT_PRECISION: |
| vtxSrc << "flat out int var;\n"; |
| break; |
| case SET_PRECISION: |
| vtxSrc << "flat out " << precision << " int var;\n"; |
| break; |
| } |
| vtxSrc << "void main (void)\n"; |
| vtxSrc << "{\n"; |
| vtxSrc << " gl_Position = a_position;\n"; |
| vtxSrc << " var = int(u_color.r);\n"; |
| vtxSrc << "}\n"; |
| outVtxSrc = vtxSrc.str(); |
| } |
| |
| void generateVarLinkFragmentShaderSrc(std::string &outFragSrc, glu::GLSLVersion glslVersion, |
| const string &precision, TestType testMode) |
| { |
| std::ostringstream fragSrc; |
| fragSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| switch (testMode) |
| { |
| case SET_DEFAULT_PRECISION: |
| fragSrc << "precision " << precision << " int;\n"; |
| // Fallthrough |
| case DEFAULT_PRECISION: |
| fragSrc << "flat in int var;\n"; |
| break; |
| case SET_PRECISION: |
| fragSrc << "flat in " << precision << " int var;\n"; |
| break; |
| } |
| fragSrc << "layout(location = 0) out mediump vec4 o_color;\n"; |
| fragSrc << "void main (void)\n"; |
| fragSrc << "{\n"; |
| fragSrc << " o_color = vec4(var);\n"; |
| fragSrc << "}\n"; |
| outFragSrc = fragSrc.str(); |
| } |
| }; |
| |
| class InterfaceMatchingCaseUInt : public InterfaceMatchingCase |
| { |
| public: |
| InterfaceMatchingCaseUInt(Context &context, const char *name, glu::GLSLVersion glslVersion) |
| : InterfaceMatchingCase(context, name, glslVersion) |
| { |
| } |
| |
| std::string getDefaultFragmentPrecision() |
| { |
| return "mediump"; |
| } |
| |
| void generateVarLinkVertexShaderSrc(std::string &outVtxSrc, glu::GLSLVersion glslVersion, const string &precision, |
| TestType testMode) |
| { |
| std::ostringstream vtxSrc; |
| vtxSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| if (glslVersion >= glu::GLSL_VERSION_410) |
| { |
| vtxSrc << "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n"; |
| } |
| vtxSrc << "in highp vec4 a_position;\n"; |
| vtxSrc << "uniform highp vec4 u_color;\n"; |
| switch (testMode) |
| { |
| case SET_DEFAULT_PRECISION: |
| vtxSrc << "precision " << precision << " int;\n"; |
| // Fallthrough |
| case DEFAULT_PRECISION: |
| vtxSrc << "flat out uint var;\n"; |
| break; |
| case SET_PRECISION: |
| vtxSrc << "flat out " << precision << " uint var;\n"; |
| break; |
| } |
| vtxSrc << "void main (void)\n"; |
| vtxSrc << "{\n"; |
| vtxSrc << " gl_Position = a_position;\n"; |
| vtxSrc << " var = uint(u_color.r);\n"; |
| vtxSrc << "}\n"; |
| outVtxSrc = vtxSrc.str(); |
| } |
| |
| void generateVarLinkFragmentShaderSrc(std::string &outFragSrc, glu::GLSLVersion glslVersion, |
| const string &precision, TestType testMode) |
| { |
| std::ostringstream fragSrc; |
| fragSrc << glu::getGLSLVersionDeclaration(glslVersion) << "\n"; |
| switch (testMode) |
| { |
| case SET_DEFAULT_PRECISION: |
| fragSrc << "precision " << precision << " int;\n"; |
| // Fallthrough |
| case DEFAULT_PRECISION: |
| fragSrc << "flat in uint var;\n"; |
| break; |
| case SET_PRECISION: |
| fragSrc << "flat in " << precision << " uint var;\n"; |
| break; |
| } |
| fragSrc << "layout(location = 0) out mediump vec4 o_color;\n"; |
| fragSrc << "void main (void)\n"; |
| fragSrc << "{\n"; |
| fragSrc << " o_color = vec4(var);\n"; |
| fragSrc << "}\n"; |
| outFragSrc = fragSrc.str(); |
| } |
| }; |
| |
| SeparateShaderObjsTests::SeparateShaderObjsTests(Context &context, glu::GLSLVersion glslVersion) |
| : TestCaseGroup(context, "sepshaderobjs", "separate_shader_object tests") |
| , m_glslVersion(glslVersion) |
| { |
| DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion >= glu::GLSL_VERSION_440); |
| } |
| |
| SeparateShaderObjsTests::~SeparateShaderObjsTests(void) |
| { |
| } |
| |
| void SeparateShaderObjsTests::init(void) |
| { |
| |
| // API validation for CreateShaderProgram |
| addChild(new CreateShadProgCase(m_context, "CreateShadProgApi", "createShaderProgram API", m_glslVersion)); |
| // API validation for UseProgramStages |
| addChild(new UseProgStagesCase(m_context, "UseProgStagesApi", "useProgramStages API", m_glslVersion)); |
| // API validation for pipeline related functions |
| addChild(new PipelineApiCase(m_context, "PipelineApi", "Pipeline API", m_glslVersion)); |
| // API validation for variations of ProgramUniform |
| addChild(new ProgramUniformCase(m_context, "ProgUniformAPI", "ProgramUniform API", m_glslVersion)); |
| // State interactions |
| addChild(new StateInteractionCase(m_context, "StateInteraction", "SSO State Interactions", m_glslVersion)); |
| // input / output precision matching |
| addChild(new InterfaceMatchingCaseFloat(m_context, "InterfacePrecisionMatchingFloat", m_glslVersion)); |
| addChild(new InterfaceMatchingCaseInt(m_context, "InterfacePrecisionMatchingInt", m_glslVersion)); |
| addChild(new InterfaceMatchingCaseUInt(m_context, "InterfacePrecisionMatchingUInt", m_glslVersion)); |
| } |
| |
| } // namespace glcts |