blob: 1167c717d6d291ebdfef31d07071e906927bf0f9 [file] [log] [blame] [edit]
//
// Copyright 2017 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.
//
// ProgramPipelineTest:
// Various tests related to Program Pipeline.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
class ProgramPipelineTest : public ANGLETest
{
protected:
ProgramPipelineTest()
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
// Verify that program pipeline is not supported in version lower than ES31.
TEST_P(ProgramPipelineTest, GenerateProgramPipelineObject)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
GLuint pipeline;
glGenProgramPipelines(1, &pipeline);
if (getClientMajorVersion() < 3 || getClientMinorVersion() < 1)
{
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
EXPECT_GL_NO_ERROR();
glDeleteProgramPipelines(1, &pipeline);
EXPECT_GL_NO_ERROR();
}
}
class ProgramPipelineTest31 : public ProgramPipelineTest
{
protected:
~ProgramPipelineTest31()
{
glDeleteProgram(mVertProg);
glDeleteProgram(mFragProg);
glDeleteProgramPipelines(1, &mPipeline);
}
void bindProgramPipeline(const GLchar *vertString, const GLchar *fragString);
void drawQuad(const std::string &positionAttribName,
const GLfloat positionAttribZ,
const GLfloat positionAttribXYScale);
GLuint mVertProg;
GLuint mFragProg;
GLuint mPipeline;
};
void ProgramPipelineTest31::bindProgramPipeline(const GLchar *vertString, const GLchar *fragString)
{
mVertProg = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vertString);
ASSERT_NE(mVertProg, 0u);
mFragProg = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragString);
ASSERT_NE(mFragProg, 0u);
// Generate a program pipeline and attach the programs to their respective stages
glGenProgramPipelines(1, &mPipeline);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(mPipeline);
EXPECT_GL_NO_ERROR();
}
// Test generate or delete program pipeline.
TEST_P(ProgramPipelineTest31, GenOrDeleteProgramPipelineTest)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
GLuint pipeline;
glGenProgramPipelines(-1, &pipeline);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glGenProgramPipelines(0, &pipeline);
EXPECT_GL_NO_ERROR();
glDeleteProgramPipelines(-1, &pipeline);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glDeleteProgramPipelines(0, &pipeline);
EXPECT_GL_NO_ERROR();
}
// Test BindProgramPipeline.
TEST_P(ProgramPipelineTest31, BindProgramPipelineTest)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
glBindProgramPipeline(0);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(2);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
GLuint pipeline;
glGenProgramPipelines(1, &pipeline);
glBindProgramPipeline(pipeline);
EXPECT_GL_NO_ERROR();
glDeleteProgramPipelines(1, &pipeline);
glBindProgramPipeline(pipeline);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test IsProgramPipeline
TEST_P(ProgramPipelineTest31, IsProgramPipelineTest)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
EXPECT_GL_FALSE(glIsProgramPipeline(0));
EXPECT_GL_NO_ERROR();
EXPECT_GL_FALSE(glIsProgramPipeline(2));
EXPECT_GL_NO_ERROR();
GLuint pipeline;
glGenProgramPipelines(1, &pipeline);
glBindProgramPipeline(pipeline);
EXPECT_GL_TRUE(glIsProgramPipeline(pipeline));
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(0);
glDeleteProgramPipelines(1, &pipeline);
EXPECT_GL_FALSE(glIsProgramPipeline(pipeline));
EXPECT_GL_NO_ERROR();
}
// Simulates a call to glCreateShaderProgramv()
GLuint createShaderProgram(GLenum type, const GLchar *shaderString)
{
GLShader shader(type);
if (!shader.get())
{
return 0;
}
glShaderSource(shader, 1, &shaderString, nullptr);
glCompileShader(shader);
GLuint program = glCreateProgram();
if (program)
{
GLint compiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
if (compiled)
{
glAttachShader(program, shader);
glLinkProgram(program);
glDetachShader(program, shader);
}
}
EXPECT_GL_NO_ERROR();
return program;
}
void ProgramPipelineTest31::drawQuad(const std::string &positionAttribName,
const GLfloat positionAttribZ,
const GLfloat positionAttribXYScale)
{
glUseProgram(0);
std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.x() *= positionAttribXYScale;
vertex.y() *= positionAttribXYScale;
vertex.z() = positionAttribZ;
}
GLint positionLocation = glGetAttribLocation(mVertProg, positionAttribName.c_str());
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
}
// Test glUseProgramStages
TEST_P(ProgramPipelineTest31, UseProgramStages)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
const GLchar *vertString = essl31_shaders::vs::Simple();
const GLchar *fragString = essl31_shaders::fs::Red();
mVertProg = createShaderProgram(GL_VERTEX_SHADER, vertString);
ASSERT_NE(mVertProg, 0u);
mFragProg = createShaderProgram(GL_FRAGMENT_SHADER, fragString);
ASSERT_NE(mFragProg, 0u);
// Generate a program pipeline and attach the programs to their respective stages
GLuint pipeline;
glGenProgramPipelines(1, &pipeline);
EXPECT_GL_NO_ERROR();
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, mVertProg);
EXPECT_GL_NO_ERROR();
glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(pipeline);
EXPECT_GL_NO_ERROR();
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test glUseProgramStages
TEST_P(ProgramPipelineTest31, UseCreateShaderProgramv)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
const GLchar *vertString = essl31_shaders::vs::Simple();
const GLchar *fragString = essl31_shaders::fs::Red();
bindProgramPipeline(vertString, fragString);
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test glUniform
TEST_P(ProgramPipelineTest31, FragmentStageUniformTest)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
const GLchar *vertString = essl31_shaders::vs::Simple();
const GLchar *fragString = R"(#version 310 es
precision highp float;
uniform float redColorIn;
uniform float greenColorIn;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(redColorIn, greenColorIn, 0.0, 1.0);
})";
bindProgramPipeline(vertString, fragString);
// Set the output color to yellow
GLint location = glGetUniformLocation(mFragProg, "redColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 1.0);
location = glGetUniformLocation(mFragProg, "greenColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 1.0);
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
// Set the output color to red
location = glGetUniformLocation(mFragProg, "redColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 1.0);
location = glGetUniformLocation(mFragProg, "greenColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 0.0);
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glDeleteProgram(mVertProg);
glDeleteProgram(mFragProg);
}
// Test varyings
TEST_P(ProgramPipelineTest31, ProgramPipelineVaryings)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
const GLchar *vertString = essl31_shaders::vs::Passthrough();
const GLchar *fragString = R"(#version 310 es
precision highp float;
in vec4 v_position;
out vec4 my_FragColor;
void main()
{
my_FragColor = round(v_position);
})";
bindProgramPipeline(vertString, fragString);
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
int w = getWindowWidth() - 2;
int h = getWindowHeight() - 2;
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
}
// Creates a program pipeline with a 2D texture and renders with it.
TEST_P(ProgramPipelineTest31, DrawWith2DTexture)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
const GLchar *vertString = R"(#version 310 es
precision highp float;
in vec4 a_position;
out vec2 texCoord;
void main()
{
gl_Position = a_position;
texCoord = vec2(a_position.x, a_position.y) * 0.5 + vec2(0.5);
})";
const GLchar *fragString = R"(#version 310 es
precision highp float;
in vec2 texCoord;
uniform sampler2D tex;
out vec4 my_FragColor;
void main()
{
my_FragColor = texture(tex, texCoord);
})";
std::array<GLColor, 4> colors = {
{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
bindProgramPipeline(vertString, fragString);
ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
int w = getWindowWidth() - 2;
int h = getWindowHeight() - 2;
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
}
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ProgramPipelineTest);
ANGLE_INSTANTIATE_TEST_ES31(ProgramPipelineTest31);
} // namespace