blob: b72ec6ed31281d35b6dc46e52b8a97a2f9e7f9e5 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014-2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief
*/ /*-------------------------------------------------------------------*/
#include "esextcTessellationShaderBarrier.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
namespace glcts
{
/** Constructor
*
* @param context Test context
* @param name Test case's name
* @param description Test case's description
**/
TessellationShaderBarrierTests::TessellationShaderBarrierTests(Context& context, const ExtParameters& extParams)
: TestCaseGroupBase(context, extParams, "tessellation_shader_tc_barriers",
"Verifies memory barrier work correctly when used in"
"tessellation control shaders")
{
/* Left blank on purpose */
}
/* Instantiates all tests and adds them as children to the node */
void TessellationShaderBarrierTests::init(void)
{
addChild(new glcts::TessellationShaderBarrier1(m_context, m_extParams));
addChild(new glcts::TessellationShaderBarrier2(m_context, m_extParams));
addChild(new glcts::TessellationShaderBarrier3(m_context, m_extParams));
}
/** Constructor
*
* @param context Test context
* @param name Test case's name
* @param description Test case's desricption
**/
TessellationShaderBarrierTestCase::TessellationShaderBarrierTestCase(Context& context, const ExtParameters& extParams,
const char* name, const char* description)
: TestCaseBase(context, extParams, name, description)
, m_bo_id(0)
, m_fs_id(0)
, m_po_id(0)
, m_tcs_id(0)
, m_tes_id(0)
, m_vao_id(0)
, m_vs_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes all ES objects created for the test. */
void TessellationShaderBarrierTestCase::deinit()
{
/** Call base class' deinit() function */
TestCaseBase::deinit();
if (!m_is_tessellation_shader_supported)
{
return;
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Disable GL_RASTERIZER_DISCARD mode the test has enabled */
gl.disable(GL_RASTERIZER_DISCARD);
/* Bring back the original GL_PATCH_VERTICES_EXT setting */
gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3);
/* Revert buffer object bindings */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */);
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */);
/* Unbind vertex array object */
gl.bindVertexArray(0);
/* Free all the objects that might have been initialized */
if (m_bo_id != 0)
{
gl.deleteBuffers(1, &m_bo_id);
m_bo_id = 0;
}
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_tcs_id != 0)
{
gl.deleteShader(m_tcs_id);
m_tcs_id = 0;
}
if (m_tes_id != 0)
{
gl.deleteShader(m_tes_id);
m_tes_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
}
/** Initializes all ES objects that will be used for the test. */
void TessellationShaderBarrierTestCase::initTest()
{
/* The test requires EXT_tessellation_shader */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (!m_is_tessellation_shader_supported)
{
return;
}
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
/* Set up storage for XFB data. Also configure the BO binding points */
const unsigned int xfb_data_size = getXFBBufferSize();
gl.genBuffers(1, &m_bo_id);
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id);
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_data_size, NULL /* data */, GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up storage for XFB data");
/* Set up shader objects */
m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
m_tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
m_tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
m_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create shader objects");
/* Set up fragment shader body */
const char* fs_body = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
"}\n";
/* Set up tessellation control shader body */
const char* tcs_body = getTCSCode();
/* Set up tessellation evaluation shader body */
const char* tes_body = getTESCode();
/* Set up vertex shader body */
const char* vs_body = getVSCode();
/* Set up a program object */
m_po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed");
/* Set up XFB */
const char** names = NULL;
int n_names = 0;
getXFBProperties(&n_names, &names);
gl.transformFeedbackVaryings(m_po_id, n_names, names, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed");
/* Try to compile and link all the shader objects */
if (!buildProgram(m_po_id, m_fs_id, 1, &fs_body, m_tcs_id, 1, &tcs_body, m_tes_id, 1, &tes_body, m_vs_id, 1,
&vs_body))
{
TCU_FAIL("Program linking failed");
}
/* All set! */
}
/** Executes the test.
*
* Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
*
* Note the function throws exception should an error occur!
*
* @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
**/
tcu::TestNode::IterateResult TessellationShaderBarrierTestCase::iterate(void)
{
initTest();
/* Do not execute if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Prepare for the draw call */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLint drawcall_count = 0;
glw::GLenum drawcall_mode = GL_NONE;
glw::GLint drawcall_n_instances = 1;
glw::GLint n_patch_vertices = 0;
glw::GLenum tf_mode = GL_NONE;
getDrawCallArgs(&drawcall_mode, &drawcall_count, &tf_mode, &n_patch_vertices, &drawcall_n_instances);
gl.enable(GL_RASTERIZER_DISCARD);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed");
gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, n_patch_vertices);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed");
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");
gl.beginTransformFeedback(tf_mode);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed");
{
if (drawcall_n_instances == 1)
{
gl.drawArrays(drawcall_mode, 0 /* first */, drawcall_count);
}
else
{
DE_ASSERT(drawcall_n_instances > 1);
gl.drawArraysInstanced(drawcall_mode, 0 /* first */, drawcall_count, drawcall_n_instances);
}
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() or glDrawArraysInstanced() failed");
}
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed");
/* Retrieve the data generated by XFB */
int bo_size = getXFBBufferSize();
const void* xfb_data = NULL;
xfb_data = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, bo_size, GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed");
/* Verify the data */
bool is_xfb_data_valid = verifyXFBBuffer(xfb_data);
/* Unmap the buffer object */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() failed");
/* Set the test result, depending on the verification outcome */
if (is_xfb_data_valid)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor
*
* @param context Test context
**/
TessellationShaderBarrier1::TessellationShaderBarrier1(Context& context, const ExtParameters& extParams)
: TessellationShaderBarrierTestCase(context, extParams, "barrier_guarded_read_calls",
"Verifies invocation A can correctly read a per-vertex and"
" per-patch attribute modified by invocation B after a barrier() call")
, m_n_result_vertices(2048)
{
m_n_input_vertices = (m_n_result_vertices / 2 /* result points per isoline */) * 4 /* vertices per patch */;
}
/** Retrieves arguments to be used for the rendering process.
*
* @param out_mode Deref will be used to store draw call mode. Must not be NULL.
* @param out_count Deref will be used to store count argument to be used for the draw call.
* Must not be NULL.
* @param out_tf_mode Deref will be used to store transform feed-back mode to be used for
* glBeginTransformFeedback() call, prior to issuing the draw call. Must
* not be NULL.
* @param out_n_patch_vertices Deref will be used to store GL_PATCH_VERTICES_EXT pname value, to be set with
* glPatchParameteriEXT() call prior to issuing the draw call. Must not be NULL.
* @param out_n_instances Deref will be used to store amount of instances to use for the draw call.
* Using a value of 1 will result in a glDrawArrays() call. Using values larger
* than 1 will trigger glDrawArraysInstanced() call. Values smaller than 1 are
* forbidden.
**/
void TessellationShaderBarrier1::getDrawCallArgs(glw::GLenum* out_mode, glw::GLint* out_count, glw::GLenum* out_tf_mode,
glw::GLint* out_n_patch_vertices, glw::GLint* out_n_instances)
{
*out_count = m_n_input_vertices;
*out_mode = GL_PATCHES_EXT;
*out_n_instances = 1;
*out_n_patch_vertices = 4;
*out_tf_mode = GL_POINTS;
}
/** Retrieves tessellation control shader body.
*
* @return TC stage shader body.
**/
const char* TessellationShaderBarrier1::getTCSCode()
{
static const char* tcs_code =
"${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (vertices = 4) out;\n"
"\n"
"flat in int vertex_id[];\n"
"\n"
"patch out int test_patch_value;\n"
" out ivec4 test_vector [];\n"
" out ivec4 test_vector2[];\n"
"\n"
"void main()\n"
"{\n"
" if (gl_InvocationID == 0)\n"
" {\n"
" test_patch_value = vertex_id[0];\n"
" }\n"
"\n"
" test_vector[gl_InvocationID] = ivec4(vertex_id[gl_InvocationID],\n"
" vertex_id[gl_InvocationID] + 1,\n"
" vertex_id[gl_InvocationID] + 2,\n"
" vertex_id[gl_InvocationID] + 3);\n"
"\n"
" barrier();\n"
"\n"
" int next_invocation = (gl_InvocationID + 1) % 4;\n"
"\n"
" test_vector2[gl_InvocationID] = ivec4(test_vector[next_invocation].xyz, test_patch_value);\n"
"\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
"}\n";
return tcs_code;
}
/** Retrieves tessellation evaluation shader body.
*
* @return TE stage shader body.
**/
const char* TessellationShaderBarrier1::getTESCode()
{
static const char* tes_code = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout(isolines, point_mode) in;\n"
"\n"
"in ivec4 test_vector2[];\n"
"\n"
"out ivec4 result_vector2;\n"
"\n"
"void main()\n"
"{\n"
" int base = gl_PrimitiveID * 4;\n"
"\n"
" if (test_vector2[0].x == (base + 1) && test_vector2[0].y == (base + 2) && "
"test_vector2[0].z == (base + 3) && test_vector2[0].w == (base + 0) &&\n"
" test_vector2[1].x == (base + 2) && test_vector2[1].y == (base + 3) && "
"test_vector2[1].z == (base + 4) && test_vector2[1].w == (base + 0) &&\n"
" test_vector2[2].x == (base + 3) && test_vector2[2].y == (base + 4) && "
"test_vector2[2].z == (base + 5) && test_vector2[2].w == (base + 0) &&\n"
" test_vector2[3].x == (base + 0) && test_vector2[3].y == (base + 1) && "
"test_vector2[3].z == (base + 2) && test_vector2[3].w == (base + 0) )\n"
" {\n"
" result_vector2 = ivec4(1);\n"
" }\n"
" else\n"
" {\n"
" result_vector2 = ivec4(0);\n"
" }\n"
"}\n";
return tes_code;
}
/** Retrieves vertex shader body.
*
* @return Vertex shader body.
**/
const char* TessellationShaderBarrier1::getVSCode()
{
static const char* vs_code = "${VERSION}\n"
"\n"
"flat out int vertex_id;\n"
"\n"
"void main()\n"
"{\n"
" vertex_id = gl_VertexID;\n"
"}\n";
return vs_code;
}
/** Retrieves amount of bytes that should be used for allocating storage space for
* a buffer object that will later be used to hold XFB result data.
*
* @return Amount of bytes required by the test.
*/
int TessellationShaderBarrier1::getXFBBufferSize()
{
return static_cast<int>(m_n_result_vertices * sizeof(int) * 4 /* components */ * 2 /* points per isoline */);
}
/** Retrieves names of transform feedback varyings and amount of those. These should be used
* prior to link the test program object.
*
* @param out_n_names Deref will be used to store the number of names that @param out_names array
* holds. Must not be NULL.
* @param out_names Deref will be used to store a pointer to an array holding TF varying names;
* Must not be NULL.
**/
void TessellationShaderBarrier1::getXFBProperties(int* out_n_names, const char*** out_names)
{
static const char* names[1] = { "result_vector2" };
*out_n_names = 1;
*out_names = names;
}
/** Verifies data captured by XFB is correct.
*
* @param data Buffer holding the result XFB data. Must not be NULL.
*
* @return true if the result data is confirmed to be valid, false otherwise.
**/
bool TessellationShaderBarrier1::verifyXFBBuffer(const void* data)
{
int* data_int = (int*)data;
/* Run through all vertices */
for (unsigned int n_vertex = 0; n_vertex < m_n_result_vertices; ++n_vertex)
{
int retrieved_x = data_int[n_vertex * 4];
int retrieved_y = data_int[n_vertex * 4 + 1];
int retrieved_z = data_int[n_vertex * 4 + 2];
int retrieved_w = data_int[n_vertex * 4 + 3];
if (retrieved_x != 1 || retrieved_y != 1 || retrieved_z != 1 || retrieved_w != 1)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value returned: expected:[1, 1, 1, 1]"
<< " retrieved: "
<< "[" << retrieved_x << ", " << retrieved_y << ", " << retrieved_z << ", "
<< retrieved_w << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid rendering result");
}
}
return true;
}
/** Constructor
*
* @param context Test context
**/
TessellationShaderBarrier2::TessellationShaderBarrier2(Context& context, const ExtParameters& extParams)
: TessellationShaderBarrierTestCase(context, extParams, "barrier_guarded_write_calls",
"Verifies it is safe to write to the same per-patch output "
"from multiple TC invocations, as long as each write happens "
"in a separate phase.")
, m_n_result_vertices(2048)
{
m_n_input_vertices = (m_n_result_vertices / 2 /* result points per isoline */) * 4 /* vertices per patch */;
}
/** Retrieves arguments to be used for the rendering process.
*
* @param out_mode Deref will be used to store draw call mode. Must not be NULL.
* @param out_count Deref will be used to store count argument to be used for the draw call.
* Must not be NULL.
* @param out_tf_mode Deref will be used to store transform feed-back mode to be used for
* glBeginTransformFeedback() call, prior to issuing the draw call. Must
* not be NULL.
* @param out_n_patch_vertices Deref will be used to store GL_PATCH_VERTICES_EXT pname value, to be set with
* glPatchParameteriEXT() call prior to issuing the draw call. Must not be NULL.
* @param out_n_instances Deref will be used to store amount of instances to use for the draw call.
* Using a value of 1 will result in a glDrawArrays() call. Using values larger
* than 1 will trigger glDrawArraysInstanced() call. Values smaller than 1 are
* forbidden.
**/
void TessellationShaderBarrier2::getDrawCallArgs(glw::GLenum* out_mode, glw::GLint* out_count, glw::GLenum* out_tf_mode,
glw::GLint* out_n_patch_vertices, glw::GLint* out_n_instances)
{
*out_count = m_n_input_vertices;
*out_mode = GL_PATCHES_EXT;
*out_n_instances = 1;
*out_n_patch_vertices = 4;
*out_tf_mode = GL_POINTS;
}
/** Retrieves tessellation control shader body.
*
* @return TC stage shader body.
**/
const char* TessellationShaderBarrier2::getTCSCode()
{
static const char* tcs_code = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (vertices = 4) out;\n"
"\n"
" out float tcs_result[];\n"
"patch out int test_patch_value;\n"
"\n"
"void main()\n"
"{\n"
/* Four invocations per input patch will be executed. Have the first one write its
* secret value to the output per-patch attribute.
*/
" if (gl_InvocationID == 0)\n"
" {\n"
" test_patch_value = 123;\n"
" }\n"
"\n"
" barrier();\n"
"\n"
/* Let's have the second invocation update the value at this point */
" if (gl_InvocationID == 1)\n"
" {\n"
" test_patch_value = 234;\n"
" }\n"
"\n"
" barrier();\n"
"\n"
/* Finally, if this is invocation number one, check if test_patch_value
* stores a correct value.
*/
" tcs_result[gl_InvocationID] = 2.0;\n"
"\n"
" if (gl_InvocationID == 0)\n"
" {\n"
" if (test_patch_value == 234)\n"
" {\n"
" tcs_result[gl_InvocationID] = 1.0;\n"
" }\n"
" }\n"
"\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
"}\n";
return tcs_code;
}
/** Retrieves tessellation evaluation shader body.
*
* @return TE stage shader body.
**/
const char* TessellationShaderBarrier2::getTESCode()
{
static const char* tes_code = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout(isolines, point_mode) in;\n"
"\n"
" in float tcs_result[];\n"
"\n"
"out float tes_result;\n"
"\n"
"void main()\n"
"{\n"
/* TCS invocation order is undefined so take a maximum of all values we received */
" if (tcs_result[0] == 1.0 &&\n"
" tcs_result[1] == 2.0 &&\n"
" tcs_result[2] == 2.0 &&\n"
" tcs_result[3] == 2.0)\n"
" {\n"
" tes_result = 1.0;\n"
" }\n"
" else\n"
" {\n"
" tes_result = 0.0;\n"
" }\n"
"}\n";
return tes_code;
}
/** Retrieves vertex shader body.
*
* @return Vertex shader body.
**/
const char* TessellationShaderBarrier2::getVSCode()
{
static const char* vs_code = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
"}\n";
return vs_code;
}
/** Retrieves amount of bytes that should be used for allocating storage space for
* a buffer object that will later be used to hold XFB result data.
*
* @return Amount of bytes required by the test.
*/
int TessellationShaderBarrier2::getXFBBufferSize()
{
return static_cast<int>(m_n_result_vertices * sizeof(float) * 2 /* points per isoline */);
}
/** Retrieves names of transform feedback varyings and amount of those. These should be used
* prior to link the test program object.
*
* @param out_n_names Deref will be used to store the number of names that @param out_names array
* holds. Must not be NULL.
* @param out_names Deref will be used to store a pointer to an array holding TF varying names;
* Must not be NULL.
**/
void TessellationShaderBarrier2::getXFBProperties(int* out_n_names, const char*** out_names)
{
static const char* names[1] = { "tes_result" };
*out_n_names = 1;
*out_names = names;
}
/** Verifies data captured by XFB is correct.
*
* @param data Buffer holding the result XFB data. Must not be NULL.
*
* @return true if the result data is confirmed to be valid, false otherwise.
**/
bool TessellationShaderBarrier2::verifyXFBBuffer(const void* data)
{
float* data_float = (float*)data;
const float epsilon = (float)1e-5;
/* Run through all vertices */
for (unsigned int n_vertex = 0; n_vertex < m_n_result_vertices; ++n_vertex)
{
if (de::abs(data_float[n_vertex] - 1.0f /* valid result value */) > epsilon)
{
TCU_FAIL("Invalid data retrieved");
}
}
return true;
}
/** Constructor
*
* @param context Test context
**/
TessellationShaderBarrier3::TessellationShaderBarrier3(Context& context, const ExtParameters& extParams)
: TessellationShaderBarrierTestCase(context, extParams, "barrier_guarded_read_write_calls",
"Verifies it is safe to write to the same per-patch output "
"from multiple TC invocations, as long as each write happens "
"in a separate phase.")
, m_n_instances(10) /* as per test spec */
, m_n_invocations(16) /* as per test spec */
, m_n_patch_vertices(8) /* as per test spec */
, m_n_patches_per_invocation(2) /* as per test spec */
, m_n_result_vertices(2 /* result points per isoline */ * m_n_patches_per_invocation * m_n_invocations *
m_n_instances)
{
m_n_input_vertices = (m_n_result_vertices / 2 /* result points per isoline */) * 4 /* vertices per patch */;
}
/** Retrieves arguments to be used for the rendering process.
*
* @param out_mode Deref will be used to store draw call mode. Must not be NULL.
* @param out_count Deref will be used to store count argument to be used for the draw call.
* Must not be NULL.
* @param out_tf_mode Deref will be used to store transform feed-back mode to be used for
* glBeginTransformFeedback() call, prior to issuing the draw call. Must
* not be NULL.
* @param out_n_patch_vertices Deref will be used to store GL_PATCH_VERTICES_EXT pname value, to be set with
* glPatchParameteriEXT() call prior to issuing the draw call. Must not be NULL.
* @param out_n_instances Deref will be used to store amount of instances to use for the draw call.
* Using a value of 1 will result in a glDrawArrays() call. Using values larger
* than 1 will trigger glDrawArraysInstanced() call. Values smaller than 1 are
* forbidden.
**/
void TessellationShaderBarrier3::getDrawCallArgs(glw::GLenum* out_mode, glw::GLint* out_count, glw::GLenum* out_tf_mode,
glw::GLint* out_n_patch_vertices, glw::GLint* out_n_instances)
{
*out_count = m_n_patches_per_invocation * m_n_patch_vertices * m_n_instances;
*out_mode = GL_PATCHES_EXT;
*out_n_instances = m_n_instances;
*out_n_patch_vertices = m_n_patch_vertices;
*out_tf_mode = GL_POINTS;
}
/** Retrieves tessellation control shader body.
*
* @return TC stage shader body.
**/
const char* TessellationShaderBarrier3::getTCSCode()
{
static const char* tcs_code =
"${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (vertices = 16) out;\n"
"\n"
" out int tcs_data [];\n"
"patch out int tcs_patch_result[16];\n"
"\n"
"void main()\n"
"{\n"
/* Even invocations should write their gl_InvocationID value to their per-vertex output. */
" if ((gl_InvocationID % 2) == 0)\n"
" {\n"
" tcs_data[gl_InvocationID] = gl_InvocationID;\n"
" }\n"
"\n"
" barrier();\n"
"\n"
/* Odd invocations should read values stored by preceding even invocation,
* add current invocation's ID to that value, and then write it to its per-vertex
* output.
*/
" if ((gl_InvocationID % 2) == 1)\n"
" {\n"
" tcs_data[gl_InvocationID] = tcs_data[gl_InvocationID - 1] + gl_InvocationID;\n"
" }\n"
"\n"
" barrier();\n"
"\n"
/* Every fourth invocation should now read & sum up per-vertex outputs for four invocations
* following it (including the one discussed), and store it in a per-patch variable */
" tcs_patch_result[gl_InvocationID] = 0;\n"
"\n"
" if ((gl_InvocationID % 4) == 0)\n"
" {\n"
" tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID];\n"
" tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID+1];\n"
" tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID+2];\n"
" tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID+3];\n"
" }\n"
"\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
"}\n";
return tcs_code;
}
/** Retrieves tessellation evaluation shader body.
*
* @return TC stage shader body.
**/
const char* TessellationShaderBarrier3::getTESCode()
{
static const char* tes_code = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout(isolines, point_mode) in;\n"
"\n"
"patch in int tcs_patch_result[16];\n"
"\n"
"flat out ivec4 tes_result1;\n"
"flat out ivec4 tes_result2;\n"
"flat out ivec4 tes_result3;\n"
"flat out ivec4 tes_result4;\n"
"\n"
"void main()\n"
"{\n"
" tes_result1 = ivec4(tcs_patch_result[0], tcs_patch_result[1], "
"tcs_patch_result[2], tcs_patch_result[3]);\n"
" tes_result2 = ivec4(tcs_patch_result[4], tcs_patch_result[5], "
"tcs_patch_result[6], tcs_patch_result[7]);\n"
" tes_result3 = ivec4(tcs_patch_result[8], tcs_patch_result[9], "
"tcs_patch_result[10], tcs_patch_result[11]);\n"
" tes_result4 = ivec4(tcs_patch_result[12], tcs_patch_result[13], "
"tcs_patch_result[14], tcs_patch_result[15]);\n"
"}\n";
return tes_code;
}
/** Retrieves vertex shader body.
*
* @return Vertex shader body.
**/
const char* TessellationShaderBarrier3::getVSCode()
{
static const char* vs_code = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
"}\n";
return vs_code;
}
/** Retrieves amount of bytes that should be used for allocating storage space for
* a buffer object that will later be used to hold XFB result data.
*
* @return Amount of bytes required by the test.
*/
int TessellationShaderBarrier3::getXFBBufferSize()
{
return static_cast<int>(m_n_instances * m_n_result_vertices * sizeof(int) * 4 /* ivec4 */ *
2 /* points per isoline */);
}
/** Retrieves names of transform feedback varyings and amount of those. These should be used
* prior to link the test program object.
*
* @param out_n_names Deref will be used to store the number of names that @param out_names array
* holds. Must not be NULL.
* @param out_names Deref will be used to store a pointer to an array holding TF varying names;
* Must not be NULL.
**/
void TessellationShaderBarrier3::getXFBProperties(int* out_n_names, const char*** out_names)
{
static const char* names[] = { "tes_result1", "tes_result2", "tes_result3", "tes_result4" };
*out_n_names = 4;
*out_names = names;
}
/** Verifies data captured by XFB is correct.
*
* @param data Buffer holding the result XFB data. Must not be NULL.
*
* @return true if the result data is confirmed to be valid, false otherwise.
**/
bool TessellationShaderBarrier3::verifyXFBBuffer(const void* data)
{
const int* data_int = (const int*)data;
std::vector<int> tcs_data(m_n_invocations, 0);
std::vector<int> tcs_patch_result(m_n_invocations, 0);
/* This is a simple C++ port of the TCS used for the test.
*
* Note: We only need to consider a single set of values stored by TES
* for a single result point, as the same set of values will be
* reported for the other point. Owing to the fact gl_InvocationID
* in TCS will iterate from 0 to 15 for all input patches and instances,
* we can re-use the data for all subsequent input patches. */
/* Phase 1 */
for (unsigned int n = 0; n < m_n_invocations; n += 2)
{
tcs_data[n] = n;
}
/* Phase 2 */
for (unsigned int n = 1; n < m_n_invocations; n += 2)
{
tcs_data[n] = tcs_data[n - 1] + n;
}
/* Phase 3 */
for (unsigned int n_patch_vertex = 0; n_patch_vertex < m_n_invocations; ++n_patch_vertex)
{
const unsigned int invocation_id = n_patch_vertex;
tcs_patch_result[invocation_id] = 0;
if ((invocation_id % 4) == 0)
{
tcs_patch_result[invocation_id] += tcs_data[invocation_id];
tcs_patch_result[invocation_id] += tcs_data[invocation_id + 1];
tcs_patch_result[invocation_id] += tcs_data[invocation_id + 2];
tcs_patch_result[invocation_id] += tcs_data[invocation_id + 3];
}
} /* for (all patch vertices) */
/* Time to do the actual comparison. */
for (unsigned int n_patch = 0; n_patch < m_n_result_vertices / m_n_invocations; ++n_patch)
{
bool are_equal = true;
const int n_points_per_line_segment = 2;
const int* patch_data_int = data_int + n_patch * m_n_invocations * n_points_per_line_segment;
for (unsigned int n_invocation = 0; n_invocation < m_n_invocations; ++n_invocation)
{
if (patch_data_int[n_invocation] != tcs_patch_result[n_invocation])
{
are_equal = false;
break;
}
} /* for (all patch vertices which have contributed for given input patch being considered) */
if (!are_equal)
{
std::stringstream logMessage;
logMessage << "Result data for patch [" << n_patch << "]: (";
for (unsigned int n_patch_vertex = 0; n_patch_vertex < m_n_patch_vertices; ++n_patch_vertex)
{
logMessage << patch_data_int[n_patch_vertex];
if (n_patch_vertex == (m_n_patch_vertices - 1))
{
logMessage << "), ";
}
else
{
logMessage << ", ";
}
} /* for (all patch vertices) */
logMessage << "expected: ";
for (unsigned int n_patch_vertex = 0; n_patch_vertex < m_n_patch_vertices; ++n_patch_vertex)
{
logMessage << tcs_patch_result[n_patch_vertex];
if (n_patch_vertex == (m_n_patch_vertices - 1))
{
logMessage << "). ";
}
else
{
logMessage << ", ";
}
} /* for (all patch vertices) */
/* Log the message */
m_testCtx.getLog() << tcu::TestLog::Message << logMessage.str().c_str() << tcu::TestLog::EndMessage;
/* Bail out */
TCU_FAIL("Invalid data captured");
} /* if (!are_equal) */
} /* for (all patches) */
return true;
}
} /* namespace glcts */