blob: 9c644939e47183f36abd8e6f0bb73a0ffd1a64dc [file] [log] [blame]
//
// 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()));