gles2: Add tests for EXT_clip_control
authorGert Wollny <gert.wollny@collabora.com>
Wed, 15 May 2019 10:12:06 +0000 (12:12 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Thu, 27 Jun 2019 12:16:08 +0000 (08:16 -0400)
These teste are a port of gl4cClipControlTests.cpp/hpp to GLES2.

Apart from requiring EXT_clip_control the depth range tests also make
use of functionality from NV_read_depth.

v2: - Fix include protector
    - test NV_read_depth in init function instead of using the constructor
    - squash commits into one (alegal-arm)

v3: - Fix copy-paste error (Tapani Pälli)
    - Update license year

v4: Add new tests to Khronos test list (Alexandros Galazin)

v5: Correct GLES version in comment (Alexander Galazin)

v6: - Fix comment for header guard
    - Replace use of NV_read_depth by a resolve draw of the depth buffer
      to a RGBA surface (James Glanville)

Change-Id: Ib6610552798b5a48445f4e2d490c72421460d4cf
Signed-off-by: Gert Wollny <gert.wollny@collabora.com>
AndroidGen.mk
android/cts/master/gles2-master.txt
external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles2-master.txt
external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles2-master.txt
framework/opengl/wrapper/glwEnums.inl
framework/opengl/wrapper/glwInitExtES.inl
modules/gles2/functional/CMakeLists.txt
modules/gles2/functional/es2fClipControlTests.cpp [new file with mode: 0644]
modules/gles2/functional/es2fClipControlTests.hpp [new file with mode: 0644]
modules/gles2/functional/es2fFunctionalTests.cpp
scripts/opengl/src_util.py

index 642e332..31e8504 100644 (file)
@@ -698,6 +698,7 @@ LOCAL_SRC_FILES := \
        modules/gles2/functional/es2fBufferObjectQueryTests.cpp \
        modules/gles2/functional/es2fBufferTestUtil.cpp \
        modules/gles2/functional/es2fBufferWriteTests.cpp \
+       modules/gles2/functional/es2fClipControlTests.cpp \
        modules/gles2/functional/es2fClippingTests.cpp \
        modules/gles2/functional/es2fColorClearTest.cpp \
        modules/gles2/functional/es2fDebugMarkerTests.cpp \
index 5cbd5dc..15b5b91 100644 (file)
@@ -23,6 +23,14 @@ dEQP-GLES2.functional.implementation_limits.max_vertex_texture_image_units
 dEQP-GLES2.functional.implementation_limits.max_texture_image_units
 dEQP-GLES2.functional.implementation_limits.max_fragment_uniform_vectors
 dEQP-GLES2.functional.implementation_limits.max_renderbuffer_size
+dEQP-GLES2.functional.clip_control.initial
+dEQP-GLES2.functional.clip_control.modify_get
+dEQP-GLES2.functional.clip_control.errors
+dEQP-GLES2.functional.clip_control.origin
+dEQP-GLES2.functional.clip_control.depth_mode_zero_to_one
+dEQP-GLES2.functional.clip_control.depth_mode_one_to_one
+dEQP-GLES2.functional.clip_control.face_culling
+dEQP-GLES2.functional.clip_control.viewport_bounds
 dEQP-GLES2.functional.color_clear.single_rgb
 dEQP-GLES2.functional.color_clear.single_rgba
 dEQP-GLES2.functional.color_clear.multiple_rgb
index 467f87a..e49fc73 100644 (file)
@@ -23,6 +23,14 @@ dEQP-GLES2.functional.implementation_limits.max_vertex_texture_image_units
 dEQP-GLES2.functional.implementation_limits.max_texture_image_units
 dEQP-GLES2.functional.implementation_limits.max_fragment_uniform_vectors
 dEQP-GLES2.functional.implementation_limits.max_renderbuffer_size
+dEQP-GLES2.functional.clip_control.initial
+dEQP-GLES2.functional.clip_control.modify_get
+dEQP-GLES2.functional.clip_control.errors
+dEQP-GLES2.functional.clip_control.origin
+dEQP-GLES2.functional.clip_control.depth_mode_zero_to_one
+dEQP-GLES2.functional.clip_control.depth_mode_one_to_one
+dEQP-GLES2.functional.clip_control.face_culling
+dEQP-GLES2.functional.clip_control.viewport_bounds
 dEQP-GLES2.functional.color_clear.single_rgb
 dEQP-GLES2.functional.color_clear.single_rgba
 dEQP-GLES2.functional.color_clear.multiple_rgb
index f59d7d6..164c179 100644 (file)
@@ -23,6 +23,14 @@ dEQP-GLES2.functional.implementation_limits.max_vertex_texture_image_units
 dEQP-GLES2.functional.implementation_limits.max_texture_image_units
 dEQP-GLES2.functional.implementation_limits.max_fragment_uniform_vectors
 dEQP-GLES2.functional.implementation_limits.max_renderbuffer_size
+dEQP-GLES2.functional.clip_control.initial
+dEQP-GLES2.functional.clip_control.modify_get
+dEQP-GLES2.functional.clip_control.errors
+dEQP-GLES2.functional.clip_control.origin
+dEQP-GLES2.functional.clip_control.depth_mode_zero_to_one
+dEQP-GLES2.functional.clip_control.depth_mode_one_to_one
+dEQP-GLES2.functional.clip_control.face_culling
+dEQP-GLES2.functional.clip_control.viewport_bounds
 dEQP-GLES2.functional.color_clear.single_rgb
 dEQP-GLES2.functional.color_clear.single_rgba
 dEQP-GLES2.functional.color_clear.multiple_rgb
index 1498559..66096d2 100644 (file)
 #define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING                                                   0x8C8F
 #define GL_POINT_SPRITE_COORD_ORIGIN                                                                   0x8CA0
 #define GL_LOWER_LEFT                                                                                                  0x8CA1
+#define GL_LOWER_LEFT_EXT                                                                                              0x8CA1
 #define GL_UPPER_LEFT                                                                                                  0x8CA2
+#define GL_UPPER_LEFT_EXT                                                                                              0x8CA2
 #define GL_STENCIL_BACK_REF                                                                                            0x8CA3
 #define GL_STENCIL_BACK_VALUE_MASK                                                                             0x8CA4
 #define GL_STENCIL_BACK_WRITEMASK                                                                              0x8CA5
 #define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX                                                             0x934B
 #define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE                                                            0x934C
 #define GL_CLIP_ORIGIN                                                                                                 0x935C
+#define GL_CLIP_ORIGIN_EXT                                                                                             0x935C
 #define GL_CLIP_DEPTH_MODE                                                                                             0x935D
+#define GL_CLIP_DEPTH_MODE_EXT                                                                                 0x935D
 #define GL_NEGATIVE_ONE_TO_ONE                                                                                 0x935E
+#define GL_NEGATIVE_ONE_TO_ONE_EXT                                                                             0x935E
 #define GL_ZERO_TO_ONE                                                                                                 0x935F
+#define GL_ZERO_TO_ONE_EXT                                                                                             0x935F
 #define GL_CLEAR_TEXTURE                                                                                               0x9365
 #define GL_TEXTURE_REDUCTION_MODE_ARB                                                                  0x9366
 #define GL_WEIGHTED_AVERAGE_ARB                                                                                        0x9367
index 3cf1b8b..44a9341 100644 (file)
@@ -67,6 +67,11 @@ if (de::contains(extSet, "GL_EXT_primitive_bounding_box"))
        gl->primitiveBoundingBox        = (glPrimitiveBoundingBoxFunc)  loader->get("glPrimitiveBoundingBoxEXT");
 }
 
+if (de::contains(extSet, "GL_EXT_clip_control"))
+{
+       gl->clipControl = (glClipControlFunc)   loader->get("glClipControlEXT");
+}
+
 if (de::contains(extSet, "GL_EXT_copy_image"))
 {
        gl->copyImageSubData    = (glCopyImageSubDataFunc)      loader->get("glCopyImageSubDataEXT");
index d10d500..43f513f 100644 (file)
@@ -5,6 +5,8 @@ set(DEQP_GLES2_FUNCTIONAL_SRCS
        es2fApiCase.hpp
        es2fAttribLocationTests.cpp
        es2fAttribLocationTests.hpp
+       es2fClipControlTests.cpp
+       es2fClipControlTests.hpp
        es2fColorClearTest.cpp
        es2fColorClearTest.hpp
        es2fDefaultVertexAttributeTests.cpp
diff --git a/modules/gles2/functional/es2fClipControlTests.cpp b/modules/gles2/functional/es2fClipControlTests.cpp
new file mode 100644 (file)
index 0000000..db94c0b
--- /dev/null
@@ -0,0 +1,1498 @@
+/*-------------------------------------------------------------------------
+ * OpenGL Conformance Test Suite
+ * -----------------------------
+ *
+ * Copyright (c) 2014-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */ /*!
+ * \file
+ * \brief
+ */ /*-------------------------------------------------------------------*/
+
+/**
+ * \file  gl2fClipControlTests.cpp
+ * \brief Implements conformance tests for "EXT_clip_control" functionality.
+ */ /*-------------------------------------------------------------------*/
+
+#include "es2fClipControlTests.hpp"
+
+#include "deSharedPtr.hpp"
+
+#include "gluContextInfo.hpp"
+#include "gluDrawUtil.hpp"
+#include "gluDefs.hpp"
+#include "gluPixelTransfer.hpp"
+#include "gluShaderProgram.hpp"
+
+#include "tcuFuzzyImageCompare.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuRenderTarget.hpp"
+#include "tcuSurface.hpp"
+#include "tcuTestLog.hpp"
+
+#include "glw.h"
+#include "glwFunctions.hpp"
+
+#include <cmath>
+
+namespace deqp
+{
+namespace gles2
+{
+namespace Functional
+{
+
+class ClipControlApi
+{
+public:
+       ClipControlApi(Context& context) : m_context(context)
+       {
+               if (!Supported(m_context))
+               {
+                       throw tcu::NotSupportedError("Required extension EXT_clip_control is not supported");
+               }
+               clipControl = context.getRenderContext().getFunctions().clipControl;
+       }
+
+       static bool Supported(Context& context)
+       {
+               return context.getContextInfo().isExtensionSupported("GL_EXT_clip_control");
+       }
+
+       glw::glClipControlFunc clipControl;
+
+private:
+       Context& m_context;
+};
+
+class ClipControlBaseTest : public TestCase
+{
+protected:
+       ClipControlBaseTest(Context& context, const char* name, const char* description)
+               : TestCase(context, name, description)
+       {
+       }
+
+       void init() override
+       {
+               ClipControlApi api(m_context);
+       }
+
+       bool verifyState(glw::GLenum origin, glw::GLenum depth)
+       {
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+               bool ret = true;
+
+               glw::GLint retI;
+               gl.getIntegerv(GL_CLIP_ORIGIN, &retI);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_CLIP_ORIGIN");
+
+               ret &= (static_cast<glw::GLenum>(retI) == origin);
+
+               gl.getIntegerv(GL_CLIP_DEPTH_MODE, &retI);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_CLIP_DEPTH_MODE");
+
+               ret &= (static_cast<glw::GLenum>(retI) == depth);
+
+               return ret;
+       }
+};
+
+class ClipControlRenderBaseTest : public ClipControlBaseTest
+{
+protected:
+       ClipControlRenderBaseTest(Context& context, const char* name, const char* description)
+               : ClipControlBaseTest(context, name, description), m_fbo(0), m_rboC(0), m_depthTexure(0)
+       {
+       }
+
+       const char* fsh()
+       {
+               return "void main() {"
+                          "\n"
+                          "    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);"
+                          "\n"
+                          "}";
+       }
+
+       bool fuzzyDepthCompare(tcu::TestLog& log, const char* imageSetName, const char* imageSetDesc,
+                                                  const tcu::TextureLevel& reference, const tcu::TextureLevel& result, float threshold,
+                                                  const tcu::TextureLevel* importanceMask = NULL)
+       {
+               (void)imageSetName;
+               (void)imageSetDesc;
+               bool  depthOk   = true;
+               float difference = 0.0f;
+
+               for (int y = 0; y < result.getHeight() && depthOk; y++)
+               {
+                       for (int x = 0; x < result.getWidth() && depthOk; x++)
+                       {
+                               float ref  = reference.getAccess().getPixDepth(x, y);
+                               float res  = result.getAccess().getPixel(x,y).x();
+                               difference = std::abs(ref - res);
+                               if (importanceMask)
+                               {
+                                       difference *= importanceMask->getAccess().getPixDepth(x, y);
+                               }
+                               depthOk &= (difference < threshold);
+                       }
+               }
+
+               if (!depthOk)
+                       log << tcu::TestLog::Message << "Image comparison failed: difference = " << difference
+                               << ", threshold = " << threshold << tcu::TestLog::EndMessage;
+               tcu::Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
+               tcu::Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
+               log << tcu::TestLog::ImageSet("Result", "Depth image comparison result")
+                       << tcu::TestLog::Image("Result", "Result", result.getAccess(), pixelScale, pixelBias)
+                       << tcu::TestLog::Image("Reference", "Reference", reference.getAccess(), pixelScale, pixelBias);
+               if (importanceMask)
+               {
+                       log << tcu::TestLog::Image("Importance mask", "mask", importanceMask->getAccess(), pixelScale, pixelBias);
+               }
+               log << tcu::TestLog::EndImageSet;
+
+               return depthOk;
+       }
+
+       virtual void init(void)
+       {
+               const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
+               glw::GLuint                              viewportW      = renderTarget.getWidth();
+               glw::GLuint                              viewportH      = renderTarget.getHeight();
+               const glw::Functions&   gl                        = m_context.getRenderContext().getFunctions();
+
+               gl.genFramebuffers(1, &m_fbo);
+               gl.genRenderbuffers(1, &m_rboC);
+               gl.genTextures(1, &m_depthTexure);
+
+               gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboC);
+               gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, viewportW, viewportH);
+
+               gl.bindTexture(GL_TEXTURE_2D, m_depthTexure);
+               gl.texImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, viewportW, viewportH, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, DE_NULL);
+
+               gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+               gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rboC);
+               gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexure, 0);
+       }
+
+       virtual void deinit(void)
+       {
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+               gl.deleteFramebuffers(1, &m_fbo);
+               gl.deleteRenderbuffers(1, &m_rboC);
+               gl.deleteTextures(1, &m_depthTexure);
+               gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+       }
+
+       GLuint getDepthTexture()
+       {
+               return m_depthTexure;
+       }
+
+private:
+       GLuint m_fbo, m_rboC, m_depthTexure;
+};
+
+/*
+ Verify the following state values are implemented and return a valid
+ initial value by calling GetIntegerv:
+
+ Get Value                                 Initial Value
+ -------------------------------------------------------
+ CLIP_ORIGIN                                  LOWER_LEFT
+ CLIP_DEPTH_MODE                     NEGATIVE_ONE_TO_ONE
+
+ Verify no GL error is generated.
+ */
+class ClipControlInitialState : public ClipControlBaseTest
+{
+public:
+       ClipControlInitialState(Context& context, const char* name)
+               : ClipControlBaseTest(context, name, "Verify initial state")
+       {
+       }
+
+       IterateResult iterate() override
+       {
+               if (!verifyState(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE))
+               {
+                       TCU_FAIL("Wrong intitial state: GL_CLIP_ORIGIN should be GL_LOWER_LEFT,"
+                                        " GL_CLIP_ORIGIN should be NEGATIVE_ONE_TO_ONE");
+               }
+
+               m_testCtx.setTestResult(QP_TEST_RESULT_PASS, qpGetTestResultName(QP_TEST_RESULT_PASS));
+               return STOP;
+       }
+};
+
+/*
+ Modify the state to each of the following combinations and after each
+ state change verify the state values:
+
+ ClipControl(UPPER_LEFT, ZERO_TO_ONE)
+ ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
+ ClipControl(LOWER_LEFT, ZERO_TO_ONE)
+ ClipControl(LOWER_LEFT, NEGATIVE_ONE_TO_ONE)
+
+ Verify no GL error is generated.
+
+ */
+class ClipControlModifyGetState : public ClipControlBaseTest
+{
+public:
+       ClipControlModifyGetState(Context& context, const char* name)
+               : ClipControlBaseTest(context,  name, "Verify initial state")
+       {
+       }
+
+       void deinit() override
+       {
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+       }
+
+       IterateResult iterate() override
+       {
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+               ClipControlApi            cc(m_context);
+
+               GLenum cases[4][2] = {
+                       { GL_UPPER_LEFT, GL_ZERO_TO_ONE },
+                       { GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE },
+                       { GL_LOWER_LEFT, GL_ZERO_TO_ONE },
+                       { GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE },
+               };
+
+               for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(cases); i++)
+               {
+                       cc.clipControl(cases[i][0], cases[i][1]);
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
+                       if (!verifyState(cases[i][0], cases[i][1]))
+                       {
+                               TCU_FAIL("Wrong ClipControl state after ClipControl() call");
+                       }
+               }
+
+               m_testCtx.setTestResult(QP_TEST_RESULT_PASS, qpGetTestResultName(QP_TEST_RESULT_PASS));
+               return STOP;
+       }
+};
+
+/*
+ Check that ClipControl generate an GL_INVALID_ENUM error if origin is
+ not GL_LOWER_LEFT or GL_UPPER_LEFT.
+
+ Check that ClipControl generate an GL_INVALID_ENUM error if depth is
+ not GL_NEGATIVE_ONE_TO_ONE or GL_ZERO_TO_ONE.
+
+ Test is based on OpenGL 4.5 Core Profile Specification May 28th Section
+ 13.5 Primitive Clipping:
+ "An INVALID_ENUM error is generated if origin is not LOWER_LEFT or
+ UPPER_LEFT.
+ An INVALID_ENUM error is generated if depth is not NEGATIVE_ONE_-
+ TO_ONE or ZERO_TO_ONE."
+ */
+class ClipControlErrors : public ClipControlBaseTest
+{
+public:
+       ClipControlErrors(Context& context, const char* name)
+               : ClipControlBaseTest(context, name, "Verify that proper errors are generated when using ClipControl.")
+       {
+       }
+
+       void deinit() override
+       {
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+       }
+
+       IterateResult iterate() override
+       {
+               /* API query */
+               tcu::TestLog&             log = m_testCtx.getLog();
+               const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
+               ClipControlApi            cc(m_context);
+
+               /* Finding improper value. */
+               GLenum improper_value = GL_NONE;
+
+               while ((GL_UPPER_LEFT == improper_value) || (GL_LOWER_LEFT == improper_value) ||
+                          (GL_ZERO_TO_ONE == improper_value) || (GL_NEGATIVE_ONE_TO_ONE == improper_value))
+               {
+                       ++improper_value;
+               }
+
+               /* Test setup. */
+               GLenum cases[5][2] = { { GL_UPPER_LEFT, improper_value },
+                                                          { GL_LOWER_LEFT, improper_value },
+                                                          { improper_value, GL_ZERO_TO_ONE },
+                                                          { improper_value, GL_NEGATIVE_ONE_TO_ONE },
+                                                          { improper_value, improper_value } };
+
+               /* Test iterations. */
+               for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(cases); i++)
+               {
+                       cc.clipControl(cases[i][0], cases[i][1]);
+
+                       if (GL_INVALID_ENUM != gl.getError())
+                       {
+                               m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, qpGetTestResultName(QP_TEST_RESULT_FAIL));
+
+                               log << tcu::TestLog::Message
+                                       << "ClipControl have not generated GL_INVALID_ENUM error when called with invalid value ("
+                                       << cases[i][0] << ", " << cases[i][1] << ")." << tcu::TestLog::EndMessage;
+                       }
+               }
+
+               m_testCtx.setTestResult(QP_TEST_RESULT_PASS, qpGetTestResultName(QP_TEST_RESULT_PASS));
+               return STOP;
+       }
+};
+
+/*
+ Clip Control Origin Test
+
+ * Basic <origin> behavior can be tested by rendering to a viewport with
+ clip coordinates where -1.0 <= x_c <= 0.0 and -1.0 <= y_c <= 0.0.
+ When <origin> is LOWER_LEFT the "bottom left" portion of the window
+ is rendered and when UPPER_LEFT is used the "top left" portion of the
+ window is rendered. The default framebuffer should be bound. Here is the
+ basic outline of the test:
+
+ - Clear the default framebuffer to red (1,0,0).
+ - Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
+ - Render a triangle fan covering (-1.0, -1.0) to (0.0, 0.0) and
+ write a pixel value of green (0,1,0).
+ - Read back the default framebuffer with ReadPixels
+ - Verify the green pixels at the top and red at the bottom.
+
+ Repeat the above test with LOWER_LEFT and verify green at the bottom
+ and red at the top.
+ */
+class ClipControlOriginTest : public ClipControlRenderBaseTest
+{
+public:
+       ClipControlOriginTest(Context& context, const char* name)
+               : ClipControlRenderBaseTest(context,  name, "Clip Control Origin Test"), m_vao(0), m_vbo(0)
+       {
+       }
+
+       void deinit() override
+       {
+               ClipControlRenderBaseTest::deinit();
+
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+
+               gl.clearColor(0.0, 0.0, 0.0, 0.0);
+               if (m_vao)
+               {
+                       gl.deleteVertexArrays(1, &m_vao);
+               }
+               if (m_vbo)
+               {
+                       gl.deleteBuffers(1, &m_vbo);
+               }
+       }
+
+       IterateResult iterate() override
+       {
+
+               tcu::TestLog&             log = m_testCtx.getLog();
+               const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
+               ClipControlApi            cc(m_context);
+
+               //Render a triangle fan covering(-1.0, -1.0) to(1.0, 0.0) and
+               //write a pixel value of green(0, 1, 0).
+
+               de::SharedPtr<glu::ShaderProgram> program(
+                                       new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
+
+               log << (*program);
+               if (!program->isOk())
+               {
+                       TCU_FAIL("Program compilation failed");
+               }
+
+               gl.genVertexArrays(1, &m_vao);
+               gl.bindVertexArray(m_vao);
+
+               gl.genBuffers(1, &m_vbo);
+
+               const float vertex_data0[] = { -1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0 };
+
+               gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
+               gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
+
+               gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+               gl.enableVertexAttribArray(0);
+
+               gl.useProgram(program->getProgram());
+
+               glw::GLenum origins[] = { GL_UPPER_LEFT, GL_LOWER_LEFT };
+
+               qpTestResult result = QP_TEST_RESULT_PASS;
+
+               for (size_t orig = 0; orig < DE_LENGTH_OF_ARRAY(origins); orig++)
+               {
+                       //Clear the default framebuffer to red(1, 0, 0).
+                       gl.clearColor(1.0, 0.0, 0.0, 1.0);
+                       gl.clear(GL_COLOR_BUFFER_BIT);
+
+                       //Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
+                       cc.clipControl(origins[orig], GL_NEGATIVE_ONE_TO_ONE);
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
+
+                       //test method modification: use GL_TRIANGLE_STRIP, not FAN.
+                       gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+                       //Read back the default framebuffer with ReadPixels
+                       //Verify the green pixels at the top and red at the bottom.
+                       qpTestResult loopResult = ValidateFramebuffer(m_context, origins[orig]);
+                       if (loopResult != QP_TEST_RESULT_PASS)
+                       {
+                               result = loopResult;
+                       }
+               }
+
+               m_testCtx.setTestResult(result, qpGetTestResultName(result));
+
+               return STOP;
+       }
+
+       const char* vsh()
+       {
+               return "attribute highp vec2 Position;"
+                          "\n"
+                          "void main() {"
+                          "\n"
+                          "    gl_Position = vec4(Position, 0.0, 1.0);"
+                          "\n"
+                          "}";
+       }
+
+       qpTestResult ValidateFramebuffer(Context& context, glw::GLenum origin)
+       {
+               const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
+               glw::GLsizei                     viewportW      = renderTarget.getWidth();
+               glw::GLsizei                     viewportH      = renderTarget.getHeight();
+               tcu::Surface                     renderedFrame(viewportW, viewportH);
+               tcu::Surface                     referenceFrame(viewportW, viewportH);
+
+               tcu::TestLog& log = context.getTestContext().getLog();
+
+               for (int y = 0; y < renderedFrame.getHeight(); y++)
+               {
+                       float yCoord = (float)(y) / (float)renderedFrame.getHeight();
+
+                       for (int x = 0; x < renderedFrame.getWidth(); x++)
+                       {
+
+                               float xCoord = (float)(x) / (float)renderedFrame.getWidth();
+
+                               bool greenQuadrant;
+
+                               if (origin == GL_UPPER_LEFT)
+                               {
+                                       greenQuadrant = (yCoord > 0.5 && xCoord <= 0.5);
+                               }
+                               else
+                               {
+                                       greenQuadrant = (yCoord <= 0.5 && xCoord <= 0.5);
+                               }
+
+                               if (greenQuadrant)
+                               {
+                                       referenceFrame.setPixel(x, y, tcu::RGBA::green());
+                               }
+                               else
+                               {
+                                       referenceFrame.setPixel(x, y, tcu::RGBA::red());
+                               }
+                       }
+               }
+
+               glu::readPixels(context.getRenderContext(), 0, 0, renderedFrame.getAccess());
+
+               if (tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f,
+                                                         tcu::COMPARE_LOG_RESULT))
+               {
+                       return QP_TEST_RESULT_PASS;
+               }
+               else
+               {
+                       return QP_TEST_RESULT_FAIL;
+               }
+       }
+
+       glw::GLuint m_vao, m_vbo;
+};
+
+
+
+/*
+ Clip Control Origin With Face Culling Test
+
+ * Face culling should be tested with both <origin> settings.
+ The reason for that is, when doing Y-inversion, implementation
+ should not flip the calculated area sign for the triangle.
+ In other words, culling of CCW and CW triangles should
+ be orthogonal to used <origin> mode. Both triangle windings
+ and both <origin> modes should be tested. Here is the basic
+ outline of the test:
+
+ - Clear the framebuffer to red (1,0,0).
+ - Enable GL_CULL_FACE, leave default front face & cull face (CCW, BACK)
+ - Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
+ - Render a counter-clockwise triangles covering
+ (-1.0, -1.0) to (0.0, 1.0) and write a pixel value of green (0,1,0).
+ - Render a clockwise triangles covering
+ (0.0, -1.0) to (1.0, 1.0) and write a pixel value of green (0,1,0).
+ - Read back the framebuffer with ReadPixels
+ - Verify the green pixels at the left and red at the right.
+
+ Repeat above test for ClipControl(LOWER_LEFT, NEGATIVE_ONE_TO_ONE)
+ */
+class ClipControlFaceCulling : public ClipControlRenderBaseTest
+{
+public:
+       ClipControlFaceCulling(Context& context, const char* name)
+               : ClipControlRenderBaseTest(context,  name, "Face culling test, both origins"), m_vao(0), m_vbo(0)
+       {
+       }
+
+       void deinit()
+       {
+               ClipControlRenderBaseTest::deinit();
+
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+
+               gl.disable(GL_CULL_FACE);
+
+               gl.clearColor(0.0, 0.0, 0.0, 0.0);
+
+               gl.disable(GL_DEPTH_TEST);
+               gl.depthFunc(GL_LESS);
+
+               if (m_vao)
+               {
+                       gl.deleteVertexArrays(1, &m_vao);
+               }
+               if (m_vbo)
+               {
+                       gl.deleteBuffers(1, &m_vbo);
+               }
+       }
+
+       IterateResult iterate()
+       {
+
+               tcu::TestLog&             log = m_testCtx.getLog();
+               const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
+               ClipControlApi            cc(m_context);
+
+               //Enable GL_CULL_FACE, leave default front face & cull face(CCW, BACK)
+               gl.enable(GL_CULL_FACE);
+
+               //Render a counter-clockwise triangles covering
+               //(-1.0, -1.0) to(0.0, 1.0) and write a pixel value of green(0, 1, 0).
+               //Render a clockwise triangles covering
+               //(0.0, -1.0) to(1.0, 1.0) and write a pixel value of green(0, 1, 0).
+               de::SharedPtr<glu::ShaderProgram> program(
+                                       new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
+
+               log << (*program);
+               if (!program->isOk())
+               {
+                       TCU_FAIL("Program compilation failed");
+               }
+
+               gl.genVertexArrays(1, &m_vao);
+               gl.bindVertexArray(m_vao);
+
+               gl.genBuffers(1, &m_vbo);
+
+               const float vertex_data0[] = {
+                       //CCW
+                       -1.0, -1.0, 0.0, -1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 1.0, -1.0, 1.0,
+                       //CW
+                       0.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
+               };
+
+               gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
+               gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
+
+               gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+               gl.enableVertexAttribArray(0);
+
+               gl.useProgram(program->getProgram());
+
+               glw::GLenum origins[] = { GL_UPPER_LEFT, GL_LOWER_LEFT };
+
+               qpTestResult result = QP_TEST_RESULT_PASS;
+
+               for (size_t orig = 0; orig < DE_LENGTH_OF_ARRAY(origins); orig++)
+               {
+                       //Clear the framebuffer to red (1,0,0).
+                       gl.clearColor(1.0, 0.0, 0.0, 1.0);
+                       gl.clear(GL_COLOR_BUFFER_BIT);
+
+                       gl.drawArrays(GL_TRIANGLES, 0, 12);
+
+                       //Set ClipControl(<origin>, NEGATIVE_ONE_TO_ONE)
+                       cc.clipControl(origins[orig], GL_NEGATIVE_ONE_TO_ONE);
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
+
+                       //Read back the framebuffer with ReadPixels
+                       //Verify the green pixels at the left and red at the right.
+                       qpTestResult loopResult = ValidateFramebuffer(m_context);
+                       if (loopResult != QP_TEST_RESULT_PASS)
+                       {
+                               result = loopResult;
+                       }
+               }
+               m_testCtx.setTestResult(result, qpGetTestResultName(result));
+
+               return STOP;
+       }
+
+       const char* vsh()
+       {
+               return "attribute  highp vec3 Position;"
+                          "\n"
+                          "void main() {"
+                          "\n"
+                          "    gl_Position = vec4(Position, 1.0);"
+                          "\n"
+                          "}";
+       }
+
+       qpTestResult ValidateFramebuffer(Context& context)
+       {
+               const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
+               glw::GLsizei                     viewportW      = renderTarget.getWidth();
+               glw::GLsizei                     viewportH      = renderTarget.getHeight();
+               tcu::Surface                     renderedColorFrame(viewportW, viewportH);
+               tcu::Surface                     referenceColorFrame(viewportW, viewportH);
+               tcu::TestLog&                    log = context.getTestContext().getLog();
+
+               for (int y = 0; y < renderedColorFrame.getHeight(); y++)
+               {
+                       for (int x = 0; x < renderedColorFrame.getWidth(); x++)
+                       {
+                               float xCoord = (float)(x) / (float)renderedColorFrame.getWidth();
+
+                               if (xCoord < 0.5f)
+                               {
+                                       referenceColorFrame.setPixel(x, y, tcu::RGBA::green());
+                               }
+                               else
+                               {
+                                       referenceColorFrame.setPixel(x, y, tcu::RGBA::red());
+                               }
+                       }
+               }
+
+               glu::readPixels(context.getRenderContext(), 0, 0, renderedColorFrame.getAccess());
+               if (!tcu::fuzzyCompare(log, "Result", "Color image comparison result", referenceColorFrame, renderedColorFrame,
+                                                          0.05f, tcu::COMPARE_LOG_RESULT))
+               {
+
+                       return QP_TEST_RESULT_FAIL;
+               }
+               return QP_TEST_RESULT_PASS;
+       }
+
+       glw::GLuint m_vao, m_vbo;
+};
+
+/*
+ Viewport Bounds Test
+
+ * Viewport bounds should be tested, to ensure that rendering with flipped
+ origin affects only viewport area.
+
+ This can be done by clearing the window to blue, making viewport
+ a non-symmetric-in-any-way subset of the window, than rendering
+ full-viewport multiple color quad. The (-1.0, -1.0)..(0.0, 0.0) quadrant
+ of a quad is red, the rest is green.
+ Whatever the origin is, the area outside of the viewport should stay blue.
+ If origin is LOWER_LEFT the "lower left" portion of the viewport is red,
+ if origin is UPPER_LEFT the "top left" portion of the viewport is red
+ (and in both cases the rest of viewport is green).
+
+ Here is the basic outline of the test:
+
+ - Clear the default framebuffer to blue (0,0,1).
+ - Set viewport to A = (x, y, w, h) = (1/8, 1/4, 1/2, 1/4)  in terms of proportional window size
+ - Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
+ - Render a triangle strip covering (-1.0, -1.0) to (1.0, 1.0).
+ Write a pixel value of red (0,1,0) to (-1.0, -1.0)..(0.0, 0.0), other parts are green
+ - Reset viewport to defaults
+ - Read back the default framebuffer with ReadPixels
+ - Verify:
+ - regions outside A viewport are green
+ - Inside A viewport upper upper left portion is red, rest is green.
+
+ Repeat the above test with LOWER_LEFT origin and lower left portion of A is red,
+ rest is green.
+ */
+class ClipControlViewportBounds : public ClipControlRenderBaseTest
+{
+public:
+       ClipControlViewportBounds(Context& context, const char* name)
+               : ClipControlRenderBaseTest(context,  name, "Clip Control Origin Test"), m_vao(0), m_vbo(0)
+       {
+       }
+
+       void deinit() override
+       {
+               ClipControlRenderBaseTest::deinit();
+
+               const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
+               glw::GLsizei                             windowW          = renderTarget.getWidth();
+               glw::GLsizei                             windowH          = renderTarget.getHeight();
+               const glw::Functions&   gl                        = m_context.getRenderContext().getFunctions();
+
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+
+               gl.clearColor(0.0, 0.0, 0.0, 0.0);
+               gl.viewport(0, 0, windowW, windowH);
+
+               if (m_vao)
+               {
+                       gl.deleteVertexArrays(1, &m_vao);
+               }
+               if (m_vbo)
+               {
+                       gl.deleteBuffers(1, &m_vbo);
+               }
+       }
+
+       IterateResult iterate() override
+       {
+               tcu::TestLog&                    log              = m_testCtx.getLog();
+               const glw::Functions&   gl                        = m_context.getRenderContext().getFunctions();
+               const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
+               glw::GLsizei                     windowW          = renderTarget.getWidth();
+               glw::GLsizei                     windowH          = renderTarget.getHeight();
+               ClipControlApi                   cc(m_context);
+
+               //Clear the default framebuffer to blue (0,0,1).
+               gl.clearColor(0.0, 0.0, 1.0, 1.0);
+               gl.clear(GL_COLOR_BUFFER_BIT);
+
+               de::SharedPtr<glu::ShaderProgram> program(
+                                       new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
+
+               log << (*program);
+               if (!program->isOk())
+               {
+                       TCU_FAIL("Program compilation failed");
+               }
+               gl.genVertexArrays(1, &m_vao);
+               gl.bindVertexArray(m_vao);
+
+               gl.genBuffers(1, &m_vbo);
+
+               const float vertex_data0[] = { -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0 };
+
+               gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
+               gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
+
+               gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
+               gl.enableVertexAttribArray(0);
+
+               gl.useProgram(program->getProgram());
+
+               glw::GLenum origins[] = { GL_UPPER_LEFT, GL_LOWER_LEFT };
+
+               qpTestResult result = QP_TEST_RESULT_PASS;
+
+               for (size_t orig = 0; orig < DE_LENGTH_OF_ARRAY(origins); orig++)
+               {
+                       //Set viewport to A = (x, y, w, h) = (1/8, 1/4, 1/2, 1/4) in terms of proportional window size
+                       gl.viewport(static_cast<glw::GLint>(0.125f * static_cast<float>(windowW)),
+                                               static_cast<glw::GLint>(0.25f * static_cast<float>(windowH)),
+                                               static_cast<glw::GLsizei>(0.5f * static_cast<float>(windowW)),
+                                               static_cast<glw::GLsizei>(0.25f * static_cast<float>(windowH)));
+
+                       //Set ClipControl(<origin>, NEGATIVE_ONE_TO_ONE)
+                       cc.clipControl(origins[orig], GL_NEGATIVE_ONE_TO_ONE);
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
+
+                       //Render a triangle strip covering (-1.0, -1.0) to (1.0, 1.0).
+                       //Write a pixel value of red (0,1,0) to (-1.0, -1.0)..(0.0, 0.0), other parts are green
+                       gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+                       gl.viewport(0, 0, windowW, windowH);
+
+                       //Read back the default framebuffer with ReadPixels
+                       //Verify the green pixels at the top and red at the bottom.
+                       qpTestResult loopResult = ValidateFramebuffer(m_context, origins[orig]);
+                       if (loopResult != QP_TEST_RESULT_PASS)
+                       {
+                               result = loopResult;
+                       }
+               }
+               m_testCtx.setTestResult(result, qpGetTestResultName(result));
+               return STOP;
+       }
+
+       const char* vsh()
+       {
+               return "attribute highp vec2 Position;"
+                          "\n"
+                          "varying highp vec2 PositionOut;"
+                          "\n"
+                          "void main() {"
+                          "\n"
+                          "    gl_Position = vec4(Position, 0.0, 1.0);"
+                          "\n"
+                          "    PositionOut = Position;"
+                          "\n"
+                          "}";
+       }
+
+       const char* fsh()
+       {
+               return "varying highp vec2 PositionOut;"
+                          "\n"
+                          "void main() {"
+                          "\n"
+                          "    if (PositionOut.x < 0.0 && PositionOut.y < 0.0)"
+                          "\n"
+                          "       gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);"
+                          "\n"
+                          "    else"
+                          "\n"
+                          "       gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);"
+                          "\n"
+                          "}";
+       }
+
+       qpTestResult ValidateFramebuffer(Context& context, glw::GLenum origin)
+       {
+               const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
+               glw::GLsizei                             windowW          = renderTarget.getWidth();
+               glw::GLsizei                             windowH          = renderTarget.getHeight();
+               tcu::Surface                     renderedFrame(windowW, windowH);
+               tcu::Surface                     referenceFrame(windowW, windowH);
+
+               tcu::TestLog& log = context.getTestContext().getLog();
+
+               for (int y = 0; y < renderedFrame.getHeight(); y++)
+               {
+                       float yCoord   = static_cast<float>(y) / static_cast<float>(renderedFrame.getHeight());
+                       float yVPCoord = (yCoord - 0.25f) * 4.0f;
+
+                       for (int x = 0; x < renderedFrame.getWidth(); x++)
+                       {
+                               float xCoord   = static_cast<float>(x) / static_cast<float>(renderedFrame.getWidth());
+                               float xVPCoord = (xCoord - 0.125f) * 2.0f;
+
+                               if (xVPCoord > 0.0f && xVPCoord < 1.0f && yVPCoord > 0.0f && yVPCoord < 1.0f)
+                               {
+
+                                       bool greenQuadrant;
+
+                                       //inside viewport
+                                       if (origin == GL_UPPER_LEFT)
+                                       {
+                                               greenQuadrant = (yVPCoord > 0.5f && xVPCoord <= 0.5f);
+                                       }
+                                       else
+                                       {
+                                               greenQuadrant = (yVPCoord <= 0.5f && xVPCoord <= 0.5f);
+                                       }
+
+                                       if (greenQuadrant)
+                                       {
+                                               referenceFrame.setPixel(x, y, tcu::RGBA::green());
+                                       }
+                                       else
+                                       {
+                                               referenceFrame.setPixel(x, y, tcu::RGBA::red());
+                                       }
+                               }
+                               else
+                               {
+                                       //outside viewport
+                                       referenceFrame.setPixel(x, y, tcu::RGBA::blue());
+                               }
+                       }
+               }
+
+               glu::readPixels(context.getRenderContext(), 0, 0, renderedFrame.getAccess());
+
+               if (tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f,
+                                                         tcu::COMPARE_LOG_RESULT))
+               {
+                       return QP_TEST_RESULT_PASS;
+               }
+               else
+               {
+                       return QP_TEST_RESULT_FAIL;
+               }
+       }
+
+       glw::GLuint m_vao, m_vbo;
+};
+
+/*   Depth Mode Test
+
+ * Basic <depth> behavior can be tested by writing specific z_c (z
+ clip coordinates) and observing its clipping and transformation.
+ Create and bind a framebuffer object with a floating-point depth
+ buffer attachment. Make sure depth clamping is disabled. The best
+ steps for verifying the correct depth mode:
+
+ - Clear the depth buffer to 0.5.
+ - Set ClipControl(LOWER_LEFT, ZERO_TO_ONE)
+ - Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
+ - Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
+ - Read back the floating-point depth buffer with ReadPixels
+ - Verify that the pixels with a Z clip coordinate less than 0.0 are
+ clipped and those coordinates from 0.0 to 1.0 update the depth
+ buffer with values 0.0 to 1.0.
+ */
+
+class ClipControlDepthModeTest : public ClipControlRenderBaseTest
+{
+public:
+       ClipControlDepthModeTest(Context& context, const char* name, const char* subname)
+               : ClipControlRenderBaseTest(context, name, subname)
+       {
+
+       }
+
+       void init() override
+       {
+               const glw::Functions&   gl                        = m_context.getRenderContext().getFunctions();
+               const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
+               glw::GLuint                              viewportW      = renderTarget.getWidth();
+               glw::GLuint                              viewportH      = renderTarget.getHeight();
+
+               ClipControlRenderBaseTest::init();
+
+               gl.genFramebuffers(1, &m_fboD);
+
+               gl.genTextures(1, &m_texDepthResolve);
+               gl.bindTexture(GL_TEXTURE_2D, m_texDepthResolve);
+               setupTexture();
+               gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, viewportW, viewportH, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+       }
+
+       void deinit() override
+       {
+               const glw::Functions&   gl                        = m_context.getRenderContext().getFunctions();
+
+               gl.deleteRenderbuffers(1, &m_texDepthResolve);
+               gl.deleteFramebuffers(1, &m_fboD);
+
+               ClipControlRenderBaseTest::deinit();
+       }
+
+       void setupTexture()
+       {
+               const glw::Functions&   gl                        = m_context.getRenderContext().getFunctions();
+               gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+               gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+               gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+               gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       }
+
+       void readDepthPixels(const tcu::PixelBufferAccess& pixelBuf)
+       {
+
+               const char* vs = "\n"
+                                                "attribute vec4 pos;\n"
+                                                "attribute vec2 UV;\n"
+                                                "varying highp vec2 vUV;\n"
+                                                "void main() {\n"
+                                                "  gl_Position = pos;\n"
+                                                "  vUV = UV;\n"
+                                                "}\n";
+
+               const char* fs = "\n"
+                                                "precision mediump float;\n"
+                                                "varying vec2 vUV;\n"
+                                                "uniform sampler2D tex;\n"
+                                                "void main() {\n"
+                                                "  gl_FragColor = texture2D(tex, vUV).rrrr;\n"
+                                                "}\n";
+
+               const glu::RenderContext&       renderContext   = m_context.getRenderContext();
+               const glw::Functions&           gl                              = renderContext.getFunctions();
+               const tcu::RenderTarget&        renderTarget    = renderContext.getRenderTarget();
+               glw::GLsizei                            windowW                 = renderTarget.getWidth();
+               glw::GLsizei                            windowH                 = renderTarget.getHeight();
+
+               glu::ShaderProgram program(renderContext,  glu::makeVtxFragSources(vs, fs));
+
+               gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboD);
+               gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texDepthResolve, 0);
+
+               gl.disable(GL_DEPTH_TEST);
+               gl.depthMask(GL_FALSE);
+               gl.disable(GL_STENCIL_TEST);
+               gl.viewport(0, 0, windowW, windowH);
+               gl.clearColor(0.0f, 0.2f, 1.0f, 1.0f);
+               gl.clear(GL_COLOR_BUFFER_BIT);
+
+               const int       texLoc  = gl.getUniformLocation(program.getProgram(), "tex");
+
+               gl.bindVertexArray(0);
+               gl.bindBuffer(GL_ARRAY_BUFFER, 0);
+
+               gl.bindTexture(GL_TEXTURE_2D, getDepthTexture());
+               setupTexture();
+
+               gl.useProgram(program.getProgram());
+               gl.uniform1i(texLoc, 0);
+
+               {
+                       const GLfloat vertices[] = {
+                               -1.0f, -1.0f, 0.0f, 1.0f,
+                               1.0f, -1.0f, 0.0f, 1.0f,
+                               -1.0f,  1.0f, 0.0f, 1.0f,
+                               1.0f,  1.0f, 0.0f, 1.0f,
+                       };
+                       const GLfloat texCoords[] = {
+                               0.0f, 0.0f,
+                               1.0f, 0.0f,
+                               0.0f, 1.0f,
+                               1.0f, 1.0f,
+                       };
+                       const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
+
+                       const glu::VertexArrayBinding   vertexArray[]   = { glu::va::Float("pos", 4, 4, 0, vertices),
+                                                                                                                               glu::va::Float("UV", 2, 4, 0, texCoords) };
+
+                       glu::draw(renderContext, program.getProgram(), 2, vertexArray,
+                                         glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), indices));
+               }
+               glu::readPixels(renderContext, 0, 0, pixelBuf);
+       }
+
+       GLuint m_fboD;
+       GLuint m_texDepthResolve;
+
+};
+
+class ClipControlDepthModeZeroToOneTest : public ClipControlDepthModeTest
+{
+public:
+       ClipControlDepthModeZeroToOneTest(Context& context, const char* name)
+               : ClipControlDepthModeTest(context, name, "Depth Mode Test, ZERO_TO_ONE"), m_vao(0), m_vbo(0)
+       {
+
+       }
+
+       void deinit() override
+       {
+               ClipControlDepthModeTest::deinit();
+
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+
+               gl.clearDepthf(0.0f);
+               gl.clearColor(0.0, 0.0, 0.0, 0.0);
+
+               gl.disable(GL_DEPTH_TEST);
+               gl.depthFunc(GL_LESS);
+
+               if (m_vao)
+               {
+                       gl.deleteVertexArrays(1, &m_vao);
+               }
+               if (m_vbo)
+               {
+                       gl.deleteBuffers(1, &m_vbo);
+               }
+       }
+
+       IterateResult iterate() override
+       {
+
+               tcu::TestLog&             log = m_testCtx.getLog();
+               const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
+               ClipControlApi            cc(m_context);
+
+               gl.clearColor(1.0, 0.0, 0.0, 1.0);
+               gl.clear(GL_COLOR_BUFFER_BIT);
+
+               //Clear the depth buffer to 0.5.
+               gl.clearDepthf(0.5);
+               gl.clear(GL_DEPTH_BUFFER_BIT);
+
+               //Set ClipControl(LOWER_LEFT, ZERO_TO_ONE)
+               cc.clipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
+
+               //Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
+               gl.enable(GL_DEPTH_TEST);
+               gl.depthFunc(GL_ALWAYS);
+
+               //Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
+               de::SharedPtr<glu::ShaderProgram> program(
+                                       new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
+
+               log << (*program);
+               if (!program->isOk())
+               {
+                       TCU_FAIL("Program compilation failed");
+               }
+
+               gl.genVertexArrays(1, &m_vao);
+               gl.bindVertexArray(m_vao);
+
+               gl.genBuffers(1, &m_vbo);
+
+               const float vertex_data0[] = {
+                       -1.0, -1.0, -1.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
+               };
+
+               gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
+               gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
+
+               gl.vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
+               gl.enableVertexAttribArray(0);
+
+               gl.useProgram(program->getProgram());
+
+               //test method modification: use GL_TRIANGLE_STRIP, not FAN.
+               gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+               //Read back the floating-point depth buffer with ReadPixels
+               //Verify that the pixels with a Z clip coordinate less than 0.0 are
+               //  clipped and those coordinates from 0.0 to 1.0 update the depth
+               //  buffer with values 0.0 to 1.0.
+               qpTestResult result = ValidateFramebuffer(m_context);
+               m_testCtx.setTestResult(result, qpGetTestResultName(result));
+
+               return STOP;
+       }
+
+       const char* vsh()
+       {
+               return "attribute vec3 Position;"
+                          "\n"
+                          "void main() {"
+                          "\n"
+                          "    gl_Position = vec4(Position, 1.0);"
+                          "\n"
+                          "}";
+       }
+
+       qpTestResult ValidateFramebuffer(Context& context)
+       {
+               const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
+               glw::GLuint                              viewportW      = renderTarget.getWidth();
+               glw::GLuint                              viewportH      = renderTarget.getHeight();
+               tcu::Surface                     renderedColorFrame(viewportW, viewportH);
+               tcu::Surface                     referenceColorFrame(viewportW, viewportH);
+               tcu::TextureFormat               depthReadbackFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
+               tcu::TextureFormat               depthFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
+               tcu::TextureLevel                renderedDepthFrame(depthReadbackFormat, viewportW, viewportH);
+               tcu::TextureLevel                referenceDepthFrame(depthFormat, viewportW, viewportH);
+               tcu::TextureLevel                importanceMaskFrame(depthFormat, viewportW, viewportH);
+
+               tcu::TestLog& log = context.getTestContext().getLog();
+
+               const float rasterizationError =
+                               2.0f / (float)renderedColorFrame.getHeight() + 2.0f / (float)renderedColorFrame.getWidth();
+
+               for (int y = 0; y < renderedColorFrame.getHeight(); y++)
+               {
+                       float yCoord = ((float)(y) + 0.5f) / (float)renderedColorFrame.getHeight();
+
+                       for (int x = 0; x < renderedColorFrame.getWidth(); x++)
+                       {
+                               float xCoord = ((float)(x) + 0.5f) / (float)renderedColorFrame.getWidth();
+
+                               if (yCoord >= 1.0 - xCoord - rasterizationError && yCoord <= 1.0 - xCoord + rasterizationError)
+                               {
+                                       importanceMaskFrame.getAccess().setPixDepth(0.0f, x, y);
+                               }
+                               else
+                               {
+                                       importanceMaskFrame.getAccess().setPixDepth(1.0f, x, y);
+                               }
+
+                               if (yCoord < 1.0 - xCoord)
+                               {
+                                       referenceColorFrame.setPixel(x, y, tcu::RGBA::red());
+                                       referenceDepthFrame.getAccess().setPixDepth(0.5f, x, y);
+                               }
+                               else
+                               {
+                                       referenceColorFrame.setPixel(x, y, tcu::RGBA::green());
+
+                                       referenceDepthFrame.getAccess().setPixDepth(-1.0f + xCoord + yCoord, x, y);
+                               }
+                       }
+               }
+
+               glu::readPixels(context.getRenderContext(), 0, 0, renderedColorFrame.getAccess());
+               if (!tcu::fuzzyCompare(log, "Result", "Color image comparison result", referenceColorFrame, renderedColorFrame,
+                                                          0.05f, tcu::COMPARE_LOG_RESULT))
+               {
+
+                       return QP_TEST_RESULT_FAIL;
+               }
+
+               readDepthPixels(renderedDepthFrame.getAccess());
+               if (!fuzzyDepthCompare(log, "Result", "Depth image comparison result", referenceDepthFrame, renderedDepthFrame,
+                                                          0.05f, &importanceMaskFrame))
+               {
+                       return QP_TEST_RESULT_FAIL;
+               }
+               return QP_TEST_RESULT_PASS;
+       }
+
+       glw::GLuint m_vao, m_vbo;
+};
+
+/*
+ Do the same as above, but use the default NEGATIVE_ONE_TO_ONE depth mode:
+
+ - Clear the depth buffer to 0.5.
+ - Set ClipControl(LOWER_LEFT, NEGATIVE_ONE_TO_ONE)
+ - Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
+ - Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
+ - Read back the floating-point depth buffer with ReadPixels
+ - Verify that no pixels are clipped and the depth buffer contains
+ values from 0.0 to 1.0.
+ */
+class ClipControlDepthModeOneToOneTest : public ClipControlDepthModeTest
+{
+public:
+       ClipControlDepthModeOneToOneTest(Context& context, const char* name)
+               : ClipControlDepthModeTest(context, name, "Depth Mode Test, ONE_TO_ONE"), m_vao(0), m_vbo(0)
+       {
+       }
+
+       void deinit() override
+       {
+               ClipControlDepthModeTest::deinit();
+
+               const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+               if (ClipControlApi::Supported(m_context))
+               {
+                       ClipControlApi cc(m_context);
+                       cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               }
+
+               gl.clearDepthf(0.0);
+               gl.clearColor(0.0, 0.0, 0.0, 0.0);
+
+               gl.disable(GL_DEPTH_TEST);
+               gl.depthFunc(GL_LESS);
+
+               if (m_vao)
+               {
+                       gl.deleteVertexArrays(1, &m_vao);
+               }
+               if (m_vbo)
+               {
+                       gl.deleteBuffers(1, &m_vbo);
+               }
+       }
+
+       IterateResult iterate() override
+       {
+               tcu::TestLog&             log = m_testCtx.getLog();
+               const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
+               ClipControlApi            cc(m_context);
+
+               gl.clearColor(1.0, 0.0, 0.0, 1.0);
+               gl.clear(GL_COLOR_BUFFER_BIT);
+
+               //Clear the depth buffer to 0.5.
+               gl.clearDepthf(0.5f);
+               gl.clear(GL_DEPTH_BUFFER_BIT);
+
+               //Set ClipControl(LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE)
+               cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
+
+               //Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
+               gl.enable(GL_DEPTH_TEST);
+               gl.depthFunc(GL_ALWAYS);
+
+               //Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
+               de::SharedPtr<glu::ShaderProgram> program(
+                                       new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
+
+               log << (*program);
+               if (!program->isOk())
+               {
+                       TCU_FAIL("Program compilation failed");
+               }
+
+               gl.genVertexArrays(1, &m_vao);
+               gl.bindVertexArray(m_vao);
+
+               gl.genBuffers(1, &m_vbo);
+
+               const float vertex_data0[] = {
+                       -1.0, -1.0, -1.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
+               };
+
+               gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
+               gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
+
+               gl.vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
+               gl.enableVertexAttribArray(0);
+
+               gl.useProgram(program->getProgram());
+
+               //test method modification: use GL_TRIANGLE_STRIP, not FAN.
+               gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+               //Read back the floating-point depth buffer with ReadPixels
+               //Verify that the pixels with a Z clip coordinate less than 0.0 are
+               //  clipped and those coordinates from 0.0 to 1.0 update the depth
+               //  buffer with values 0.0 to 1.0.
+               qpTestResult result = ValidateFramebuffer(m_context);
+               m_testCtx.setTestResult(result, qpGetTestResultName(result));
+
+               return STOP;
+       }
+
+       const char* vsh()
+       {
+               return "attribute vec3 Position;"
+                          "\n"
+                          "void main() {"
+                          "\n"
+                          "    gl_Position = vec4(Position, 1.0);"
+                          "\n"
+                          "}";
+       }
+
+       qpTestResult ValidateFramebuffer(Context& context)
+       {
+               const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
+               glw::GLuint                              viewportW      = renderTarget.getWidth();
+               glw::GLuint                              viewportH      = renderTarget.getHeight();
+               tcu::Surface                     renderedColorFrame(viewportW, viewportH);
+               tcu::Surface                     referenceColorFrame(viewportW, viewportH);
+               tcu::TextureFormat               depthReadbackFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
+               tcu::TextureFormat               depthFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
+               tcu::TextureLevel                renderedDepthFrame(depthReadbackFormat, viewportW, viewportH);
+               tcu::TextureLevel                referenceDepthFrame(depthFormat, viewportW, viewportH);
+
+               tcu::TestLog& log = context.getTestContext().getLog();
+
+               for (int y = 0; y < renderedColorFrame.getHeight(); y++)
+               {
+                       float yCoord = (float)(y) / (float)renderedColorFrame.getHeight();
+                       for (int x = 0; x < renderedColorFrame.getWidth(); x++)
+                       {
+                               float xCoord = (float)(x) / (float)renderedColorFrame.getWidth();
+
+                               referenceColorFrame.setPixel(x, y, tcu::RGBA::green());
+                               referenceDepthFrame.getAccess().setPixDepth((xCoord + yCoord) * 0.5f, x, y);
+                       }
+               }
+
+               glu::readPixels(context.getRenderContext(), 0, 0, renderedColorFrame.getAccess());
+               if (!tcu::fuzzyCompare(log, "Result", "Color image comparison result", referenceColorFrame, renderedColorFrame,
+                                                          0.05f, tcu::COMPARE_LOG_RESULT))
+               {
+                       return QP_TEST_RESULT_FAIL;
+               }
+
+               readDepthPixels(renderedDepthFrame.getAccess());
+               if (!fuzzyDepthCompare(log, "Result", "Depth image comparison result", referenceDepthFrame, renderedDepthFrame,
+                                                          0.05f))
+               {
+                       return QP_TEST_RESULT_FAIL;
+               }
+
+               return QP_TEST_RESULT_PASS;
+       }
+
+       glw::GLuint m_vao, m_vbo;
+};
+
+
+/** Constructor.
+ *
+ *  @param context Rendering context.
+ **/
+ClipControlTests::ClipControlTests(Context& context)
+       : TestCaseGroup(context, "clip_control", "Verifies \"clip_control\" functionality")
+{
+       /* Left blank on purpose */
+}
+
+/** Destructor.
+ *
+ **/
+ClipControlTests::~ClipControlTests()
+{
+}
+
+/** Initializes a texture_storage_multisample test group.
+ *
+ **/
+void ClipControlTests::init(void)
+{
+       addChild(new ClipControlInitialState(m_context, "initial"));
+       addChild(new ClipControlModifyGetState(m_context, "modify_get"));
+       addChild(new ClipControlErrors(m_context, "errors"));
+       addChild(new ClipControlOriginTest(m_context, "origin"));
+       addChild(new ClipControlDepthModeZeroToOneTest(m_context, "depth_mode_zero_to_one"));
+       addChild(new ClipControlDepthModeOneToOneTest(m_context, "depth_mode_one_to_one"));
+       addChild(new ClipControlFaceCulling(m_context, "face_culling"));
+       addChild(new ClipControlViewportBounds(m_context, "viewport_bounds"));
+}
+}
+}
+}
+
diff --git a/modules/gles2/functional/es2fClipControlTests.hpp b/modules/gles2/functional/es2fClipControlTests.hpp
new file mode 100644 (file)
index 0000000..d96050d
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _ES2FCLIPCONTROLTESTS_HPP
+#define _ES2FCLIPCONTROLTESTS_HPP
+/*-------------------------------------------------------------------------
+ * OpenGL Conformance Test Suite
+ * -----------------------------
+ *
+ * Copyright (c) 2014-2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */ /*!
+ * \file
+ * \brief
+ */ /*-------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tes2TestCase.hpp"
+
+namespace deqp
+{
+namespace gles2
+{
+namespace Functional
+{
+
+class ClipControlTests : public TestCaseGroup
+{
+public:
+
+       ClipControlTests(Context& context);
+       ~ClipControlTests(void);
+       void init(void);
+
+private:
+       ClipControlTests(const ClipControlTests& other);
+       ClipControlTests& operator=(const ClipControlTests& other);
+};
+
+} // Functional
+} // gles2
+} // deqp
+
+#endif // _ES2FCLIPCONTROLTESTS_HPP
index 7094be4..7a169db 100644 (file)
@@ -47,6 +47,7 @@
 #include "es2fDepthRangeTests.hpp"
 #include "es2fDitheringTests.hpp"
 #include "es2fClippingTests.hpp"
+#include "es2fClipControlTests.hpp"
 #include "es2fPolygonOffsetTests.hpp"
 #include "es2fDrawTests.hpp"
 #include "es2fFragOpInteractionTests.hpp"
@@ -311,6 +312,7 @@ void FunctionalTests::init (void)
 {
        addChild(new PrerequisiteTests                  (m_context));
        addChild(new ImplementationLimitTests   (m_context));
+       addChild(new ClipControlTests                   (m_context));
        addChild(new ColorClearTest                             (m_context));
        addChild(new DepthStencilClearTests             (m_context));
        addChild(new BufferTests                                (m_context));
index c06ccb3..09d0b23 100644 (file)
@@ -61,6 +61,7 @@ EXTENSIONS                    = [
        'GL_EXT_primitive_bounding_box',
        'GL_EXT_texture_compression_s3tc',
        'GL_EXT_texture_type_2_10_10_10_REV',
+       'GL_EXT_clip_control',
        'GL_EXT_copy_image',
        'GL_EXT_depth_bounds_test',
        'GL_EXT_direct_state_access',