// // 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 #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 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);