blob: daa555ce4c08ac7cf1f914687131e465c54276ba [file] [log] [blame]
// Copyright (c) 2012 The Chromium 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 <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES2/gl2extchromium.h>
#include <stdint.h>
#include "gpu/command_buffer/tests/gl_manager.h"
#include "gpu/command_buffer/tests/gl_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#define SHADER(Src) #Src
namespace gpu {
class BindUniformLocationTest : public testing::TestWithParam<bool> {
protected:
static const GLsizei kResolution = 4;
void SetUp() override {
GLManager::Options options;
options.size = gfx::Size(kResolution, kResolution);
options.force_shader_name_hashing = GetParam();
gl_.Initialize(options);
}
void TearDown() override { gl_.Destroy(); }
GLManager gl_;
};
TEST_P(BindUniformLocationTest, Basic) {
ASSERT_TRUE(
GLTestHelper::HasExtension("GL_CHROMIUM_bind_uniform_location"));
static const char* v_shader_str = SHADER(
attribute vec4 a_position;
void main()
{
gl_Position = a_position;
}
);
static const char* f_shader_str = SHADER(
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 color_a_location = 3;
GLint color_b_location = 10;
GLint color_c_location = 5;
GLuint vertex_shader = GLTestHelper::LoadShader(
GL_VERTEX_SHADER, v_shader_str);
GLuint fragment_shader = GLTestHelper::LoadShader(
GL_FRAGMENT_SHADER, f_shader_str);
GLuint program = glCreateProgram();
glBindUniformLocationCHROMIUM(program, color_a_location, "u_colorA");
glBindUniformLocationCHROMIUM(program, color_b_location, "u_colorB[0]");
glBindUniformLocationCHROMIUM(program, color_c_location, "u_colorC");
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
// Link the program
glLinkProgram(program);
// Check the link status
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
EXPECT_EQ(1, linked);
GLint position_loc = glGetAttribLocation(program, "a_position");
GLTestHelper::SetupUnitQuad(position_loc);
glUseProgram(program);
static const float color_b[] = {
0.0f, 0.50f, 0.0f, 0.0f,
0.0f, 0.0f, 0.75f, 0.0f,
};
glUniform4f(color_a_location, 0.25f, 0.0f, 0.0f, 0.0f);
glUniform4fv(color_b_location, 2, color_b);
glUniform4f(color_c_location, 0.0f, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, 6);
static const uint8_t expected[] = {64, 128, 192, 255};
EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kResolution, kResolution, 1,
expected, nullptr));
GLTestHelper::CheckGLError("no errors", __LINE__);
}
TEST_P(BindUniformLocationTest, ConflictsDetection) {
ASSERT_TRUE(
GLTestHelper::HasExtension("GL_CHROMIUM_bind_uniform_location"));
static const char* v_shader_str = SHADER(
attribute vec4 a_position;
void main()
{
gl_Position = a_position;
}
);
static const char* f_shader_str = SHADER(
precision mediump float;
uniform vec4 u_colorA;
uniform vec4 u_colorB;
void main()
{
gl_FragColor = u_colorA + u_colorB;
}
);
GLint color_a_location = 3;
GLint color_b_location = 4;
GLuint vertex_shader = GLTestHelper::LoadShader(
GL_VERTEX_SHADER, v_shader_str);
GLuint fragment_shader = GLTestHelper::LoadShader(
GL_FRAGMENT_SHADER, f_shader_str);
GLuint program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glBindUniformLocationCHROMIUM(program, color_a_location, "u_colorA");
// Bind u_colorB to location a, causing conflicts, link should fail.
glBindUniformLocationCHROMIUM(program, color_a_location, "u_colorB");
glLinkProgram(program);
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
EXPECT_EQ(0, linked);
// Bind u_colorB to location b, no conflicts, link should succeed.
glBindUniformLocationCHROMIUM(program, color_b_location, "u_colorB");
glLinkProgram(program);
linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
EXPECT_EQ(1, linked);
GLTestHelper::CheckGLError("no errors", __LINE__);
}
TEST_P(BindUniformLocationTest, Compositor) {
ASSERT_TRUE(
GLTestHelper::HasExtension("GL_CHROMIUM_bind_uniform_location"));
static const char* v_shader_str = SHADER(
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;
}
);
static const char* f_shader_str = SHADER(
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 matrix_location = counter++;
int color_a_location = counter++;
int color_b_location = counter++;
int alpha_location = counter++;
int multiplier_location = counter++;
int color_c_location = counter++;
GLuint vertex_shader = GLTestHelper::LoadShader(
GL_VERTEX_SHADER, v_shader_str);
GLuint fragment_shader = GLTestHelper::LoadShader(
GL_FRAGMENT_SHADER, f_shader_str);
GLuint program = glCreateProgram();
glBindUniformLocationCHROMIUM(program, matrix_location, "matrix");
glBindUniformLocationCHROMIUM(program, color_a_location, "color_a");
glBindUniformLocationCHROMIUM(program, color_b_location, "color_b");
glBindUniformLocationCHROMIUM(program, alpha_location, "alpha");
glBindUniformLocationCHROMIUM(program, multiplier_location, "multiplier");
glBindUniformLocationCHROMIUM(program, color_c_location, "color_c");
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
// Link the program
glLinkProgram(program);
// Check the link status
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
EXPECT_EQ(1, linked);
GLint position_loc = glGetAttribLocation(program, "a_position");
GLTestHelper::SetupUnitQuad(position_loc);
glUseProgram(program);
static const float color_a[] = {
0.1f, 0.1f, 0.1f, 0.1f,
0.1f, 0.1f, 0.1f, 0.1f,
};
static const float color_c[] = {
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(matrix_location, 1, false, identity);
glUniform2fv(color_a_location, 4, color_a);
glUniform4f(color_b_location, 0.2f, 0.2f, 0.2f, 0.2f);
glUniform1f(alpha_location, 0.8f);
glUniform4f(multiplier_location, 0.5f, 0.5f, 0.5f, 0.5f);
glUniform3fv(color_c_location, 8, color_c);
glDrawArrays(GL_TRIANGLES, 0, 6);
static const uint8_t expected[] = {204, 204, 204, 204};
EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kResolution, kResolution, 1,
expected, nullptr));
GLTestHelper::CheckGLError("no errors", __LINE__);
}
TEST_P(BindUniformLocationTest, UnusedUniformUpdate) {
ASSERT_TRUE(GLTestHelper::HasExtension("GL_CHROMIUM_bind_uniform_location"));
// clang-format off
static const char* kVertexShaderString = SHADER(
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
);
static const char* kFragmentShaderString = SHADER(
precision mediump float;
uniform vec4 u_colorA;
uniform float u_colorU;
uniform vec4 u_colorC;
void main() {
gl_FragColor = u_colorA + u_colorC;
}
);
// clang-format on
const GLint kColorULocation = 1;
const GLint kNonexistingLocation = 5;
const GLint kUnboundLocation = 6;
GLuint vertex_shader =
GLTestHelper::LoadShader(GL_VERTEX_SHADER, kVertexShaderString);
GLuint fragment_shader =
GLTestHelper::LoadShader(GL_FRAGMENT_SHADER, kFragmentShaderString);
GLuint program = glCreateProgram();
glBindUniformLocationCHROMIUM(program, kColorULocation, "u_colorU");
// The non-existing uniform should behave like existing, but optimized away
// uniform.
glBindUniformLocationCHROMIUM(program, kNonexistingLocation, "nonexisting");
// Let A and C be assigned automatic locations.
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
EXPECT_EQ(1, linked);
glUseProgram(program);
// No errors on bound locations, since caller does not know
// if the driver optimizes them away or not.
glUniform1f(kColorULocation, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
// No errors on bound locations of names that do not exist
// in the shader. Otherwise it would be inconsistent wrt the
// optimization case.
glUniform1f(kNonexistingLocation, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
// The above are equal to updating -1.
glUniform1f(-1, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
// No errors when updating with other type either.
// The type can not be known with the non-existing case.
glUniform2f(kColorULocation, 0.25f, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
glUniform2f(kNonexistingLocation, 0.25f, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
glUniform2f(-1, 0.25f, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
// Ensure that driver or ANGLE has optimized the variable
// away and the test tests what it is supposed to.
EXPECT_EQ(-1, glGetUniformLocation(program, "u_colorU"));
// The bound location gets marked as used and the driver
// does not allocate other variables to that location.
EXPECT_NE(kColorULocation, glGetUniformLocation(program, "u_colorA"));
EXPECT_NE(kColorULocation, glGetUniformLocation(program, "u_colorC"));
EXPECT_NE(kNonexistingLocation, glGetUniformLocation(program, "u_colorA"));
EXPECT_NE(kNonexistingLocation, glGetUniformLocation(program, "u_colorC"));
// Unintuitive: while specifying value works, getting the value does not.
GLfloat get_result = 0.0f;
glGetUniformfv(program, kColorULocation, &get_result);
EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
glGetUniformfv(program, kNonexistingLocation, &get_result);
EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
glGetUniformfv(program, -1, &get_result);
EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
// Updating an unbound, non-existing location still causes
// an error.
glUniform1f(kUnboundLocation, 0.25f);
EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
}
// Test for a bug where using a sampler caused GL error if the program 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) {
enum {
kTexLocation = 54
};
// clang-format off
static const char* vertexShaderString = SHADER(
void main() {
gl_Position = vec4(0);
}
);
static const char* fragmentShaderString = SHADER(
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, vec2(1));
}
);
// clang-format on
GLuint vs = GLTestHelper::CompileShader(GL_VERTEX_SHADER, vertexShaderString);
GLuint fs = GLTestHelper::CompileShader(GL_FRAGMENT_SHADER,
fragmentShaderString);
GLuint program = glCreateProgram();
glBindUniformLocationCHROMIUM(program, kTexLocation, "tex");
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
EXPECT_NE(0, linked);
glUseProgram(program);
glUniform1i(kTexLocation, 0);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
}
INSTANTIATE_TEST_SUITE_P(WithAndWithoutShaderNameMapping,
BindUniformLocationTest,
::testing::Bool());
} // namespace gpu