blob: 848772921c0c513d978b521dc285a523cc994ec6 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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
*/ /*-------------------------------------------------------------------*/
/*!
* \file esextcTessellationShaderPrimitiveCoverage.cpp
* \brief TessellationShadePrimitiveCoverage (Test 31)
*/ /*-------------------------------------------------------------------*/
#include "esextcTessellationShaderPrimitiveCoverage.hpp"
#include "esextcTessellationShaderUtils.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
#include <cstdarg>
#include <cstddef>
#include <cstdlib>
namespace glcts
{
/* Vertex shader */
const char* TessellationShaderPrimitiveCoverage::m_vs_code = "${VERSION}\n"
"\n"
"precision highp float;\n"
"\n"
"layout(location = 0) in vec4 position;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}\n";
/* Tessellation Control Shaders' source code */
const char* TessellationShaderPrimitiveCoverage::m_quad_tessellation_tcs_code =
"${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"precision highp float;\n"
"\n"
"layout(vertices = 4) out;\n"
"\n"
"uniform vec2 innerLevel;"
"uniform vec4 outerLevel;"
"\n"
"void main()\n"
"{\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
"\n"
" gl_TessLevelInner[0] = innerLevel.x;\n"
" gl_TessLevelInner[1] = innerLevel.y;\n"
" gl_TessLevelOuter[0] = outerLevel.x;\n"
" gl_TessLevelOuter[1] = outerLevel.y;\n"
" gl_TessLevelOuter[2] = outerLevel.z;\n"
" gl_TessLevelOuter[3] = outerLevel.w;\n"
"}\n";
/* Tessellation Evaluation Shaders' source code */
const char* TessellationShaderPrimitiveCoverage::m_quad_tessellation_tes_code =
"${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"precision highp float;\n"
"\n"
"layout (quads) in;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = gl_in[0].gl_Position * (1.0 - gl_TessCoord.x) * (1.0 - "
"gl_TessCoord.y)\n" /* Specifying the vertex’s position */
" + gl_in[1].gl_Position * ( gl_TessCoord.x) * (1.0 - "
"gl_TessCoord.y)\n" /* using the bilinear interpolation */
" + gl_in[2].gl_Position * ( gl_TessCoord.x) * ( "
"gl_TessCoord.y)\n"
" + gl_in[3].gl_Position * (1.0 - gl_TessCoord.x) * ( "
"gl_TessCoord.y);\n"
"}\n";
/* Tessellation Control Shaders' source code */
const char* TessellationShaderPrimitiveCoverage::m_triangles_tessellation_tcs_code =
"${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"precision highp float;\n"
"\n"
"layout(vertices = 3) out;\n"
"\n"
"uniform vec2 innerLevel;"
"uniform vec4 outerLevel;"
"\n"
"void main()\n"
"{\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
"\n"
" gl_TessLevelInner[0] = innerLevel.x;\n"
" gl_TessLevelInner[1] = innerLevel.y;\n"
" gl_TessLevelOuter[0] = outerLevel.x;\n"
" gl_TessLevelOuter[1] = outerLevel.y;\n"
" gl_TessLevelOuter[2] = outerLevel.z;\n"
" gl_TessLevelOuter[3] = outerLevel.w;\n"
"}\n";
/* Tessellation Evaluation Shader code for triangle test */
const char* TessellationShaderPrimitiveCoverage::m_triangles_tessellation_tes_code =
"${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"precision highp float;\n"
"\n"
"layout (triangles) in;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = gl_in[0].gl_Position * gl_TessCoord.x\n" /* Specifying the vertex’s position */
" + gl_in[1].gl_Position * gl_TessCoord.y\n" /* using the barycentric interpolation */
" + gl_in[2].gl_Position * gl_TessCoord.z;\n"
"}\n";
/* Fragment Shader code */
const char* TessellationShaderPrimitiveCoverage::m_fs_code = "${VERSION}\n"
"\n"
"precision highp float;\n"
"\n"
"uniform highp vec4 stencil_fail_color;\n"
"\n"
"layout(location = 0) out highp vec4 color;\n"
"\n"
"void main()\n"
"{\n"
" color = stencil_fail_color;\n"
"}\n";
/* A clear color used to set up framebuffer */
const glw::GLfloat TessellationShaderPrimitiveCoverage::m_clear_color[4] = {
255.f / 255.f, /* red */
128.f / 255.f, /* green */
64.f / 255.f, /* blue */
32.f / 255.f /* alpha */
};
/* A color used to draw differences between the tesselated primitive
* and the reference primitive (when stencil test passes)
*/
const glw::GLfloat TessellationShaderPrimitiveCoverage::m_stencil_pass_color[4] = {
32.f / 255.f, /* red */
64.f / 255.f, /* green */
128.f / 255.f, /* blue */
255.f / 255.f /* alpha */
};
/* Rendering area height */
const glw::GLuint TessellationShaderPrimitiveCoverage::m_height =
2048; /* minimum maximum as required by ES specification */
/* Number of components as used for color attachment */
const glw::GLuint TessellationShaderPrimitiveCoverage::m_n_components = 4;
/* Rendering area width */
const glw::GLuint TessellationShaderPrimitiveCoverage::m_width =
2048; /* minimum maximum as required by ES specification */
/* Buffer size for fetched pixels */
const glw::GLuint TessellationShaderPrimitiveCoverage::m_rendered_data_buffer_size = m_width /* width */
* m_height /* height */
* m_n_components /* components */;
/** Constructor
*
* @param context Test context
* @param name Test case's name
* @param description Test case's description
**/
TessellationShaderPrimitiveCoverage::TessellationShaderPrimitiveCoverage(Context& context,
const ExtParameters& extParams)
: TestCaseBase(context, extParams, "primitive_coverage",
"Verifies that no fragments are generated more than once when the "
"rendering pipeline (consisting of TC+TE stages) generates a "
"tessellated full-screen quad or two tessellated triangles.")
, m_vao_id(0)
, m_quad_tessellation_po_id(0)
, m_stencil_verification_po_id(0)
, m_triangles_tessellation_po_id(0)
, m_bo_id(0)
, m_fs_id(0)
, m_quad_tessellation_tcs_id(0)
, m_quad_tessellation_tes_id(0)
, m_triangles_tessellation_tcs_id(0)
, m_triangles_tessellation_tes_id(0)
, m_vs_id(0)
, m_fbo_id(0)
, m_color_rbo_id(0)
, m_stencil_rbo_id(0)
, m_rendered_data_buffer(DE_NULL)
{
/* Nothing to be done here */
}
/** Deinitializes all ES objects created for the test. */
void TessellationShaderPrimitiveCoverage::deinit(void)
{
/* Deinitialize base class */
TestCaseBase::deinit();
if (!m_is_tessellation_shader_supported)
{
return;
}
/* Retrieve ES entry-points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Reset OpenGL ES state */
gl.useProgram(0);
gl.bindBuffer(GL_ARRAY_BUFFER, 0);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.disable(GL_STENCIL_TEST);
gl.bindVertexArray(0);
/* Reset GL_PATCH_VERTICES_EXT to the default value. */
gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed!");
/* Delete buffers */
if (m_rendered_data_buffer != DE_NULL)
{
free(m_rendered_data_buffer);
m_rendered_data_buffer = DE_NULL;
}
/* Delete program and shader objects */
if (m_quad_tessellation_po_id != 0)
{
gl.deleteProgram(m_quad_tessellation_po_id);
m_quad_tessellation_po_id = 0;
}
if (m_stencil_verification_po_id != 0)
{
gl.deleteProgram(m_stencil_verification_po_id);
m_stencil_verification_po_id = 0;
}
if (m_triangles_tessellation_po_id != 0)
{
gl.deleteProgram(m_triangles_tessellation_po_id);
m_triangles_tessellation_po_id = 0;
}
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_quad_tessellation_tcs_id != 0)
{
gl.deleteShader(m_quad_tessellation_tcs_id);
m_quad_tessellation_tcs_id = 0;
}
if (m_quad_tessellation_tes_id != 0)
{
gl.deleteShader(m_quad_tessellation_tes_id);
m_quad_tessellation_tes_id = 0;
}
if (m_triangles_tessellation_tcs_id != 0)
{
gl.deleteShader(m_triangles_tessellation_tcs_id);
m_triangles_tessellation_tcs_id = 0;
}
if (m_triangles_tessellation_tes_id != 0)
{
gl.deleteShader(m_triangles_tessellation_tes_id);
m_triangles_tessellation_tes_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
/* Delete framebuffer and renderbuffer objects */
if (m_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_fbo_id);
m_fbo_id = 0;
}
if (m_color_rbo_id != 0)
{
gl.deleteRenderbuffers(1, &m_color_rbo_id);
m_color_rbo_id = 0;
}
if (m_stencil_rbo_id != 0)
{
gl.deleteRenderbuffers(1, &m_stencil_rbo_id);
m_stencil_rbo_id = 0;
}
/* Delete buffer objects */
if (m_bo_id != 0)
{
gl.deleteBuffers(1, &m_bo_id);
m_bo_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
}
/** Initializes the test.
*
* Note the function throws exception should an error occur!
**/
void TessellationShaderPrimitiveCoverage::initTest(void)
{
/* Skip if GL_EXT_tessellation_shader extension is not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__);
}
/* Retrieve ES entry-points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate and bind VAO */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
/* Initialize objects used by the test */
initProgramObjects();
initFramebuffer();
initBufferObjects();
/* setup of pixels buffer for fetching data*/
m_rendered_data_buffer = (glw::GLubyte*)malloc(m_rendered_data_buffer_size);
/* Enable stencil test. */
gl.enable(GL_STENCIL_TEST);
GLU_EXPECT_NO_ERROR(gl.getError(), "Stencil test could not be enabled!");
/* Set up viewport */
gl.viewport(0 /* x */, 0 /* y */, m_width, m_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() failed!");
}
/** Initializes program objects used by the test.
*
* Note the function throws exception should an error occur!
**/
void TessellationShaderPrimitiveCoverage::initProgramObjects(void)
{
/* Retrieve ES entry-points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Create program objects needed for the test */
m_stencil_verification_po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed!");
m_quad_tessellation_po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed!");
m_triangles_tessellation_po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed!");
/* Set up shader objects */
m_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_VERTEX_SHADER) failed!");
m_quad_tessellation_tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_CONTROL_SHADER_EXT) failed!");
m_quad_tessellation_tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_EVALUATION_SHADER_EXT) failed!");
m_triangles_tessellation_tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_CONTROL_SHADER_EXT) failed!");
m_triangles_tessellation_tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_EVALUATION_SHADER_EXT) failed!");
m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_FRAGMENT_SHADER) failed!");
/* Build a program object that does not define the tessellation stage */
if (!buildProgram(m_stencil_verification_po_id, m_vs_id, 1, &m_vs_code, m_fs_id, 1, &m_fs_code))
{
TCU_FAIL("Could not create a program object");
}
/* Build a program object that uses quad tessellation */
if (!buildProgram(m_quad_tessellation_po_id, m_vs_id, 1, &m_vs_code, m_quad_tessellation_tcs_id, 1,
&m_quad_tessellation_tcs_code, m_quad_tessellation_tes_id, 1, &m_quad_tessellation_tes_code,
m_fs_id, 1, &m_fs_code))
{
TCU_FAIL("Could not create a program object");
}
/* Build a program object that uses triangle tessellation */
if (!buildProgram(m_triangles_tessellation_po_id, m_vs_id, 1, &m_vs_code, m_triangles_tessellation_tcs_id, 1,
&m_triangles_tessellation_tcs_code, m_triangles_tessellation_tes_id, 1,
&m_triangles_tessellation_tes_code, m_fs_id, 1, &m_fs_code))
{
TCU_FAIL("Could not create a program object");
}
}
/** Initializes a framebuffer object.
*
* Note the function throws exception should an error occur!
**/
void TessellationShaderPrimitiveCoverage::initFramebuffer(void)
{
/* Retrieve ES entry-points. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Framebuffer setup */
gl.genFramebuffers(1, &m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() failed!");
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() failed!");
/* Set up a renderbuffer object and bind it as a color attachment to the
* framebuffer object */
gl.genRenderbuffers(1, &m_color_rbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers() failed!");
gl.bindRenderbuffer(GL_RENDERBUFFER, m_color_rbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed!");
gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, m_width, m_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage() failed!");
gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_color_rbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer() failed!");
/* Set up a renderbuffer object and bind it as a stencil attachment to
* the framebuffer object */
gl.genRenderbuffers(1, &m_stencil_rbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers() failed!");
gl.bindRenderbuffer(GL_RENDERBUFFER, m_stencil_rbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed!");
gl.renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, m_width, m_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage() failed!");
gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencil_rbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer() failed!");
gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed!");
/* Check framebuffer completness */
if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
TCU_FAIL("Test framebuffer has been reported as incomplete");
}
}
/** Initializes buffer objects used by the test.
*
* Note the function throws exception should an error occur!
**/
void TessellationShaderPrimitiveCoverage::initBufferObjects(void)
{
/* Retrieve ES entry-points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up an array of vertices that will be fed into vertex shader */
glw::GLfloat vertices[6 /* vertices */ * m_n_components /* components */] = {
-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 0.0f, 1.0f, /* A half-screen triangle (patch) (CCW) is defined until here */
1.0f, 1.0f, 0.0f, 1.0f, /* A full screen quad (patch) (CCW) is defined until here */
-1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f /* A full screen quad (2 triangles) (CCW) is defined until here */
};
/* Configure a buffer object to hold vertex data */
gl.genBuffers(1, &m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed!");
gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed!");
gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed!");
/* Configure "position" vertex attribute array and enable it */
gl.vertexAttribPointer(0, /* index */
4, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized */
0, /* stride */
0); /* pointer */
GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() failed!");
gl.enableVertexAttribArray(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() failed!");
}
/** The function:
*
* 1) Clears the FBO's color/stencil attachments with zeros;
* 2) Issues a draw call using a program object, for which either triangles or quads
* tessellation has been enabled. This draw call updates both color and stencil
* buffers of the framebuffer: fragment shader stores vec4(1) in the color attachment
* and stencil index is set to 0x1 for all affected fragments;
* 3) Issues a draw call using another program object, this time without tessellation
* stage enabled. Stencil test is configured to only pass those fragments, for which
* stencil index is not equal to 0xFF.
*
* Note the function throws exception should an error occur!
*
* @param po_id program object handle to draw tesselated primitive
* @param n_patch_vertices number of input vertices for a single patch (must
* be 3 for triangles or 4 for quads).
* @param n_draw_call_vertices number of input vertices for a reference draw call
* (must be 3 for triangle or 6 for quad (2 triangles)).
* @param inner_levels array of the inner tessellation levels
* @param outer_levels array of the outer tessellation levels
*
* @return false if the test failed, or true if test passed.
**/
void TessellationShaderPrimitiveCoverage::drawPatch(glw::GLuint po_id, glw::GLuint n_patch_vertices,
glw::GLuint n_draw_call_vertices, const glw::GLfloat inner_levels[],
const glw::GLfloat outer_levels[])
{
/* Sanity check */
DE_ASSERT(n_draw_call_vertices == 3 || n_draw_call_vertices == 6);
/* Retrieve ES entry-points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Activate user-provided program object with tessellation stage defined*/
gl.useProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed!");
/* Set up tessellation levels */
glw::GLint innerLevelUniformLocation = -1;
glw::GLint outerLevelUniformLocation = -1;
innerLevelUniformLocation = gl.getUniformLocation(po_id, "innerLevel");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() failed!");
outerLevelUniformLocation = gl.getUniformLocation(po_id, "outerLevel");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() failed!");
gl.uniform2fv(innerLevelUniformLocation, 1 /* count */, inner_levels);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform2fv() failed!");
gl.uniform4fv(outerLevelUniformLocation, 1 /* count */, outer_levels);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() failed!");
gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, n_patch_vertices);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed!");
/* Set up clear color */
gl.clearColor(m_clear_color[0], /* red */
m_clear_color[1], /* green */
m_clear_color[2], /* blue */
m_clear_color[3]); /* alpha */
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor() failed!");
/* Set up fragment color to be used for the first stage */
glw::GLint stencilPassColorUniformLocation = -1;
stencilPassColorUniformLocation = gl.getUniformLocation(po_id, "stencil_fail_color");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() failed!");
gl.uniform4fv(stencilPassColorUniformLocation, 1, m_stencil_pass_color);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() failed!");
/* Draw to stencil buffer */
gl.clearStencil(0 /* s */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearStencil() failed!");
gl.clear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() failed!");
gl.stencilOp(GL_REPLACE /* sfail */, GL_KEEP /* dpfail */, GL_KEEP /* dppass */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glStencilOp() failed!");
gl.stencilFunc(GL_NEVER /* func */, 1 /* ref */, 0xFF /* mask */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glStencilFunc() failed!");
gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, n_patch_vertices);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed");
/* Now verify the stencil buffer data contents by doing another
* full-screen draw call. This time without any tessellation.
* The pass will output fragments of predefined color, if stencil
* test passes (which will only happen if the stencil buffer is
* not filled with predefined index value).
*/
gl.useProgram(m_stencil_verification_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed!");
/* Color setup for 2nd program*/
stencilPassColorUniformLocation = gl.getUniformLocation(m_stencil_verification_po_id, "stencil_fail_color");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() failed!");
gl.uniform4fv(stencilPassColorUniformLocation, 1 /* count */, m_stencil_pass_color);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() failed!");
/* Draw to framebuffer */
gl.stencilOp(GL_KEEP /* sfail */, GL_KEEP /* dpfail */, GL_KEEP /* dppass */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glStencilOp() failed!");
gl.stencilFunc(GL_NOTEQUAL /* func */, 1 /* ref */, 0xFF /* mask */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glStencilFunc() failed!");
gl.drawArrays(GL_TRIANGLES, 0 /* first */, n_draw_call_vertices);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed");
}
/** Retrieves data from test FBO's zeroth color attachment and verifies
* no cracks are present.
*
* Note the function throws exception should an error occur!
*
* @return false if the check failed or true if check passed.
**/
bool TessellationShaderPrimitiveCoverage::verifyDrawBufferContents(void)
{
/* Retrieve ES entry-points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Fetch the data */
gl.readBuffer(GL_COLOR_ATTACHMENT0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadBuffer() failed");
gl.readPixels(0, /* x */
0, /* y */
m_height, m_width, GL_RGBA, /* format */
GL_UNSIGNED_BYTE, /* type */
m_rendered_data_buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed");
/* Verify result data */
bool result = true;
for (glw::GLuint n_pixel = 0; n_pixel < (m_rendered_data_buffer_size >> 2); n_pixel += m_n_components)
{
glw::GLubyte expected_result_ubyte[] = { (glw::GLubyte)(m_clear_color[0] * 255.0f),
(glw::GLubyte)(m_clear_color[1] * 255.0f),
(glw::GLubyte)(m_clear_color[2] * 255.0f),
(glw::GLubyte)(m_clear_color[3] * 255.0f) };
glw::GLubyte rendered_result_ubyte[] = { m_rendered_data_buffer[n_pixel + 0],
m_rendered_data_buffer[n_pixel + 1],
m_rendered_data_buffer[n_pixel + 2],
m_rendered_data_buffer[n_pixel + 3] };
if (memcmp(expected_result_ubyte, rendered_result_ubyte, sizeof(rendered_result_ubyte)) != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Rendered pixel at index (" << n_pixel << ") of value"
" ("
<< rendered_result_ubyte[0] << ", " << rendered_result_ubyte[1] << ", "
<< rendered_result_ubyte[2] << ", " << rendered_result_ubyte[3]
<< ") does not match expected pixel"
" ("
<< expected_result_ubyte[0] << ", " << expected_result_ubyte[1] << ", "
<< expected_result_ubyte[2] << ", " << expected_result_ubyte[3] << ")"
<< tcu::TestLog::EndMessage;
result = false;
break;
} /* if (rendered pixel is invalid) */
} /* for (all pixels) */
return result;
}
/** Executes the test.
*
* Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
*
* Note the function throws exception should an error occur!
*
* @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
**/
tcu::TestNode::IterateResult TessellationShaderPrimitiveCoverage::iterate(void)
{
/* Retriveing GL. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Initialize test */
initTest();
bool has_test_passed = true;
/* Define tessellation level values that we will use to draw single tessellated primitive */
const glw::GLfloat inner_tess_levels_single[] = { 1.0f, 1.0f };
const glw::GLfloat outer_tess_levels_single[] = { 1.0f, 1.0f, 1.0f, 1.0f };
/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
glw::GLint gl_max_tess_gen_level_value = 0;
gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
/* Verify no cracks can be found if a degenerate triangle
* is outputted by the tessellator */
drawPatch(m_triangles_tessellation_po_id, 3 /* n_patch_vertices */, 3 /* n_triangle_vertices */,
inner_tess_levels_single, outer_tess_levels_single);
has_test_passed &= verifyDrawBufferContents();
/* Verify no cracks can be found if multiple triangles
* are outputted by the tessellator.
*/
_tessellation_levels_set levels_sets = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, gl_max_tess_gen_level_value,
TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE);
for (_tessellation_levels_set_const_iterator set_iterator = levels_sets.begin(); set_iterator != levels_sets.end();
set_iterator++)
{
const _tessellation_levels& set = *set_iterator;
drawPatch(m_triangles_tessellation_po_id, 3 /* n_patch_vertices */, 3 /* n_triangle_vertices */, set.inner,
set.outer);
has_test_passed &= verifyDrawBufferContents();
}
/* Verify no cracks can be found if a degenerate quad
* is outputted by the tessellator.
*/
drawPatch(m_quad_tessellation_po_id, 4 /* n_patch_vertices */, 6 /* n_triangle_vertices (quad == 2 triangles) */,
inner_tess_levels_single, outer_tess_levels_single);
has_test_passed &= verifyDrawBufferContents();
/* Verify no cracks can be found if multiple triangles
* (to which the generated quads will be broken into)
* are outputted by the tessellator.
*/
levels_sets = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, gl_max_tess_gen_level_value,
TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE);
for (_tessellation_levels_set_const_iterator set_iterator = levels_sets.begin(); set_iterator != levels_sets.end();
set_iterator++)
{
const _tessellation_levels& set = *set_iterator;
drawPatch(m_quad_tessellation_po_id, 4 /* n_patch_vertices */,
6 /* n_triangle_vertices (quad == 2 triangles) */, set.inner, set.outer);
has_test_passed &= verifyDrawBufferContents();
}
/* Has the test passed? */
if (has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
} /* namespace glcts */