| // |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class GLSLTest : public ANGLETest |
| { |
| protected: |
| GLSLTest() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| std::string GenerateVaryingType(GLint vectorSize) |
| { |
| char varyingType[10]; |
| |
| if (vectorSize == 1) |
| { |
| sprintf(varyingType, "float"); |
| } |
| else |
| { |
| sprintf(varyingType, "vec%d", vectorSize); |
| } |
| |
| return std::string(varyingType); |
| } |
| |
| std::string GenerateVectorVaryingDeclaration(GLint vectorSize, GLint arraySize, GLint id) |
| { |
| char buff[100]; |
| |
| if (arraySize == 1) |
| { |
| sprintf(buff, "varying %s v%d;\n", GenerateVaryingType(vectorSize).c_str(), id); |
| } |
| else |
| { |
| sprintf(buff, "varying %s v%d[%d];\n", GenerateVaryingType(vectorSize).c_str(), id, |
| arraySize); |
| } |
| |
| return std::string(buff); |
| } |
| |
| std::string GenerateVectorVaryingSettingCode(GLint vectorSize, GLint arraySize, GLint id) |
| { |
| std::string returnString; |
| char buff[100]; |
| |
| if (arraySize == 1) |
| { |
| sprintf(buff, "\t v%d = %s(1.0);\n", id, GenerateVaryingType(vectorSize).c_str()); |
| returnString += buff; |
| } |
| else |
| { |
| for (int i = 0; i < arraySize; i++) |
| { |
| sprintf(buff, "\t v%d[%d] = %s(1.0);\n", id, i, |
| GenerateVaryingType(vectorSize).c_str()); |
| returnString += buff; |
| } |
| } |
| |
| return returnString; |
| } |
| |
| std::string GenerateVectorVaryingUseCode(GLint arraySize, GLint id) |
| { |
| if (arraySize == 1) |
| { |
| char buff[100]; |
| sprintf(buff, "v%d + ", id); |
| return std::string(buff); |
| } |
| else |
| { |
| std::string returnString; |
| for (int i = 0; i < arraySize; i++) |
| { |
| char buff[100]; |
| sprintf(buff, "v%d[%d] + ", id, i); |
| returnString += buff; |
| } |
| return returnString; |
| } |
| } |
| |
| void GenerateGLSLWithVaryings(GLint floatCount, |
| GLint floatArrayCount, |
| GLint vec2Count, |
| GLint vec2ArrayCount, |
| GLint vec3Count, |
| GLint vec3ArrayCount, |
| GLint vec4Count, |
| GLint vec4ArrayCount, |
| bool useFragCoord, |
| bool usePointCoord, |
| bool usePointSize, |
| std::string *fragmentShader, |
| std::string *vertexShader) |
| { |
| // Generate a string declaring the varyings, to share between the fragment shader and the |
| // vertex shader. |
| std::string varyingDeclaration; |
| |
| unsigned int varyingCount = 0; |
| |
| for (GLint i = 0; i < floatCount; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(1, 1, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < floatArrayCount; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(1, 2, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < vec2Count; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(2, 1, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < vec2ArrayCount; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(2, 2, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < vec3Count; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(3, 1, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < vec3ArrayCount; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(3, 2, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < vec4Count; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(4, 1, varyingCount); |
| varyingCount += 1; |
| } |
| |
| for (GLint i = 0; i < vec4ArrayCount; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(4, 2, varyingCount); |
| varyingCount += 1; |
| } |
| |
| // Generate the vertex shader |
| vertexShader->clear(); |
| vertexShader->append(varyingDeclaration); |
| vertexShader->append("\nvoid main()\n{\n"); |
| |
| unsigned int currentVSVarying = 0; |
| |
| for (GLint i = 0; i < floatCount; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(1, 1, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < floatArrayCount; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(1, 2, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec2Count; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(2, 1, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec2ArrayCount; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(2, 2, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec3Count; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(3, 1, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec3ArrayCount; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(3, 2, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec4Count; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(4, 1, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec4ArrayCount; i++) |
| { |
| vertexShader->append(GenerateVectorVaryingSettingCode(4, 2, currentVSVarying)); |
| currentVSVarying += 1; |
| } |
| |
| if (usePointSize) |
| { |
| vertexShader->append("gl_PointSize = 1.0;\n"); |
| } |
| |
| vertexShader->append("}\n"); |
| |
| // Generate the fragment shader |
| fragmentShader->clear(); |
| fragmentShader->append("precision highp float;\n"); |
| fragmentShader->append(varyingDeclaration); |
| fragmentShader->append("\nvoid main() \n{ \n\tvec4 retColor = vec4(0,0,0,0);\n"); |
| |
| unsigned int currentFSVarying = 0; |
| |
| // Make use of the float varyings |
| fragmentShader->append("\tretColor += vec4("); |
| |
| for (GLint i = 0; i < floatCount; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < floatArrayCount; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| fragmentShader->append("0.0, 0.0, 0.0, 0.0);\n"); |
| |
| // Make use of the vec2 varyings |
| fragmentShader->append("\tretColor += vec4("); |
| |
| for (GLint i = 0; i < vec2Count; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec2ArrayCount; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| fragmentShader->append("vec2(0.0, 0.0), 0.0, 0.0);\n"); |
| |
| // Make use of the vec3 varyings |
| fragmentShader->append("\tretColor += vec4("); |
| |
| for (GLint i = 0; i < vec3Count; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec3ArrayCount; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| fragmentShader->append("vec3(0.0, 0.0, 0.0), 0.0);\n"); |
| |
| // Make use of the vec4 varyings |
| fragmentShader->append("\tretColor += "); |
| |
| for (GLint i = 0; i < vec4Count; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| for (GLint i = 0; i < vec4ArrayCount; i++) |
| { |
| fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying)); |
| currentFSVarying += 1; |
| } |
| |
| fragmentShader->append("vec4(0.0, 0.0, 0.0, 0.0);\n"); |
| |
| // Set gl_FragColor, and use special variables if requested |
| fragmentShader->append("\tgl_FragColor = retColor"); |
| |
| if (useFragCoord) |
| { |
| fragmentShader->append(" + gl_FragCoord"); |
| } |
| |
| if (usePointCoord) |
| { |
| fragmentShader->append(" + vec4(gl_PointCoord, 0.0, 0.0)"); |
| } |
| |
| fragmentShader->append(";\n}"); |
| } |
| |
| void VaryingTestBase(GLint floatCount, |
| GLint floatArrayCount, |
| GLint vec2Count, |
| GLint vec2ArrayCount, |
| GLint vec3Count, |
| GLint vec3ArrayCount, |
| GLint vec4Count, |
| GLint vec4ArrayCount, |
| bool useFragCoord, |
| bool usePointCoord, |
| bool usePointSize, |
| bool expectSuccess) |
| { |
| std::string fragmentShaderSource; |
| std::string vertexShaderSource; |
| |
| GenerateGLSLWithVaryings(floatCount, floatArrayCount, vec2Count, vec2ArrayCount, vec3Count, |
| vec3ArrayCount, vec4Count, vec4ArrayCount, useFragCoord, |
| usePointCoord, usePointSize, &fragmentShaderSource, |
| &vertexShaderSource); |
| |
| GLuint program = CompileProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str()); |
| |
| if (expectSuccess) |
| { |
| EXPECT_NE(0u, program); |
| } |
| else |
| { |
| EXPECT_EQ(0u, program); |
| } |
| } |
| |
| void CompileGLSLWithUniformsAndSamplers(GLint vertexUniformCount, |
| GLint fragmentUniformCount, |
| GLint vertexSamplersCount, |
| GLint fragmentSamplersCount, |
| bool expectSuccess) |
| { |
| std::stringstream vertexShader; |
| std::stringstream fragmentShader; |
| |
| // Generate the vertex shader |
| vertexShader << "precision mediump float;\n"; |
| |
| for (int i = 0; i < vertexUniformCount; i++) |
| { |
| vertexShader << "uniform vec4 v" << i << ";\n"; |
| } |
| |
| for (int i = 0; i < vertexSamplersCount; i++) |
| { |
| vertexShader << "uniform sampler2D s" << i << ";\n"; |
| } |
| |
| vertexShader << "void main()\n{\n"; |
| |
| for (int i = 0; i < vertexUniformCount; i++) |
| { |
| vertexShader << " gl_Position += v" << i << ";\n"; |
| } |
| |
| for (int i = 0; i < vertexSamplersCount; i++) |
| { |
| vertexShader << " gl_Position += texture2D(s" << i << ", vec2(0.0, 0.0));\n"; |
| } |
| |
| if (vertexUniformCount == 0 && vertexSamplersCount == 0) |
| { |
| vertexShader << " gl_Position = vec4(0.0);\n"; |
| } |
| |
| vertexShader << "}\n"; |
| |
| // Generate the fragment shader |
| fragmentShader << "precision mediump float;\n"; |
| |
| for (int i = 0; i < fragmentUniformCount; i++) |
| { |
| fragmentShader << "uniform vec4 v" << i << ";\n"; |
| } |
| |
| for (int i = 0; i < fragmentSamplersCount; i++) |
| { |
| fragmentShader << "uniform sampler2D s" << i << ";\n"; |
| } |
| |
| fragmentShader << "void main()\n{\n"; |
| |
| for (int i = 0; i < fragmentUniformCount; i++) |
| { |
| fragmentShader << " gl_FragColor += v" << i << ";\n"; |
| } |
| |
| for (int i = 0; i < fragmentSamplersCount; i++) |
| { |
| fragmentShader << " gl_FragColor += texture2D(s" << i << ", vec2(0.0, 0.0));\n"; |
| } |
| |
| if (fragmentUniformCount == 0 && fragmentSamplersCount == 0) |
| { |
| fragmentShader << " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"; |
| } |
| |
| fragmentShader << "}\n"; |
| |
| GLuint program = CompileProgram(vertexShader.str().c_str(), fragmentShader.str().c_str()); |
| |
| if (expectSuccess) |
| { |
| EXPECT_NE(0u, program); |
| } |
| else |
| { |
| EXPECT_EQ(0u, program); |
| } |
| } |
| |
| std::string QueryErrorMessage(GLuint program) |
| { |
| GLint infoLogLength; |
| glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); |
| EXPECT_GL_NO_ERROR(); |
| |
| if (infoLogLength >= 1) |
| { |
| std::vector<GLchar> infoLog(infoLogLength); |
| glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr, |
| infoLog.data()); |
| EXPECT_GL_NO_ERROR(); |
| return infoLog.data(); |
| } |
| |
| return ""; |
| } |
| |
| void validateComponentsInErrorMessage(const char *vertexShader, |
| const char *fragmentShader, |
| const char *expectedErrorType, |
| const char *expectedVariableFullName) |
| { |
| GLuint vs = CompileShader(GL_VERTEX_SHADER, vertexShader); |
| GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader); |
| |
| GLuint program = glCreateProgram(); |
| glAttachShader(program, vs); |
| glAttachShader(program, fs); |
| glLinkProgram(program); |
| |
| glDetachShader(program, vs); |
| glDetachShader(program, fs); |
| glDeleteShader(vs); |
| glDeleteShader(fs); |
| |
| const std::string &errorMessage = QueryErrorMessage(program); |
| printf("%s\n", errorMessage.c_str()); |
| |
| EXPECT_NE(std::string::npos, errorMessage.find(expectedErrorType)); |
| EXPECT_NE(std::string::npos, errorMessage.find(expectedVariableFullName)); |
| |
| glDeleteProgram(program); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void verifyAttachment2DColor(unsigned int index, |
| GLuint textureName, |
| GLenum target, |
| GLint level, |
| GLColor color) |
| { |
| glReadBuffer(GL_COLOR_ATTACHMENT0 + index); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, color) |
| << "index " << index; |
| } |
| }; |
| |
| class GLSLTestNoValidation : public GLSLTest |
| { |
| public: |
| GLSLTestNoValidation() { setNoErrorEnabled(true); } |
| }; |
| |
| class GLSLTest_ES3 : public GLSLTest |
| {}; |
| |
| class GLSLTest_ES31 : public GLSLTest |
| {}; |
| |
| std::string BuildBigInitialStackShader(int length) |
| { |
| std::string result; |
| result += "void main() { \n"; |
| for (int i = 0; i < length; i++) |
| { |
| result += " if (true) { \n"; |
| } |
| result += " int temp; \n"; |
| for (int i = 0; i <= length; i++) |
| { |
| result += "} \n"; |
| } |
| return result; |
| } |
| |
| TEST_P(GLSLTest, NamelessScopedStructs) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| void main() |
| { |
| struct |
| { |
| float q; |
| } b; |
| |
| gl_FragColor = vec4(1, 0, 0, 1); |
| gl_FragColor.a += b.q; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that array of fragment shader outputs is processed properly and draws |
| // E.g. was issue with "out vec4 frag_color[4];" |
| TEST_P(GLSLTest_ES3, FragmentShaderOutputArray) |
| { |
| GLuint fbo; |
| glGenFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); |
| |
| GLuint textures[4]; |
| glGenTextures(4, textures); |
| |
| for (size_t texIndex = 0; texIndex < ArraySize(textures); texIndex++) |
| { |
| glBindTexture(GL_TEXTURE_2D, textures[texIndex]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| } |
| |
| GLint maxDrawBuffers; |
| glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); |
| ASSERT_GE(maxDrawBuffers, 4); |
| |
| GLuint readFramebuffer; |
| glGenFramebuffers(1, &readFramebuffer); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, readFramebuffer); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| |
| out vec4 frag_color[4]; |
| |
| void main() |
| { |
| frag_color[0] = vec4(1.0, 0.0, 0.0, 1.0); |
| frag_color[1] = vec4(0.0, 1.0, 0.0, 1.0); |
| frag_color[2] = vec4(0.0, 0.0, 1.0, 1.0); |
| frag_color[3] = vec4(1.0, 1.0, 1.0, 1.0); |
| } |
| )"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| GLenum allBufs[4] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, |
| GL_COLOR_ATTACHMENT3}; |
| |
| constexpr GLuint kMaxBuffers = 4; |
| |
| // Enable all draw buffers. |
| for (GLuint texIndex = 0; texIndex < kMaxBuffers; texIndex++) |
| { |
| glBindTexture(GL_TEXTURE_2D, textures[texIndex]); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + texIndex, GL_TEXTURE_2D, |
| textures[texIndex], 0); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + texIndex, GL_TEXTURE_2D, |
| textures[texIndex], 0); |
| } |
| glDrawBuffers(kMaxBuffers, allBufs); |
| |
| // Draw with simple program. |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| |
| verifyAttachment2DColor(0, textures[0], GL_TEXTURE_2D, 0, GLColor::red); |
| verifyAttachment2DColor(1, textures[1], GL_TEXTURE_2D, 0, GLColor::green); |
| verifyAttachment2DColor(2, textures[2], GL_TEXTURE_2D, 0, GLColor::blue); |
| verifyAttachment2DColor(3, textures[3], GL_TEXTURE_2D, 0, GLColor::white); |
| } |
| |
| // Test that inactive fragment shader outputs don't cause a crash. |
| TEST_P(GLSLTest_ES3, InactiveFragmentShaderOutput) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| |
| // Make color0 inactive but specify color1 first. The Vulkan backend assigns bogus locations when |
| // compiling and fixes it up in SPIR-V. If color0's location is not fixed, it will return location |
| // 1 (aliasing color1). This will lead to a Vulkan validation warning about attachment 0 not being |
| // written to, which shouldn't be fatal. |
| layout(location = 1) out vec4 color1; |
| layout(location = 0) out vec4 color0; |
| |
| void main() |
| { |
| color1 = vec4(0.0, 1.0, 0.0, 1.0); |
| } |
| )"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| constexpr GLint kDrawBufferCount = 2; |
| |
| GLint maxDrawBuffers; |
| glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); |
| ASSERT_GE(maxDrawBuffers, kDrawBufferCount); |
| |
| GLTexture textures[kDrawBufferCount]; |
| |
| for (GLint texIndex = 0; texIndex < kDrawBufferCount; ++texIndex) |
| { |
| glBindTexture(GL_TEXTURE_2D, textures[texIndex]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| } |
| |
| GLenum allBufs[kDrawBufferCount] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); |
| |
| // Enable all draw buffers. |
| for (GLint texIndex = 0; texIndex < kDrawBufferCount; ++texIndex) |
| { |
| glBindTexture(GL_TEXTURE_2D, textures[texIndex]); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + texIndex, GL_TEXTURE_2D, |
| textures[texIndex], 0); |
| } |
| glDrawBuffers(kDrawBufferCount, allBufs); |
| |
| // Draw with simple program. |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| TEST_P(GLSLTest, ScopedStructsOrderBug) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| struct T |
| { |
| float f; |
| }; |
| |
| void main() |
| { |
| T a; |
| |
| struct T |
| { |
| float q; |
| }; |
| |
| T b; |
| |
| gl_FragColor = vec4(1, 0, 0, 1); |
| gl_FragColor.a += a.f; |
| gl_FragColor.a += b.q; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that defining a struct together with an inactive uniform, then using it in a scope that has |
| // another struct with the same name declared works. |
| TEST_P(GLSLTest, ScopedStructsOrderBug2) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| uniform struct T |
| { |
| float f; |
| } x; |
| |
| void main() |
| { |
| T a; |
| |
| struct T |
| { |
| float q; |
| }; |
| |
| T b; |
| |
| gl_FragColor = vec4(1, 0, 0, 1); |
| gl_FragColor.a += a.f; |
| gl_FragColor.a += b.q; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| } |
| |
| // Regression test based on WebGL's conformance/glsl/misc/empty-declaration.html |
| TEST_P(GLSLTest, StructEmptyDeclaratorBug) |
| { |
| constexpr char kVS[] = R"( |
| struct S { |
| float member; |
| }, a; |
| void main() { |
| a.member = 0.0; |
| gl_Position = vec4(a.member); |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| precision mediump float; |
| void main() |
| { |
| gl_FragColor = vec4(1.0,0.0,0.0,1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Regression test based on WebGL's conformance/ogles/GL/build/build_001_to_008.html |
| TEST_P(GLSLTest, StructConstantFoldingBug) |
| { |
| constexpr char kVS[] = R"( |
| void main() |
| { |
| |
| const struct s2 { |
| int i; |
| vec3 v3; |
| bvec4 bv4; |
| } s22 = s2(8, vec3(9, 10, 11), bvec4(true, false, true, false)); |
| |
| struct s4 { |
| int ii; |
| vec4 v4; |
| }; |
| |
| const struct s1 { |
| s2 ss; |
| int i; |
| float f; |
| mat4 m; |
| s4 s44; |
| } s11 = s1(s22, 2, 4.0, mat4(5), s4(6, vec4(7, 8, 9, 10))) ; |
| |
| const int field3 = s11.i * s11.ss.i; // constant folding (int * int) |
| const vec4 field4 = s11.s44.v4 * s11.s44.v4; // constant folding (vec4 * vec4) |
| // 49, 64, 81, 100 |
| const vec4 v4 = vec4(s11.ss.v3.y, s11.m[3][3], field3, field4[2]); // 10.0, 5.0, 16.0, 81.0 |
| gl_Position = v4; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| precision mediump float; |
| void main() |
| { |
| gl_FragColor = vec4(1.0,0.0,0.0,1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that constant folding doesn't remove struct declaration. |
| TEST_P(GLSLTest, StructConstantFoldingBug2) |
| { |
| constexpr char kVS[] = R"( |
| uniform vec4 u; |
| |
| void main() |
| { |
| |
| const struct s2 { |
| int i; |
| vec3 v3; |
| bvec4 bv4; |
| } s22 = s2(8, vec3(9, 10, 11), bvec4(true, false, true, false)); |
| |
| s2 x; |
| x.v3 = u.xyz; |
| |
| gl_Position = vec4(x.v3, float(s22.i)); |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| precision mediump float; |
| void main() |
| { |
| gl_FragColor = vec4(1.0,0.0,0.0,1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| TEST_P(GLSLTest, ScopedStructsBug) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| struct T_0 |
| { |
| float f; |
| }; |
| |
| void main() |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| |
| struct T |
| { |
| vec2 v; |
| }; |
| |
| T_0 a; |
| T b; |
| |
| gl_FragColor.a += a.f; |
| gl_FragColor.a += b.v.x; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| } |
| |
| TEST_P(GLSLTest, DxPositionBug) |
| { |
| constexpr char kVS[] = R"(attribute vec4 inputAttribute; |
| varying float dx_Position; |
| void main() |
| { |
| gl_Position = vec4(inputAttribute); |
| dx_Position = 0.0; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| varying float dx_Position; |
| |
| void main() |
| { |
| gl_FragColor = vec4(dx_Position, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Draw an array of points with the first vertex offset at 0 using gl_VertexID |
| TEST_P(GLSLTest_ES3, GLVertexIDOffsetZeroDrawArray) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(isSwiftshader()); |
| constexpr int kStartIndex = 0; |
| constexpr int kArrayLength = 5; |
| constexpr char kVS[] = R"(#version 300 es |
| precision highp float; |
| void main() { |
| gl_Position = vec4(float(gl_VertexID)/10.0, 0, 0, 1); |
| gl_PointSize = 3.0; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| void main() { |
| outColor = vec4(1.0, 0.0, 0.0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glUseProgram(program); |
| glDrawArrays(GL_POINTS, kStartIndex, kArrayLength); |
| |
| double pointCenterX = static_cast<double>(getWindowWidth()) / 2.0; |
| double pointCenterY = static_cast<double>(getWindowHeight()) / 2.0; |
| for (int i = kStartIndex; i < kStartIndex + kArrayLength; i++) |
| { |
| double pointOffsetX = static_cast<double>(i * getWindowWidth()) / 20.0; |
| EXPECT_PIXEL_COLOR_EQ(static_cast<int>(pointCenterX + pointOffsetX), |
| static_cast<int>(pointCenterY), GLColor::red); |
| } |
| } |
| |
| // Helper function for the GLVertexIDIntegerTextureDrawArrays test |
| void GLVertexIDIntegerTextureDrawArrays_helper(int first, int count, GLenum err) |
| { |
| glDrawArrays(GL_POINTS, first, count); |
| |
| int pixel[4]; |
| glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, pixel); |
| // If we call this function with err as GL_NO_ERROR, then we expect no error and check the |
| // pixels. |
| if (err == static_cast<GLenum>(GL_NO_ERROR)) |
| { |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(pixel[0], first + count - 1); |
| } |
| else |
| { |
| // If we call this function with err set, we will allow the error, but check the pixels if |
| // the error hasn't occurred. |
| GLenum glError = glGetError(); |
| if (glError == err || glError == static_cast<GLenum>(GL_NO_ERROR)) |
| { |
| EXPECT_EQ(pixel[0], first + count - 1); |
| } |
| } |
| } |
| |
| // Ensure gl_VertexID gets passed to an integer texture properly when drawArrays is called. This |
| // is based off the WebGL test: |
| // https://github.com/KhronosGroup/WebGL/blob/master/sdk/tests/conformance2/rendering/vertex-id.html |
| TEST_P(GLSLTest_ES3, GLVertexIDIntegerTextureDrawArrays) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(isSwiftshader()); |
| // http://anglebug.com/5232 |
| ANGLE_SKIP_TEST_IF(IsMetal()); |
| // TODO(anglebug.com/5360): Failing on ARM-based Apple DTKs. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsDesktopOpenGL()); |
| // TODO(anglebug.com/5491): Failing on iOS, probably related to the ARM Mac failure above. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| // Have to set a large point size because the window size is much larger than the texture |
| constexpr char kVS[] = R"(#version 300 es |
| flat out highp int vVertexID; |
| void main() { |
| vVertexID = gl_VertexID; |
| gl_Position = vec4(0,0,0,1); |
| gl_PointSize = 1000.0; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| flat in highp int vVertexID; |
| out highp int oVertexID; |
| void main() { |
| oVertexID = vVertexID; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32I, 1, 1); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Clear the texture to 42 to ensure the first test case doesn't accidentally pass |
| GLint val[4] = {42}; |
| glClearBufferiv(GL_COLOR, 0, val); |
| int pixel[4]; |
| glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, pixel); |
| EXPECT_EQ(pixel[0], val[0]); |
| |
| GLVertexIDIntegerTextureDrawArrays_helper(0, 1, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(1, 1, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(10000, 1, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(100000, 1, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(1000000, 1, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(0, 2, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(1, 2, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(10000, 2, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(100000, 2, GL_NO_ERROR); |
| GLVertexIDIntegerTextureDrawArrays_helper(1000000, 2, GL_NO_ERROR); |
| |
| int32_t int32Max = 0x7FFFFFFF; |
| GLVertexIDIntegerTextureDrawArrays_helper(int32Max - 2, 1, GL_OUT_OF_MEMORY); |
| GLVertexIDIntegerTextureDrawArrays_helper(int32Max - 1, 1, GL_OUT_OF_MEMORY); |
| GLVertexIDIntegerTextureDrawArrays_helper(int32Max, 1, GL_OUT_OF_MEMORY); |
| } |
| |
| // Draw an array of points with the first vertex offset at 5 using gl_VertexID |
| TEST_P(GLSLTest_ES3, GLVertexIDOffsetFiveDrawArray) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(isSwiftshader()); |
| // Bug in Nexus drivers, offset does not work. (anglebug.com/3264) |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsOpenGLES()); |
| |
| constexpr int kStartIndex = 5; |
| constexpr int kArrayLength = 5; |
| constexpr char kVS[] = R"(#version 300 es |
| precision highp float; |
| void main() { |
| gl_Position = vec4(float(gl_VertexID)/10.0, 0, 0, 1); |
| gl_PointSize = 3.0; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| void main() { |
| outColor = vec4(1.0, 0.0, 0.0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glUseProgram(program); |
| glDrawArrays(GL_POINTS, kStartIndex, kArrayLength); |
| |
| double pointCenterX = static_cast<double>(getWindowWidth()) / 2.0; |
| double pointCenterY = static_cast<double>(getWindowHeight()) / 2.0; |
| for (int i = kStartIndex; i < kStartIndex + kArrayLength; i++) |
| { |
| double pointOffsetX = static_cast<double>(i * getWindowWidth()) / 20.0; |
| EXPECT_PIXEL_COLOR_EQ(static_cast<int>(pointCenterX + pointOffsetX), |
| static_cast<int>(pointCenterY), GLColor::red); |
| } |
| } |
| |
| TEST_P(GLSLTest, ElseIfRewriting) |
| { |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "varying float v;\n" |
| "void main() {\n" |
| " gl_Position = a_position;\n" |
| " v = 1.0;\n" |
| " if (a_position.x <= 0.5) {\n" |
| " v = 0.0;\n" |
| " } else if (a_position.x >= 0.5) {\n" |
| " v = 2.0;\n" |
| " }\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "precision highp float;\n" |
| "varying float v;\n" |
| "void main() {\n" |
| " vec4 color = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| " if (v >= 1.0) color = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " if (v >= 2.0) color = vec4(0.0, 0.0, 1.0, 1.0);\n" |
| " gl_FragColor = color;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| drawQuad(program, "a_position", 0.5f); |
| |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| EXPECT_PIXEL_EQ(getWindowWidth() - 1, 0, 0, 255, 0, 255); |
| } |
| |
| TEST_P(GLSLTest, TwoElseIfRewriting) |
| { |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "varying float v;\n" |
| "void main() {\n" |
| " gl_Position = a_position;\n" |
| " if (a_position.x == 0.0) {\n" |
| " v = 1.0;\n" |
| " } else if (a_position.x > 0.5) {\n" |
| " v = 0.0;\n" |
| " } else if (a_position.x > 0.75) {\n" |
| " v = 0.5;\n" |
| " }\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "precision highp float;\n" |
| "varying float v;\n" |
| "void main() {\n" |
| " gl_FragColor = vec4(v, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| TEST_P(GLSLTest, FrontFacingAndVarying) |
| { |
| EGLPlatformParameters platform = GetParam().eglParameters; |
| |
| constexpr char kVS[] = R"(attribute vec4 a_position; |
| varying float v_varying; |
| void main() |
| { |
| v_varying = a_position.x; |
| gl_Position = a_position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying float v_varying; |
| void main() |
| { |
| vec4 c; |
| |
| if (gl_FrontFacing) |
| { |
| c = vec4(v_varying, 0, 0, 1.0); |
| } |
| else |
| { |
| c = vec4(0, v_varying, 0, 1.0); |
| } |
| gl_FragColor = c; |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| |
| // Compilation should fail on D3D11 feature level 9_3, since gl_FrontFacing isn't supported. |
| if (platform.renderer == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) |
| { |
| if (platform.majorVersion == 9 && platform.minorVersion == 3) |
| { |
| EXPECT_EQ(0u, program); |
| return; |
| } |
| } |
| |
| // Otherwise, compilation should succeed |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that we can release the shader compiler and still compile things properly. |
| TEST_P(GLSLTest, ReleaseCompilerThenCompile) |
| { |
| // Draw with the first program. |
| ANGLE_GL_PROGRAM(program1, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program1, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Clear and release shader compiler. |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| glReleaseShaderCompiler(); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw with a second program. |
| ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program2, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Verify that linking shaders declaring different shading language versions fails. |
| TEST_P(GLSLTest_ES3, VersionMismatch) |
| { |
| GLuint program = CompileProgram(essl3_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| program = CompileProgram(essl1_shaders::vs::Simple(), essl3_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that declaring varying as invariant only in vertex shader fails in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantVaryingOut) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "invariant varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that declaring varying as invariant only in vertex shader succeeds in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantVaryingOut) |
| { |
| // TODO: ESSL 3.00 -> GLSL 1.20 translation should add "invariant" in fragment shader |
| // for varyings which are invariant in vertex shader (http://anglebug.com/1293) |
| ANGLE_SKIP_TEST_IF(IsDesktopOpenGL()); |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 a_position;\n" |
| "invariant out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that declaring varying as invariant only in fragment shader fails in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantVaryingIn) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "invariant varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that declaring varying as invariant only in fragment shader fails in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantVaryingIn) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "invariant in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 a_position;\n" |
| "out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that declaring varying as invariant in both shaders succeeds in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantVaryingBoth) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "invariant varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "invariant varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that declaring varying as invariant in both shaders fails in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantVaryingBoth) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "invariant in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 a_position;\n" |
| "invariant out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that declaring gl_Position as invariant succeeds in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantGLPosition) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "invariant gl_Position;\n" |
| "varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that declaring gl_Position as invariant succeeds in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantGLPosition) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 a_position;\n" |
| "invariant gl_Position;\n" |
| "out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that using invariant(all) in both shaders fails in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantAllBoth) |
| { |
| constexpr char kFS[] = |
| "#pragma STDGL invariant(all)\n" |
| "precision mediump float;\n" |
| "varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#pragma STDGL invariant(all)\n" |
| "attribute vec4 a_position;\n" |
| "varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that using a struct as both invariant and non-invariant output works. |
| TEST_P(GLSLTest_ES31, StructBothInvariantAndNot) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| struct S |
| { |
| vec4 s; |
| }; |
| |
| out Output |
| { |
| vec4 x; |
| invariant S s; |
| }; |
| |
| out S s2; |
| |
| void main(){ |
| x = vec4(0); |
| s.s = vec4(1); |
| s2.s = vec4(2); |
| S s3 = s; |
| s.s = s3.s; |
| })"; |
| |
| GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS); |
| EXPECT_NE(0u, shader); |
| glDeleteShader(shader); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnFloat) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "float f() { if (v_varying > 0.0) return 1.0; }\n" |
| "void main() { gl_Position = vec4(f(), 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnVec2) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "vec2 f() { if (v_varying > 0.0) return vec2(1.0, 1.0); }\n" |
| "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnVec3) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "vec3 f() { if (v_varying > 0.0) return vec3(1.0, 1.0, 1.0); }\n" |
| "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnVec4) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "vec4 f() { if (v_varying > 0.0) return vec4(1.0, 1.0, 1.0, 1.0); }\n" |
| "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnIVec4) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "ivec4 f() { if (v_varying > 0.0) return ivec4(1, 1, 1, 1); }\n" |
| "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnMat4) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "mat4 f() { if (v_varying > 0.0) return mat4(1.0); }\n" |
| "void main() { gl_Position = vec4(f()[0][0], 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest, MissingReturnStruct) |
| { |
| constexpr char kVS[] = |
| "varying float v_varying;\n" |
| "struct s { float a; int b; vec2 c; };\n" |
| "s f() { if (v_varying > 0.0) return s(1.0, 1, vec2(1.0, 1.0)); }\n" |
| "void main() { gl_Position = vec4(f().a, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest_ES3, MissingReturnArray) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in float v_varying;\n" |
| "vec2[2] f() { if (v_varying > 0.0) { return vec2[2](vec2(1.0, 1.0), vec2(1.0, 1.0)); } }\n" |
| "void main() { gl_Position = vec4(f()[0].x, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest_ES3, MissingReturnArrayOfStructs) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in float v_varying;\n" |
| "struct s { float a; int b; vec2 c; };\n" |
| "s[2] f() { if (v_varying > 0.0) { return s[2](s(1.0, 1, vec2(1.0, 1.0)), s(1.0, 1, " |
| "vec2(1.0, 1.0))); } }\n" |
| "void main() { gl_Position = vec4(f()[0].a, 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that functions without return statements still compile |
| TEST_P(GLSLTest_ES3, MissingReturnStructOfArrays) |
| { |
| // TODO(crbug.com/998505): Test failing on Android FYI Release (NVIDIA Shield TV) |
| ANGLE_SKIP_TEST_IF(IsNVIDIAShield()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in float v_varying;\n" |
| "struct s { float a[2]; int b[2]; vec2 c[2]; };\n" |
| "s f() { if (v_varying > 0.0) { return s(float[2](1.0, 1.0), int[2](1, 1)," |
| "vec2[2](vec2(1.0, 1.0), vec2(1.0, 1.0))); } }\n" |
| "void main() { gl_Position = vec4(f().a[0], 0, 0, 1); }\n"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that non-const index used on an array returned by a function compiles |
| TEST_P(GLSLTest_ES3, ReturnArrayOfStructsThenNonConstIndex) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| in float v_varying; |
| struct s { float a; int b; vec2 c; }; |
| s[2] f() |
| { |
| return s[2](s(v_varying, 1, vec2(1.0, 1.0)), s(v_varying / 2.0, 1, vec2(1.0, 1.0))); |
| } |
| void main() |
| { |
| gl_Position = vec4(f()[uint(v_varying)].a, 0, 0, 1); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that using invariant(all) in both shaders fails in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantAllBoth) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "#pragma STDGL invariant(all)\n" |
| "precision mediump float;\n" |
| "in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "#pragma STDGL invariant(all)\n" |
| "in vec4 a_position;\n" |
| "out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that using invariant(all) only in fragment shader succeeds in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantAllIn) |
| { |
| constexpr char kFS[] = |
| "#pragma STDGL invariant(all)\n" |
| "precision mediump float;\n" |
| "varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "attribute vec4 a_position;\n" |
| "varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that using invariant(all) only in fragment shader fails in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantAllIn) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "#pragma STDGL invariant(all)\n" |
| "precision mediump float;\n" |
| "in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 a_position;\n" |
| "out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that using invariant(all) only in vertex shader fails in ESSL 1.00. |
| TEST_P(GLSLTest, InvariantAllOut) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying float v_varying;\n" |
| "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#pragma STDGL invariant(all)\n" |
| "attribute vec4 a_position;\n" |
| "varying float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that using invariant(all) only in vertex shader succeeds in ESSL 3.00. |
| TEST_P(GLSLTest_ES3, InvariantAllOut) |
| { |
| // TODO: ESSL 3.00 -> GLSL 1.20 translation should add "invariant" in fragment shader |
| // for varyings which are invariant in vertex shader, |
| // because of invariant(all) being used in vertex shader (http://anglebug.com/1293) |
| ANGLE_SKIP_TEST_IF(IsDesktopOpenGL()); |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "in float v_varying;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n"; |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "#pragma STDGL invariant(all)\n" |
| "in vec4 a_position;\n" |
| "out float v_varying;\n" |
| "void main() { v_varying = a_position.x; gl_Position = a_position; }\n"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| TEST_P(GLSLTest, MaxVaryingVec4) |
| { |
| // TODO(geofflang): Find out why this doesn't compile on Apple AMD OpenGL drivers |
| // (http://anglebug.com/1291) |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsAMD() && IsOpenGL()); |
| |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, false, false, false, true); |
| } |
| |
| // Verify we can pack registers with one builtin varying. |
| TEST_P(GLSLTest, MaxVaryingVec4_OneBuiltin) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Generate shader code that uses gl_FragCoord. |
| VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings - 1, 0, true, false, false, true); |
| } |
| |
| // Verify we can pack registers with two builtin varyings. |
| TEST_P(GLSLTest, MaxVaryingVec4_TwoBuiltins) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Generate shader code that uses gl_FragCoord and gl_PointCoord. |
| VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings - 2, 0, true, true, false, true); |
| } |
| |
| // Verify we can pack registers with three builtin varyings. |
| TEST_P(GLSLTest, MaxVaryingVec4_ThreeBuiltins) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Generate shader code that uses gl_FragCoord, gl_PointCoord and gl_PointSize. |
| VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings - 3, 0, true, true, true, true); |
| } |
| |
| // This covers a problematic case in D3D9 - we are limited by the number of available semantics, |
| // rather than total register use. |
| TEST_P(GLSLTest, MaxVaryingsSpecialCases) |
| { |
| ANGLE_SKIP_TEST_IF(!IsD3D9()); |
| |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(maxVaryings, 0, 0, 0, 0, 0, 0, 0, true, false, false, false); |
| VaryingTestBase(maxVaryings - 1, 0, 0, 0, 0, 0, 0, 0, true, true, false, false); |
| VaryingTestBase(maxVaryings - 2, 0, 0, 0, 0, 0, 0, 0, true, true, false, true); |
| |
| // Special case for gl_PointSize: we get it for free on D3D9. |
| VaryingTestBase(maxVaryings - 2, 0, 0, 0, 0, 0, 0, 0, true, true, true, true); |
| } |
| |
| // This covers a problematic case in D3D9 - we are limited by the number of available semantics, |
| // rather than total register use. |
| TEST_P(GLSLTest, MaxMinusTwoVaryingVec2PlusOneSpecialVariable) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Generate shader code that uses gl_FragCoord. |
| VaryingTestBase(0, 0, maxVaryings, 0, 0, 0, 0, 0, true, false, false, !IsD3D9()); |
| } |
| |
| TEST_P(GLSLTest, MaxVaryingVec3) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 0, 0, maxVaryings, 0, 0, 0, false, false, false, true); |
| } |
| |
| TEST_P(GLSLTest, MaxVaryingVec3Array) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 0, 0, 0, maxVaryings / 2, 0, 0, false, false, false, true); |
| } |
| |
| // Only fails on D3D9 because of packing limitations. |
| TEST_P(GLSLTest, MaxVaryingVec3AndOneFloat) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(1, 0, 0, 0, maxVaryings, 0, 0, 0, false, false, false, !IsD3D9()); |
| } |
| |
| // Only fails on D3D9 because of packing limitations. |
| TEST_P(GLSLTest, MaxVaryingVec3ArrayAndOneFloatArray) |
| { |
| // TODO(anglebug.com/5360): Failing on ARM-based Apple DTKs. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal()); |
| |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 1, 0, 0, 0, maxVaryings / 2, 0, 0, false, false, false, !IsD3D9()); |
| } |
| |
| // Only fails on D3D9 because of packing limitations. |
| TEST_P(GLSLTest, TwiceMaxVaryingVec2) |
| { |
| // TODO(geofflang): Figure out why this fails on NVIDIA's GLES driver |
| // (http://anglebug.com/3849) |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGLES()); |
| |
| // TODO(geofflang): Find out why this doesn't compile on Apple AMD OpenGL drivers |
| // (http://anglebug.com/1291) |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsAMD() && IsOpenGL()); |
| |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 2 * maxVaryings, 0, 0, 0, 0, 0, false, false, false, !IsD3D9()); |
| } |
| |
| // Disabled because of a failure in D3D9 |
| TEST_P(GLSLTest, MaxVaryingVec2Arrays) |
| { |
| ANGLE_SKIP_TEST_IF(IsD3DSM3()); |
| |
| // TODO(geofflang): Figure out why this fails on NVIDIA's GLES driver |
| ANGLE_SKIP_TEST_IF(IsOpenGLES()); |
| |
| // TODO(geofflang): Find out why this doesn't compile on Apple AMD OpenGL drivers |
| // (http://anglebug.com/1291) |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsAMD() && IsOpenGL()); |
| |
| // TODO(anglebug.com/5360): Failing on ARM-based Apple DTKs. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal()); |
| |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Special case: because arrays of mat2 are packed as small grids of two rows by two columns, |
| // we should be aware that when we're packing into an odd number of varying registers the |
| // last row will be empty and can not fit the final vec2 arrary. |
| GLint maxVec2Arrays = (maxVaryings >> 1) << 1; |
| |
| VaryingTestBase(0, 0, 0, maxVec2Arrays, 0, 0, 0, 0, false, false, false, true); |
| } |
| |
| // Verify max varying with feedback and gl_line enabled |
| TEST_P(GLSLTest_ES3, MaxVaryingWithFeedbackAndGLline) |
| { |
| // (http://anglebug.com/4439) |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsVulkan()); |
| |
| // http://anglebug.com/4446 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL()); |
| |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| std::stringstream vertexShaderSource; |
| std::stringstream fragmentShaderSource; |
| |
| // substract 1 here for gl_PointSize |
| const GLint vec4Count = maxVaryings - 1; |
| unsigned int varyingCount = 0; |
| std::string varyingDeclaration; |
| for (GLint i = 0; i < vec4Count; i++) |
| { |
| varyingDeclaration += GenerateVectorVaryingDeclaration(4, 1, varyingCount); |
| varyingCount += 1; |
| } |
| // Generate the vertex shader |
| vertexShaderSource.clear(); |
| vertexShaderSource << varyingDeclaration; |
| vertexShaderSource << "\nattribute vec4 a_position;\n"; |
| vertexShaderSource << "\nvoid main()\n{\n"; |
| unsigned int currentVSVarying = 0; |
| for (GLint i = 0; i < vec4Count; i++) |
| { |
| vertexShaderSource << GenerateVectorVaryingSettingCode(4, 1, currentVSVarying); |
| currentVSVarying += 1; |
| } |
| vertexShaderSource << "\tgl_Position = vec4(a_position.rgb, 1);\n"; |
| vertexShaderSource << "\tgl_PointSize = 1.0;\n"; |
| vertexShaderSource << "}\n"; |
| |
| // Generate the fragment shader |
| fragmentShaderSource.clear(); |
| fragmentShaderSource << "precision highp float;\n"; |
| fragmentShaderSource << varyingDeclaration; |
| fragmentShaderSource << "\nvoid main() \n{ \n\tvec4 retColor = vec4(0,0,0,0);\n"; |
| unsigned int currentFSVarying = 0; |
| // Make use of the vec4 varyings |
| fragmentShaderSource << "\tretColor += "; |
| for (GLint i = 0; i < vec4Count; i++) |
| { |
| fragmentShaderSource << GenerateVectorVaryingUseCode(1, currentFSVarying); |
| currentFSVarying += 1; |
| } |
| fragmentShaderSource << "vec4(0.0, 0.0, 0.0, 0.0);\n"; |
| constexpr GLuint testValue = 234; |
| fragmentShaderSource << "\tgl_FragColor = (retColor/vec4(" << std::to_string(currentFSVarying) |
| << ")) /255.0*" << std::to_string(testValue) << ".0;\n"; |
| fragmentShaderSource << "}\n"; |
| |
| std::vector<std::string> tfVaryings = {"gl_Position", "gl_PointSize"}; |
| ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program1, vertexShaderSource.str().c_str(), |
| fragmentShaderSource.str().c_str(), tfVaryings, |
| GL_INTERLEAVED_ATTRIBS); |
| |
| GLBuffer xfbBuffer; |
| glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer); |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 6 * (sizeof(float[4]) + sizeof(float)), nullptr, |
| GL_STATIC_DRAW); |
| |
| GLTransformFeedback xfb; |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer); |
| |
| glUseProgram(program1); |
| |
| const GLint positionLocation = glGetAttribLocation(program1, essl1_shaders::PositionAttrib()); |
| GLBuffer vertexBuffer; |
| // need to shift half pixel to make sure the line covers the center of the pixel |
| const Vector3 vertices[2] = { |
| {-1.0f, -1.0f + 0.5f / static_cast<float>(getWindowHeight()), 0.0f}, |
| {1.0f, -1.0f + 0.5f / static_cast<float>(getWindowHeight()), 0.0f}}; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(*vertices) * 2, vertices, GL_STATIC_DRAW); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glBeginTransformFeedback(GL_LINES); |
| glDrawArrays(GL_LINES, 0, 2); |
| glEndTransformFeedback(); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(testValue, testValue, testValue, testValue)); |
| } |
| |
| // Verify shader source with a fixed length that is less than the null-terminated length will |
| // compile. |
| TEST_P(GLSLTest, FixedShaderLength) |
| { |
| GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| const std::string appendGarbage = "abcdefghijklmnopqrstuvwxyz"; |
| const std::string source = "void main() { gl_FragColor = vec4(0, 0, 0, 0); }" + appendGarbage; |
| const char *sourceArray[1] = {source.c_str()}; |
| GLint lengths[1] = {static_cast<GLint>(source.length() - appendGarbage.length())}; |
| glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_NE(compileResult, 0); |
| } |
| |
| // Verify that a negative shader source length is treated as a null-terminated length. |
| TEST_P(GLSLTest, NegativeShaderLength) |
| { |
| GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| const char *sourceArray[1] = {essl1_shaders::fs::Red()}; |
| GLint lengths[1] = {-10}; |
| glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_NE(compileResult, 0); |
| } |
| |
| // Check that having an invalid char after the "." doesn't cause an assert. |
| TEST_P(GLSLTest, InvalidFieldFirstChar) |
| { |
| GLuint shader = glCreateShader(GL_VERTEX_SHADER); |
| const char *source = "void main() {vec4 x; x.}"; |
| glShaderSource(shader, 1, &source, 0); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_EQ(0, compileResult); |
| } |
| |
| // Verify that a length array with mixed positive and negative values compiles. |
| TEST_P(GLSLTest, MixedShaderLengths) |
| { |
| GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| const char *sourceArray[] = { |
| "void main()", |
| "{", |
| " gl_FragColor = vec4(0, 0, 0, 0);", |
| "}", |
| }; |
| GLint lengths[] = { |
| -10, |
| 1, |
| static_cast<GLint>(strlen(sourceArray[2])), |
| -1, |
| }; |
| ASSERT_EQ(ArraySize(sourceArray), ArraySize(lengths)); |
| |
| glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_NE(compileResult, 0); |
| } |
| |
| // Verify that zero-length shader source does not affect shader compilation. |
| TEST_P(GLSLTest, ZeroShaderLength) |
| { |
| GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| const char *sourceArray[] = { |
| "abcdefg", "34534", "void main() { gl_FragColor = vec4(0, 0, 0, 0); }", "", "abcdefghijklm", |
| }; |
| GLint lengths[] = { |
| 0, 0, -1, 0, 0, |
| }; |
| ASSERT_EQ(ArraySize(sourceArray), ArraySize(lengths)); |
| |
| glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_NE(compileResult, 0); |
| } |
| |
| // Tests that bad index expressions don't crash ANGLE's translator. |
| // https://code.google.com/p/angleproject/issues/detail?id=857 |
| TEST_P(GLSLTest, BadIndexBug) |
| { |
| constexpr char kFSSourceVec[] = |
| "precision mediump float;\n" |
| "uniform vec4 uniformVec;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(uniformVec[int()]);\n" |
| "}"; |
| |
| GLuint shader = CompileShader(GL_FRAGMENT_SHADER, kFSSourceVec); |
| EXPECT_EQ(0u, shader); |
| |
| if (shader != 0) |
| { |
| glDeleteShader(shader); |
| } |
| |
| constexpr char kFSSourceMat[] = |
| "precision mediump float;\n" |
| "uniform mat4 uniformMat;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(uniformMat[int()]);\n" |
| "}"; |
| |
| shader = CompileShader(GL_FRAGMENT_SHADER, kFSSourceMat); |
| EXPECT_EQ(0u, shader); |
| |
| if (shader != 0) |
| { |
| glDeleteShader(shader); |
| } |
| |
| constexpr char kFSSourceArray[] = |
| "precision mediump float;\n" |
| "uniform vec4 uniformArray;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(uniformArray[int()]);\n" |
| "}"; |
| |
| shader = CompileShader(GL_FRAGMENT_SHADER, kFSSourceArray); |
| EXPECT_EQ(0u, shader); |
| |
| if (shader != 0) |
| { |
| glDeleteShader(shader); |
| } |
| } |
| |
| // Test that structs defined in uniforms are translated correctly. |
| TEST_P(GLSLTest, StructSpecifiersUniforms) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| uniform struct S { float field; } s; |
| |
| void main() |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| gl_FragColor.a += s.field; |
| })"; |
| |
| GLuint program = CompileProgram(essl1_shaders::vs::Simple(), kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that structs declaration followed directly by an initialization is translated correctly. |
| TEST_P(GLSLTest, StructWithInitializer) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| struct S { float a; } s = S(1.0); |
| |
| void main() |
| { |
| gl_FragColor = vec4(0, 0, 0, 1); |
| gl_FragColor.r += s.a; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| // Test drawing, should be red. |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that structs without initializer, followed by a uniform usage works as expected. |
| TEST_P(GLSLTest, UniformStructWithoutInitializer) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| struct S { float a; }; |
| uniform S u_s; |
| |
| void main() |
| { |
| gl_FragColor = vec4(u_s.a); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| // Test drawing, should be red. |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::transparentBlack); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that structs declaration followed directly by an initialization in a uniform. |
| TEST_P(GLSLTest, StructWithUniformInitializer) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| struct S { float a; } s = S(1.0); |
| uniform S us; |
| |
| void main() |
| { |
| gl_FragColor = vec4(0, 0, 0, 1); |
| gl_FragColor.r += s.a; |
| gl_FragColor.g += us.a; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| // Test drawing, should be red. |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that gl_DepthRange is not stored as a uniform location. Since uniforms |
| // beginning with "gl_" are filtered out by our validation logic, we must |
| // bypass the validation to test the behaviour of the implementation. |
| // (note this test is still Impl-independent) |
| TEST_P(GLSLTestNoValidation, DepthRangeUniforms) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| void main() |
| { |
| gl_FragColor = vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| |
| // We need to bypass validation for this call. |
| GLint nearIndex = glGetUniformLocation(program.get(), "gl_DepthRange.near"); |
| EXPECT_EQ(-1, nearIndex); |
| |
| // Test drawing does not throw an exception. |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| std::string GenerateSmallPowShader(double base, double exponent) |
| { |
| std::stringstream stream; |
| |
| stream.precision(8); |
| |
| double result = pow(base, exponent); |
| |
| stream << "precision highp float;\n" |
| << "float fun(float arg)\n" |
| << "{\n" |
| << " return pow(arg, " << std::fixed << exponent << ");\n" |
| << "}\n" |
| << "\n" |
| << "void main()\n" |
| << "{\n" |
| << " const float a = " << std::scientific << base << ";\n" |
| << " float b = fun(a);\n" |
| << " if (abs(" << result << " - b) < " << std::abs(result * 0.001) << ")\n" |
| << " {\n" |
| << " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| << " }\n" |
| << " else\n" |
| << " {\n" |
| << " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| << " }\n" |
| << "}\n"; |
| |
| return stream.str(); |
| } |
| |
| // Covers the WebGL test 'glsl/bugs/pow-of-small-constant-in-user-defined-function' |
| // See http://anglebug.com/851 |
| TEST_P(GLSLTest, PowOfSmallConstant) |
| { |
| // Test with problematic exponents that are close to an integer. |
| std::vector<double> testExponents; |
| std::array<double, 5> epsilonMultipliers = {-100.0, -1.0, 0.0, 1.0, 100.0}; |
| for (double epsilonMultiplier : epsilonMultipliers) |
| { |
| for (int i = -4; i <= 5; ++i) |
| { |
| if (i >= -1 && i <= 1) |
| continue; |
| const double epsilon = 1.0e-8; |
| double bad = static_cast<double>(i) + epsilonMultiplier * epsilon; |
| testExponents.push_back(bad); |
| } |
| } |
| |
| // Also test with a few exponents that are not close to an integer. |
| testExponents.push_back(3.6); |
| testExponents.push_back(3.4); |
| |
| for (double testExponent : testExponents) |
| { |
| const std::string &fragmentShaderSource = GenerateSmallPowShader(1.0e-6, testExponent); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), fragmentShaderSource.c_str()); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_GL_NO_ERROR(); |
| } |
| } |
| |
| // Test that fragment shaders which contain non-constant loop indexers and compiled for FL9_3 and |
| // below |
| // fail with a specific error message. |
| // Additionally test that the same fragment shader compiles successfully with feature levels greater |
| // than FL9_3. |
| TEST_P(GLSLTest, LoopIndexingValidation) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| uniform float loopMax; |
| |
| void main() |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| for (float l = 0.0; l < loopMax; l++) |
| { |
| if (loopMax > 3.0) |
| { |
| gl_FragColor.a += 0.1; |
| } |
| } |
| })"; |
| |
| GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| const char *sourceArray[1] = {kFS}; |
| glShaderSource(shader, 1, sourceArray, nullptr); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| |
| // If the test is configured to run limited to Feature Level 9_3, then it is |
| // assumed that shader compilation will fail with an expected error message containing |
| // "Loop index cannot be compared with non-constant expression" |
| if ((GetParam() == ES2_D3D11_FL9_3() || GetParam() == ES2_D3D9())) |
| { |
| if (compileResult != 0) |
| { |
| FAIL() << "Shader compilation succeeded, expected failure"; |
| } |
| else |
| { |
| GLint infoLogLength; |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); |
| |
| std::string infoLog; |
| infoLog.resize(infoLogLength); |
| glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]); |
| |
| if (infoLog.find("Loop index cannot be compared with non-constant expression") == |
| std::string::npos) |
| { |
| FAIL() << "Shader compilation failed with unexpected error message"; |
| } |
| } |
| } |
| else |
| { |
| EXPECT_NE(0, compileResult); |
| } |
| |
| if (shader != 0) |
| { |
| glDeleteShader(shader); |
| } |
| } |
| |
| // Tests that the maximum uniforms count returned from querying GL_MAX_VERTEX_UNIFORM_VECTORS |
| // can actually be used. |
| TEST_P(GLSLTest, VerifyMaxVertexUniformVectors) |
| { |
| // crbug.com/680631 |
| ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel()); |
| |
| int maxUniforms = 10000; |
| glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); |
| EXPECT_GL_NO_ERROR(); |
| std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS = " << maxUniforms << std::endl; |
| |
| CompileGLSLWithUniformsAndSamplers(maxUniforms, 0, 0, 0, true); |
| } |
| |
| // Tests that the maximum uniforms count returned from querying GL_MAX_VERTEX_UNIFORM_VECTORS |
| // can actually be used along with the maximum number of texture samplers. |
| TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsWithSamplers) |
| { |
| ANGLE_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); |
| |
| // Times out on D3D11 on test infra. http://anglebug.com/5076 |
| ANGLE_SKIP_TEST_IF(IsD3D11() && IsIntel()); |
| |
| int maxUniforms = 10000; |
| glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); |
| EXPECT_GL_NO_ERROR(); |
| std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS = " << maxUniforms << std::endl; |
| |
| int maxTextureImageUnits = 0; |
| glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); |
| |
| CompileGLSLWithUniformsAndSamplers(maxUniforms, 0, maxTextureImageUnits, 0, true); |
| } |
| |
| // Tests that the maximum uniforms count + 1 from querying GL_MAX_VERTEX_UNIFORM_VECTORS |
| // fails shader compilation. |
| TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsExceeded) |
| { |
| int maxUniforms = 10000; |
| glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); |
| EXPECT_GL_NO_ERROR(); |
| std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS + 1 = " << maxUniforms + 1 << std::endl; |
| |
| CompileGLSLWithUniformsAndSamplers(maxUniforms + 1, 0, 0, 0, false); |
| } |
| |
| // Tests that the maximum uniforms count returned from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS |
| // can actually be used. |
| TEST_P(GLSLTest, VerifyMaxFragmentUniformVectors) |
| { |
| // crbug.com/680631 |
| ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel()); |
| |
| int maxUniforms = 10000; |
| glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); |
| EXPECT_GL_NO_ERROR(); |
| std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS = " << maxUniforms << std::endl; |
| |
| CompileGLSLWithUniformsAndSamplers(0, maxUniforms, 0, 0, true); |
| } |
| |
| // Tests that the maximum uniforms count returned from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS |
| // can actually be used along with the maximum number of texture samplers. |
| TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsWithSamplers) |
| { |
| ANGLE_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); |
| |
| int maxUniforms = 10000; |
| glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); |
| EXPECT_GL_NO_ERROR(); |
| |
| int maxTextureImageUnits = 0; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); |
| |
| CompileGLSLWithUniformsAndSamplers(0, maxUniforms, 0, maxTextureImageUnits, true); |
| } |
| |
| // Tests that the maximum uniforms count + 1 from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS |
| // fails shader compilation. |
| TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsExceeded) |
| { |
| int maxUniforms = 10000; |
| glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); |
| EXPECT_GL_NO_ERROR(); |
| std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS + 1 = " << maxUniforms + 1 |
| << std::endl; |
| |
| CompileGLSLWithUniformsAndSamplers(0, maxUniforms + 1, 0, 0, false); |
| } |
| |
| // Test compiling shaders using the GL_EXT_shader_texture_lod extension |
| TEST_P(GLSLTest, TextureLOD) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_texture_lod")); |
| |
| constexpr char kFS[] = |
| "#extension GL_EXT_shader_texture_lod : require\n" |
| "uniform sampler2D u_texture;\n" |
| "void main() {\n" |
| " gl_FragColor = texture2DGradEXT(u_texture, vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.0, " |
| "0.0));\n" |
| "}\n"; |
| |
| GLuint shader = CompileShader(GL_FRAGMENT_SHADER, kFS); |
| ASSERT_NE(0u, shader); |
| glDeleteShader(shader); |
| } |
| |
| // HLSL generates extra lod0 variants of functions. There was a bug that incorrectly reworte |
| // function calls to use them in vertex shaders. http://anglebug.com/3471 |
| TEST_P(GLSLTest, TextureLODRewriteInVertexShader) |
| { |
| constexpr char kVS[] = R"( |
| precision highp float; |
| uniform int uni; |
| uniform sampler2D texture; |
| |
| vec4 A(); |
| |
| vec4 B() { |
| vec4 a; |
| for(int r=0; r<14; r++){ |
| if (r < uni) return vec4(0.0); |
| a = A(); |
| } |
| return a; |
| } |
| |
| vec4 A() { |
| return texture2D(texture, vec2(0.0, 0.0)); |
| } |
| |
| void main() { |
| gl_Position = B(); |
| })"; |
| |
| constexpr char kFS[] = R"( |
| void main() { gl_FragColor = vec4(gl_FragCoord.x / 640.0, gl_FragCoord.y / 480.0, 0, 1); } |
| )"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test to verify the a shader can have a sampler unused in a vertex shader |
| // but used in the fragment shader. |
| TEST_P(GLSLTest, VerifySamplerInBothVertexAndFragmentShaders) |
| { |
| constexpr char kVS[] = R"( |
| attribute vec2 position; |
| varying mediump vec2 texCoord; |
| uniform sampler2D tex; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| texCoord = position * 0.5 + vec2(0.5); |
| })"; |
| |
| constexpr char kFS[] = R"( |
| varying mediump vec2 texCoord; |
| uniform sampler2D tex; |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texCoord); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| // Initialize basic red texture. |
| const std::vector<GLColor> redColors(4, GLColor::red); |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, redColors.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(program, "position", 0.0f); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red); |
| } |
| |
| // Test that array of structs containing array of samplers work as expected. |
| TEST_P(GLSLTest, ArrayOfStructContainingArrayOfSamplers) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "struct Data { mediump sampler2D data[2]; };\n" |
| "uniform Data test[2];\n" |
| "void main() {\n" |
| " gl_FragColor = vec4(texture2D(test[1].data[1], vec2(0.0, 0.0)).r,\n" |
| " texture2D(test[1].data[0], vec2(0.0, 0.0)).r,\n" |
| " texture2D(test[0].data[1], vec2(0.0, 0.0)).r,\n" |
| " texture2D(test[0].data[0], vec2(0.0, 0.0)).r);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[4]; |
| GLColor expected = MakeGLColor(32, 64, 96, 255); |
| GLubyte data[8] = {}; // 4 bytes of padding, so that texture can be initialized with 4 bytes |
| memcpy(data, expected.data(), sizeof(expected)); |
| for (int i = 0; i < 4; i++) |
| { |
| int outerIdx = i % 2; |
| int innerIdx = i / 2; |
| glActiveTexture(GL_TEXTURE0 + i); |
| glBindTexture(GL_TEXTURE_2D, textures[i]); |
| // Each element provides two components. |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + i); |
| std::stringstream uniformName; |
| uniformName << "test[" << innerIdx << "].data[" << outerIdx << "]"; |
| // Then send it as a uniform |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // The uniform should be active. |
| EXPECT_NE(uniformLocation, -1); |
| |
| glUniform1i(uniformLocation, 3 - i); |
| } |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, expected); |
| } |
| |
| // Test that if a non-preprocessor token is seen in a disabled if-block then it does not disallow |
| // extension pragmas later |
| TEST_P(GLSLTest, NonPreprocessorTokensInIfBlocks) |
| { |
| constexpr const char *kFS = R"( |
| #if __VERSION__ >= 300 |
| inout mediump vec4 fragData; |
| #else |
| #extension GL_EXT_shader_texture_lod :enable |
| #endif |
| |
| void main() |
| { |
| } |
| )"; |
| |
| GLuint shader = CompileShader(GL_FRAGMENT_SHADER, kFS); |
| EXPECT_NE(0u, shader); |
| } |
| |
| // Test that two constructors which have vec4 and mat2 parameters get disambiguated (issue in |
| // HLSL). |
| TEST_P(GLSLTest_ES3, AmbiguousConstructorCall2x2) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "in vec4 a_vec;\n" |
| "in mat2 a_mat;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_vec) + vec4(a_mat);\n" |
| "}"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that two constructors which have mat2x3 and mat3x2 parameters get disambiguated. |
| // This was suspected to be an issue in HLSL, but HLSL seems to be able to natively choose between |
| // the function signatures in this case. |
| TEST_P(GLSLTest_ES3, AmbiguousConstructorCall2x3) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "in mat3x2 a_matA;\n" |
| "in mat2x3 a_matB;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_matA) + vec4(a_matB);\n" |
| "}"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that two functions which have vec4 and mat2 parameters get disambiguated (issue in HLSL). |
| TEST_P(GLSLTest_ES3, AmbiguousFunctionCall2x2) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "in vec4 a_vec;\n" |
| "in mat2 a_mat;\n" |
| "vec4 foo(vec4 a)\n" |
| "{\n" |
| " return a;\n" |
| "}\n" |
| "vec4 foo(mat2 a)\n" |
| "{\n" |
| " return vec4(a[0][0]);\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = foo(a_vec) + foo(a_mat);\n" |
| "}"; |
| |
| GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that constructing matrices from non-float types works. |
| TEST_P(GLSLTest_ES3, ConstructMatrixFromNonFloat) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 color; |
| |
| uniform int i; |
| uniform uint u; |
| uniform bool b; |
| |
| void main() |
| { |
| mat3x2 mi = mat3x2(i); |
| mat4 mu = mat4(u); |
| mat2x4 mb = mat2x4(b); |
| |
| mat3x2 m = mat3x2(ivec2(i), uvec2(u), bvec2(b)); |
| |
| color = vec4(mi[0][0] == float(i) ? 1 : 0, |
| mu[2][2] == float(u) ? 1 : 0, |
| mb[1][1] == float(b) ? 1 : 0, |
| m[0][1] == float(i) && m[1][0] == float(u) && m[2][0] == float(b) ? 1 : 0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint iloc = glGetUniformLocation(program, "i"); |
| GLint uloc = glGetUniformLocation(program, "u"); |
| GLint bloc = glGetUniformLocation(program, "b"); |
| ASSERT_NE(iloc, -1); |
| ASSERT_NE(uloc, -1); |
| ASSERT_NE(bloc, -1); |
| glUniform1i(iloc, -123); |
| glUniform1ui(uloc, 456); |
| glUniform1ui(bloc, 1); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that constructing vectors from non-float types works. |
| TEST_P(GLSLTest_ES3, ConstructVectorFromNonFloat) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 color; |
| |
| uniform ivec2 i; |
| uniform uvec2 u; |
| uniform bvec2 b; |
| |
| void main() |
| { |
| vec2 v2 = vec2(i.x, b); |
| vec3 v3 = vec3(b, u); |
| vec4 v4 = vec4(i, u); |
| |
| color = vec4(v2.x == float(i.x) && v2.y == float(b.x) ? 1 : 0, |
| v3.x == float(b.x) && v3.y == float(b.y) && v3.z == float(u.x) ? 1 : 0, |
| v4.x == float(i.x) && v4.y == float(i.y) && v4.z == float(u.x) && v4.w == float(u.y) ? 1 : 0, |
| 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint iloc = glGetUniformLocation(program, "i"); |
| GLint uloc = glGetUniformLocation(program, "u"); |
| GLint bloc = glGetUniformLocation(program, "b"); |
| ASSERT_NE(iloc, -1); |
| ASSERT_NE(uloc, -1); |
| ASSERT_NE(bloc, -1); |
| glUniform2i(iloc, -123, -23); |
| glUniform2ui(uloc, 456, 76); |
| glUniform2ui(bloc, 1, 0); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that constructing non-float vectors from matrix types works. |
| TEST_P(GLSLTest_ES3, ConstructNonFloatVectorFromMatrix) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 color; |
| |
| uniform float f; |
| |
| void main() |
| { |
| mat4 m = mat4(f); |
| ivec3 vi = ivec3(m); |
| uvec2 vu = uvec2(m); |
| bvec4 vb = bvec4(m); |
| bvec2 vb2 = bvec2(vi.x, m); |
| |
| color = vec4(vi.x == int(f) ? 1 : 0, |
| vu.x == uint(f) ? 1 : 0, |
| vb.x == bool(f) ? 1 : 0, |
| vb2.x == bool(f) && vb2.y == bool(f) ? 1 : 0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint floc = glGetUniformLocation(program, "f"); |
| ASSERT_NE(floc, -1); |
| glUniform1f(floc, 123); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that == and != for vector and matrix types work. |
| TEST_P(GLSLTest_ES3, NonScalarEqualOperator) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 color; |
| |
| uniform float f; |
| uniform int i; |
| uniform uint u; |
| |
| void main() |
| { |
| mat3x2 m32_1 = mat3x2(vec2(f), vec2(i), vec2(u)); |
| mat3x2 m32_2 = mat3x2(m32_1); |
| mat3x2 m32_3 = mat3x2(vec2(i), vec2(u), vec2(f)); |
| mat2x3 m23_1 = mat2x3(vec3(f), vec3(i)); |
| mat2x3 m23_2 = mat2x3(m23_1); |
| mat2x3 m23_3 = mat2x3(vec3(i), vec3(u)); |
| vec2 v2_1 = m32_1[0]; |
| vec2 v2_2 = m32_2[0]; |
| ivec3 v3_1 = ivec3(transpose(m32_1)[0]); |
| ivec3 v3_2 = ivec3(transpose(m32_2)[0]); |
| uvec4 v4_1 = uvec4(m32_1[1], m32_1[2]); |
| uvec4 v4_2 = uvec4(m32_2[1], m32_2[2]); |
| |
| color = vec4((m32_1 == m32_2 ? 0.5 : 0.0) + (m23_1 == m23_2 ? 0.5 : 0.0), |
| v2_1 == v2_2 ? 1 : 0, |
| (v3_1 == v3_2 ? 0.5 : 0.0) + |
| (v4_1 == v4_2 ? 0.5 : 0.0), |
| (m32_1 != m32_3 ? 0.125 : 0.0) + |
| (m23_1 != m23_3 ? 0.125 : 0.0) + |
| (v2_1 != vec2(v3_2) ? 0.25 : 0.0) + |
| (v3_1 != ivec3(v4_2) ? 0.25 : 0.0) + |
| (v4_1 != uvec4(v2_1, v2_2) ? 0.25 : 0.0)); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint floc = glGetUniformLocation(program, "f"); |
| GLint iloc = glGetUniformLocation(program, "i"); |
| GLint uloc = glGetUniformLocation(program, "u"); |
| ASSERT_NE(floc, -1); |
| ASSERT_NE(iloc, -1); |
| ASSERT_NE(uloc, -1); |
| glUniform1f(floc, 1.5); |
| glUniform1i(iloc, -123); |
| glUniform1ui(uloc, 456); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that == and != for structs and array types work. |
| TEST_P(GLSLTest_ES31, StructAndArrayEqualOperator) |
| { |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| out vec4 color; |
| |
| uniform float f; |
| uniform int i; |
| uniform uint u; |
| |
| struct S |
| { |
| float f; |
| int i; |
| uint u; |
| vec4 v; |
| ivec3 iv; |
| uvec2 uv; |
| mat3x2 m32; |
| mat2x3 m23; |
| float fa[3][4][5]; |
| int ia[4]; |
| uint ua[6][2]; |
| }; |
| |
| struct T |
| { |
| S s1; |
| S s2[3][2]; |
| }; |
| |
| void main() |
| { |
| float fa[5] = float[5](f, f, f, f, f); |
| int ia[4] = int[4](i, i, i, i); |
| uint ua[2] = uint[2](u, u); |
| |
| S s1 = S(f, i, u, vec4(f), ivec3(i), uvec2(u), |
| mat3x2(vec2(f), vec2(i), vec2(u)), |
| mat2x3(vec3(f), vec3(i)), |
| float[3][4][5]( |
| float[4][5](fa, fa, fa, fa), |
| float[4][5](fa, fa, fa, fa), |
| float[4][5](fa, fa, fa, fa)), |
| ia, |
| uint[6][2](ua, ua, ua, ua, ua, ua)); |
| |
| S s2[2] = S[2](s1, s1); |
| s2[1].fa[0][1][2] = float(i); |
| |
| T t1 = T(s1, S[3][2](s2, s2, s2)); |
| T t2 = T(s2[1], S[3][2](s2, s2, s2)); |
| |
| T ta1[2] = T[2](t1, t2); |
| T ta2[2] = T[2](t1, t2); |
| T ta3[2] = T[2](t2, t1); |
| |
| color = vec4((s1 == s2[0] ? 0.5 : 0.0) + (s1 != s2[1] ? 0.5 : 0.0), |
| (s1.fa[0] == s2[0].fa[0] ? 0.5 : 0.0) + (s1.fa[0] != s2[1].fa[0] ? 0.5 : 0.0), |
| (ta1[0] == t1 ? 0.5 : 0.0) + (ta1[1] != t1 ? 0.5 : 0.0), |
| (ta1 == ta2 ? 0.5 : 0.0) + (ta1 != ta3 ? 0.5 : 0.0)); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint floc = glGetUniformLocation(program, "f"); |
| GLint iloc = glGetUniformLocation(program, "i"); |
| GLint uloc = glGetUniformLocation(program, "u"); |
| ASSERT_NE(floc, -1); |
| ASSERT_NE(iloc, -1); |
| ASSERT_NE(uloc, -1); |
| glUniform1f(floc, 1.5); |
| glUniform1i(iloc, -123); |
| glUniform1ui(uloc, 456); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that an user-defined function with a large number of float4 parameters doesn't fail due to |
| // the function name being too long. |
| TEST_P(GLSLTest_ES3, LargeNumberOfFloat4Parameters) |
| { |
| std::stringstream vertexShaderStream; |
| // Note: SPIR-V doesn't allow more than 255 parameters to a function. |
| const unsigned int paramCount = (IsVulkan() || IsMetal()) ? 255u : 1024u; |
| |
| vertexShaderStream << "#version 300 es\n" |
| "precision highp float;\n" |
| "in vec4 a_vec;\n" |
| "vec4 lotsOfVec4Parameters("; |
| for (unsigned int i = 0; i < paramCount - 1; ++i) |
| { |
| vertexShaderStream << "vec4 a" << i << ", "; |
| } |
| vertexShaderStream << "vec4 aLast)\n" |
| "{\n" |
| " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"; |
| for (unsigned int i = 0; i < paramCount - 1; ++i) |
| { |
| vertexShaderStream << " sum += a" << i << ";\n"; |
| } |
| vertexShaderStream << " sum += aLast;\n" |
| " return sum;\n " |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = lotsOfVec4Parameters("; |
| for (unsigned int i = 0; i < paramCount - 1; ++i) |
| { |
| vertexShaderStream << "a_vec, "; |
| } |
| vertexShaderStream << "a_vec);\n" |
| "}"; |
| |
| GLuint program = CompileProgram(vertexShaderStream.str().c_str(), essl3_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // This test was written specifically to stress DeferGlobalInitializers AST transformation. |
| // Test a shader where a global constant array is initialized with an expression containing array |
| // indexing. This initializer is tricky to constant fold, so if it's not constant folded it needs to |
| // be handled in a way that doesn't generate statements in the global scope in HLSL output. |
| // Also includes multiple array initializers in one declaration, where only the second one has |
| // array indexing. This makes sure that the qualifier for the declaration is set correctly if |
| // transformations are applied to the declaration also in the case of ESSL output. |
| TEST_P(GLSLTest_ES3, InitGlobalArrayWithArrayIndexing) |
| { |
| // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1428 is fixed |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "out vec4 my_FragColor;\n" |
| "const highp float f[2] = float[2](0.1, 0.2);\n" |
| "const highp float[2] g = float[2](0.3, 0.4), h = float[2](0.5, f[1]);\n" |
| "void main()\n" |
| "{\n" |
| " my_FragColor = vec4(h[1]);\n" |
| "}"; |
| |
| GLuint program = CompileProgram(essl3_shaders::vs::Simple(), kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that index-constant sampler array indexing is supported. |
| TEST_P(GLSLTest, IndexConstantSamplerArrayIndexing) |
| { |
| ANGLE_SKIP_TEST_IF(IsD3D11_FL93()); |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "uniform sampler2D uni[2];\n" |
| "\n" |
| "float zero(int x)\n" |
| "{\n" |
| " return float(x) - float(x);\n" |
| "}\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " vec4 c = vec4(0,0,0,0);\n" |
| " for (int ii = 1; ii < 3; ++ii) {\n" |
| " if (c.x > 255.0) {\n" |
| " c.x = 255.0 + zero(ii);\n" |
| " break;\n" |
| " }\n" |
| // Index the sampler array with a predictable loop index (index-constant) as opposed to |
| // a true constant. This is valid in OpenGL ES but isn't in many Desktop OpenGL versions, |
| // without an extension. |
| " c += texture2D(uni[ii - 1], vec2(0.5, 0.5));\n" |
| " }\n" |
| " gl_FragColor = c;\n" |
| "}"; |
| |
| GLuint program = CompileProgram(essl1_shaders::vs::Simple(), kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that the #pragma directive is supported and doesn't trigger a compilation failure on the |
| // native driver. The only pragma that gets passed to the OpenGL driver is "invariant" but we don't |
| // want to test its behavior, so don't use any varyings. |
| TEST_P(GLSLTest, PragmaDirective) |
| { |
| constexpr char kVS[] = |
| "#pragma STDGL invariant(all)\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| GLuint program = CompileProgram(kVS, essl1_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9). |
| // The function call that returns the array needs to be evaluated after ++j for the expression to |
| // return the correct value (true). |
| TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderArray) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor; \n" |
| "int[2] func(int param) {\n" |
| " return int[2](param, param);\n" |
| "}\n" |
| "void main() {\n" |
| " int a[2]; \n" |
| " for (int i = 0; i < 2; ++i) {\n" |
| " a[i] = 1;\n" |
| " }\n" |
| " int j = 0; \n" |
| " bool result = ((++j), (a == func(j)));\n" |
| " my_FragColor = vec4(0.0, (result ? 1.0 : 0.0), 0.0, 1.0);\n" |
| "}\n"; |
| |
| GLuint program = CompileProgram(essl3_shaders::vs::Simple(), kFS); |
| ASSERT_NE(0u, program); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9). |
| // The short-circuiting expression needs to be evaluated after ++j for the expression to return the |
| // correct value (true). |
| TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderShortCircuit) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor; \n" |
| "void main() {\n" |
| " int j = 0; \n" |
| " bool result = ((++j), (j == 1 ? true : (++j == 3)));\n" |
| " my_FragColor = vec4(0.0, ((result && j == 1) ? 1.0 : 0.0), 0.0, 1.0);\n" |
| "}\n"; |
| |
| GLuint program = CompileProgram(essl3_shaders::vs::Simple(), kFS); |
| ASSERT_NE(0u, program); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9). |
| // Indexing the vector needs to be evaluated after func() for the right result. |
| TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderDynamicVectorIndexingInLValue) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform int u_zero;\n" |
| "int sideEffectCount = 0;\n" |
| "float func() {\n" |
| " ++sideEffectCount;\n" |
| " return -1.0;\n" |
| "}\n" |
| "void main() {\n" |
| " vec4 v = vec4(0.0, 2.0, 4.0, 6.0); \n" |
| " float f = (func(), (++v[u_zero + sideEffectCount]));\n" |
| " bool green = abs(f - 3.0) < 0.01 && abs(v[1] - 3.0) < 0.01 && sideEffectCount == 1;\n" |
| " my_FragColor = vec4(0.0, (green ? 1.0 : 0.0), 0.0, 1.0);\n" |
| "}\n"; |
| |
| GLuint program = CompileProgram(essl3_shaders::vs::Simple(), kFS); |
| ASSERT_NE(0u, program); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that using gl_PointCoord with GL_TRIANGLES doesn't produce a link error. |
| // From WebGL test conformance/rendering/point-specific-shader-variables.html |
| // See http://anglebug.com/1380 |
| TEST_P(GLSLTest, RenderTrisWithPointCoord) |
| { |
| constexpr char kVS[] = |
| "attribute vec2 aPosition;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(aPosition, 0, 1);\n" |
| " gl_PointSize = 1.0;\n" |
| "}"; |
| constexpr char kFS[] = |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(gl_PointCoord.xy, 0, 1);\n" |
| " gl_FragColor = vec4(0, 1, 0, 1);\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(prog, kVS, kFS); |
| drawQuad(prog.get(), "aPosition", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Convers a bug with the integer pow statement workaround. |
| TEST_P(GLSLTest, NestedPowStatements) |
| { |
| // https://crbug.com/1127866 - possible NVIDIA driver issue |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsVulkan() && IsWindows()); |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "float func(float v)\n" |
| "{\n" |
| " float f1 = pow(v, 2.0);\n" |
| " return pow(f1 + v, 2.0);\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " float v = func(2.0);\n" |
| " gl_FragColor = abs(v - 36.0) < 0.001 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(prog, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(prog.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that -float calculation is correct. |
| TEST_P(GLSLTest_ES3, UnaryMinusOperatorFloat) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "out highp vec4 o_color;\n" |
| "void main() {\n" |
| " highp float f = -1.0;\n" |
| " // atan(tan(0.5), -f) should be 0.5.\n" |
| " highp float v = atan(tan(0.5), -f);\n" |
| " o_color = abs(v - 0.5) < 0.001 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(prog, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(prog.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that atan(vec2, vec2) calculation is correct. |
| TEST_P(GLSLTest_ES3, AtanVec2) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "out highp vec4 o_color;\n" |
| "void main() {\n" |
| " highp float f = 1.0;\n" |
| " // atan(tan(0.5), f) should be 0.5.\n" |
| " highp vec2 v = atan(vec2(tan(0.5)), vec2(f));\n" |
| " o_color = (abs(v[0] - 0.5) < 0.001 && abs(v[1] - 0.5) < 0.001) ? vec4(0, 1, 0, 1) : " |
| "vec4(1, 0, 0, 1);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(prog, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(prog.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Convers a bug with the unary minus operator on signed integer workaround. |
| TEST_P(GLSLTest_ES3, UnaryMinusOperatorSignedInt) |
| { |
| // http://anglebug.com/5242 |
| // Test times out on dual-GPU MacBook Pros that don't show up as |
| // IsIntel(); skip on all Metal for now. |
| // See also http://anglebug.com/6174 . |
| ANGLE_SKIP_TEST_IF(IsMetal()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in highp vec4 position;\n" |
| "out mediump vec4 v_color;\n" |
| "uniform int ui_one;\n" |
| "uniform int ui_two;\n" |
| "uniform int ui_three;\n" |
| "void main() {\n" |
| " int s[3];\n" |
| " s[0] = ui_one;\n" |
| " s[1] = -(-(-ui_two + 1) + 1);\n" // s[1] = -ui_two |
| " s[2] = ui_three;\n" |
| " int result = 0;\n" |
| " for (int i = 0; i < ui_three; i++) {\n" |
| " result += s[i];\n" |
| " }\n" |
| " v_color = (result == 2) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n" |
| " gl_Position = position;\n" |
| "}\n"; |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "in mediump vec4 v_color;\n" |
| "layout(location=0) out mediump vec4 o_color;\n" |
| "void main() {\n" |
| " o_color = v_color;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(prog, kVS, kFS); |
| |
| GLint oneIndex = glGetUniformLocation(prog.get(), "ui_one"); |
| ASSERT_NE(-1, oneIndex); |
| GLint twoIndex = glGetUniformLocation(prog.get(), "ui_two"); |
| ASSERT_NE(-1, twoIndex); |
| GLint threeIndex = glGetUniformLocation(prog.get(), "ui_three"); |
| ASSERT_NE(-1, threeIndex); |
| glUseProgram(prog.get()); |
| glUniform1i(oneIndex, 1); |
| glUniform1i(twoIndex, 2); |
| glUniform1i(threeIndex, 3); |
| |
| drawQuad(prog.get(), "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Convers a bug with the unary minus operator on unsigned integer workaround. |
| TEST_P(GLSLTest_ES3, UnaryMinusOperatorUnsignedInt) |
| { |
| // http://anglebug.com/5242 |
| // Test times out on dual-GPU MacBook Pros that don't show up as |
| // IsIntel(); skip on all Metal for now. |
| // See also http://anglebug.com/6174 . |
| ANGLE_SKIP_TEST_IF(IsMetal()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in highp vec4 position;\n" |
| "out mediump vec4 v_color;\n" |
| "uniform uint ui_one;\n" |
| "uniform uint ui_two;\n" |
| "uniform uint ui_three;\n" |
| "void main() {\n" |
| " uint s[3];\n" |
| " s[0] = ui_one;\n" |
| " s[1] = -(-(-ui_two + 1u) + 1u);\n" // s[1] = -ui_two |
| " s[2] = ui_three;\n" |
| " uint result = 0u;\n" |
| " for (uint i = 0u; i < ui_three; i++) {\n" |
| " result += s[i];\n" |
| " }\n" |
| " v_color = (result == 2u) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n" |
| " gl_Position = position;\n" |
| "}\n"; |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "in mediump vec4 v_color;\n" |
| "layout(location=0) out mediump vec4 o_color;\n" |
| "void main() {\n" |
| " o_color = v_color;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(prog, kVS, kFS); |
| |
| GLint oneIndex = glGetUniformLocation(prog.get(), "ui_one"); |
| ASSERT_NE(-1, oneIndex); |
| GLint twoIndex = glGetUniformLocation(prog.get(), "ui_two"); |
| ASSERT_NE(-1, twoIndex); |
| GLint threeIndex = glGetUniformLocation(prog.get(), "ui_three"); |
| ASSERT_NE(-1, threeIndex); |
| glUseProgram(prog.get()); |
| glUniform1ui(oneIndex, 1u); |
| glUniform1ui(twoIndex, 2u); |
| glUniform1ui(threeIndex, 3u); |
| |
| drawQuad(prog.get(), "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test a nested sequence operator with a ternary operator inside. The ternary operator is |
| // intended to be such that it gets converted to an if statement on the HLSL backend. |
| TEST_P(GLSLTest, NestedSequenceOperatorWithTernaryInside) |
| { |
| // Note that the uniform keep_flop_positive doesn't need to be set - the test expects it to have |
| // its default value false. |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "uniform bool keep_flop_positive;\n" |
| "float flop;\n" |
| "void main() {\n" |
| " flop = -1.0,\n" |
| " (flop *= -1.0,\n" |
| " keep_flop_positive ? 0.0 : flop *= -1.0),\n" |
| " gl_FragColor = vec4(0, -flop, 0, 1);\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(prog, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(prog.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that nesting ternary and short-circuitting operators work. |
| TEST_P(GLSLTest, NestedTernaryAndShortCircuit) |
| { |
| // Note that the uniform doesn't need to be set, and will contain the default value of false. |
| constexpr char kFS[] = R"( |
| precision mediump float; |
| uniform bool u; |
| void main() |
| { |
| int a = u ? 12345 : 2; // will be 2 |
| int b = u ? 12345 : 4; // will be 4 |
| int c = u ? 12345 : 0; // will be 0 |
| |
| if (a == 2 // true path is taken |
| ? (b == 3 // false path is taken |
| ? (a=0) != 0 |
| : b != 0 // true |
| ) && ( // short-circuit evaluates RHS |
| (a=7) == 7 // true, modifies a |
| || // short-circuit doesn't evaluate RHS |
| (b=8) == 8 |
| ) |
| : (a == 0 && b == 0 |
| ? (c += int((a=0) == 0 && (b=0) == 0)) != 0 |
| : (c += int((a=0) != 0 && (b=0) != 0)) != 0)) |
| { |
| c += 15; // will execute |
| } |
| |
| // Verify that a is 7, b is 4 and c is 15. |
| gl_FragColor = vec4(a == 7, b == 4, c == 15, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(prog, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(prog.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that uniform bvecN passed to functions work. |
| TEST_P(GLSLTest_ES3, UniformBoolVectorPassedToFunctions) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| uniform bvec4 u; |
| out vec4 color; |
| |
| bool f(bvec4 bv) |
| { |
| return all(bv.xz) && !any(bv.yw); |
| } |
| |
| void main() { |
| color = f(u) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(prog, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(prog); |
| |
| GLint uloc = glGetUniformLocation(prog, "u"); |
| ASSERT_NE(uloc, -1); |
| glUniform4ui(uloc, true, false, true, false); |
| |
| drawQuad(prog.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that bvecN in storage buffer passed to functions work. |
| TEST_P(GLSLTest_ES31, StorageBufferBoolVectorPassedToFunctions) |
| { |
| constexpr char kCS[] = R"(#version 310 es |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| layout(binding = 0, std430) buffer Output { |
| bvec4 b; |
| bool valid; |
| } outbuf; |
| |
| bool f_in(bvec4 bv) |
| { |
| return all(bv.xz) && !any(bv.yw); |
| } |
| |
| bool f_inout(inout bvec4 bv) |
| { |
| bool ok = all(bv.xz) && !any(bv.yw); |
| bv.xw = bvec2(false, true); |
| return ok; |
| } |
| |
| void f_out(out bvec4 bv) |
| { |
| bv = bvec4(false, true, false, true); |
| } |
| |
| void main() { |
| bool valid = f_in(outbuf.b); |
| valid = f_inout(outbuf.b) && valid; |
| f_out(outbuf.b); |
| outbuf.valid = valid; |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| glUseProgram(program); |
| |
| constexpr std::array<GLuint, 5> kOutputInitData = {true, false, true, false, false}; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kOutputInitData), kOutputInitData.data(), |
| GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(kOutputInitData), GL_MAP_READ_BIT)); |
| fprintf(stderr, "%d %d %d %d %d\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]); |
| EXPECT_FALSE(ptr[0]); |
| EXPECT_TRUE(ptr[1]); |
| EXPECT_FALSE(ptr[2]); |
| EXPECT_TRUE(ptr[3]); |
| EXPECT_TRUE(ptr[4]); |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that using a sampler2D and samplerExternalOES in the same shader works (anglebug.com/1534) |
| TEST_P(GLSLTest, ExternalAnd2DSampler) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_EGL_image_external")); |
| |
| constexpr char kFS[] = R"(#extension GL_OES_EGL_image_external : enable |
| precision mediump float; |
| uniform samplerExternalOES tex0; |
| uniform sampler2D tex1; |
| void main(void) |
| { |
| vec2 uv = vec2(0.0, 0.0); |
| gl_FragColor = texture2D(tex0, uv) + texture2D(tex1, uv); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that using a varying matrix array is supported. |
| TEST_P(GLSLTest, VaryingMatrixArray) |
| { |
| constexpr char kVS[] = |
| "uniform vec2 u_a1;\n" |
| "uniform vec2 u_a2;\n" |
| "attribute vec4 a_position;\n" |
| "varying mat2 v_mat[2];\n" |
| "void main() {\n" |
| " v_mat[0] = mat2(u_a1, u_a2);\n" |
| " v_mat[1] = mat2(1.0 - u_a2, 1.0 - u_a1);\n" |
| " gl_Position = a_position;\n" |
| "}"; |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying mat2 v_mat[2];\n" |
| "void main(void)\n" |
| "{\n" |
| " gl_FragColor = vec4(v_mat[0][0].x, v_mat[0][0].y, v_mat[1][0].x, 1.0);\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| GLint oneIndex = glGetUniformLocation(program, "u_a1"); |
| ASSERT_NE(-1, oneIndex); |
| GLint twoIndex = glGetUniformLocation(program, "u_a2"); |
| ASSERT_NE(-1, twoIndex); |
| glUseProgram(program); |
| glUniform2f(oneIndex, 1, 0.5f); |
| glUniform2f(twoIndex, 0.25f, 0.125f); |
| |
| drawQuad(program, "a_position", 0.5f); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 127, 255 - 63, 255), 1.0); |
| } |
| |
| // Test that using a centroid varying matrix array is supported. |
| TEST_P(GLSLTest_ES3, CentroidVaryingMatrixArray) |
| { |
| // TODO(anglebug.com/5491): Skipping initial failures so we can set up a passing iOS test bot. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "uniform vec2 u_a1;\n" |
| "uniform vec2 u_a2;\n" |
| "in vec4 a_position;\n" |
| "centroid out mat3x2 v_mat[2];\n" |
| "void main() {\n" |
| " v_mat[0] = mat3x2(u_a1, u_a2, vec2(0.0));\n" |
| " v_mat[1] = mat3x2(vec2(0.0), 1.0 - u_a2, 1.0 - u_a1);\n" |
| " gl_Position = a_position;\n" |
| "}"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "centroid in mat3x2 v_mat[2];\n" |
| "layout(location = 0) out vec4 out_color;\n" |
| "void main(void)\n" |
| "{\n" |
| " out_color = vec4(v_mat[0][0].x, v_mat[0][0].y, v_mat[1][1].x, 1.0);\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| GLint oneIndex = glGetUniformLocation(program, "u_a1"); |
| ASSERT_NE(-1, oneIndex); |
| GLint twoIndex = glGetUniformLocation(program, "u_a2"); |
| ASSERT_NE(-1, twoIndex); |
| glUseProgram(program); |
| glUniform2f(oneIndex, 1, 0.5f); |
| glUniform2f(twoIndex, 0.25f, 0.125f); |
| |
| drawQuad(program, "a_position", 0.5f); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 127, 255 - 63, 255), 1.0); |
| } |
| |
| // Test that using a flat varying matrix array is supported. |
| TEST_P(GLSLTest_ES3, FlatVaryingMatrixArray) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "uniform vec2 u_a1;\n" |
| "uniform vec2 u_a2;\n" |
| "in vec4 a_position;\n" |
| "flat out mat2 v_mat[2];\n" |
| "void main() {\n" |
| " v_mat[0] = mat2(u_a1, u_a2);\n" |
| " v_mat[1] = mat2(u_a2, u_a1);\n" |
| " gl_Position = a_position;\n" |
| "}"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "flat in mat2 v_mat[2];\n" |
| "layout(location = 0) out vec4 out_color;\n" |
| "void main(void)\n" |
| "{\n" |
| " out_color = vec4(v_mat[0][0].x, v_mat[0][0].y, v_mat[1][0].x, 1.0);\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| GLint oneIndex = glGetUniformLocation(program, "u_a1"); |
| ASSERT_NE(-1, oneIndex); |
| GLint twoIndex = glGetUniformLocation(program, "u_a2"); |
| ASSERT_NE(-1, twoIndex); |
| glUseProgram(program); |
| glUniform2f(oneIndex, 1, 0.5f); |
| glUniform2f(twoIndex, 0.25f, 0.125f); |
| |
| drawQuad(program, "a_position", 0.5f); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 127, 63, 255), 1.0); |
| } |
| |
| // Test that literal infinity can be written out from the shader translator. |
| // A similar test can't be made for NaNs, since ESSL 3.00.6 requirements for NaNs are very loose. |
| TEST_P(GLSLTest_ES3, LiteralInfinityOutput) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "out vec4 out_color;\n" |
| "uniform float u;\n" |
| "void main()\n" |
| "{\n" |
| " float infVar = 1.0e40 - u;\n" |
| " bool correct = isinf(infVar) && infVar > 0.0;\n" |
| " out_color = correct ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that literal negative infinity can be written out from the shader translator. |
| // A similar test can't be made for NaNs, since ESSL 3.00.6 requirements for NaNs are very loose. |
| TEST_P(GLSLTest_ES3, LiteralNegativeInfinityOutput) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "out vec4 out_color;\n" |
| "uniform float u;\n" |
| "void main()\n" |
| "{\n" |
| " float infVar = -1.0e40 + u;\n" |
| " bool correct = isinf(infVar) && infVar < 0.0;\n" |
| " out_color = correct ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // The following MultipleDeclaration* tests are testing TranslatorHLSL specific simplification |
| // passes. Because the interaction of multiple passes must be tested, it is difficult to write |
| // a unittest for them. Instead we add the tests as end2end so will in particular test |
| // TranslatorHLSL when run on Windows. |
| |
| // Test that passes splitting multiple declarations and comma operators are correctly ordered. |
| TEST_P(GLSLTest_ES3, MultipleDeclarationWithCommaOperator) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| uniform float u; |
| float c = 0.0; |
| float sideEffect() |
| { |
| c = u; |
| return c; |
| } |
| |
| void main(void) |
| { |
| float a = 0.0, b = ((gl_FragCoord.x < 0.5 ? a : sideEffect()), a); |
| color = vec4(b + c); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that passes splitting multiple declarations and comma operators and for loops are |
| // correctly ordered. |
| TEST_P(GLSLTest_ES3, MultipleDeclarationWithCommaOperatorInForLoop) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| uniform float u; |
| float c = 0.0; |
| float sideEffect() |
| { |
| c = u; |
| return c; |
| } |
| |
| void main(void) |
| { |
| for(float a = 0.0, b = ((gl_FragCoord.x < 0.5 ? a : sideEffect()), a); a < 10.0; a++) |
| { |
| b += 1.0; |
| color = vec4(b); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that splitting multiple declaration in for loops works with no loop condition |
| TEST_P(GLSLTest_ES3, MultipleDeclarationInForLoopEmptyCondition) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 color;\n" |
| "void main(void)\n" |
| "{\n" |
| " for(float a = 0.0, b = 1.0;; a++)\n" |
| " {\n" |
| " b += 1.0;\n" |
| " if (a > 10.0) {break;}\n" |
| " color = vec4(b);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that splitting multiple declaration in for loops works with no loop expression |
| TEST_P(GLSLTest_ES3, MultipleDeclarationInForLoopEmptyExpression) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 color;\n" |
| "void main(void)\n" |
| "{\n" |
| " for(float a = 0.0, b = 1.0; a < 10.0;)\n" |
| " {\n" |
| " b += 1.0;\n" |
| " a += 1.0;\n" |
| " color = vec4(b);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that dynamic indexing of a matrix inside a dynamic indexing of a vector in an l-value works |
| // correctly. |
| TEST_P(GLSLTest_ES3, NestedDynamicIndexingInLValue) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform int u_zero;\n" |
| "void main() {\n" |
| " mat2 m = mat2(0.0, 0.0, 0.0, 0.0);\n" |
| " m[u_zero + 1][u_zero + 1] = float(u_zero + 1);\n" |
| " float f = m[1][1];\n" |
| " my_FragColor = vec4(1.0 - f, f, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| class WebGLGLSLTest : public GLSLTest |
| { |
| protected: |
| WebGLGLSLTest() { setWebGLCompatibilityEnabled(true); } |
| }; |
| |
| class WebGL2GLSLTest : public GLSLTest |
| { |
| protected: |
| WebGL2GLSLTest() { setWebGLCompatibilityEnabled(true); } |
| }; |
| |
| TEST_P(WebGLGLSLTest, MaxVaryingVec4PlusFragCoord) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Generate shader code that uses gl_FragCoord, a special fragment shader variables. |
| // This test should fail, since we are really using (maxVaryings + 1) varyings. |
| VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, true, false, false, false); |
| } |
| |
| TEST_P(WebGLGLSLTest, MaxVaryingVec4PlusPointCoord) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| // Generate shader code that uses gl_FragCoord, a special fragment shader variables. |
| // This test should fail, since we are really using (maxVaryings + 1) varyings. |
| VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, false, true, false, false); |
| } |
| |
| TEST_P(WebGLGLSLTest, MaxPlusOneVaryingVec3) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 0, 0, maxVaryings + 1, 0, 0, 0, false, false, false, false); |
| } |
| |
| TEST_P(WebGLGLSLTest, MaxPlusOneVaryingVec3Array) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 0, 0, 0, maxVaryings / 2 + 1, 0, 0, false, false, false, false); |
| } |
| |
| TEST_P(WebGLGLSLTest, MaxVaryingVec3AndOneVec2) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 1, 0, maxVaryings, 0, 0, 0, false, false, false, false); |
| } |
| |
| TEST_P(WebGLGLSLTest, MaxPlusOneVaryingVec2) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, 0, 2 * maxVaryings + 1, 0, 0, 0, 0, 0, false, false, false, false); |
| } |
| |
| TEST_P(WebGLGLSLTest, MaxVaryingVec3ArrayAndMaxPlusOneFloatArray) |
| { |
| GLint maxVaryings = 0; |
| glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); |
| |
| VaryingTestBase(0, maxVaryings / 2 + 1, 0, 0, 0, 0, 0, maxVaryings / 2, false, false, false, |
| false); |
| } |
| |
| // Test that FindLSB and FindMSB return correct values in their corner cases. |
| TEST_P(GLSLTest_ES31, FindMSBAndFindLSBCornerCases) |
| { |
| // Suspecting AMD driver bug - failure seen on bots running on AMD R5 230. |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() && IsLinux()); |
| |
| // Failing on N5X Oreo http://anglebug.com/2304 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform int u_zero;\n" |
| "void main() {\n" |
| " if (findLSB(u_zero) == -1 && findMSB(u_zero) == -1 && findMSB(u_zero - 1) == -1)\n" |
| " {\n" |
| " my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " }\n" |
| " else\n" |
| " {\n" |
| " my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that writing into a swizzled vector that is dynamically indexed succeeds. |
| TEST_P(GLSLTest_ES3, WriteIntoDynamicIndexingOfSwizzledVector) |
| { |
| // http://anglebug.com/1924 |
| ANGLE_SKIP_TEST_IF(IsOpenGL()); |
| |
| // The shader first assigns v.x to v.z (1.0) |
| // Then v.y to v.y (2.0) |
| // Then v.z to v.x (1.0) |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "out vec4 my_FragColor;\n" |
| "void main() {\n" |
| " vec3 v = vec3(1.0, 2.0, 3.0);\n" |
| " for (int i = 0; i < 3; i++) {\n" |
| " v.zyx[i] = v[i];\n" |
| " }\n" |
| " my_FragColor = distance(v, vec3(1.0, 2.0, 1.0)) < 0.01 ? vec4(0, 1, 0, 1) : vec4(1, " |
| "0, 0, 1);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that the length() method is correctly translated in Vulkan atomic counter buffer emulation. |
| TEST_P(GLSLTest_ES31, AtomicCounterArrayLength) |
| { |
| // Crashes on an assertion. The driver reports no atomic counter buffers when queried from the |
| // program, but ANGLE believes there to be one. |
| // |
| // This is likely due to the fact that ANGLE generates the following code, as a side effect of |
| // the code on which .length() is being called: |
| // |
| // _uac1[(_uvalue = _utestSideEffectValue)]; |
| // |
| // The driver is optimizing the subscription out, and calling the atomic counter inactive. This |
| // was observed on nvidia, mesa and amd/windows. |
| // |
| // The fix would be for ANGLE to skip uniforms it believes should exist, but when queried, the |
| // driver says don't. |
| // |
| // http://anglebug.com/3782 |
| ANGLE_SKIP_TEST_IF(IsOpenGL()); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| precision mediump float; |
| layout(local_size_x=1) in; |
| |
| layout(binding = 0) uniform atomic_uint ac1[2][3][4]; |
| uniform uint testSideEffectValue; |
| |
| layout(binding = 1, std140) buffer Result |
| { |
| uint value; |
| } result; |
| |
| void main() { |
| bool passed = true; |
| if (ac1.length() != 2) |
| { |
| passed = false; |
| } |
| uint value = 0u; |
| if (ac1[value = testSideEffectValue].length() != 3) |
| { |
| passed = false; |
| } |
| if (value != testSideEffectValue) |
| { |
| passed = false; |
| } |
| if (ac1[1][value = testSideEffectValue + 1u].length() != 4) |
| { |
| passed = false; |
| } |
| if (value != testSideEffectValue + 1u) |
| { |
| passed = false; |
| } |
| result.value = passed ? 255u : 127u; |
| })"; |
| |
| constexpr unsigned int kUniformTestValue = 17; |
| constexpr unsigned int kExpectedSuccessValue = 255; |
| constexpr unsigned int kAtomicCounterRows = 2; |
| constexpr unsigned int kAtomicCounterCols = 3; |
| |
| GLint maxAtomicCounters = 0; |
| glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTERS, &maxAtomicCounters); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Required minimum is 8 by the spec |
| EXPECT_GE(maxAtomicCounters, 8); |
| ANGLE_SKIP_TEST_IF(static_cast<uint32_t>(maxAtomicCounters) < |
| kAtomicCounterRows * kAtomicCounterCols); |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| glUseProgram(program.get()); |
| |
| constexpr unsigned int kBufferData[kAtomicCounterRows * kAtomicCounterCols] = {}; |
| GLBuffer atomicCounterBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(kBufferData), kBufferData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer); |
| |
| constexpr unsigned int kOutputInitValue = 0; |
| GLBuffer shaderStorageBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kOutputInitValue), &kOutputInitValue, |
| GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer); |
| |
| GLint uniformLocation = glGetUniformLocation(program, "testSideEffectValue"); |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1ui(uniformLocation, kUniformTestValue); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), GL_MAP_READ_BIT)); |
| EXPECT_EQ(*ptr, kExpectedSuccessValue); |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that inactive images don't cause any errors. |
| TEST_P(GLSLTest_ES31, InactiveImages) |
| { |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| layout(rgba32ui) uniform highp readonly uimage2D image1; |
| layout(rgba32ui) uniform highp readonly uimage2D image2[4]; |
| void main() |
| { |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| |
| glUseProgram(program.get()); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Verify that the images are indeed inactive. |
| GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, "image1"); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(GL_INVALID_INDEX, index); |
| |
| index = glGetProgramResourceIndex(program, GL_UNIFORM, "image2"); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(GL_INVALID_INDEX, index); |
| } |
| |
| // Test that inactive atomic counters don't cause any errors. |
| TEST_P(GLSLTest_ES31, InactiveAtomicCounters) |
| { |
| constexpr char kCS[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| layout(binding = 0, offset = 0) uniform atomic_uint ac1; |
| layout(binding = 0, offset = 4) uniform atomic_uint ac2[5]; |
| void main() |
| { |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| |
| glUseProgram(program.get()); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Verify that the atomic counters are indeed inactive. |
| GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, "ac1"); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(GL_INVALID_INDEX, index); |
| |
| index = glGetProgramResourceIndex(program, GL_UNIFORM, "ac2"); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(GL_INVALID_INDEX, index); |
| } |
| |
| // Test that inactive samplers in structs don't cause any errors. |
| TEST_P(GLSLTest_ES31, InactiveSamplersInStructCS) |
| { |
| constexpr char kCS[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| struct S |
| { |
| vec4 v; |
| sampler2D t[10]; |
| }; |
| uniform S s; |
| void main() |
| { |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| |
| glUseProgram(program.get()); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that array indices for arrays of arrays of basic types work as expected. |
| TEST_P(GLSLTest_ES31, ArraysOfArraysBasicType) |
| { |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform ivec2 test[2][2];\n" |
| "void main() {\n" |
| " bool passed = true;\n" |
| " for (int i = 0; i < 2; i++) {\n" |
| " for (int j = 0; j < 2; j++) {\n" |
| " if (test[i][j] != ivec2(i + 1, j + 1)) {\n" |
| " passed = false;\n" |
| " }\n" |
| " }\n" |
| " }\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "][" << j << "]"; |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform2i(uniformLocation, i + 1, j + 1); |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that array indices for arrays of arrays of basic types work as expected |
| // inside blocks. |
| TEST_P(GLSLTest_ES31, ArraysOfArraysBlockBasicType) |
| { |
| // anglebug.com/3821 - fails on AMD Windows |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL()); |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "layout(packed) uniform UBO { ivec2 test[2][2]; } ubo_data;\n" |
| "void main() {\n" |
| " bool passed = true;\n" |
| " for (int i = 0; i < 2; i++) {\n" |
| " for (int j = 0; j < 2; j++) {\n" |
| " if (ubo_data.test[i][j] != ivec2(i + 1, j + 1)) {\n" |
| " passed = false;\n" |
| " }\n" |
| " }\n" |
| " }\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| // Use interface queries to determine buffer size and offset |
| GLuint uboBlockIndex = glGetProgramResourceIndex(program.get(), GL_UNIFORM_BLOCK, "UBO"); |
| GLenum uboDataSizeProp = GL_BUFFER_DATA_SIZE; |
| GLint uboDataSize; |
| glGetProgramResourceiv(program.get(), GL_UNIFORM_BLOCK, uboBlockIndex, 1, &uboDataSizeProp, 1, |
| nullptr, &uboDataSize); |
| std::unique_ptr<char[]> uboData(new char[uboDataSize]); |
| for (int i = 0; i < 2; i++) |
| { |
| std::stringstream resourceName; |
| resourceName << "UBO.test[" << i << "][0]"; |
| GLenum resourceProps[] = {GL_ARRAY_STRIDE, GL_OFFSET}; |
| struct |
| { |
| GLint stride; |
| GLint offset; |
| } values; |
| GLuint resourceIndex = |
| glGetProgramResourceIndex(program.get(), GL_UNIFORM, resourceName.str().c_str()); |
| ASSERT_NE(resourceIndex, GL_INVALID_INDEX); |
| glGetProgramResourceiv(program.get(), GL_UNIFORM, resourceIndex, 2, &resourceProps[0], 2, |
| nullptr, &values.stride); |
| for (int j = 0; j < 2; j++) |
| { |
| GLint(&dataPtr)[2] = |
| *reinterpret_cast<GLint(*)[2]>(&uboData[values.offset + j * values.stride]); |
| dataPtr[0] = i + 1; |
| dataPtr[1] = j + 1; |
| } |
| } |
| GLBuffer ubo; |
| glBindBuffer(GL_UNIFORM_BUFFER, ubo.get()); |
| glBufferData(GL_UNIFORM_BUFFER, uboDataSize, &uboData[0], GL_STATIC_DRAW); |
| GLuint ubo_index = glGetUniformBlockIndex(program.get(), "UBO"); |
| ASSERT_NE(ubo_index, GL_INVALID_INDEX); |
| glUniformBlockBinding(program.get(), ubo_index, 5); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 5, ubo.get()); |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that arrays of arrays of samplers work as expected. |
| TEST_P(GLSLTest_ES31, ArraysOfArraysSampler) |
| { |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform mediump isampler2D test[2][2];\n" |
| "void main() {\n" |
| " bool passed = true;\n" |
| "#define DO_CHECK(i,j) \\\n" |
| " if (texture(test[i][j], vec2(0.0, 0.0)) != ivec4(i + 1, j + 1, 0, 1)) { \\\n" |
| " passed = false; \\\n" |
| " }\n" |
| " DO_CHECK(0, 0)\n" |
| " DO_CHECK(0, 1)\n" |
| " DO_CHECK(1, 0)\n" |
| " DO_CHECK(1, 1)\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[2][2]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| // First generate the texture |
| int textureUnit = i * 2 + j; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j]); |
| GLint texData[2] = {i + 1, j + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32I, 1, 1, 0, GL_RG_INTEGER, GL_INT, &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "][" << j << "]"; |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that arrays of arrays of images work as expected. |
| TEST_P(GLSLTest_ES31, ArraysOfArraysImage) |
| { |
| // http://anglebug.com/5072 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL()); |
| |
| // Fails on D3D due to mistranslation. |
| ANGLE_SKIP_TEST_IF(IsD3D()); |
| |
| // Fails on Android on GLES. |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| GLint maxTextures, maxComputeImageUniforms; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures); |
| glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms); |
| ANGLE_SKIP_TEST_IF(maxTextures < 1 * 2 * 3); |
| ANGLE_SKIP_TEST_IF(maxComputeImageUniforms < 1 * 2 * 3); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| layout(binding = 0, r32ui) uniform highp readonly uimage2D image[1][2][3]; |
| layout(binding = 1, std430) buffer Output { |
| uint image_value; |
| } outbuf; |
| |
| void main(void) |
| { |
| outbuf.image_value = uint(0.0); |
| outbuf.image_value += imageLoad(image[0][0][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image[0][0][1], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image[0][0][2], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image[0][1][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image[0][1][1], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image[0][1][2], ivec2(0, 0)).x; |
| })"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| GLuint outputInitData[1] = {10}; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLuint imageData = 200u; |
| GLTexture images[1][2][3]; |
| for (int i = 0; i < 1; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| for (int k = 0; k < 3; k++) |
| { |
| glBindTexture(GL_TEXTURE_2D, images[i][j][k]); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, |
| &imageData); |
| glBindImageTexture(i * 6 + j * 3 + k, images[i][j][k], 0, GL_FALSE, 0, GL_READ_ONLY, |
| GL_R32UI); |
| EXPECT_GL_NO_ERROR(); |
| } |
| } |
| } |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| memcpy(outputInitData, ptr, sizeof(outputInitData)); |
| EXPECT_EQ(outputInitData[0], imageData * 1 * 2 * 3); |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that multiple arrays of arrays of images work as expected. |
| TEST_P(GLSLTest_ES31, ConsecutiveArraysOfArraysImage) |
| { |
| // http://anglebug.com/5072 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL()); |
| |
| // Fails on D3D due to mistranslation. |
| ANGLE_SKIP_TEST_IF(IsD3D()); |
| |
| constexpr GLsizei kImage1Layers = 3; |
| constexpr GLsizei kImage1Rows = 2; |
| constexpr GLsizei kImage1Cols = 1; |
| constexpr GLsizei kImage2Rows = 2; |
| constexpr GLsizei kImage2Cols = 4; |
| |
| constexpr GLsizei kImage1Units = kImage1Layers * kImage1Rows * kImage1Cols; |
| constexpr GLsizei kImage2Units = kImage2Rows * kImage2Cols; |
| constexpr GLsizei kImage3Units = 1; |
| |
| constexpr GLsizei kTotalImageCount = kImage1Units + kImage2Units + kImage3Units; |
| |
| GLint maxTextures, maxComputeImageUniforms; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures); |
| glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms); |
| ANGLE_SKIP_TEST_IF(maxTextures < kTotalImageCount); |
| ANGLE_SKIP_TEST_IF(maxComputeImageUniforms < kTotalImageCount); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| layout(binding = 0, r32ui) uniform highp readonly uimage2D image1[3][2][1]; |
| layout(binding = 6, r32ui) uniform highp readonly uimage2D image2[2][4]; |
| layout(binding = 14, r32ui) uniform highp readonly uimage2D image3; |
| layout(binding = 0, std430) buffer Output { |
| uint image_value; |
| } outbuf; |
| |
| void main(void) |
| { |
| outbuf.image_value = uint(0.0); |
| |
| outbuf.image_value += imageLoad(image1[0][0][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image1[0][1][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image1[1][0][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image1[1][1][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image1[2][0][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image1[2][1][0], ivec2(0, 0)).x; |
| |
| outbuf.image_value += imageLoad(image2[0][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[0][1], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[0][2], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[0][3], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[1][0], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[1][1], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[1][2], ivec2(0, 0)).x; |
| outbuf.image_value += imageLoad(image2[1][3], ivec2(0, 0)).x; |
| |
| outbuf.image_value += imageLoad(image3, ivec2(0, 0)).x; |
| })"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| constexpr GLuint kOutputInitData = 10; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kOutputInitData), &kOutputInitData, |
| GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr GLsizei kImage1Binding = 0; |
| constexpr GLsizei kImage2Binding = kImage1Binding + kImage1Units; |
| constexpr GLsizei kImage3Binding = kImage2Binding + kImage2Units; |
| |
| constexpr GLuint kImage1Data = 13; |
| GLTexture images1[kImage1Layers][kImage1Rows][kImage1Cols]; |
| for (int layer = 0; layer < kImage1Layers; layer++) |
| { |
| for (int row = 0; row < kImage1Rows; row++) |
| { |
| for (int col = 0; col < kImage1Cols; col++) |
| { |
| glBindTexture(GL_TEXTURE_2D, images1[layer][row][col]); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, |
| &kImage1Data); |
| glBindImageTexture(kImage1Binding + (layer * kImage1Rows + row) * kImage1Cols + col, |
| images1[layer][row][col], 0, GL_FALSE, 0, GL_READ_ONLY, |
| GL_R32UI); |
| EXPECT_GL_NO_ERROR(); |
| } |
| } |
| } |
| |
| constexpr GLuint kImage2Data = 17; |
| GLTexture images2[kImage2Rows][kImage2Cols]; |
| for (int row = 0; row < kImage2Rows; row++) |
| { |
| for (int col = 0; col < kImage2Cols; col++) |
| { |
| glBindTexture(GL_TEXTURE_2D, images2[row][col]); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, |
| &kImage2Data); |
| glBindImageTexture(kImage2Binding + row * kImage2Cols + col, images2[row][col], 0, |
| GL_FALSE, 0, GL_READ_ONLY, GL_R32UI); |
| EXPECT_GL_NO_ERROR(); |
| } |
| } |
| |
| constexpr GLuint kImage3Data = 19; |
| GLTexture image3; |
| glBindTexture(GL_TEXTURE_2D, image3); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &kImage3Data); |
| glBindImageTexture(kImage3Binding, image3, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(kOutputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(*ptr, |
| kImage1Data * kImage1Units + kImage2Data * kImage2Units + kImage3Data * kImage3Units); |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that arrays of arrays of images of r32f format work when passed to functions. |
| TEST_P(GLSLTest_ES31, ArraysOfArraysOfR32fImages) |
| { |
| // Skip if GL_OES_shader_image_atomic is not enabled. |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_shader_image_atomic")); |
| |
| // http://anglebug.com/5072 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL()); |
| |
| // Fails on D3D due to mistranslation. |
| ANGLE_SKIP_TEST_IF(IsD3D()); |
| |
| // Fails on Android on GLES. |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| // http://anglebug.com/5353 |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL()); |
| |
| GLint maxComputeImageUniforms; |
| glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms); |
| ANGLE_SKIP_TEST_IF(maxComputeImageUniforms < 7); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| #extension GL_OES_shader_image_atomic : require |
| |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| layout(binding = 0, r32f) uniform highp image2D image1[2][3]; |
| layout(binding = 6, r32f) uniform highp image2D image2; |
| |
| void testFunction(image2D imageOut[2][3]) |
| { |
| // image1 is an array of 1x1 images. |
| // image2 is a 1x4 image with the following data: |
| // |
| // (0, 0): 234.5 |
| // (0, 1): 4.0 |
| // (0, 2): 456.0 |
| // (0, 3): 987.0 |
| |
| |
| // Write to [0][0] |
| imageStore(imageOut[0][0], ivec2(0, 0), vec4(1234.5)); |
| |
| // Write to [0][1] |
| imageStore(imageOut[0][1], ivec2(0, 0), imageLoad(image2, ivec2(0, 0))); |
| |
| // Write to [0][2] |
| imageStore(imageOut[0][2], ivec2(0, 0), vec4(imageSize(image2).y)); |
| |
| // Write to [1][0] |
| imageStore(imageOut[1][0], ivec2(0, |
| imageSize(image2).y - int(imageLoad(image2, ivec2(0, 1)).x) |
| ), vec4(678.0)); |
| |
| // Write to [1][1] |
| imageStore(imageOut[1][1], ivec2(0, 0), |
| vec4(imageAtomicExchange(image2, ivec2(0, 2), 135.0))); |
| |
| // Write to [1][2] |
| imageStore(imageOut[1][2], ivec2(0, 0), |
| imageLoad(image2, ivec2(imageSize(image2).x - 1, 3))); |
| } |
| |
| void main(void) |
| { |
| testFunction(image1); |
| })"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| constexpr GLsizei kImageRows = 2; |
| constexpr GLsizei kImageCols = 3; |
| constexpr GLfloat kImageData = 0; |
| GLTexture images[kImageRows][kImageCols]; |
| for (size_t row = 0; row < kImageRows; row++) |
| { |
| for (size_t col = 0; col < kImageCols; col++) |
| { |
| glBindTexture(GL_TEXTURE_2D, images[row][col]); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED, GL_FLOAT, &kImageData); |
| glBindImageTexture(row * kImageCols + col, images[row][col], 0, GL_FALSE, 0, |
| GL_READ_WRITE, GL_R32F); |
| EXPECT_GL_NO_ERROR(); |
| } |
| } |
| |
| constexpr GLsizei kImage2Size = 4; |
| constexpr std::array<GLfloat, kImage2Size> kImage2Data = { |
| 234.5f, |
| 4.0f, |
| 456.0f, |
| 987.0f, |
| }; |
| GLTexture image2; |
| glBindTexture(GL_TEXTURE_2D, image2); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, 1, kImage2Size); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, kImage2Size, GL_RED, GL_FLOAT, kImage2Data.data()); |
| glBindImageTexture(6, image2, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32F); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); |
| |
| // Verify the previous dispatch with another dispatch |
| constexpr char kVerifyShader[] = R"(#version 310 es |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| layout(binding = 0, r32f) uniform highp readonly image2D image1[2][3]; |
| layout(binding = 6, r32f) uniform highp readonly image2D image2; |
| layout(binding = 0, std430) buffer Output { |
| float image2Data[4]; |
| float image1Data[6]; |
| } outbuf; |
| |
| void main(void) |
| { |
| for (int i = 0; i < 4; ++i) |
| { |
| outbuf.image2Data[i] = imageLoad(image2, ivec2(0, i)).x; |
| } |
| outbuf.image1Data[0] = imageLoad(image1[0][0], ivec2(0, 0)).x; |
| outbuf.image1Data[1] = imageLoad(image1[0][1], ivec2(0, 0)).x; |
| outbuf.image1Data[2] = imageLoad(image1[0][2], ivec2(0, 0)).x; |
| outbuf.image1Data[3] = imageLoad(image1[1][0], ivec2(0, 0)).x; |
| outbuf.image1Data[4] = imageLoad(image1[1][1], ivec2(0, 0)).x; |
| outbuf.image1Data[5] = imageLoad(image1[1][2], ivec2(0, 0)).x; |
| })"; |
| ANGLE_GL_COMPUTE_PROGRAM(verifyProgram, kVerifyShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(verifyProgram); |
| |
| constexpr std::array<GLfloat, kImage2Size + kImageRows *kImageCols> kOutputInitData = {}; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kOutputInitData), kOutputInitData.data(), |
| GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // Verify |
| const GLfloat *ptr = reinterpret_cast<const GLfloat *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(kOutputInitData), GL_MAP_READ_BIT)); |
| |
| EXPECT_EQ(ptr[0], kImage2Data[0]); |
| EXPECT_EQ(ptr[1], kImage2Data[1]); |
| EXPECT_NEAR(ptr[2], 135.0f, 0.0001f); |
| EXPECT_EQ(ptr[3], kImage2Data[3]); |
| |
| EXPECT_NEAR(ptr[4], 1234.5f, 0.0001f); |
| EXPECT_NEAR(ptr[5], kImage2Data[0], 0.0001f); |
| EXPECT_NEAR(ptr[6], kImage2Size, 0.0001f); |
| EXPECT_NEAR(ptr[7], 678.0f, 0.0001f); |
| EXPECT_NEAR(ptr[8], kImage2Data[2], 0.0001f); |
| EXPECT_NEAR(ptr[9], kImage2Data[3], 0.0001f); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that structs containing arrays of samplers work as expected. |
| TEST_P(GLSLTest_ES31, StructArraySampler) |
| { |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump sampler2D data[2]; };\n" |
| "uniform Data test;\n" |
| "void main() {\n" |
| " my_FragColor = vec4(texture(test.data[0], vec2(0.0, 0.0)).rg,\n" |
| " texture(test.data[1], vec2(0.0, 0.0)).rg);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[2]; |
| GLColor expected = MakeGLColor(32, 64, 96, 255); |
| GLubyte data[6] = {}; // Two bytes of padding, so that texture can be initialized with 4 bytes |
| memcpy(data, expected.data(), sizeof(expected)); |
| for (int i = 0; i < 2; i++) |
| { |
| glActiveTexture(GL_TEXTURE0 + i); |
| glBindTexture(GL_TEXTURE_2D, textures[i]); |
| // Each element provides two components. |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + 2 * i); |
| std::stringstream uniformName; |
| uniformName << "test.data[" << i << "]"; |
| // Then send it as a uniform |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // The uniform should be active. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, i); |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, expected); |
| } |
| |
| // Test that arrays of arrays of samplers inside structs work as expected. |
| TEST_P(GLSLTest_ES31, StructArrayArraySampler) |
| { |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump isampler2D data[2][2]; };\n" |
| "uniform Data test;\n" |
| "void main() {\n" |
| " bool passed = true;\n" |
| "#define DO_CHECK(i,j) \\\n" |
| " if (texture(test.data[i][j], vec2(0.0, 0.0)) != ivec4(i + 1, j + 1, 0, 1)) { \\\n" |
| " passed = false; \\\n" |
| " }\n" |
| " DO_CHECK(0, 0)\n" |
| " DO_CHECK(0, 1)\n" |
| " DO_CHECK(1, 0)\n" |
| " DO_CHECK(1, 1)\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[2][2]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| // First generate the texture |
| int textureUnit = i * 2 + j; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j]); |
| GLint texData[2] = {i + 1, j + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32I, 1, 1, 0, GL_RG_INTEGER, GL_INT, &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test.data[" << i << "][" << j << "]"; |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that an array of structs with arrays of arrays of samplers works. |
| TEST_P(GLSLTest_ES31, ArrayStructArrayArraySampler) |
| { |
| GLint numTextures; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures); |
| ANGLE_SKIP_TEST_IF(numTextures < 2 * (2 * 2 + 2 * 2)); |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump isampler2D data0[2][2]; mediump isampler2D data1[2][2]; };\n" |
| "uniform Data test[2];\n" |
| "void main() {\n" |
| " bool passed = true;\n" |
| "#define DO_CHECK_ikl(i,k,l) \\\n" |
| " if (texture(test[i].data0[k][l], vec2(0.0, 0.0)) != ivec4(i, 0, k, l)+1) { \\\n" |
| " passed = false; \\\n" |
| " } \\\n" |
| " if (texture(test[i].data1[k][l], vec2(0.0, 0.0)) != ivec4(i, 1, k, l)+1) { \\\n" |
| " passed = false; \\\n" |
| " }\n" |
| "#define DO_CHECK_ik(i,k) \\\n" |
| " DO_CHECK_ikl(i, k, 0) \\\n" |
| " DO_CHECK_ikl(i, k, 1)\n" |
| "#define DO_CHECK_i(i) \\\n" |
| " DO_CHECK_ik(i, 0) \\\n" |
| " DO_CHECK_ik(i, 1)\n" |
| " DO_CHECK_i(0)\n" |
| " DO_CHECK_i(1)\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[2][2][2][2]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| for (int k = 0; k < 2; k++) |
| { |
| for (int l = 0; l < 2; l++) |
| { |
| // First generate the texture |
| int textureUnit = l + 2 * (k + 2 * (j + 2 * i)); |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j][k][l]); |
| GLint texData[4] = {i + 1, j + 1, k + 1, l + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32I, 1, 1, 0, GL_RGBA_INTEGER, GL_INT, |
| &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "].data" << j << "[" << k << "][" << l << "]"; |
| GLint uniformLocation = |
| glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a complex chain of structs and arrays of samplers works as expected. |
| TEST_P(GLSLTest_ES31, ComplexStructArraySampler) |
| { |
| GLint numTextures; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures); |
| ANGLE_SKIP_TEST_IF(numTextures < 2 * 3 * (2 + 3)); |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump isampler2D data0[2]; mediump isampler2D data1[3]; };\n" |
| "uniform Data test[2][3];\n" |
| "const vec2 ZERO = vec2(0.0, 0.0);\n" |
| "void main() {\n" |
| " bool passed = true;\n" |
| "#define DO_CHECK_INNER0(i,j,l) \\\n" |
| " if (texture(test[i][j].data0[l], ZERO) != ivec4(i, j, 0, l) + 1) { \\\n" |
| " passed = false; \\\n" |
| " }\n" |
| "#define DO_CHECK_INNER1(i,j,l) \\\n" |
| " if (texture(test[i][j].data1[l], ZERO) != ivec4(i, j, 1, l) + 1) { \\\n" |
| " passed = false; \\\n" |
| " }\n" |
| "#define DO_CHECK(i,j) \\\n" |
| " DO_CHECK_INNER0(i, j, 0) \\\n" |
| " DO_CHECK_INNER0(i, j, 1) \\\n" |
| " DO_CHECK_INNER1(i, j, 0) \\\n" |
| " DO_CHECK_INNER1(i, j, 1) \\\n" |
| " DO_CHECK_INNER1(i, j, 2)\n" |
| " DO_CHECK(0, 0)\n" |
| " DO_CHECK(0, 1)\n" |
| " DO_CHECK(0, 2)\n" |
| " DO_CHECK(1, 0)\n" |
| " DO_CHECK(1, 1)\n" |
| " DO_CHECK(1, 2)\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| struct Data |
| { |
| GLTexture data1[2]; |
| GLTexture data2[3]; |
| }; |
| Data textures[2][3]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 3; j++) |
| { |
| GLTexture *arrays[] = {&textures[i][j].data1[0], &textures[i][j].data2[0]}; |
| size_t arrayLengths[] = {2, 3}; |
| size_t arrayOffsets[] = {0, 2}; |
| size_t totalArrayLength = 5; |
| for (int k = 0; k < 2; k++) |
| { |
| GLTexture *array = arrays[k]; |
| size_t arrayLength = arrayLengths[k]; |
| size_t arrayOffset = arrayOffsets[k]; |
| for (int l = 0; l < static_cast<int>(arrayLength); l++) |
| { |
| // First generate the texture |
| int textureUnit = arrayOffset + l + totalArrayLength * (j + 3 * i); |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, array[l]); |
| GLint texData[4] = {i + 1, j + 1, k + 1, l + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32I, 1, 1, 0, GL_RGBA_INTEGER, GL_INT, |
| &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "][" << j << "].data" << k << "[" << l << "]"; |
| GLint uniformLocation = |
| glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| TEST_P(GLSLTest_ES31, ArraysOfArraysStructDifferentTypesSampler) |
| { |
| GLint numTextures; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures); |
| ANGLE_SKIP_TEST_IF(numTextures < 3 * (2 + 2)); |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump isampler2D data0[2]; mediump sampler2D data1[2]; };\n" |
| "uniform Data test[3];\n" |
| "ivec4 f2i(vec4 x) { return ivec4(x * 4.0 + 0.5); }" |
| "void main() {\n" |
| " bool passed = true;\n" |
| "#define DO_CHECK_ik(i,k) \\\n" |
| " if (texture(test[i].data0[k], vec2(0.0, 0.0)) != ivec4(i, 0, k, 0)+1) { \\\n" |
| " passed = false; \\\n" |
| " } \\\n" |
| " if (f2i(texture(test[i].data1[k], vec2(0.0, 0.0))) != ivec4(i, 1, k, 0)+1) { \\\n" |
| " passed = false; \\\n" |
| " }\n" |
| "#define DO_CHECK_i(i) \\\n" |
| " DO_CHECK_ik(i, 0) \\\n" |
| " DO_CHECK_ik(i, 1)\n" |
| " DO_CHECK_i(0)\n" |
| " DO_CHECK_i(1)\n" |
| " DO_CHECK_i(2)\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[3][2][2]; |
| for (int i = 0; i < 3; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| for (int k = 0; k < 2; k++) |
| { |
| // First generate the texture |
| int textureUnit = k + 2 * (j + 2 * i); |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j][k]); |
| GLint texData[4] = {i + 1, j + 1, k + 1, 1}; |
| GLubyte texDataFloat[4] = {static_cast<GLubyte>((i + 1) * 64 - 1), |
| static_cast<GLubyte>((j + 1) * 64 - 1), |
| static_cast<GLubyte>((k + 1) * 64 - 1), 64}; |
| if (j == 0) |
| { |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32I, 1, 1, 0, GL_RGBA_INTEGER, GL_INT, |
| &texData[0]); |
| } |
| else |
| { |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &texDataFloat[0]); |
| } |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "].data" << j << "[" << k << "]"; |
| GLint uniformLocation = |
| glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that arrays of arrays of samplers as parameters works as expected. |
| TEST_P(GLSLTest_ES31, ParameterArraysOfArraysSampler) |
| { |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform mediump isampler2D test[2][3];\n" |
| "const vec2 ZERO = vec2(0.0, 0.0);\n" |
| "\n" |
| "bool check(isampler2D data[2][3]);\n" |
| "bool check(isampler2D data[2][3]) {\n" |
| "#define DO_CHECK(i,j) \\\n" |
| " if (texture(data[i][j], ZERO) != ivec4(i+1, j+1, 0, 1)) { \\\n" |
| " return false; \\\n" |
| " }\n" |
| " DO_CHECK(0, 0)\n" |
| " DO_CHECK(0, 1)\n" |
| " DO_CHECK(0, 2)\n" |
| " DO_CHECK(1, 0)\n" |
| " DO_CHECK(1, 1)\n" |
| " DO_CHECK(1, 2)\n" |
| " return true;\n" |
| "}\n" |
| "void main() {\n" |
| " bool passed = check(test);\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[2][3]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 3; j++) |
| { |
| // First generate the texture |
| int textureUnit = i * 3 + j; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j]); |
| GLint texData[2] = {i + 1, j + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32I, 1, 1, 0, GL_RG_INTEGER, GL_INT, &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "][" << j << "]"; |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that structs with arrays of arrays of samplers as parameters works as expected. |
| TEST_P(GLSLTest_ES31, ParameterStructArrayArraySampler) |
| { |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump isampler2D data[2][3]; };\n" |
| "uniform Data test;\n" |
| "const vec2 ZERO = vec2(0.0, 0.0);\n" |
| "\n" |
| "bool check(Data data) {\n" |
| "#define DO_CHECK(i,j) \\\n" |
| " if (texture(data.data[i][j], ZERO) != ivec4(i+1, j+1, 0, 1)) { \\\n" |
| " return false; \\\n" |
| " }\n" |
| " DO_CHECK(0, 0)\n" |
| " DO_CHECK(0, 1)\n" |
| " DO_CHECK(0, 2)\n" |
| " DO_CHECK(1, 0)\n" |
| " DO_CHECK(1, 1)\n" |
| " DO_CHECK(1, 2)\n" |
| " return true;\n" |
| "}\n" |
| "void main() {\n" |
| " bool passed = check(test);\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[2][3]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 3; j++) |
| { |
| // First generate the texture |
| int textureUnit = i * 3 + j; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j]); |
| GLint texData[2] = {i + 1, j + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32I, 1, 1, 0, GL_RG_INTEGER, GL_INT, &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test.data[" << i << "][" << j << "]"; |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that arrays of arrays of structs with arrays of arrays of samplers |
| // as parameters works as expected. |
| TEST_P(GLSLTest_ES31, ParameterArrayArrayStructArrayArraySampler) |
| { |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| GLint numTextures; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures); |
| ANGLE_SKIP_TEST_IF(numTextures < 3 * 2 * 2 * 2); |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "struct Data { mediump isampler2D data[2][2]; };\n" |
| "uniform Data test[3][2];\n" |
| "const vec2 ZERO = vec2(0.0, 0.0);\n" |
| "\n" |
| "bool check(Data data[3][2]) {\n" |
| "#define DO_CHECK_ijkl(i,j,k,l) \\\n" |
| " if (texture(data[i][j].data[k][l], ZERO) != ivec4(i, j, k, l) + 1) { \\\n" |
| " return false; \\\n" |
| " }\n" |
| "#define DO_CHECK_ij(i,j) \\\n" |
| " DO_CHECK_ijkl(i, j, 0, 0) \\\n" |
| " DO_CHECK_ijkl(i, j, 0, 1) \\\n" |
| " DO_CHECK_ijkl(i, j, 1, 0) \\\n" |
| " DO_CHECK_ijkl(i, j, 1, 1)\n" |
| " DO_CHECK_ij(0, 0)\n" |
| " DO_CHECK_ij(1, 0)\n" |
| " DO_CHECK_ij(2, 0)\n" |
| " DO_CHECK_ij(0, 1)\n" |
| " DO_CHECK_ij(1, 1)\n" |
| " DO_CHECK_ij(2, 1)\n" |
| " return true;\n" |
| "}\n" |
| "void main() {\n" |
| " bool passed = check(test);\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures[3][2][2][2]; |
| for (int i = 0; i < 3; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| for (int k = 0; k < 2; k++) |
| { |
| for (int l = 0; l < 2; l++) |
| { |
| // First generate the texture |
| int textureUnit = l + 2 * (k + 2 * (j + 2 * i)); |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[i][j][k][l]); |
| GLint texData[4] = {i + 1, j + 1, k + 1, l + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32I, 1, 1, 0, GL_RGBA_INTEGER, GL_INT, |
| &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "][" << j << "].data[" << k << "][" << l << "]"; |
| GLint uniformLocation = |
| glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| } |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that 3D arrays with sub-arrays passed as parameters works as expected. |
| TEST_P(GLSLTest_ES31, ParameterArrayArrayArraySampler) |
| { |
| GLint numTextures; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures); |
| ANGLE_SKIP_TEST_IF(numTextures < 2 * 3 * 4 + 4); |
| |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| // http://anglebug.com/5546 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "uniform mediump isampler2D test[2][3][4];\n" |
| "uniform mediump isampler2D test2[4];\n" |
| "const vec2 ZERO = vec2(0.0, 0.0);\n" |
| "\n" |
| "bool check1D(isampler2D arr[4], int x, int y) {\n" |
| " if (texture(arr[0], ZERO) != ivec4(x, y, 0, 0)+1) return false;\n" |
| " if (texture(arr[1], ZERO) != ivec4(x, y, 1, 0)+1) return false;\n" |
| " if (texture(arr[2], ZERO) != ivec4(x, y, 2, 0)+1) return false;\n" |
| " if (texture(arr[3], ZERO) != ivec4(x, y, 3, 0)+1) return false;\n" |
| " return true;\n" |
| "}\n" |
| "bool check2D(isampler2D arr[3][4], int x) {\n" |
| " if (!check1D(arr[0], x, 0)) return false;\n" |
| " if (!check1D(arr[1], x, 1)) return false;\n" |
| " if (!check1D(arr[2], x, 2)) return false;\n" |
| " return true;\n" |
| "}\n" |
| "bool check3D(isampler2D arr[2][3][4]) {\n" |
| " if (!check2D(arr[0], 0)) return false;\n" |
| " if (!check2D(arr[1], 1)) return false;\n" |
| " return true;\n" |
| "}\n" |
| "void main() {\n" |
| " bool passed = check3D(test) && check1D(test2, 7, 8);\n" |
| " my_FragColor = passed ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program.get()); |
| GLTexture textures1[2][3][4]; |
| GLTexture textures2[4]; |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 3; j++) |
| { |
| for (int k = 0; k < 4; k++) |
| { |
| // First generate the texture |
| int textureUnit = k + 4 * (j + 3 * i); |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures1[i][j][k]); |
| GLint texData[3] = {i + 1, j + 1, k + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32I, 1, 1, 0, GL_RGB_INTEGER, GL_INT, |
| &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test[" << i << "][" << j << "][" << k << "]"; |
| GLint uniformLocation = |
| glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| } |
| } |
| for (int k = 0; k < 4; k++) |
| { |
| // First generate the texture |
| int textureUnit = 2 * 3 * 4 + k; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures2[k]); |
| GLint texData[3] = {7 + 1, 8 + 1, k + 1}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32I, 1, 1, 0, GL_RGB_INTEGER, GL_INT, &texData[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Then send it as a uniform |
| std::stringstream uniformName; |
| uniformName << "test2[" << k << "]"; |
| GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str()); |
| // All array indices should be used. |
| EXPECT_NE(uniformLocation, -1); |
| glUniform1i(uniformLocation, textureUnit); |
| } |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that names do not collide when translating arrays of arrays of samplers. |
| TEST_P(GLSLTest_ES31, ArraysOfArraysNameCollisionSampler) |
| { |
| ANGLE_SKIP_TEST_IF(IsVulkan()); // anglebug.com/3604 - rewriter can create name collisions |
| GLint numTextures; |
| glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures); |
| ANGLE_SKIP_TEST_IF(numTextures < 2 * 2 + 3 * 3 + 4 * 4); |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump sampler2D;\n" |
| "precision mediump float;\n" |
| "uniform sampler2D test_field1_field2[2][2];\n" |
| "struct S1 { sampler2D field2[3][3]; }; uniform S1 test_field1;\n" |
| "struct S2 { sampler2D field1_field2[4][4]; }; uniform S2 test;\n" |
| "vec4 func1(sampler2D param_field1_field2[2][2],\n" |
| " int param_field1_field2_offset,\n" |
| " S1 param_field1,\n" |
| " S2 param) {\n" |
| " return vec4(0.0, 1.0, 0.0, 0.0);\n" |
| "}\n" |
| "out vec4 my_FragColor;\n" |
| "void main() {\n" |
| " my_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| " my_FragColor += func1(test_field1_field2, 0, test_field1, test);\n" |
| " vec2 uv = vec2(0.0);\n" |
| " my_FragColor += texture(test_field1_field2[0][0], uv) +\n" |
| " texture(test_field1.field2[0][0], uv) +\n" |
| " texture(test.field1_field2[0][0], uv);\n" |
| "}\n"; |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glActiveTexture(GL_TEXTURE0); |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLint zero = 0; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &zero); |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that regular arrays are unmodified. |
| TEST_P(GLSLTest_ES31, BasicTypeArrayAndArrayOfSampler) |
| { |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision mediump sampler2D;\n" |
| "precision mediump float;\n" |
| "uniform sampler2D sampler_array[2][2];\n" |
| "uniform int array[3][2];\n" |
| "vec4 func1(int param[2],\n" |
| " int param2[3]) {\n" |
| " return vec4(0.0, 1.0, 0.0, 0.0);\n" |
| "}\n" |
| "out vec4 my_FragColor;\n" |
| "void main() {\n" |
| " my_FragColor = texture(sampler_array[0][0], vec2(0.0));\n" |
| " my_FragColor += func1(array[1], int[](1, 2, 3));\n" |
| "}\n"; |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glActiveTexture(GL_TEXTURE0); |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLint zero = 0; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &zero); |
| drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // This test covers a bug (and associated workaround) with nested sampling operations in the HLSL |
| // compiler DLL. |
| TEST_P(GLSLTest_ES3, NestedSamplingOperation) |
| { |
| // This seems to be bugged on some version of Android. Might not affect the newest versions. |
| // TODO(jmadill): Lift suppression when Chromium bots are upgraded. |
| // Test skipped on Android because of bug with Nexus 5X. |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "out vec2 texCoord;\n" |
| "in vec2 position;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(position, 0, 1);\n" |
| " texCoord = position * 0.5 + vec2(0.5);\n" |
| "}\n"; |
| |
| constexpr char kSimpleFS[] = |
| "#version 300 es\n" |
| "in mediump vec2 texCoord;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main()\n" |
| "{\n" |
| " fragColor = vec4(texCoord, 0, 1);\n" |
| "}\n"; |
| |
| constexpr char kNestedFS[] = |
| "#version 300 es\n" |
| "uniform mediump sampler2D samplerA;\n" |
| "uniform mediump sampler2D samplerB;\n" |
| "in mediump vec2 texCoord;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main ()\n" |
| "{\n" |
| " fragColor = texture(samplerB, texture(samplerA, texCoord).xy);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(initProg, kVS, kSimpleFS); |
| ANGLE_GL_PROGRAM(nestedProg, kVS, kNestedFS); |
| |
| // Initialize a first texture with default texCoord data. |
| GLTexture texA; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, texA); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texA, 0); |
| |
| drawQuad(initProg, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Initialize a second texture with a simple color pattern. |
| GLTexture texB; |
| glActiveTexture(GL_TEXTURE1); |
| glBindTexture(GL_TEXTURE_2D, texB); |
| |
| std::array<GLColor, 4> simpleColors = { |
| {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| simpleColors.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| // Draw with the nested program, using the first texture to index the second. |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glUseProgram(nestedProg); |
| GLint samplerALoc = glGetUniformLocation(nestedProg, "samplerA"); |
| ASSERT_NE(-1, samplerALoc); |
| glUniform1i(samplerALoc, 0); |
| GLint samplerBLoc = glGetUniformLocation(nestedProg, "samplerB"); |
| ASSERT_NE(-1, samplerBLoc); |
| glUniform1i(samplerBLoc, 1); |
| |
| drawQuad(nestedProg, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Compute four texel centers. |
| Vector2 windowSize(getWindowWidth(), getWindowHeight()); |
| Vector2 quarterWindowSize = windowSize / 4; |
| Vector2 ul = quarterWindowSize; |
| Vector2 ur(windowSize.x() - quarterWindowSize.x(), quarterWindowSize.y()); |
| Vector2 ll(quarterWindowSize.x(), windowSize.y() - quarterWindowSize.y()); |
| Vector2 lr = windowSize - quarterWindowSize; |
| |
| EXPECT_PIXEL_COLOR_EQ_VEC2(ul, simpleColors[0]); |
| EXPECT_PIXEL_COLOR_EQ_VEC2(ur, simpleColors[1]); |
| EXPECT_PIXEL_COLOR_EQ_VEC2(ll, simpleColors[2]); |
| EXPECT_PIXEL_COLOR_EQ_VEC2(lr, simpleColors[3]); |
| } |
| |
| // Tests that using a constant declaration as the only statement in a for loop without curly braces |
| // doesn't crash. |
| TEST_P(GLSLTest, ConstantStatementInForLoop) |
| { |
| constexpr char kVS[] = |
| "void main()\n" |
| "{\n" |
| " for (int i = 0; i < 10; ++i)\n" |
| " const int b = 0;\n" |
| "}\n"; |
| |
| GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS); |
| EXPECT_NE(0u, shader); |
| glDeleteShader(shader); |
| } |
| |
| // Tests that using a constant declaration as a loop init expression doesn't crash. Note that this |
| // test doesn't work on D3D9 due to looping limitations, so it is only run on ES3. |
| TEST_P(GLSLTest_ES3, ConstantStatementAsLoopInit) |
| { |
| constexpr char kVS[] = |
| "void main()\n" |
| "{\n" |
| " for (const int i = 0; i < 0;) {}\n" |
| "}\n"; |
| |
| GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS); |
| EXPECT_NE(0u, shader); |
| glDeleteShader(shader); |
| } |
| |
| // Test that uninitialized local variables are initialized to 0. |
| TEST_P(WebGL2GLSLTest, InitUninitializedLocals) |
| { |
| // Test skipped on Android GLES because local variable initialization is disabled. |
| // http://anglebug.com/2046 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 my_FragColor;\n" |
| "int result = 0;\n" |
| "void main()\n" |
| "{\n" |
| " int u;\n" |
| " result += u;\n" |
| " int k = 0;\n" |
| " for (int i[2], j = i[0] + 1; k < 2; ++k)\n" |
| " {\n" |
| " result += j;\n" |
| " }\n" |
| " if (result == 2)\n" |
| " {\n" |
| " my_FragColor = vec4(0, 1, 0, 1);\n" |
| " }\n" |
| " else\n" |
| " {\n" |
| " my_FragColor = vec4(1, 0, 0, 1);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| // [WebGL 1.0] |
| // DrawArrays or drawElements will generate an INVALID_OPERATION error |
| // if a vertex attribute is enabled as an array via enableVertexAttribArray |
| // but no buffer is bound to that attribute. |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that uninitialized structs containing arrays of structs are initialized to 0. This |
| // specifically tests with two different struct variables declared in the same block. |
| TEST_P(WebGL2GLSLTest, InitUninitializedStructContainingArrays) |
| { |
| // Test skipped on Android GLES because local variable initialization is disabled. |
| // http://anglebug.com/2046 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "struct T\n" |
| "{\n" |
| " int a[2];\n" |
| "};\n" |
| "struct S\n" |
| "{\n" |
| " T t[2];\n" |
| "};\n" |
| "void main()\n" |
| "{\n" |
| " S s;\n" |
| " S s2;\n" |
| " if (s.t[1].a[1] == 0 && s2.t[1].a[1] == 0)\n" |
| " {\n" |
| " gl_FragColor = vec4(0, 1, 0, 1);\n" |
| " }\n" |
| " else\n" |
| " {\n" |
| " gl_FragColor = vec4(1, 0, 0, 1);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Verify that two shaders with the same uniform name and members but different structure names will |
| // not link. |
| TEST_P(GLSLTest, StructureNameMatchingTest) |
| { |
| const char *vsSource = |
| "// Structures must have the same name, sequence of type names, and\n" |
| "// type definitions, and field names to be considered the same type.\n" |
| "// GLSL 1.017 4.2.4\n" |
| "precision mediump float;\n" |
| "struct info {\n" |
| " vec4 pos;\n" |
| " vec4 color;\n" |
| "};\n" |
| "\n" |
| "uniform info uni;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = uni.pos;\n" |
| "}\n"; |
| |
| GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource); |
| ASSERT_NE(0u, vs); |
| glDeleteShader(vs); |
| |
| const char *fsSource = |
| "// Structures must have the same name, sequence of type names, and\n" |
| "// type definitions, and field names to be considered the same type.\n" |
| "// GLSL 1.017 4.2.4\n" |
| "precision mediump float;\n" |
| "struct info1 {\n" |
| " vec4 pos;\n" |
| " vec4 color;\n" |
| "};\n" |
| "\n" |
| "uniform info1 uni;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = uni.color;\n" |
| "}\n"; |
| |
| GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource); |
| ASSERT_NE(0u, fs); |
| glDeleteShader(fs); |
| |
| GLuint program = CompileProgram(vsSource, fsSource); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Test that an uninitialized nameless struct inside a for loop init statement works. |
| TEST_P(WebGL2GLSLTest, UninitializedNamelessStructInForInitStatement) |
| { |
| // Test skipped on Android GLES because local variable initialization is disabled. |
| // http://anglebug.com/2046 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "out vec4 my_FragColor;\n" |
| "void main()\n" |
| "{\n" |
| " my_FragColor = vec4(1, 0, 0, 1);\n" |
| " for (struct { float q; } b; b.q < 2.0; b.q++) {\n" |
| " my_FragColor = vec4(0, 1, 0, 1);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that uninitialized global variables are initialized to 0. |
| TEST_P(WebGLGLSLTest, InitUninitializedGlobals) |
| { |
| // http://anglebug.com/2862 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "int result;\n" |
| "int i[2], j = i[0] + 1;\n" |
| "void main()\n" |
| "{\n" |
| " result += j;\n" |
| " if (result == 1)\n" |
| " {\n" |
| " gl_FragColor = vec4(0, 1, 0, 1);\n" |
| " }\n" |
| " else\n" |
| " {\n" |
| " gl_FragColor = vec4(1, 0, 0, 1);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that an uninitialized nameless struct in the global scope works. |
| TEST_P(WebGLGLSLTest, UninitializedNamelessStructInGlobalScope) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "struct { float q; } b;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(1, 0, 0, 1);\n" |
| " if (b.q == 0.0)\n" |
| " {\n" |
| " gl_FragColor = vec4(0, 1, 0, 1);\n" |
| " }\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests nameless struct uniforms. |
| TEST_P(GLSLTest, EmbeddedStructUniform) |
| { |
| const char kFragmentShader[] = R"(precision mediump float; |
| uniform struct { float q; } b; |
| void main() |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| if (b.q == 0.5) |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| glUseProgram(program); |
| GLint uniLoc = glGetUniformLocation(program, "b.q"); |
| ASSERT_NE(-1, uniLoc); |
| glUniform1f(uniLoc, 0.5f); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests that rewriting samplers in structs doesn't mess up indexing. |
| TEST_P(GLSLTest, SamplerInStructMemberIndexing) |
| { |
| const char kVertexShader[] = R"(attribute vec2 position; |
| varying vec2 texCoord; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| texCoord = position * 0.5 + vec2(0.5); |
| })"; |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { sampler2D samp; bool b; }; |
| uniform S uni; |
| varying vec2 texCoord; |
| void main() |
| { |
| if (uni.b) |
| { |
| gl_FragColor = texture2D(uni.samp, texCoord); |
| } |
| else |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVertexShader, kFragmentShader); |
| glUseProgram(program); |
| |
| GLint bLoc = glGetUniformLocation(program, "uni.b"); |
| ASSERT_NE(-1, bLoc); |
| GLint sampLoc = glGetUniformLocation(program, "uni.samp"); |
| ASSERT_NE(-1, sampLoc); |
| |
| glUniform1i(bLoc, 1); |
| |
| std::array<GLColor, 4> kGreenPixels = { |
| {GLColor::green, GLColor::green, GLColor::green, GLColor::green}}; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kGreenPixels.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(program, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests two nameless struct uniforms. |
| TEST_P(GLSLTest, TwoEmbeddedStructUniforms) |
| { |
| const char kFragmentShader[] = R"(precision mediump float; |
| uniform struct { float q; } b, c; |
| void main() |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| if (b.q == 0.5 && c.q == 1.0) |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| glUseProgram(program); |
| |
| GLint uniLocB = glGetUniformLocation(program, "b.q"); |
| ASSERT_NE(-1, uniLocB); |
| glUniform1f(uniLocB, 0.5f); |
| |
| GLint uniLocC = glGetUniformLocation(program, "c.q"); |
| ASSERT_NE(-1, uniLocC); |
| glUniform1f(uniLocC, 1.0f); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a loop condition that has an initializer declares a variable. |
| TEST_P(GLSLTest_ES3, ConditionInitializerDeclaresVariable) |
| { |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision highp float;\n" |
| "out vec4 my_FragColor;\n" |
| "void main()\n" |
| "{\n" |
| " float i = 0.0;\n" |
| " while (bool foo = (i < 1.5))\n" |
| " {\n" |
| " if (!foo)\n" |
| " {\n" |
| " ++i;\n" |
| " }\n" |
| " if (i > 3.5)\n" |
| " {\n" |
| " break;\n" |
| " }\n" |
| " ++i;\n" |
| " }\n" |
| " my_FragColor = vec4(i * 0.5 - 1.0, i * 0.5, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a variable hides a user-defined function with the same name after its initializer. |
| // GLSL ES 1.00.17 section 4.2.2: "A variable declaration is visible immediately following the |
| // initializer if present, otherwise immediately following the identifier" |
| TEST_P(GLSLTest, VariableHidesUserDefinedFunctionAfterInitializer) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "uniform vec4 u;\n" |
| "vec4 foo()\n" |
| "{\n" |
| " return u;\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " vec4 foo = foo();\n" |
| " gl_FragColor = foo + vec4(0, 1, 0, 1);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that structs with identical members are not ambiguous as function arguments. |
| TEST_P(GLSLTest, StructsWithSameMembersDisambiguatedByName) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "uniform float u_zero;\n" |
| "struct S { float foo; };\n" |
| "struct S2 { float foo; };\n" |
| "float get(S s) { return s.foo + u_zero; }\n" |
| "float get(S2 s2) { return 0.25 + s2.foo + u_zero; }\n" |
| "void main()\n" |
| "{\n" |
| " S s;\n" |
| " s.foo = 0.5;\n" |
| " S2 s2;\n" |
| " s2.foo = 0.25;\n" |
| " gl_FragColor = vec4(0.0, get(s) + get(s2), 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that an inactive varying in vertex shader but used in fragment shader can be linked |
| // successfully. |
| TEST_P(GLSLTest, InactiveVaryingInVertexActiveInFragment) |
| { |
| // http://anglebug.com/4820 |
| ANGLE_SKIP_TEST_IF((IsOSX() && IsOpenGL()) || (IsIOS() && IsOpenGLES())); |
| |
| constexpr char kVS[] = |
| "attribute vec4 inputAttribute;\n" |
| "varying vec4 varColor;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = inputAttribute;\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying vec4 varColor;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = varColor;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that a varying struct that's not statically used in the fragment shader works. |
| // GLSL ES 3.00.6 section 4.3.10. |
| TEST_P(GLSLTest_ES3, VaryingStructNotStaticallyUsedInFragmentShader) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "out S varStruct;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(1.0);\n" |
| " varStruct.field = vec4(0.0, 0.5, 0.0, 0.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "in S varStruct;\n" |
| "out vec4 col;\n" |
| "void main()\n" |
| "{\n" |
| " col = vec4(1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that a shader IO block varying that's not declared in the fragment shader links |
| // successfully. |
| TEST_P(GLSLTest_ES31, VaryingIOBlockNotDeclaredInFragmentShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = |
| R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| precision highp float; |
| in vec4 inputAttribute; |
| out Block_inout { vec4 value; } user_out; |
| |
| void main() |
| { |
| gl_Position = inputAttribute; |
| user_out.value = vec4(4.0, 5.0, 6.0, 7.0); |
| })"; |
| |
| constexpr char kFS[] = |
| R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| precision highp float; |
| layout(location = 0) out mediump vec4 color; |
| void main() |
| { |
| color = vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test that a shader IO block varying that's not declared in the vertex shader links |
| // successfully. |
| TEST_P(GLSLTest_ES31, VaryingIOBlockNotDeclaredInVertexShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = |
| R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| precision highp float; |
| in vec4 inputAttribute; |
| |
| void main() |
| { |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = |
| R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| precision highp float; |
| in Block_inout { vec4 value; } user_in; |
| layout(location = 0) out mediump vec4 color; |
| |
| void main() |
| { |
| color = vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test that a shader with sample in / sample out can be linked successfully. |
| TEST_P(GLSLTest_ES31, VaryingTessellationSampleInAndOut) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_tessellation_shader")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_shader_multisample_interpolation")); |
| |
| constexpr char kVS[] = |
| R"(#version 310 es |
| #extension GL_OES_shader_multisample_interpolation : require |
| |
| precision highp float; |
| in vec4 inputAttribute; |
| |
| sample out mediump float tc_in; |
| void main() |
| { |
| tc_in = inputAttribute[0]; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kTCS[] = |
| R"(#version 310 es |
| #extension GL_EXT_tessellation_shader : require |
| #extension GL_OES_shader_multisample_interpolation : require |
| layout (vertices=3) out; |
| |
| sample in mediump float tc_in[]; |
| sample out mediump float tc_out[]; |
| void main() |
| { |
| tc_out[gl_InvocationID] = tc_in[gl_InvocationID]; |
| gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; |
| gl_TessLevelInner[0] = 2.0; |
| gl_TessLevelInner[1] = 2.0; |
| gl_TessLevelOuter[0] = 2.0; |
| gl_TessLevelOuter[1] = 2.0; |
| gl_TessLevelOuter[2] = 2.0; |
| gl_TessLevelOuter[3] = 2.0; |
| })"; |
| |
| constexpr char kTES[] = |
| R"(#version 310 es |
| #extension GL_EXT_tessellation_shader : require |
| #extension GL_OES_shader_multisample_interpolation : require |
| layout (triangles) in; |
| |
| sample in mediump float tc_out[]; |
| sample out mediump float te_out; |
| void main() |
| { |
| te_out = tc_out[2]; |
| gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position; |
| })"; |
| |
| constexpr char kFS[] = |
| R"(#version 310 es |
| #extension GL_OES_shader_multisample_interpolation : require |
| |
| precision highp float; |
| sample in mediump float te_out; |
| layout(location = 0) out mediump vec4 color; |
| |
| void main() |
| { |
| float out0 = te_out; |
| color = vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_TESS(program, kVS, kTCS, kTES, kFS); |
| drawPatches(program.get(), "inputAttribute", 0.5f, 1.0f, GL_FALSE); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that a varying struct that's not declared in the fragment shader links successfully. |
| // GLSL ES 3.00.6 section 4.3.10. |
| TEST_P(GLSLTest_ES3, VaryingStructNotDeclaredInFragmentShader) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "out S varStruct;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(1.0);\n" |
| " varStruct.field = vec4(0.0, 0.5, 0.0, 0.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "void main()\n" |
| "{\n" |
| " col = vec4(1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that a varying struct that's not declared in the vertex shader, and is unused in the |
| // fragment shader links successfully. |
| TEST_P(GLSLTest_ES3, VaryingStructNotDeclaredInVertexShader) |
| { |
| // GLSL ES allows the vertex shader to not declare a varying if the fragment shader is not |
| // going to use it. See section 9.1 in |
| // https://www.khronos.org/registry/OpenGL/specs/es/3.2/GLSL_ES_Specification_3.20.pdf or |
| // section 4.3.5 in https://www.khronos.org/files/opengles_shading_language.pdf |
| // |
| // However, nvidia OpenGL ES drivers fail to link this program. |
| // |
| // http://anglebug.com/3413 |
| ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsNVIDIA()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "in S varStruct;\n" |
| "void main()\n" |
| "{\n" |
| " col = vec4(1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that a varying struct that's not initialized in the vertex shader links successfully. |
| TEST_P(WebGL2GLSLTest, VaryingStructNotInitializedInVertexShader) |
| { |
| // GLSL ES allows the vertex shader to declare but not initialize a varying (with a |
| // specification that the varying values are undefined in the fragment stage). See section 9.1 |
| // in https://www.khronos.org/registry/OpenGL/specs/es/3.2/GLSL_ES_Specification_3.20.pdf |
| // or section 4.3.5 in https://www.khronos.org/files/opengles_shading_language.pdf |
| // |
| // However, windows and mac OpenGL drivers fail to link this program. With a message like: |
| // |
| // > Input of fragment shader 'varStruct' not written by vertex shader |
| // |
| // http://anglebug.com/3413 |
| ANGLE_SKIP_TEST_IF(IsDesktopOpenGL() && (IsOSX() || (IsWindows() && !IsNVIDIA()))); |
| // TODO(anglebug.com/5491): iOS thinks that the precision qualifiers don't match on the |
| // struct member. Not sure if it's being overly strict. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "out S varStruct;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "in S varStruct;\n" |
| "void main()\n" |
| "{\n" |
| " col = varStruct.field;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that a varying struct that gets used in the fragment shader works. |
| TEST_P(GLSLTest_ES3, VaryingStructUsedInFragmentShader) |
| { |
| // TODO(anglebug.com/5491): iOS thinks that the precision qualifiers don't match on the |
| // struct member. Not sure if it's being overly strict. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 inputAttribute;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "out S varStruct;\n" |
| "out S varStruct2;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = inputAttribute;\n" |
| " varStruct.field = vec4(0.0, 0.5, 0.0, 1.0);\n" |
| " varStruct2.field = vec4(0.0, 0.5, 0.0, 1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "in S varStruct;\n" |
| "in S varStruct2;\n" |
| "void main()\n" |
| "{\n" |
| " col = varStruct.field + varStruct2.field;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // This is a regression test to make sure a red quad is rendered without issues |
| // when a passthrough function with a vec3 input parameter is used in the fragment shader. |
| TEST_P(GLSLTest_ES31, SamplerPassthroughFailedLink) |
| { |
| constexpr char kVS[] = |
| "precision mediump float;\n" |
| "attribute vec4 inputAttribute;\n" |
| "varying mediump vec2 texCoord;\n" |
| "void main() {\n" |
| " texCoord = inputAttribute.xy;\n" |
| " gl_Position = vec4(inputAttribute.x, inputAttribute.y, 0.0, 1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying mediump vec2 texCoord;\n" |
| "uniform sampler2D testSampler;\n" |
| "vec3 passthrough(vec3 c) {\n" |
| " return c;\n" |
| "}\n" |
| "void main() {\n" |
| " gl_FragColor = vec4(passthrough(texture2D(testSampler, texCoord).rgb), 1.0);\n" |
| "}\n"; |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| // Initialize basic red texture. |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::red.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // This is a regression test to make sure a red quad is rendered without issues |
| // when a passthrough function with a vec4 input parameter is used in the fragment shader. |
| TEST_P(GLSLTest_ES31, SamplerPassthroughIncorrectColor) |
| { |
| constexpr char kVS[] = |
| "precision mediump float;\n" |
| "attribute vec4 inputAttribute;\n" |
| "varying mediump vec2 texCoord;\n" |
| "void main() {\n" |
| " texCoord = inputAttribute.xy;\n" |
| " gl_Position = vec4(inputAttribute.x, inputAttribute.y, 0.0, 1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "varying mediump vec2 texCoord;\n" |
| "uniform sampler2D testSampler;\n" |
| "vec4 passthrough(vec4 c) {\n" |
| " return c;\n" |
| "}\n" |
| "void main() {\n" |
| " gl_FragColor = vec4(passthrough(texture2D(testSampler, texCoord)));\n" |
| "}\n"; |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| // Initialize basic red texture. |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::red.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test that multiple multi-field varying structs that get used in the fragment shader work. |
| TEST_P(GLSLTest_ES3, ComplexVaryingStructsUsedInFragmentShader) |
| { |
| // TODO(syoussefi): fails on android with: |
| // |
| // > Internal Vulkan error: A return array was too small for the result |
| // |
| // http://anglebug.com/3220 |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid()); |
| // TODO(anglebug.com/5491): iOS thinks that the precision qualifiers don't match on the |
| // struct members. Not sure if it's being overly strict. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 inputAttribute;\n" |
| "struct S {\n" |
| " vec4 field1;\n" |
| " vec4 field2;\n" |
| "};\n" |
| "out S varStruct;\n" |
| "out S varStruct2;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = inputAttribute;\n" |
| " varStruct.field1 = vec4(0.0, 0.5, 0.0, 1.0);\n" |
| " varStruct.field2 = vec4(0.0, 0.5, 0.0, 1.0);\n" |
| " varStruct2.field1 = vec4(0.0, 0.5, 0.0, 1.0);\n" |
| " varStruct2.field2 = vec4(0.0, 0.5, 0.0, 1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "struct S {\n" |
| " vec4 field1;\n" |
| " vec4 field2;\n" |
| "};\n" |
| "in S varStruct;\n" |
| "in S varStruct2;\n" |
| "void main()\n" |
| "{\n" |
| " col = varStruct.field1 + varStruct2.field2;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that an inactive varying array that doesn't get used in the fragment shader works. |
| TEST_P(GLSLTest_ES3, InactiveVaryingArrayUnusedInFragmentShader) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 inputAttribute;\n" |
| "out vec4 varArray[4];\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = inputAttribute;\n" |
| " varArray[0] = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| " varArray[1] = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " varArray[2] = vec4(0.0, 0.0, 1.0, 1.0);\n" |
| " varArray[3] = vec4(1.0, 1.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "void main()\n" |
| "{\n" |
| " col = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| } |
| |
| // Test that an inactive varying struct that doesn't get used in the fragment shader works. |
| TEST_P(GLSLTest_ES3, InactiveVaryingStructUnusedInFragmentShader) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 inputAttribute;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "out S varStruct;\n" |
| "out S varStruct2;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = inputAttribute;\n" |
| " varStruct.field = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| " varStruct2.field = vec4(0.0, 1.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "struct S {\n" |
| " vec4 field;\n" |
| "};\n" |
| "in S varStruct;\n" |
| "in S varStruct2;\n" |
| "void main()\n" |
| "{\n" |
| " col = varStruct.field;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that multiple varying matrices that get used in the fragment shader work. |
| TEST_P(GLSLTest_ES3, VaryingMatrices) |
| { |
| constexpr char kVS[] = |
| "#version 300 es\n" |
| "in vec4 inputAttribute;\n" |
| "out mat2x2 varMat;\n" |
| "out mat2x2 varMat2;\n" |
| "out mat4x3 varMat3;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = inputAttribute;\n" |
| " varMat[0] = vec2(1, 1);\n" |
| " varMat[1] = vec2(1, 1);\n" |
| " varMat2[0] = vec2(0.5, 0.5);\n" |
| " varMat2[1] = vec2(0.5, 0.5);\n" |
| " varMat3[0] = vec3(0.75, 0.75, 0.75);\n" |
| " varMat3[1] = vec3(0.75, 0.75, 0.75);\n" |
| " varMat3[2] = vec3(0.75, 0.75, 0.75);\n" |
| " varMat3[3] = vec3(0.75, 0.75, 0.75);\n" |
| "}\n"; |
| |
| constexpr char kFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "out vec4 col;\n" |
| "in mat2x2 varMat;\n" |
| "in mat2x2 varMat2;\n" |
| "in mat4x3 varMat3;\n" |
| "void main()\n" |
| "{\n" |
| " col = vec4(varMat[0].x, varMat2[1].y, varMat3[2].z, 1);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 127, 191, 255), 1); |
| } |
| |
| // This test covers passing a struct containing a sampler as a function argument. |
| TEST_P(GLSLTest, StructsWithSamplersAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { sampler2D samplerMember; }; |
| uniform S uStruct; |
| uniform vec2 uTexCoord; |
| vec4 foo(S structVar) |
| { |
| return texture2D(structVar.samplerMember, uTexCoord); |
| } |
| void main() |
| { |
| gl_FragColor = foo(uStruct); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| |
| // This test covers passing a struct containing a sampler as a function argument. |
| TEST_P(GLSLTest, StructsWithSamplersAsFunctionArgWithPrototype) |
| { |
| // Shader failed to compile on Android. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES()); |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { sampler2D samplerMember; }; |
| uniform S uStruct; |
| uniform vec2 uTexCoord; |
| vec4 foo(S structVar); |
| vec4 foo(S structVar) |
| { |
| return texture2D(structVar.samplerMember, uTexCoord); |
| } |
| void main() |
| { |
| gl_FragColor = foo(uStruct); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| // This test covers passing an array of structs containing samplers as a function argument. |
| TEST_P(GLSLTest, ArrayOfStructsWithSamplersAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "struct S\n" |
| "{\n" |
| " sampler2D samplerMember; \n" |
| "};\n" |
| "uniform S uStructs[2];\n" |
| "uniform vec2 uTexCoord;\n" |
| "\n" |
| "vec4 foo(S[2] structs)\n" |
| "{\n" |
| " return texture2D(structs[0].samplerMember, uTexCoord);\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = foo(uStructs);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStructs[0].samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| |
| // This test covers passing a struct containing an array of samplers as a function argument. |
| TEST_P(GLSLTest, StructWithSamplerArrayAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "struct S\n" |
| "{\n" |
| " sampler2D samplerMembers[2];\n" |
| "};\n" |
| "uniform S uStruct;\n" |
| "uniform vec2 uTexCoord;\n" |
| "\n" |
| "vec4 foo(S str)\n" |
| "{\n" |
| " return texture2D(str.samplerMembers[0], uTexCoord);\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = foo(uStruct);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMembers[0]"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| |
| // This test covers passing nested structs containing a sampler as a function argument. |
| TEST_P(GLSLTest, NestedStructsWithSamplersAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| // TODO(anglebug.com/5360): Failing on ARM-based Apple DTKs. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsDesktopOpenGL()); |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { sampler2D samplerMember; }; |
| struct T { S nest; }; |
| uniform T uStruct; |
| uniform vec2 uTexCoord; |
| vec4 foo2(S structVar) |
| { |
| return texture2D(structVar.samplerMember, uTexCoord); |
| } |
| vec4 foo(T structVar) |
| { |
| return foo2(structVar.nest); |
| } |
| void main() |
| { |
| gl_FragColor = foo(uStruct); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.nest.samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| |
| // This test covers passing a compound structs containing a sampler as a function argument. |
| TEST_P(GLSLTest, CompoundStructsWithSamplersAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { sampler2D samplerMember; bool b; }; |
| uniform S uStruct; |
| uniform vec2 uTexCoord; |
| vec4 foo(S structVar) |
| { |
| if (structVar.b) |
| return texture2D(structVar.samplerMember, uTexCoord); |
| else |
| return vec4(1, 0, 0, 1); |
| } |
| void main() |
| { |
| gl_FragColor = foo(uStruct); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| GLint bLoc = glGetUniformLocation(program, "uStruct.b"); |
| ASSERT_NE(-1, bLoc); |
| glUniform1i(bLoc, 1); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| |
| // This test covers passing nested compound structs containing a sampler as a function argument. |
| TEST_P(GLSLTest, NestedCompoundStructsWithSamplersAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| // TODO(anglebug.com/5360): Failing on ARM-based Apple DTKs. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsDesktopOpenGL()); |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { sampler2D samplerMember; bool b; }; |
| struct T { S nest; bool b; }; |
| uniform T uStruct; |
| uniform vec2 uTexCoord; |
| vec4 foo2(S structVar) |
| { |
| if (structVar.b) |
| return texture2D(structVar.samplerMember, uTexCoord); |
| else |
| return vec4(1, 0, 0, 1); |
| } |
| vec4 foo(T structVar) |
| { |
| if (structVar.b) |
| return foo2(structVar.nest); |
| else |
| return vec4(1, 0, 0, 1); |
| } |
| void main() |
| { |
| gl_FragColor = foo(uStruct); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.nest.samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| GLint bLoc = glGetUniformLocation(program, "uStruct.b"); |
| ASSERT_NE(-1, bLoc); |
| glUniform1i(bLoc, 1); |
| |
| GLint nestbLoc = glGetUniformLocation(program, "uStruct.nest.b"); |
| ASSERT_NE(-1, nestbLoc); |
| glUniform1i(nestbLoc, 1); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| |
| // Same as the prior test but with reordered struct members. |
| TEST_P(GLSLTest, MoreNestedCompoundStructsWithSamplersAsFunctionArg) |
| { |
| // Shader failed to compile on Nexus devices. http://anglebug.com/2114 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| |
| // TODO(anglebug.com/5360): Failing on ARM-based Apple DTKs. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsDesktopOpenGL()); |
| |
| const char kFragmentShader[] = R"(precision mediump float; |
| struct S { bool b; sampler2D samplerMember; }; |
| struct T { bool b; S nest; }; |
| uniform T uStruct; |
| uniform vec2 uTexCoord; |
| vec4 foo2(S structVar) |
| { |
| if (structVar.b) |
| return texture2D(structVar.samplerMember, uTexCoord); |
| else |
| return vec4(1, 0, 0, 1); |
| } |
| vec4 foo(T structVar) |
| { |
| if (structVar.b) |
| return foo2(structVar.nest); |
| else |
| return vec4(1, 0, 0, 1); |
| } |
| void main() |
| { |
| gl_FragColor = foo(uStruct); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader); |
| |
| // Initialize the texture with green. |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {0u, 255u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| glUseProgram(program); |
| GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.nest.samplerMember"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord"); |
| ASSERT_NE(-1, texCoordLoc); |
| glUniform2f(texCoordLoc, 0.5f, 0.5f); |
| |
| GLint bLoc = glGetUniformLocation(program, "uStruct.b"); |
| ASSERT_NE(-1, bLoc); |
| glUniform1i(bLoc, 1); |
| |
| GLint nestbLoc = glGetUniformLocation(program, "uStruct.nest.b"); |
| ASSERT_NE(-1, nestbLoc); |
| glUniform1i(nestbLoc, 1); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green); |
| } |
| // Test that a global variable declared after main() works. This is a regression test for an issue |
| // in global variable initialization. |
| TEST_P(WebGLGLSLTest, GlobalVariableDeclaredAfterMain) |
| { |
| constexpr char kFS[] = |
| "precision mediump float;\n" |
| "int getFoo();\n" |
| "uniform int u_zero;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(1, 0, 0, 1);\n" |
| " if (getFoo() == 0)\n" |
| " {\n" |
| " gl_FragColor = vec4(0, 1, 0, 1);\n" |
| " }\n" |
| "}\n" |
| "int foo;\n" |
| "int getFoo()\n" |
| "{\n" |
| " foo = u_zero;\n" |
| " return foo;\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test calling array length() with a "this" expression having side effects inside a loop condition. |
| // The spec says that sequence operator operands need to run in sequence. |
| TEST_P(GLSLTest_ES3, ArrayLengthOnExpressionWithSideEffectsInLoopCondition) |
| { |
| // "a" gets doubled three times in the below program. |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| uniform int u_zero; |
| int a; |
| int[2] doubleA() |
| { |
| a *= 2; |
| return int[2](a, a); |
| } |
| void main() |
| { |
| a = u_zero + 1; |
| for (int i = 0; i < doubleA().length(); ++i) |
| {} |
| if (a == 8) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test calling array length() with a "this" expression having side effects that interact with side |
| // effects of another operand of the same sequence operator. The spec says that sequence operator |
| // operands need to run in order from left to right (ESSL 3.00.6 section 5.9). |
| TEST_P(GLSLTest_ES3, ArrayLengthOnExpressionWithSideEffectsInSequence) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| uniform int u_zero; |
| int a; |
| int[3] doubleA() |
| { |
| a *= 2; |
| return int[3](a, a, a); |
| } |
| void main() |
| { |
| a = u_zero; |
| int b = (a++, doubleA().length()); |
| if (b == 3 && a == 2) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test calling array length() with a "this" expression that also contains a call of array length(). |
| // Both "this" expressions also have side effects. |
| TEST_P(GLSLTest_ES3, NestedArrayLengthMethodsWithSideEffects) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| uniform int u_zero; |
| int a; |
| int[3] multiplyA(int multiplier) |
| { |
| a *= multiplier; |
| return int[3](a, a, a); |
| } |
| void main() |
| { |
| a = u_zero + 1; |
| int b = multiplyA(multiplyA(2).length()).length(); |
| if (b == 3 && a == 6) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test calling array length() with a "this" expression having side effects inside an if condition. |
| // This is an issue if the the side effect can be short circuited. |
| TEST_P(GLSLTest_ES3, ArrayLengthOnShortCircuitedExpressionWithSideEffectsInIfCondition) |
| { |
| // Bug in the shader translator. http://anglebug.com/3829 |
| ANGLE_SKIP_TEST_IF(true); |
| |
| // "a" shouldn't get modified by this shader. |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| uniform int u_zero; |
| int a; |
| int[2] doubleA() |
| { |
| a *= 2; |
| return int[2](a, a); |
| } |
| void main() |
| { |
| a = u_zero + 1; |
| if (u_zero != 0 && doubleA().length() == 2) |
| { |
| ++a; |
| } |
| if (a == 1) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test calling array length() with a "this" expression having side effects in a statement where the |
| // side effect can be short circuited. |
| TEST_P(GLSLTest_ES3, ArrayLengthOnShortCircuitedExpressionWithSideEffectsInStatement) |
| { |
| // Bug in the shader translator. http://anglebug.com/3829 |
| ANGLE_SKIP_TEST_IF(true); |
| |
| // "a" shouldn't get modified by this shader. |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| uniform int u_zero; |
| int a; |
| int[2] doubleA() |
| { |
| a *= 2; |
| return int[2](a, a); |
| } |
| void main() |
| { |
| a = u_zero + 1; |
| bool test = u_zero != 0 && doubleA().length() == 2; |
| if (a == 1) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that statements inside switch() get translated to correct HLSL. |
| TEST_P(GLSLTest_ES3, DifferentStatementsInsideSwitch) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| uniform int u; |
| void main() |
| { |
| switch (u) |
| { |
| case 0: |
| ivec2 i; |
| i.yx; |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that switch fall-through works correctly. |
| // This is a regression test for http://anglebug.com/2178 |
| TEST_P(GLSLTest_ES3, SwitchFallThroughCodeDuplication) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| uniform int u_zero; |
| |
| void main() |
| { |
| int i = 0; |
| // switch should fall through both cases. |
| switch(u_zero) |
| { |
| case 0: |
| i += 1; |
| case 1: |
| i += 2; |
| } |
| if (i == 3) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test switch/case where default is last. |
| TEST_P(GLSLTest_ES3, SwitchWithDefaultAtTheEnd) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| uniform int u_zero; |
| |
| void main() |
| { |
| switch (u_zero) |
| { |
| case 1: |
| my_FragColor = vec4(1, 0, 0, 1); |
| break; |
| default: |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a switch statement with an empty block inside as a final statement compiles. |
| TEST_P(GLSLTest_ES3, SwitchFinalCaseHasEmptyBlock) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision mediump float; |
| uniform int i; |
| void main() |
| { |
| switch (i) |
| { |
| case 0: |
| break; |
| default: |
| {} |
| } |
| })"; |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test that a switch statement with an empty declaration inside as a final statement compiles. |
| TEST_P(GLSLTest_ES3, SwitchFinalCaseHasEmptyDeclaration) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision mediump float; |
| uniform int i; |
| void main() |
| { |
| switch (i) |
| { |
| case 0: |
| break; |
| default: |
| float; |
| } |
| })"; |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| } |
| |
| // Test switch/case where break/return statements are within blocks. |
| TEST_P(GLSLTest_ES3, SwitchBreakOrReturnInsideBlocks) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| |
| uniform int u_zero; |
| out vec4 my_FragColor; |
| |
| bool test(int n) |
| { |
| switch(n) { |
| case 0: |
| { |
| { |
| break; |
| } |
| } |
| case 1: |
| { |
| return true; |
| } |
| case 2: |
| { |
| n++; |
| } |
| } |
| return false; |
| } |
| |
| void main() |
| { |
| my_FragColor = test(u_zero + 1) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test switch/case where a variable is declared inside one of the cases and is accessed by a |
| // subsequent case. |
| TEST_P(GLSLTest_ES3, SwitchWithVariableDeclarationInside) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| uniform int u_zero; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| switch (u_zero) |
| { |
| case 0: |
| ivec2 i; |
| i = ivec2(1, 0); |
| default: |
| my_FragColor = vec4(0, i[0], 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test nested switch/case where a variable is declared inside one of the cases and is accessed by a |
| // subsequent case. |
| TEST_P(GLSLTest_ES3, NestedSwitchWithVariableDeclarationInside) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| uniform int u_zero; |
| uniform int u_zero2; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| switch (u_zero) |
| { |
| case 0: |
| ivec2 i; |
| i = ivec2(1, 0); |
| switch (u_zero2) |
| { |
| case 0: |
| int j; |
| default: |
| j = 1; |
| i *= j; |
| } |
| default: |
| my_FragColor = vec4(0, i[0], 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that an empty switch/case statement is translated in a way that compiles and executes the |
| // init-statement. |
| TEST_P(GLSLTest_ES3, EmptySwitch) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| |
| uniform int u_zero; |
| out vec4 my_FragColor; |
| |
| void main() |
| { |
| int i = u_zero; |
| switch(++i) {} |
| my_FragColor = (i == 1) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a constant struct inside an expression is handled correctly. |
| TEST_P(GLSLTest_ES3, ConstStructInsideExpression) |
| { |
| // Incorrect output color was seen on Android. http://anglebug.com/2226 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && !IsNVIDIA() && IsOpenGLES()); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| uniform float u_zero; |
| |
| struct S |
| { |
| float field; |
| }; |
| |
| void main() |
| { |
| const S constS = S(1.0); |
| S nonConstS = constS; |
| nonConstS.field = u_zero; |
| bool fail = (constS == nonConstS); |
| my_FragColor = vec4(0, 1, 0, 1); |
| if (fail) |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a varying struct that's defined as a part of the declaration is handled correctly. |
| TEST_P(GLSLTest_ES3, VaryingStructWithInlineDefinition) |
| { |
| // TODO(anglebug.com/5491): iOS thinks that the precision qualifiers don't match on the |
| // struct member. Not sure if it's being overly strict. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| constexpr char kVS[] = R"(#version 300 es |
| in vec4 inputAttribute; |
| |
| flat out struct S |
| { |
| int field; |
| } v_s; |
| |
| void main() |
| { |
| v_s.field = 1; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| flat in struct S |
| { |
| int field; |
| } v_s; |
| |
| void main() |
| { |
| bool success = (v_s.field == 1); |
| my_FragColor = vec4(1, 0, 0, 1); |
| if (success) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test mismatched precision in varying is handled correctly. |
| TEST_P(GLSLTest_ES3, MismatchPrecisionFloat) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| in vec4 position; |
| uniform highp float inVal; |
| out highp float myVarying; |
| |
| void main() |
| { |
| myVarying = inVal; |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| in mediump float myVarying; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| if (myVarying > 1.0) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glUseProgram(program.get()); |
| GLint positionLocation = glGetAttribLocation(program.get(), "position"); |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); |
| for (Vector3 &vertex : quadVertices) |
| { |
| vertex.z() = 0.5f; |
| } |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); |
| glEnableVertexAttribArray(positionLocation); |
| |
| GLint inValLoc = glGetUniformLocation(program, "inVal"); |
| ASSERT_NE(-1, inValLoc); |
| glUniform1f(inValLoc, static_cast<GLfloat>(1.003)); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test mismatched precision in varying is handled correctly. |
| TEST_P(GLSLTest_ES3, MismatchPrecisionlowpFloat) |
| { |
| // Note: SPIRV only has relaxed precision so both lowp and mediump turn into "relaxed |
| // precision", thus this is the same test as MismatchPrecisionFloat but including it for |
| // completeness in case something changes. |
| constexpr char kVS[] = R"(#version 300 es |
| in vec4 position; |
| uniform highp float inVal; |
| out highp float myVarying; |
| |
| void main() |
| { |
| myVarying = inVal; |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| in lowp float myVarying; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| if (myVarying > 1.0) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glUseProgram(program.get()); |
| GLint positionLocation = glGetAttribLocation(program.get(), "position"); |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); |
| for (Vector3 &vertex : quadVertices) |
| { |
| vertex.z() = 0.5f; |
| } |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); |
| glEnableVertexAttribArray(positionLocation); |
| |
| GLint inValLoc = glGetUniformLocation(program, "inVal"); |
| ASSERT_NE(-1, inValLoc); |
| glUniform1f(inValLoc, static_cast<GLfloat>(1.003)); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test mismatched precision in varying is handled correctly. |
| TEST_P(GLSLTest_ES3, MismatchPrecisionVec2UnusedVarying) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| in vec2 position; |
| uniform highp float inVal; |
| out highp float myVarying; |
| out highp vec2 texCoord; |
| |
| void main() |
| { |
| myVarying = inVal; |
| gl_Position = vec4(position, 0, 1); |
| texCoord = position * 0.5 + vec2(0.5); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| in mediump float myVarying; |
| in mediump vec2 texCoord; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| if (myVarying > 1.0) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glUseProgram(program.get()); |
| GLint positionLocation = glGetAttribLocation(program.get(), "position"); |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); |
| for (Vector3 &vertex : quadVertices) |
| { |
| vertex.z() = 0.5f; |
| } |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); |
| glEnableVertexAttribArray(positionLocation); |
| |
| GLint inValLoc = glGetUniformLocation(program, "inVal"); |
| ASSERT_NE(-1, inValLoc); |
| glUniform1f(inValLoc, static_cast<GLfloat>(1.003)); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test mismatched precision in varying is handled correctly. |
| TEST_P(GLSLTest_ES3, MismatchPrecisionMedToHigh) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| in vec2 position; |
| uniform highp float inVal; |
| out mediump float myVarying; |
| |
| void main() |
| { |
| myVarying = inVal; |
| gl_Position = vec4(position, 0, 1); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| in highp float myVarying; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| if (myVarying > 1.0) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glUseProgram(program.get()); |
| GLint positionLocation = glGetAttribLocation(program.get(), "position"); |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); |
| for (Vector3 &vertex : quadVertices) |
| { |
| vertex.z() = 0.5f; |
| } |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); |
| glEnableVertexAttribArray(positionLocation); |
| |
| GLint inValLoc = glGetUniformLocation(program, "inVal"); |
| ASSERT_NE(-1, inValLoc); |
| glUniform1f(inValLoc, static_cast<GLfloat>(1.003)); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test vector/scalar arithmetic (in this case multiplication and addition). |
| TEST_P(GLSLTest, VectorScalarMultiplyAndAddInLoop) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| void main() { |
| gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); |
| for (int i = 0; i < 2; i++) |
| { |
| gl_FragColor += (2.0 * gl_FragCoord.x); |
| } |
| if (gl_FragColor.g == gl_FragColor.r && |
| gl_FragColor.b == gl_FragColor.r && |
| gl_FragColor.a == gl_FragColor.r) |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test vector/scalar arithmetic (in this case compound division and addition). |
| TEST_P(GLSLTest, VectorScalarDivideAndAddInLoop) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| |
| void main() { |
| gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); |
| for (int i = 0; i < 2; i++) |
| { |
| float x = gl_FragCoord.x; |
| gl_FragColor = gl_FragColor + (x /= 2.0); |
| } |
| if (gl_FragColor.g == gl_FragColor.r && |
| gl_FragColor.b == gl_FragColor.r && |
| gl_FragColor.a == gl_FragColor.r) |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that a varying with a flat qualifier that is used as an operand of a folded ternary operator |
| // is handled correctly. |
| TEST_P(GLSLTest_ES3, FlatVaryingUsedInFoldedTernary) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| |
| in vec4 inputAttribute; |
| |
| flat out int v; |
| |
| void main() |
| { |
| v = 1; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| flat in int v; |
| |
| void main() |
| { |
| my_FragColor = vec4(0, (true ? v : 0), 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "inputAttribute", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Verify that the link error message from last link failure is cleared when the new link is |
| // finished. |
| TEST_P(GLSLTest, ClearLinkErrorLog) |
| { |
| constexpr char kVS[] = R"(attribute vec4 vert_in; |
| varying vec4 vert_out; |
| void main() |
| { |
| gl_Position = vert_in; |
| vert_out = vert_in; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec4 frag_in; |
| void main() |
| { |
| gl_FragColor = frag_in; |
| })"; |
| |
| GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); |
| GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); |
| |
| GLuint program = glCreateProgram(); |
| |
| // The first time the program link fails because of lack of fragment shader. |
| glAttachShader(program, vs); |
| glLinkProgram(program); |
| GLint linkStatus = GL_TRUE; |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); |
| ASSERT_FALSE(linkStatus); |
| |
| const std::string &lackOfFragmentShader = QueryErrorMessage(program); |
| |
| // The second time the program link fails because of the mismatch of the varying types. |
| glAttachShader(program, fs); |
| glLinkProgram(program); |
| linkStatus = GL_TRUE; |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); |
| ASSERT_FALSE(linkStatus); |
| |
| const std::string &varyingTypeMismatch = QueryErrorMessage(program); |
| |
| EXPECT_EQ(std::string::npos, varyingTypeMismatch.find(lackOfFragmentShader)); |
| |
| glDetachShader(program, vs); |
| glDetachShader(program, fs); |
| glDeleteShader(vs); |
| glDeleteShader(fs); |
| glDeleteProgram(program); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Verify that a valid program still draws correctly after a shader link error |
| TEST_P(GLSLTest, DrawAfterShaderLinkError) |
| { |
| constexpr char kVS[] = R"(attribute vec4 position; |
| varying vec4 vColor; |
| void main() |
| { |
| vColor = vec4(0.0, 1.0, 0.0, 1.0); |
| gl_Position = position; |
| })"; |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec4 vColor; |
| void main() |
| { |
| gl_FragColor = vColor; |
| })"; |
| constexpr char kBadFS[] = R"(WILL NOT COMPILE;)"; |
| |
| GLuint fsBad = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| // Create bad fragment shader |
| { |
| const char *sourceArray[1] = {kBadFS}; |
| glShaderSource(fsBad, 1, sourceArray, nullptr); |
| glCompileShader(fsBad); |
| |
| GLint compileResult; |
| glGetShaderiv(fsBad, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_FALSE(compileResult); |
| } |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| GLuint fs = GetProgramShader(program.get(), GL_FRAGMENT_SHADER); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glUseProgram(program.get()); |
| GLint positionLocation = glGetAttribLocation(program.get(), "position"); |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); |
| for (Vector3 &vertex : quadVertices) |
| { |
| vertex.z() = 0.5f; |
| } |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); |
| glEnableVertexAttribArray(positionLocation); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| glDetachShader(program.get(), fs); |
| glAttachShader(program.get(), fsBad); |
| glLinkProgram(program.get()); |
| GLint linkStatus = GL_TRUE; |
| glGetProgramiv(program.get(), GL_LINK_STATUS, &linkStatus); |
| ASSERT_FALSE(linkStatus); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Validate error messages when the link mismatch occurs on the type of a non-struct varying. |
| TEST_P(GLSLTest, ErrorMessageOfVaryingMismatch) |
| { |
| constexpr char kVS[] = R"(attribute vec4 inputAttribute; |
| varying vec4 vertex_out; |
| void main() |
| { |
| vertex_out = inputAttribute; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying float vertex_out; |
| void main() |
| { |
| gl_FragColor = vec4(vertex_out, 0.0, 0.0, 1.0); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "Types", "varying 'vertex_out'"); |
| } |
| |
| // Validate error messages when the link mismatch occurs on the name of a varying field. |
| TEST_P(GLSLTest_ES3, ErrorMessageOfVaryingStructFieldNameMismatch) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| in vec4 inputAttribute; |
| struct S { |
| float val1; |
| vec4 val2; |
| }; |
| out S vertex_out; |
| void main() |
| { |
| vertex_out.val2 = inputAttribute; |
| vertex_out.val1 = inputAttribute[0]; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| struct S { |
| float val1; |
| vec4 val3; |
| }; |
| in S vertex_out; |
| layout (location = 0) out vec4 frag_out; |
| void main() |
| { |
| frag_out = vec4(vertex_out.val1, 0.0, 0.0, 1.0); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "Field names", "varying 'vertex_out'"); |
| } |
| |
| // Validate error messages when the link mismatch occurs on the type of a varying field. |
| TEST_P(GLSLTest_ES3, ErrorMessageOfVaryingStructFieldMismatch) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| in vec4 inputAttribute; |
| struct S { |
| float val1; |
| vec4 val2; |
| }; |
| out S vertex_out; |
| void main() |
| { |
| vertex_out.val2 = inputAttribute; |
| vertex_out.val1 = inputAttribute[0]; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| struct S { |
| float val1; |
| vec2 val2; |
| }; |
| in S vertex_out; |
| layout (location = 0) out vec4 frag_out; |
| void main() |
| { |
| frag_out = vec4(vertex_out.val1, 0.0, 0.0, 1.0); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "Types", |
| "varying 'vertex_out' member 'vertex_out.val2'"); |
| } |
| |
| // Validate error messages when the link mismatch occurs on the name of a struct member of a uniform |
| // field. |
| TEST_P(GLSLTest, ErrorMessageOfLinkUniformStructFieldNameMismatch) |
| { |
| constexpr char kVS[] = R"( |
| struct T |
| { |
| vec2 t1; |
| vec3 t2; |
| }; |
| struct S { |
| T val1; |
| vec4 val2; |
| }; |
| uniform S uni; |
| |
| attribute vec4 inputAttribute; |
| varying vec4 vertex_out; |
| void main() |
| { |
| vertex_out = uni.val2; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(precision highp float; |
| struct T |
| { |
| vec2 t1; |
| vec3 t3; |
| }; |
| struct S { |
| T val1; |
| vec4 val2; |
| }; |
| uniform S uni; |
| |
| varying vec4 vertex_out; |
| void main() |
| { |
| gl_FragColor = vec4(uni.val1.t1[0], 0.0, 0.0, 1.0); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "Field names", "uniform 'uni' member 'uni.val1'"); |
| } |
| |
| // Validate error messages when the link mismatch occurs on the type of a non-struct uniform block |
| // field. |
| TEST_P(GLSLTest_ES3, ErrorMessageOfLinkInterfaceBlockFieldMismatch) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| uniform S { |
| vec2 val1; |
| vec4 val2; |
| } uni; |
| |
| in vec4 inputAttribute; |
| out vec4 vertex_out; |
| void main() |
| { |
| vertex_out = uni.val2; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| uniform S { |
| vec2 val1; |
| vec3 val2; |
| } uni; |
| |
| in vec4 vertex_out; |
| layout (location = 0) out vec4 frag_out; |
| void main() |
| { |
| frag_out = vec4(uni.val1[0], 0.0, 0.0, 1.0); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "Types", "uniform block 'S' member 'S.val2'"); |
| } |
| |
| // Validate error messages when the link mismatch occurs on the type of a member of a uniform block |
| // struct field. |
| TEST_P(GLSLTest_ES3, ErrorMessageOfLinkInterfaceBlockStructFieldMismatch) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| struct T |
| { |
| vec2 t1; |
| vec3 t2; |
| }; |
| uniform S { |
| T val1; |
| vec4 val2; |
| } uni; |
| |
| in vec4 inputAttribute; |
| out vec4 vertex_out; |
| void main() |
| { |
| vertex_out = uni.val2; |
| gl_Position = inputAttribute; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| struct T |
| { |
| vec2 t1; |
| vec4 t2; |
| }; |
| uniform S { |
| T val1; |
| vec4 val2; |
| } uni; |
| |
| in vec4 vertex_out; |
| layout (location = 0) out vec4 frag_out; |
| void main() |
| { |
| frag_out = vec4(uni.val1.t1[0], 0.0, 0.0, 1.0); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "Types", "uniform block 'S' member 'S.val1.t2'"); |
| } |
| |
| // Test a vertex shader that doesn't declare any varyings with a fragment shader that statically |
| // uses a varying, but in a statement that gets trivially optimized out by the compiler. |
| TEST_P(GLSLTest_ES3, FragmentShaderStaticallyUsesVaryingMissingFromVertex) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| precision mediump float; |
| |
| void main() |
| { |
| gl_Position = vec4(0, 1, 0, 1); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| in float foo; |
| out vec4 my_FragColor; |
| |
| void main() |
| { |
| if (false) |
| { |
| float unreferenced = foo; |
| } |
| my_FragColor = vec4(0, 1, 0, 1); |
| })"; |
| |
| validateComponentsInErrorMessage(kVS, kFS, "does not match any", "foo"); |
| } |
| |
| // Test a varying that is statically used but not active in the fragment shader. |
| TEST_P(GLSLTest_ES3, VaryingStaticallyUsedButNotActiveInFragmentShader) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| precision mediump float; |
| in vec4 iv; |
| out vec4 v; |
| void main() |
| { |
| gl_Position = iv; |
| v = iv; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| in vec4 v; |
| out vec4 color; |
| void main() |
| { |
| color = true ? vec4(0.0) : v; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that linking varyings by location works. |
| TEST_P(GLSLTest_ES31, LinkVaryingsByLocation) |
| { |
| constexpr char kVS[] = R"(#version 310 es |
| precision highp float; |
| in vec4 position; |
| layout(location = 1) out vec4 shaderOutput; |
| void main() { |
| gl_Position = position; |
| shaderOutput = vec4(0.0, 1.0, 0.0, 1.0); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| layout(location = 1) in vec4 shaderInput; |
| out vec4 outColor; |
| void main() { |
| outColor = shaderInput; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test nesting floor() calls with a large multiplier inside. |
| TEST_P(GLSLTest_ES3, NestedFloorWithLargeMultiplierInside) |
| { |
| // D3D11 seems to ignore the floor() calls in this particular case, so one of the corners ends |
| // up red. http://crbug.com/838885 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| void main() |
| { |
| vec2 coord = gl_FragCoord.xy / 500.0; |
| my_FragColor = vec4(1, 0, 0, 1); |
| if (coord.y + 0.1 > floor(1e-6 * floor(coord.x*4e5))) |
| { |
| my_FragColor = vec4(0, 1, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| // Verify that all the corners of the rendered result are green. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() - 1, GLColor::green); |
| } |
| |
| // Verify that a link error is generated when the sum of the number of active image uniforms and |
| // active shader storage blocks in a rendering pipeline exceeds |
| // GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES. |
| TEST_P(GLSLTest_ES31, ExceedCombinedShaderOutputResourcesInVSAndFS) |
| { |
| // TODO(jiawei.shao@intel.com): enable this test when shader storage buffer is supported on |
| // D3D11 back-ends. |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| GLint maxVertexShaderStorageBlocks; |
| GLint maxVertexImageUniforms; |
| GLint maxFragmentShaderStorageBlocks; |
| GLint maxFragmentImageUniforms; |
| GLint maxCombinedShaderStorageBlocks; |
| GLint maxCombinedImageUniforms; |
| glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks); |
| glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &maxVertexImageUniforms); |
| glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks); |
| glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &maxFragmentImageUniforms); |
| glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &maxCombinedShaderStorageBlocks); |
| glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &maxCombinedImageUniforms); |
| |
| ASSERT_GE(maxCombinedShaderStorageBlocks, maxVertexShaderStorageBlocks); |
| ASSERT_GE(maxCombinedShaderStorageBlocks, maxFragmentShaderStorageBlocks); |
| ASSERT_GE(maxCombinedImageUniforms, maxVertexImageUniforms); |
| ASSERT_GE(maxCombinedImageUniforms, maxFragmentImageUniforms); |
| |
| GLint vertexSSBOs = maxVertexShaderStorageBlocks; |
| GLint fragmentSSBOs = maxFragmentShaderStorageBlocks; |
| // Limit the sum of ssbos in vertex and fragment shaders to maxCombinedShaderStorageBlocks. |
| if (vertexSSBOs + fragmentSSBOs > maxCombinedShaderStorageBlocks) |
| { |
| fragmentSSBOs = maxCombinedShaderStorageBlocks - vertexSSBOs; |
| } |
| |
| GLint vertexImages = maxVertexImageUniforms; |
| GLint fragmentImages = maxFragmentImageUniforms; |
| // Limit the sum of images in vertex and fragment shaders to maxCombinedImageUniforms. |
| if (vertexImages + fragmentImages > maxCombinedImageUniforms) |
| { |
| vertexImages = maxCombinedImageUniforms - fragmentImages; |
| } |
| |
| GLint maxDrawBuffers; |
| glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); |
| |
| GLint maxCombinedShaderOutputResources; |
| glGetIntegerv(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, &maxCombinedShaderOutputResources); |
| ASSERT_GL_NO_ERROR(); |
| |
| ANGLE_SKIP_TEST_IF(vertexSSBOs + fragmentSSBOs + vertexImages + fragmentImages + |
| maxDrawBuffers <= |
| maxCombinedShaderOutputResources); |
| |
| std::ostringstream vertexStream; |
| vertexStream << "#version 310 es\n"; |
| for (int i = 0; i < vertexSSBOs; ++i) |
| { |
| vertexStream << "layout(shared, binding = " << i << ") buffer blockName" << i |
| << "{\n" |
| " float data;\n" |
| "} ssbo" |
| << i << ";\n"; |
| } |
| vertexStream << "layout(r32f, binding = 0) uniform highp image2D imageArray[" << vertexImages |
| << "];\n"; |
| vertexStream << "void main()\n" |
| "{\n" |
| " float val = 0.1;\n" |
| " vec4 val2 = vec4(0.0);\n"; |
| for (int i = 0; i < vertexSSBOs; ++i) |
| { |
| vertexStream << " val += ssbo" << i << ".data; \n"; |
| } |
| for (int i = 0; i < vertexImages; ++i) |
| { |
| vertexStream << " val2 += imageLoad(imageArray[" << i << "], ivec2(0, 0)); \n"; |
| } |
| vertexStream << " gl_Position = vec4(val, val2);\n" |
| "}\n"; |
| |
| std::ostringstream fragmentStream; |
| fragmentStream << "#version 310 es\n" |
| << "precision highp float;\n"; |
| for (int i = 0; i < fragmentSSBOs; ++i) |
| { |
| fragmentStream << "layout(shared, binding = " << i << ") buffer blockName" << i |
| << "{\n" |
| " float data;\n" |
| "} ssbo" |
| << i << ";\n"; |
| } |
| fragmentStream << "layout(r32f, binding = 0) uniform highp image2D imageArray[" |
| << fragmentImages << "];\n"; |
| fragmentStream << "layout (location = 0) out vec4 foutput[" << maxDrawBuffers << "];\n"; |
| |
| fragmentStream << "void main()\n" |
| "{\n" |
| " float val = 0.1;\n" |
| " vec4 val2 = vec4(0.0);\n"; |
| for (int i = 0; i < fragmentSSBOs; ++i) |
| { |
| fragmentStream << " val += ssbo" << i << ".data; \n"; |
| } |
| for (int i = 0; i < fragmentImages; ++i) |
| { |
| fragmentStream << " val2 += imageLoad(imageArray[" << i << "], ivec2(0, 0)); \n"; |
| } |
| for (int i = 0; i < maxDrawBuffers; ++i) |
| { |
| fragmentStream << " foutput[" << i << "] = vec4(val, val2);\n"; |
| } |
| fragmentStream << "}\n"; |
| |
| GLuint program = CompileProgram(vertexStream.str().c_str(), fragmentStream.str().c_str()); |
| EXPECT_EQ(0u, program); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that assigning an assignment expression to a swizzled vector field in a user-defined |
| // function works correctly. |
| TEST_P(GLSLTest_ES3, AssignAssignmentToSwizzled) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| uniform float uzero; |
| |
| vec3 fun(float s, float v) |
| { |
| vec3 r = vec3(0); |
| if (s < 1.0) { |
| r.x = r.y = r.z = v; |
| return r; |
| } |
| return r; |
| } |
| |
| void main() |
| { |
| my_FragColor.a = 1.0; |
| my_FragColor.rgb = fun(uzero, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test a fragment shader that returns inside if (that being the only branch that actually gets |
| // executed). Regression test for http://anglebug.com/2325 |
| TEST_P(GLSLTest, IfElseIfAndReturn) |
| { |
| constexpr char kVS[] = R"(attribute vec4 a_position; |
| varying vec2 vPos; |
| void main() |
| { |
| gl_Position = a_position; |
| vPos = a_position.xy; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec2 vPos; |
| void main() |
| { |
| if (vPos.x < 1.0) // This colors the whole canvas green |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| return; |
| } |
| else if (vPos.x < 1.1) // This should have no effect |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program.get(), "a_position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that if-else blocks whose contents get pruned due to compile-time constant conditions work. |
| TEST_P(GLSLTest, IfElsePrunedBlocks) |
| { |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform float u; |
| void main() |
| { |
| // if with only a pruned true block |
| if (u > 0.0) |
| if (false) discard; |
| |
| // if with a pruned true block and a false block |
| if (u > 0.0) |
| { |
| if (false) discard; |
| } |
| else |
| ; |
| |
| // if with a true block and a pruned false block |
| if (u > 0.0) |
| ; |
| else |
| if (false) discard; |
| |
| // if with a pruned true block and a pruned false block |
| if (u > 0.0) |
| { |
| if (false) discard; |
| } |
| else |
| if (false) discard; |
| |
| gl_FragColor = vec4(0, 1, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests that PointCoord behaves the same betweeen a user FBO and the back buffer. |
| TEST_P(GLSLTest, PointCoordConsistency) |
| { |
| // AMD's OpenGL drivers may have the same issue. http://anglebug.com/1643 |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL()); |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(isSwiftshader()); |
| |
| constexpr char kPointCoordVS[] = R"(attribute vec2 position; |
| uniform vec2 viewportSize; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| gl_PointSize = viewportSize.x; |
| })"; |
| |
| constexpr char kPointCoordFS[] = R"(void main() |
| { |
| gl_FragColor = vec4(gl_PointCoord.xy, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kPointCoordVS, kPointCoordFS); |
| glUseProgram(program); |
| |
| GLint uniLoc = glGetUniformLocation(program, "viewportSize"); |
| ASSERT_NE(-1, uniLoc); |
| glUniform2f(uniLoc, static_cast<GLfloat>(getWindowWidth()), |
| static_cast<GLfloat>(getWindowHeight())); |
| |
| // Draw to backbuffer. |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDrawArrays(GL_POINTS, 0, 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| std::vector<GLColor> backbufferData(getWindowWidth() * getWindowHeight()); |
| glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, |
| backbufferData.data()); |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Draw to user FBO. |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDrawArrays(GL_POINTS, 0, 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| std::vector<GLColor> userFBOData(getWindowWidth() * getWindowHeight()); |
| glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, |
| userFBOData.data()); |
| |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(userFBOData.size(), backbufferData.size()); |
| EXPECT_EQ(userFBOData, backbufferData); |
| } |
| |
| bool SubrectEquals(const std::vector<GLColor> &bigArray, |
| const std::vector<GLColor> &smallArray, |
| int bigSize, |
| int offset, |
| int smallSize) |
| { |
| int badPixels = 0; |
| for (int y = 0; y < smallSize; y++) |
| { |
| for (int x = 0; x < smallSize; x++) |
| { |
| int bigOffset = (y + offset) * bigSize + x + offset; |
| int smallOffset = y * smallSize + x; |
| if (bigArray[bigOffset] != smallArray[smallOffset]) |
| badPixels++; |
| } |
| } |
| return badPixels == 0; |
| } |
| |
| // Tests that FragCoord behaves the same betweeen a user FBO and the back buffer. |
| TEST_P(GLSLTest, FragCoordConsistency) |
| { |
| constexpr char kFragCoordShader[] = R"(uniform mediump vec2 viewportSize; |
| void main() |
| { |
| gl_FragColor = vec4(gl_FragCoord.xy / viewportSize, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragCoordShader); |
| glUseProgram(program); |
| |
| GLint uniLoc = glGetUniformLocation(program, "viewportSize"); |
| ASSERT_NE(-1, uniLoc); |
| glUniform2f(uniLoc, static_cast<GLfloat>(getWindowWidth()), |
| static_cast<GLfloat>(getWindowHeight())); |
| |
| // Draw to backbuffer. |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5); |
| ASSERT_GL_NO_ERROR(); |
| |
| std::vector<GLColor> backbufferData(getWindowWidth() * getWindowHeight()); |
| glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, |
| backbufferData.data()); |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Draw to user FBO. |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5); |
| ASSERT_GL_NO_ERROR(); |
| |
| std::vector<GLColor> userFBOData(getWindowWidth() * getWindowHeight()); |
| glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, |
| userFBOData.data()); |
| |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(userFBOData.size(), backbufferData.size()); |
| EXPECT_EQ(userFBOData, backbufferData) |
| << "FragCoord should be the same to default and user FBO"; |
| |
| // Repeat the same test but with a smaller viewport. |
| ASSERT_EQ(getWindowHeight(), getWindowWidth()); |
| const int kQuarterSize = getWindowWidth() >> 2; |
| glViewport(kQuarterSize, kQuarterSize, kQuarterSize * 2, kQuarterSize * 2); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5); |
| |
| std::vector<GLColor> userFBOViewportData(kQuarterSize * kQuarterSize * 4); |
| glReadPixels(kQuarterSize, kQuarterSize, kQuarterSize * 2, kQuarterSize * 2, GL_RGBA, |
| GL_UNSIGNED_BYTE, userFBOViewportData.data()); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5); |
| |
| std::vector<GLColor> defaultFBOViewportData(kQuarterSize * kQuarterSize * 4); |
| glReadPixels(kQuarterSize, kQuarterSize, kQuarterSize * 2, kQuarterSize * 2, GL_RGBA, |
| GL_UNSIGNED_BYTE, defaultFBOViewportData.data()); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_EQ(userFBOViewportData, defaultFBOViewportData) |
| << "FragCoord should be the same to default and user FBO even with a custom viewport"; |
| |
| // Check that the subrectangles are the same between the viewport and non-viewport modes. |
| EXPECT_TRUE(SubrectEquals(userFBOData, userFBOViewportData, getWindowWidth(), kQuarterSize, |
| kQuarterSize * 2)); |
| EXPECT_TRUE(SubrectEquals(backbufferData, defaultFBOViewportData, getWindowWidth(), |
| kQuarterSize, kQuarterSize * 2)); |
| } |
| |
| // Ensure that using defined in a macro works in this simple case. This mirrors a dEQP test. |
| TEST_P(GLSLTest, DefinedInMacroSucceeds) |
| { |
| constexpr char kVS[] = R"(precision mediump float; |
| attribute highp vec4 position; |
| varying vec2 out0; |
| |
| void main() |
| { |
| #define AAA defined(BBB) |
| |
| #if !AAA |
| out0 = vec2(0.0, 1.0); |
| #else |
| out0 = vec2(1.0, 0.0); |
| #endif |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec2 out0; |
| void main() |
| { |
| gl_FragColor = vec4(out0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Validate the defined operator is evaluated when the macro is called, not when defined. |
| TEST_P(GLSLTest, DefinedInMacroWithUndef) |
| { |
| constexpr char kVS[] = R"(precision mediump float; |
| attribute highp vec4 position; |
| varying vec2 out0; |
| |
| void main() |
| { |
| #define BBB 1 |
| #define AAA defined(BBB) |
| #undef BBB |
| |
| #if AAA |
| out0 = vec2(1.0, 0.0); |
| #else |
| out0 = vec2(0.0, 1.0); |
| #endif |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec2 out0; |
| void main() |
| { |
| gl_FragColor = vec4(out0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Validate the defined operator is evaluated when the macro is called, not when defined. |
| TEST_P(GLSLTest, DefinedAfterMacroUsage) |
| { |
| constexpr char kVS[] = R"(precision mediump float; |
| attribute highp vec4 position; |
| varying vec2 out0; |
| |
| void main() |
| { |
| #define AAA defined(BBB) |
| #define BBB 1 |
| |
| #if AAA |
| out0 = vec2(0.0, 1.0); |
| #else |
| out0 = vec2(1.0, 0.0); |
| #endif |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec2 out0; |
| void main() |
| { |
| gl_FragColor = vec4(out0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test generating "defined" by concatenation when a macro is called. This is not allowed. |
| TEST_P(GLSLTest, DefinedInMacroConcatenationNotAllowed) |
| { |
| constexpr char kVS[] = R"(precision mediump float; |
| attribute highp vec4 position; |
| varying vec2 out0; |
| |
| void main() |
| { |
| #define BBB 1 |
| #define AAA(defi, ned) defi ## ned(BBB) |
| |
| #if AAA(defi, ned) |
| out0 = vec2(0.0, 1.0); |
| #else |
| out0 = vec2(1.0, 0.0); |
| #endif |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec2 out0; |
| void main() |
| { |
| gl_FragColor = vec4(out0, 0, 1); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| glDeleteProgram(program); |
| } |
| |
| // Test using defined in a macro parameter name. This is not allowed. |
| TEST_P(GLSLTest, DefinedAsParameterNameNotAllowed) |
| { |
| constexpr char kVS[] = R"(precision mediump float; |
| attribute highp vec4 position; |
| varying vec2 out0; |
| |
| void main() |
| { |
| #define BBB 1 |
| #define AAA(defined) defined(BBB) |
| |
| #if AAA(defined) |
| out0 = vec2(0.0, 1.0); |
| #else |
| out0 = vec2(1.0, 0.0); |
| #endif |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying vec2 out0; |
| void main() |
| { |
| gl_FragColor = vec4(out0, 0, 1); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| glDeleteProgram(program); |
| } |
| |
| // Ensure that defined in a macro is no accepted in WebGL. |
| TEST_P(WebGLGLSLTest, DefinedInMacroFails) |
| { |
| constexpr char kVS[] = R"(precision mediump float; |
| attribute highp vec4 position; |
| varying float out0; |
| |
| void main() |
| { |
| #define AAA defined(BBB) |
| |
| #if !AAA |
| out0 = 1.0; |
| #else |
| out0 = 0.0; |
| #endif |
| gl_Position = dEQP_Position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| varying float out0; |
| void main() |
| { |
| gl_FragColor = vec4(out0, 0, 0, 1); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| glDeleteProgram(program); |
| } |
| |
| // Simple test using a define macro in WebGL. |
| TEST_P(WebGLGLSLTest, DefinedGLESSymbol) |
| { |
| constexpr char kVS[] = R"(void main() |
| { |
| gl_Position = vec4(1, 0, 0, 1); |
| })"; |
| |
| constexpr char kFS[] = R"(#if defined(GL_ES) |
| precision mediump float; |
| void main() |
| { |
| gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); |
| } |
| #else |
| foo |
| #endif |
| )"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| } |
| |
| // Test that clamp applied on non-literal indices is correct on es 100 shaders. |
| TEST_P(GLSLTest, ValidIndexClampES100) |
| { |
| // http://anglebug.com/6027 |
| ANGLE_SKIP_TEST_IF(IsD3D9()); |
| |
| constexpr char kFS[] = R"( |
| precision mediump float; |
| uniform int u; |
| uniform mat4 m[2]; |
| void main() |
| { |
| gl_FragColor = vec4(m[u][1].xyz, 1); |
| } |
| )"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint uniformLocation = glGetUniformLocation(program, "u"); |
| ASSERT_NE(-1, uniformLocation); |
| |
| GLint matrixLocation = glGetUniformLocation(program, "m"); |
| ASSERT_NE(matrixLocation, -1); |
| const std::array<GLfloat, 32> mValue = {{0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, |
| 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, |
| 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, |
| 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}; |
| glUniformMatrix4fv(matrixLocation, 2, false, mValue.data()); |
| |
| glUniform1i(uniformLocation, 1); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that clamp applied on non-literal indices is correct on es 300 shaders. |
| TEST_P(GLSLTest_ES3, ValidIndexClampES300) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| uniform int u; |
| mat4 m[4] = mat4[4](mat4(0.25), mat4(0.5), mat4(1), mat4(0.75)); |
| void main() |
| { |
| color = vec4(m[u][2].xyz, 1); |
| } |
| )"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint uniformLocation = glGetUniformLocation(program, "u"); |
| ASSERT_NE(-1, uniformLocation); |
| |
| glUniform1i(uniformLocation, 2); |
| EXPECT_GL_NO_ERROR(); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| } |
| |
| // Tests constant folding of non-square 'matrixCompMult'. |
| TEST_P(GLSLTest_ES3, NonSquareMatrixCompMult) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| |
| const mat4x2 matA = mat4x2(2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0); |
| const mat4x2 matB = mat4x2(1.0/2.0, 1.0/4.0, 1.0/8.0, 1.0/16.0, 1.0/32.0, 1.0/64.0, 1.0/128.0, 1.0/256.0); |
| |
| out vec4 color; |
| |
| void main() |
| { |
| mat4x2 result = matrixCompMult(matA, matB); |
| vec2 vresult = result * vec4(1.0, 1.0, 1.0, 1.0); |
| if (vresult == vec2(4.0, 4.0)) |
| { |
| color = vec4(0.0, 1.0, 0.0, 1.0); |
| } |
| else |
| { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test initializing an array with the same name of previously declared array |
| TEST_P(GLSLTest_ES3, InitSameNameArray) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| |
| void main() |
| { |
| float arr[2] = float[2](1.0, 1.0); |
| { |
| float arr[2] = arr; |
| my_FragColor = vec4(0.0, arr[0], 0.0, arr[1]); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests using gl_FragData[0] instead of gl_FragColor. |
| TEST_P(GLSLTest, FragData) |
| { |
| // Ensures that we don't regress and emit Vulkan layer warnings. |
| // TODO(jonahr): http://anglebug.com/3900 - Remove check once warnings are cleaned up |
| if (IsVulkan()) |
| { |
| treatPlatformWarningsAsErrors(); |
| } |
| |
| constexpr char kFS[] = R"(void main() { gl_FragData[0] = vec4(1, 0, 0, 1); })"; |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test angle can handle big initial stack size with dynamic stack allocation. |
| TEST_P(GLSLTest, MemoryExhaustedTest) |
| { |
| ANGLE_SKIP_TEST_IF(IsD3D11_FL93()); |
| GLuint program = |
| CompileProgram(essl1_shaders::vs::Simple(), BuildBigInitialStackShader(36).c_str()); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that inactive samplers in structs don't cause any errors. |
| TEST_P(GLSLTest, InactiveSamplersInStruct) |
| { |
| constexpr char kVS[] = R"(attribute vec4 a_position; |
| void main() { |
| gl_Position = a_position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision highp float; |
| struct S |
| { |
| vec4 v; |
| sampler2D t[10]; |
| }; |
| uniform S s; |
| void main() { |
| gl_FragColor = s.v; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| drawQuad(program, "a_position", 0.5f); |
| } |
| |
| // Helper functions for MixedRowAndColumnMajorMatrices* tests |
| |
| // Round up to alignment, assuming it's a power of 2 |
| uint32_t RoundUpPow2(uint32_t value, uint32_t alignment) |
| { |
| return (value + alignment - 1) & ~(alignment - 1); |
| } |
| |
| void CreateOutputBuffer(GLBuffer *buffer, uint32_t binding) |
| { |
| unsigned int outputInitData = 0; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, *buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, *buffer); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Fill provided buffer with matrices based on the given dimensions. The buffer should be large |
| // enough to accomodate the data. |
| uint32_t FillBuffer(const std::pair<uint32_t, uint32_t> matrixDims[], |
| const bool matrixIsColMajor[], |
| size_t matrixCount, |
| float data[], |
| bool isStd430, |
| bool isTransposed) |
| { |
| size_t offset = 0; |
| for (size_t m = 0; m < matrixCount; ++m) |
| { |
| uint32_t cols = matrixDims[m].first; |
| uint32_t rows = matrixDims[m].second; |
| bool isColMajor = matrixIsColMajor[m] != isTransposed; |
| |
| uint32_t arraySize = isColMajor ? cols : rows; |
| uint32_t arrayElementComponents = isColMajor ? rows : cols; |
| // Note: stride is generally 4 with std140, except for scalar and gvec2 types (which |
| // MixedRowAndColumnMajorMatrices* tests don't use). With std430, small matrices can have |
| // a stride of 2 between rows/columns. |
| uint32_t stride = isStd430 ? RoundUpPow2(arrayElementComponents, 2) : 4; |
| |
| offset = RoundUpPow2(offset, stride); |
| |
| for (uint32_t i = 0; i < arraySize; ++i) |
| { |
| for (uint32_t c = 0; c < arrayElementComponents; ++c) |
| { |
| uint32_t row = isColMajor ? c : i; |
| uint32_t col = isColMajor ? i : c; |
| |
| data[offset + i * stride + c] = col * 4 + row; |
| } |
| } |
| |
| offset += arraySize * stride; |
| } |
| return offset; |
| } |
| |
| // Initialize and bind the buffer. |
| template <typename T> |
| void InitBuffer(GLuint program, |
| const char *name, |
| GLuint buffer, |
| uint32_t bindingIndex, |
| const T data[], |
| uint32_t dataSize, |
| bool isUniform) |
| { |
| GLenum bindPoint = isUniform ? GL_UNIFORM_BUFFER : GL_SHADER_STORAGE_BUFFER; |
| |
| glBindBufferBase(bindPoint, bindingIndex, buffer); |
| glBufferData(bindPoint, dataSize * sizeof(*data), data, GL_STATIC_DRAW); |
| |
| if (isUniform) |
| { |
| GLint blockIndex = glGetUniformBlockIndex(program, name); |
| glUniformBlockBinding(program, blockIndex, bindingIndex); |
| } |
| } |
| |
| // Verify that buffer data is written by the shader as expected. |
| template <typename T> |
| bool VerifyBuffer(GLuint buffer, const T data[], uint32_t dataSize) |
| { |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); |
| |
| const T *ptr = reinterpret_cast<const T *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, dataSize, GL_MAP_READ_BIT)); |
| |
| bool isCorrect = memcmp(ptr, data, dataSize * sizeof(*data)) == 0; |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| |
| return isCorrect; |
| } |
| |
| // Verify that the success output of the shader is as expected. |
| bool VerifySuccess(GLuint buffer) |
| { |
| uint32_t success = 1; |
| return VerifyBuffer(buffer, reinterpret_cast<const float *>(&success), 1); |
| } |
| |
| // Test reading from UBOs and SSBOs and writing to SSBOs with mixed row- and colum-major layouts in |
| // both std140 and std430 layouts. Tests many combinations of std140 vs std430, struct being used |
| // as row- or column-major in different UBOs, reading from UBOs and SSBOs and writing to SSBOs, |
| // nested structs, matrix arrays, inout parameters etc. |
| // |
| // Some very specific corner cases that are not covered here are tested in the subsequent tests. |
| TEST_P(GLSLTest_ES31, MixedRowAndColumnMajorMatrices) |
| { |
| GLint maxComputeShaderStorageBlocks; |
| glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &maxComputeShaderStorageBlocks); |
| |
| // The test uses 9 SSBOs. Skip if not that many are supported. |
| ANGLE_SKIP_TEST_IF(maxComputeShaderStorageBlocks < 9); |
| |
| // Fails on Nvidia because having |Matrices| qualified as row-major in one UBO makes the other |
| // UBO also see it as row-major despite explicit column-major qualifier. |
| // http://anglebug.com/3830 |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL()); |
| |
| // Fails on mesa because in the first UBO which is qualified as column-major, |Matrices| is |
| // read column-major despite explicit row-major qualifier. http://anglebug.com/3837 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL()); |
| |
| // Fails on windows AMD on GL: http://anglebug.com/3838 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsOpenGL() && IsAMD()); |
| |
| // Fails to compile the shader on Android. http://anglebug.com/3839 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL()); |
| |
| // Fails on assertion in translation to D3D. http://anglebug.com/3841 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| // Fails on SSBO validation on Android/Vulkan. http://anglebug.com/3840 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan()); |
| |
| // Fails input verification as well as std140 SSBO validation. http://anglebug.com/3844 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan()); |
| |
| // Fails on ARM on Vulkan. http://anglebug.com/4492 |
| ANGLE_SKIP_TEST_IF(IsARM() && IsVulkan()); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| precision highp float; |
| layout(local_size_x=1) in; |
| |
| struct Inner |
| { |
| mat3x4 m3c4r; |
| mat4x3 m4c3r; |
| }; |
| |
| struct Matrices |
| { |
| mat2 m2c2r; |
| mat2x3 m2c3r[2]; |
| mat3x2 m3c2r; |
| Inner inner; |
| }; |
| |
| // For simplicity, the layouts are either of: |
| // - col-major mat4, row-major rest |
| // - row-major mat4, col-major rest |
| // |
| // The former is tagged with c, the latter with r. |
| layout(std140, column_major) uniform Ubo140c |
| { |
| mat4 m4c4r; |
| layout(row_major) Matrices m; |
| } ubo140cIn; |
| |
| layout(std140, row_major) uniform Ubo140r |
| { |
| mat4 m4c4r; |
| layout(column_major) Matrices m; |
| } ubo140rIn; |
| |
| layout(std140, row_major, binding = 0) buffer Ssbo140c |
| { |
| layout(column_major) mat4 m4c4r; |
| Matrices m; |
| } ssbo140cIn; |
| |
| layout(std140, column_major, binding = 1) buffer Ssbo140r |
| { |
| layout(row_major) mat4 m4c4r; |
| Matrices m; |
| } ssbo140rIn; |
| |
| layout(std430, column_major, binding = 2) buffer Ssbo430c |
| { |
| mat4 m4c4r; |
| layout(row_major) Matrices m; |
| } ssbo430cIn; |
| |
| layout(std430, row_major, binding = 3) buffer Ssbo430r |
| { |
| mat4 m4c4r; |
| layout(column_major) Matrices m; |
| } ssbo430rIn; |
| |
| layout(std140, row_major, binding = 4) buffer Ssbo140cOut |
| { |
| layout(column_major) mat4 m4c4r; |
| Matrices m; |
| } ssbo140cOut; |
| |
| layout(std140, column_major, binding = 5) buffer Ssbo140rOut |
| { |
| layout(row_major) mat4 m4c4r; |
| Matrices m; |
| } ssbo140rOut; |
| |
| layout(std430, column_major, binding = 6) buffer Ssbo430cOut |
| { |
| mat4 m4c4r; |
| layout(row_major) Matrices m; |
| } ssbo430cOut; |
| |
| layout(std430, row_major, binding = 7) buffer Ssbo430rOut |
| { |
| mat4 m4c4r; |
| layout(column_major) Matrices m; |
| } ssbo430rOut; |
| |
| layout(std140, binding = 8) buffer Result |
| { |
| uint success; |
| } resultOut; |
| |
| #define EXPECT(result, expression, value) if ((expression) != value) { result = false; } |
| #define EXPECTV(result, expression, value) if (any(notEqual(expression, value))) { result = false; } |
| |
| #define VERIFY_IN(result, mat, cols, rows) \ |
| EXPECT(result, mat[0].x, 0.0); \ |
| EXPECT(result, mat[0][1], 1.0); \ |
| EXPECTV(result, mat[0].xy, vec2(0, 1)); \ |
| EXPECTV(result, mat[1].xy, vec2(4, 5)); \ |
| for (int c = 0; c < cols; ++c) \ |
| { \ |
| for (int r = 0; r < rows; ++r) \ |
| { \ |
| EXPECT(result, mat[c][r], float(c * 4 + r)); \ |
| } \ |
| } |
| |
| #define COPY(matIn, matOut, cols, rows) \ |
| matOut = matOut + matIn; \ |
| /* random operations for testing */ \ |
| matOut[0].x += matIn[0].x + matIn[1].x; \ |
| matOut[0].x -= matIn[1].x; \ |
| matOut[0][1] += matIn[0][1]; \ |
| matOut[1] += matIn[1]; \ |
| matOut[1].xy -= matIn[1].xy; \ |
| /* undo the above to get back matIn */ \ |
| matOut[0].x -= matIn[0].x; \ |
| matOut[0][1] -= matIn[0][1]; \ |
| matOut[1] -= matIn[1]; \ |
| matOut[1].xy += matIn[1].xy; |
| |
| bool verifyMatrices(in Matrices m) |
| { |
| bool result = true; |
| VERIFY_IN(result, m.m2c2r, 2, 2); |
| VERIFY_IN(result, m.m2c3r[0], 2, 3); |
| VERIFY_IN(result, m.m2c3r[1], 2, 3); |
| VERIFY_IN(result, m.m3c2r, 3, 2); |
| VERIFY_IN(result, m.inner.m3c4r, 3, 4); |
| VERIFY_IN(result, m.inner.m4c3r, 4, 3); |
| return result; |
| } |
| |
| mat4 copyMat4(in mat4 m) |
| { |
| return m; |
| } |
| |
| void copyMatrices(in Matrices mIn, inout Matrices mOut) |
| { |
| COPY(mIn.m2c2r, mOut.m2c2r, 2, 2); |
| COPY(mIn.m2c3r[0], mOut.m2c3r[0], 2, 3); |
| COPY(mIn.m2c3r[1], mOut.m2c3r[1], 2, 3); |
| COPY(mIn.m3c2r, mOut.m3c2r, 3, 2); |
| COPY(mIn.inner.m3c4r, mOut.inner.m3c4r, 3, 4); |
| COPY(mIn.inner.m4c3r, mOut.inner.m4c3r, 4, 3); |
| } |
| |
| void main() |
| { |
| bool result = true; |
| |
| VERIFY_IN(result, ubo140cIn.m4c4r, 4, 4); |
| VERIFY_IN(result, ubo140cIn.m.m2c3r[0], 2, 3); |
| EXPECT(result, verifyMatrices(ubo140cIn.m), true); |
| |
| VERIFY_IN(result, ubo140rIn.m4c4r, 4, 4); |
| VERIFY_IN(result, ubo140rIn.m.m2c2r, 2, 2); |
| EXPECT(result, verifyMatrices(ubo140rIn.m), true); |
| |
| VERIFY_IN(result, ssbo140cIn.m4c4r, 4, 4); |
| VERIFY_IN(result, ssbo140cIn.m.m3c2r, 3, 2); |
| EXPECT(result, verifyMatrices(ssbo140cIn.m), true); |
| |
| VERIFY_IN(result, ssbo140rIn.m4c4r, 4, 4); |
| VERIFY_IN(result, ssbo140rIn.m.inner.m4c3r, 4, 3); |
| EXPECT(result, verifyMatrices(ssbo140rIn.m), true); |
| |
| VERIFY_IN(result, ssbo430cIn.m4c4r, 4, 4); |
| VERIFY_IN(result, ssbo430cIn.m.m2c3r[1], 2, 3); |
| EXPECT(result, verifyMatrices(ssbo430cIn.m), true); |
| |
| VERIFY_IN(result, ssbo430rIn.m4c4r, 4, 4); |
| VERIFY_IN(result, ssbo430rIn.m.inner.m3c4r, 3, 4); |
| EXPECT(result, verifyMatrices(ssbo430rIn.m), true); |
| |
| // Only assign to SSBO from a single invocation. |
| if (gl_GlobalInvocationID.x == 0u) |
| { |
| ssbo140cOut.m4c4r = copyMat4(ssbo140cIn.m4c4r); |
| copyMatrices(ssbo430cIn.m, ssbo140cOut.m); |
| ssbo140cOut.m.m2c3r[1] = mat2x3(0); |
| COPY(ssbo430cIn.m.m2c3r[1], ssbo140cOut.m.m2c3r[1], 2, 3); |
| |
| ssbo140rOut.m4c4r = copyMat4(ssbo140rIn.m4c4r); |
| copyMatrices(ssbo430rIn.m, ssbo140rOut.m); |
| ssbo140rOut.m.inner.m3c4r = mat3x4(0); |
| COPY(ssbo430rIn.m.inner.m3c4r, ssbo140rOut.m.inner.m3c4r, 3, 4); |
| |
| ssbo430cOut.m4c4r = copyMat4(ssbo430cIn.m4c4r); |
| copyMatrices(ssbo140cIn.m, ssbo430cOut.m); |
| ssbo430cOut.m.m3c2r = mat3x2(0); |
| COPY(ssbo430cIn.m.m3c2r, ssbo430cOut.m.m3c2r, 3, 2); |
| |
| ssbo430rOut.m4c4r = copyMat4(ssbo430rIn.m4c4r); |
| copyMatrices(ssbo140rIn.m, ssbo430rOut.m); |
| ssbo430rOut.m.inner.m4c3r = mat4x3(0); |
| COPY(ssbo430rIn.m.inner.m4c3r, ssbo430rOut.m.inner.m4c3r, 4, 3); |
| |
| resultOut.success = uint(result); |
| } |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 7; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, {2, 2}, {2, 3}, {2, 3}, {3, 2}, {3, 4}, {4, 3}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = { |
| true, false, false, false, false, false, false, |
| }; |
| |
| float dataStd140ColMajor[kMatrixCount * 4 * 4] = {}; |
| float dataStd140RowMajor[kMatrixCount * 4 * 4] = {}; |
| float dataStd430ColMajor[kMatrixCount * 4 * 4] = {}; |
| float dataStd430RowMajor[kMatrixCount * 4 * 4] = {}; |
| float dataZeros[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t sizeStd140ColMajor = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, dataStd140ColMajor, false, false); |
| const uint32_t sizeStd140RowMajor = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, dataStd140RowMajor, false, true); |
| const uint32_t sizeStd430ColMajor = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, dataStd430ColMajor, true, false); |
| const uint32_t sizeStd430RowMajor = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, dataStd430RowMajor, true, true); |
| |
| GLBuffer uboStd140ColMajor, uboStd140RowMajor; |
| GLBuffer ssboStd140ColMajor, ssboStd140RowMajor; |
| GLBuffer ssboStd430ColMajor, ssboStd430RowMajor; |
| GLBuffer ssboStd140ColMajorOut, ssboStd140RowMajorOut; |
| GLBuffer ssboStd430ColMajorOut, ssboStd430RowMajorOut; |
| |
| InitBuffer(program, "Ubo140c", uboStd140ColMajor, 0, dataStd140ColMajor, sizeStd140ColMajor, |
| true); |
| InitBuffer(program, "Ubo140r", uboStd140RowMajor, 1, dataStd140RowMajor, sizeStd140RowMajor, |
| true); |
| InitBuffer(program, "Ssbo140c", ssboStd140ColMajor, 0, dataStd140ColMajor, sizeStd140ColMajor, |
| false); |
| InitBuffer(program, "Ssbo140r", ssboStd140RowMajor, 1, dataStd140RowMajor, sizeStd140RowMajor, |
| false); |
| InitBuffer(program, "Ssbo430c", ssboStd430ColMajor, 2, dataStd430ColMajor, sizeStd430ColMajor, |
| false); |
| InitBuffer(program, "Ssbo430r", ssboStd430RowMajor, 3, dataStd430RowMajor, sizeStd430RowMajor, |
| false); |
| InitBuffer(program, "Ssbo140cOut", ssboStd140ColMajorOut, 4, dataZeros, sizeStd140ColMajor, |
| false); |
| InitBuffer(program, "Ssbo140rOut", ssboStd140RowMajorOut, 5, dataZeros, sizeStd140RowMajor, |
| false); |
| InitBuffer(program, "Ssbo430cOut", ssboStd430ColMajorOut, 6, dataZeros, sizeStd430ColMajor, |
| false); |
| InitBuffer(program, "Ssbo430rOut", ssboStd430RowMajorOut, 7, dataZeros, sizeStd430RowMajor, |
| false); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLBuffer outputBuffer; |
| CreateOutputBuffer(&outputBuffer, 8); |
| |
| glUseProgram(program); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_TRUE(VerifySuccess(outputBuffer)); |
| |
| EXPECT_TRUE(VerifyBuffer(ssboStd140ColMajorOut, dataStd140ColMajor, sizeStd140ColMajor)); |
| EXPECT_TRUE(VerifyBuffer(ssboStd140RowMajorOut, dataStd140RowMajor, sizeStd140RowMajor)); |
| EXPECT_TRUE(VerifyBuffer(ssboStd430ColMajorOut, dataStd430ColMajor, sizeStd430ColMajor)); |
| EXPECT_TRUE(VerifyBuffer(ssboStd430RowMajorOut, dataStd430RowMajor, sizeStd430RowMajor)); |
| } |
| |
| // Test that array UBOs are transformed correctly. |
| TEST_P(GLSLTest_ES3, MixedRowAndColumnMajorMatrices_ArrayBufferDeclaration) |
| { |
| // Fails to compile the shader on Android: http://anglebug.com/3839 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL()); |
| |
| // http://anglebug.com/3837 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL()); |
| |
| // Fails on Mac on Intel and AMD: http://anglebug.com/3842 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL() && (IsIntel() || IsAMD())); |
| |
| // Fails on windows AMD on GL: http://anglebug.com/3838 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsOpenGL() && IsAMD()); |
| |
| // Fails on D3D due to mistranslation: http://anglebug.com/3841 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| |
| layout(std140, column_major) uniform Ubo |
| { |
| mat4 m1; |
| layout(row_major) mat4 m2; |
| } ubo[3]; |
| |
| #define EXPECT(result, expression, value) if ((expression) != value) { result = false; } |
| |
| #define VERIFY_IN(result, mat, cols, rows) \ |
| for (int c = 0; c < cols; ++c) \ |
| { \ |
| for (int r = 0; r < rows; ++r) \ |
| { \ |
| EXPECT(result, mat[c][r], float(c * 4 + r)); \ |
| } \ |
| } |
| |
| void main() |
| { |
| bool result = true; |
| |
| VERIFY_IN(result, ubo[0].m1, 4, 4); |
| VERIFY_IN(result, ubo[0].m2, 4, 4); |
| |
| VERIFY_IN(result, ubo[1].m1, 4, 4); |
| VERIFY_IN(result, ubo[1].m2, 4, 4); |
| |
| VERIFY_IN(result, ubo[2].m1, 4, 4); |
| VERIFY_IN(result, ubo[2].m2, 4, 4); |
| |
| outColor = result ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 2; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, |
| {4, 4}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = { |
| true, |
| false, |
| }; |
| |
| float data[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t size = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, data, false, false); |
| |
| GLBuffer ubos[3]; |
| |
| InitBuffer(program, "Ubo[0]", ubos[0], 0, data, size, true); |
| InitBuffer(program, "Ubo[1]", ubos[1], 0, data, size, true); |
| InitBuffer(program, "Ubo[2]", ubos[2], 0, data, size, true); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that side effects when transforming read operations are preserved. |
| TEST_P(GLSLTest_ES3, MixedRowAndColumnMajorMatrices_ReadSideEffect) |
| { |
| // Fails on Mac on Intel and AMD: http://anglebug.com/3842 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL() && (IsIntel() || IsAMD())); |
| |
| // Fails on D3D due to mistranslation: http://anglebug.com/3841 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| |
| struct S |
| { |
| mat2x3 m2[3]; |
| }; |
| |
| layout(std140, column_major) uniform Ubo |
| { |
| mat4 m1; |
| layout(row_major) S s[2]; |
| } ubo; |
| |
| #define EXPECT(result, expression, value) if ((expression) != value) { result = false; } |
| |
| #define VERIFY_IN(result, mat, cols, rows) \ |
| for (int c = 0; c < cols; ++c) \ |
| { \ |
| for (int r = 0; r < rows; ++r) \ |
| { \ |
| EXPECT(result, mat[c][r], float(c * 4 + r)); \ |
| } \ |
| } |
| |
| bool verify2x3(mat2x3 mat) |
| { |
| bool result = true; |
| |
| for (int c = 0; c < 2; ++c) |
| { |
| for (int r = 0; r < 3; ++r) |
| { |
| EXPECT(result, mat[c][r], float(c * 4 + r)); |
| } |
| } |
| |
| return result; |
| } |
| |
| void main() |
| { |
| bool result = true; |
| |
| int sideEffect = 0; |
| VERIFY_IN(result, ubo.m1, 4, 4); |
| EXPECT(result, verify2x3(ubo.s[0].m2[0]), true); |
| EXPECT(result, verify2x3(ubo.s[0].m2[sideEffect += 1]), true); |
| EXPECT(result, verify2x3(ubo.s[0].m2[sideEffect += 1]), true); |
| |
| EXPECT(result, sideEffect, 2); |
| |
| EXPECT(result, verify2x3(ubo.s[sideEffect = 1].m2[0]), true); |
| EXPECT(result, verify2x3(ubo.s[1].m2[(sideEffect = 4) - 3]), true); |
| EXPECT(result, verify2x3(ubo.s[1].m2[sideEffect - 2]), true); |
| |
| EXPECT(result, sideEffect, 4); |
| |
| outColor = result ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 7; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = { |
| true, false, false, false, false, false, false, |
| }; |
| |
| float data[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t size = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, data, false, false); |
| |
| GLBuffer ubo; |
| InitBuffer(program, "Ubo", ubo, 0, data, size, true); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that side effects respect the order of logical expression operands. |
| TEST_P(GLSLTest_ES3, MixedRowAndColumnMajorMatrices_ReadSideEffectOrder) |
| { |
| // http://anglebug.com/3837 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL()); |
| |
| // IntermTraverser::insertStatementsInParentBlock that's used to move side effects does not |
| // respect the order of evaluation of logical expressions. http://anglebug.com/3829. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL()); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| |
| layout(std140, column_major) uniform Ubo |
| { |
| mat4 m1; |
| layout(row_major) mat4 m2[2]; |
| } ubo; |
| |
| void main() |
| { |
| bool result = true; |
| |
| int x = 0; |
| if (x == 0 && ubo.m2[x = 1][1][1] == 5.0) |
| { |
| result = true; |
| } |
| else |
| { |
| result = false; |
| } |
| |
| outColor = result ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 3; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, |
| {4, 4}, |
| {4, 4}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = {true, false, false}; |
| |
| float data[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t size = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, data, false, false); |
| |
| GLBuffer ubo; |
| InitBuffer(program, "Ubo", ubo, 0, data, size, true); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that side effects respect short-circuit. |
| TEST_P(GLSLTest_ES3, MixedRowAndColumnMajorMatrices_ReadSideEffectShortCircuit) |
| { |
| // Fails on Android: http://anglebug.com/3839 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL()); |
| |
| // IntermTraverser::insertStatementsInParentBlock that's used to move side effects does not |
| // respect the order of evaluation of logical expressions. http://anglebug.com/3829. |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL()); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| |
| layout(std140, column_major) uniform Ubo |
| { |
| mat4 m1; |
| layout(row_major) mat4 m2[2]; |
| } ubo; |
| |
| void main() |
| { |
| bool result = true; |
| |
| int x = 0; |
| if (x == 1 && ubo.m2[x = 1][1][1] == 5.0) |
| { |
| // First x == 1 should prevent the side effect of the second expression (x = 1) from |
| // being executed. If x = 1 is run before the if, the condition of the if would be true, |
| // which is a failure. |
| result = false; |
| } |
| if (x == 1) |
| { |
| result = false; |
| } |
| |
| outColor = result ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 3; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, |
| {4, 4}, |
| {4, 4}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = {true, false, false}; |
| |
| float data[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t size = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, data, false, false); |
| |
| GLBuffer ubo; |
| InitBuffer(program, "Ubo", ubo, 0, data, size, true); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that dynamic indexing of swizzled l-values should work. |
| // A simple porting of sdk/tests/conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html |
| TEST_P(GLSLTest_ES3, DynamicIndexingOfSwizzledLValuesShouldWork) |
| { |
| // The shader first assigns v.x to v.z (1.0) |
| // Then v.y to v.y (2.0) |
| // Then v.z to v.x (1.0) |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 my_FragColor; |
| void main() { |
| vec3 v = vec3(1.0, 2.0, 3.0); |
| for (int i = 0; i < 3; i++) { |
| v.zyx[i] = v[i]; |
| } |
| my_FragColor = distance(v, vec3(1.0, 2.0, 1.0)) < 0.01 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| EXPECT_GL_NO_ERROR(); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that dead code after discard, return, continue and branch are pruned. |
| TEST_P(GLSLTest_ES3, DeadCodeIsPruned) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| vec4 f(vec4 c) |
| { |
| return c; |
| // dead code |
| c = vec4(0, 0, 1, 1); |
| return c; |
| } |
| |
| void main() |
| { |
| vec4 result = vec4(0, 0.5, 0, 1); |
| int var = int(result.y * 2.2); |
| |
| { |
| if (result.x > 1.0) |
| { |
| discard; |
| // dead code |
| result = vec4(1, 0, 0, 1); |
| } |
| for (int i = 0; i < 3; ++i) |
| { |
| if (i < 2) |
| { |
| result = f(result); |
| continue; |
| // dead code |
| result = vec4(1, 0, 1, 1); |
| } |
| result = f(result); |
| break; |
| // dead code |
| result = vec4(1, 0, 1, 0); |
| } |
| while (true) |
| { |
| if (result.x > -1.0) |
| { |
| { |
| result = f(result); |
| { |
| break; |
| // dead code |
| result = vec4(1, 0, 0, 0); |
| } |
| // dead code |
| for (int j = 0; j < 3; ++j) |
| { |
| if (j > 1) continue; |
| result = vec4(0, 0, 1, 0); |
| color = vec4(0.5, 0, 0.5, 0.5); |
| return; |
| } |
| } |
| // dead code |
| result = vec4(0.5, 0, 0, 0); |
| } |
| } |
| switch (var) |
| { |
| case 2: |
| return; |
| // dead code |
| color = vec4(0.25, 0, 0.25, 0.25); |
| case 1: |
| { |
| // Make sure this path is not pruned due to the return in the previous case. |
| result.y += 0.5; |
| break; |
| // dead code |
| color = vec4(0.25, 0, 0, 0); |
| } |
| // dead code |
| color = vec4(0, 0, 0.25, 0); |
| break; |
| default: |
| break; |
| } |
| |
| color = result; |
| return; |
| // dead code |
| color = vec4(0, 0, 0.5, 0); |
| } |
| // dead code |
| color = vec4(0, 0, 0, 0.5); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Regression test based on fuzzer issue. If a case has statements that are pruned, and those |
| // pruned statements in turn have branches, and another case follows, a prior implementation of |
| // dead-code elimination doubly pruned some statements. |
| TEST_P(GLSLTest_ES3, DeadCodeBranchInPrunedStatementsInCaseBeforeAnotherCase) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| void main() |
| { |
| color = vec4(0, 1, 0, 1); |
| switch(0) |
| { |
| case 0: |
| break; |
| break; |
| color = vec4(1, 0, 0, 1); // The bug was pruning this statement twice |
| default: |
| color = vec4(0, 0, 1, 1); |
| break; |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test shader with all resources (default uniform, UBO, SSBO, image, sampler and atomic counter) to |
| // make sure they are all linked ok. The front-end sorts these resources and traverses the list of |
| // "uniforms" to find the range for each resource. A bug there was causing some resource ranges to |
| // be empty in the presence of other resources. |
| TEST_P(GLSLTest_ES31, MixOfAllResources) |
| { |
| // http://anglebug.com/5072 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL()); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| layout(binding = 1, std430) buffer Output { |
| uint ubo_value; |
| uint default_value; |
| uint sampler_value; |
| uint ac_value; |
| uint image_value; |
| } outbuf; |
| uniform Input { |
| uint input_value; |
| } inbuf; |
| uniform uint default_uniform; |
| uniform sampler2D smplr; |
| layout(binding=0) uniform atomic_uint ac; |
| layout(r32ui) uniform highp readonly uimage2D image; |
| |
| void main(void) |
| { |
| outbuf.ubo_value = inbuf.input_value; |
| outbuf.default_value = default_uniform; |
| outbuf.sampler_value = uint(texture(smplr, vec2(0.5, 0.5)).x * 255.0); |
| outbuf.ac_value = atomicCounterIncrement(ac); |
| outbuf.image_value = imageLoad(image, ivec2(0, 0)).x; |
| } |
| )"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| unsigned int inputData = 89u; |
| GLBuffer inputBuffer; |
| glBindBuffer(GL_UNIFORM_BUFFER, inputBuffer); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(inputData), &inputData, GL_STATIC_DRAW); |
| GLuint inputBufferIndex = glGetUniformBlockIndex(program.get(), "Input"); |
| ASSERT_NE(inputBufferIndex, GL_INVALID_INDEX); |
| glUniformBlockBinding(program.get(), inputBufferIndex, 0); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, inputBuffer); |
| |
| unsigned int outputInitData[5] = {0x12345678u, 0x09ABCDEFu, 0x56789ABCu, 0x0DEF1234u, |
| 0x13579BDFu}; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| unsigned int uniformData = 456u; |
| GLint uniformLocation = glGetUniformLocation(program, "default_uniform"); |
| ASSERT_NE(uniformLocation, -1); |
| glUniform1ui(uniformLocation, uniformData); |
| |
| unsigned int acData = 2u; |
| GLBuffer atomicCounterBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(acData), &acData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| unsigned int imageData = 33u; |
| GLTexture image; |
| glBindTexture(GL_TEXTURE_2D, image); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &imageData); |
| glBindImageTexture(0, image, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLColor textureData(127, 18, 189, 211); |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &textureData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(ptr[0], inputData); |
| EXPECT_EQ(ptr[1], uniformData); |
| EXPECT_NEAR(ptr[2], textureData.R, 1.0); |
| EXPECT_EQ(ptr[3], acData); |
| EXPECT_EQ(ptr[4], imageData); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that sending mixture of resources to functions works. |
| TEST_P(GLSLTest_ES31, MixOfResourcesAsFunctionArgs) |
| { |
| // http://anglebug.com/5546 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); |
| |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| |
| layout(binding = 1, std430) buffer Output { |
| uint success; |
| } outbuf; |
| |
| uniform uint initialAcValue; |
| uniform sampler2D smplr[2][3]; |
| layout(binding=0) uniform atomic_uint ac; |
| |
| bool sampler1DAndAtomicCounter(uvec3 sExpect, in sampler2D s[3], in atomic_uint a, uint aExpect) |
| { |
| uvec3 sResult = uvec3(uint(texture(s[0], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[1], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[2], vec2(0.5, 0.5)).x * 255.0)); |
| uint aResult = atomicCounterIncrement(a); |
| |
| return sExpect == sResult && aExpect == aResult; |
| } |
| |
| bool sampler2DAndAtomicCounter(in sampler2D s[2][3], uint aInitial, in atomic_uint a) |
| { |
| bool success = true; |
| success = sampler1DAndAtomicCounter(uvec3(0, 127, 255), s[0], a, aInitial) && success; |
| success = sampler1DAndAtomicCounter(uvec3(31, 63, 191), s[1], a, aInitial + 1u) && success; |
| return success; |
| } |
| |
| void main(void) |
| { |
| outbuf.success = uint(sampler2DAndAtomicCounter(smplr, initialAcValue, ac)); |
| } |
| )"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| unsigned int outputInitData = 0x12345678u; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| unsigned int acData = 2u; |
| GLint uniformLocation = glGetUniformLocation(program, "initialAcValue"); |
| ASSERT_NE(uniformLocation, -1); |
| glUniform1ui(uniformLocation, acData); |
| |
| GLBuffer atomicCounterBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(acData), &acData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| const std::array<GLColor, 6> kTextureData = { |
| GLColor(0, 0, 0, 0), GLColor(127, 0, 0, 0), GLColor(255, 0, 0, 0), |
| GLColor(31, 0, 0, 0), GLColor(63, 0, 0, 0), GLColor(191, 0, 0, 0), |
| }; |
| GLTexture textures[2][3]; |
| |
| for (int dim1 = 0; dim1 < 2; ++dim1) |
| { |
| for (int dim2 = 0; dim2 < 3; ++dim2) |
| { |
| int textureUnit = dim1 * 3 + dim2; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &kTextureData[textureUnit]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| std::stringstream uniformName; |
| uniformName << "smplr[" << dim1 << "][" << dim2 << "]"; |
| GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(samplerLocation, -1); |
| glUniform1i(samplerLocation, textureUnit); |
| } |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(ptr[0], 1u); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that array of array of samplers used as function parameter with an index that has a |
| // side-effect works. |
| TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerAsFunctionParameterIndexedWithSideEffect) |
| { |
| // http://anglebug.com/5546 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); |
| |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| // Skip if EXT_gpu_shader5 is not enabled. |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5")); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| #extension GL_EXT_gpu_shader5 : require |
| |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| |
| layout(binding = 1, std430) buffer Output { |
| uint success; |
| } outbuf; |
| |
| uniform sampler2D smplr[2][3]; |
| layout(binding=0) uniform atomic_uint ac; |
| |
| bool sampler1DAndAtomicCounter(uvec3 sExpect, in sampler2D s[3], in atomic_uint a, uint aExpect) |
| { |
| uvec3 sResult = uvec3(uint(texture(s[0], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[1], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[2], vec2(0.5, 0.5)).x * 255.0)); |
| uint aResult = atomicCounter(a); |
| |
| return sExpect == sResult && aExpect == aResult; |
| } |
| |
| bool sampler2DAndAtomicCounter(in sampler2D s[2][3], uint aInitial, in atomic_uint a) |
| { |
| bool success = true; |
| success = sampler1DAndAtomicCounter(uvec3(0, 127, 255), |
| s[atomicCounterIncrement(ac)], a, aInitial + 1u) && success; |
| success = sampler1DAndAtomicCounter(uvec3(31, 63, 191), |
| s[atomicCounterIncrement(ac)], a, aInitial + 2u) && success; |
| return success; |
| } |
| |
| void main(void) |
| { |
| outbuf.success = uint(sampler2DAndAtomicCounter(smplr, 0u, ac)); |
| } |
| )"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| unsigned int outputInitData = 0x12345678u; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| unsigned int acData = 0u; |
| GLBuffer atomicCounterBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(acData), &acData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| const std::array<GLColor, 6> kTextureData = { |
| GLColor(0, 0, 0, 0), GLColor(127, 0, 0, 0), GLColor(255, 0, 0, 0), |
| GLColor(31, 0, 0, 0), GLColor(63, 0, 0, 0), GLColor(191, 0, 0, 0), |
| }; |
| GLTexture textures[2][3]; |
| |
| for (int dim1 = 0; dim1 < 2; ++dim1) |
| { |
| for (int dim2 = 0; dim2 < 3; ++dim2) |
| { |
| int textureUnit = dim1 * 3 + dim2; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &kTextureData[textureUnit]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| std::stringstream uniformName; |
| uniformName << "smplr[" << dim1 << "][" << dim2 << "]"; |
| GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(samplerLocation, -1); |
| glUniform1i(samplerLocation, textureUnit); |
| } |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(ptr[0], 1u); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that array of array of samplers can be indexed correctly with dynamic indices. |
| TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerDynamicIndex) |
| { |
| // Skip if EXT_gpu_shader5 is not enabled. |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5")); |
| |
| int maxTextureImageUnits = 0; |
| glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); |
| ANGLE_SKIP_TEST_IF(maxTextureImageUnits < 24); |
| |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| // http://anglebug.com/5546 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| #extension GL_EXT_gpu_shader5 : require |
| |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| |
| layout(binding = 1, std430) buffer Output { |
| uint success; |
| } outbuf; |
| |
| uniform sampler2D smplr[2][3][4]; |
| layout(binding=0) uniform atomic_uint ac; |
| |
| bool sampler1DAndAtomicCounter(uvec4 sExpect, in sampler2D s[4], in atomic_uint a, uint aExpect) |
| { |
| uvec4 sResult = uvec4(uint(texture(s[0], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[1], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[2], vec2(0.5, 0.5)).x * 255.0), |
| uint(texture(s[3], vec2(0.5, 0.5)).x * 255.0)); |
| uint aResult = atomicCounter(a); |
| |
| return sExpect == sResult && aExpect == aResult; |
| } |
| |
| bool sampler3DAndAtomicCounter(in sampler2D s[2][3][4], uint aInitial, in atomic_uint a) |
| { |
| bool success = true; |
| // [0][0] |
| success = sampler1DAndAtomicCounter(uvec4(0, 8, 16, 24), |
| s[atomicCounterIncrement(ac)][0], a, aInitial + 1u) && success; |
| // [1][0] |
| success = sampler1DAndAtomicCounter(uvec4(96, 104, 112, 120), |
| s[atomicCounterIncrement(ac)][0], a, aInitial + 2u) && success; |
| // [0][1] |
| success = sampler1DAndAtomicCounter(uvec4(32, 40, 48, 56), |
| s[0][atomicCounterIncrement(ac) - 1u], a, aInitial + 3u) && success; |
| // [0][2] |
| success = sampler1DAndAtomicCounter(uvec4(64, 72, 80, 88), |
| s[0][atomicCounterIncrement(ac) - 1u], a, aInitial + 4u) && success; |
| // [1][1] |
| success = sampler1DAndAtomicCounter(uvec4(128, 136, 144, 152), |
| s[1][atomicCounterIncrement(ac) - 3u], a, aInitial + 5u) && success; |
| // [1][2] |
| uint acValue = atomicCounterIncrement(ac); // Returns 5 |
| success = sampler1DAndAtomicCounter(uvec4(160, 168, 176, 184), |
| s[acValue - 4u][atomicCounterIncrement(ac) - 4u], a, aInitial + 7u) && success; |
| |
| return success; |
| } |
| |
| void main(void) |
| { |
| outbuf.success = uint(sampler3DAndAtomicCounter(smplr, 0u, ac)); |
| } |
| )"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| unsigned int outputInitData = 0x12345678u; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| unsigned int acData = 0u; |
| GLBuffer atomicCounterBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(acData), &acData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| const std::array<GLColor, 24> kTextureData = { |
| GLColor(0, 0, 0, 0), GLColor(8, 0, 0, 0), GLColor(16, 0, 0, 0), GLColor(24, 0, 0, 0), |
| GLColor(32, 0, 0, 0), GLColor(40, 0, 0, 0), GLColor(48, 0, 0, 0), GLColor(56, 0, 0, 0), |
| GLColor(64, 0, 0, 0), GLColor(72, 0, 0, 0), GLColor(80, 0, 0, 0), GLColor(88, 0, 0, 0), |
| GLColor(96, 0, 0, 0), GLColor(104, 0, 0, 0), GLColor(112, 0, 0, 0), GLColor(120, 0, 0, 0), |
| GLColor(128, 0, 0, 0), GLColor(136, 0, 0, 0), GLColor(144, 0, 0, 0), GLColor(152, 0, 0, 0), |
| GLColor(160, 0, 0, 0), GLColor(168, 0, 0, 0), GLColor(176, 0, 0, 0), GLColor(184, 0, 0, 0), |
| }; |
| GLTexture textures[2][3][4]; |
| |
| for (int dim1 = 0; dim1 < 2; ++dim1) |
| { |
| for (int dim2 = 0; dim2 < 3; ++dim2) |
| { |
| for (int dim3 = 0; dim3 < 4; ++dim3) |
| { |
| int textureUnit = (dim1 * 3 + dim2) * 4 + dim3; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2][dim3]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &kTextureData[textureUnit]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| std::stringstream uniformName; |
| uniformName << "smplr[" << dim1 << "][" << dim2 << "][" << dim3 << "]"; |
| GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(samplerLocation, -1); |
| glUniform1i(samplerLocation, textureUnit); |
| } |
| } |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(ptr[0], 1u); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that array of array of samplers can be indexed correctly with dynamic indices. Uses |
| // samplers in structs. |
| TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerInStructDynamicIndex) |
| { |
| // Skip if EXT_gpu_shader5 is not enabled. |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5")); |
| |
| int maxTextureImageUnits = 0; |
| glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); |
| ANGLE_SKIP_TEST_IF(maxTextureImageUnits < 24); |
| |
| // http://anglebug.com/5072 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL()); |
| |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| // http://anglebug.com/5546 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| #extension GL_EXT_gpu_shader5 : require |
| |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| |
| layout(binding = 1, std430) buffer Output { |
| uint success; |
| } outbuf; |
| |
| struct I |
| { |
| uint index; |
| }; |
| |
| struct S |
| { |
| sampler2D smplr[4]; |
| I nested; |
| }; |
| |
| struct T |
| { |
| S nested[3]; |
| uint tIndex; |
| }; |
| |
| uniform T u[2]; |
| |
| uint getValue(in sampler2D s) |
| { |
| return uint(texture(s, vec2(0.5, 0.5)).x * 255.0); |
| } |
| |
| bool sampler1DTest(uvec4 sExpect, in sampler2D s[4]) |
| { |
| uvec4 sResult = uvec4(getValue(s[0]), getValue(s[1]), |
| getValue(s[2]), getValue(s[3])); |
| |
| return sExpect == sResult; |
| } |
| |
| bool samplerTest(T t, uint N) |
| { |
| // u[N].tIndex == 0 + N*4 |
| // u[N].nested[0].nested.index == 1 + N*4 |
| // u[N].nested[1].nested.index == 2 + N*4 |
| // u[N].nested[2].nested.index == 3 + N*4 |
| |
| uvec4 colorOffset = N * 3u * 4u * uvec4(8); |
| |
| bool success = true; |
| // [N][0] |
| success = sampler1DTest(uvec4(0, 8, 16, 24) + colorOffset, |
| t.nested[t.nested[0].nested.index - t.tIndex - 1u].smplr) && success; |
| // [N][1] |
| success = sampler1DTest(uvec4(32, 40, 48, 56) + colorOffset, |
| t.nested[t.nested[1].nested.index - t.tIndex - 1u].smplr) && success; |
| // [N][2] |
| success = sampler1DTest(uvec4(64, 72, 80, 88) + colorOffset, |
| t.nested[t.nested[2].nested.index - t.tIndex - 1u].smplr) && success; |
| |
| return success; |
| } |
| |
| bool uniformTest(T t, uint N) |
| { |
| // Also verify that expressions that involve structs-with-samplers are correct when not |
| // referecing the sampler. |
| |
| bool success = true; |
| success = (t.nested[0].nested.index - t.tIndex == 1u) && success; |
| success = (t.nested[1].nested.index - t.tIndex == 2u) && success; |
| success = (t.nested[2].nested.index - t.tIndex == 3u) && success; |
| |
| success = (t.nested[t.nested[0].nested.index - t.tIndex - 1u].nested.index - t.tIndex == 1u) |
| && success; |
| success = (t.nested[t.nested[0].nested.index - t.tIndex ].nested.index - t.tIndex == 2u) |
| && success; |
| success = (t.nested[t.nested[0].nested.index - t.tIndex + 1u].nested.index - t.tIndex == 3u) |
| && success; |
| |
| success = (t.nested[ |
| t.nested[ |
| t.nested[2].nested.index - t.tIndex - 1u // 2 |
| ].nested.index - t.tIndex - 2u // 1 |
| ].nested.index - t.tIndex // 2 |
| == 2u) && success; |
| |
| return success; |
| } |
| |
| void main(void) |
| { |
| bool success = samplerTest(u[0], 0u) && samplerTest(u[1], 1u) |
| && uniformTest(u[0], 0u) && uniformTest(u[1], 1u); |
| outbuf.success = uint(success); |
| } |
| )"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| unsigned int outputInitData = 0x12345678u; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| const std::array<GLColor, 24> kTextureData = { |
| GLColor(0, 0, 0, 0), GLColor(8, 0, 0, 0), GLColor(16, 0, 0, 0), GLColor(24, 0, 0, 0), |
| GLColor(32, 0, 0, 0), GLColor(40, 0, 0, 0), GLColor(48, 0, 0, 0), GLColor(56, 0, 0, 0), |
| GLColor(64, 0, 0, 0), GLColor(72, 0, 0, 0), GLColor(80, 0, 0, 0), GLColor(88, 0, 0, 0), |
| GLColor(96, 0, 0, 0), GLColor(104, 0, 0, 0), GLColor(112, 0, 0, 0), GLColor(120, 0, 0, 0), |
| GLColor(128, 0, 0, 0), GLColor(136, 0, 0, 0), GLColor(144, 0, 0, 0), GLColor(152, 0, 0, 0), |
| GLColor(160, 0, 0, 0), GLColor(168, 0, 0, 0), GLColor(176, 0, 0, 0), GLColor(184, 0, 0, 0), |
| }; |
| GLTexture textures[2][3][4]; |
| |
| for (int dim1 = 0; dim1 < 2; ++dim1) |
| { |
| for (int dim2 = 0; dim2 < 3; ++dim2) |
| { |
| for (int dim3 = 0; dim3 < 4; ++dim3) |
| { |
| int textureUnit = (dim1 * 3 + dim2) * 4 + dim3; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2][dim3]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &kTextureData[textureUnit]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| std::stringstream uniformName; |
| uniformName << "u[" << dim1 << "].nested[" << dim2 << "].smplr[" << dim3 << "]"; |
| GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(samplerLocation, -1); |
| glUniform1i(samplerLocation, textureUnit); |
| } |
| |
| std::stringstream uniformName; |
| uniformName << "u[" << dim1 << "].nested[" << dim2 << "].nested.index"; |
| GLint nestedIndexLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(nestedIndexLocation, -1); |
| glUniform1ui(nestedIndexLocation, dim1 * 4 + dim2 + 1); |
| } |
| |
| std::stringstream uniformName; |
| uniformName << "u[" << dim1 << "].tIndex"; |
| GLint indexLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(indexLocation, -1); |
| glUniform1ui(indexLocation, dim1 * 4); |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(ptr[0], 1u); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that array of array of samplers work when indexed with an expression that's derived from an |
| // array of array of samplers. |
| TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerIndexedWithArrayOfArrayOfSamplers) |
| { |
| // Skip if EXT_gpu_shader5 is not enabled. |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5")); |
| |
| // anglebug.com/3832 - no sampler array params on Android |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| constexpr char kComputeShader[] = R"(#version 310 es |
| #extension GL_EXT_gpu_shader5 : require |
| |
| layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; |
| |
| layout(binding = 1, std430) buffer Output { |
| uint success; |
| } outbuf; |
| |
| uniform sampler2D smplr[2][3]; |
| |
| uint getValue(in sampler2D s) |
| { |
| return uint(texture(s, vec2(0.5, 0.5)).x * 255.0); |
| } |
| |
| bool runTest(in sampler2D s[2][3]) |
| { |
| // s[0][0] should contain 2 |
| // s[0][1] should contain 0 |
| // s[0][2] should contain 1 |
| // s[1][0] should contain 1 |
| // s[1][1] should contain 2 |
| // s[1][2] should contain 0 |
| |
| uint result = getValue( |
| s[ |
| getValue( |
| s[ |
| getValue(s[0][1]) // 0 |
| ][ |
| getValue(s[0][0]) // 2 |
| ] |
| ) // s[0][2] -> 1 |
| ][ |
| getValue( |
| s[ |
| getValue(s[1][0]) // 1 |
| ][ |
| getValue(s[1][1]) // 2 |
| ] |
| ) // s[1][2] -> 0 |
| ] |
| ); // s[1][0] -> 1 |
| |
| return result == 1u; |
| } |
| |
| void main(void) |
| { |
| outbuf.success = uint(runTest(smplr)); |
| } |
| )"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| |
| unsigned int outputInitData = 0x12345678u; |
| GLBuffer outputBuffer; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| const std::array<GLColor, 6> kTextureData = { |
| GLColor(2, 0, 0, 0), GLColor(0, 0, 0, 0), GLColor(1, 0, 0, 0), |
| GLColor(1, 0, 0, 0), GLColor(2, 0, 0, 0), GLColor(0, 0, 0, 0), |
| }; |
| GLTexture textures[2][3]; |
| |
| for (int dim1 = 0; dim1 < 2; ++dim1) |
| { |
| for (int dim2 = 0; dim2 < 3; ++dim2) |
| { |
| int textureUnit = dim1 * 3 + dim2; |
| glActiveTexture(GL_TEXTURE0 + textureUnit); |
| glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &kTextureData[textureUnit]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| std::stringstream uniformName; |
| uniformName << "smplr[" << dim1 << "][" << dim2 << "]"; |
| GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str()); |
| EXPECT_NE(samplerLocation, -1); |
| glUniform1i(samplerLocation, textureUnit); |
| } |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| // read back |
| const GLuint *ptr = reinterpret_cast<const GLuint *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT)); |
| EXPECT_EQ(ptr[0], 1u); |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Test that multiple nested assignments are handled correctly. |
| TEST_P(GLSLTest_ES31, MixedRowAndColumnMajorMatrices_WriteSideEffect) |
| { |
| // http://anglebug.com/3831 |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL()); |
| |
| // Fails on windows AMD on GL: http://anglebug.com/3838 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsOpenGL() && IsAMD()); |
| // http://anglebug.com/5384 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); |
| |
| // Fails on D3D due to mistranslation: http://anglebug.com/3841 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| precision highp float; |
| layout(local_size_x=1) in; |
| |
| layout(std140, column_major) uniform Ubo |
| { |
| mat4 m1; |
| layout(row_major) mat4 m2; |
| } ubo; |
| |
| layout(std140, row_major, binding = 0) buffer Ssbo |
| { |
| layout(column_major) mat4 m1; |
| mat4 m2; |
| } ssbo; |
| |
| layout(std140, binding = 1) buffer Result |
| { |
| uint success; |
| } resultOut; |
| |
| void main() |
| { |
| bool result = true; |
| |
| // Only assign to SSBO from a single invocation. |
| if (gl_GlobalInvocationID.x == 0u) |
| { |
| if ((ssbo.m2 = ssbo.m1 = ubo.m1) != ubo.m2) |
| { |
| result = false; |
| } |
| |
| resultOut.success = uint(result); |
| } |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 2; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, |
| {4, 4}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = { |
| true, |
| false, |
| }; |
| |
| float data[kMatrixCount * 4 * 4] = {}; |
| float zeros[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t size = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, data, false, false); |
| |
| GLBuffer ubo, ssbo; |
| |
| InitBuffer(program, "Ubo", ubo, 0, data, size, true); |
| InitBuffer(program, "Ssbo", ssbo, 0, zeros, size, false); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLBuffer outputBuffer; |
| CreateOutputBuffer(&outputBuffer, 1); |
| |
| glUseProgram(program); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_TRUE(VerifySuccess(outputBuffer)); |
| |
| EXPECT_TRUE(VerifyBuffer(ssbo, data, size)); |
| } |
| |
| // Test that assignments to array of array of matrices are handled correctly. |
| TEST_P(GLSLTest_ES31, MixedRowAndColumnMajorMatrices_WriteArrayOfArray) |
| { |
| // Fails on windows AMD on GL: http://anglebug.com/3838 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsOpenGL() && IsAMD()); |
| // http://anglebug.com/5384 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); |
| |
| // Fails on D3D due to mistranslation: http://anglebug.com/3841 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| // Fails compiling shader on Android/Vulkan. http://anglebug.com/4290 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan()); |
| |
| // Fails on ARM on Vulkan. http://anglebug.com/4492 |
| ANGLE_SKIP_TEST_IF(IsARM() && IsVulkan()); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| precision highp float; |
| layout(local_size_x=1) in; |
| |
| layout(std140, column_major) uniform Ubo |
| { |
| mat4 m1; |
| layout(row_major) mat4 m2[2][3]; |
| } ubo; |
| |
| layout(std140, row_major, binding = 0) buffer Ssbo |
| { |
| layout(column_major) mat4 m1; |
| mat4 m2[2][3]; |
| } ssbo; |
| |
| layout(std140, binding = 1) buffer Result |
| { |
| uint success; |
| } resultOut; |
| |
| void main() |
| { |
| bool result = true; |
| |
| // Only assign to SSBO from a single invocation. |
| if (gl_GlobalInvocationID.x == 0u) |
| { |
| ssbo.m1 = ubo.m1; |
| ssbo.m2 = ubo.m2; |
| |
| resultOut.success = uint(result); |
| } |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr size_t kMatrixCount = 7; |
| constexpr std::pair<uint32_t, uint32_t> kMatrixDims[kMatrixCount] = { |
| {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, |
| }; |
| constexpr bool kMatrixIsColMajor[kMatrixCount] = { |
| true, false, false, false, false, false, false, |
| }; |
| |
| float data[kMatrixCount * 4 * 4] = {}; |
| float zeros[kMatrixCount * 4 * 4] = {}; |
| |
| const uint32_t size = |
| FillBuffer(kMatrixDims, kMatrixIsColMajor, kMatrixCount, data, false, false); |
| |
| GLBuffer ubo, ssbo; |
| |
| InitBuffer(program, "Ubo", ubo, 0, data, size, true); |
| InitBuffer(program, "Ssbo", ssbo, 0, zeros, size, false); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLBuffer outputBuffer; |
| CreateOutputBuffer(&outputBuffer, 1); |
| |
| glUseProgram(program); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_TRUE(VerifySuccess(outputBuffer)); |
| |
| EXPECT_TRUE(VerifyBuffer(ssbo, data, size)); |
| } |
| |
| // Verify that types used differently (in different block storages, differently qualified etc) work |
| // when copied around. |
| TEST_P(GLSLTest_ES31, TypesUsedInDifferentBlockStorages) |
| { |
| constexpr char kCS[] = R"(#version 310 es |
| precision highp float; |
| layout(local_size_x=1) in; |
| |
| struct Inner |
| { |
| mat3x2 m; |
| float f[3]; |
| uvec2 u[2][4]; |
| ivec3 i; |
| mat2x3 m2[3][2]; |
| }; |
| |
| struct Outer |
| { |
| Inner i[2]; |
| }; |
| |
| layout(std140, column_major) uniform Ubo140c |
| { |
| mat2 m; |
| layout(row_major) Outer o; |
| } ubo140cIn; |
| |
| layout(std430, row_major, binding = 0) buffer Ubo430r |
| { |
| mat2 m; |
| layout(column_major) Outer o; |
| } ubo430rIn; |
| |
| layout(std140, column_major, binding = 1) buffer Ssbo140c |
| { |
| layout(row_major) mat2 m[2]; |
| Outer o; |
| layout(row_major) Inner i; |
| } ssbo140cOut; |
| |
| layout(std430, row_major, binding = 2) buffer Ssbo430r |
| { |
| layout(column_major) mat2 m[2]; |
| Outer o; |
| layout(column_major) Inner i; |
| } ssbo430rOut; |
| |
| void writeArgToStd140(uvec2 u[2][4], int innerIndex) |
| { |
| ssbo140cOut.o.i[innerIndex].u = u; |
| } |
| |
| void writeBlockArgToStd140(Inner i, int innerIndex) |
| { |
| ssbo140cOut.o.i[innerIndex] = i; |
| } |
| |
| mat2x3[3][2] readFromStd140(int innerIndex) |
| { |
| return ubo140cIn.o.i[0].m2; |
| } |
| |
| Inner readBlockFromStd430(int innerIndex) |
| { |
| return ubo430rIn.o.i[innerIndex]; |
| } |
| |
| void copyFromStd140(out Inner i) |
| { |
| i = ubo140cIn.o.i[1]; |
| } |
| |
| void main(){ |
| // Directly copy from one layout to another. |
| ssbo140cOut.m[0] = ubo140cIn.m; |
| ssbo140cOut.m[1] = ubo430rIn.m; |
| ssbo140cOut.o.i[0].m = ubo140cIn.o.i[0].m; |
| ssbo140cOut.o.i[0].f = ubo140cIn.o.i[0].f; |
| ssbo140cOut.o.i[0].i = ubo140cIn.o.i[0].i; |
| |
| // Read from block and pass to function. |
| writeArgToStd140(ubo140cIn.o.i[0].u, 0); |
| writeBlockArgToStd140(ubo430rIn.o.i[0], 1); |
| |
| // Have function return value read from block. |
| ssbo140cOut.o.i[0].m2 = readFromStd140(0); |
| |
| // Have function fill in value as out parameter. |
| copyFromStd140(ssbo140cOut.i); |
| |
| // Initialize local variable. |
| mat2 mStd140 = ubo140cIn.m; |
| |
| // Copy to variable, through multiple assignments. |
| mat2 mStd430, temp; |
| mStd430 = temp = ubo430rIn.m; |
| |
| // Copy from local variable |
| ssbo430rOut.m[0] = mStd140; |
| ssbo430rOut.m[1] = mStd430; |
| |
| // Construct from struct. |
| Inner iStd140 = ubo140cIn.o.i[1]; |
| Outer oStd140 = Outer(Inner[2](iStd140, ubo430rIn.o.i[1])); |
| |
| // Copy struct from local variable. |
| ssbo430rOut.o = oStd140; |
| |
| // Construct from arrays |
| Inner iStd430 = Inner(ubo430rIn.o.i[1].m, |
| ubo430rIn.o.i[1].f, |
| ubo430rIn.o.i[1].u, |
| ubo430rIn.o.i[1].i, |
| ubo430rIn.o.i[1].m2); |
| ssbo430rOut.i = iStd430; |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Test data, laid out with padding (0) based on std140/std430 rules. |
| // clang-format off |
| const std::vector<float> ubo140cData = { |
| // m (mat2, column-major) |
| 1, 2, 0, 0, 3, 4, 0, 0, |
| |
| // o.i[0].m (mat3x2, row-major) |
| 5, 7, 9, 0, 6, 8, 10, 0, |
| // o.i[0].f (float[3]) |
| 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, |
| // o.i[0].u (uvec2[2][4]) |
| 15, 16, 0, 0, 17, 18, 0, 0, 19, 20, 0, 0, 21, 22, 0, 0, |
| 23, 24, 0, 0, 25, 26, 0, 0, 27, 28, 0, 0, 29, 30, 0, 0, |
| // o.i[0].i (ivec3) |
| 31, 32, 33, 0, |
| // o.i[0].m2 (mat2x3[3][2], row-major) |
| 34, 37, 0, 0, 35, 38, 0, 0, 36, 39, 0, 0, |
| 40, 43, 0, 0, 41, 44, 0, 0, 42, 45, 0, 0, |
| 46, 49, 0, 0, 47, 50, 0, 0, 48, 51, 0, 0, |
| 52, 55, 0, 0, 53, 56, 0, 0, 54, 57, 0, 0, |
| 58, 61, 0, 0, 59, 62, 0, 0, 60, 63, 0, 0, |
| 64, 67, 0, 0, 65, 68, 0, 0, 66, 69, 0, 0, |
| |
| // o.i[1].m (mat3x2, row-major) |
| 70, 72, 74, 0, 71, 73, 75, 0, |
| // o.i[1].f (float[3]) |
| 77, 0, 0, 0, 78, 0, 0, 0, 79, 0, 0, 0, |
| // o.i[1].u (uvec2[2][4]) |
| 80, 81, 0, 0, 82, 83, 0, 0, 84, 85, 0, 0, 86, 87, 0, 0, |
| 88, 89, 0, 0, 90, 91, 0, 0, 92, 93, 0, 0, 94, 95, 0, 0, |
| // o.i[1].i (ivec3) |
| 96, 97, 98, 0, |
| // o.i[1].m2 (mat2x3[3][2], row-major) |
| 99, 102, 0, 0, 100, 103, 0, 0, 101, 104, 0, 0, |
| 105, 108, 0, 0, 106, 109, 0, 0, 107, 110, 0, 0, |
| 111, 114, 0, 0, 112, 115, 0, 0, 113, 116, 0, 0, |
| 117, 120, 0, 0, 118, 121, 0, 0, 119, 122, 0, 0, |
| 123, 126, 0, 0, 124, 127, 0, 0, 125, 128, 0, 0, |
| 129, 132, 0, 0, 130, 133, 0, 0, 131, 134, 0, 0, |
| }; |
| const std::vector<float> ubo430rData = { |
| // m (mat2, row-major) |
| 135, 137, 136, 138, |
| |
| // o.i[0].m (mat3x2, column-major) |
| 139, 140, 141, 142, 143, 144, |
| // o.i[0].f (float[3]) |
| 146, 147, 148, 0, |
| // o.i[0].u (uvec2[2][4]) |
| 149, 150, 151, 152, 153, 154, 155, 156, |
| 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, |
| // o.i[0].i (ivec3) |
| 165, 166, 167, 0, |
| // o.i[0].m2 (mat2x3[3][2], column-major) |
| 168, 169, 170, 0, 171, 172, 173, 0, |
| 174, 175, 176, 0, 177, 178, 179, 0, |
| 180, 181, 182, 0, 183, 184, 185, 0, |
| 186, 187, 188, 0, 189, 190, 191, 0, |
| 192, 193, 194, 0, 195, 196, 197, 0, |
| 198, 199, 200, 0, 201, 202, 203, 0, |
| |
| // o.i[1].m (mat3x2, column-major) |
| 204, 205, 206, 207, 208, 209, |
| // o.i[1].f (float[3]) |
| 211, 212, 213, 0, |
| // o.i[1].u (uvec2[2][4]) |
| 214, 215, 216, 217, 218, 219, 220, 221, |
| 222, 223, 224, 225, 226, 227, 228, 229, 0, 0, |
| // o.i[1].i (ivec3) |
| 230, 231, 232, 0, |
| // o.i[1].m2 (mat2x3[3][2], column-major) |
| 233, 234, 235, 0, 236, 237, 238, 0, |
| 239, 240, 241, 0, 242, 243, 244, 0, |
| 245, 246, 247, 0, 248, 249, 250, 0, |
| 251, 252, 253, 0, 254, 255, 256, 0, |
| 257, 258, 259, 0, 260, 261, 262, 0, |
| 263, 264, 265, 0, 266, 267, 268, 0, |
| }; |
| const std::vector<float> ssbo140cExpect = { |
| // m (mat2[2], row-major), m[0] copied from ubo140cIn.m, m[1] from ubo430rIn.m |
| 1, 3, 0, 0, 2, 4, 0, 0, |
| 135, 137, 0, 0, 136, 138, 0, 0, |
| |
| // o.i[0].m (mat3x2, column-major), copied from ubo140cIn.o.i[0].m |
| 5, 6, 0, 0, 7, 8, 0, 0, 9, 10, 0, 0, |
| // o.i[0].f (float[3]), copied from ubo140cIn.o.i[0].f |
| 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, |
| // o.i[0].u (uvec2[2][4]), copied from ubo140cIn.o.i[0].u |
| 15, 16, 0, 0, 17, 18, 0, 0, 19, 20, 0, 0, 21, 22, 0, 0, |
| 23, 24, 0, 0, 25, 26, 0, 0, 27, 28, 0, 0, 29, 30, 0, 0, |
| // o.i[0].i (ivec3), copied from ubo140cIn.o.i[0].i |
| 31, 32, 33, 0, |
| // o.i[0].m2 (mat2x3[3][2], column-major), copied from ubo140cIn.o.i[0].m2 |
| 34, 35, 36, 0, 37, 38, 39, 0, |
| 40, 41, 42, 0, 43, 44, 45, 0, |
| 46, 47, 48, 0, 49, 50, 51, 0, |
| 52, 53, 54, 0, 55, 56, 57, 0, |
| 58, 59, 60, 0, 61, 62, 63, 0, |
| 64, 65, 66, 0, 67, 68, 69, 0, |
| |
| // o.i[1].m (mat3x2, column-major), copied from ubo430rIn.o.i[0].m |
| 139, 140, 0, 0, 141, 142, 0, 0, 143, 144, 0, 0, |
| // o.i[1].f (float[3]), copied from ubo430rIn.o.i[0].f |
| 146, 0, 0, 0, 147, 0, 0, 0, 148, 0, 0, 0, |
| // o.i[1].u (uvec2[2][4]), copied from ubo430rIn.o.i[0].u |
| 149, 150, 0, 0, 151, 152, 0, 0, 153, 154, 0, 0, 155, 156, 0, 0, |
| 157, 158, 0, 0, 159, 160, 0, 0, 161, 162, 0, 0, 163, 164, 0, 0, |
| // o.i[1].i (ivec3), copied from ubo430rIn.o.i[0].i |
| 165, 166, 167, 0, |
| // o.i[1].m2 (mat2x3[3][2], column-major), copied from ubo430rIn.o.i[0].m2 |
| 168, 169, 170, 0, 171, 172, 173, 0, |
| 174, 175, 176, 0, 177, 178, 179, 0, |
| 180, 181, 182, 0, 183, 184, 185, 0, |
| 186, 187, 188, 0, 189, 190, 191, 0, |
| 192, 193, 194, 0, 195, 196, 197, 0, |
| 198, 199, 200, 0, 201, 202, 203, 0, |
| |
| // i.m (mat3x2, row-major), copied from ubo140cIn.o.i[1].m |
| 70, 72, 74, 0, 71, 73, 75, 0, |
| // i.f (float[3]), copied from ubo140cIn.o.i[1].f |
| 77, 0, 0, 0, 78, 0, 0, 0, 79, 0, 0, 0, |
| // i.u (uvec2[2][4]), copied from ubo430rIn.o.i[1].u |
| 80, 81, 0, 0, 82, 83, 0, 0, 84, 85, 0, 0, 86, 87, 0, 0, |
| 88, 89, 0, 0, 90, 91, 0, 0, 92, 93, 0, 0, 94, 95, 0, 0, |
| // i.i (ivec3), copied from ubo140cIn.o.i[1].i |
| 96, 97, 98, 0, |
| // i.m2 (mat2x3[3][2], row-major), copied from ubo140cIn.o.i[1].m2 |
| 99, 102, 0, 0, 100, 103, 0, 0, 101, 104, 0, 0, |
| 105, 108, 0, 0, 106, 109, 0, 0, 107, 110, 0, 0, |
| 111, 114, 0, 0, 112, 115, 0, 0, 113, 116, 0, 0, |
| 117, 120, 0, 0, 118, 121, 0, 0, 119, 122, 0, 0, |
| 123, 126, 0, 0, 124, 127, 0, 0, 125, 128, 0, 0, |
| 129, 132, 0, 0, 130, 133, 0, 0, 131, 134, 0, 0, |
| }; |
| const std::vector<float> ssbo430rExpect = { |
| // m (mat2[2], column-major), m[0] copied from ubo140cIn.m, m[1] from ubo430rIn.m |
| 1, 2, 3, 4, |
| 135, 136, 137, 138, |
| |
| // o.i[0].m (mat3x2, row-major), copied from ubo140cIn.o.i[1].m |
| 70, 72, 74, 0, 71, 73, 75, 0, |
| // o.i[0].f (float[3]), copied from ubo140cIn.o.i[1].f |
| 77, 78, 79, 0, |
| // o.i[0].u (uvec2[2][4]), copied from ubo140cIn.o.i[1].u |
| 80, 81, 82, 83, 84, 85, 86, 87, |
| 88, 89, 90, 91, 92, 93, 94, 95, |
| // o.i[0].i (ivec3), copied from ubo140cIn.o.i[1].i |
| 96, 97, 98, 0, |
| // o.i[0].m2 (mat2x3[3][2], row-major), copied from ubo140cIn.o.i[1].m2 |
| 99, 102, 100, 103, 101, 104, |
| 105, 108, 106, 109, 107, 110, |
| 111, 114, 112, 115, 113, 116, |
| 117, 120, 118, 121, 119, 122, |
| 123, 126, 124, 127, 125, 128, |
| 129, 132, 130, 133, 131, 134, |
| |
| // o.i[1].m (mat3x2, row-major), copied from ubo430rIn.o.i[1].m |
| 204, 206, 208, 0, 205, 207, 209, 0, |
| // o.i[1].f (float[3]), copied from ubo430rIn.o.i[1].f |
| 211, 212, 213, 0, |
| // o.i[1].u (uvec2[2][4]), copied from ubo430rIn.o.i[1].u |
| 214, 215, 216, 217, 218, 219, 220, 221, |
| 222, 223, 224, 225, 226, 227, 228, 229, |
| // o.i[1].i (ivec3), copied from ubo430rIn.o.i[1].i |
| 230, 231, 232, 0, |
| // o.i[1].m2 (mat2x3[3][2], row-major), copied from ubo430rIn.o.i[1].m2 |
| 233, 236, 234, 237, 235, 238, |
| 239, 242, 240, 243, 241, 244, |
| 245, 248, 246, 249, 247, 250, |
| 251, 254, 252, 255, 253, 256, |
| 257, 260, 258, 261, 259, 262, |
| 263, 266, 264, 267, 265, 268, |
| |
| // i.m (mat3x2, column-major), copied from ubo430rIn.o.i[1].m |
| 204, 205, 206, 207, 208, 209, |
| // i.f (float[3]), copied from ubo430rIn.o.i[1].f |
| 211, 212, 213, 0, |
| // i.u (uvec2[2][4]), copied from ubo430rIn.o.i[1].u |
| 214, 215, 216, 217, 218, 219, 220, 221, |
| 222, 223, 224, 225, 226, 227, 228, 229, 0, 0, |
| // i.i (ivec3), copied from ubo430rIn.o.i[1].i |
| 230, 231, 232, 0, |
| // i.m2 (mat2x3[3][2], column-major), copied from ubo430rIn.o.i[1].m2 |
| 233, 234, 235, 0, 236, 237, 238, 0, |
| 239, 240, 241, 0, 242, 243, 244, 0, |
| 245, 246, 247, 0, 248, 249, 250, 0, |
| 251, 252, 253, 0, 254, 255, 256, 0, |
| 257, 258, 259, 0, 260, 261, 262, 0, |
| 263, 264, 265, 0, 266, 267, 268, 0, |
| }; |
| const std::vector<float> zeros(std::max(ssbo140cExpect.size(), ssbo430rExpect.size()), 0); |
| // clang-format on |
| |
| GLBuffer uboStd140ColMajor, uboStd430RowMajor; |
| GLBuffer ssboStd140ColMajor, ssboStd430RowMajor; |
| |
| InitBuffer(program, "Ubo140c", uboStd140ColMajor, 0, ubo140cData.data(), |
| static_cast<uint32_t>(ubo140cData.size()), true); |
| InitBuffer(program, "Ubo430r", uboStd430RowMajor, 0, ubo430rData.data(), |
| static_cast<uint32_t>(ubo430rData.size()), false); |
| InitBuffer(program, "Ssbo140c", ssboStd140ColMajor, 1, zeros.data(), |
| static_cast<uint32_t>(ssbo140cExpect.size()), false); |
| InitBuffer(program, "Ssbo430r", ssboStd430RowMajor, 2, zeros.data(), |
| static_cast<uint32_t>(ssbo430rExpect.size()), false); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_TRUE(VerifyBuffer(ssboStd140ColMajor, ssbo140cExpect.data(), |
| static_cast<uint32_t>(ssbo140cExpect.size()))); |
| EXPECT_TRUE(VerifyBuffer(ssboStd430RowMajor, ssbo430rExpect.data(), |
| static_cast<uint32_t>(ssbo430rExpect.size()))); |
| } |
| |
| // Verify that bool in interface blocks work. |
| TEST_P(GLSLTest_ES31, BoolInInterfaceBlocks) |
| { |
| constexpr char kCS[] = R"(#version 310 es |
| precision highp float; |
| layout(local_size_x=1) in; |
| |
| struct Inner |
| { |
| bool b; |
| bvec2 b2; |
| bvec3 b3; |
| bvec4 b4; |
| bool ba[5]; |
| bvec2 b2a[2][3]; |
| }; |
| |
| struct Outer |
| { |
| Inner i[2]; |
| }; |
| |
| layout(std140) uniform Ubo140 |
| { |
| Outer o; |
| }; |
| |
| layout(std430, binding = 0) buffer Ubo430 |
| { |
| Outer o; |
| } ubo430In; |
| |
| layout(std140, binding = 1) buffer Ssbo140 |
| { |
| bool valid; |
| Inner i; |
| } ssbo140Out; |
| |
| layout(std430, binding = 2) buffer Ssbo430 |
| { |
| bool valid; |
| Inner i; |
| }; |
| |
| void writeArgToStd430(bool ba[5]) |
| { |
| i.ba = ba; |
| } |
| |
| bool[5] readFromStd430(uint innerIndex) |
| { |
| return ubo430In.o.i[innerIndex].ba; |
| } |
| |
| void copyFromStd430(out bvec2 b2a[2][3]) |
| { |
| b2a = ubo430In.o.i[0].b2a; |
| } |
| |
| bool destroyContent(inout Inner iOut) |
| { |
| iOut.b = true; |
| iOut.b2 = bvec2(true); |
| iOut.b3 = bvec3(true); |
| iOut.b4 = bvec4(true); |
| iOut.ba = bool[5](true, true, true, true, true); |
| bvec2 true3[3] = bvec2[3](iOut.b2, iOut.b2, iOut.b2); |
| iOut.b2a = bvec2[2][3](true3, true3); |
| return true; |
| } |
| |
| void main(){ |
| // Directly copy from one layout to another. |
| i.b = o.i[0].b; |
| i.b2 = o.i[0].b2; |
| i.b2a = o.i[0].b2a; |
| |
| // Copy to temp with swizzle. |
| bvec4 t1 = o.i[0].b3.yxzy; |
| bvec4 t2 = o.i[0].b4.xxyy; |
| bvec4 t3 = o.i[0].b4.zzww; |
| |
| // Copy from temp with swizzle. |
| i.b3 = t1.ywz; |
| i.b4.yz = bvec2(t2.z, t3.y); |
| i.b4.wx = bvec2(t3.w, t2.x); |
| |
| // Copy by passing argument to function. |
| writeArgToStd430(o.i[0].ba); |
| |
| // Copy by return value. |
| ssbo140Out.i.ba = readFromStd430(0u); |
| |
| // Copy by out parameter. |
| copyFromStd430(ssbo140Out.i.b2a); |
| |
| // Logical operations |
| uvec4 t4 = ubo430In.o.i[0].b ? uvec4(0) : uvec4(1); |
| ssbo140Out.i.b = all(equal(t4, uvec4(1))) && (ubo430In.o.i[0].b ? false : true); |
| ssbo140Out.i.b2 = not(ubo430In.o.i[0].b2); |
| ssbo140Out.i.b3 = bvec3(all(ubo430In.o.i[0].b3), any(ubo430In.o.i[0].b3), any(ubo430In.o.i[0].b3.yx)); |
| ssbo140Out.i.b4 = equal(ubo430In.o.i[0].b4, bvec4(true, false, true, false)); |
| |
| ssbo140Out.valid = true; |
| ssbo140Out.valid = ssbo140Out.valid && all(equal(bvec3(o.i[1].b, o.i[1].b2), o.i[1].b3)); |
| ssbo140Out.valid = ssbo140Out.valid && |
| all(notEqual(o.i[1].b4, bvec4(o.i[1].ba[0], o.i[1].ba[1], o.i[1].ba[2], o.i[1].ba[3]))); |
| ssbo140Out.valid = ssbo140Out.valid && uint(o.i[1].ba[4]) == 1u; |
| for (int x = 0; x < o.i[1].b2a.length(); ++x) |
| { |
| for (int y = 0; y < o.i[1].b2a[x].length(); ++y) |
| { |
| ssbo140Out.valid = ssbo140Out.valid && all(equal(uvec2(o.i[1].b2a[x][y]), uvec2(x % 2, y % 2))); |
| } |
| } |
| |
| valid = o.i[1] == ubo430In.o.i[1]; |
| |
| // Make sure short-circuiting behavior is correct. |
| bool falseVar = !valid && destroyContent(i); |
| if (falseVar && destroyContent(ssbo140Out.i)) |
| { |
| valid = false; |
| } |
| |
| if (valid || o.i[uint((i.ba = bool[5](true, true, true, true, true))[1])].b) |
| { |
| } |
| else |
| { |
| ssbo140Out.valid = false; |
| } |
| })"; |
| |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Test data, laid out with padding (0) based on std140/std430 rules. |
| // clang-format off |
| const std::vector<uint32_t> ubo140Data = { |
| // o.i[0].b (bool) |
| true, 0, |
| // o.i[0].b2 (bvec2) |
| true, false, |
| // o.i[0].b3 (bvec3) |
| true, true, false, 0, |
| // o.i[0].b4 (bvec4) |
| false, true, false, true, |
| // o.i[0].ba (bool[5]) |
| true, 0, 0, 0, |
| false, 0, 0, 0, |
| false, 0, 0, 0, |
| true, 0, 0, 0, |
| true, 0, 0, 0, |
| // o.i[0].b2a (bool[2][3]) |
| false, true, 0, 0, true, true, 0, 0, true, false, 0, 0, |
| true, false, 0, 0, false, false, 0, 0, true, true, 0, 0, |
| |
| // o.i[1].b (bool) |
| false, 0, |
| // o.i[1].b2 (bvec2) |
| true, true, |
| // o.i[1].b3 (bvec3), expected to be equal to (b, b2) |
| false, true, true, 0, |
| // o.i[1].b4 (bvec4) |
| true, false, true, true, |
| // o.i[1].ba (bool[5]), expected to be equal to (not(b4), 1) |
| false, 0, 0, 0, |
| true, 0, 0, 0, |
| false, 0, 0, 0, |
| false, 0, 0, 0, |
| true, 0, 0, 0, |
| // o.i[1].b2a (bvec2[2][3]), [x][y] expected to equal (x%2,y%2) |
| false, false, 0, 0, false, true, 0, 0, false, false, 0, 0, |
| true, false, 0, 0, true, true, 0, 0, true, false, 0, 0, |
| }; |
| const std::vector<uint32_t> ubo430Data = { |
| // o.i[0].b (bool) |
| false, 0, |
| // o.i[0].b2 (bvec2) |
| true, true, |
| // o.i[0].b3 (bvec3) |
| false, false, true, 0, |
| // o.i[0].b4 (bvec4) |
| true, false, true, true, |
| // o.i[0].ba (bool[5]) |
| false, false, false, true, false, 0, |
| // o.i[0].b2a (bool[2][3]) |
| true, false, true, false, true, true, |
| false, true, true, true, false, false, 0, 0, |
| |
| // o.i[1] expected to be equal to ubo140In.o.i[1] |
| // o.i[1].b (bool) |
| false, 0, |
| // o.i[1].b2 (bvec2) |
| true, true, |
| // o.i[1].b3 (bvec3) |
| false, true, true, 0, |
| // o.i[1].b4 (bvec4) |
| true, false, true, true, |
| // o.i[1].ba (bool[5]) |
| false, true, false, false, true, 0, |
| // o.i[1].b2a (bvec2[2][3]) |
| false, false, false, true, false, false, |
| true, false, true, true, true, false, |
| }; |
| const std::vector<uint32_t> ssbo140Expect = { |
| // valid, expected to be true |
| true, 0, 0, 0, |
| |
| // i.b (bool), ubo430In.o.i[0].b ? false : true |
| true, 0, |
| // i.b2 (bvec2), not(ubo430In.o.i[0].b2) |
| false, false, |
| // i.b3 (bvec3), all(ubo430In.o.i[0].b3), any(...b3), any(...b3.yx) |
| false, true, false, 0, |
| // i.b4 (bvec4), ubo430In.o.i[0].b4 == (true, false, true, false) |
| true, true, true, false, |
| // i.ba (bool[5]), copied from ubo430In.o.i[0].ba |
| false, 0, 0, 0, |
| false, 0, 0, 0, |
| false, 0, 0, 0, |
| true, 0, 0, 0, |
| false, 0, 0, 0, |
| // i.b2a (bool[2][3]), copied from ubo430In.o.i[0].b2a |
| true, false, 0, 0, true, false, 0, 0, true, true, 0, 0, |
| false, true, 0, 0, true, true, 0, 0, false, false, 0, 0, |
| }; |
| const std::vector<uint32_t> ssbo430Expect = { |
| // valid, expected to be true |
| true, 0, 0, 0, |
| |
| // o.i[0].b (bool), copied from (Ubo140::)o.i[0].b |
| true, 0, |
| // o.i[0].b2 (bvec2), copied from (Ubo140::)o.i[0].b2 |
| true, false, |
| // o.i[0].b3 (bvec3), copied from (Ubo140::)o.i[0].b3 |
| true, true, false, 0, |
| // o.i[0].b4 (bvec4), copied from (Ubo140::)o.i[0].b4 |
| false, true, false, true, |
| // o.i[0].ba (bool[5]), copied from (Ubo140::)o.i[0].ba |
| true, false, false, true, true, 0, |
| // o.i[0].b2a (bool[2][3]), copied from (Ubo140::)o.i[0].b2a |
| false, true, true, true, true, false, |
| true, false, false, false, true, true, 0, 0, |
| }; |
| const std::vector<uint32_t> zeros(std::max(ssbo140Expect.size(), ssbo430Expect.size()), 0); |
| // clang-format on |
| |
| GLBuffer uboStd140, uboStd430; |
| GLBuffer ssboStd140, ssboStd430; |
| |
| InitBuffer(program, "Ubo140", uboStd140, 0, ubo140Data.data(), |
| static_cast<uint32_t>(ubo140Data.size()), true); |
| InitBuffer(program, "Ubo430", uboStd430, 0, ubo430Data.data(), |
| static_cast<uint32_t>(ubo430Data.size()), false); |
| InitBuffer(program, "Ssbo140", ssboStd140, 1, zeros.data(), |
| static_cast<uint32_t>(ssbo140Expect.size()), false); |
| InitBuffer(program, "Ssbo430", ssboStd430, 2, zeros.data(), |
| static_cast<uint32_t>(ssbo430Expect.size()), false); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(program); |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_TRUE(VerifyBuffer(ssboStd140, ssbo140Expect.data(), |
| static_cast<uint32_t>(ssbo140Expect.size()))); |
| EXPECT_TRUE(VerifyBuffer(ssboStd430, ssbo430Expect.data(), |
| static_cast<uint32_t>(ssbo430Expect.size()))); |
| } |
| |
| // Test that the precise keyword is not reserved before ES3.1. |
| TEST_P(GLSLTest_ES3, PreciseNotReserved) |
| { |
| // Skip in ES3.1+ as the precise keyword is reserved/core. |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() > 3 || |
| (getClientMajorVersion() == 3 && getClientMinorVersion() >= 1)); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| in float precise; |
| out vec4 my_FragColor; |
| void main() { my_FragColor = vec4(precise, 0, 0, 1.0); })"; |
| |
| constexpr char kVS[] = R"(#version 300 es |
| in vec4 a_position; |
| out float precise; |
| void main() { precise = a_position.x; gl_Position = a_position; })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that the precise keyword is reserved on ES3.0 without GL_EXT_gpu_shader5. |
| TEST_P(GLSLTest_ES31, PreciseReservedWithoutExtension) |
| { |
| // Skip if EXT_gpu_shader5 is enabled. |
| ANGLE_SKIP_TEST_IF(IsGLExtensionEnabled("GL_EXT_gpu_shader5")); |
| // Skip in ES3.2+ as the precise keyword is core. |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() > 3 || |
| (getClientMajorVersion() == 3 && getClientMinorVersion() >= 2)); |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision mediump float; |
| in float v_varying; |
| out vec4 my_FragColor; |
| void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); })"; |
| |
| constexpr char kVS[] = R"(#version 310 es |
| in vec4 a_position; |
| precise out float v_varying; |
| void main() { v_varying = a_position.x; gl_Position = a_position; })"; |
| |
| // Should fail, as precise is a reserved keyword when the extension is not enabled. |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Test that reusing the same variable name for different uses across stages links fine. Glslang |
| // wrapper's SPIR-V transformation should ignore all names for non-shader-interface variables and |
| // not get confused by them. |
| TEST_P(GLSLTest_ES31, VariableNameReuseAcrossStages) |
| { |
| // Fails to compile the fragment shader with error "undeclared identifier '_g'" |
| // http://anglebug.com/4404 |
| ANGLE_SKIP_TEST_IF(IsD3D11()); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| precision mediump float; |
| uniform highp vec4 a; |
| in highp vec4 b; |
| in highp vec4 c; |
| in highp vec4 d; |
| out highp vec4 e; |
| |
| vec4 f(vec4 a) |
| { |
| return a; |
| } |
| |
| vec4 g(vec4 f) |
| { |
| return f + f; |
| } |
| |
| void main() { |
| e = f(b) + a; |
| gl_Position = g(c) + f(d); |
| } |
| )"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision mediump float; |
| in highp vec4 e; |
| uniform sampler2D f; |
| layout(rgba8) uniform highp readonly image2D g; |
| uniform A |
| { |
| vec4 x; |
| } c; |
| layout(std140, binding=0) buffer B |
| { |
| vec4 x; |
| } d[2]; |
| out vec4 col; |
| |
| vec4 h(vec4 c) |
| { |
| return texture(f, c.xy) + imageLoad(g, ivec2(c.zw)); |
| } |
| |
| vec4 i(vec4 x, vec4 y) |
| { |
| return vec4(x.xy, y.zw); |
| } |
| |
| void main() { |
| col = h(e) + i(c.x, d[0].x) + d[1].x; |
| } |
| )"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Test that reusing the same uniform variable name for different uses across stages links fine. |
| TEST_P(GLSLTest_ES31, UniformVariableNameReuseAcrossStages) |
| { |
| constexpr char kVS[] = R"(#version 310 es |
| precision mediump float; |
| in highp vec4 variableWithSameName; |
| |
| void main() { |
| gl_Position = variableWithSameName; |
| } |
| )"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision mediump float; |
| uniform vec4 variableWithSameName; |
| out vec4 col; |
| |
| void main() { |
| col = vec4(variableWithSameName); |
| } |
| )"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_NE(0u, program); |
| } |
| |
| // Verify that precision match validation of uniforms is performed only if they are statically used |
| TEST_P(GLSLTest_ES31, UniformPrecisionMatchValidation) |
| { |
| // Nvidia driver bug: http://anglebug.com/5240 |
| ANGLE_SKIP_TEST_IF(IsOpenGL() && IsWindows() && IsNVIDIA()); |
| |
| constexpr char kVSUnused[] = R"(#version 300 es |
| precision highp float; |
| uniform highp vec4 positionIn; |
| |
| void main() |
| { |
| gl_Position = vec4(1, 0, 0, 1); |
| })"; |
| |
| constexpr char kVSStaticUse[] = R"(#version 300 es |
| precision highp float; |
| uniform highp vec4 positionIn; |
| |
| void main() |
| { |
| gl_Position = positionIn; |
| })"; |
| |
| constexpr char kFSUnused[] = R"(#version 300 es |
| precision highp float; |
| uniform highp vec4 positionIn; |
| out vec4 my_FragColor; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, 1); |
| })"; |
| |
| constexpr char kFSStaticUse[] = R"(#version 300 es |
| precision highp float; |
| uniform mediump vec4 positionIn; |
| out vec4 my_FragColor; |
| |
| void main() |
| { |
| my_FragColor = vec4(1, 0, 0, positionIn.z); |
| })"; |
| |
| GLuint program = 0; |
| |
| program = CompileProgram(kVSUnused, kFSUnused); |
| EXPECT_NE(0u, program); |
| |
| program = CompileProgram(kVSUnused, kFSStaticUse); |
| EXPECT_NE(0u, program); |
| |
| program = CompileProgram(kVSStaticUse, kFSUnused); |
| EXPECT_NE(0u, program); |
| |
| program = CompileProgram(kVSStaticUse, kFSStaticUse); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails when two instanceless interface blocks with different block names but |
| // same field names are present. |
| TEST_P(GLSLTest_ES31, AmbiguousInstancelessInterfaceBlockFields) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| in highp vec4 position; |
| layout(binding = 0) buffer BlockA { mediump float a; }; |
| void main() |
| { |
| a = 0.0; |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| uniform BlockB { float a; }; |
| void main() |
| { |
| color = vec4(a, a, a, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify I/O block array locations |
| TEST_P(GLSLTest_ES31, IOBlockLocations) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| in highp vec4 position; |
| |
| layout(location = 0) out vec4 aOut; |
| |
| layout(location = 6) out VSBlock |
| { |
| vec4 b; // location 6 |
| vec4 c; // location 7 |
| layout(location = 1) vec4 d; |
| vec4 e; // location 2 |
| vec4 f[2]; // locations 3 and 4 |
| } blockOut; |
| |
| layout(location = 5) out vec4 gOut; |
| |
| void main() |
| { |
| aOut = vec4(0.03, 0.06, 0.09, 0.12); |
| blockOut.b = vec4(0.15, 0.18, 0.21, 0.24); |
| blockOut.c = vec4(0.27, 0.30, 0.33, 0.36); |
| blockOut.d = vec4(0.39, 0.42, 0.45, 0.48); |
| blockOut.e = vec4(0.51, 0.54, 0.57, 0.6); |
| blockOut.f[0] = vec4(0.63, 0.66, 0.66, 0.69); |
| blockOut.f[1] = vec4(0.72, 0.75, 0.78, 0.81); |
| gOut = vec4(0.84, 0.87, 0.9, 0.93); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (triangles) in; |
| layout (triangle_strip, max_vertices = 3) out; |
| |
| // Input varyings |
| layout(location = 0) in vec4 aIn[]; |
| |
| layout(location = 6) in VSBlock |
| { |
| vec4 b; |
| vec4 c; |
| layout(location = 1) vec4 d; |
| vec4 e; |
| vec4 f[2]; |
| } blockIn[]; |
| |
| layout(location = 5) in vec4 gIn[]; |
| |
| // Output varyings |
| layout(location = 1) out vec4 aOut; |
| |
| layout(location = 0) out GSBlock |
| { |
| vec4 b; // location 0 |
| layout(location = 3) vec4 c; |
| layout(location = 7) vec4 d; |
| layout(location = 5) vec4 e[2]; |
| layout(location = 4) vec4 f; |
| } blockOut; |
| |
| layout(location = 2) out vec4 gOut; |
| |
| void main() |
| { |
| int n; |
| for (n = 0; n < gl_in.length(); n++) |
| { |
| gl_Position = gl_in[n].gl_Position; |
| |
| aOut = aIn[n]; |
| blockOut.b = blockIn[n].b; |
| blockOut.c = blockIn[n].c; |
| blockOut.d = blockIn[n].d; |
| blockOut.e[0] = blockIn[n].e; |
| blockOut.e[1] = blockIn[n].f[0]; |
| blockOut.f = blockIn[n].f[1]; |
| gOut = gIn[n]; |
| |
| EmitVertex(); |
| } |
| EndPrimitive(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| |
| layout(location = 0) out mediump vec4 color; |
| |
| layout(location = 1) in vec4 aIn; |
| |
| layout(location = 0) in GSBlock |
| { |
| vec4 b; |
| layout(location = 3) vec4 c; |
| layout(location = 7) vec4 d; |
| layout(location = 5) vec4 e[2]; |
| layout(location = 4) vec4 f; |
| } blockIn; |
| |
| layout(location = 2) in vec4 gIn; |
| |
| bool isEq(vec4 a, vec4 b) { return all(lessThan(abs(a-b), vec4(0.001))); } |
| |
| void main() |
| { |
| bool passR = isEq(aIn, vec4(0.03, 0.06, 0.09, 0.12)); |
| bool passG = isEq(blockIn.b, vec4(0.15, 0.18, 0.21, 0.24)) && |
| isEq(blockIn.c, vec4(0.27, 0.30, 0.33, 0.36)) && |
| isEq(blockIn.d, vec4(0.39, 0.42, 0.45, 0.48)) && |
| isEq(blockIn.e[0], vec4(0.51, 0.54, 0.57, 0.6)) && |
| isEq(blockIn.e[1], vec4(0.63, 0.66, 0.66, 0.69)) && |
| isEq(blockIn.f, vec4(0.72, 0.75, 0.78, 0.81)); |
| bool passB = isEq(gIn, vec4(0.84, 0.87, 0.9, 0.93)); |
| |
| color = vec4(passR, passG, passB, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_GS(program, kVS, kGS, kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture color; |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| |
| drawQuad(program, "position", 0); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test varying packing in presence of multiple I/O blocks |
| TEST_P(GLSLTest_ES31, MultipleIOBlocks) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| in highp vec4 position; |
| |
| out VSBlock1 |
| { |
| vec4 a; |
| vec4 b[2]; |
| } blockOut1; |
| |
| out VSBlock2 |
| { |
| vec4 c[2]; |
| vec4 d; |
| } blockOut2; |
| |
| void main() |
| { |
| blockOut1.a = vec4(0.15, 0.18, 0.21, 0.24); |
| blockOut1.b[0] = vec4(0.27, 0.30, 0.33, 0.36); |
| blockOut1.b[1] = vec4(0.39, 0.42, 0.45, 0.48); |
| blockOut2.c[0] = vec4(0.51, 0.54, 0.57, 0.6); |
| blockOut2.c[1] = vec4(0.63, 0.66, 0.66, 0.69); |
| blockOut2.d = vec4(0.72, 0.75, 0.78, 0.81); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| |
| layout(location = 0) out mediump vec4 color; |
| |
| in VSBlock1 |
| { |
| vec4 a; |
| vec4 b[2]; |
| } blockIn1; |
| |
| in VSBlock2 |
| { |
| vec4 c[2]; |
| vec4 d; |
| } blockIn2; |
| |
| bool isEq(vec4 a, vec4 b) { return all(lessThan(abs(a-b), vec4(0.001))); } |
| |
| void main() |
| { |
| bool passR = isEq(blockIn1.a, vec4(0.15, 0.18, 0.21, 0.24)); |
| bool passG = isEq(blockIn1.b[0], vec4(0.27, 0.30, 0.33, 0.36)) && |
| isEq(blockIn1.b[1], vec4(0.39, 0.42, 0.45, 0.48)); |
| bool passB = isEq(blockIn2.c[0], vec4(0.51, 0.54, 0.57, 0.6)) && |
| isEq(blockIn2.c[1], vec4(0.63, 0.66, 0.66, 0.69)); |
| bool passA = isEq(blockIn2.d, vec4(0.72, 0.75, 0.78, 0.81)); |
| |
| color = vec4(passR, passG, passB, passA); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture color; |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| |
| drawQuad(program, "position", 0); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test varying packing in presence of I/O block arrays |
| TEST_P(GLSLTest_ES31, IOBlockArray) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| |
| in highp vec4 position; |
| |
| out VSBlock1 |
| { |
| vec4 b[2]; |
| } blockOut1[2]; |
| |
| out VSBlock2 |
| { |
| vec4 d; |
| } blockOut2[3]; |
| |
| void main() |
| { |
| blockOut1[0].b[0] = vec4(0.15, 0.18, 0.21, 0.24); |
| blockOut1[0].b[1] = vec4(0.27, 0.30, 0.33, 0.36); |
| blockOut1[1].b[0] = vec4(0.39, 0.42, 0.45, 0.48); |
| blockOut1[1].b[1] = vec4(0.51, 0.54, 0.57, 0.6); |
| blockOut2[0].d = vec4(0.63, 0.66, 0.66, 0.69); |
| blockOut2[1].d = vec4(0.72, 0.75, 0.78, 0.81); |
| blockOut2[2].d = vec4(0.84, 0.87, 0.9, 0.93); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| |
| layout(location = 0) out mediump vec4 color; |
| |
| in VSBlock1 |
| { |
| vec4 b[2]; |
| } blockIn1[2]; |
| |
| in VSBlock2 |
| { |
| vec4 d; |
| } blockIn2[3]; |
| |
| bool isEq(vec4 a, vec4 b) { return all(lessThan(abs(a-b), vec4(0.001))); } |
| |
| void main() |
| { |
| bool passR = isEq(blockIn1[0].b[0], vec4(0.15, 0.18, 0.21, 0.24)) && |
| isEq(blockIn1[0].b[1], vec4(0.27, 0.30, 0.33, 0.36)); |
| bool passG = isEq(blockIn1[1].b[0], vec4(0.39, 0.42, 0.45, 0.48)) && |
| isEq(blockIn1[1].b[1], vec4(0.51, 0.54, 0.57, 0.6)); |
| bool passB = isEq(blockIn2[0].d, vec4(0.63, 0.66, 0.66, 0.69)); |
| bool passA = isEq(blockIn2[1].d, vec4(0.72, 0.75, 0.78, 0.81)) && |
| isEq(blockIn2[2].d, vec4(0.84, 0.87, 0.9, 0.93)); |
| |
| color = vec4(passR, passG, passB, passA); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture color; |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| |
| drawQuad(program, "position", 0); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Validate that link fails with I/O block member name mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberNameMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| out VSBlock { vec4 a; vec4 b[2]; } blockOut1; |
| void main() |
| { |
| blockOut1.a = vec4(0); |
| blockOut1.b[0] = vec4(0); |
| blockOut1.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| in VSBlock { vec4 c; vec4 b[2]; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.c.x, blockIn1.b[0].y, blockIn1.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member array size mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberArraySizeMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| out VSBlock { vec4 a; vec4 b[2]; } blockOut1; |
| void main() |
| { |
| blockOut1.a = vec4(0); |
| blockOut1.b[0] = vec4(0); |
| blockOut1.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| in VSBlock { vec4 a; vec4 b[3]; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.a.x, blockIn1.b[0].y, blockIn1.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member type mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberTypeMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| out VSBlock { vec4 a; vec4 b[2]; } blockOut1; |
| void main() |
| { |
| blockOut1.a = vec4(0); |
| blockOut1.b[0] = vec4(0); |
| blockOut1.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| in VSBlock { vec3 a; vec4 b[2]; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.a.x, blockIn1.b[0].y, blockIn1.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block location mismatches |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkLocationMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| layout(location = 2) out VSBlock { vec4 a; vec4 b[2]; } blockOut1; |
| void main() |
| { |
| blockOut1.a = vec4(0); |
| blockOut1.b[0] = vec4(0); |
| blockOut1.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| layout(location = 1) in VSBlock { vec4 a; vec4 b[2]; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.a.x, blockIn1.b[0].y, blockIn1.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member location mismatches |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberLocationMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| out VSBlock { vec4 a; layout(location = 2) vec4 b[2]; } blockOut1; |
| void main() |
| { |
| blockOut1.a = vec4(0); |
| blockOut1.b[0] = vec4(0); |
| blockOut1.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| in VSBlock { vec4 a; layout(location = 3) vec4 b[2]; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.a.x, blockIn1.b[0].y, blockIn1.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member struct name mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberStructNameMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| struct S1 { vec4 a; vec4 b[2]; }; |
| out VSBlock { S1 s; } blockOut1; |
| void main() |
| { |
| blockOut1.s.a = vec4(0); |
| blockOut1.s.b[0] = vec4(0); |
| blockOut1.s.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| struct S2 { vec4 a; vec4 b[2]; }; |
| in VSBlock { S2 s; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.s.a.x, blockIn1.s.b[0].y, blockIn1.s.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member struct member name mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberStructMemberNameMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| struct S { vec4 c; vec4 b[2]; }; |
| out VSBlock { S s; } blockOut1; |
| void main() |
| { |
| blockOut1.s.c = vec4(0); |
| blockOut1.s.b[0] = vec4(0); |
| blockOut1.s.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| struct S { vec4 a; vec4 b[2]; }; |
| in VSBlock { S s; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.s.a.x, blockIn1.s.b[0].y, blockIn1.s.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member struct member type mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberStructMemberTypeMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| struct S { vec4 a; vec4 b[2]; }; |
| out VSBlock { S s; } blockOut1; |
| void main() |
| { |
| blockOut1.s.a = vec4(0); |
| blockOut1.s.b[0] = vec4(0); |
| blockOut1.s.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| struct S { vec3 a; vec4 b[2]; }; |
| in VSBlock { S s; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.s.a.x, blockIn1.s.b[0].y, blockIn1.s.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member struct member array size mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberStructMemberArraySizeMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| struct S { vec4 a; vec4 b[3]; }; |
| out VSBlock { S s; } blockOut1; |
| void main() |
| { |
| blockOut1.s.a = vec4(0); |
| blockOut1.s.b[0] = vec4(0); |
| blockOut1.s.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| struct S { vec4 a; vec4 b[2]; }; |
| in VSBlock { S s; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.s.a.x, blockIn1.s.b[0].y, blockIn1.s.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member struct member count mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberStructMemberCountMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| struct S { vec4 a; vec4 b[2]; vec4 c; }; |
| out VSBlock { S s; } blockOut1; |
| void main() |
| { |
| blockOut1.s.c = vec4(0); |
| blockOut1.s.b[0] = vec4(0); |
| blockOut1.s.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| struct S { vec4 a; vec4 b[2]; }; |
| in VSBlock { S s; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.s.a.x, blockIn1.s.b[0].y, blockIn1.s.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Validate that link fails with I/O block member nested struct mismatches. |
| TEST_P(GLSLTest_ES31, NegativeIOBlocksLinkMemberNestedStructMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| in highp vec4 position; |
| struct S1 { vec4 c; vec4 b[2]; }; |
| struct S2 { S1 s; }; |
| struct S3 { S2 s; }; |
| out VSBlock { S3 s; } blockOut1; |
| void main() |
| { |
| blockOut1.s.s.s.c = vec4(0); |
| blockOut1.s.s.s.b[0] = vec4(0); |
| blockOut1.s.s.s.b[1] = vec4(0); |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| layout(location = 0) out mediump vec4 color; |
| struct S1 { vec4 a; vec4 b[2]; }; |
| struct S2 { S1 s; }; |
| struct S3 { S2 s; }; |
| in VSBlock { S3 s; } blockIn1; |
| void main() |
| { |
| color = vec4(blockIn1.s.s.s.a.x, blockIn1.s.s.s.b[0].y, blockIn1.s.s.s.b[1].z, 1.0); |
| })"; |
| |
| GLuint program = CompileProgram(kVS, kFS); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Regression test for transformation bug which separates struct declarations from uniform |
| // declarations. The bug was that the uniform variable usage in the initializer of a new |
| // declaration (y below) was not being processed. |
| TEST_P(GLSLTest, UniformStructBug) |
| { |
| constexpr char kVS[] = R"(precision highp float; |
| |
| uniform struct Global |
| { |
| float x; |
| } u_global; |
| |
| void main() { |
| float y = u_global.x; |
| |
| gl_Position = vec4(y); |
| })"; |
| |
| GLuint shader = glCreateShader(GL_VERTEX_SHADER); |
| |
| const char *sourceArray[1] = {kVS}; |
| GLint lengths[1] = {static_cast<GLint>(sizeof(kVS) - 1)}; |
| glShaderSource(shader, 1, sourceArray, lengths); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_NE(compileResult, 0); |
| } |
| |
| // Regression test for transformation bug which separates struct declarations from uniform |
| // declarations. The bug was that the arrayness of the declaration was not being applied to the |
| // replaced uniform variable. |
| TEST_P(GLSLTest_ES31, UniformStructBug2) |
| { |
| constexpr char kVS[] = R"(#version 310 es |
| precision highp float; |
| |
| uniform struct Global |
| { |
| float x; |
| } u_global[2][3]; |
| |
| void main() { |
| float y = u_global[0][0].x; |
| |
| gl_Position = vec4(y); |
| })"; |
| |
| GLuint shader = glCreateShader(GL_VERTEX_SHADER); |
| |
| const char *sourceArray[1] = {kVS}; |
| GLint lengths[1] = {static_cast<GLint>(sizeof(kVS) - 1)}; |
| glShaderSource(shader, 1, sourceArray, lengths); |
| glCompileShader(shader); |
| |
| GLint compileResult; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); |
| EXPECT_NE(compileResult, 0); |
| } |
| |
| // Test that providing more components to a matrix constructor than necessary works. Based on a |
| // clusterfuzz test that caught an OOB array write in glslang. |
| TEST_P(GLSLTest, MatrixConstructor) |
| { |
| constexpr char kVS[] = R"(attribute vec4 aPosition; |
| varying vec4 vColor; |
| void main() |
| { |
| gl_Position = aPosition; |
| vec4 color = vec4(aPosition.xy, 0, 1); |
| mat4 m4 = mat4(color, color.yzwx, color.zwx, color.zwxy, color.wxyz); |
| vColor = m4[0]; |
| })"; |
| |
| GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS); |
| EXPECT_NE(0u, shader); |
| glDeleteShader(shader); |
| } |
| |
| // Test that scalar(nonScalar) constructors work. |
| TEST_P(GLSLTest_ES3, ScalarConstructor) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| uniform vec4 u; |
| out vec4 color; |
| void main() |
| { |
| float f1 = float(u); |
| mat3 m = mat3(u, u, u); |
| int i = int(m); |
| color = vec4(f1, float(i), 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint uloc = glGetUniformLocation(program, "u"); |
| ASSERT_NE(uloc, -1); |
| glUniform4f(uloc, 1.0, 0.4, 0.2, 0.7); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow); |
| } |
| |
| // Test that initializing global variables with non-constant values work |
| TEST_P(GLSLTest_ES3, InitGlobalNonConstant) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_non_constant_global_initializers")); |
| |
| constexpr char kVS[] = R"(#version 300 es |
| #extension GL_EXT_shader_non_constant_global_initializers : require |
| uniform vec4 u; |
| out vec4 color; |
| |
| vec4 global1 = u; |
| vec4 global2 = u + vec4(1); |
| vec4 global3 = global1 * global2; |
| void main() |
| { |
| color = global3; |
| })"; |
| |
| GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS); |
| EXPECT_NE(0u, shader); |
| glDeleteShader(shader); |
| } |
| |
| // Test that initializing global variables with complex constants work |
| TEST_P(GLSLTest_ES3, InitGlobalComplexConstant) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 color; |
| |
| struct T |
| { |
| float f; |
| }; |
| |
| struct S |
| { |
| vec4 v; |
| mat3x4 m[2]; |
| T t; |
| }; |
| |
| S s = S( |
| vec4(0, 1, 2, 3), |
| mat3x4[2]( |
| mat3x4( |
| vec4(4, 5, 6, 7), |
| vec4(8, 9, 10, 11), |
| vec4(12, 13, 14, 15) |
| ), |
| mat3x4( |
| vec4(16, 17, 18, 19), |
| vec4(20, 21, 22, 23), |
| vec4(24, 25, 26, 27) |
| ) |
| ), |
| T(28.0) |
| ); |
| |
| void main() |
| { |
| vec4 result = vec4(0, 1, 0, 1); |
| |
| if (s.v != vec4(0, 1, 2, 3)) |
| result = vec4(1, 0, 0, 0); |
| |
| for (int index = 0; index < 2; ++index) |
| { |
| for (int column = 0; column < 3; ++column) |
| { |
| int expect = index * 12 + column * 4 + 4; |
| if (s.m[index][column] != vec4(expect, expect + 1, expect + 2, expect + 3)) |
| result = vec4(float(index + 1) / 2.0, 0, float(column + 1) / 3.0, 1); |
| } |
| } |
| |
| if (s.t.f != 28.0) |
| result = vec4(0, 0, 1, 0); |
| |
| color = result; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Test that built-ins with out parameters work |
| TEST_P(GLSLTest_ES31, BuiltInsWithOutParameters) |
| { |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| precision highp int; |
| |
| out vec4 color; |
| |
| uniform float f; // = 3.41 |
| uniform uvec4 u1; // = 0xFEDCBA98, 0x13579BDF, 0xFEDCBA98, 0x13579BDF |
| uniform uvec4 u2; // = 0xECA86420, 0x12345678, 0x12345678, 0xECA86420 |
| |
| struct S |
| { |
| float fvalue; |
| int ivalues[2]; |
| uvec4 uvalues[3]; |
| }; |
| |
| struct T |
| { |
| S s[2]; |
| }; |
| |
| void main() |
| { |
| float integer; |
| float fraction = modf(f, integer); |
| |
| T t; |
| |
| t.s[0].fvalue = frexp(f, t.s[0].ivalues[0]); |
| float significand = t.s[0].fvalue; |
| int exponent = t.s[0].ivalues[0]; |
| |
| t.s[0].uvalues[0] = uaddCarry(u1, u2, t.s[0].uvalues[1].yxwz); |
| uvec4 addResult = t.s[0].uvalues[0]; |
| uvec4 addCarry = t.s[0].uvalues[1].yxwz; |
| |
| t.s[0].uvalues[2].wx = usubBorrow(u1.wx, u2.wx, t.s[1].uvalues[0].wx); |
| uvec2 subResult = t.s[0].uvalues[2].wx; |
| uvec2 subBorrow = t.s[1].uvalues[0].wx; |
| |
| umulExtended(u1, u2, t.s[1].uvalues[1], t.s[1].uvalues[2]); |
| uvec4 mulMsb = t.s[1].uvalues[1]; |
| uvec4 mulLsb = t.s[1].uvalues[2]; |
| |
| ivec2 imulMsb, imulLsb; |
| imulExtended(ivec2(u1.wz), ivec2(u2.wz), imulMsb.yx, imulLsb.yx); |
| |
| bool modfPassed = abs(fraction - 0.41) < 0.0001 && integer == 3.0; |
| bool frexpPassed = abs(significand - 0.8525) < 0.0001 && exponent == 2; |
| bool addPassed = |
| addResult == uvec4(0xEB851EB8, 0x258BF257, 0x11111110, 0xFFFFFFFF) && |
| addCarry == uvec4(1, 0, 1, 0); |
| bool subPassed = subResult == uvec2(0x26AF37BF, 0x12345678) && subBorrow == uvec2(1, 0); |
| bool mulPassed = |
| mulMsb == uvec4(0xEB9B208C, 0x01601D49, 0x121FA00A, 0x11E17CC0) && |
| mulLsb == uvec4(0xA83AB300, 0xD6B9FA88, 0x35068740, 0x822E97E0); |
| bool imulPassed = |
| imulMsb == ivec2(0xFFEB4992, 0xFE89E0E1) && |
| imulLsb == ivec2(0x35068740, 0x822E97E0); |
| |
| color = vec4(modfPassed ? 1 : 0, |
| frexpPassed ? 1 : 0, |
| (addPassed ? 0.4 : 0.0) + (subPassed ? 0.6 : 0.0), |
| (mulPassed ? 0.4 : 0.0) + (imulPassed ? 0.6 : 0.0)); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint floc = glGetUniformLocation(program, "f"); |
| GLint u1loc = glGetUniformLocation(program, "u1"); |
| GLint u2loc = glGetUniformLocation(program, "u2"); |
| ASSERT_NE(floc, -1); |
| ASSERT_NE(u1loc, -1); |
| ASSERT_NE(u2loc, -1); |
| glUniform1f(floc, 3.41); |
| glUniform4ui(u1loc, 0xFEDCBA98u, 0x13579BDFu, 0xFEDCBA98u, 0x13579BDFu); |
| glUniform4ui(u2loc, 0xECA86420u, 0x12345678u, 0x12345678u, 0xECA86420u); |
| |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| } |
| |
| // Test that interpolateAt* work with swizzle. This test is disabled as swizzled interpolants are |
| // only allowed in desktop GLSL. |
| TEST_P(GLSLTest_ES31, InterpolateAtWithSwizzle) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_shader_multisample_interpolation")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| |
| out vec4 interpolant; |
| |
| void main() |
| { |
| // The following triangle is being drawn over the framebuffer. |
| // |
| // (-1,3) |\ |
| // | \ |
| // | \ |
| // | \ |
| // | \ |
| // +--------------+ |
| // | | \ |
| // | | \ |
| // | Framebuffer | \ |
| // | | \ |
| // | | \ |
| // (-1,-1) +--------------+--------------- (3,-1) |
| // |
| // Interpolant is set such that interpolateAtCentroid would produce the desired value for |
| // position == (0, 0), and interpolateAtOffset(0.5, -0.5) for position == (1,-1) |
| if (gl_VertexID == 0) |
| { |
| gl_Position = vec4(-1, -1, 0, 1); |
| interpolant = vec4(1.5, 0.5, 0, 0); |
| } |
| else if (gl_VertexID == 1) |
| { |
| gl_Position = vec4(3, -1, 0, 1); |
| interpolant = vec4(0, 0, 1, 2); |
| } |
| else |
| { |
| gl_Position = vec4(-1, 3, 0, 1); |
| interpolant = vec4(0, 1, -1, 2); |
| } |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_OES_shader_multisample_interpolation : require |
| precision highp float; |
| |
| in vec4 interpolant; |
| out vec4 color; |
| |
| void main() |
| { |
| // Should result in (0.75, 1.0) |
| vec2 atCentroid = interpolateAtCentroid(interpolant.xw); |
| // Selecting the bottom-right corner, this should result in (0.5, 0.25), but interpolateAtOffset |
| // doesn't make guarantees regarding the range and granularity of the offset. The interpolant |
| // is given values such that the bottom-left/top-right diagonal is interpolated to a constant |
| // value of (0, 0.5). The top-left corner has the value (-0.5, 0.75). We therefore make a |
| // coarse test to make sure that atOffset.x > 0 and atOffset.y < 0.5, thus ensuring at least |
| // that the offset is in the correct half of the pixel. |
| vec2 atOffset = interpolateAtOffset(interpolant.zy, vec2(0.5, -0.5)); |
| |
| color = vec4(atCentroid, atOffset.x > 0.0 ? 1 : 0, atOffset.y < 0.5 ? 1 : 0); |
| })"; |
| |
| GLRenderbuffer rbo; |
| glBindRenderbuffer(GL_RENDERBUFFER, rbo); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| |
| glViewport(0, 0, 1, 1); |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_NEAR(0, 0, 191, 255, 255, 255, 1); |
| } |
| |
| class GLSLTestLoops : public GLSLTest |
| { |
| protected: |
| void runTest(const char *fs) |
| { |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs); |
| |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| }; |
| |
| // Test basic for loops |
| TEST_P(GLSLTestLoops, BasicFor) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; j < 8; ++j) |
| { |
| for (int k = 0; k < 2; ++k, ++j) ++result; |
| for (int k = 0; k < 3; ++k) ++result; |
| for (int k = 0; k < 0; ++k) ++result; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop without condition |
| TEST_P(GLSLTestLoops, ForNoCondition) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; ; ++j) |
| { |
| for (int k = 0; k < 2; ++k, ++j) ++result; |
| for (int k = 0; k < 3; ++k) ++result; |
| for (int k = 0; k < 0; ++k) ++result; |
| |
| if (j >= 8) |
| break; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop without init and expression |
| TEST_P(GLSLTestLoops, ForNoInitConditionOrExpression) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| { |
| int j = 0; |
| for (;;) |
| { |
| for (int k = 0; k < 2; ++k, ++j) ++result; |
| for (int k = 0; k < 3; ++k) ++result; |
| for (int k = 0; k < 0; ++k) ++result; |
| |
| if (j >= 8) |
| break; |
| ++j; |
| } |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop with continue |
| TEST_P(GLSLTestLoops, ForContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; j < 8; ++j) |
| { |
| for (int k = 0; k < 2; ++k, ++j) ++result; |
| for (int k = 0; k < 3; ++k) ++result; |
| if (i > 3) |
| continue; |
| for (int k = 0; k < 0; ++k) ++result; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop with continue at the end of block |
| TEST_P(GLSLTestLoops, ForUnconditionalContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; j < 8; ++j) |
| { |
| for (int k = 0; k < 2; ++k, ++j) ++result; |
| for (int k = 0; k < 3; ++k) ++result; |
| for (int k = 0; k < 0; ++k) ++result; |
| continue; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop with break at the end of block |
| TEST_P(GLSLTestLoops, ForUnconditionalBreak) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; j < 8; ++j) |
| { |
| for (int k = 0; k < 2; ++k, ++j) ++result; |
| for (int k = 0; k < 3; ++k) ++result; |
| for (int k = 0; k < 0; ++k) ++result; |
| break; |
| } |
| |
| color = result == 50 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop with break and continue |
| TEST_P(GLSLTestLoops, ForBreakContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; j < 8; ++j) |
| { |
| if (j < 2) continue; |
| if (j > 6) break; |
| if (i < 3) continue; |
| if (i > 8) break; |
| ++result; |
| } |
| |
| color = result == 30 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test basic while loops |
| TEST_P(GLSLTestLoops, BasicWhile) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| while (i < 10) |
| { |
| int j = 0; |
| while (j < 8) |
| { |
| int k = 0; |
| while (k < 2) { ++result; ++k; ++j; } |
| while (k < 5) { ++result; ++k; } |
| while (k < 4) { ++result; } |
| ++j; |
| } |
| ++i; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test while loops with continue |
| TEST_P(GLSLTestLoops, WhileContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| while (i < 10) |
| { |
| int j = 0; |
| while (j < 8) |
| { |
| int k = 0; |
| while (k < 2) { ++result; ++k; ++j; } |
| while (k < 5) { ++result; ++k; } |
| if (i > 3) |
| { |
| ++j; |
| continue; |
| } |
| while (k < 4) { ++result; } |
| ++j; |
| } |
| ++i; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test while loops with continue at the end of block |
| TEST_P(GLSLTestLoops, WhileUnconditionalContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| while (i < 10) |
| { |
| int j = 0; |
| while (j < 8) |
| { |
| int k = 0; |
| while (k < 2) { ++result; ++k; ++j; } |
| while (k < 5) { ++result; ++k; } |
| while (k < 4) { ++result; } |
| ++j; |
| continue; |
| } |
| ++i; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test while loops with break |
| TEST_P(GLSLTestLoops, WhileBreak) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| while (i < 10) |
| { |
| int j = 0; |
| while (true) |
| { |
| int k = 0; |
| while (k < 2) { ++result; ++k; ++j; } |
| while (k < 5) { ++result; ++k; } |
| while (k < 4) { ++result; } |
| ++j; |
| if (j >= 8) |
| break; |
| } |
| ++i; |
| } |
| |
| color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test while loops with continue at the end of block |
| TEST_P(GLSLTestLoops, WhileUnconditionalBreak) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| while (i < 10) |
| { |
| int j = 0; |
| while (j < 8) |
| { |
| int k = 0; |
| while (k < 2) { ++result; ++k; ++j; } |
| while (k < 5) { ++result; ++k; } |
| while (k < 4) { ++result; } |
| ++j; |
| break; |
| } |
| ++i; |
| } |
| |
| color = result == 50 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test basic do-while loops |
| TEST_P(GLSLTestLoops, BasicDoWhile) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| do |
| { |
| int j = 0; |
| do |
| { |
| int k = 0; |
| do { ++result; ++k; ++j; } while (k < 2); |
| do { ++result; ++k; } while (k < 5); |
| do { ++result; } while (k < 3); |
| ++j; |
| } while (j < 8); |
| ++i; |
| } while (i < 10); |
| |
| color = result == 180 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test do-while loops with continue |
| TEST_P(GLSLTestLoops, DoWhileContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| do |
| { |
| int j = 0; |
| do |
| { |
| int k = 0; |
| do { ++result; ++k; ++j; } while (k < 2); |
| if (i > 3) |
| { |
| ++j; |
| continue; |
| } |
| do { ++result; ++k; } while (k < 5); |
| do { ++result; } while (k < 3); |
| ++j; |
| } while (j < 8); |
| ++i; |
| } while (i < 10); |
| |
| color = result == 108 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test do-while loops with continue at the end of block |
| TEST_P(GLSLTestLoops, DoWhileUnconditionalContinue) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| do |
| { |
| int j = 0; |
| do |
| { |
| int k = 0; |
| do { ++result; ++k; ++j; continue; } while (k < 2); |
| do { ++result; ++k; continue; } while (k < 5); |
| do { ++result; continue; } while (k < 3); |
| ++j; |
| } while (j < 8); |
| ++i; |
| } while (i < 10); |
| |
| color = result == 180 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test do-while loops with break |
| TEST_P(GLSLTestLoops, DoWhileBreak) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| do |
| { |
| int j = 0; |
| do |
| { |
| int k = 0; |
| do { ++result; ++k; ++j; } while (k < 2); |
| do { ++result; ++k; } while (k < 5); |
| do { ++result; } while (k < 3); |
| ++j; |
| if (j >= 8) |
| break; |
| } while (true); |
| ++i; |
| } while (i < 10); |
| |
| color = result == 180 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test do-while loops with break at the end of block |
| TEST_P(GLSLTestLoops, DoWhileUnconditionalBreak) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| do |
| { |
| int j = 0; |
| do |
| { |
| int k = 0; |
| do { ++result; ++k; ++j; break; } while (k < 2); |
| do { ++result; ++k; break; } while (k < 5); |
| do { ++result; break; } while (k < 3); |
| ++j; |
| } while (j < 8); |
| ++i; |
| } while (i < 10); |
| |
| color = result == 120 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test for loop with continue inside switch. |
| TEST_P(GLSLTestLoops, ForContinueInSwitch) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| for (int i = 0; i < 10; ++i) |
| for (int j = 0; j < 8; ++j) |
| { |
| switch (j) |
| { |
| case 2: |
| case 3: |
| case 4: |
| ++result; |
| // fallthrough |
| case 5: |
| case 6: |
| ++result; |
| break; |
| default: |
| continue; |
| } |
| } |
| |
| color = result == 80 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test while loop with continue inside switch |
| TEST_P(GLSLTestLoops, WhileContinueInSwitch) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| while (i < 10) |
| { |
| int j = 0; |
| while (j < 8) |
| { |
| switch (j) |
| { |
| case 2: |
| default: |
| case 3: |
| case 4: |
| ++j; |
| ++result; |
| continue; |
| case 0: |
| case 1: |
| case 7: |
| break; |
| } |
| ++j; |
| } |
| ++i; |
| } |
| |
| color = result == 50 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| // Test do-while loops with continue in switch |
| TEST_P(GLSLTestLoops, DoWhileContinueInSwitch) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| |
| void main() |
| { |
| int result = 0; |
| int i = 0; |
| do |
| { |
| int j = 0; |
| do |
| { |
| switch (j) |
| { |
| case 0: |
| ++j; |
| continue; |
| default: |
| case 2: |
| case 3: |
| case 4: |
| ++j; |
| ++result; |
| if (j >= 2 && j <= 6) |
| break; |
| else |
| continue; |
| } |
| ++result; |
| } while (j < 8); |
| ++i; |
| } while (i < 10); |
| |
| color = result == 120 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); |
| })"; |
| |
| runTest(kFS); |
| } |
| |
| TEST_P(GLSLTest, AAA) |
| { |
| constexpr char kFS[] = R"( |
| // It is assumed that uTest is set to 0. It's here to make the expression not constant. |
| uniform mediump float uTest; |
| void main() { |
| // exact representation of 4096.5 requires 13 bits of relative precision. |
| const highp float c = 4096.5; |
| mediump float a = 0.0; |
| // Below, addition should be evaluated at highp, since one of the operands has the highp qualifier. |
| // Thus fract should also be evaluated at highp. |
| // See OpenGL ES Shading Language spec section 4.5.2. |
| // This should make the result 0.5, since highp provides at least 16 bits of relative precision. |
| // (exceptions for operation precision are allowed for a small number of computationally |
| // intensive built-in functions, but it is reasonable to think that fract is not one of those). |
| // However, if fract() is incorrectly evaluated at minimum precision fulfilling mediump criteria, |
| // or at IEEE half float precision, the result is 0.0. |
| a = fract(c + uTest); |
| // Multiply by 2.0 to make the color green. |
| gl_FragColor = vec4(0.0, 2.0 * a, 0.0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| } // anonymous namespace |
| |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(GLSLTest, WithDirectSPIRVGeneration(ES2_VULKAN())); |
| |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTestNoValidation); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTest_ES3); |
| ANGLE_INSTANTIATE_TEST_ES3_AND(GLSLTest_ES3, WithDirectSPIRVGeneration(ES3_VULKAN())); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTestLoops); |
| ANGLE_INSTANTIATE_TEST_ES3_AND(GLSLTestLoops, WithDirectSPIRVGeneration(ES3_VULKAN())); |
| |
| ANGLE_INSTANTIATE_TEST_ES2_AND(WebGLGLSLTest, WithDirectSPIRVGeneration(ES2_VULKAN())); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2GLSLTest); |
| ANGLE_INSTANTIATE_TEST_ES3_AND(WebGL2GLSLTest, WithDirectSPIRVGeneration(ES3_VULKAN())); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTest_ES31); |
| ANGLE_INSTANTIATE_TEST_ES31_AND(GLSLTest_ES31, WithDirectSPIRVGeneration(ES31_VULKAN())); |