| // |
| // 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. |
| // |
| |
| // BindUniformLocationTest.cpp : Tests of the GL_CHROMIUM_bind_uniform_location extension. |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| |
| #include <cmath> |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class BindUniformLocationTest : public ANGLETest |
| { |
| protected: |
| BindUniformLocationTest() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void testTearDown() override |
| { |
| if (mProgram != 0) |
| { |
| glDeleteProgram(mProgram); |
| } |
| } |
| |
| GLuint mProgram = 0; |
| }; |
| |
| // Test basic functionality of GL_CHROMIUM_bind_uniform_location |
| TEST_P(BindUniformLocationTest, Basic) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform vec4 u_colorC; |
| uniform vec4 u_colorB[2]; |
| uniform vec4 u_colorA; |
| void main() |
| { |
| gl_FragColor = u_colorA + u_colorB[0] + u_colorB[1] + u_colorC; |
| })"; |
| |
| GLint colorALocation = 3; |
| GLint colorBLocation = 10; |
| GLint colorCLocation = 5; |
| |
| mProgram = CompileProgram(essl1_shaders::vs::Simple(), kFS, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, colorALocation, "u_colorA"); |
| glBindUniformLocationCHROMIUM(program, colorBLocation, "u_colorB[0]"); |
| glBindUniformLocationCHROMIUM(program, colorCLocation, "u_colorC"); |
| }); |
| ASSERT_NE(0u, mProgram); |
| |
| glUseProgram(mProgram); |
| |
| static const float colorB[] = { |
| 0.0f, 0.50f, 0.0f, 0.0f, 0.0f, 0.0f, 0.75f, 0.0f, |
| }; |
| |
| glUniform4f(colorALocation, 0.25f, 0.0f, 0.0f, 0.0f); |
| glUniform4fv(colorBLocation, 2, colorB); |
| glUniform4f(colorCLocation, 0.0f, 0.0f, 0.0f, 1.0f); |
| |
| drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_NEAR(0, 0, 64, 128, 192, 255, 1.0); |
| } |
| |
| // Force a sampler location and make sure it samples the correct texture |
| TEST_P(BindUniformLocationTest, SamplerLocation) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform vec4 u_colorA; |
| uniform vec4 u_colorB[2]; |
| uniform sampler2D u_sampler; |
| void main() |
| { |
| gl_FragColor = u_colorA + u_colorB[0] + u_colorB[1] + texture2D(u_sampler, vec2(0, 0)); |
| })"; |
| |
| GLint colorALocation = 3; |
| GLint colorBLocation = 10; |
| GLint samplerLocation = 1; |
| |
| mProgram = CompileProgram(essl1_shaders::vs::Simple(), kFS, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, colorALocation, "u_colorA"); |
| glBindUniformLocationCHROMIUM(program, colorBLocation, "u_colorB[0]"); |
| glBindUniformLocationCHROMIUM(program, samplerLocation, "u_sampler"); |
| }); |
| ASSERT_NE(0u, mProgram); |
| |
| glUseProgram(mProgram); |
| |
| static const float colorB[] = { |
| 0.0f, 0.50f, 0.0f, 0.0f, 0.0f, 0.0f, 0.75f, 0.0f, |
| }; |
| |
| glUniform4f(colorALocation, 0.25f, 0.0f, 0.0f, 0.0f); |
| glUniform4fv(colorBLocation, 2, colorB); |
| |
| // Point the texture at texture unit 2 |
| glUniform1i(samplerLocation, 2); |
| |
| GLTexture texture; |
| glActiveTexture(GL_TEXTURE2); |
| glBindTexture(GL_TEXTURE_2D, texture); |
| constexpr GLubyte kTextureData[] = {32, 32, 32, 255}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, kTextureData); |
| |
| drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_NEAR(0, 0, 96, 160, 224, 255, 1.0); |
| } |
| |
| // Test that conflicts are detected when two uniforms are bound to the same location |
| TEST_P(BindUniformLocationTest, ConflictsDetection) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| R"(precision mediump float; |
| uniform vec4 u_colorA; |
| uniform vec4 u_colorB; |
| void main() |
| { |
| gl_FragColor = u_colorA + u_colorB; |
| })"; |
| |
| GLint colorALocation = 3; |
| GLint colorBLocation = 4; |
| |
| GLuint vs = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple()); |
| GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); |
| |
| mProgram = glCreateProgram(); |
| glAttachShader(mProgram, vs); |
| glDeleteShader(vs); |
| glAttachShader(mProgram, fs); |
| glDeleteShader(fs); |
| |
| glBindUniformLocationCHROMIUM(mProgram, colorALocation, "u_colorA"); |
| // Bind u_colorB to location a, causing conflicts, link should fail. |
| glBindUniformLocationCHROMIUM(mProgram, colorALocation, "u_colorB"); |
| glLinkProgram(mProgram); |
| GLint linked = 0; |
| glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); |
| ASSERT_EQ(0, linked); |
| |
| // Bind u_colorB to location b, no conflicts, link should succeed. |
| glBindUniformLocationCHROMIUM(mProgram, colorBLocation, "u_colorB"); |
| glLinkProgram(mProgram); |
| linked = 0; |
| glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); |
| EXPECT_EQ(1, linked); |
| } |
| |
| // Test a use case of the chromium compositor |
| TEST_P(BindUniformLocationTest, Compositor) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kVS[] = |
| R"(attribute vec4 a_position; |
| attribute vec2 a_texCoord; |
| uniform mat4 matrix; |
| uniform vec2 color_a[4]; |
| uniform vec4 color_b; |
| varying vec4 v_color; |
| void main() |
| { |
| v_color.xy = color_a[0] + color_a[1]; |
| v_color.zw = color_a[2] + color_a[3]; |
| v_color += color_b; |
| gl_Position = matrix * a_position; |
| })"; |
| |
| constexpr char kFS[] = |
| R"(precision mediump float; |
| varying vec4 v_color; |
| uniform float alpha; |
| uniform vec4 multiplier; |
| uniform vec3 color_c[8]; |
| void main() |
| { |
| vec4 color_c_sum = vec4(0.0); |
| color_c_sum.xyz += color_c[0]; |
| color_c_sum.xyz += color_c[1]; |
| color_c_sum.xyz += color_c[2]; |
| color_c_sum.xyz += color_c[3]; |
| color_c_sum.xyz += color_c[4]; |
| color_c_sum.xyz += color_c[5]; |
| color_c_sum.xyz += color_c[6]; |
| color_c_sum.xyz += color_c[7]; |
| color_c_sum.w = alpha; |
| color_c_sum *= multiplier; |
| gl_FragColor = v_color + color_c_sum; |
| })"; |
| |
| int counter = 6; |
| int matrixLocation = counter++; |
| int colorALocation = counter++; |
| int colorBLocation = counter++; |
| int alphaLocation = counter++; |
| int multiplierLocation = counter++; |
| int colorCLocation = counter++; |
| |
| mProgram = CompileProgram(kVS, kFS, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, matrixLocation, "matrix"); |
| glBindUniformLocationCHROMIUM(program, colorALocation, "color_a"); |
| glBindUniformLocationCHROMIUM(program, colorBLocation, "color_b"); |
| glBindUniformLocationCHROMIUM(program, alphaLocation, "alpha"); |
| glBindUniformLocationCHROMIUM(program, multiplierLocation, "multiplier"); |
| glBindUniformLocationCHROMIUM(program, colorCLocation, "color_c"); |
| }); |
| ASSERT_NE(0u, mProgram); |
| |
| glUseProgram(mProgram); |
| |
| static const float colorA[] = { |
| 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, |
| }; |
| |
| static const float colorC[] = { |
| 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, |
| 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, |
| }; |
| |
| static const float identity[] = { |
| 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, |
| }; |
| |
| glUniformMatrix4fv(matrixLocation, 1, false, identity); |
| glUniform2fv(colorALocation, 4, colorA); |
| glUniform4f(colorBLocation, 0.2f, 0.2f, 0.2f, 0.2f); |
| glUniform1f(alphaLocation, 0.8f); |
| glUniform4f(multiplierLocation, 0.5f, 0.5f, 0.5f, 0.5f); |
| glUniform3fv(colorCLocation, 8, colorC); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| drawQuad(mProgram, "a_position", 0.5f); |
| |
| EXPECT_PIXEL_EQ(0, 0, 204, 204, 204, 204); |
| } |
| |
| // Test that unused uniforms don't conflict when bound to the same location |
| TEST_P(BindUniformLocationTest, UnusedUniformUpdate) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| ASSERT_NE(nullptr, glBindUniformLocationCHROMIUM); |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform vec4 u_colorA; |
| uniform float u_colorU; |
| uniform vec4 u_colorC; |
| void main() |
| { |
| gl_FragColor = u_colorA + u_colorC; |
| })"; |
| |
| const GLint colorULocation = 1; |
| const GLint nonexistingLocation = 5; |
| const GLint unboundLocation = 6; |
| |
| mProgram = CompileProgram(essl1_shaders::vs::Simple(), kFS, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, colorULocation, "u_colorU"); |
| // The non-existing uniform should behave like existing, but optimized away |
| // uniform. |
| glBindUniformLocationCHROMIUM(program, nonexistingLocation, "nonexisting"); |
| // Let A and C be assigned automatic locations. |
| }); |
| ASSERT_NE(0u, mProgram); |
| |
| glUseProgram(mProgram); |
| |
| // No errors on bound locations, since caller does not know |
| // if the driver optimizes them away or not. |
| glUniform1f(colorULocation, 0.25f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // No errors on bound locations of names that do not exist |
| // in the shader. Otherwise it would be inconsistent wrt the |
| // optimization case. |
| glUniform1f(nonexistingLocation, 0.25f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // The above are equal to updating -1. |
| glUniform1f(-1, 0.25f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // No errors when updating with other type either. |
| // The type can not be known with the non-existing case. |
| glUniform2f(colorULocation, 0.25f, 0.25f); |
| EXPECT_GL_NO_ERROR(); |
| glUniform2f(nonexistingLocation, 0.25f, 0.25f); |
| EXPECT_GL_NO_ERROR(); |
| glUniform2f(-1, 0.25f, 0.25f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Ensure that driver or ANGLE has optimized the variable |
| // away and the test tests what it is supposed to. |
| EXPECT_EQ(-1, glGetUniformLocation(mProgram, "u_colorU")); |
| |
| // The bound location gets marked as used and the driver |
| // does not allocate other variables to that location. |
| EXPECT_NE(colorULocation, glGetUniformLocation(mProgram, "u_colorA")); |
| EXPECT_NE(colorULocation, glGetUniformLocation(mProgram, "u_colorC")); |
| EXPECT_NE(nonexistingLocation, glGetUniformLocation(mProgram, "u_colorA")); |
| EXPECT_NE(nonexistingLocation, glGetUniformLocation(mProgram, "u_colorC")); |
| |
| // Unintuitive: while specifying value works, getting the value does not. |
| GLfloat getResult = 0.0f; |
| glGetUniformfv(mProgram, colorULocation, &getResult); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| glGetUniformfv(mProgram, nonexistingLocation, &getResult); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| glGetUniformfv(mProgram, -1, &getResult); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // Updating an unbound, non-existing location still causes |
| // an error. |
| glUniform1f(unboundLocation, 0.25f); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // Test for a bug where using a sampler caused GL error if the mProgram had |
| // uniforms that were optimized away by the driver. This was only a problem with |
| // glBindUniformLocationCHROMIUM implementation. This could be reproed by |
| // binding the sampler to a location higher than the amount of active uniforms. |
| TEST_P(BindUniformLocationTest, UseSamplerWhenUnusedUniforms) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| R"(uniform sampler2D tex; |
| void main() |
| { |
| gl_FragColor = texture2D(tex, vec2(1)); |
| })"; |
| |
| const GLuint texLocation = 54; |
| |
| mProgram = CompileProgram(essl1_shaders::vs::Simple(), kFS, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, texLocation, "tex"); |
| }); |
| ASSERT_NE(0u, mProgram); |
| |
| glUseProgram(mProgram); |
| glUniform1i(texLocation, 0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test for binding a statically used uniform to the same location as a non-statically used uniform. |
| // This is valid according to the extension spec. |
| TEST_P(BindUniformLocationTest, SameLocationForUsedAndUnusedUniform) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| R"(precision mediump float; |
| uniform vec4 a; |
| uniform vec4 b; |
| void main() |
| { |
| gl_FragColor = a; |
| })"; |
| |
| const GLuint location = 54; |
| |
| mProgram = CompileProgram(essl1_shaders::vs::Zero(), kFS, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, location, "a"); |
| glBindUniformLocationCHROMIUM(program, location, "b"); |
| }); |
| ASSERT_NE(0u, mProgram); |
| |
| glUseProgram(mProgram); |
| glUniform4f(location, 0.0, 1.0, 0.0, 1.0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| class BindUniformLocationES31Test : public BindUniformLocationTest |
| { |
| protected: |
| BindUniformLocationES31Test() : BindUniformLocationTest() {} |
| |
| void linkProgramWithUniformLocation(const char *vs, |
| const char *fs, |
| const char *uniformName, |
| GLint uniformLocation) |
| { |
| mProgram = CompileProgram(vs, fs, [&](GLuint program) { |
| glBindUniformLocationCHROMIUM(program, uniformLocation, uniformName); |
| }); |
| } |
| }; |
| |
| // Test for when the shader specifies an explicit uniform location with a layout qualifier and the |
| // bindUniformLocation API sets a consistent location. |
| TEST_P(BindUniformLocationES31Test, ConsistentWithLocationLayoutQualifier) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "uniform layout(location=2) highp sampler2D tex;\n" |
| "out highp vec4 my_FragColor;\n" |
| "void main()\n" |
| "{\n" |
| " my_FragColor = texture(tex, vec2(1));\n" |
| "}\n"; |
| |
| const GLuint texLocation = 2; |
| |
| linkProgramWithUniformLocation(essl31_shaders::vs::Zero(), kFS, "tex", texLocation); |
| |
| GLint linked = GL_FALSE; |
| glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); |
| ASSERT_GL_TRUE(linked); |
| |
| EXPECT_EQ(static_cast<GLint>(texLocation), glGetUniformLocation(mProgram, "tex")); |
| glUseProgram(mProgram); |
| glUniform1i(texLocation, 0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test for when the shader specifies an explicit uniform location with a layout qualifier and the |
| // bindUniformLocation API sets a conflicting location for the same variable. The shader-set |
| // location should prevail. |
| TEST_P(BindUniformLocationES31Test, LocationLayoutQualifierOverridesAPIBinding) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "uniform layout(location=2) highp sampler2D tex;\n" |
| "out highp vec4 my_FragColor;\n" |
| "void main()\n" |
| "{\n" |
| " my_FragColor = texture(tex, vec2(1));\n" |
| "}\n"; |
| |
| const GLuint shaderTexLocation = 2; |
| const GLuint texLocation = 3; |
| |
| linkProgramWithUniformLocation(essl31_shaders::vs::Zero(), kFS, "tex", texLocation); |
| |
| GLint linked = GL_FALSE; |
| glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); |
| ASSERT_GL_TRUE(linked); |
| |
| EXPECT_EQ(static_cast<GLint>(shaderTexLocation), glGetUniformLocation(mProgram, "tex")); |
| glUseProgram(mProgram); |
| glUniform1i(shaderTexLocation, 1); |
| EXPECT_GL_NO_ERROR(); |
| glUniform1i(texLocation, 2); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test for when the shader specifies an explicit uniform location with a layout qualifier and the |
| // bindUniformLocation API sets a conflicting location for a different variable. Linking should |
| // fail. |
| TEST_P(BindUniformLocationES31Test, LocationLayoutQualifierConflictsWithAPIBinding) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "uniform layout(location=2) highp sampler2D tex;\n" |
| "uniform highp sampler2D tex2;\n" |
| "out highp vec4 my_FragColor;\n" |
| "void main()\n" |
| "{\n" |
| " my_FragColor = texture(tex2, vec2(1));\n" |
| "}\n"; |
| |
| const GLuint tex2Location = 2; |
| |
| linkProgramWithUniformLocation(essl31_shaders::vs::Zero(), kFS, "tex2", tex2Location); |
| |
| GLint linked = GL_FALSE; |
| glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); |
| ASSERT_GL_FALSE(linked); |
| } |
| |
| // Test for binding a location for an array of arrays uniform. |
| TEST_P(BindUniformLocationES31Test, ArrayOfArrays) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_bind_uniform_location")); |
| |
| constexpr char kFS[] = |
| R"(#version 310 es |
| precision highp float; |
| uniform vec4 sourceColor[2][1]; |
| out highp vec4 my_FragColor; |
| void main() |
| { |
| my_FragColor = sourceColor[1][0]; |
| })"; |
| |
| const GLuint location = 8; |
| |
| linkProgramWithUniformLocation(essl31_shaders::vs::Simple(), kFS, "sourceColor[1]", location); |
| |
| GLint linked = GL_FALSE; |
| glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); |
| ASSERT_GL_TRUE(linked); |
| |
| glUseProgram(mProgram); |
| glUniform4f(location, 0.0f, 1.0f, 0.0f, 1.0f); |
| |
| drawQuad(mProgram, essl31_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| ANGLE_INSTANTIATE_TEST_ES2(BindUniformLocationTest); |
| |
| ANGLE_INSTANTIATE_TEST_ES31(BindUniformLocationES31Test); |
| |
| } // namespace |