blob: 3ae70f412928261b26c57f143ddb439cf6b153b3 [file] [log] [blame]
//
// Copyright 2020 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.
//
// ContextNoErrorTest:
// Tests pertaining to GL_KHR_no_error
//
#include <gtest/gtest.h>
#include "common/platform.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
class ContextNoErrorTest : public ANGLETest<>
{
protected:
ContextNoErrorTest() : mNaughtyTexture(0) { setNoErrorEnabled(true); }
void testTearDown() override
{
if (mNaughtyTexture != 0)
{
glDeleteTextures(1, &mNaughtyTexture);
}
}
void bindNaughtyTexture()
{
glGenTextures(1, &mNaughtyTexture);
ASSERT_GL_NO_ERROR();
glBindTexture(GL_TEXTURE_CUBE_MAP, mNaughtyTexture);
ASSERT_GL_NO_ERROR();
// mNaughtyTexture should now be a GL_TEXTURE_CUBE_MAP texture, so rebinding it to
// GL_TEXTURE_2D is an error
glBindTexture(GL_TEXTURE_2D, mNaughtyTexture);
}
GLuint mNaughtyTexture = 0;
};
class ContextNoErrorTestES3 : public ContextNoErrorTest
{};
class ContextNoErrorPPOTest31 : public ContextNoErrorTest
{
protected:
void testTearDown() override
{
glDeleteProgram(mVertProg);
glDeleteProgram(mFragProg);
glDeleteProgramPipelines(1, &mPipeline);
}
void bindProgramPipeline(const GLchar *vertString, const GLchar *fragString);
void drawQuadWithPPO(const std::string &positionAttribName,
const GLfloat positionAttribZ,
const GLfloat positionAttribXYScale);
GLuint mVertProg = 0;
GLuint mFragProg = 0;
GLuint mPipeline = 0;
};
void ContextNoErrorPPOTest31::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();
}
void ContextNoErrorPPOTest31::drawQuadWithPPO(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);
}
// Tests that error reporting is suppressed when GL_KHR_no_error is enabled
TEST_P(ContextNoErrorTest, NoError)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
bindNaughtyTexture();
EXPECT_GL_NO_ERROR();
}
// Test glDetachShader to make sure it resolves linking with a no error context and doesn't assert
TEST_P(ContextNoErrorTest, DetachAfterLink)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
GLuint vs = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple());
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Red());
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glDetachShader(program, vs);
glDetachShader(program, fs);
glDeleteShader(vs);
glDeleteShader(fs);
glDeleteProgram(program);
EXPECT_GL_NO_ERROR();
}
// Tests that we can draw with a program pipeline when GL_KHR_no_error is enabled.
TEST_P(ContextNoErrorPPOTest31, DrawWithPPO)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
// Only the Vulkan backend supports PPOs
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);
drawQuadWithPPO("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test drawing with program and then with PPO to make sure it resolves linking of both the program
// and the PPO with a no error context.
TEST_P(ContextNoErrorPPOTest31, DrawWithProgramThenPPO)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
// Only the Vulkan backend supports PPOs
ANGLE_SKIP_TEST_IF(!IsVulkan());
ANGLE_GL_PROGRAM(simpleProgram, essl31_shaders::vs::Simple(), essl31_shaders::fs::Red());
ASSERT_NE(simpleProgram, 0u);
EXPECT_GL_NO_ERROR();
// 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::Green();
// Bind the PPO
bindProgramPipeline(vertString, fragString);
// Bind the program
glUseProgram(simpleProgram);
EXPECT_GL_NO_ERROR();
// Draw and expect red since program overrides PPO
drawQuad(simpleProgram, essl31_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
// Unbind the program
glUseProgram(0);
EXPECT_GL_NO_ERROR();
// Draw and expect green
drawQuadWithPPO("a_position", 0.5f, 1.0f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test glUseProgramStages with different programs
TEST_P(ContextNoErrorPPOTest31, UseProgramStagesWithDifferentPrograms)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
// Only the Vulkan backend supports PPOs
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 *fragString1 = 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);
})";
const GLchar *fragString2 = R"(#version 310 es
precision highp float;
uniform float greenColorIn;
uniform float blueColorIn;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(0.0, greenColorIn, blueColorIn, 1.0);
})";
bindProgramPipeline(vertString, fragString1);
// Set the output color to red
GLint location = glGetUniformLocation(mFragProg, "redColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 1.0);
location = glGetUniformLocation(mFragProg, "greenColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 0.0);
drawQuadWithPPO("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
GLuint fragProg;
fragProg = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragString2);
ASSERT_NE(fragProg, 0u);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, fragProg);
EXPECT_GL_NO_ERROR();
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
// Set the output color to blue
location = glGetUniformLocation(fragProg, "greenColorIn");
glActiveShaderProgram(mPipeline, fragProg);
glUniform1f(location, 0.0);
location = glGetUniformLocation(fragProg, "blueColorIn");
glActiveShaderProgram(mPipeline, fragProg);
glUniform1f(location, 1.0);
drawQuadWithPPO("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
EXPECT_GL_NO_ERROR();
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuadWithPPO("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glDeleteProgram(mVertProg);
glDeleteProgram(mFragProg);
glDeleteProgram(fragProg);
}
// Test glUseProgramStages with repeated calls to glUseProgramStages with the same programs.
TEST_P(ContextNoErrorPPOTest31, RepeatedCallToUseProgramStagesWithSamePrograms)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
// Only the Vulkan backend supports PPOs
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 red
GLint location = glGetUniformLocation(mFragProg, "redColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 1.0);
location = glGetUniformLocation(mFragProg, "greenColorIn");
glActiveShaderProgram(mPipeline, mFragProg);
glUniform1f(location, 0.0);
// These following calls to glUseProgramStages should not cause a re-link.
glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
EXPECT_GL_NO_ERROR();
drawQuadWithPPO("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glDeleteProgram(mVertProg);
glDeleteProgram(mFragProg);
}
// Tests that an incorrect enum to GetInteger does not cause an application crash.
TEST_P(ContextNoErrorTest, InvalidGetIntegerDoesNotCrash)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
GLint value = 1;
glGetIntegerv(GL_TEXTURE_2D, &value);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(value, 1);
}
// Test that we ignore an invalid texture type when EGL_KHR_create_context_no_error is enabled.
TEST_P(ContextNoErrorTest, InvalidTextureType)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
GLTexture texture;
constexpr GLenum kInvalidTextureType = 0;
glBindTexture(kInvalidTextureType, texture);
ASSERT_GL_NO_ERROR();
glTexParameteri(kInvalidTextureType, GL_TEXTURE_BASE_LEVEL, 0);
ASSERT_GL_NO_ERROR();
}
// Tests that we can draw with a program that is relinking when GL_KHR_no_error is enabled.
TEST_P(ContextNoErrorTestES3, DrawWithRelinkedProgram)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_no_error"));
int w = getWindowWidth();
int h = getWindowHeight();
glViewport(0, 0, w, h);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
constexpr char kVS[] = R"(#version 300 es
void main()
{
vec2 position = vec2(-1, -1);
if (gl_VertexID == 1)
position = vec2(3, -1);
else if (gl_VertexID == 2)
position = vec2(-1, 3);
gl_Position = vec4(position, 0, 1);
})";
GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS);
GLuint red = CompileShader(GL_FRAGMENT_SHADER, essl3_shaders::fs::Red());
GLuint bad = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Blue());
GLuint green = CompileShader(GL_FRAGMENT_SHADER, essl3_shaders::fs::Green());
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, red);
glLinkProgram(program);
// Use the program once; it's executable will be installed.
glUseProgram(program);
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, w / 4, h);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Make it fail compilation, the draw should continue to use the old executable
glDetachShader(program, red);
glAttachShader(program, bad);
glLinkProgram(program);
glScissor(w / 4, 0, w / 2 - w / 4, h);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Relink the program while it's bound. It should finish compiling before the following draw is
// attempted.
glDetachShader(program, bad);
glAttachShader(program, green);
glLinkProgram(program);
glScissor(w / 2, 0, w - w / 2, h);
glDrawArrays(GL_TRIANGLES, 0, 3);
EXPECT_PIXEL_RECT_EQ(0, 0, w / 2, h, GLColor::red);
EXPECT_PIXEL_RECT_EQ(w / 2, 0, w - w / 2, h, GLColor::green);
ASSERT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ContextNoErrorTest);
ANGLE_INSTANTIATE_TEST_ES3(ContextNoErrorTestES3);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextNoErrorPPOTest31);
ANGLE_INSTANTIATE_TEST_ES31(ContextNoErrorPPOTest31);