blob: 84bd8d3a7552f3c944cc39e766f0265d54daa5de [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2015-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 gl3cCullDistanceTests.cpp
* \brief Cull Distance Test Suite Implementation
*/ /*-------------------------------------------------------------------*/
#include "gl3cCullDistanceTests.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "gluStrUtil.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include <cmath>
#include <sstream>
#include <string>
#include <vector>
#ifndef GL_MAX_CULL_DISTANCES
#define GL_MAX_CULL_DISTANCES (0x82F9)
#endif
#ifndef GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES
#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES (0x82FA)
#endif
namespace glcts
{
/** @brief Build OpenGL program
*
* @param [in] gl OpenGL function bindings
* @param [in] testCtx Context
* @param [in] cs_body Compute shader source code
* @param [in] fs_body Fragment shader source code
* @param [in] gs_body Geometric shader source code
* @param [in] tc_body Tessellation control shader source code
* @param [in] te_body Tessellation evaluation shader source code
* @param [in] vs_body Vertex shader source code
* @param [in] n_tf_varyings Number of transform feedback varyings
* @param [in] tf_varyings Transform feedback varyings names
*
* @param [out] out_program If succeeded output program GL handle, 0 otherwise.
*/
void CullDistance::Utilities::buildProgram(const glw::Functions& gl, tcu::TestContext& testCtx,
const glw::GLchar* cs_body, const glw::GLchar* fs_body,
const glw::GLchar* gs_body, const glw::GLchar* tc_body,
const glw::GLchar* te_body, const glw::GLchar* vs_body,
const glw::GLuint& n_tf_varyings, const glw::GLchar** tf_varyings,
glw::GLuint* out_program)
{
glw::GLuint po_id = 0;
struct _shaders_configuration
{
glw::GLenum type;
const glw::GLchar* body;
glw::GLuint id;
} shaders_configuration[] = { { GL_COMPUTE_SHADER, cs_body, 0 }, { GL_FRAGMENT_SHADER, fs_body, 0 },
{ GL_GEOMETRY_SHADER, gs_body, 0 }, { GL_TESS_CONTROL_SHADER, tc_body, 0 },
{ GL_TESS_EVALUATION_SHADER, te_body, 0 }, { GL_VERTEX_SHADER, vs_body, 0 } };
const glw::GLuint n_shaders_configuration = sizeof(shaders_configuration) / sizeof(shaders_configuration[0]);
/* Guard allocated OpenGL resources */
try
{
/* Create needed programs */
po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed.");
for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
{
if (shaders_configuration[n_shader_index].body != DE_NULL)
{
/* Generate shader object */
shaders_configuration[n_shader_index].id = gl.createShader(shaders_configuration[n_shader_index].type);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed");
glw::GLint compile_status = GL_FALSE;
const glw::GLuint so_id = shaders_configuration[n_shader_index].id;
/* Assign shader source code */
gl.shaderSource(shaders_configuration[n_shader_index].id, 1, /* count */
&shaders_configuration[n_shader_index].body, DE_NULL); /* length */
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed");
gl.compileShader(so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
gl.getShaderiv(so_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
if (compile_status == GL_FALSE)
{
std::vector<glw::GLchar> log_array(1);
glw::GLint log_length = 0;
std::string log_string("Failed to retrieve log");
/* Retrive compilation log length */
gl.getShaderiv(so_id, GL_INFO_LOG_LENGTH, &log_length);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
log_array.resize(log_length + 1, 0);
gl.getShaderInfoLog(so_id, log_length, DE_NULL, &log_array[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog() call failed.");
log_string = std::string(&log_array[0]);
testCtx.getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n"
<< "Shader type: " << shaders_configuration[n_shader_index].type << "\n"
<< "Shader compilation error log:\n"
<< log_string << "\n"
<< "Shader source code:\n"
<< shaders_configuration[n_shader_index].body << "\n"
<< tcu::TestLog::EndMessage;
TCU_FAIL("Shader compilation has failed.");
}
/* Also attach the shader to the corresponding program object */
gl.attachShader(po_id, so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed");
} /* if (shaders_configuration[n_shader_index].body != DE_NULL) */
} /* for (all shader object IDs) */
/* Set transform feedback if requested */
if (n_tf_varyings > 0)
{
gl.transformFeedbackVaryings(po_id, n_tf_varyings, tf_varyings, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed");
}
/* Try to link the program objects */
if (po_id != 0)
{
glw::GLint link_status = GL_FALSE;
gl.linkProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed");
gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed");
if (link_status == GL_FALSE)
{
std::vector<glw::GLchar> log_array(1);
glw::GLsizei log_length = 0;
std::string log_string;
/* Retreive compilation log length */
gl.getProgramiv(po_id, GL_INFO_LOG_LENGTH, &log_length);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
log_array.resize(log_length + 1, 0);
/* Retreive compilation log */
gl.getProgramInfoLog(po_id, log_length, DE_NULL, &log_array[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog() call failed.");
log_string = std::string(&log_array[0]);
/* Log linking error message */
testCtx.getLog() << tcu::TestLog::Message << "Program linking has failed.\n"
<< "Linking error log:\n"
<< log_string << "\n"
<< tcu::TestLog::EndMessage;
/* Log shader source code of shaders involved */
for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
{
if (shaders_configuration[n_shader_index].body != DE_NULL)
{
testCtx.getLog() << tcu::TestLog::Message << "Shader source code of type "
<< shaders_configuration[n_shader_index].type << " follows:\n"
<< shaders_configuration[n_shader_index].body << "\n"
<< tcu::TestLog::EndMessage;
}
}
TCU_FAIL("Program linking failed");
}
} /* if (po_id != 0) */
/* Delete all shaders we've created */
for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
{
const glw::GLuint so_id = shaders_configuration[n_shader_index].id;
if (so_id != 0)
{
gl.deleteShader(so_id);
shaders_configuration[n_shader_index].id = 0;
GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call failed.");
}
}
/* Store the result progrtam IDs */
*out_program = po_id;
}
catch (...)
{
/* Delete all shaders we've created */
for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
{
const glw::GLuint so_id = shaders_configuration[n_shader_index].id;
if (so_id != 0)
{
gl.deleteShader(so_id);
shaders_configuration[n_shader_index].id = 0;
}
}
/* Delete the program object */
if (po_id != 0)
{
gl.deleteProgram(po_id);
po_id = 0;
}
/* Rethrow */
throw;
}
}
/** @brief Replace all occurences of a substring in a string by a substring
*
* @param [in,out] str string to be edited
* @param [in] from substring to be replaced
* @param [out] to new substring
*/
void CullDistance::Utilities::replaceAll(std::string& str, const std::string& from, const std::string& to)
{
for (size_t start_pos = str.find(from, 0); start_pos != std::string::npos; start_pos = str.find(from, start_pos))
{
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return;
}
/** @brief Convert integer to string representation
*
* @param [in] integer input integer to be converted
*
* @return String representation of integer
*/
std::string CullDistance::Utilities::intToString(glw::GLint integer)
{
std::stringstream temp_sstream;
temp_sstream << integer;
return temp_sstream.str();
}
/** Constructor.
*
* @param context Rendering context handle.
**/
CullDistance::APICoverageTest::APICoverageTest(deqp::Context& context)
: TestCase(context, "coverage", "Cull Distance API Coverage Test")
, m_bo_id(0)
, m_cs_id(0)
, m_cs_to_id(0)
, m_fbo_draw_id(0)
, m_fbo_draw_to_id(0)
, m_fbo_read_id(0)
, m_fs_id(0)
, m_gs_id(0)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_vao_id(0)
, m_vs_id(0)
{
/* Left blank on purpose */
}
/** @brief Cull Distance API Coverage Test deinitialization */
void CullDistance::APICoverageTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_bo_id != 0)
{
gl.deleteBuffers(1, &m_bo_id);
m_bo_id = 0;
}
if (m_cs_id != 0)
{
gl.deleteShader(m_cs_id);
m_cs_id = 0;
}
if (m_cs_to_id != 0)
{
gl.deleteTextures(1, &m_cs_to_id);
m_cs_to_id = 0;
}
if (m_fbo_draw_id != 0)
{
gl.deleteFramebuffers(1, &m_fbo_draw_id);
m_fbo_draw_id = 0;
}
if (m_fbo_draw_to_id != 0)
{
gl.deleteTextures(1, &m_fbo_draw_to_id);
m_fbo_draw_to_id = 0;
}
if (m_fbo_read_id != 0)
{
gl.deleteFramebuffers(1, &m_fbo_read_id);
m_fbo_read_id = 0;
}
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
/* Restore default pack alignment value */
gl.pixelStorei(GL_PACK_ALIGNMENT, 4);
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult CullDistance::APICoverageTest::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* This test should only be executed if ARB_cull_distance is supported, or if
* we're running a GL4.5 context
*/
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_cull_distance") &&
!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
{
throw tcu::NotSupportedError("GL_ARB_cull_distance is not supported");
}
/* Check that calling GetIntegerv with MAX_CULL_DISTANCES doesn't generate
* any errors and returns a value at least 8.
*
* Check that calling GetIntegerv with MAX_COMBINED_CLIP_AND_CULL_DISTANCES
* doesn't generate any errors and returns a value at least 8.
*
*/
glw::GLint error_code = GL_NO_ERROR;
glw::GLint gl_max_cull_distances_value = 0;
glw::GLint gl_max_combined_clip_and_cull_distances_value = 0;
gl.getIntegerv(GL_MAX_CULL_DISTANCES, &gl_max_cull_distances_value);
error_code = gl.getError();
if (error_code != GL_NO_ERROR)
{
m_testCtx.getLog() << tcu::TestLog::Message << "glGetIntegerv() returned error code "
<< "[" << glu::getErrorStr(error_code) << "] for GL_MAX_CULL_DISTANCES"
" query instead of GL_NO_ERROR"
<< tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
return STOP;
}
gl.getIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, &gl_max_combined_clip_and_cull_distances_value);
error_code = gl.getError();
if (error_code != GL_NO_ERROR)
{
m_testCtx.getLog() << tcu::TestLog::Message << "glGetIntegerv() returned error code "
<< "[" << glu::getErrorStr(error_code) << "] for "
"GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES query "
"instead of GL_NO_ERROR"
<< tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
return STOP;
}
/* Before we proceed with the two other tests, initialize a buffer & a texture
* object we will need to capture data from the programs */
static const glw::GLuint bo_size = sizeof(int) * 4 /* components */ * 4 /* result points */;
gl.genBuffers(1, &m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
gl.genFramebuffers(1, &m_fbo_draw_id);
gl.genFramebuffers(1, &m_fbo_read_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call(s) failed.");
gl.genTextures(1, &m_cs_to_id);
gl.genTextures(1, &m_fbo_draw_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed.");
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */
m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() or glBindBufferBase() call(s) failed.");
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL, GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
for (glw::GLuint n_to_id = 0; n_to_id < 2; /* CS, FBO */ ++n_to_id)
{
gl.bindTexture(GL_TEXTURE_2D, (n_to_id == 0) ? m_cs_to_id : m_fbo_draw_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
GL_R32I, 1, /* width */
1); /* height */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed.");
}
gl.bindImageTexture(0, /* unit */
m_cs_to_id, 0, /* level */
GL_FALSE, /* layered */
0, /* layer */
GL_WRITE_ONLY, GL_R32I);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture() call failed.");
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_draw_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_fbo_draw_to_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
gl.viewport(0, /* x */
0, /* y */
1, /* width */
1); /* height */
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed.");
gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPixelStorei() call failed.");
/* There are two new GL constants, where value we need to verify */
struct _run
{
const glw::GLchar* essl_token_value;
glw::GLenum gl_enum;
glw::GLint gl_value;
glw::GLint min_value;
const glw::GLchar* name;
} runs[] = { { "gl_MaxCullDistances", GL_MAX_CULL_DISTANCES, gl_max_cull_distances_value, 8 /*minimum required */,
"GL_MAX_CULL_DISTANCES" },
{ "gl_MaxCombinedClipAndCullDistances", GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES,
gl_max_combined_clip_and_cull_distances_value, 8 /*minimum required */,
"GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES" } };
static const glw::GLuint n_runs = sizeof(runs) / sizeof(runs[0]);
for (glw::GLuint n_run = 0; n_run < n_runs; ++n_run)
{
_run& current_run = runs[n_run];
static const struct _stage
{
bool use_cs;
bool use_fs;
bool use_gs;
bool use_tc;
bool use_te;
bool use_vs;
const glw::GLchar* fs_input;
const glw::GLchar* gs_input;
const glw::GLchar* tc_input;
const glw::GLchar* te_input;
const glw::GLchar* tf_output_name;
const glw::GLenum tf_mode;
glw::GLenum draw_call_mode;
glw::GLuint n_draw_call_vertices;
} stages[] = { /* CS only test */
{
/* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
true, false, false, false, false, false,
NULL, /* fs_input */
NULL, /* gs_input */
NULL, /* tc_input */
NULL, /* te_input */
NULL, /* tf_output_name */
GL_NONE, /* tf_mode */
GL_NONE, /* draw_call_mode */
0, /* n_draw_call_vertices */
},
/* VS+GS+TC+TE+FS test */
{
/* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
false, true, true, true, true, true,
"out_gs", /* fs_input */
"out_te", /* gs_input */
"out_vs", /* tc_input */
"out_tc", /* te_input */
"out_gs", /* tf_output_name */
GL_TRIANGLES, /* tf_mode */
GL_PATCHES, /* draw_call_mode */
3, /* n_draw_call_vertices */
},
/* VS+GS+FS test */
{
/* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
false, true, true, false, false, true,
"out_gs", /* fs_input */
"out_vs", /* gs_input */
NULL, /* tc_input */
NULL, /* te_input */
"out_gs", /* tf_output_name */
GL_TRIANGLES, /* tf_mode */
GL_POINTS, /* draw_call_mode */
1, /* n_draw_call_vertices */
},
/* VS+TC+TE+FS test */
{
/* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
false, true, false, true, true, true,
"out_te", /* fs_input */
NULL, /* gs_input */
"out_vs", /* tc_input */
"out_tc", /* te_input */
"out_te", /* tf_output_name */
GL_POINTS, /* tf_mode */
GL_PATCHES, /* draw_call_mode */
3 /* n_draw_call_vertices */
},
/* VS test */
{
/* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
false, false, false, false, false, true,
"out_vs", /* fs_input */
NULL, /* gs_input */
NULL, /* tc_input */
NULL, /* te_input */
"out_vs", /* tf_output_name */
GL_POINTS, /* tf_mode */
GL_POINTS, /* draw_call_mode */
1 /* n_draw_call_vertices */
}
};
const glw::GLuint n_stages = sizeof(stages) / sizeof(stages[0]);
/* Run through all test stages */
for (glw::GLuint n_stage = 0; n_stage < n_stages; ++n_stage)
{
/* Check for OpenGL feature support */
if (stages[n_stage].use_cs)
{
if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 3)) &&
!m_context.getContextInfo().isExtensionSupported("GL_ARB_compute_shader"))
{
continue; // no compute shader support
}
}
if (stages[n_stage].use_tc || stages[n_stage].use_te)
{
if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0)) &&
!m_context.getContextInfo().isExtensionSupported("GL_ARB_tessellation_shader"))
{
continue; // no tessellation shader support
}
}
/* Check that use of the GLSL built-in constant gl_MaxCullDistance in any
* shader stage (including compute shader) does not affect the shader
* compilation & program linking process.
*/
static const glw::GLchar* cs_body_template =
"#version 150\n"
"\n"
"#extension GL_ARB_compute_shader : require\n"
"#extension GL_ARB_cull_distance : require\n"
"#extension GL_ARB_shader_image_load_store : require\n"
"\n"
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
"\n"
"layout(r32i) uniform writeonly iimage2D result;\n"
"\n"
"void main()\n"
"{\n"
" imageStore(result, ivec2(0),ivec4(TOKEN) );\n"
"}\n";
std::string cs_body = cs_body_template;
static const glw::GLchar* fs_body_template = "#version 150\n"
"\n"
"#extension GL_ARB_cull_distance : require\n"
"\n"
"flat in int INPUT_FS_NAME;\n"
"out int out_fs;\n"
"\n"
"void main()\n"
"{\n"
" if (INPUT_FS_NAME == TOKEN)\n"
" {\n"
" out_fs = TOKEN;\n"
" }\n"
" else\n"
" {\n"
" out_fs = -1;\n"
" }\n"
"}\n";
std::string fs_body = fs_body_template;
static const glw::GLchar* gs_body_template =
"#version 150\n"
"\n"
"#extension GL_ARB_cull_distance : require\n"
"\n"
"flat in int INPUT_GS_NAME[];\n"
"flat out int out_gs;\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
"void main()\n"
"{\n"
" int result_value = (INPUT_GS_NAME[0] == TOKEN) ? TOKEN : -1;\n"
"\n"
/* Draw a full-screen quad */
" gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
" out_gs = result_value;\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
" out_gs = result_value;\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(1.0, 1.0, 0.0, 1.0);\n"
" out_gs = result_value;\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(1.0, -1.0, 0.0, 1.0);\n"
" out_gs = result_value;\n"
" EmitVertex();\n"
" EndPrimitive();\n"
"}\n";
std::string gs_body = gs_body_template;
static const glw::GLchar* tc_body_template =
"#version 400\n"
"\n"
"#extension GL_ARB_cull_distance : require\n"
"\n"
"layout(vertices = 1) out;\n"
"\n"
"flat in int INPUT_TC_NAME[];\n"
"flat out int out_tc [];\n"
"\n"
"void main()\n"
"{\n"
" int result_value = (INPUT_TC_NAME[0] == TOKEN) ? TOKEN : -1;\n"
"\n"
" out_tc[gl_InvocationID] = result_value;\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
" gl_TessLevelOuter[3] = 1.0;\n"
"}\n";
std::string tc_body = tc_body_template;
static const glw::GLchar* te_body_template =
"#version 400\n"
"\n"
"#extension GL_ARB_cull_distance : require\n"
"\n"
"flat in int INPUT_TE_NAME[];\n"
"flat out int out_te;\n"
"\n"
"layout(isolines, point_mode) in;\n"
"\n"
"void main()\n"
"{\n"
" int result_value = (INPUT_TE_NAME[0] == TOKEN) ? TOKEN : 0;\n"
"\n"
" out_te = result_value;\n"
"\n"
" gl_Position = vec4(0.0, 0.0, 0.0, 1.);\n"
"}\n";
std::string te_body = te_body_template;
static const glw::GLchar* vs_body_template = "#version 150\n"
"\n"
"#extension GL_ARB_cull_distance : require\n"
"\n"
"flat out int out_vs;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
" out_vs = TOKEN;\n"
"}\n";
std::string vs_body = vs_body_template;
const _stage& current_stage = stages[n_stage];
/* Build shader bodies */
struct _shader_body
{
std::string* body_ptr;
glw::GLenum gl_type;
} shader_bodies[] = { { &cs_body, GL_COMPUTE_SHADER }, { &fs_body, GL_FRAGMENT_SHADER },
{ &gs_body, GL_GEOMETRY_SHADER }, { &tc_body, GL_TESS_CONTROL_SHADER },
{ &te_body, GL_TESS_EVALUATION_SHADER }, { &vs_body, GL_VERTEX_SHADER } };
static const glw::GLchar* input_fs_token_string = "INPUT_FS_NAME";
static const glw::GLchar* input_gs_token_string = "INPUT_GS_NAME";
static const glw::GLchar* input_te_token_string = "INPUT_TE_NAME";
static const glw::GLchar* input_tc_token_string = "INPUT_TC_NAME";
static const glw::GLuint n_shader_bodies = sizeof(shader_bodies) / sizeof(shader_bodies[0]);
std::size_t token_position = std::string::npos;
static const glw::GLchar* token_string = "TOKEN";
for (glw::GLuint n_shader_body = 0; n_shader_body < n_shader_bodies; ++n_shader_body)
{
_shader_body& current_body = shader_bodies[n_shader_body];
/* Is this stage actually used? */
if (((current_body.gl_type == GL_COMPUTE_SHADER) && (!current_stage.use_cs)) ||
((current_body.gl_type == GL_FRAGMENT_SHADER) && (!current_stage.use_fs)) ||
((current_body.gl_type == GL_TESS_CONTROL_SHADER) && (!current_stage.use_tc)) ||
((current_body.gl_type == GL_TESS_EVALUATION_SHADER) && (!current_stage.use_te)) ||
((current_body.gl_type == GL_VERTEX_SHADER) && (!current_stage.use_vs)))
{
/* Skip the iteration. */
continue;
}
/* Iterate over all token and replace them with stage-specific values */
struct _token_value_pair
{
const glw::GLchar* token;
const glw::GLchar* value;
} token_value_pairs[] = {
/* NOTE: The last entry is filled by the switch() block below */
{ token_string, current_run.essl_token_value },
{ NULL, NULL },
};
const size_t n_token_value_pairs = sizeof(token_value_pairs) / sizeof(token_value_pairs[0]);
switch (current_body.gl_type)
{
case GL_COMPUTE_SHADER:
case GL_VERTEX_SHADER:
break;
case GL_FRAGMENT_SHADER:
{
token_value_pairs[1].token = input_fs_token_string;
token_value_pairs[1].value = current_stage.fs_input;
break;
}
case GL_GEOMETRY_SHADER:
{
token_value_pairs[1].token = input_gs_token_string;
token_value_pairs[1].value = current_stage.gs_input;
break;
}
case GL_TESS_CONTROL_SHADER:
{
token_value_pairs[1].token = input_tc_token_string;
token_value_pairs[1].value = current_stage.tc_input;
break;
}
case GL_TESS_EVALUATION_SHADER:
{
token_value_pairs[1].token = input_te_token_string;
token_value_pairs[1].value = current_stage.te_input;
break;
}
default:
TCU_FAIL("Unrecognized shader body type");
}
for (glw::GLuint n_pair = 0; n_pair < n_token_value_pairs; ++n_pair)
{
const _token_value_pair& current_pair = token_value_pairs[n_pair];
if (current_pair.token == NULL || current_pair.value == NULL)
{
continue;
}
while ((token_position = current_body.body_ptr->find(current_pair.token)) != std::string::npos)
{
current_body.body_ptr->replace(token_position, strlen(current_pair.token), current_pair.value);
}
} /* for (all token+value pairs) */
} /* for (all sader bodies) */
/* Build the test program */
CullDistance::Utilities::buildProgram(
gl, m_testCtx, current_stage.use_cs ? cs_body.c_str() : DE_NULL,
current_stage.use_fs ? fs_body.c_str() : DE_NULL, current_stage.use_gs ? gs_body.c_str() : DE_NULL,
current_stage.use_tc ? tc_body.c_str() : DE_NULL, current_stage.use_te ? te_body.c_str() : DE_NULL,
current_stage.use_vs ? vs_body.c_str() : DE_NULL, (current_stage.tf_output_name != NULL) ? 1 : 0,
(const glw::GLchar**)&current_stage.tf_output_name, &m_po_id);
/* Bind the test program */
DE_ASSERT(m_po_id != 0);
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
/* Execute the draw call. Transform Feed-back should be enabled for all iterations
* par the CS one, since we use a different tool to capture the result data in the
* latter case.
*/
if (!current_stage.use_cs)
{
gl.beginTransformFeedback(current_stage.tf_mode);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed.");
gl.drawArrays(current_stage.draw_call_mode, 0, /* first */
current_stage.n_draw_call_vertices); /* count */
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed.");
} /* if (uses_tf) */
else
{
gl.dispatchCompute(1, /* num_groups_x */
1, /* num_groups_y */
1); /* num_groups_z */
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() call failed.");
}
/* Verify the result values */
if (!current_stage.use_cs)
{
glw::GLint* result_data_ptr = DE_NULL;
/* Retrieve the data captured by Transform Feedback */
result_data_ptr = (glw::GLint*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
sizeof(unsigned int) * 1, GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed.");
if (*result_data_ptr != current_run.gl_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << current_run.name << " value "
"["
<< *result_data_ptr << "]"
" does not match the one reported by glGetIntegerv() "
"["
<< current_run.gl_value << "]" << tcu::TestLog::EndMessage;
TCU_FAIL("GL constant value does not match the ES SL equivalent");
}
if (*result_data_ptr < current_run.min_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << current_run.name << " value "
"["
<< *result_data_ptr << "]"
" does not meet the minimum specification requirements "
"["
<< current_run.min_value << "]" << tcu::TestLog::EndMessage;
TCU_FAIL("GL constant value does not meet minimum specification requirements");
}
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed.");
}
for (glw::GLuint n_stage_internal = 0; n_stage_internal < 2; /* CS, FS write to separate textures */
++n_stage_internal)
{
glw::GLuint to_id = (n_stage_internal == 0) ? m_cs_to_id : m_fbo_draw_to_id;
if (((n_stage_internal == 0) && (!current_stage.use_cs)) ||
((n_stage_internal == 1) && (!current_stage.use_fs)))
{
/* Skip the iteration */
continue;
}
/* Check the image data the test CS / FS should have written */
glw::GLint result_value = 0;
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, to_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
/* NOTE: We're using our custom read framebuffer here, so we'll be reading
* from the texture, that the writes have been issued to earlier. */
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "glMemoryBarrier() call failed.");
gl.readPixels(0, /* x */
0, /* y */
1, /* width */
1, /* height */
GL_RED_INTEGER, GL_INT, &result_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed.");
if (result_value != current_run.gl_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << current_run.name
<< " value accessible to the compute / fragment shader "
"["
<< result_value << "]"
" does not match the one reported by glGetIntegerv() "
"["
<< current_run.gl_value << "]" << tcu::TestLog::EndMessage;
TCU_FAIL("GL constant value does not match the ES SL equivalent");
}
if (result_value < current_run.min_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << current_run.name
<< " value accessible to the compute / fragment shader "
"["
<< result_value << "]"
" does not meet the minimum specification requirements "
"["
<< current_run.min_value << "]" << tcu::TestLog::EndMessage;
TCU_FAIL("GL constant value does not meet minimum specification requirements");
}
}
/* Clear the data buffer before we continue */
static const glw::GLubyte bo_clear_data[bo_size] = { 0 };
gl.bufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
bo_size, bo_clear_data);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call failed.");
/* Clear the texture mip-map before we continue */
glw::GLint clear_values[4] = { 0, 0, 0, 0 };
gl.clearBufferiv(GL_COLOR, 0, /* drawbuffer */
clear_values);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferiv() call failed.");
/* Release program before we move on to the next iteration */
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
} /* for (all stages) */
} /* for (both runs) */
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param context Rendering context handle.
**/
CullDistance::FunctionalTest::FunctionalTest(deqp::Context& context)
: TestCase(context, "functional", "Cull Distance Functional Test")
, m_bo_data()
, m_bo_id(0)
, m_fbo_id(0)
, m_po_id(0)
, m_render_primitives(0)
, m_render_vertices(0)
, m_sub_grid_cell_size(0)
, m_to_id(0)
, m_vao_id(0)
, m_to_height(512)
, m_to_width(512)
, m_to_pixel_data_cache()
{
/* Left blank on purpose */
}
/** @brief Build OpenGL program for functional tests
*
* @param [in] clipdistances_array_size use size of gl_ClipDistance array
* @param [in] culldistances_array_size use size of gl_CullDistance array
* @param [in] dynamic_index_writes use dunamic indexing for setting the gl_ClipDistance and gl_CullDistance arrays
* @param [in] primitive_mode primitive_mode will be used for rendering
* @param [in] redeclare_clipdistances redeclare gl_ClipDistance
* @param [in] redeclare_culldistances redeclare gl_CullDistance
* @param [in] use_core_functionality use core OpenGL functionality
* @param [in] use_gs use geometry shader
* @param [in] use_ts use tessellation shader
* @param [in] fetch_culldistance_from_fs fetch check sum of gl_ClipDistance and gl_CullDistance from fragment shader
*/
void CullDistance::FunctionalTest::buildPO(glw::GLuint clipdistances_array_size, glw::GLuint culldistances_array_size,
bool dynamic_index_writes, _primitive_mode primitive_mode,
bool redeclare_clipdistances, bool redeclare_culldistances,
bool use_core_functionality, bool use_gs, bool use_ts,
bool fetch_culldistance_from_fs)
{
deinitPO();
/* Form the vertex shader */
glw::GLuint clipdistances_input_size =
clipdistances_array_size > 0 ? clipdistances_array_size : 1; /* Avoid zero-sized array compilation error */
glw::GLuint culldistances_input_size =
culldistances_array_size > 0 ? culldistances_array_size : 1; /* Avoid zero-sized array compilation error */
static const glw::GLchar* dynamic_array_setters =
"\n"
"#if TEMPLATE_N_GL_CLIPDISTANCE_ENTRIES\n"
" for (int n_clipdistance_entry = 0;\n"
" n_clipdistance_entry < TEMPLATE_N_GL_CLIPDISTANCE_ENTRIES;\n"
" ++n_clipdistance_entry)\n"
" {\n"
" ASSIGN_CLIP_DISTANCE(n_clipdistance_entry);\n"
" }\n"
"#endif"
"\n"
"#if TEMPLATE_N_GL_CULLDISTANCE_ENTRIES \n"
" for (int n_culldistance_entry = 0;\n"
" n_culldistance_entry < TEMPLATE_N_GL_CULLDISTANCE_ENTRIES;\n"
" ++n_culldistance_entry)\n"
" {\n"
" ASSIGN_CULL_DISTANCE(n_culldistance_entry);\n"
" }\n"
"#endif\n";
static const glw::GLchar* core_functionality = "#version 450\n";
static const glw::GLchar* extention_functionality = "#version 440\n"
"\n"
"#extension GL_ARB_cull_distance : require\n"
"\n"
"#ifndef GL_ARB_cull_distance\n"
" #error GL_ARB_cull_distance is undefined\n"
"#endif\n";
static const glw::GLchar* fetch_function = "highp float fetch()\n"
"{\n"
" highp float sum = 0.0;\n"
"\n"
"TEMPLATE_SUM_SETTER"
"\n"
" return sum / TEMPLATE_SUM_DIVIDER;\n"
"}\n"
"\n"
"#define ASSIGN_RETURN_VALUE fetch()";
static const glw::GLchar* fs_template = "TEMPLATE_HEADER_DECLARATION\n"
"\n"
"TEMPLATE_REDECLARE_CLIPDISTANCE\n"
"TEMPLATE_REDECLARE_CULLDISTANCE\n"
"\n"
"TEMPLATE_ASSIGN_RETURN_VALUE\n"
"\n"
"out vec4 out_fs;\n"
"\n"
"/* Fragment shader main function */\n"
"void main()\n"
"{\n"
" out_fs = vec4(ASSIGN_RETURN_VALUE, 1.0, 1.0, 1.0);\n"
"}\n";
static const glw::GLchar* gs_template = "TEMPLATE_HEADER_DECLARATION\n"
"\n"
"TEMPLATE_LAYOUT_IN\n"
"TEMPLATE_LAYOUT_OUT\n"
"\n"
"TEMPLATE_REDECLARE_CLIPDISTANCE\n"
"TEMPLATE_REDECLARE_CULLDISTANCE\n"
"\n"
"#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
"#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
"\n"
"/* Geometry shader (passthrough) main function */\n"
"void main()\n"
"{\n"
" for (int n_vertex_index = 0;\n"
" n_vertex_index < gl_in.length();\n"
" n_vertex_index ++)\n"
" {\n"
" gl_Position = gl_in[n_vertex_index].gl_Position;\n"
"\n"
" TEMPLATE_ARRAY_SETTERS\n"
"\n"
" EmitVertex();\n"
" }\n"
"\n"
" EndPrimitive();\n"
"}\n";
static const glw::GLchar* tc_template =
"TEMPLATE_HEADER_DECLARATION\n"
"\n"
"TEMPLATE_LAYOUT_OUT\n"
"\n"
"out gl_PerVertex {\n"
"TEMPLATE_REDECLARE_CLIPDISTANCE\n"
"TEMPLATE_REDECLARE_CULLDISTANCE\n"
"vec4 gl_Position;\n"
"} gl_out[];\n"
"\n"
"#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
"#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
"\n"
"/* Tesselation control shader main function */\n"
"void main()\n"
"{\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
" gl_TessLevelOuter[3] = 1.0;\n"
" /* Clipdistance and culldistance array setters */\n"
" {\n"
" TEMPLATE_ARRAY_SETTERS\n"
" }\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
"}\n";
static const glw::GLchar* te_template = "TEMPLATE_HEADER_DECLARATION\n"
"\n"
"TEMPLATE_LAYOUT_IN\n"
"\n"
"in gl_PerVertex {\n"
"TEMPLATE_REDECLARE_IN_CLIPDISTANCE\n"
"TEMPLATE_REDECLARE_IN_CULLDISTANCE\n"
"vec4 gl_Position;\n"
"} gl_in[];\n"
"\n"
"TEMPLATE_REDECLARE_CLIPDISTANCE\n"
"TEMPLATE_REDECLARE_CULLDISTANCE\n"
"\n"
"#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
"#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
"\n"
"/* Tesselation evaluation shader main function */\n"
"void main()\n"
"{\n"
" /* Clipdistance and culldistance array setters */\n"
" {\n"
" TEMPLATE_ARRAY_SETTERS\n"
" }\n"
" gl_Position = TEMPLATE_OUT_FORMULA;\n"
"}\n";
static const glw::GLchar* vs_template =
"TEMPLATE_HEADER_DECLARATION\n"
"\n"
"in float clipdistance_data[TEMPLATE_CLIPDISTANCE_INPUT_SIZE];\n"
"in float culldistance_data[TEMPLATE_CULLDISTANCE_INPUT_SIZE];\n"
"in vec2 position;\n"
"\n"
"TEMPLATE_REDECLARE_CLIPDISTANCE\n"
"TEMPLATE_REDECLARE_CULLDISTANCE\n"
"\n"
"#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
"#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
"\n"
"/* Vertex shader main function */\n"
"void main()\n"
"{\n"
" /* Clipdistance and culldistance array setters */\n"
" {\n"
" TEMPLATE_ARRAY_SETTERS\n"
" }\n"
" gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, 0.0, 1.0);\n"
"}\n";
std::string* shader_body_string_fs = DE_NULL;
std::string* shader_body_string_gs = DE_NULL;
std::string* shader_body_string_tc = DE_NULL;
std::string* shader_body_string_te = DE_NULL;
std::string* shader_body_string_vs = DE_NULL;
std::string shader_header_declaration = use_core_functionality ? core_functionality : extention_functionality;
struct _shaders_configuration
{
glw::GLenum type;
const glw::GLchar* shader_template;
std::string body;
const bool use;
} shaders_configuration[] = { {
GL_FRAGMENT_SHADER, fs_template, std::string(), true,
},
{
GL_GEOMETRY_SHADER, gs_template, std::string(), use_gs,
},
{
GL_TESS_CONTROL_SHADER, tc_template, std::string(), use_ts,
},
{
GL_TESS_EVALUATION_SHADER, te_template, std::string(), use_ts,
},
{
GL_VERTEX_SHADER, vs_template, std::string(), true,
} };
const glw::GLuint n_shaders_configuration = sizeof(shaders_configuration) / sizeof(shaders_configuration[0]);
/* Construct shader bodies out of templates */
for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
{
if (shaders_configuration[n_shader_index].use)
{
std::string array_setters;
std::string clipdistance_array_declaration;
std::string culldistance_array_declaration;
std::string clipdistance_in_array_declaration;
std::string culldistance_in_array_declaration;
std::string& shader_source = shaders_configuration[n_shader_index].body;
/* Copy template into shader body source */
shader_source = shaders_configuration[n_shader_index].shader_template;
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_HEADER_DECLARATION"),
shader_header_declaration);
/* Shader-specific actions */
switch (shaders_configuration[n_shader_index].type)
{
case GL_FRAGMENT_SHADER:
{
shader_body_string_fs = &shaders_configuration[n_shader_index].body;
if (fetch_culldistance_from_fs)
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_RETURN_VALUE"),
std::string(fetch_function));
std::string fetch_sum_setters = "";
for (glw::GLuint i = 0; i < clipdistances_array_size; ++i)
{
fetch_sum_setters.append(" sum += abs(gl_ClipDistance[");
fetch_sum_setters.append(CullDistance::Utilities::intToString(i));
fetch_sum_setters.append("]) * ");
fetch_sum_setters.append(CullDistance::Utilities::intToString(i + 1));
fetch_sum_setters.append(".0;\n");
}
fetch_sum_setters.append("\n");
for (glw::GLuint i = 0; i < culldistances_array_size; ++i)
{
fetch_sum_setters.append(" sum += abs(gl_CullDistance[");
fetch_sum_setters.append(CullDistance::Utilities::intToString(i));
fetch_sum_setters.append("]) * ");
fetch_sum_setters.append(
CullDistance::Utilities::intToString(i + 1 + clipdistances_array_size));
fetch_sum_setters.append(".0;\n");
}
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_SUM_SETTER"),
std::string(fetch_sum_setters));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_SUM_DIVIDER"),
std::string(CullDistance::Utilities::intToString(
(clipdistances_array_size + culldistances_array_size) *
((clipdistances_array_size + culldistances_array_size + 1))))
.append(".0"));
}
else
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_RETURN_VALUE"),
std::string("#define ASSIGN_RETURN_VALUE 1.0"));
}
break;
}
case GL_GEOMETRY_SHADER:
{
shader_body_string_gs = &shaders_configuration[n_shader_index].body;
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
std::string("gl_ClipDistance[IDX] = gl_in[n_vertex_index].gl_ClipDistance[IDX]"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
std::string("gl_CullDistance[IDX] = gl_in[n_vertex_index].gl_CullDistance[IDX]"));
switch (primitive_mode)
{
case PRIMITIVE_MODE_LINES:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
std::string("layout(lines) in;"));
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
std::string("layout(line_strip, max_vertices = 2) out;"));
break;
}
case PRIMITIVE_MODE_POINTS:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
std::string("layout(points) in;"));
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
std::string("layout(points, max_vertices = 1) out;"));
break;
}
case PRIMITIVE_MODE_TRIANGLES:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
std::string("layout(triangles) in;"));
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
std::string("layout(triangle_strip, max_vertices = 3) out;"));
break;
}
default:
TCU_FAIL("Unknown primitive mode");
}
break;
}
case GL_TESS_CONTROL_SHADER:
{
shader_body_string_tc = &shaders_configuration[n_shader_index].body;
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
std::string(
"gl_out[gl_InvocationID].gl_ClipDistance[IDX] = gl_in[gl_InvocationID].gl_ClipDistance[IDX]"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
std::string(
"gl_out[gl_InvocationID].gl_CullDistance[IDX] = gl_in[gl_InvocationID].gl_CullDistance[IDX]"));
switch (primitive_mode)
{
case PRIMITIVE_MODE_LINES:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
std::string("layout(vertices = 2) out;"));
break;
}
case PRIMITIVE_MODE_POINTS:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
std::string("layout(vertices = 1) out;"));
break;
}
case PRIMITIVE_MODE_TRIANGLES:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
std::string("layout(vertices = 3) out;"));
break;
}
default:
TCU_FAIL("Unknown primitive mode");
}
break;
}
case GL_TESS_EVALUATION_SHADER:
{
shader_body_string_te = &shaders_configuration[n_shader_index].body;
switch (primitive_mode)
{
case PRIMITIVE_MODE_LINES:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
std::string("layout(isolines) in;"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_OUT_FORMULA"),
std::string("mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x)"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
std::string("gl_ClipDistance[IDX] = mix(gl_in[0].gl_ClipDistance[IDX], "
"gl_in[1].gl_ClipDistance[IDX], gl_TessCoord.x)"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
std::string("gl_CullDistance[IDX] = mix(gl_in[0].gl_CullDistance[IDX], "
"gl_in[1].gl_CullDistance[IDX], gl_TessCoord.x)"));
break;
}
case PRIMITIVE_MODE_POINTS:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
std::string("layout(isolines, point_mode) in;"));
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_OUT_FORMULA"),
std::string("gl_in[0].gl_Position"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
std::string("gl_ClipDistance[IDX] = gl_in[0].gl_ClipDistance[IDX]"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
std::string("gl_CullDistance[IDX] = gl_in[0].gl_CullDistance[IDX]"));
break;
}
case PRIMITIVE_MODE_TRIANGLES:
{
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
std::string("layout(triangles) in;"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_OUT_FORMULA"),
std::string("vec4(mat3(gl_in[0].gl_Position.xyz, gl_in[1].gl_Position.xyz, "
"gl_in[2].gl_Position.xyz) * gl_TessCoord, 1.0)"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
std::string("gl_ClipDistance[IDX] = dot(vec3(gl_in[0].gl_ClipDistance[IDX], "
"gl_in[1].gl_ClipDistance[IDX], gl_in[2].gl_ClipDistance[IDX]), gl_TessCoord)"));
CullDistance::Utilities::replaceAll(
shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
std::string("gl_CullDistance[IDX] = dot(vec3(gl_in[0].gl_CullDistance[IDX], "
"gl_in[1].gl_CullDistance[IDX], gl_in[2].gl_CullDistance[IDX]), gl_TessCoord)"));
break;
}
default:
TCU_FAIL("Unknown primitive mode");
}
break;
}
case GL_VERTEX_SHADER:
{
shader_body_string_vs = &shaders_configuration[n_shader_index].body;
/* Specify input data size for clipdistances data */
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_CLIPDISTANCE_INPUT_SIZE"),
CullDistance::Utilities::intToString(clipdistances_input_size));
/* Specify input data size for culldistances data */
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_CULLDISTANCE_INPUT_SIZE"),
CullDistance::Utilities::intToString(culldistances_input_size));
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
std::string("gl_ClipDistance[IDX] = clipdistance_data[IDX]"));
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
std::string("gl_CullDistance[IDX] = culldistance_data[IDX]"));
break;
}
default:
TCU_FAIL("Unknown shader type");
}
/* Adjust clipdistances declaration */
if (redeclare_clipdistances && clipdistances_array_size > 0)
{
if (shaders_configuration[n_shader_index].type == GL_FRAGMENT_SHADER)
{
if (fetch_culldistance_from_fs)
{
clipdistance_array_declaration =
std::string("in float gl_ClipDistance[") +
CullDistance::Utilities::intToString(clipdistances_array_size) + std::string("];");
}
}
else if (shaders_configuration[n_shader_index].type == GL_TESS_CONTROL_SHADER)
{
clipdistance_array_declaration = std::string("float gl_ClipDistance[") +
CullDistance::Utilities::intToString(clipdistances_array_size) +
std::string("];");
}
else
{
clipdistance_array_declaration = std::string("out float gl_ClipDistance[") +
CullDistance::Utilities::intToString(clipdistances_array_size) +
std::string("];");
clipdistance_in_array_declaration = std::string("in float gl_ClipDistance[") +
CullDistance::Utilities::intToString(clipdistances_array_size) +
std::string("];");
}
}
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_CLIPDISTANCE"),
clipdistance_array_declaration);
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_IN_CLIPDISTANCE"),
clipdistance_in_array_declaration);
/* Adjust culldistances declaration */
if (redeclare_culldistances && culldistances_array_size > 0)
{
if (shaders_configuration[n_shader_index].type == GL_FRAGMENT_SHADER)
{
if (fetch_culldistance_from_fs)
{
culldistance_array_declaration =
std::string("in float gl_CullDistance[") +
CullDistance::Utilities::intToString(culldistances_array_size) + std::string("];");
}
}
else if (shaders_configuration[n_shader_index].type == GL_TESS_CONTROL_SHADER)
{
culldistance_array_declaration = std::string("float gl_CullDistance[") +
CullDistance::Utilities::intToString(culldistances_array_size) +
std::string("];");
}
else
{
culldistance_array_declaration = std::string("out float gl_CullDistance[") +
CullDistance::Utilities::intToString(culldistances_array_size) +
std::string("];");
culldistance_in_array_declaration = std::string("in float gl_CullDistance[") +
CullDistance::Utilities::intToString(culldistances_array_size) +
std::string("];");
}
}
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_CULLDISTANCE"),
culldistance_array_declaration);
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_IN_CULLDISTANCE"),
culldistance_in_array_declaration);
/* Adjust clip/cull distances setters */
if (dynamic_index_writes)
{
array_setters = dynamic_array_setters;
CullDistance::Utilities::replaceAll(array_setters, std::string("TEMPLATE_N_GL_CLIPDISTANCE_ENTRIES"),
CullDistance::Utilities::intToString(clipdistances_array_size));
CullDistance::Utilities::replaceAll(array_setters, std::string("TEMPLATE_N_GL_CULLDISTANCE_ENTRIES"),
CullDistance::Utilities::intToString(culldistances_array_size));
}
else
{
std::stringstream static_array_setters_sstream;
static_array_setters_sstream << "\n";
for (glw::GLuint clipdistances_array_entry = 0; clipdistances_array_entry < clipdistances_array_size;
++clipdistances_array_entry)
{
static_array_setters_sstream << " ASSIGN_CLIP_DISTANCE(" << clipdistances_array_entry
<< ");\n";
}
static_array_setters_sstream << "\n";
for (glw::GLuint culldistances_array_entry = 0; culldistances_array_entry < culldistances_array_size;
++culldistances_array_entry)
{
static_array_setters_sstream << " ASSIGN_CULL_DISTANCE(" << culldistances_array_entry
<< ");\n";
}
array_setters = static_array_setters_sstream.str();
}
CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ARRAY_SETTERS"), array_setters);
}
}
/* Build the geometry shader */
CullDistance::Utilities::buildProgram(
m_context.getRenderContext().getFunctions(), m_testCtx, DE_NULL, /* Compute shader */
shader_body_string_fs != DE_NULL ? shader_body_string_fs->c_str() :
DE_NULL, /* Fragment shader */
shader_body_string_gs != DE_NULL ? shader_body_string_gs->c_str() :
DE_NULL, /* Geometry shader */
shader_body_string_tc != DE_NULL ? shader_body_string_tc->c_str() :
DE_NULL, /* Tesselation control shader */
shader_body_string_te != DE_NULL ? shader_body_string_te->c_str() :
DE_NULL, /* Tesselation evaluation shader */
shader_body_string_vs != DE_NULL ? shader_body_string_vs->c_str() :
DE_NULL, /* Vertex shader */
0, /* Transform feedback varyings count */
DE_NULL, /* Transform feedback varyings */
&m_po_id /* Program object id */
);
}
/** Generates primitive data required to test a case with specified
* gl_ClipDistance and glCullDistance array sizes for specified
* primitive mode. Generated primitive data is stored in m_bo_data
* as well uploaded into buffer specified in m_bo_id buffer.
* Also the procedure binds vertex attribute locations to
* program object m_po_id.
*
* @param clipdistances_array_size gl_ClipDistance array size. Can be 0.
* @param culldistances_array_size gl_CullDistance array size. Can be 0.
* @param _primitive_mode Primitives to be generated. Can be:
* PRIMITIVE_MODE_POINTS,
* PRIMITIVE_MODE_LINES,
* PRIMITIVE_MODE_TRIANGLES.
*/
void CullDistance::FunctionalTest::configureVAO(glw::GLuint clipdistances_array_size,
glw::GLuint culldistances_array_size, _primitive_mode primitive_mode)
{
/* Detailed test description.
*
* configureVAO() generates primitives layouted in grid. Primitve
* consists of up to 3 vertices and each vertex is accompanied by:
* - array of clipdistances (clipdistances_array_size floats);
* - array of culldistances (culldistances_array_size floats);
* - rendering position coordinates (x and y);
* - check position coordinates (x and y).
*
* The grid has following layout:
*
* Grid | gl_CullDistance[x] |
* | 0 .. culldistances_array_size - 1 |
* | 0th | 1st | 2nd | .......... |
* ---------------------------+-------+-------+-------+------------+
* 0th gl_ClipDistance |Subgrid|Subgrid|Subgrid| .......... |
* 1st gl_ClipDistance |Subgrid|Subgrid|Subgrid| .......... |
* ... | ... | ... | ... | .......... |
* y-th gl_ClipDistance |Subgrid|Subgrid|Subgrid| .......... |
* ... | ... | ... | ... | .......... |
* clipdistances_array_size-1 |Subgrid|Subgrid|Subgrid| .......... |
*
* Each grid cell contains subgrid of 3*3 items in size with following
* structure:
*
* Subgrid | x-th gl_CullDistance test |
* | |
* y-th | all vertices | 0th vertex | all vertices |
* gl_ClipDistance| in primitive | in primitive | in primitive |
* tests | dist[x] > 0 | dist[x] < 0 | dist[x] < 0 |
* ---------------+--------------+--------------+--------------+
* all vertices| primitive #0 | primitive #1 | primitive #2 |
* in primitive| | | |
* dist[y] > 0 | visible | visible | culled |
* ---------------+--------------+--------------+--------------+
* 0th vertex | primitive #3 | primitive #4 | primitive #5 |
* in primitive| 0th vertex | 0th vertex | |
* dist[y] < 0 | clipped | clipped | culled |
* ---------------+--------------+--------------+--------------+
* all vertices| primitive #6 | primitive #7 | primitive #8 |
* in primitive| | | |
* dist[y] < 0 | clipped | clipped | culled |
* ---------------+--------------+--------------+--------------+
*
* Expected rendering result is specified in cell bottom.
* It can be one of the following:
* - "visible" means the primitive is not affected neither by gl_CullDistance
* nor by gl_ClipDistance and rendered as a whole;
* - "clipped" for the vertex means the vertex is not rendered, while other
* primitive vertices and some filling fragments are rendered;
* - "clipped" for primitive means none of primitive vertices and fragments
* are rendered and thus primitive is not rendered and is invisible;
* - "culled" means, that neither primitive vertices, nor primitive filling
* fragments are rendered (primitive is invisible).
*
* All subgrid items contain same primitive rendered. Depending on
* test case running it would be either triangle, or line, or point:
*
* triangle line point
* 8x8 box 8x8 box 3x3 box
* ........ ........ ...
* .0----2. .0...... .0.
* ..\@@@|. ..\..... ...
* ...\@@|. ...\....
* ....\@|. ....\...
* .....\|. .....\..
* ......1. ......1.
* ........ ........
*
* where 0 - is a 0th vertex primitive
* 1 - is a 1st vertex primitive
* 2 - is a 2nd vertex primitive
*
* The culldistances_array_size can be 0. In that case, grid height
* is assumed equal to 1, but 0 glCullDistances is specified.
* Similar handled clipdistances_array_size.
*
* The data generated is used and checked in executeRenderTest().
* After rendering each primitive vertex is tested:
* - if it is rendered, if it have to be rendered (according distance);
* - if it is not rendered, if it have to be not rendered (according distance).
* Due to "top-left" rasterization rule check position is
* different from rendering vertex position.
*
* Also one pixel width guarding box is checked to be clear.
*/
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const glw::GLuint n_sub_grid_cells = 3; /* Tested distance is positive for all vertices in the primitive;
* Tested distance is negative for 0th vertex in the primitive;
* Tested distance is negative for all vertices in the primitive;
*/
const glw::GLuint sub_grid_cell_size =
((primitive_mode == PRIMITIVE_MODE_LINES) ? 8 : (primitive_mode == PRIMITIVE_MODE_POINTS) ? 3 : 8);
const glw::GLuint grid_cell_size = n_sub_grid_cells * sub_grid_cell_size;
const glw::GLuint n_primitive_vertices =
((primitive_mode == PRIMITIVE_MODE_LINES) ? 2 : (primitive_mode == PRIMITIVE_MODE_POINTS) ? 1 : 3);
const glw::GLuint n_grid_cells_x = culldistances_array_size != 0 ? culldistances_array_size : 1;
const glw::GLuint n_grid_cells_y = clipdistances_array_size != 0 ? clipdistances_array_size : 1;
const glw::GLuint n_pervertex_float_attributes = clipdistances_array_size + culldistances_array_size +
2 /* vertex' draw x, y */ + 2 /* vertex' checkpoint x, y */;
const glw::GLuint n_primitives_total = n_grid_cells_x * n_sub_grid_cells * n_grid_cells_y * n_sub_grid_cells;
const glw::GLuint n_vertices_total = n_primitives_total * n_primitive_vertices;
const glw::GLuint offsets_line_draw_x[2] = {
1, sub_grid_cell_size - 1
}; /* vertex x offsets to subgrid cell origin for line primitive */
const glw::GLuint offsets_line_draw_y[2] = {
1, sub_grid_cell_size - 1
}; /* vertex y offsets to subgrid cell origin for line primitive */
const glw::GLuint offsets_line_checkpoint_x[2] = {
1, sub_grid_cell_size - 2
}; /* pixel x offsets to subgrid cell origin for line primitive */
const glw::GLuint offsets_line_checkpoint_y[2] = {
1, sub_grid_cell_size - 2
}; /* pixel y offsets to subgrid cell origin for line primitive */
const glw::GLuint offsets_point_draw_x[1] = {
1
}; /* vertex x offsets to subgrid cell origin for point primitive */
const glw::GLuint offsets_point_draw_y[1] = {
1
}; /* vertex y offsets to subgrid cell origin for point primitive */
const glw::GLuint offsets_point_checkpoint_x[1] = {
1
}; /* pixel x offsets to subgrid cell origin for point primitive */
const glw::GLuint offsets_point_checkpoint_y[1] = {
1
}; /* pixel y offsets to subgrid cell origin for point primitive */
const glw::GLuint offsets_triangle_draw_x[3] = {
1, sub_grid_cell_size - 1, sub_grid_cell_size - 1
}; /* vertex x offsets to subgrid cell origin for triangle primitive */
const glw::GLuint offsets_triangle_draw_y[3] = {
1, sub_grid_cell_size - 1, 1
}; /* vertex y offsets to subgrid cell origin for triangle primitive */
const glw::GLuint offsets_triangle_checkpoint_x[3] = {
1, sub_grid_cell_size - 2, sub_grid_cell_size - 2
}; /* pixel x offsets to subgrid cell origin for triangle primitive */
const glw::GLuint offsets_triangle_checkpoint_y[3] = {
1, sub_grid_cell_size - 2, 1
}; /* pixel y offsets to subgrid cell origin for triangle primitive */
const glw::GLfloat offsets_pixel_center_x = (primitive_mode == PRIMITIVE_MODE_POINTS) ? 0.5f : 0;
const glw::GLfloat offsets_pixel_center_y = (primitive_mode == PRIMITIVE_MODE_POINTS) ? 0.5f : 0;
/* Clear data left from previous tests. */
m_bo_data.clear();
/* No data to render */
m_render_primitives = 0;
m_render_vertices = 0;
/* Preallocate space for bo_points_count */
m_bo_data.reserve(n_vertices_total * n_pervertex_float_attributes);
/* Generate test data for cell_y-th clip distance */
for (glw::GLuint cell_y = 0; cell_y < n_grid_cells_y; cell_y++)
{
/* Generate test data for cell_x-th cull distance */
for (glw::GLuint cell_x = 0; cell_x < n_grid_cells_x; cell_x++)
{
/* Check clip distance sub cases:
* 0. Tested distance is positive for all vertices in the primitive;
* 1. Tested distance is negative for 0th vertex in the primitive;
* 2. Tested distance is negative for all vertices in the primitive;
*/
for (glw::GLuint n_sub_cell_y = 0; n_sub_cell_y < n_sub_grid_cells; n_sub_cell_y++)
{
/* Check cull distance sub cases:
* 0. Tested distance is positive for all vertices in the primitive;
* 1. Tested distance is negative for 0th vertex in the primitive;
* 2. Tested distance is negative for all vertices in the primitive;
*/
for (glw::GLuint n_sub_cell_x = 0; n_sub_cell_x < n_sub_grid_cells; n_sub_cell_x++)
{
/* Generate vertices in primitive */
for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < n_primitive_vertices;
n_primitive_vertex++)
{
/* Fill in clipdistance array for the n_primitive_vertex vertex in primitive */
for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size;
n_clipdistance_entry++)
{
glw::GLfloat distance_value = 0.0f;
bool negative = true;
/* Special approach to tested clipdistance entry. */
if (n_clipdistance_entry == cell_y)
{
/* The primitive vertex should be affected by the clip distance */
switch (n_sub_cell_y)
{
case 0:
{
/* subgrid row 0: all primitive vertices have tested distance value positive */
negative = false;
break;
}
case 1:
{
/* subgrid row 1: tested distance value for 0th primitive vertex is negative,
all other primitive vertices have tested distance value positive */
negative = (n_primitive_vertex == 0) ? true : false;
break;
}
case 2:
{
/* subgrid row 2: tested distance value is negative for all primitive vertices */
negative = true;
break;
}
default:
TCU_FAIL("Invalid subgrid cell index");
}
distance_value = (negative ? -1.0f : 1.0f) * glw::GLfloat(n_clipdistance_entry + 1);
}
else
{
/* For clip distances other than tested: assign positive value to avoid its influence. */
distance_value = glw::GLfloat(clipdistances_array_size + n_clipdistance_entry + 1);
}
m_bo_data.push_back(distance_value / glw::GLfloat(clipdistances_array_size));
} /* for (all gl_ClipDistance[] array values) */
/* Fill in culldistance array for the n_primitive_vertex vertex in primitive */
for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size;
n_culldistance_entry++)
{
glw::GLfloat distance_value = 0.0f;
bool negative = true;
/* Special approach to tested culldistance entry. */
if (n_culldistance_entry == cell_x)
{
/* The primitive vertex should be affected by the cull distance */
switch (n_sub_cell_x)
{
case 0:
{
/* subgrid column 0: all primitive vertices have tested distance value positive */
negative = false;
break;
}
case 1:
{
/* subgrid column 1: tested distance value for 0th primitive vertex is negative,
all other primitive vertices have tested distance value positive */
negative = (n_primitive_vertex == 0) ? true : false;
break;
}
case 2:
{
/* subgrid column 2: tested distance value is negative for all primitive vertices */
negative = true;
break;
}
default:
TCU_FAIL("Invalid subgrid cell index");
}
distance_value = (negative ? -1.0f : 1.0f) * glw::GLfloat(n_culldistance_entry + 1);
}
else
{
/* For cull distances other than tested: assign 0th vertex negative value,
to check absence of between-distances influence. */
if (n_primitive_vertices > 1 && n_primitive_vertex == 0)
{
distance_value = -glw::GLfloat(culldistances_array_size + n_culldistance_entry + 1);
}
else
{
/* This culldistance is out of interest: assign positive value. */
distance_value = glw::GLfloat(culldistances_array_size + n_culldistance_entry + 1);
}
}
m_bo_data.push_back(distance_value / glw::GLfloat(culldistances_array_size));
} /* for (all gl_CullDistance[] array values) */
/* Generate primitve vertex draw and checkpoint coordinates */
glw::GLint vertex_draw_pixel_offset_x = 0;
glw::GLint vertex_draw_pixel_offset_y = 0;
glw::GLint vertex_checkpoint_pixel_offset_x = 0;
glw::GLint vertex_checkpoint_pixel_offset_y = 0;
switch (primitive_mode)
{
case PRIMITIVE_MODE_LINES:
{
vertex_draw_pixel_offset_x = offsets_line_draw_x[n_primitive_vertex];
vertex_draw_pixel_offset_y = offsets_line_draw_y[n_primitive_vertex];
vertex_checkpoint_pixel_offset_x = offsets_line_checkpoint_x[n_primitive_vertex];
vertex_checkpoint_pixel_offset_y = offsets_line_checkpoint_y[n_primitive_vertex];
break;
}
case PRIMITIVE_MODE_POINTS:
{
vertex_draw_pixel_offset_x = offsets_point_draw_x[n_primitive_vertex];
vertex_draw_pixel_offset_y = offsets_point_draw_y[n_primitive_vertex];
vertex_checkpoint_pixel_offset_x = offsets_point_checkpoint_x[n_primitive_vertex];
vertex_checkpoint_pixel_offset_y = offsets_point_checkpoint_y[n_primitive_vertex];
break;
}
case PRIMITIVE_MODE_TRIANGLES:
{
vertex_draw_pixel_offset_x = offsets_triangle_draw_x[n_primitive_vertex];
vertex_draw_pixel_offset_y = offsets_triangle_draw_y[n_primitive_vertex];
vertex_checkpoint_pixel_offset_x = offsets_triangle_checkpoint_x[n_primitive_vertex];
vertex_checkpoint_pixel_offset_y = offsets_triangle_checkpoint_y[n_primitive_vertex];
break;
}
default:
TCU_FAIL("Unknown primitive mode");
}
/* Origin of sub_cell */
glw::GLint sub_cell_origin_x = cell_x * grid_cell_size + n_sub_cell_x * sub_grid_cell_size;
glw::GLint sub_cell_origin_y = cell_y * grid_cell_size + n_sub_cell_y * sub_grid_cell_size;
/* Normalized texture coordinates of vertex draw position. */
glw::GLfloat x =
(glw::GLfloat(sub_cell_origin_x + vertex_draw_pixel_offset_x) + offsets_pixel_center_x) /
glw::GLfloat(m_to_width);
glw::GLfloat y =
(glw::GLfloat(sub_cell_origin_y + vertex_draw_pixel_offset_y) + offsets_pixel_center_y) /
glw::GLfloat(m_to_height);
/* Normalized texture coordinates of vertex checkpoint position. */
glw::GLfloat checkpoint_x = glw::GLfloat(sub_cell_origin_x + vertex_checkpoint_pixel_offset_x) /
glw::GLfloat(m_to_width);
glw::GLfloat checkpoint_y = glw::GLfloat(sub_cell_origin_y + vertex_checkpoint_pixel_offset_y) /
glw::GLfloat(m_to_height);
/* Add vertex draw coordinates into buffer. */
m_bo_data.push_back(x);
m_bo_data.push_back(y);
/* Add vertex checkpoint coordinates into buffer. */
m_bo_data.push_back(checkpoint_x);
m_bo_data.push_back(checkpoint_y);
} /* for (all vertices in primitive) */
} /* for (all horizontal sub cells) */
} /* for (all vertical sub cells) */
} /* for (all horizontal cells) */
} /* for (all vertical cells) */
/* Sanity check: make sure we pushed required amount of data */
DE_ASSERT(m_bo_data.size() == n_vertices_total * n_pervertex_float_attributes);
/* Save number of primitives to render */
m_render_primitives = n_primitives_total;
m_render_vertices = n_vertices_total;
m_sub_grid_cell_size = sub_grid_cell_size;
/* Copy the data to the buffer object */
gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed.");
gl.bufferData(GL_ARRAY_BUFFER, m_bo_data.size() * sizeof(glw::GLfloat), &m_bo_data[0], GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
DE_ASSERT(m_po_id != 0);
/* Bind VAO data to program */
glw::GLint po_clipdistance_array_location = -1;
glw::GLint po_culldistance_array_location = -1;
glw::GLint po_position_location = -1;
/* Retrieve clipdistance and culldistance attribute locations */
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
po_clipdistance_array_location = gl.getAttribLocation(m_po_id, "clipdistance_data[0]");
po_culldistance_array_location = gl.getAttribLocation(m_po_id, "culldistance_data[0]");
po_position_location = gl.getAttribLocation(m_po_id, "position");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation() call(s) failed.");
if (clipdistances_array_size > 0)
{
DE_ASSERT(po_clipdistance_array_location != -1);
}
if (culldistances_array_size > 0)
{
DE_ASSERT(po_culldistance_array_location != -1);
}
DE_ASSERT(po_position_location != -1);
glw::GLintptr current_offset = 0;
const glw::GLint stride = static_cast<glw::GLint>(n_pervertex_float_attributes * sizeof(glw::GLfloat));
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size; ++n_clipdistance_entry)
{
gl.vertexAttribPointer(po_clipdistance_array_location + n_clipdistance_entry, 1, /* size */
GL_FLOAT, GL_FALSE, /* normalized */
stride, (const glw::GLvoid*)current_offset);
GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed.");
gl.enableVertexAttribArray(po_clipdistance_array_location + n_clipdistance_entry);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed.");
current_offset += sizeof(glw::GLfloat);
} /* for (all clip distance array value attributes) */
for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size; ++n_culldistance_entry)
{
gl.vertexAttribPointer(po_culldistance_array_location + n_culldistance_entry, 1, /* size */
GL_FLOAT, GL_FALSE, /* normalized */
stride, (const glw::GLvoid*)current_offset);
GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed.");
gl.enableVertexAttribArray(po_culldistance_array_location + n_culldistance_entry);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed.");
current_offset += sizeof(glw::GLfloat);
} /* for (all cull distance array value attributes) */
gl.vertexAttribPointer(po_position_location, 2, /* size */
GL_FLOAT, GL_FALSE, /* normalized */
stride, (const glw::GLvoid*)current_offset);
GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed");
gl.enableVertexAttribArray(po_position_location);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed");
}
/** @brief Cull Distance Functional Test deinitialization */
void CullDistance::FunctionalTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_fbo_id);
m_fbo_id = 0;
}
if (m_to_id != 0)
{
gl.deleteTextures(1, &m_to_id);
m_to_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
deinitPO();
}
/** @brief Cull Distance Functional Test deinitialization of OpenGL programs */
void CullDistance::FunctionalTest::deinitPO()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
}
/** @brief Executes single render test case
*
* @param [in] clipdistances_array_size Size of gl_ClipDistance[] array
* @param [in] culldistances_array_size Size of gl_CullDistance[] array
* @param [in] primitive_mode Type of primitives to be rendered (see enum _primitive_mode)
* @param [in] use_tesselation Indicate whether to use tessellation shader
* @param [in] fetch_culldistance_from_fs Indicate whether to fetch gl_CullDistance and gl_ClipDistance values from the fragment shader
*/
void CullDistance::FunctionalTest::executeRenderTest(glw::GLuint clipdistances_array_size,
glw::GLuint culldistances_array_size,
_primitive_mode primitive_mode, bool use_tesselation,
bool fetch_culldistance_from_fs)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLenum mode = GL_NONE;
glw::GLuint n_clipped_vertices_real = 0;
glw::GLuint n_culled_primitives_real = 0;
glw::GLuint n_not_clipped_vertices_real = 0;
const glw::GLuint primitive_vertices_count =
((primitive_mode == PRIMITIVE_MODE_LINES) ? 2 : (primitive_mode == PRIMITIVE_MODE_POINTS) ? 1 : 3);
const glw::GLuint stride_in_floats =
clipdistances_array_size + culldistances_array_size + 2 /* position's x, y*/ + 2 /* checkpoint x,y */;
switch (primitive_mode)
{
case PRIMITIVE_MODE_LINES:
{
mode = GL_LINES;
break;
}
case PRIMITIVE_MODE_POINTS:
{
mode = GL_POINTS;
break;
}
case PRIMITIVE_MODE_TRIANGLES:
{
mode = GL_TRIANGLES;
break;
}
default:
TCU_FAIL("Unknown primitive mode");
}
if (use_tesselation)
{
mode = GL_PATCHES;
gl.patchParameteri(GL_PATCH_VERTICES, primitive_vertices_count);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteri() call failed.");
}
gl.clear(GL_COLOR_BUFFER_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed.");
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size; n_clipdistance_entry++)
{
gl.enable(GL_CLIP_DISTANCE0 + n_clipdistance_entry);
GLU_EXPECT_NO_ERROR(gl.getError(), "gl.enable(GL_CLIP_DISTANCE)() call failed.");
} /* for (all clip distance array value attributes) */
gl.drawArrays(mode, 0, m_render_vertices);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArray() call(s) failed.");
for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size; n_clipdistance_entry++)
{
gl.disable(GL_CLIP_DISTANCE0 + n_clipdistance_entry);
GLU_EXPECT_NO_ERROR(gl.getError(), "gl.disable(GL_CLIP_DISTANCE)() call failed.");
} /* for (all clip distance array value attributes) */
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
/* Read generated texture into m_to_pixel_data_cache */
readTexturePixels();
for (glw::GLint n_primitive_index = 0; n_primitive_index < m_render_primitives; n_primitive_index++)
{
glw::GLuint base_index_of_primitive = n_primitive_index * primitive_vertices_count * stride_in_floats;
bool primitive_culled = false;
glw::GLint primitive_culled_by_distance = -1;
/* Check the bounding box is clear */
glw::GLuint base_index_of_vertex = base_index_of_primitive;
glw::GLuint checkpoint_position_index = base_index_of_vertex + clipdistances_array_size +
culldistances_array_size + 2 /* ignore vertex coordinates */;
glw::GLint checkpoint_x = glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index]);
glw::GLint checkpoint_y = glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index + 1]);
glw::GLint origin_x = checkpoint_x - 1;
glw::GLint origin_y = checkpoint_y - 1;
for (glw::GLint pixel_offset = 0; pixel_offset < m_sub_grid_cell_size; pixel_offset++)
{
if (readRedPixelValue(origin_x + pixel_offset, origin_y) != 0)
{
TCU_FAIL("Top edge of bounding box is overwritten");
}
if (readRedPixelValue(origin_x + m_sub_grid_cell_size - 1, origin_y + pixel_offset) != 0)
{
TCU_FAIL("Right edge of bounding box is overwritten");
}
if (readRedPixelValue(origin_x + m_sub_grid_cell_size - 1 - pixel_offset,
origin_y + m_sub_grid_cell_size - 1) != 0)
{
TCU_FAIL("Bottom edge of bounding box is overwritten");
}
if (readRedPixelValue(origin_x, origin_y + m_sub_grid_cell_size - 1 - pixel_offset) != 0)
{
TCU_FAIL("Left edge of bounding box is overwritten");
}
}
/* Determine if primitive has been culled */
for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size;
n_culldistance_entry++)
{
bool distance_negative_in_all_primitive_vertices = true;
for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count;
n_primitive_vertex++)
{
glw::GLint base_index_of_vertex_internal =
base_index_of_primitive + n_primitive_vertex * stride_in_floats;
glw::GLint culldistance_array_offset = base_index_of_vertex_internal + clipdistances_array_size;
glw::GLfloat* vertex_culldistance_array = &m_bo_data[culldistance_array_offset];
if (vertex_culldistance_array[n_culldistance_entry] >= 0)
{
/* Primitive is not culled, due to one of its distances is not negative */
distance_negative_in_all_primitive_vertices = false;
/* Skip left vertices for this distance */
break;
}
}
/* The distance is negative in all primitive vertices, so this distance culls the primitive */
if (distance_negative_in_all_primitive_vertices)
{
primitive_culled = true;
primitive_culled_by_distance = n_culldistance_entry;
n_culled_primitives_real++;
/* Skip left distances from check */
break;
}
}
/* Validate culling */
if (primitive_culled)
{
/* Check whether primitive was culled and all its vertices are invisible */
for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count;
n_primitive_vertex++)
{
glw::GLint base_index_of_vertex_internal =
base_index_of_primitive + n_primitive_vertex * stride_in_floats;
glw::GLint checkpoint_position_index_internal = base_index_of_vertex_internal +
clipdistances_array_size + culldistances_array_size +
2 /* ignore vertex coordinates */;
glw::GLint checkpoint_x_internal =
glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index_internal]);
glw::GLint checkpoint_y_internal =
glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index_internal + 1]);
glw::GLint vertex_color_red_value = readRedPixelValue(checkpoint_x_internal, checkpoint_y_internal);
/* Make sure vertex is invisible */
if (vertex_color_red_value != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Primitive number [" << n_primitive_index << "] "
<< "should be culled by distance [" << primitive_culled_by_distance << "]"
<< "but primitive vertex at (" << checkpoint_x << "," << checkpoint_y
<< ") is visible." << tcu::TestLog::EndMessage;
TCU_FAIL("Primitive is expected to be culled, but one of its vertices is visible.");
}
}
/* Primitive is culled, no reason to check clipping */
continue;
}
bool all_vertices_are_clipped = true;
for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count; n_primitive_vertex++)
{
glw::GLuint base_index_of_vertex_internal = base_index_of_primitive + n_primitive_vertex * stride_in_floats;
glw::GLuint clipdistance_array_index = base_index_of_vertex_internal;
glw::GLuint checkpoint_position_index_internal = base_index_of_vertex_internal + clipdistances_array_size +
culldistances_array_size +
2 /* ignore vertex coordinates */;
glw::GLint checkpoint_x_internal =
glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index_internal]);
glw::GLint checkpoint_y_internal =
glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index_internal + 1]);
glw::GLfloat* vertex_clipdistance_array = &m_bo_data[clipdistance_array_index];
bool vertex_clipped = false;
glw::GLint vertex_clipped_by_distance = 0;
glw::GLint vertex_color_red_value = readRedPixelValue(checkpoint_x_internal, checkpoint_y_internal);
/* Check whether pixel should be clipped */
for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size;
n_clipdistance_entry++)
{
if (vertex_clipdistance_array[n_clipdistance_entry] < 0)
{
vertex_clipped = true;
vertex_clipped_by_distance = n_clipdistance_entry;
break;
}
}
all_vertices_are_clipped &= vertex_clipped;
/* Validate whether real data same as expected */
if (vertex_clipped)
{
if (vertex_color_red_value != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "In primitive number [" << n_primitive_index << "] "
<< "vertex at (" << checkpoint_x << "," << checkpoint_y << ") "
<< "should be clipped by distance [" << vertex_clipped_by_distance << "] "
<< "(distance value [" << vertex_clipdistance_array[vertex_clipped_by_distance]
<< "])" << tcu::TestLog::EndMessage;
TCU_FAIL("Vertex is expected to be clipped and invisible, while it is visible.");
}
else
{
n_clipped_vertices_real++;
}
}
else
{
if (vertex_color_red_value == 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "In primitive number [" << n_primitive_index << "] "
<< "vertex at (" << checkpoint_x << "," << checkpoint_y << ") "
<< "should not be clipped." << tcu::TestLog::EndMessage;
TCU_FAIL("Vertex is unexpectedly clipped or invisible");
}
else
{
n_not_clipped_vertices_real++;
}
}
}
if (!all_vertices_are_clipped)
{
/* Check fetched values from the shader (Point 2 of Basic Outline : "Use program that...") */
if (fetch_culldistance_from_fs)
{
for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count;
n_primitive_vertex++)
{
/* Get shader output value */
glw::GLuint base_index_of_vertex_internal =
base_index_of_primitive + n_primitive_vertex * stride_in_floats;
glw::GLuint checkpoint_position_index_internal =
base_index_of_vertex_internal + clipdistances_array_size + culldistances_array_size +
2 /* ignore vertex coordinates */;
glw::GLuint culldistances_index = base_index_of_vertex_internal + clipdistances_array_size;
glw::GLint checkpoint_x_internal =
glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index_internal]);
glw::GLint checkpoint_y_internal =
glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index_internal + 1]);
glw::GLint vertex_color_red_value = readRedPixelValue(checkpoint_x_internal, checkpoint_y_internal);
/* Calculate culldistances check sum hash */
float sum = 0.f;
for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size;
++n_clipdistance_entry)
{
sum += de::abs(m_bo_data[base_index_of_vertex_internal + n_clipdistance_entry]) *
float(n_clipdistance_entry + 1);
}
for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size;
++n_culldistance_entry)
{
sum += de::abs(m_bo_data[culldistances_index + n_culldistance_entry]) *
float(n_culldistance_entry + 1 + clipdistances_array_size);
}
/* limit sum and return */
glw::GLint sum_hash =
glw::GLint(sum / glw::GLfloat((clipdistances_array_size + culldistances_array_size) *
(clipdistances_array_size + culldistances_array_size + 1)) *
65535.f /* normalizing to short */);
sum_hash = (sum_hash < 65536) ? sum_hash : 65535; /* clamping to short */
/* Compare against setup value */
if (std::abs(vertex_color_red_value - sum_hash) > 4 /* precision 4/65536 */)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Primitive number [" << n_primitive_index << "] "
<< "should have culldistance hash sum " << sum_hash
<< "but primitive vertex at (" << checkpoint_x << "," << checkpoint_y
<< ") has sum hash equal to " << vertex_color_red_value
<< tcu::TestLog::EndMessage;
TCU_FAIL("Culled distances returned from fragment shader dose not match expected values.");
}
}
}
}
}
/* sub_grid cell size is 3*3 */
DE_ASSERT(m_render_primitives % 9 == 0);
/* Sanity check */
switch (primitive_mode)
{
case PRIMITIVE_MODE_LINES:
case PRIMITIVE_MODE_TRIANGLES:
{
/* Validate culled primitives */
if (culldistances_array_size == 0)
{
DE_ASSERT(n_culled_primitives_real == 0);
}
else
{
/* Each 3rd line or triangle should be culled by test design */
DE_ASSERT(glw::GLsizei(n_culled_primitives_real) == m_render_primitives / 3);
}
/* Validate clipped vertices */
if (clipdistances_array_size == 0)
{
DE_ASSERT(n_clipped_vertices_real == 0);
}
else
{
#if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD)
glw::GLint one_third_of_rendered_primitives = (m_render_primitives - n_culled_primitives_real) / 3;
glw::GLint n_clipped_vertices_expected = /* One third of primitives has 0th vertex clipped */
one_third_of_rendered_primitives +
/* One third of primitives clipped completely */
one_third_of_rendered_primitives * primitive_vertices_count;
DE_ASSERT(glw::GLint(n_clipped_vertices_real) == n_clipped_vertices_expected);
#endif
}
break;
}
case PRIMITIVE_MODE_POINTS:
{
/* Validate culled primitives */
if (culldistances_array_size == 0)
{
DE_ASSERT(n_culled_primitives_real == 0);
}
else
{
/* 2/3 points should be culled by test design */
DE_ASSERT(glw::GLsizei(n_culled_primitives_real) == m_render_primitives * 2 / 3);
}
/* Validate clipped vertices */
if (clipdistances_array_size == 0)
{
DE_ASSERT(n_clipped_vertices_real == 0);
}
else
{
#if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD)
glw::GLint one_third_of_rendered_primitives = (m_render_primitives - n_culled_primitives_real) / 3;
/* 2/3 of rendered points should be clipped by test design */
DE_ASSERT(glw::GLint(n_clipped_vertices_real) == 2 * one_third_of_rendered_primitives);
#endif
}
break;
}
default:
TCU_FAIL("Unknown primitive mode");
}
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult CullDistance::FunctionalTest::iterate()
{
/* This test should only be executed if ARB_cull_distance is supported, or if
* we're running a GL4.5 context
*/
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_cull_distance") &&
!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
{
throw tcu::NotSupportedError("GL_ARB_cull_distance is not supported");
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool has_succeeded = true;
bool is_core = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
/* Retrieve important GL constant values */
glw::GLint gl_max_clip_distances_value = 0;
glw::GLint gl_max_combined_clip_and_cull_distances_value = 0;
glw::GLint gl_max_cull_distances_value = 0;
gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &gl_max_clip_distances_value);
gl.getIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, &gl_max_combined_clip_and_cull_distances_value);
gl.getIntegerv(GL_MAX_CULL_DISTANCES, &gl_max_cull_distances_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call(s) failed.");
gl.genTextures(1, &m_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed.");
gl.bindTexture(GL_TEXTURE_2D, m_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
GL_R32F, m_to_width, m_to_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed.");
/* Set up the draw/read FBO */
gl.genFramebuffers(1, &m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed.");
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
/* Prepare a buffer object */
gl.genBuffers(1, &m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
/* Prepare a VAO. We will configure separately for each iteration. */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
/* Iterate over all functional tests */
struct _test_item
{
bool redeclare_clipdistances_array;
bool redeclare_culldistances_array;
bool dynamic_index_writes;
bool use_passthrough_gs;
bool use_passthrough_ts;
bool use_core_functionality;
bool fetch_culldistances;
} test_items[] = { /* Use the basic outline to test the basic functionality of cull distances. */
{
true, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use the basic outline but don't redeclare gl_ClipDistance with a size. */
{
false, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use the basic outline but don't redeclare gl_CullDistance with a size. */
{
true, /* redeclare_clipdistances_array */
false, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use the basic outline but don't redeclare either gl_ClipDistance or
* gl_CullDistance with a size.
*/
{
false, /* redeclare_clipdistances_array */
false, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use the basic outline but use dynamic indexing when writing the elements
* of the gl_ClipDistance and gl_CullDistance arrays.
*/
{
true, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
true, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use the basic outline but add a geometry shader to the program that
* simply passes through all written clip and cull distances.
*/
{
true, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
true, /* use_passthrough_gs */
false, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use the basic outline but add a tessellation control and tessellation
* evaluation shader to the program which simply pass through all written
* clip and cull distances.
*/
{
true, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
true, /* use_passthrough_ts */
is_core, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Test that using #extension with GL_ARB_cull_distance allows using the
* feature even with an earlier version of GLSL. Also test that the
* extension name is available as preprocessor #define.
*/
{
true, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
false, /* use_core_functionality */
false /* fetch_culldistances */
},
/* Use a program that has only a vertex shader and a fragment shader.
* The vertex shader should redeclare gl_ClipDistance with a size that
* fits all enabled cull distances. Also redeclare gl_CullDistance with a
* size. The sum of the two sizes should not be more than MAX_COMBINED_-
* CLIP_AND_CULL_DISTANCES. The fragment shader should output the cull
* distances written by the vertex shader by reading them from the built-in
* array gl_CullDistance.
*/
{
true, /* redeclare_clipdistances_array */
true, /* redeclare_culldistances_array */
false, /* dynamic_index_writes */
false, /* use_passthrough_gs */
false, /* use_passthrough_ts */
false, /* use_core_functionality */
true /* fetch_culldistances */
}
};
const glw::GLuint n_test_items = sizeof(test_items) / sizeof(test_items[0]);
gl.viewport(0, 0, m_to_width, m_to_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed.");
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor() call failed.");
for (glw::GLuint n_test_item = 0; n_test_item < n_test_items; ++n_test_item)
{
/* Check for OpenGL feature support */
if (test_items[n_test_item].use_passthrough_ts)
{
if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0)) &&
!m_context.getContextInfo().isExtensionSupported("GL_ARB_tessellation_shader"))
{
continue; // no tessellation shader support
}
}
const _test_item& current_test_item = test_items[n_test_item];
const _primitive_mode primitive_modes[PRIMITIVE_MODE_COUNT] = { PRIMITIVE_MODE_LINES, PRIMITIVE_MODE_POINTS,
PRIMITIVE_MODE_TRIANGLES };
for (glw::GLuint primitive_mode_index = 0; primitive_mode_index < PRIMITIVE_MODE_COUNT; ++primitive_mode_index)
{
_primitive_mode primitive_mode = primitive_modes[primitive_mode_index];
/* Iterate over a set of gl_ClipDistances[] and gl_CullDistances[] array sizes */
for (glw::GLint n_iteration = 0; n_iteration <= gl_max_combined_clip_and_cull_distances_value;
++n_iteration)
{
glw::GLuint clipdistances_array_size = 0;
glw::GLuint culldistances_array_size = 0;
if (n_iteration != 0 && n_iteration <= gl_max_clip_distances_value)
{
clipdistances_array_size = n_iteration;
}
if ((gl_max_combined_clip_and_cull_distances_value - n_iteration) < gl_max_cull_distances_value)
{
culldistances_array_size = gl_max_combined_clip_and_cull_distances_value - n_iteration;
}
else
{
culldistances_array_size = gl_max_cull_distances_value;
}
if (clipdistances_array_size == 0 && culldistances_array_size == 0)
{
/* Skip the dummy iteration */
continue;
}
if (current_test_item.fetch_culldistances && (primitive_mode != PRIMITIVE_MODE_POINTS))
{
continue;
}
/* Create a program to run */
buildPO(clipdistances_array_size, culldistances_array_size, current_test_item.dynamic_index_writes,
primitive_mode, current_test_item.redeclare_clipdistances_array,
current_test_item.redeclare_culldistances_array, current_test_item.use_core_functionality,
current_test_item.use_passthrough_gs, current_test_item.use_passthrough_ts,
current_test_item.fetch_culldistances);
/* Initialize VAO data */
configureVAO(clipdistances_array_size, culldistances_array_size, primitive_mode);
/* Run GLSL program and check results */
executeRenderTest(clipdistances_array_size, culldistances_array_size, primitive_mode,
current_test_item.use_passthrough_ts, current_test_item.fetch_culldistances);
} /* for (all iterations) */
} /* for (all test modes) */
} /* for (all test items) */
/* All done */
if (has_succeeded)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Returns pixel red component read from texture at position x, y.
*
* @param x x-coordinate to read pixel color component from
* @param y y-coordinate to read pixel color component from
**/
glw::GLint CullDistance::FunctionalTest::readRedPixelValue(glw::GLint x, glw::GLint y)
{
glw::GLint result = -1;
DE_ASSERT(x >= 0 && (glw::GLuint)x < m_to_width);
DE_ASSERT(y >= 0 && (glw::GLuint)y < m_to_height);
result = m_to_pixel_data_cache[(m_to_width * y + x) * m_to_pixel_data_cache_color_components];
return result;
}
/** Reads texture into m_to_pixel_data_cache.
* Texture size determined by fields m_to_width, m_to_height
**/
void CullDistance::FunctionalTest::readTexturePixels()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
m_to_pixel_data_cache.clear();
m_to_pixel_data_cache.resize(m_to_width * m_to_height * m_to_pixel_data_cache_color_components);
/* Read vertex from texture */
gl.readPixels(0, /* x */
0, /* y */
m_to_width, /* width */
m_to_height, /* height */
GL_RGBA, GL_UNSIGNED_SHORT, &m_to_pixel_data_cache[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed.");
}
/** Constructor.
*
* @param context Rendering context handle.
**/
CullDistance::NegativeTest::NegativeTest(deqp::Context& context)
: TestCase(context, "negative", "Cull Distance Negative Test")
, m_fs_id(0)
, m_po_id(0)
, m_temp_buffer(DE_NULL)
, m_vs_id(0)
{
/* Left blank on purpose */
}
/** @brief Cull Distance Negative Test deinitialization */
void CullDistance::NegativeTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
if (m_temp_buffer != DE_NULL)
{
delete[] m_temp_buffer;
m_temp_buffer = DE_NULL;
}
}
/** @brief Get string description of test with given parameters
*
* @param [in] n_test_iteration Test iteration number
* @param [in] should_redeclare_output_variables Indicate whether test redeclared gl_ClipDistance and gl_CullDistance
* @param [in] use_dynamic_index_based_writes Indicate whether test used dynamic index-based setters
*
* @return String containing description.
*/
std::string CullDistance::NegativeTest::getTestDescription(int n_test_iteration, bool should_redeclare_output_variables,
bool use_dynamic_index_based_writes)
{
std::stringstream stream;
stream << "Test iteration [" << n_test_iteration << "] which uses a vertex shader that:\n\n"
<< ((should_redeclare_output_variables) ?
"* redeclares gl_ClipDistance and gl_CullDistance arrays\n" :
"* does not redeclare gl_ClipDistance and gl_CullDistance arrays\n")
<< ((use_dynamic_index_based_writes) ? "* uses dynamic index-based writes\n" : "* uses static writes\n");
return stream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult CullDistance::NegativeTest::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Build the test shaders. */
const glw::GLchar* token_dynamic_index_based_writes = "DYNAMIC_INDEX_BASED_WRITES";
const glw::GLchar* token_insert_static_writes = "INSERT_STATIC_WRITES";
const glw::GLchar* token_n_gl_clipdistance_entries = "N_GL_CLIPDISTANCE_ENTRIES";
const glw::GLchar* token_n_gl_culldistance_entries = "N_GL_CULLDISTANCE_ENTRIES";
const glw::GLchar* token_redeclare_output_variables = "REDECLARE_OUTPUT_VARIABLES";
const glw::GLchar* fs_body = "#version 130\n"
"\n"
"void main()\n"
"{\n"
"}\n";
const glw::GLchar* vs_body_preamble = "#version 130\n"
"\n"
" #extension GL_ARB_cull_distance : require\n"
"\n";
const glw::GLchar* vs_body_main = "#ifdef REDECLARE_OUTPUT_VARIABLES\n"
" out float gl_ClipDistance[N_GL_CLIPDISTANCE_ENTRIES];\n"
" out float gl_CullDistance[N_GL_CULLDISTANCE_ENTRIES];\n"
"#endif\n"
"\n"
"void main()\n"
"{\n"
"#ifdef DYNAMIC_INDEX_BASED_WRITES\n"
" for (int n_clipdistance_entry = 0;\n"
" n_clipdistance_entry < N_GL_CLIPDISTANCE_ENTRIES;\n"
" ++n_clipdistance_entry)\n"
" {\n"
" gl_ClipDistance[n_clipdistance_entry] = float(n_clipdistance_entry) / "
"float(N_GL_CLIPDISTANCE_ENTRIES);\n"
" }\n"
"\n"
" for (int n_culldistance_entry = 0;\n"
" n_culldistance_entry < N_GL_CULLDISTANCE_ENTRIES;\n"
" ++n_culldistance_entry)\n"
" {\n"
" gl_CullDistance[n_culldistance_entry] = float(n_culldistance_entry) / "
"float(N_GL_CULLDISTANCE_ENTRIES);\n"
" }\n"
"#else\n"
" INSERT_STATIC_WRITES\n"
"#endif\n"
"}\n";
/* This test should only be executed if ARB_cull_distance is supported, or if
* we're running a GL4.5 context
*/
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_cull_distance") &&
!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
{
throw tcu::NotSupportedError("GL_ARB_cull_distance is not supported");
}
/* It only makes sense to run this test if GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES
* is lower than a sum of GL_MAX_CLIP_DISTANCES and GL_MAX_CLIP_CULL_DISTANCES.
*/
glw::GLint gl_max_clip_distances_value = 0;
glw::GLint gl_max_combined_clip_and_cull_distances_value = 0;
glw::GLint gl_max_cull_distances_value = 0;
glw::GLuint n_gl_clipdistance_array_items = 0;
std::string n_gl_clipdistance_array_items_string;
glw::GLuint n_gl_culldistance_array_items = 0;
std::string n_gl_culldistance_array_items_string;
std::string static_write_shader_body_part;
gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &gl_max_clip_distances_value);
gl.getIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, &gl_max_combined_clip_and_cull_distances_value);
gl.getIntegerv(GL_MAX_CULL_DISTANCES, &gl_max_cull_distances_value);
if (gl_max_clip_distances_value + gl_max_cull_distances_value < gl_max_combined_clip_and_cull_distances_value)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES is larger than or equal to "
"the sum of GL_MAX_CLIP_DISTANCES and GL_MAX_CULL_DISTANCES. Skipping."
<< tcu::TestLog::EndMessage;