// // Copyright 2023 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. // // Test cases for GL_EXT_clip_control // These tests complement dEQP-GLES2.functional.clip_control.* // #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" #include "util/EGLWindow.h" #include "util/test_utils.h" using namespace angle; class ClipControlTest : public ANGLETest<> { protected: static const int w = 64; static const int h = 64; ClipControlTest() { setWindowWidth(w); setWindowHeight(h); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); setConfigDepthBits(24); setExtensionsEnabled(false); } }; // Test state queries and updates TEST_P(ClipControlTest, StateQuery) { // Queries with the extension disabled GLint clipOrigin = -1; glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); EXPECT_EQ(clipOrigin, -1); EXPECT_GL_ERROR(GL_INVALID_ENUM); GLint clipDepthMode = -1; glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_EQ(clipDepthMode, -1); EXPECT_GL_ERROR(GL_INVALID_ENUM); // Command with the extension disabled glClipControlEXT(GL_UPPER_LEFT_EXT, GL_ZERO_TO_ONE_EXT); EXPECT_GL_ERROR(GL_INVALID_OPERATION); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); ASSERT_GL_NO_ERROR(); // Default state with the extension enabled glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_GLENUM_EQ(GL_LOWER_LEFT_EXT, clipOrigin); EXPECT_GLENUM_EQ(GL_NEGATIVE_ONE_TO_ONE_EXT, clipDepthMode); ASSERT_GL_NO_ERROR(); // Check valid state updates glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_GLENUM_EQ(GL_LOWER_LEFT_EXT, clipOrigin); EXPECT_GLENUM_EQ(GL_NEGATIVE_ONE_TO_ONE_EXT, clipDepthMode); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT); glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_GLENUM_EQ(GL_LOWER_LEFT_EXT, clipOrigin); EXPECT_GLENUM_EQ(GL_ZERO_TO_ONE_EXT, clipDepthMode); glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_GLENUM_EQ(GL_UPPER_LEFT_EXT, clipOrigin); EXPECT_GLENUM_EQ(GL_NEGATIVE_ONE_TO_ONE_EXT, clipDepthMode); glClipControlEXT(GL_UPPER_LEFT_EXT, GL_ZERO_TO_ONE_EXT); glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_GLENUM_EQ(GL_UPPER_LEFT_EXT, clipOrigin); EXPECT_GLENUM_EQ(GL_ZERO_TO_ONE_EXT, clipDepthMode); ASSERT_GL_NO_ERROR(); // Check invalid state updates glClipControlEXT(GL_LOWER_LEFT_EXT, 0); EXPECT_GL_ERROR(GL_INVALID_ENUM); glClipControlEXT(0, GL_NEGATIVE_ONE_TO_ONE_EXT); EXPECT_GL_ERROR(GL_INVALID_ENUM); // Invalid command does not change the state glGetIntegerv(GL_CLIP_ORIGIN_EXT, &clipOrigin); glGetIntegerv(GL_CLIP_DEPTH_MODE_EXT, &clipDepthMode); EXPECT_GLENUM_EQ(GL_UPPER_LEFT_EXT, clipOrigin); EXPECT_GLENUM_EQ(GL_ZERO_TO_ONE_EXT, clipDepthMode); ASSERT_GL_NO_ERROR(); } // Test that clip origin does not affect scissored clears TEST_P(ClipControlTest, OriginScissorClear) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); auto test = [&](std::string name, bool useES3) { // Start with lower-left glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); // Make a draw call without color writes to sync the state glColorMask(false, false, false, false); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); glColorMask(true, true, true, true); // Clear to red glDisable(GL_SCISSOR_TEST); if (useES3) { float color[4] = {1.0, 0.0, 0.0, 1.0}; glClearBufferfv(GL_COLOR, 0, color); } else { glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); } // Flip the clip origin glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); // Make a draw call without color writes to sync the state glColorMask(false, false, false, false); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); glColorMask(true, true, true, true); // Clear lower half to green glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w, h / 2); if (useES3) { float color[4] = {0.0, 1.0, 0.0, 1.0}; glClearBufferfv(GL_COLOR, 0, color); } else { glClearColor(0.0, 1.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); } ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0.5 * w, 0.25 * h, GLColor::green) << name; EXPECT_PIXEL_COLOR_EQ(0.5 * w, 0.75 * h, GLColor::red) << name; }; test("Default framebuffer", getClientMajorVersion() > 2); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_OES_rgb8_rgba8")); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); GLFramebuffer fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); test("User framebuffer", getClientMajorVersion() > 2); } // Test that changing clip origin state does not affect location of scissor area TEST_P(ClipControlTest, OriginScissorDraw) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); constexpr char kVS[] = R"( attribute vec2 a_position; void main() { // Square at (0.25, 0.25) -> (0.75, 0.75) gl_Position = vec4(a_position * 0.25 + 0.5, 0.0, 1.0); })"; ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Blue()); glUseProgram(program); ASSERT_GL_NO_ERROR(); auto test = [&](std::string name) { glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glDisable(GL_SCISSOR_TEST); glClear(GL_COLOR_BUFFER_BIT); // Draw only to the lower half glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w, h / 2); // Draw blue quad in the upper-right part of the framebuffer; scissor test must fail drawQuad(program, "a_position", 0); ASSERT_GL_NO_ERROR(); // Switch the clip origin and draw again; scissor test must pass glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); drawQuad(program, "a_position", 0); ASSERT_GL_NO_ERROR(); // Reads are unaffected by clip origin EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.25 * h, GLColor::blue) << name; EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.75 * h, GLColor::transparentBlack) << name; }; test("Default framebuffer"); GLFramebuffer fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); test("User framebuffer"); } // Test that changing clip origin state does not affect copyTexImage TEST_P(ClipControlTest, OriginCopyTexImage) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); auto test = [&](std::string name) { glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); // Clear to red glDisable(GL_SCISSOR_TEST); glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); // Clear lower half-space to green glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w, h / 2); glClearColor(0.0, 1.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); // Switch clip origin state, it must have no effect on the next commands glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); GLTexture tex; glBindTexture(GL_TEXTURE_2D, tex); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, w / 4, h / 4, 0); ASSERT_GL_NO_ERROR(); GLFramebuffer readFb; glBindFramebuffer(GL_FRAMEBUFFER, readFb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); // Copied texture must contain values from the lower half-space EXPECT_PIXEL_COLOR_EQ(w / 8, h / 8, GLColor::green) << name; }; test("Default framebuffer"); GLFramebuffer fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); test("User framebuffer"); } // Test that changing clip origin state does not affect copyTexImage // with Luma format that may use draw calls internally TEST_P(ClipControlTest, OriginCopyTexImageLuma) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); // Clear to zero glClear(GL_COLOR_BUFFER_BIT); // Clear lower half-space to one glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w, h / 2); glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); // Switch clip origin state, it must have no effect on the next commands glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); GLTexture tex; glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0, w, h, 0); ASSERT_GL_NO_ERROR(); glClearColor(1.0, 0.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); // Draw the luma texture glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(w / 2, h * 1 / 4, GLColor::white); EXPECT_PIXEL_COLOR_EQ(w / 2, h * 3 / 4, GLColor::black); } // Test that clip origin does not affect gl_FragCoord TEST_P(ClipControlTest, OriginFragCoord) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); const char kFS[] = R"(precision mediump float; void main() { gl_FragColor = vec4(sign(gl_FragCoord.xy / 64.0 - 0.5), 0.0, 1.0); })"; ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); glClearColor(1.0, 0.0, 1.0, 1.0); for (GLenum origin : {GL_LOWER_LEFT_EXT, GL_UPPER_LEFT_EXT}) { glClipControlEXT(origin, GL_NEGATIVE_ONE_TO_ONE_EXT); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); EXPECT_PIXEL_COLOR_EQ(w - 1, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, h - 1, GLColor::green); EXPECT_PIXEL_COLOR_EQ(w - 1, h - 1, GLColor::yellow); GLFramebuffer fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); glClear(GL_COLOR_BUFFER_BIT); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); EXPECT_PIXEL_COLOR_EQ(w - 1, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, h - 1, GLColor::green); EXPECT_PIXEL_COLOR_EQ(w - 1, h - 1, GLColor::yellow); } } // Test that clip origin does not affect gl_PointCoord TEST_P(ClipControlTest, OriginPointCoord) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); float pointSizeRange[2] = {}; glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange); ANGLE_SKIP_TEST_IF(pointSizeRange[1] < 32); const char kVS[] = R"(precision mediump float; void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 32.0; })"; const char kFS[] = R"(precision mediump float; void main() { gl_FragColor = vec4(sign(gl_PointCoord.xy - 0.5), 0.0, 1.0); })"; ANGLE_GL_PROGRAM(program, kVS, kFS); glUseProgram(program); ASSERT_GL_NO_ERROR(); glClearColor(1.0, 0.0, 1.0, 1.0); for (GLenum origin : {GL_LOWER_LEFT_EXT, GL_UPPER_LEFT_EXT}) { glClipControlEXT(origin, GL_NEGATIVE_ONE_TO_ONE_EXT); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_POINTS, 0, 1); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(w / 2 - 15, h / 2 + 15, GLColor::black); EXPECT_PIXEL_COLOR_EQ(w / 2 + 15, h / 2 + 15, GLColor::red); EXPECT_PIXEL_COLOR_EQ(w / 2 - 15, h / 2 - 15, GLColor::green); EXPECT_PIXEL_COLOR_EQ(w / 2 + 15, h / 2 - 15, GLColor::yellow); GLFramebuffer fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_POINTS, 0, 1); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(w / 2 - 15, h / 2 + 15, GLColor::black); EXPECT_PIXEL_COLOR_EQ(w / 2 + 15, h / 2 + 15, GLColor::red); EXPECT_PIXEL_COLOR_EQ(w / 2 - 15, h / 2 - 15, GLColor::green); EXPECT_PIXEL_COLOR_EQ(w / 2 + 15, h / 2 - 15, GLColor::yellow); } } // Test that clip origin does not affect gl_FrontFacing TEST_P(ClipControlTest, OriginFrontFacing) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); const char kFS[] = R"(precision mediump float; void main() { gl_FragColor = vec4(gl_FrontFacing ? vec2(1.0, 0.0) : vec2(0.0, 1.0), 0.0, 1.0); })"; ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); for (GLenum origin : {GL_LOWER_LEFT_EXT, GL_UPPER_LEFT_EXT}) { glClipControlEXT(origin, GL_NEGATIVE_ONE_TO_ONE_EXT); glBindFramebuffer(GL_FRAMEBUFFER, 0); glFrontFace(GL_CCW); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); glFrontFace(GL_CW); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); GLFramebuffer fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); glFrontFace(GL_CCW); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); glFrontFace(GL_CW); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); } } // Test that clip origin does not affect readPixels TEST_P(ClipControlTest, OriginReadPixels) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); const char kFS[] = R"(precision mediump float; uniform float blue; varying vec4 v_position; void main() { gl_FragColor = (v_position.y > 0.0) ? vec4(1.0, 0.0, blue, 1.0) : vec4(0.0, 1.0, blue, 1.0); })"; ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), kFS); const GLint blueUniformLocation = glGetUniformLocation(program, "blue"); glUseProgram(program); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glUniform1f(blueUniformLocation, 0.0f); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_RECT_EQ(0, h / 2 + 2, w, h / 2 - 2, GLColor::red); EXPECT_PIXEL_RECT_EQ(0, 0, w, h / 2 - 2, GLColor::green); // Update clip origin and make a draw call that fails the depth test to // ensure that the backend is synced while the framebuffer is unchanged. glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glUniform1f(blueUniformLocation, 1.0f); drawQuad(program, essl1_shaders::PositionAttrib(), 1.0); // Check that the second draw call has failed the depth test and // reading from the framebuffer returns the same values as before. EXPECT_PIXEL_RECT_EQ(0, h / 2 + 2, w, h / 2 - 2, GLColor::red); EXPECT_PIXEL_RECT_EQ(0, 0, w, h / 2 - 2, GLColor::green); } // Test that changing only the clip depth mode syncs the state correctly TEST_P(ClipControlTest, DepthModeSimple) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); const GLint colorUniformLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform()); glUseProgram(program); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glUniform4fv(colorUniformLocation, 1, GLColor::red.toNormalizedVector().data()); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT); glUniform4fv(colorUniformLocation, 1, GLColor::green.toNormalizedVector().data()); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); } // Test that gl_FragCoord.z has expected values for ZERO_TO_ONE clip depth mode TEST_P(ClipControlTest, DepthFragCoord) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); const char kFS[] = R"(precision mediump float; void main() { gl_FragColor = vec4(gl_FragCoord.z, 0, 0, 1); })"; ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT); ASSERT_GL_NO_ERROR(); drawQuad(program, essl1_shaders::PositionAttrib(), 1.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5); EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(127, 0, 0, 255), 1); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); } class ClipControlTestES3 : public ClipControlTest {}; // Test that clip origin state does not affect framebuffer blits TEST_P(ClipControlTestES3, OriginBlit) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_clip_control")); constexpr char kFS[] = R"( precision mediump float; varying vec4 v_position; void main() { gl_FragColor = vec4(sign(v_position.xy), 0.0, 1.0); })"; ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), kFS); glUseProgram(program); ASSERT_GL_NO_ERROR(); // Blit default to custom { GLFramebuffer fb; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); drawQuad(program, "a_position", 0); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); EXPECT_PIXEL_COLOR_EQ(0.25 * w, 0.25 * h, GLColor::black); EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.25 * h, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0.25 * w, 0.75 * h, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.75 * h, GLColor::yellow); } // Blit custom to default { GLFramebuffer fb; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); GLRenderbuffer rb; glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); drawQuad(program, "a_position", 0); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); EXPECT_PIXEL_COLOR_EQ(0.25 * w, 0.25 * h, GLColor::black); EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.25 * h, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0.25 * w, 0.75 * h, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.75 * h, GLColor::yellow); } // Blit custom to custom { GLFramebuffer fb1; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb1); GLRenderbuffer rb1; glBindRenderbuffer(GL_RENDERBUFFER, rb1); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb1); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); GLFramebuffer fb2; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb2); GLRenderbuffer rb2; glBindRenderbuffer(GL_RENDERBUFFER, rb2); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb2); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb1); glClipControlEXT(GL_LOWER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); drawQuad(program, "a_position", 0); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_READ_FRAMEBUFFER, fb1); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb2); glClipControlEXT(GL_UPPER_LEFT_EXT, GL_NEGATIVE_ONE_TO_ONE_EXT); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_READ_FRAMEBUFFER, fb2); EXPECT_PIXEL_COLOR_EQ(0.25 * w, 0.25 * h, GLColor::black); EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.25 * h, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0.25 * w, 0.75 * h, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0.75 * w, 0.75 * h, GLColor::yellow); } } ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(ClipControlTest, ES2_OPENGLES().enable(Feature::EmulateClipOrigin), ES3_OPENGLES().enable(Feature::EmulateClipOrigin)); ANGLE_INSTANTIATE_TEST_ES3_AND(ClipControlTestES3, ES3_OPENGLES().enable(Feature::EmulateClipOrigin));