From 1bc91b14a2c414b6810b2e748e0870c70c5a5108 Mon Sep 17 00:00:00 2001 From: Gert Wollny Date: Wed, 15 May 2019 12:12:06 +0200 Subject: [PATCH] gles2: Add tests for EXT_clip_control MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- AndroidGen.mk | 1 + android/cts/master/gles2-master.txt | 8 + .../gles/aosp_mustpass/3.2.5.x/gles2-master.txt | 8 + .../gles/aosp_mustpass/master/gles2-master.txt | 8 + framework/opengl/wrapper/glwEnums.inl | 6 + framework/opengl/wrapper/glwInitExtES.inl | 5 + modules/gles2/functional/CMakeLists.txt | 2 + modules/gles2/functional/es2fClipControlTests.cpp | 1498 ++++++++++++++++++++ modules/gles2/functional/es2fClipControlTests.hpp | 53 + modules/gles2/functional/es2fFunctionalTests.cpp | 2 + scripts/opengl/src_util.py | 1 + 11 files changed, 1592 insertions(+) create mode 100644 modules/gles2/functional/es2fClipControlTests.cpp create mode 100644 modules/gles2/functional/es2fClipControlTests.hpp diff --git a/AndroidGen.mk b/AndroidGen.mk index 642e332..31e8504 100644 --- a/AndroidGen.mk +++ b/AndroidGen.mk @@ -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 \ diff --git a/android/cts/master/gles2-master.txt b/android/cts/master/gles2-master.txt index 5cbd5dc..15b5b91 100644 --- a/android/cts/master/gles2-master.txt +++ b/android/cts/master/gles2-master.txt @@ -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 diff --git a/external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles2-master.txt b/external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles2-master.txt index 467f87a..e49fc73 100644 --- a/external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles2-master.txt +++ b/external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles2-master.txt @@ -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 diff --git a/external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles2-master.txt b/external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles2-master.txt index f59d7d6..164c179 100644 --- a/external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles2-master.txt +++ b/external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles2-master.txt @@ -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 diff --git a/framework/opengl/wrapper/glwEnums.inl b/framework/opengl/wrapper/glwEnums.inl index 1498559..66096d2 100644 --- a/framework/opengl/wrapper/glwEnums.inl +++ b/framework/opengl/wrapper/glwEnums.inl @@ -1036,7 +1036,9 @@ #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 @@ -1712,9 +1714,13 @@ #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 diff --git a/framework/opengl/wrapper/glwInitExtES.inl b/framework/opengl/wrapper/glwInitExtES.inl index 3cf1b8b..44a9341 100644 --- a/framework/opengl/wrapper/glwInitExtES.inl +++ b/framework/opengl/wrapper/glwInitExtES.inl @@ -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"); diff --git a/modules/gles2/functional/CMakeLists.txt b/modules/gles2/functional/CMakeLists.txt index d10d500..43f513f 100644 --- a/modules/gles2/functional/CMakeLists.txt +++ b/modules/gles2/functional/CMakeLists.txt @@ -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 index 0000000..db94c0b --- /dev/null +++ b/modules/gles2/functional/es2fClipControlTests.cpp @@ -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 + +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(retI) == origin); + + gl.getIntegerv(GL_CLIP_DEPTH_MODE, &retI); + GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_CLIP_DEPTH_MODE"); + + ret &= (static_cast(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 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 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 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 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 mode. Both triangle windings + and both 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 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(, 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 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(0.125f * static_cast(windowW)), + static_cast(0.25f * static_cast(windowH)), + static_cast(0.5f * static_cast(windowW)), + static_cast(0.25f * static_cast(windowH))); + + //Set ClipControl(, 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(y) / static_cast(renderedFrame.getHeight()); + float yVPCoord = (yCoord - 0.25f) * 4.0f; + + for (int x = 0; x < renderedFrame.getWidth(); x++) + { + float xCoord = static_cast(x) / static_cast(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 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 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 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 index 0000000..d96050d --- /dev/null +++ b/modules/gles2/functional/es2fClipControlTests.hpp @@ -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 diff --git a/modules/gles2/functional/es2fFunctionalTests.cpp b/modules/gles2/functional/es2fFunctionalTests.cpp index 7094be4..7a169db 100644 --- a/modules/gles2/functional/es2fFunctionalTests.cpp +++ b/modules/gles2/functional/es2fFunctionalTests.cpp @@ -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)); diff --git a/scripts/opengl/src_util.py b/scripts/opengl/src_util.py index c06ccb3..09d0b23 100644 --- a/scripts/opengl/src_util.py +++ b/scripts/opengl/src_util.py @@ -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', -- 2.7.4