blob: 2171b932f3b62f20e61357d463db4de0ec759e3e [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 "esextcGeometryShaderRendering.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
#include <cmath>
#include <cstdlib>
#include <cstring>
namespace glcts
{
/** Constructor of the geometry shader test base class.
*
* @param context Rendering context
* @param name Name of the test
* @param description Description of the test
**/
GeometryShaderRendering::GeometryShaderRendering(Context& context, const ExtParameters& extParams, const char* name,
const char* description)
: TestCaseGroupBase(context, extParams, name, description)
{
/* Left blank intentionally */
}
/** Retrieves test name for specific <input layout qualifier,
* output layout qualifier, draw call mode> combination.
*
* @param input Input layout qualifier.
* @param output_type Output layout qualifier.
* @param drawcall_mode Draw call mode.
*
* NOTE: This function throws TestError exception if the requested combination
* is considered invalid.
*
* @return Requested string.
**/
const char* GeometryShaderRendering::getTestName(_shader_input input, _shader_output_type output_type,
glw::GLenum drawcall_mode)
{
const char* result = NULL;
switch (input)
{
case SHADER_INPUT_POINTS:
{
switch (output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
result = "points_input_line_strip_output";
break;
case SHADER_OUTPUT_TYPE_POINTS:
result = "points_input_points_output";
break;
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
result = "points_input_triangles_output";
break;
default:
{
TCU_FAIL("Unrecognized shader output type requested");
}
} /* switch (output_type) */
break;
}
case SHADER_INPUT_LINES:
{
switch (output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
switch (drawcall_mode)
{
case GL_LINES:
result = "lines_input_line_strip_output_lines_drawcall";
break;
case GL_LINE_STRIP:
result = "lines_input_line_strip_output_line_strip_drawcall";
break;
case GL_LINE_LOOP:
result = "lines_input_line_strip_output_line_loop_drawcall";
break;
default:
{
TCU_FAIL("UNrecognized draw call mode");
}
} /* switch (drawcall_mode) */
break;
} /* case SHADER_OUTPUT_TYPE_LINE_STRIP: */
case SHADER_OUTPUT_TYPE_POINTS:
{
switch (drawcall_mode)
{
case GL_LINES:
result = "lines_input_points_output_lines_drawcall";
break;
case GL_LINE_STRIP:
result = "lines_input_points_output_line_strip_drawcall";
break;
case GL_LINE_LOOP:
result = "lines_input_points_output_line_loop_drawcall";
break;
default:
{
TCU_FAIL("UNrecognized draw call mode");
}
} /* switch (drawcall_mode) */
break;
} /* case SHADER_OUTPUT_TYPE_POINTS: */
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
switch (drawcall_mode)
{
case GL_LINES:
result = "lines_input_triangle_strip_output_lines_drawcall";
break;
case GL_LINE_STRIP:
result = "lines_input_triangle_strip_output_line_strip_drawcall";
break;
case GL_LINE_LOOP:
result = "lines_input_triangle_strip_output_line_loop_drawcall";
break;
default:
{
TCU_FAIL("UNrecognized draw call mode");
}
} /* switch (drawcall_mode) */
break;
} /* case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP: */
default:
{
TCU_FAIL("Unrecognized shader output type requested");
}
} /* switch (output_type) */
break;
} /* case SHADER_INPUT_LINES:*/
case SHADER_INPUT_LINES_WITH_ADJACENCY:
{
switch (output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
if ((drawcall_mode == GL_LINES_ADJACENCY_EXT) || (drawcall_mode == GL_LINES_ADJACENCY))
{
result = "lines_with_adjacency_input_line_strip_output_lines_adjacency_drawcall";
}
else if ((drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT) || (drawcall_mode == GL_LINE_STRIP_ADJACENCY))
{
result = "lines_with_adjacency_input_line_strip_output_line_strip_drawcall";
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
} /* case SHADER_OUTPUT_TYPE_LINE_STRIP: */
case SHADER_OUTPUT_TYPE_POINTS:
{
if ((drawcall_mode == GL_LINES_ADJACENCY_EXT) || (drawcall_mode == GL_LINES_ADJACENCY))
{
result = "lines_with_adjacency_input_points_output_lines_adjacency_drawcall";
}
else if ((drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT) || (drawcall_mode == GL_LINE_STRIP_ADJACENCY))
{
result = "lines_with_adjacency_input_points_output_line_strip_drawcall";
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
} /* case SHADER_OUTPUT_TYPE_POINTS: */
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
if ((drawcall_mode == GL_LINES_ADJACENCY_EXT) || (drawcall_mode == GL_LINES_ADJACENCY))
{
result = "lines_with_adjacency_input_triangle_strip_output_lines_adjacency_drawcall";
}
else if ((drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT) || (drawcall_mode == GL_LINE_STRIP_ADJACENCY))
{
result = "lines_with_adjacency_input_triangle_strip_output_line_strip_drawcall";
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
} /* case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP: */
default:
{
TCU_FAIL("Unrecognized shader output type requested");
}
} /* switch (output_type) */
break;
} /* case SHADER_INPUT_LINES_WITH_ADJACENCY: */
case SHADER_INPUT_TRIANGLES:
{
switch (output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
switch (drawcall_mode)
{
case GL_TRIANGLES:
result = "triangles_input_line_strip_output_triangles_drawcall";
break;
case GL_TRIANGLE_FAN:
result = "triangles_input_line_strip_output_triangle_fan_drawcall";
break;
case GL_TRIANGLE_STRIP:
result = "triangles_input_line_strip_output_triangle_strip_drawcall";
break;
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (drawcall_mode) */
break;
} /* case SHADER_OUTPUT_TYPE_LINE_STRIP: */
case SHADER_OUTPUT_TYPE_POINTS:
{
switch (drawcall_mode)
{
case GL_TRIANGLES:
result = "triangles_input_points_output_triangles_drawcall";
break;
case GL_TRIANGLE_FAN:
result = "triangles_input_points_output_triangle_fan_drawcall";
break;
case GL_TRIANGLE_STRIP:
result = "triangles_input_points_output_triangle_strip_drawcall";
break;
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (drawcall_mode) */
break;
} /* case SHADER_OUTPUT_TYPE_POINTS: */
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
switch (drawcall_mode)
{
case GL_TRIANGLES:
result = "triangles_input_triangle_strip_output_triangles_drawcall";
break;
case GL_TRIANGLE_FAN:
result = "triangles_input_triangle_strip_output_triangle_fan_drawcall";
break;
case GL_TRIANGLE_STRIP:
result = "triangles_input_triangle_strip_output_triangle_strip_drawcall";
break;
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (drawcall_mode) */
break;
} /* case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP: */
default:
{
TCU_FAIL("Unrecognized shader output type requested");
}
} /* switch (output_type) */
break;
} /* case SHADER_INPUT_TRIANGLES: */
case SHADER_INPUT_TRIANGLES_WITH_ADJACENCY:
{
switch (output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
if ((drawcall_mode == GL_TRIANGLES_ADJACENCY_EXT) || (drawcall_mode == GL_TRIANGLES_ADJACENCY))
{
result = "triangles_with_adjacency_input_line_strip_output_triangles_adjacency_drawcall";
}
else if ((drawcall_mode == GL_TRIANGLE_STRIP_ADJACENCY_EXT) ||
(drawcall_mode == GL_TRIANGLE_STRIP_ADJACENCY))
{
result = "triangles_with_adjacency_input_line_strip_output_triangle_strip_adjacency_drawcall";
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
} /* case SHADER_OUTPUT_TYPE_LINE_STRIP: */
case SHADER_OUTPUT_TYPE_POINTS:
{
if ((drawcall_mode == GL_TRIANGLES_ADJACENCY_EXT) || (drawcall_mode == GL_TRIANGLES_ADJACENCY))
{
result = "triangles_with_adjacency_input_points_output_triangles_adjacency_drawcall";
}
else if ((drawcall_mode == GL_TRIANGLE_STRIP_ADJACENCY_EXT) ||
(drawcall_mode == GL_TRIANGLE_STRIP_ADJACENCY))
{
result = "triangles_with_adjacency_input_points_output_triangle_strip_adjacency_drawcall";
break;
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
} /* case SHADER_OUTPUT_TYPE_POINTS:*/
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
if ((drawcall_mode == GL_TRIANGLES_ADJACENCY_EXT) || (drawcall_mode == GL_TRIANGLES_ADJACENCY))
{
result = "triangles_with_adjacency_input_triangle_strip_output_triangles_adjacency_drawcall";
}
else if ((drawcall_mode == GL_TRIANGLE_STRIP_ADJACENCY_EXT) ||
(drawcall_mode == GL_TRIANGLE_STRIP_ADJACENCY))
{
result = "triangles_with_adjacency_input_triangle_strip_output_triangle_strip_adjacency_drawcall";
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
} /* case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP: */
default:
{
TCU_FAIL("Unrecognized shader output type requested");
}
} /* switch (output_type) */
break;
} /* case SHADER_INPUT_TRIANGLES_WITH_ADJACENCY: */
default:
{
/* Unrecognized geometry shader input layout qualifier */
TCU_FAIL("Unrecognized layout qualifier");
}
} /* switch (input) */
return result;
}
/* Initializes child instances of the group that will execute actual tests. */
void GeometryShaderRendering::init(void)
{
/* Initialize base class */
TestCaseGroupBase::init();
/* General variables */
glw::GLenum drawcall_mode = GL_NONE;
_shader_input input = SHADER_INPUT_UNKNOWN;
const glw::GLenum iterations[] = { /* geometry shader input layout qualifier */ /* draw call mode. */
SHADER_INPUT_POINTS, GL_POINTS, SHADER_INPUT_LINES, GL_LINES, SHADER_INPUT_LINES,
GL_LINE_STRIP, SHADER_INPUT_LINES, GL_LINE_LOOP,
SHADER_INPUT_LINES_WITH_ADJACENCY, GL_LINES_ADJACENCY_EXT,
SHADER_INPUT_LINES_WITH_ADJACENCY, GL_LINE_STRIP_ADJACENCY_EXT,
SHADER_INPUT_TRIANGLES, GL_TRIANGLES, SHADER_INPUT_TRIANGLES, GL_TRIANGLE_FAN,
SHADER_INPUT_TRIANGLES, GL_TRIANGLE_STRIP, SHADER_INPUT_TRIANGLES_WITH_ADJACENCY,
GL_TRIANGLES_ADJACENCY_EXT, SHADER_INPUT_TRIANGLES_WITH_ADJACENCY,
GL_TRIANGLE_STRIP_ADJACENCY_EXT };
const unsigned int n_iterations = sizeof(iterations) / sizeof(iterations[0]) / 2;
unsigned int n_output = 0;
for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration)
{
/* Retrieve iteration-specific input layout qualifier and draw call mode data */
input = (_shader_input)iterations[n_iteration * 2 + 0];
drawcall_mode = iterations[n_iteration * 2 + 1];
/* Instantiate a worker. Each worker needs to be initialized & executed separately for each
* of the three supported input layout qualifiers.*/
for (n_output = 0; n_output < SHADER_OUTPUT_TYPE_COUNT; ++n_output)
{
_shader_output_type output_type = (_shader_output_type)n_output;
const char* name = getTestName(input, output_type, drawcall_mode);
switch (input)
{
case SHADER_INPUT_POINTS:
{
addChild(
new GeometryShaderRenderingPointsCase(m_context, m_extParams, name, drawcall_mode, output_type));
break;
}
case SHADER_INPUT_LINES:
{
addChild(new GeometryShaderRenderingLinesCase(m_context, m_extParams, name, false, drawcall_mode,
output_type));
break;
} /* case SHADER_INPUT_LINES:*/
case SHADER_INPUT_LINES_WITH_ADJACENCY:
{
addChild(new GeometryShaderRenderingLinesCase(m_context, m_extParams, name, true, drawcall_mode,
output_type));
break;
} /* case SHADER_INPUT_LINES_WITH_ADJACENCY: */
case SHADER_INPUT_TRIANGLES:
{
addChild(new GeometryShaderRenderingTrianglesCase(m_context, m_extParams, name, false, drawcall_mode,
output_type));
break;
}
case SHADER_INPUT_TRIANGLES_WITH_ADJACENCY:
{
addChild(new GeometryShaderRenderingTrianglesCase(m_context, m_extParams, name, true, drawcall_mode,
output_type));
break;
}
default:
{
/* Unrecognized geometry shader input layout qualifier */
TCU_FAIL("Unrecognized layout qualifier");
}
} /* switch (input) */
} /* for (all output layout qualifiers) */
} /* for (all iterations) */
}
/** Base worker implementation */
/** Constructor.
*
* @param context Rendering context.
* @param testContext Test context.
* @param name Name of the test.
* @param description Description of what the test does.
**/
GeometryShaderRenderingCase::GeometryShaderRenderingCase(Context& context, const ExtParameters& extParams,
const char* name, const char* description)
: TestCaseBase(context, extParams, name, description)
, m_context(context)
, m_instanced_raw_arrays_bo_id(0)
, m_instanced_unordered_arrays_bo_id(0)
, m_instanced_unordered_elements_bo_id(0)
, m_noninstanced_raw_arrays_bo_id(0)
, m_noninstanced_unordered_arrays_bo_id(0)
, m_noninstanced_unordered_elements_bo_id(0)
, m_fs_id(0)
, m_gs_id(0)
, m_po_id(0)
, m_renderingTargetSize_uniform_location(0)
, m_singleRenderingTargetSize_uniform_location(0)
, m_vao_id(0)
, m_vs_id(0)
, m_fbo_id(0)
, m_read_fbo_id(0)
, m_to_id(0)
, m_instanced_fbo_id(0)
, m_instanced_read_fbo_id(0)
, m_instanced_to_id(0)
{
/* Left blank intentionally */
}
/** Releases all GLES objects initialized by the base class for geometry shader rendering case implementations. */
void GeometryShaderRenderingCase::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_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_instanced_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_instanced_fbo_id);
m_instanced_fbo_id = 0;
}
if (m_instanced_read_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_instanced_read_fbo_id);
m_instanced_read_fbo_id = 0;
}
if (m_instanced_to_id != 0)
{
gl.deleteTextures(1, &m_instanced_to_id);
m_instanced_to_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_instanced_raw_arrays_bo_id != 0)
{
gl.deleteBuffers(1, &m_instanced_raw_arrays_bo_id);
m_instanced_raw_arrays_bo_id = 0;
}
if (m_instanced_unordered_arrays_bo_id != 0)
{
gl.deleteBuffers(1, &m_instanced_unordered_arrays_bo_id);
m_instanced_unordered_arrays_bo_id = 0;
}
if (m_instanced_unordered_elements_bo_id != 0)
{
gl.deleteBuffers(1, &m_instanced_unordered_elements_bo_id);
m_instanced_unordered_elements_bo_id = 0;
}
if (m_noninstanced_raw_arrays_bo_id != 0)
{
gl.deleteBuffers(1, &m_noninstanced_raw_arrays_bo_id);
m_noninstanced_raw_arrays_bo_id = 0;
}
if (m_noninstanced_unordered_arrays_bo_id != 0)
{
gl.deleteBuffers(1, &m_noninstanced_unordered_arrays_bo_id);
m_noninstanced_unordered_arrays_bo_id = 0;
}
if (m_noninstanced_unordered_elements_bo_id != 0)
{
gl.deleteBuffers(1, &m_noninstanced_unordered_elements_bo_id);
m_noninstanced_unordered_elements_bo_id = 0;
}
if (m_read_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_read_fbo_id);
m_read_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;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Executes actual geometry shader-based rendering test, using an user-specified draw call.
*
* @param type Type of the draw call to use for the test.
**/
void GeometryShaderRenderingCase::executeTest(GeometryShaderRenderingCase::_draw_call_type type)
{
glw::GLuint draw_fbo_id = 0;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const glw::GLenum index_type = getUnorderedElementsDataType();
const glw::GLubyte max_elements_index = getUnorderedElementsMaxIndex();
const glw::GLubyte min_elements_index = getUnorderedElementsMinIndex();
const glw::GLenum mode = getDrawCallMode();
unsigned int n_elements = getAmountOfElementsPerInstance();
unsigned int n_instance = 0;
unsigned int n_instances = getAmountOfDrawInstances();
const unsigned int n_vertices = getAmountOfVerticesPerInstance();
const glw::GLint position_attribute_location = gl.getAttribLocation(m_po_id, "position");
glw::GLuint read_fbo_id = 0;
unsigned int rt_height = 0;
unsigned int rt_width = 0;
unsigned int single_rt_height = 0;
unsigned int single_rt_width = 0;
/* Reduce n_instances to 1 for non-instanced draw call modes */
if (type == DRAW_CALL_TYPE_GL_DRAW_ARRAYS || type == DRAW_CALL_TYPE_GL_DRAW_ELEMENTS ||
type == DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS)
{
draw_fbo_id = m_fbo_id;
n_instances = 1;
read_fbo_id = m_read_fbo_id;
}
else
{
draw_fbo_id = m_instanced_fbo_id;
read_fbo_id = m_instanced_read_fbo_id;
}
/* Retrieve render-target size */
getRenderTargetSize(n_instances, &rt_width, &rt_height);
getRenderTargetSize(1, &single_rt_width, &single_rt_height);
/* Configure GL_ARRAY_BUFFER binding */
switch (type)
{
case DRAW_CALL_TYPE_GL_DRAW_ARRAYS:
{
gl.bindBuffer(GL_ARRAY_BUFFER, m_noninstanced_raw_arrays_bo_id);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ARRAYS_INSTANCED:
{
gl.bindBuffer(GL_ARRAY_BUFFER, m_instanced_raw_arrays_bo_id);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED:
{
gl.bindBuffer(GL_ARRAY_BUFFER, m_instanced_unordered_arrays_bo_id);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ELEMENTS:
case DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS:
{
gl.bindBuffer(GL_ARRAY_BUFFER, m_noninstanced_unordered_arrays_bo_id);
break;
}
default:
{
/* Unrecognized draw call mode */
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (type) */
gl.vertexAttribPointer(position_attribute_location, 4, GL_FLOAT, GL_FALSE, 0 /* stride */, NULL);
gl.enableVertexAttribArray(position_attribute_location);
/* Configure GL_ELEMENT_ARRAY_BUFFER binding */
switch (type)
{
case DRAW_CALL_TYPE_GL_DRAW_ARRAYS:
case DRAW_CALL_TYPE_GL_DRAW_ARRAYS_INSTANCED:
{
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED:
{
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_instanced_unordered_elements_bo_id);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ELEMENTS:
case DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS:
{
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_noninstanced_unordered_elements_bo_id);
break;
}
default:
{
/* Unrecognized draw call mode */
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (type) */
/* Configure draw framebuffer */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fbo_id);
gl.viewport(0 /* x */, 0 /* y */, rt_width, rt_height);
/* Clear the color buffer. */
gl.clearColor(0.0f /* red */, 0.0f /* green */, 0.0f /* blue */, 0.0f /* alpha */);
gl.clear(GL_COLOR_BUFFER_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "Test set-up failed.");
/* Render the test geometry */
gl.useProgram(m_po_id);
gl.uniform2i(m_renderingTargetSize_uniform_location, rt_width, rt_height);
gl.uniform2i(m_singleRenderingTargetSize_uniform_location, single_rt_width, single_rt_height);
setUniformsBeforeDrawCall(type);
switch (type)
{
case DRAW_CALL_TYPE_GL_DRAW_ARRAYS:
{
gl.drawArrays(mode, 0 /* first */, n_vertices);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ARRAYS_INSTANCED:
{
gl.drawArraysInstanced(mode, 0 /* first */, n_vertices, n_instances);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ELEMENTS:
{
gl.drawElements(mode, n_elements, index_type, NULL);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED:
{
gl.drawElementsInstanced(mode, n_elements, index_type, NULL, n_instances);
break;
}
case DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS:
{
gl.drawRangeElements(mode, min_elements_index, max_elements_index, n_elements, index_type, NULL);
break;
}
default:
{
/* Unrecognized draw call mode */
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (type) */
GLU_EXPECT_NO_ERROR(gl.getError(), "Draw call failed.");
/* Retrieve rendered data */
unsigned char* rendered_data = new unsigned char[rt_height * rt_width * 4 /* components */];
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo_id);
gl.readPixels(0 /* x */, 0 /* y */, rt_width, rt_height, GL_RGBA, GL_UNSIGNED_BYTE, rendered_data);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed");
/* Verify if the test succeeded */
for (n_instance = 0; n_instance < n_instances; ++n_instance)
{
verify(type, n_instance, rendered_data);
} /* for (all instances) */
/* Release the data buffer */
delete[] rendered_data;
}
/** Initializes ES objects required to execute a set of tests for particular input layout qualifier. */
void GeometryShaderRenderingCase::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Retrieve derivative class' properties */
std::string fs_code = getFragmentShaderCode();
std::string gs_code = getGeometryShaderCode();
unsigned int n = 0;
unsigned int n_instances = getAmountOfDrawInstances();
std::string vs_code = getVertexShaderCode();
/* Set pixel storage properties before we continue */
gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
gl.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
/* Create buffer objects the derivative implementation will fill with data */
gl.genBuffers(1, &m_instanced_raw_arrays_bo_id);
gl.genBuffers(1, &m_instanced_unordered_arrays_bo_id);
gl.genBuffers(1, &m_instanced_unordered_elements_bo_id);
gl.genBuffers(1, &m_noninstanced_raw_arrays_bo_id);
gl.genBuffers(1, &m_noninstanced_unordered_arrays_bo_id);
gl.genBuffers(1, &m_noninstanced_unordered_elements_bo_id);
/* Set up a separate set of objects, that will be used for testing instanced and non-instanced draw calls */
for (n = 0; n < 2 /* non-/instanced draw calls */; ++n)
{
bool is_instanced = (n == 1);
glw::GLuint* draw_fbo_id_ptr = !is_instanced ? &m_fbo_id : &m_instanced_fbo_id;
glw::GLuint* read_fbo_id_ptr = (!is_instanced) ? &m_read_fbo_id : &m_instanced_read_fbo_id;
unsigned int to_height = 0;
unsigned int to_width = 0;
glw::GLuint* to_id_ptr = (!is_instanced) ? &m_to_id : &m_instanced_to_id;
/* n == 0: non-instanced draw calls, instanced otherwise */
getRenderTargetSize((n == 0) ? 1 : n_instances, &to_width, &to_height);
/* Create a texture object we will be rendering to */
gl.genTextures(1, to_id_ptr);
gl.bindTexture(GL_TEXTURE_2D, *to_id_ptr);
gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, GL_RGBA8, to_width, to_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure a 2D texture object");
/* Create and configure a framebuffer object that will be used for rendering */
gl.genFramebuffers(1, draw_fbo_id_ptr);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *draw_fbo_id_ptr);
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *to_id_ptr, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure draw framebuffer object");
if (gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
TCU_FAIL("Draw framebuffer is reported to be incomplete");
} /* if (gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) */
/* Create and bind a FBO we will use for reading rendered data */
gl.genFramebuffers(1, read_fbo_id_ptr);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *read_fbo_id_ptr);
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *to_id_ptr, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure read framebuffer object");
if (gl.checkFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
TCU_FAIL("Read framebuffer is reported to be incomplete");
} /* if (gl.checkFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) */
/* Fill the iteration-specific buffer objects with data */
glw::GLuint raw_arrays_bo_id = (is_instanced) ? m_instanced_raw_arrays_bo_id : m_noninstanced_raw_arrays_bo_id;
unsigned int raw_arrays_bo_size = getRawArraysDataBufferSize(is_instanced);
const void* raw_arrays_data = getRawArraysDataBuffer(is_instanced);
glw::GLuint unordered_arrays_bo_id =
(is_instanced) ? m_instanced_unordered_arrays_bo_id : m_noninstanced_unordered_arrays_bo_id;
unsigned int unordered_arrays_bo_size = getUnorderedArraysDataBufferSize(is_instanced);
const void* unordered_arrays_data = getUnorderedArraysDataBuffer(is_instanced);
glw::GLuint unordered_elements_bo_id =
(is_instanced) ? m_instanced_unordered_elements_bo_id : m_noninstanced_unordered_elements_bo_id;
unsigned int unordered_elements_bo_size = getUnorderedElementsDataBufferSize(is_instanced);
const void* unordered_elements_data = getUnorderedElementsDataBuffer(is_instanced);
gl.bindBuffer(GL_ARRAY_BUFFER, raw_arrays_bo_id);
gl.bufferData(GL_ARRAY_BUFFER, raw_arrays_bo_size, raw_arrays_data, GL_STATIC_COPY);
gl.bindBuffer(GL_ARRAY_BUFFER, unordered_arrays_bo_id);
gl.bufferData(GL_ARRAY_BUFFER, unordered_arrays_bo_size, unordered_arrays_data, GL_STATIC_COPY);
gl.bindBuffer(GL_ARRAY_BUFFER, unordered_elements_bo_id);
gl.bufferData(GL_ARRAY_BUFFER, unordered_elements_bo_size, unordered_elements_data, GL_STATIC_COPY);
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure test buffer objects");
/* Create and bind a VAO */
gl.genVertexArrays(1, &m_vao_id);
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate & bind a vertex array object");
/* Create and configure program & shader objects that will be used for the test case */
const char* fs_code_ptr = fs_code.c_str();
const char* gs_code_ptr = gs_code.c_str();
const char* vs_code_ptr = vs_code.c_str();
m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
m_gs_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER);
m_po_id = gl.createProgram();
m_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create shader/program objects");
if (!buildProgram(m_po_id, m_fs_id, 1, &fs_code_ptr, m_gs_id, 1, &gs_code_ptr, m_vs_id, 1, &vs_code_ptr))
{
TCU_FAIL("Could not build shader program");
}
/* Retrieve uniform locations */
m_renderingTargetSize_uniform_location = gl.getUniformLocation(m_po_id, "renderingTargetSize");
m_singleRenderingTargetSize_uniform_location = gl.getUniformLocation(m_po_id, "singleRenderingTargetSize");
}
/* Checks all draw call types for a specific Geometry Shader input layout qualifier.
*
* @return Always STOP.
*/
tcu::TestNode::IterateResult GeometryShaderRenderingCase::iterate()
{
if (!m_is_geometry_shader_extension_supported)
{
throw tcu::NotSupportedError(GEOMETRY_SHADER_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__);
}
initTest();
executeTest(GeometryShaderRenderingCase::DRAW_CALL_TYPE_GL_DRAW_ARRAYS);
executeTest(GeometryShaderRenderingCase::DRAW_CALL_TYPE_GL_DRAW_ARRAYS_INSTANCED);
executeTest(GeometryShaderRenderingCase::DRAW_CALL_TYPE_GL_DRAW_ELEMENTS);
executeTest(GeometryShaderRenderingCase::DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED);
executeTest(GeometryShaderRenderingCase::DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS);
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param drawcall_mode Draw call mode that should be tested with this test case instance.
* @param output_type Geometry shader output type to be used.
* @param context Rendering context;
* @param testContext Test context;
* @param name Test name.
**/
GeometryShaderRenderingPointsCase::GeometryShaderRenderingPointsCase(Context& context, const ExtParameters& extParams,
const char* name, glw::GLenum drawcall_mode,
_shader_output_type output_type)
: GeometryShaderRenderingCase(
context, extParams, name,
"Verifies all draw calls work correctly for specific input+output+draw call mode combination")
, m_output_type(output_type)
{
/* Sanity checks */
if (drawcall_mode != GL_POINTS)
{
TCU_FAIL("Only GL_POINTS draw call mode is supported for 'points' geometry shader input layout qualifier test "
"implementation");
}
if (output_type != SHADER_OUTPUT_TYPE_POINTS && output_type != SHADER_OUTPUT_TYPE_LINE_STRIP &&
output_type != SHADER_OUTPUT_TYPE_TRIANGLE_STRIP)
{
TCU_FAIL("Unsupported output layout qualifier type requested for 'points' geometry shader input layout "
"qualifier test implementation");
}
/* Retrieve rendertarget resolution for a single-instance case.
*
* Y coordinates will be dynamically updated in a vertex shader for
* multiple-instance tests. That's fine, since in multi-instanced tests
* we only expand the rendertarget's height; its width is unaffected.
*/
unsigned int rendertarget_height = 0;
unsigned int rendertarget_width = 0;
getRenderTargetSize(1, &rendertarget_width, &rendertarget_height);
/* Generate raw vertex array data. Note we do not differentiate between instanced and
* non-instanced cases for geometry shaders that emit points */
const unsigned int raw_array_data_size = getRawArraysDataBufferSize(false);
m_raw_array_data = new float[raw_array_data_size / sizeof(float)];
for (unsigned int n_point = 0; n_point < 8 /* points, as per test spec */; ++n_point)
{
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
m_raw_array_data[n_point * 4 + 0] =
-1 + ((float(3 + 7 * n_point) + 0.5f) / float(rendertarget_width)) * 2.0f;
m_raw_array_data[n_point * 4 + 1] = -1 + ((float(3.0f + 0.5f)) / float(rendertarget_height)) * 2.0f;
m_raw_array_data[n_point * 4 + 2] = 0.0f;
m_raw_array_data[n_point * 4 + 3] = 1.0f;
break;
}
case SHADER_OUTPUT_TYPE_POINTS:
{
m_raw_array_data[n_point * 4 + 0] =
-1 + ((float(1 + 5 * n_point) + 0.5f) / float(rendertarget_width)) * 2.0f;
m_raw_array_data[n_point * 4 + 1] = -1 + (float(1.5f + 0.5f) / float(rendertarget_height)) * 2.0f;
m_raw_array_data[n_point * 4 + 2] = 0.0f;
m_raw_array_data[n_point * 4 + 3] = 1.0f;
break;
}
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
m_raw_array_data[n_point * 4 + 0] = -1 + ((float(6 * n_point) + 0.5f) / float(rendertarget_width)) * 2.0f;
m_raw_array_data[n_point * 4 + 1] = -1.0f;
m_raw_array_data[n_point * 4 + 2] = 0.0f;
m_raw_array_data[n_point * 4 + 3] = 1.0f;
break;
}
default:
{
TCU_FAIL("Unsupported shader output layout qualifier requested");
}
} /* switch (m_output_type) */
} /* for (all points) */
/* Generate unordered data - we do not differentiate between instanced & non-instanced cases
* for "points" tests
*/
const unsigned int unordered_array_data_size = getUnorderedArraysDataBufferSize(false);
const unsigned int unordered_elements_data_size = getUnorderedElementsDataBufferSize(false);
m_unordered_array_data = new float[unordered_array_data_size / sizeof(float)];
m_unordered_elements_data = new unsigned char[unordered_elements_data_size];
for (unsigned int n_point = 0; n_point < 8 /* points, as per test spec */; ++n_point)
{
memcpy(m_unordered_array_data + n_point * 4, m_raw_array_data + (8 - n_point - 1) * 4,
sizeof(float) * 4 /* components */);
} /* for (all points) */
for (unsigned int n_point = 0; n_point < 8 /* points, as per test spec */; ++n_point)
{
m_unordered_elements_data[n_point] = (unsigned char)((n_point * 2) % 8 + n_point / 4);
} /* for (all points) */
}
/** Destructor. */
GeometryShaderRenderingPointsCase::~GeometryShaderRenderingPointsCase()
{
if (m_raw_array_data != NULL)
{
delete[] m_raw_array_data;
m_raw_array_data = NULL;
}
if (m_unordered_array_data != NULL)
{
delete[] m_unordered_array_data;
m_unordered_array_data = NULL;
}
if (m_unordered_elements_data != NULL)
{
delete[] m_unordered_elements_data;
m_unordered_elements_data = NULL;
}
}
/** Retrieves amount of instances that should be drawn with glDraw*Elements() calls.
*
* @return As per description.
**/
unsigned int GeometryShaderRenderingPointsCase::getAmountOfDrawInstances()
{
return 4 /* instances */;
}
/** Retrieves amount of indices that should be used for rendering a single instance
* (glDraw*Elements() calls only)
*
* @return As per description.
*/
unsigned int GeometryShaderRenderingPointsCase::getAmountOfElementsPerInstance()
{
return 8 /* elements */;
}
/** Retrieves amount of vertices that should be used for rendering a single instance
* (glDrawArrays*() calls only)
*
* @return As per description.
**/
unsigned int GeometryShaderRenderingPointsCase::getAmountOfVerticesPerInstance()
{
return 8 /* points */;
}
/** Draw call mode that should be used glDraw*() calls.
*
* @return As per description.
**/
glw::GLenum GeometryShaderRenderingPointsCase::getDrawCallMode()
{
return GL_POINTS;
}
/** Source code for a fragment shader that should be used for the test.
*
* @return As per description.
**/
std::string GeometryShaderRenderingPointsCase::getFragmentShaderCode()
{
static std::string fs_code = "${VERSION}\n"
"\n"
"precision highp float;\n"
"\n"
"in vec4 gs_fs_color;\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n"
" result = gs_fs_color;\n"
"}\n";
return fs_code;
}
/** Source code for a geometry shader that should be used for the test.
*
* @return As per description.
**/
std::string GeometryShaderRenderingPointsCase::getGeometryShaderCode()
{
static const char* lines_gs_code = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(points) in;\n"
"layout(line_strip, max_vertices=15) out;\n"
"\n"
"in vec4 vs_gs_color[1];\n"
"out vec4 gs_fs_color;\n"
"\n"
"uniform ivec2 renderingTargetSize;\n"
"\n"
"void main()\n"
"{\n"
" float bias = 0.0035;"
" for (int delta = 1; delta <= 3; ++delta)\n"
" {\n"
" float dx = float(delta * 2) / float(renderingTargetSize.x) + bias;\n"
" float dy = float(delta * 2) / float(renderingTargetSize.y) + bias;\n"
"\n"
/* TL->TR */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(-dx, -dy, 0, 0);\n"
" EmitVertex();\n"
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(dx, -dy, 0, 0);\n"
" EmitVertex();\n"
/* TR->BR */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(dx, dy, 0, 0);\n"
" EmitVertex();\n"
/* BR->BL */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(-dx, dy, 0, 0);\n"
" EmitVertex();\n"
/* BL->TL */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(-dx, -dy, 0, 0);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
" }\n"
"}\n";
static const char* points_gs_code = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(points) in;\n"
"layout(points, max_vertices=9) out;\n"
"\n"
"in vec4 vs_gs_color[1];\n"
"out vec4 gs_fs_color;\n"
"\n"
"uniform ivec2 renderingTargetSize;\n"
"\n"
"void main()\n"
"{\n"
" float dx = 2.0 / float(renderingTargetSize.x);\n"
" float dy = 2.0 / float(renderingTargetSize.y);\n"
"\n"
/* TL */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(-dx, -dy, 0, 0);\n"
" EmitVertex();\n"
/* TM */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(0, -dy, 0, 0);\n"
" EmitVertex();\n"
/* TR */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(dx, -dy, 0, 0);\n"
" EmitVertex();\n"
/* L */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(-dx, 0, 0, 0); \n"
" EmitVertex();\n"
/* M */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position;\n"
" EmitVertex();\n"
/* R */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(dx, 0, 0, 0);\n"
" EmitVertex();\n"
/* BL */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(-dx, dy, 0, 0);\n"
" EmitVertex();\n"
/* BM */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(0, dy, 0, 0);\n"
" EmitVertex();\n"
/* BR */
" gs_fs_color = vs_gs_color[0];\n"
" gl_Position = gl_in[0].gl_Position + vec4(dx, dy, 0, 0);\n"
" EmitVertex();\n"
"}\n";
static const char* triangles_gs_code = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices=6) out;\n"
"\n"
"in vec4 vs_gs_color[1];\n"
"out vec4 gs_fs_color;\n"
"\n"
"uniform ivec2 renderingTargetSize;\n"
"\n"
"void main()\n"
"{\n"
/* Assume that point position corresponds to TL corner. */
" float dx = float(2.0) / float(renderingTargetSize.x);\n"
" float dy = float(2.0) / float(renderingTargetSize.y);\n"
"\n"
/* BL 1 */
" gs_fs_color = vec4(vs_gs_color[0].x, 0, 0, 0);\n"
" gl_Position = gl_in[0].gl_Position + vec4(0, 6.0 * dy, 0, 0);\n"
" EmitVertex();\n"
/* TL 1 */
" gs_fs_color = vec4(vs_gs_color[0].x, 0, 0, 0);\n"
" gl_Position = gl_in[0].gl_Position;\n"
" EmitVertex();\n"
/* BR 1 */
" gs_fs_color = vec4(vs_gs_color[0].x, 0, 0, 0);\n"
" gl_Position = gl_in[0].gl_Position + vec4(6.0 * dx, 6.0 * dy, 0, 0);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
/* BR 2 */
" gs_fs_color = vec4(0, vs_gs_color[0].y, 0, 0);\n"
" gl_Position = gl_in[0].gl_Position + vec4(6.0 * dx, 6.0 * dy, 0, 0);\n"
" EmitVertex();\n"
/* TL 2 */
" gs_fs_color = vec4(0, vs_gs_color[0].y, 0, 0);\n"
" gl_Position = gl_in[0].gl_Position;\n"
" EmitVertex();\n"
/* TR 2 */
" gs_fs_color = vec4(0, vs_gs_color[0].y, 0, 0);\n"
" gl_Position = gl_in[0].gl_Position + vec4(6.0 * dx, 0, 0, 0);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
"}\n";
const char* result = NULL;
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
result = lines_gs_code;
break;
}
case SHADER_OUTPUT_TYPE_POINTS:
{
result = points_gs_code;
break;
}
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
result = triangles_gs_code;
break;
}
default:
{
TCU_FAIL("Requested shader output layout qualifier is unsupported");
}
} /* switch (m_output_type) */
return result;
}
/** Returns amount of bytes that should be allocated for a buffer object to hold
* vertex data to be used for glDrawArrays*() calls.
*
* @param instanced Ignored.
*
* @return As per description.
**/
glw::GLuint GeometryShaderRenderingPointsCase::getRawArraysDataBufferSize(bool /*instanced*/)
{
return sizeof(float) * (1 /* point */ * 4 /* coordinates */) * 8 /* points in total, as per test spec */;
}
/** Returns vertex data for the test. Only to be used for glDrawArrays*() calls.
*
* @param instanced Ignored.
*
* @return As per description.
**/
const void* GeometryShaderRenderingPointsCase::getRawArraysDataBuffer(bool /*instanced*/)
{
return m_raw_array_data;
}
/** Retrieves resolution of off-screen rendering buffer that should be used for the test.
*
* @param n_instances Amount of draw call instances this render target will be used for.
* @param out_width Deref will be used to store rendertarget's width. Must not be NULL.
* @param out_height Deref will be used to store rendertarget's height. Must not be NULL.
**/
void GeometryShaderRenderingPointsCase::getRenderTargetSize(unsigned int n_instances, unsigned int* out_width,
unsigned int* out_height)
{
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
*out_width = 56; /* as per test spec */
*out_height = 7 * n_instances; /* as per test spec */
break;
}
case SHADER_OUTPUT_TYPE_POINTS:
{
*out_width = 38; /* as per test spec */
*out_height = 3 * n_instances + 2 * (n_instances - 1); /* as per test spec */
break;
}
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
*out_width = 48; /* as per test spec */
*out_height = 6 * n_instances; /* as per test spec */
break;
}
default:
{
TCU_FAIL("Unsupported shader output type");
}
} /* switch (m_output_type) */
}
/** Returns amount of bytes that should be allocated for a buffer object to hold
* vertex data to be used for glDrawElements*() calls.
*
* @param instanced Ignored.
*
* @return As per description.
**/
glw::GLuint GeometryShaderRenderingPointsCase::getUnorderedArraysDataBufferSize(bool /*instanced*/)
{
/* Note: No difference between non-instanced and instanced cases for this class */
return sizeof(float) * (1 /* point */ * 4 /* coordinates */) * 8 /* points in total, as per test spec */;
}
/** Returns vertex data for the test. Only to be used for glDrawElements*() calls.
*
* @param instanced Ignored.
*
* @return As per description.
**/
const void* GeometryShaderRenderingPointsCase::getUnorderedArraysDataBuffer(bool /*instanced*/)
{
/* Note: No difference between non-instanced and instanced cases for this class */
return m_unordered_array_data;
}
/** Returns amount of bytes that should be allocated for a buffer object to hold
* index data to be used for glDrawElements*() calls.
*
* @param instanced Ignored.
*
* @return As per description.
*/
glw::GLuint GeometryShaderRenderingPointsCase::getUnorderedElementsDataBufferSize(bool /*instanced*/)
{
/* Note: No difference between non-instanced and instanced cases for this class */
return 8 /* indices */ * sizeof(unsigned char);
}
/** Returns index data for the test. Only to be used for glDrawElements*() calls.
*
* @param instanced Ignored.
*
* @return As per description.
**/
const void* GeometryShaderRenderingPointsCase::getUnorderedElementsDataBuffer(bool /*instanced*/)
{
/* Note: No difference between non-instanced and instanced cases for this class */
return m_unordered_elements_data;
}
/** Returns type of the index, to be used for glDrawElements*() calls.
*
* @return As per description.
**/
glw::GLenum GeometryShaderRenderingPointsCase::getUnorderedElementsDataType()
{
return GL_UNSIGNED_BYTE;
}
/** Retrieves maximum index value. To be used for glDrawRangeElements() test only.
*
* @return As per description.
**/
glw::GLubyte GeometryShaderRenderingPointsCase::getUnorderedElementsMaxIndex()
{
return 8 /* up to 8 points will be rendered */;
}
/** Retrieves minimum index value. To be used for glDrawRangeElements() test only.
*
* @return As per description.
**/
glw::GLubyte GeometryShaderRenderingPointsCase::getUnorderedElementsMinIndex()
{
return 0;
}
/** Retrieves vertex shader code to be used for the test.
*
* @return As per description.
**/
std::string GeometryShaderRenderingPointsCase::getVertexShaderCode()
{
static std::string lines_vs_code =
"${VERSION}\n"
"\n"
"in vec4 position;\n"
"uniform ivec2 renderingTargetSize;\n"
"out vec4 vs_gs_color;\n"
"\n"
"void main()\n"
"{\n"
/* non-instanced draw call cases */
" if (renderingTargetSize.y == 7)\n"
" {\n"
" gl_Position = position;"
" }\n"
" else\n"
" {\n"
/* instanced draw call cases: */
" gl_Position = vec4(position.x, \n"
" -1.0 + ((float(3 + gl_InstanceID * 7)) / float(renderingTargetSize.y - 1)) * 2.0,\n"
" position.zw);\n"
" }\n"
"\n"
" switch(gl_VertexID)\n"
" {\n"
" case 0: vs_gs_color = vec4(1, 0, 0, 0); break;\n"
" case 1: vs_gs_color = vec4(0, 1, 0, 0); break;\n"
" case 2: vs_gs_color = vec4(0, 0, 1, 0); break;\n"
" case 3: vs_gs_color = vec4(0, 0, 0, 1); break;\n"
" case 4: vs_gs_color = vec4(1, 1, 0, 0); break;\n"
" case 5: vs_gs_color = vec4(1, 0, 1, 0); break;\n"
" case 6: vs_gs_color = vec4(1, 0, 0, 1); break;\n"
" case 7: vs_gs_color = vec4(1, 1, 1, 0); break;\n"
" case 8: vs_gs_color = vec4(1, 1, 0, 1); break;\n"
" }\n"
"}\n";
static std::string points_vs_code =
"${VERSION}\n"
"\n"
"in vec4 position;\n"
"uniform ivec2 renderingTargetSize;\n"
"out vec4 vs_gs_color;\n"
"\n"
"void main()\n"
"{\n"
" gl_PointSize = 1.0;\n"
"\n"
/* non-instanced draw call cases */
" if (renderingTargetSize.y == 3)\n"
" {\n"
" gl_Position = position;\n"
" }\n"
/* instanced draw call cases */
" else\n"
" {\n"
" gl_Position = vec4(position.x,\n"
" -1.0 + (1.5 + float(5 * gl_InstanceID)) / float(renderingTargetSize.y) * 2.0,\n"
" position.zw);\n"
" }\n"
"\n"
" switch(gl_VertexID)\n"
" {\n"
" case 0: vs_gs_color = vec4(1, 0, 0, 0); break;\n"
" case 1: vs_gs_color = vec4(0, 1, 0, 0); break;\n"
" case 2: vs_gs_color = vec4(0, 0, 1, 0); break;\n"
" case 3: vs_gs_color = vec4(0, 0, 0, 1); break;\n"
" case 4: vs_gs_color = vec4(1, 1, 0, 0); break;\n"
" case 5: vs_gs_color = vec4(1, 0, 1, 0); break;\n"
" case 6: vs_gs_color = vec4(1, 0, 0, 1); break;\n"
" case 7: vs_gs_color = vec4(1, 1, 1, 0); break;\n"
" case 8: vs_gs_color = vec4(1, 1, 0, 1); break;\n"
" }\n"
"}\n";
static std::string triangles_vs_code =
"${VERSION}\n"
"\n"
"in vec4 position;\n"
"uniform ivec2 renderingTargetSize;\n"
"out vec4 vs_gs_color;\n"
"\n"
"void main()\n"
"{\n"
" if (renderingTargetSize.y == 6)\n"
" {\n"
" gl_Position = position;\n"
" }\n"
" else\n"
" {\n"
" gl_Position = vec4(position.x,\n"
" position.y + float(gl_InstanceID) * 6.0 / float(renderingTargetSize.y) * 2.0,\n"
" position.zw);\n"
" }\n"
"\n"
" switch(gl_VertexID)\n"
" {\n"
" case 0: vs_gs_color = vec4(0.1, 0.8, 0, 0); break;\n"
" case 1: vs_gs_color = vec4(0.2, 0.7, 0, 0); break;\n"
" case 2: vs_gs_color = vec4(0.3, 0.6, 1, 0); break;\n"
" case 3: vs_gs_color = vec4(0.4, 0.5, 0, 1); break;\n"
" case 4: vs_gs_color = vec4(0.5, 0.4, 0, 0); break;\n"
" case 5: vs_gs_color = vec4(0.6, 0.3, 1, 0); break;\n"
" case 6: vs_gs_color = vec4(0.7, 0.2, 0, 1); break;\n"
" case 7: vs_gs_color = vec4(0.8, 0.1, 1, 0); break;\n"
" }\n"
"}\n";
std::string result;
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
result = lines_vs_code;
break;
}
case SHADER_OUTPUT_TYPE_POINTS:
{
result = points_vs_code;
break;
}
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
result = triangles_vs_code;
break;
}
default:
{
TCU_FAIL("Unsupported shader output type used");
}
} /* switch (m_output_type) */
return result;
}
/** Verifies that the rendered data is correct.
*
* @param drawcall_type Type of the draw call that was used to render the geometry.
* @param instance_id Instance ID to be verified. Use 0 for non-instanced draw calls.
* @param data Contents of the rendertarget after the test has finished rendering.
**/
void GeometryShaderRenderingPointsCase::verify(_draw_call_type drawcall_type, unsigned int instance_id,
const unsigned char* data)
{
/* The array is directly related to vertex shader contents for "points" and "line_strip"
* outputs */
static const unsigned char ref_data[] = { 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0,
0, 0, 0, 255, 255, 255, 0, 0, 255, 0, 255, 0,
255, 0, 0, 255, 255, 255, 255, 0, 255, 255, 0, 255 };
/* The array refers to colors as defined in vertex shader used for "triangle_strip"
* output.
*/
static const unsigned char ref_data_triangle_strip[] = {
(unsigned char)(0.1f * 255), (unsigned char)(0.8f * 255), 0, 0,
(unsigned char)(0.2f * 255), (unsigned char)(0.7f * 255), 0, 0,
(unsigned char)(0.3f * 255), (unsigned char)(0.6f * 255), 255, 0,
(unsigned char)(0.4f * 255), (unsigned char)(0.5f * 255), 0, 255,
(unsigned char)(0.5f * 255), (unsigned char)(0.4f * 255), 0, 0,
(unsigned char)(0.6f * 255), (unsigned char)(0.3f * 255), 255, 0,
(unsigned char)(0.7f * 255), (unsigned char)(0.2f * 255), 0, 255,
(unsigned char)(0.8f * 255), (unsigned char)(0.1f * 255), 255, 0
};
unsigned int rt_height = 0;
unsigned int rt_width = 0;
/* Retrieve render-target size */
if (drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ARRAYS_INSTANCED ||
drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED)
{
getRenderTargetSize(getAmountOfDrawInstances(), &rt_width, &rt_height);
}
else
{
getRenderTargetSize(1, &rt_width, &rt_height);
}
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
for (unsigned int n_point = 0; n_point < 8 /* points */; ++n_point)
{
/* Each point takes a 7x7 block. The test should verify that the second "nested" block
* is of the valid color. Blocks can be built on top of each other if instanced draw
* calls are used. */
const unsigned char* reference_data = NULL;
const unsigned char* rendered_data = NULL;
const int row_width = rt_width * 4 /* components */;
const int sample_region_edge_px = 5 /* pixels */;
for (int n_edge = 0; n_edge < 4 /* edges */; ++n_edge)
{
/* Edge 1: TL->TR
* Edge 2: TR->BR
* Edge 3: BR->BL
* Edge 4: BL->TL
*/
int dx = 0;
int dy = 0;
int x = 0;
int x_start = 0;
int y = 0;
int y_start = 0;
switch (n_edge)
{
/* NOTE: The numbers here are as per test spec */
case 0:
dx = 1 /* px */;
dy = 0 /* px */;
x_start = 1 /* px */;
y_start = 1 /* px */;
break;
case 1:
dx = 0 /* px */;
dy = 1 /* px */;
x_start = 5 /* px */;
y_start = 1 /* px */;
break;
case 2:
dx = -1 /* px */;
dy = 0 /* px */;
x_start = 5 /* px */;
y_start = 5 /* px */;
break;
case 3:
dx = 0 /* px */;
dy = -1 /* px */;
x_start = 1 /* px */;
y_start = 5 /* px */;
break;
default:
{
TCU_FAIL("Invalid edge index");
}
} /* switch (n_edge) */
/* What color should the pixels have? */
if (drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ARRAYS ||
drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ARRAYS_INSTANCED)
{
reference_data = ref_data + n_point * 4 /* components */;
x = x_start + n_point * 7 /* pixel block size */;
y = y_start + instance_id * 7 /* pixel block size */;
}
else
{
int index = m_unordered_elements_data[n_point];
reference_data = ref_data + (8 - 1 - index) * 4 /* components */;
x = x_start + index * 7 /* pixel block size */;
y = y_start + instance_id * 7 /* pixel block size */;
}
/* Verify edge pixels */
for (int n_pixel = 0; n_pixel < sample_region_edge_px; ++n_pixel)
{
rendered_data = data + y * row_width + x * 4 /* components */;
if (memcmp(rendered_data, reference_data, 4 /* components */) != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "At (" << (int)x << ", " << (int)y
<< "), "
"rendered data ("
<< (int)rendered_data[0] << ", " << (int)rendered_data[1] << ", "
<< (int)rendered_data[2] << ", " << (int)rendered_data[3]
<< ") "
"is different from reference data ("
<< (int)reference_data[0] << ", " << (int)reference_data[1] << ", "
<< (int)reference_data[2] << ", " << (int)reference_data[3] << ")"
<< tcu::TestLog::EndMessage;
TCU_FAIL("Data comparison failed");
} /* if (data comparison failed) */
/* Move to next pixel */
x += dx;
y += dy;
} /* for (all pixels) */
} /* for (all edges) */
} /* for (all points) */
break;
} /* case SHADER_OUTPUT_TYPE_LINE_STRIP: */
case SHADER_OUTPUT_TYPE_POINTS:
{
/* For each 3px high row, we want to sample the second row. Area of a single "pixel" (incl. delta)
* can take no more than 5px, with the actual sampled area located in the second pixel.
*/
const int sample_region_width_px = 5; /* pixels */
for (int n = 0; n < 8 /* points */; ++n)
{
int x = 0;
int y = 0;
const unsigned char* reference_data = NULL;
const unsigned char* rendered_data = NULL;
/* Different ordering is used for indexed draw calls */
if (drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ELEMENTS ||
drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED ||
drawcall_type == DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS)
{
/* For indexed calls, vertex data is laid out in a reversed ordered */
int index = m_unordered_elements_data[n];
x = index * sample_region_width_px + 1;
y = instance_id * 5 + 1; /* middle row */
reference_data = ref_data + ((8 - 1 - index) * 4 /* components */);
rendered_data = data + (y * rt_width + x) * 4 /* components */;
} /* if (draw call is indiced) */
else
{
x = n * sample_region_width_px + 1;
y = instance_id * 5 + 1; /* middle row */
reference_data = ref_data + (n * 4 /* components */);
rendered_data = data + (y * rt_width + x) * 4 /* components */;
}
if (memcmp(rendered_data, reference_data, 4 /* components */) != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Reference data: [" << (int)reference_data[0] << ", "
<< (int)reference_data[1] << ", " << (int)reference_data[2] << ", "
<< (int)reference_data[3] << "] "
"for pixel at ("
<< x << ", " << y << ") "
"does not match ["
<< (int)rendered_data[0] << ", " << (int)rendered_data[1] << ", "
<< (int)rendered_data[2] << ", " << (int)rendered_data[3] << ").";
TCU_FAIL("Data comparison failed.");
} /* if (memcmp(rendered_data, reference_data, 4) != 0) */
} /* for (all points) */
break;
} /* case SHADER_OUTPUT_TYPE_POINTS: */
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
/* A pair of adjacent triangles is contained within a 6x6 bounding box.
* The box is defined by top-left corner which is located at input point's
* location.
* The left triangle should only use red channel, the one on the right
* should only use green channel.
* We find centroid for each triangle, sample its color and make sure
* it's valid.
*/
for (int n_point = 0; n_point < 8 /* points */; ++n_point)
{
const unsigned int epsilon = 1;
int point_x = 0;
int point_y = 6 * instance_id;
const unsigned char* rendered_data = NULL;
const unsigned char* reference_data = 0;
const unsigned int row_width = rt_width * 4 /* components */;
int t1[6 /* 3 * {x, y} */] = { 0 };
int t2[6 /* 3 * {x, y} */] = { 0 };
int t1_center[2 /* {x, y} */] = { 0 };
int t2_center[2 /* {x, y} */] = { 0 };
/* Different ordering is used for indexed draw calls */
if (drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ELEMENTS ||
drawcall_type == DRAW_CALL_TYPE_GL_DRAW_ELEMENTS_INSTANCED ||
drawcall_type == DRAW_CALL_TYPE_GL_DRAW_RANGE_ELEMENTS)
{
int index = m_unordered_elements_data[n_point];
point_x = 6 * index;
reference_data = ref_data_triangle_strip + (8 - 1 - index) * 4 /* components */;
}
else
{
point_x = 6 * n_point;
reference_data = ref_data_triangle_strip + n_point * 4 /* components */;
}
/* Calculate triangle vertex locations (corresponds to geometry shader logic) */
t1[0] = point_x;
t1[1] = point_y + 6;
t1[2] = point_x;
t1[3] = point_y;
t1[4] = point_x + 6;
t1[5] = point_y + 6;
t2[0] = point_x + 6;
t2[1] = point_y + 6;
t2[2] = point_x;
t2[3] = point_y;
t2[4] = point_x + 6;
t2[5] = point_y;
/* Calculate centroid locations */
t1_center[0] = (t1[0] + t1[2] + t1[4]) / 3;
t2_center[0] = (t2[0] + t2[2] + t2[4]) / 3;
t1_center[1] = (t1[1] + t1[3] + t1[5]) / 3;
t2_center[1] = (t2[1] + t2[3] + t2[5]) / 3;
/* Check the first triangle */
point_x = t1_center[0];
point_y = t1_center[1];
rendered_data = data + point_y * row_width + point_x * 4 /* components */;
if ((unsigned int)de::abs((int)rendered_data[0] - reference_data[0]) > epsilon || rendered_data[1] != 0 ||
rendered_data[2] != 0 || rendered_data[3] != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "At (" << (int)point_x << ", " << (int)point_y << ") "
<< "rendered pixel (" << (int)rendered_data[0] << ", " << (int)rendered_data[1]
<< ", " << (int)rendered_data[2] << ", " << (int)rendered_data[3] << ") "
<< "is different than (" << (int)reference_data[0] << ", 0, 0, 0)."
<< tcu::TestLog::EndMessage;
TCU_FAIL("Data comparison failed.");
}
/* Check the other triangle */
point_x = t2_center[0];
point_y = t2_center[1];
rendered_data = data + point_y * row_width + point_x * 4 /* components */;
if (rendered_data[0] != 0 || (unsigned int)de::abs((int)rendered_data[1] - reference_data[1]) > epsilon ||
rendered_data[2] != 0 || rendered_data[3] != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "At (" << (int)point_x << ", " << (int)point_y << ") "
<< "rendered pixel (" << (int)rendered_data[0] << ", " << (int)rendered_data[1]
<< ", " << (int)rendered_data[2] << ", " << (int)rendered_data[3] << ") "
<< "is different than (0, " << (int)reference_data[1] << ", 0, 0)."
<< tcu::TestLog::EndMessage;
TCU_FAIL("Data comparison failed.");
}
} /* for (all points) */
break;
} /* case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:*/
default:
{
TCU_FAIL("Unsupported output layout qualifier requested.");
break;
}
} /* switch(m_output_type) */
}
/* "lines" input primitive test implementation */
/** Constructor.
*
* @param use_adjacency_data true if the test case is being instantiated for draw call modes that will
* features adjacency data,
* false otherwise.
* @param drawcall_mode GL draw call mode that will be used for the tests.
* @param output_type Shader output type that the test case is being instantiated for.
* @param context Rendering context;
* @param testContext Test context;
* @param name Test name.
**/
GeometryShaderRenderingLinesCase::GeometryShaderRenderingLinesCase(Context& context, const ExtParameters& extParams,
const char* name, bool use_adjacency_data,
glw::GLenum drawcall_mode,
_shader_output_type output_type)
: GeometryShaderRenderingCase(
context, extParams, name,
"Verifies all draw calls work correctly for specific input+output+draw call mode combination")
, m_output_type(output_type)
, m_drawcall_mode(drawcall_mode)
, m_use_adjacency_data(use_adjacency_data)
, m_raw_array_instanced_data(0)
, m_raw_array_instanced_data_size(0)
, m_raw_array_noninstanced_data(0)
, m_raw_array_noninstanced_data_size(0)
, m_unordered_array_instanced_data(0)
, m_unordered_array_instanced_data_size(0)
, m_unordered_array_noninstanced_data(0)
, m_unordered_array_noninstanced_data_size(0)
, m_unordered_elements_instanced_data(0)
, m_unordered_elements_instanced_data_size(0)
, m_unordered_elements_noninstanced_data(0)
, m_unordered_elements_noninstanced_data_size(0)
, m_unordered_elements_max_index(16) /* maximum amount of vertices generated for this case */
, m_unordered_elements_min_index(0)
{
/* Sanity checks */
if (!m_use_adjacency_data)
{
if (drawcall_mode != GL_LINE_LOOP && drawcall_mode != GL_LINE_STRIP && drawcall_mode != GL_LINES)
{
TCU_FAIL("Only GL_LINE_LOOP or GL_LINE_STRIP or GL_LINES draw call modes are supported for 'lines' "
"geometry shader input layout qualifier test implementation");
}
}
else
{
if (drawcall_mode != GL_LINES_ADJACENCY_EXT && drawcall_mode != GL_LINE_STRIP_ADJACENCY_EXT)
{
TCU_FAIL("Only GL_LINES_ADJACENCY_EXT or GL_LINE_STRIP_ADJACENCY_EXT draw call modes are supported for "
"'lines_adjacency' geometry shader input layout qualifier test implementation");
}
}
if (output_type != SHADER_OUTPUT_TYPE_POINTS && output_type != SHADER_OUTPUT_TYPE_LINE_STRIP &&
output_type != SHADER_OUTPUT_TYPE_TRIANGLE_STRIP)
{
TCU_FAIL("Unsupported output layout qualifier type requested for 'lines' geometry shader input layout "
"qualifier test implementation");
}
/* Generate data in two flavors - one for non-instanced case, the other one for instanced case */
for (int n_case = 0; n_case < 2 /* cases */; ++n_case)
{
bool is_instanced = (n_case != 0);
int n_instances = 0;
float** raw_arrays_data_ptr = NULL;
unsigned int* raw_arrays_data_size_ptr = NULL;
unsigned int rendertarget_height = 0;
unsigned int rendertarget_width = 0;
float** unordered_arrays_data_ptr = NULL;
unsigned int* unordered_arrays_data_size_ptr = NULL;
unsigned char** unordered_elements_data_ptr = NULL;
unsigned int* unordered_elements_data_size_ptr = NULL;
if (!is_instanced)
{
/* Non-instanced case */
n_instances = 1;
raw_arrays_data_ptr = &m_raw_array_noninstanced_data;
raw_arrays_data_size_ptr = &m_raw_array_noninstanced_data_size;
unordered_arrays_data_ptr = &m_unordered_array_noninstanced_data;
unordered_arrays_data_size_ptr = &m_unordered_array_noninstanced_data_size;
unordered_elements_data_ptr = &m_unordered_elements_noninstanced_data;
unordered_elements_data_size_ptr = &m_unordered_elements_noninstanced_data_size;
}
else
{
/* Instanced case */
n_instances = getAmountOfDrawInstances();
raw_arrays_data_ptr = &m_raw_array_instanced_data;
raw_arrays_data_size_ptr = &m_raw_array_instanced_data_size;
unordered_arrays_data_ptr = &m_unordered_array_instanced_data;
unordered_arrays_data_size_ptr = &m_unordered_array_instanced_data_size;
unordered_elements_data_ptr = &m_unordered_elements_instanced_data;
unordered_elements_data_size_ptr = &m_unordered_elements_instanced_data_size;
}
getRenderTargetSize(n_instances, &rendertarget_width, &rendertarget_height);
/* Store full-screen quad coordinates that will be used for actual array data generation. */
float dx = 2.0f / float(rendertarget_width);
float dy = 2.0f / float(rendertarget_height);
/* Generate raw vertex array data */
float* raw_array_data_traveller = NULL;
unsigned int single_rt_height = 0;
unsigned int single_rt_width = 0;
unsigned int whole_rt_width = 0;
unsigned int whole_rt_height = 0;
switch (m_drawcall_mode)
{
case GL_LINE_LOOP:
{
*raw_arrays_data_size_ptr = 4 /* vertices making up the line strip */ * 4 /* components */ * sizeof(float);
break;
}
case GL_LINE_STRIP:
{
*raw_arrays_data_size_ptr = 5 /* vertices making up the line strip */ * 4 /* components */ * sizeof(float);
break;
}
case GL_LINE_STRIP_ADJACENCY_EXT:
{
*raw_arrays_data_size_ptr =
(5 /* vertices making up the line strip */ + 2 /* additional start/end adjacency vertices */) *
4 /* components */
* sizeof(float);
break;
}
case GL_LINES:
{
*raw_arrays_data_size_ptr =
2 /* points per line segment */ * 4 /* lines */ * 4 /* components */ * sizeof(float);
break;
}
case GL_LINES_ADJACENCY_EXT:
{
*raw_arrays_data_size_ptr =
4 /* points per line segment */ * 4 /* lines */ * 4 /* components */ * sizeof(float);
break;
}
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (m_drawcall_mode) */
*raw_arrays_data_ptr = new float[*raw_arrays_data_size_ptr / sizeof(float)];
raw_array_data_traveller = *raw_arrays_data_ptr;
getRenderTargetSize(1, &single_rt_width, &single_rt_height);
getRenderTargetSize(getAmountOfDrawInstances(), &whole_rt_width, &whole_rt_height);
/* Generate the data */
float end_y = 0;
std::vector<tcu::Vec4> quad_coordinates;
float start_y = 0;
float w = 1.0f;
if (n_instances != 1)
{
float delta = float(single_rt_height) / float(whole_rt_height);
/* Y coordinates are calculated in a vertex shader in multi-instanced case */
start_y = 0.0f;
end_y = 0.0f;
w = delta;
}
else
{
start_y = -1;
end_y = 1;
}
/* X, Y coordinates: correspond to X & Y locations of the vertex.
* Z coordinates: set to 0 if the vertex is located on top edge, otherwise set to 1.
* W coordinate: stores the delta (single-instanced RT height / multi-instanced RT height).
*/
float dx_multiplier = 1.5f; /* Default value for lines and line_strip output layout qualifiers */
float dy_multiplier = 1.5f; /* Default value for lines and line_strip output layout qualifiers */
if (m_output_type == SHADER_OUTPUT_TYPE_TRIANGLE_STRIP)
{
dx_multiplier = 0.0f;
dy_multiplier = 0.0f;
}
quad_coordinates.push_back(tcu::Vec4(-1 + dx_multiplier * dx, start_y + dy_multiplier * dy, 0, w)); /* TL */
quad_coordinates.push_back(tcu::Vec4(1 - dx_multiplier * dx, start_y + dy_multiplier * dy, 0, w)); /* TR */
quad_coordinates.push_back(tcu::Vec4(1 - dx_multiplier * dx, end_y - dy_multiplier * dy, 1, w)); /* BR */
quad_coordinates.push_back(tcu::Vec4(-1 + dx_multiplier * dx, end_y - dy_multiplier * dy, 1, w)); /* BL */
for (int n_line_segment = 0; n_line_segment < 4 /* edges */; ++n_line_segment)
{
/* Note we need to clamp coordinate indices here */
int coordinate0_index = (4 + n_line_segment - 1) % 4; /* protect against negative modulo values */
int coordinate1_index = (n_line_segment) % 4;
int coordinate2_index = (n_line_segment + 1) % 4;
int coordinate3_index = (n_line_segment + 2) % 4;
const tcu::Vec4& coordinate0 = quad_coordinates[coordinate0_index];
const tcu::Vec4& coordinate1 = quad_coordinates[coordinate1_index];
const tcu::Vec4& coordinate2 = quad_coordinates[coordinate2_index];
const tcu::Vec4& coordinate3 = quad_coordinates[coordinate3_index];
/* For GL_LINES, we need to explicitly define start & end-points for each segment.
* For GL_LINE_STRIP, we only need to explicitly define first start point. Following
* vertices define subsequent points making up the line strip.
* For GL_LINE_LOOP, we need all the data we used for GL_LINE_STRIP excluding the very
* last vertex.
*
* For GL_LINES_ADJACENCY_EXT, we extend GL_LINES data by vertices preceding and following points
* that make up a single line segment.
* For GL_LINE_STRIP_ADJACENCY_EXT, we extend GL_LINE_STRIP data by including a vertex preceding the
* actual first vertex, and by including a vertex that follows the end vertex closing the line
* strip.
*/
/* Preceding vertex */
if (m_drawcall_mode == GL_LINES_ADJACENCY_EXT ||
(m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT && n_line_segment == 0))
{
*raw_array_data_traveller = coordinate0.x();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate0.y();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate0.z();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate0.w();
raw_array_data_traveller++;
}
/* Vertex 1 */
if ((m_drawcall_mode != GL_LINE_STRIP && m_drawcall_mode != GL_LINE_STRIP_ADJACENCY_EXT &&
m_drawcall_mode != GL_LINE_LOOP) ||
((m_drawcall_mode == GL_LINE_STRIP || m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT ||
m_drawcall_mode == GL_LINE_LOOP) &&
n_line_segment == 0))
{
*raw_array_data_traveller = coordinate1.x();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate1.y();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate1.z();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate1.w();
raw_array_data_traveller++;
}
/* Vertex 2 */
if (m_drawcall_mode != GL_LINE_LOOP || (m_drawcall_mode == GL_LINE_LOOP && n_line_segment != 3))
{
*raw_array_data_traveller = coordinate2.x();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate2.y();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate2.z();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate2.w();
raw_array_data_traveller++;
}
/* Following vertex */
if (m_drawcall_mode == GL_LINES_ADJACENCY_EXT ||
(m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT && n_line_segment == 3))
{
*raw_array_data_traveller = coordinate3.x();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate3.y();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate3.z();
raw_array_data_traveller++;
*raw_array_data_traveller = coordinate3.w();
raw_array_data_traveller++;
}
} /* for (all line segments) */
/* Generate unordered data:
*
* The way we organise data in this case is that:
*
* - For index data, start and end points are flipped for each line segment.
* - For vertex data, vertex locations are stored to correspond to this order.
*
* We *DO NOT* modify the order in which we draw the line segments, since that could make the verification
* process even more complex than it already is.
*/
switch (m_drawcall_mode)
{
case GL_LINE_LOOP:
{
*unordered_arrays_data_size_ptr = *raw_arrays_data_size_ptr;
*unordered_arrays_data_ptr = new float[*unordered_arrays_data_size_ptr / sizeof(float)];
*unordered_elements_data_size_ptr = sizeof(unsigned char) * 4 /* points */;
*unordered_elements_data_ptr = new unsigned char[*unordered_elements_data_size_ptr];
for (unsigned int index = 0; index < 4 /* points */; ++index)
{
/* Gives 3-{0, 1, 2, 3} */
int new_index = 3 - index;
/* Store index data */
(*unordered_elements_data_ptr)[index] = (unsigned char)new_index;
/* Store vertex data */
memcpy((*unordered_arrays_data_ptr) + new_index * 4 /* components */,
(*raw_arrays_data_ptr) + index * 4 /* components */, sizeof(float) * 4 /* components */);
} /* for (all indices) */
break;
}
case GL_LINE_STRIP:
{
*unordered_arrays_data_size_ptr = *raw_arrays_data_size_ptr;
*unordered_arrays_data_ptr = new float[*unordered_arrays_data_size_ptr / sizeof(float)];
*unordered_elements_data_size_ptr = sizeof(unsigned char) * 5 /* points */;
*unordered_elements_data_ptr = new unsigned char[*unordered_elements_data_size_ptr];
for (unsigned int index = 0; index < 5 /* points */; ++index)
{
/* Gives 4-{0, 1, 2, 3, 4} */
int new_index = 4 - index;
/* Store index data */
(*unordered_elements_data_ptr)[index] = (unsigned char)new_index;
/* Store vertex data */
memcpy((*unordered_arrays_data_ptr) + new_index * 4 /* components */,
(*raw_arrays_data_ptr) + index * 4 /* components */, sizeof(float) * 4 /* components */);
} /* for (all indices) */
break;
}
case GL_LINES:
{
*unordered_arrays_data_size_ptr = sizeof(float) * 8 /* points */ * 4 /* components */;
*unordered_arrays_data_ptr = new float[*unordered_arrays_data_size_ptr / sizeof(float)];
*unordered_elements_data_size_ptr = sizeof(unsigned char) * 8 /* points */;
*unordered_elements_data_ptr = new unsigned char[*unordered_elements_data_size_ptr];
for (unsigned int index = 0; index < 8 /* points */; ++index)
{
/* Gives 7-{(1, 0), (3, 2), (5, 4), (7, 6)} */
int new_index = 7 - ((index / 2) * 2 + (index + 1) % 2);
/* Store index data */
(*unordered_elements_data_ptr)[index] = (unsigned char)new_index;
/* Store vertex data */
memcpy((*unordered_arrays_data_ptr) + new_index * 4 /* components */,
(*raw_arrays_data_ptr) + index * 4 /* components */, sizeof(float) * 4 /* components */);
} /* for (all indices) */
break;
} /* case GL_LINES: */
case GL_LINES_ADJACENCY_EXT:
case GL_LINE_STRIP_ADJACENCY_EXT:
{
/* For adjacency case, we may simplify the approach. Since the index data is now also going
* to include references to adjacent vertices, we can use the same ordering as in raw arrays data.
* Should the implementation misinterpret the data, it will treat adjacent vertex indices as actual
* vertex indices, breaking the verification.
*/
/* For array data, just point to unique vertex locations. Use the same order as in raw arrays data case
* to simplify the vertex shader for the pass.
**/
*unordered_arrays_data_size_ptr = sizeof(float) * 4 /* points */ * 4 /* components */;
*unordered_arrays_data_ptr = new float[*unordered_arrays_data_size_ptr / sizeof(float)];
if (m_drawcall_mode == GL_LINES_ADJACENCY_EXT)
{
*unordered_elements_data_size_ptr =
sizeof(unsigned char) * 4 /* vertices per line segment */ * 4 /* line segments */;
}
else
{
*unordered_elements_data_size_ptr =
sizeof(unsigned char) * (5 /* vertices making up a line strip */ + 2 /* start/end vertices */);
}
*unordered_elements_data_ptr = new unsigned char[*unordered_elements_data_size_ptr];
for (int n = 0; n < 4; ++n)
{
(*unordered_arrays_data_ptr)[4 * n + 0] = quad_coordinates[n].x();
(*unordered_arrays_data_ptr)[4 * n + 1] = quad_coordinates[n].y();
(*unordered_arrays_data_ptr)[4 * n + 2] = quad_coordinates[n].z();
(*unordered_arrays_data_ptr)[4 * n + 3] = quad_coordinates[n].w();
}
/* For elements data, we just walk over the quad and make sure we turn a full circle */
unsigned char* elements_data_traveller_ptr = *unordered_elements_data_ptr;
for (int n = 0; n < 4; ++n)
{
int component0_index = (n + 4 - 1) % 4; /* protect against underflow */
int component1_index = (n) % 4;
int component2_index = (n + 1) % 4;
int component3_index = (n + 2) % 4;
if (m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT)
{
/* Vertex adjacent to start vertex - include only at start */
if (n == 0)
{
*elements_data_traveller_ptr = (unsigned char)component0_index;
++elements_data_traveller_ptr;
}
/* Vertex index */
*elements_data_traveller_ptr = (unsigned char)component1_index;
++elements_data_traveller_ptr;
/* End vertex and the adjacent vertex - include only for final iteration */
if (n == 3)
{
/* End vertex */
*elements_data_traveller_ptr = (unsigned char)component2_index;
++elements_data_traveller_ptr;
/* Adjacent vertex */
*elements_data_traveller_ptr = (unsigned char)component3_index;
++elements_data_traveller_ptr;
}
}
else
{
/* GL_LINES_ADJACENCY_EXT */
*elements_data_traveller_ptr = (unsigned char)component0_index;
++elements_data_traveller_ptr;
*elements_data_traveller_ptr = (unsigned char)component1_index;
++elements_data_traveller_ptr;
*elements_data_traveller_ptr = (unsigned char)component2_index;
++elements_data_traveller_ptr;
*elements_data_traveller_ptr = (unsigned char)component3_index;
++elements_data_traveller_ptr;
}
}
break;
} /* case GL_LINES: */
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (m_drawcall_mode) */
} /* for (both cases) */
}
/** Destructor. */
GeometryShaderRenderingLinesCase::~GeometryShaderRenderingLinesCase()
{
if (m_raw_array_instanced_data != NULL)
{
delete[] m_raw_array_instanced_data;
m_raw_array_instanced_data = NULL;
}
if (m_raw_array_noninstanced_data != NULL)
{
delete[] m_raw_array_noninstanced_data;
m_raw_array_noninstanced_data = NULL;
}
if (m_unordered_array_instanced_data != NULL)
{
delete[] m_unordered_array_instanced_data;
m_unordered_array_instanced_data = NULL;
}
if (m_unordered_array_noninstanced_data != NULL)
{
delete[] m_unordered_array_noninstanced_data;
m_unordered_array_noninstanced_data = NULL;
}
if (m_unordered_elements_instanced_data != NULL)
{
delete[] m_unordered_elements_instanced_data;
m_unordered_elements_instanced_data = NULL;
}
if (m_unordered_elements_noninstanced_data != NULL)
{
delete[] m_unordered_elements_noninstanced_data;
m_unordered_elements_noninstanced_data = NULL;
}
}
/** Retrieves amount of instances that should be drawn with glDraw*Elements() calls.
*
* @return As per description.
**/
unsigned int GeometryShaderRenderingLinesCase::getAmountOfDrawInstances()
{
return 4;
}
/** Retrieves amount of indices that should be used for rendering a single instance
* (glDraw*Elements() calls only)
*
* @return As per description.
*/
unsigned int GeometryShaderRenderingLinesCase::getAmountOfElementsPerInstance()
{
unsigned int result = 0;
switch (m_drawcall_mode)
{
case GL_LINE_LOOP:
result = 4;
break;
case GL_LINE_STRIP:
result = 5;
break;
case GL_LINE_STRIP_ADJACENCY_EXT:
result = 7;
break;
case GL_LINES:
result = 8;
break;
case GL_LINES_ADJACENCY_EXT:
result = 16;
break;
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (m_drawcall_mode) */
return result;
}
/** Retrieves amount of vertices that should be used for rendering a single instance
* (glDrawArrays*() calls only)
*
* @return As per description.
**/
unsigned int GeometryShaderRenderingLinesCase::getAmountOfVerticesPerInstance()
{
unsigned int result = 0;
switch (m_drawcall_mode)
{
case GL_LINE_LOOP:
result = 4;
break;
case GL_LINE_STRIP:
result = 5;
break;
case GL_LINE_STRIP_ADJACENCY_EXT:
result = 7;
break;
case GL_LINES:
result = 8;
break;
case GL_LINES_ADJACENCY_EXT:
result = 16;
break;
default:
{
TCU_FAIL("Unrecognized draw call mode");
}
} /* switch (m_drawcall_mode) */
return result;
}
/** Draw call mode that should be used glDraw*() calls.
*
* @return As per description.
**/
glw::GLenum GeometryShaderRenderingLinesCase::getDrawCallMode()
{
return m_drawcall_mode;
}
/** Source code for a fragment shader that should be used for the test.
*
* @return As per description.
**/
std::string GeometryShaderRenderingLinesCase::getFragmentShaderCode()
{
static std::string fs_code = "${VERSION}\n"
"\n"
"precision highp float;\n"
"\n"
"in vec4 gs_fs_color;\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n"
" result = gs_fs_color;\n"
"}\n";
return fs_code;
}
/** Source code for a geometry shader that should be used for the test.
*
* @return As per description.
**/
std::string GeometryShaderRenderingLinesCase::getGeometryShaderCode()
{
static const char* lines_in_line_strip_out_gs_code_preamble = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(lines) in;\n"
"layout(line_strip, max_vertices=6) out;\n"
"\n"
"#define N_VERTICES_IN (2)\n"
"#define N_VERTEX0 (0)\n"
"#define N_VERTEX1 (1)\n"
"\n";
static const char* lines_adjacency_in_line_strip_out_gs_code_preamble = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(lines_adjacency) in;\n"
"layout(line_strip, max_vertices=6) out;\n"
"\n"
"#define N_VERTICES_IN (4)\n"
"#define N_VERTEX0 (1)\n"
"#define N_VERTEX1 (2)\n"
"\n";
static const char* lines_gs_code_main = "\n"
"in vec4 vs_gs_color[N_VERTICES_IN];\n"
"out vec4 gs_fs_color;\n"
"\n"
"uniform ivec2 renderingTargetSize;\n"
"\n"
"void main()\n"
"{\n"
" float dx = float(2.0) / float(renderingTargetSize.x);\n"
" float dy = float(2.0) / float(renderingTargetSize.y);\n"
"\n"
" vec4 start_pos = gl_in[N_VERTEX0].gl_Position;\n"
" vec4 end_pos = gl_in[N_VERTEX1].gl_Position;\n"
" vec4 start_col = vs_gs_color[N_VERTEX0];\n"
" vec4 end_col = vs_gs_color[N_VERTEX1];\n"
"\n"
/* Determine if this is a horizontal or vertical edge */
" if (start_pos.x != end_pos.x)\n"
" {\n"
/* Bottom line segment */
" gl_Position = vec4(-1.0, start_pos.y + dy, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(1.0, end_pos.y + dy, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
/* Middle line segment */
" gl_Position = vec4(-1.0, start_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(1.0, end_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
/* Top line segment */
" gl_Position = vec4(-1.0, start_pos.y - dy, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(1.0, end_pos.y - dy, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
" }\n"
" else\n"
" {\n"
/* Left line segment */
" gl_Position = vec4(start_pos.x - dx, start_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(end_pos.x - dx, end_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
/* Middle line segment */
" gl_Position = vec4(start_pos.x, start_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(end_pos.x, end_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
/* Right line segment */
" gl_Position = vec4(start_pos.x + dx, start_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(end_pos.x + dx, end_pos.y, 0, 1);\n"
" gs_fs_color = mix(start_col, end_col, 0.5);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
" }\n"
"}\n";
static const char* lines_in_points_out_gs_code_preamble = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(lines) in;\n"
"layout(points, max_vertices=72) out;\n"
"\n"
"#define N_VERTEX0 (0)\n"
"#define N_VERTEX1 (1)\n"
"#define N_VERTICES_IN (2)\n"
"\n";
static const char* lines_adjacency_in_points_out_gs_code_preamble = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(lines_adjacency) in;\n"
"layout(points, max_vertices=72) out;\n"
"\n"
"#define N_VERTEX0 (1)\n"
"#define N_VERTEX1 (2)\n"
"#define N_VERTICES_IN (4)\n"
"\n";
static const char* points_gs_code_main = "\n"
"in vec4 vs_gs_color[N_VERTICES_IN];\n"
"out vec4 gs_fs_color;\n"
"\n"
"uniform ivec2 renderingTargetSize;\n"
"\n"
"void main()\n"
"{\n"
" float dx = float(2.0) / float(renderingTargetSize.x);\n"
" float dy = float(2.0) / float(renderingTargetSize.y);\n"
"\n"
" vec4 start_pos = gl_in[N_VERTEX0].gl_Position;\n"
" vec4 end_pos = gl_in[N_VERTEX1].gl_Position;\n"
" vec4 start_col = vs_gs_color[N_VERTEX0];\n"
" vec4 end_col = vs_gs_color[N_VERTEX1];\n"
" vec4 delta_col = (end_col - start_col) / vec4(7.0);\n"
" vec4 delta_pos = (end_pos - start_pos) / vec4(7.0);\n"
"\n"
" for (int n_point = 0; n_point < 8; ++n_point)\n"
" {\n"
" vec4 ref_color = start_col + vec4(float(n_point)) * delta_col;\n"
" vec4 ref_pos = start_pos + vec4(float(n_point)) * delta_pos;\n"
"\n"
/* TL */
" gl_Position = ref_pos + vec4(-dx, -dy, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* TM */
" gl_Position = ref_pos + vec4(0, -dy, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* TR */
" gl_Position = ref_pos + vec4(dx, -dy, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* ML */
" gl_Position = ref_pos + vec4(-dx, 0, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* MM */
" gl_Position = ref_pos + vec4(0, 0, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* MR */
" gl_Position = ref_pos + vec4(dx, 0, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* BL */
" gl_Position = ref_pos + vec4(-dx, dy, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* BM */
" gl_Position = ref_pos + vec4(0, dy, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
/* BR */
" gl_Position = ref_pos + vec4(dx, dy, 0, 0);\n"
" gs_fs_color = ref_color;\n"
" EmitVertex();\n"
" }\n"
"}\n";
static const char* lines_adjacency_in_triangle_strip_out_gs_code_preamble =
"${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(lines_adjacency) in;\n"
"layout(triangle_strip, max_vertices=3) out;\n"
"\n"
"#define N_VERTEX0 (1)\n"
"#define N_VERTEX1 (2)\n"
"#define N_VERTICES_IN (4)\n";
static const char* lines_in_triangle_strip_out_gs_code_preamble = "${VERSION}\n"
"\n"
"${GEOMETRY_SHADER_ENABLE}\n"
"\n"
"layout(lines) in;\n"
"layout(triangle_strip, max_vertices=3) out;\n"
"\n"
"#define N_VERTEX0 (0)\n"
"#define N_VERTEX1 (1)\n"
"#define N_VERTICES_IN (2)\n";
static const char* triangles_gs_code_main = "flat in int instance_id[N_VERTICES_IN];\n"
" in vec4 vs_gs_color[N_VERTICES_IN];\n"
" out vec4 gs_fs_color;\n"
"\n"
"uniform ivec2 renderingTargetSize;\n"
"\n"
"void main()\n"
"{\n"
" float dx = float(1.5) / float(renderingTargetSize.x);\n"
" float dy = float(1.5) / float(renderingTargetSize.y);\n"
"\n"
" gl_Position = gl_in[N_VERTEX0].gl_Position;\n"
" gs_fs_color = vs_gs_color[N_VERTEX0];\n"
" EmitVertex();\n"
"\n"
" gl_Position = gl_in[N_VERTEX1].gl_Position;\n"
" gs_fs_color = vs_gs_color[N_VERTEX0];\n"
" EmitVertex();\n"
"\n"
" if (renderingTargetSize.y == 45 /* block size */)\n"
" {\n"
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
" }\n"
" else\n"
" {\n"
/* Each block takes 1/4th of the total render target.
* Third vertex should be placed in the middle of the block.
*/
" float y = -1.0 + 1.0 / 4.0 + float(instance_id[0]) * 0.5;\n"
"\n"
" gl_Position = vec4(0.0, y, 0.0, 1.0);\n"
" }\n"
"\n"
" gs_fs_color = vs_gs_color[N_VERTEX0];\n"
" EmitVertex();\n"
"\n"
" EndPrimitive();\n"
"}\n";
std::string result;
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
{
std::stringstream lines_adjacency_gs_code_stringstream;
std::string lines_adjacency_gs_code_string;
std::stringstream lines_gs_code_stringstream;
std::string lines_gs_code_string;
if (m_drawcall_mode == GL_LINES_ADJACENCY_EXT || m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT)
{
/* First request for lines_adjacency GS, form the string */
lines_adjacency_gs_code_stringstream << lines_adjacency_in_line_strip_out_gs_code_preamble
<< lines_gs_code_main;
lines_adjacency_gs_code_string = lines_adjacency_gs_code_stringstream.str();
result = lines_adjacency_gs_code_string;
}
else if (m_drawcall_mode == GL_LINES || m_drawcall_mode == GL_LINE_LOOP || m_drawcall_mode == GL_LINE_STRIP)
{
/* First request for lines GS, form the string */
lines_gs_code_stringstream << lines_in_line_strip_out_gs_code_preamble << lines_gs_code_main;
lines_gs_code_string = lines_gs_code_stringstream.str();
result = lines_gs_code_string;
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
}
case SHADER_OUTPUT_TYPE_POINTS:
{
std::stringstream lines_adjacency_gs_code_stringstream;
std::string lines_adjacency_gs_code_string;
std::stringstream lines_gs_code_stringstream;
std::string lines_gs_code_string;
if (m_drawcall_mode == GL_LINES_ADJACENCY_EXT || m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT)
{
/* First request for lines_adjacency GS, form the string */
lines_adjacency_gs_code_stringstream << lines_adjacency_in_points_out_gs_code_preamble
<< points_gs_code_main;
lines_adjacency_gs_code_string = lines_adjacency_gs_code_stringstream.str();
result = lines_adjacency_gs_code_string;
}
else if (m_drawcall_mode == GL_LINES || m_drawcall_mode == GL_LINE_LOOP || m_drawcall_mode == GL_LINE_STRIP)
{
/* First request for lines GS, form the string */
lines_gs_code_stringstream << lines_in_points_out_gs_code_preamble << points_gs_code_main;
lines_gs_code_string = lines_gs_code_stringstream.str();
result = lines_gs_code_string;
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
}
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
std::stringstream lines_adjacency_gs_code_stringstream;
std::string lines_adjacency_gs_code_string;
std::stringstream lines_gs_code_stringstream;
std::string lines_gs_code_string;
if (m_drawcall_mode == GL_LINES_ADJACENCY_EXT || m_drawcall_mode == GL_LINE_STRIP_ADJACENCY_EXT)
{
/* First request for lines_adjacency GS, form the string */
lines_adjacency_gs_code_stringstream << lines_adjacency_in_triangle_strip_out_gs_code_preamble
<< triangles_gs_code_main;
lines_adjacency_gs_code_string = lines_adjacency_gs_code_stringstream.str();
result = lines_adjacency_gs_code_string;
}
else if (m_drawcall_mode == GL_LINES || m_drawcall_mode == GL_LINE_LOOP || m_drawcall_mode == GL_LINE_STRIP)
{
/* First request for lines GS, form the string */
lines_gs_code_stringstream << lines_in_triangle_strip_out_gs_code_preamble << triangles_gs_code_main;
lines_gs_code_string = lines_gs_code_stringstream.str();
result = lines_gs_code_string;
}
else
{
TCU_FAIL("Unrecognized draw call mode");
}
break;
}
default:
{
TCU_FAIL("Requested shader output layout qualifier is unsupported");
}
} /* switch (m_output_type) */
return result;
}
/** Returns amount of bytes that should be allocated for a buffer object to hold
* vertex data to be used for glDrawArrays*() calls.
*
* @param instanced True if the data is to be used in regard to instanced draw calls,
* false otherwise.
*
* @return As per description.
**/
glw::GLuint GeometryShaderRenderingLinesCase::getRawArraysDataBufferSize(bool instanced)
{
return instanced ? m_raw_array_instanced_data_size : m_raw_array_noninstanced_data_size;
}
/** Returns vertex data for the test. Only to be used for glDrawArrays*() calls.
*
* @param instanced True if the data is to be used in regard to instanced draw calls,
* false otherwise.
*
* @return As per description.
**/
const void* GeometryShaderRenderingLinesCase::getRawArraysDataBuffer(bool instanced)
{
return instanced ? m_raw_array_instanced_data : m_raw_array_noninstanced_data;
}
/** Retrieves resolution of off-screen rendering buffer that should be used for the test.
*
* @param n_instances Amount of draw call instances this render target will be used for.
* @param out_width Deref will be used to store rendertarget's width. Must not be NULL.
* @param out_height Deref will be used to store rendertarget's height. Must not be NULL.
**/
void GeometryShaderRenderingLinesCase::getRenderTargetSize(unsigned int n_instances, unsigned int* out_width,
unsigned int* out_height)
{
switch (m_output_type)
{
case SHADER_OUTPUT_TYPE_LINE_STRIP:
case SHADER_OUTPUT_TYPE_POINTS:
case SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
{
/* For SHADER_OUTPUT_TYPE_POINTS:
* An edge size of 45px should be used. Given that each input will generate a 3x3 block,
* this should give us a delta of 3px between the "quads".
*
* For instanced draw calls, use a delta of 3px as well.
*
* For SHADER_OUTPUT_TYPE_LINE_STRIP:
* Each rectangle outline will take a 45x45 block. No vertical delta needs to be used.
*
* For SHADER_OUTPUT_TYPE_TRIANGLE_STRIP:
* Each combination of 4 triangles makes up a triangles that takes 45x45 area.
* No vertical delta needs to be used.
*/
*out_width = 3 /* 'pixel' size */ * 8 + 3 /* Delta size */ * 7;
*out_height = (3 /* 'pixel' size */ * 8 + 3 /* Delta size */ * 7) * n_instances;
break;
}
default:
{
TCU_FAIL("Unsupported shader output type");
}
} /* switch (m_output_type) */
}
/** Returns amount of bytes that should be allocated for a buffer object to hold
* vertex data to be used for glDrawElements*() calls.
*
* @param instanced true if the call is being made for an instanced draw call, false otherwise.
*
* @return As per description.
**/
glw::GLuint GeometryShaderRenderingLinesCase::getUnorderedArraysDataBufferSize(bool instanced)
{
return (instanced) ? m_unordered_array_instanced_data_size : m_unordered_array_noninstanced_data_size;
}
/** Returns vertex data for the test. Only to be used for glDrawElements*() calls.
*
* @param instanced true if the call is being made for an instanced draw call, false otherwise.
*
* @return As per description.
**/
const void* GeometryShaderRenderingLinesCase::getUnorderedArraysDataBuffer(bool instanced)
{
return instanced ? m_unordered_array_instanced_data : m_unordered_array_noninstanced_data;
}
/** Returns amount of bytes that should be allocated for a buffer object to hold
* index data to be used for glDrawElements*() calls.
*
* @param instanced true if the call is being made for an instanced draw call, false otherwise.
*
* @return As per description.
**/
glw::GLuint GeometryShaderRenderingLinesCase::getUnorderedElementsDataBufferSize(bool instanced)
{
return instanced ? m_unordered_elements_instanced_data_size : m_unordered_elements_noninstanced_data_size;
}
/** Returns index data for the test. Only to be used for glDrawElements*() calls.
*
* @param instanced true if the call is being made for an instanced draw call, false otherwise.
*
**/
const void* GeometryShaderRenderingLinesCase::getUnorderedElementsDataBuffer(bool instanced)
{
return instanced ? m_unordered_elements_instanced_data : m_unordered_elements_noninstanced_data;
}
/** Returns type of the index, to be used for glDrawElements*() calls.
*
* @return As per description.
**/
glw::GLenum GeometryShaderRenderingLinesCase::getUnorderedElementsDataType()
{
return GL_UNSIGNED_BYTE;
}
/** Retrieves maximum index value. To be used for glDrawRangeElements() test only.
*
* @return As per description.
**/
glw::GLubyte GeometryShaderRenderingLinesCase::getUnorderedElementsMaxIndex()
{
return m_unordered_elements_max_index;
}
/** Retrieves minimum index value. To be used for glDrawRangeElements() test only.
*
* @return As per description.
**/
glw::GLubyte GeometryShaderRenderingLinesCase::getUnorderedElementsMinIndex()
{
return m_unordered_elements_min_index;
}
/** Retrieves vertex shader code to be used for the test.
*
* @return As per description.
**/
std::string GeometryShaderRenderingLinesCase::getVertexShaderCode()
{
static std::string vs_code =
"${VERSION}\n"
"\n"
" in vec4 position;\n"
" uniform bool is_indexed_draw_call;\n"
" uniform bool is_gl_lines_adjacency_draw_call;\n"
" uniform bool is_gl_line_strip_adjacency_draw_call;\n"
" uniform bool is_gl_lines_draw_call;\n"
" uniform bool is_gl_line_loop_draw_call;\n"
" uniform ivec2 renderingTargetSize;\n"
"flat out int instance_id;\n"
" out vec4 vs_gs_color;\n"
"\n"
"void main()\n"
"{\n"
" instance_id = gl_InstanceID;\n"
"\n"
/* non-instanced */
" if (renderingTargetSize.y == 45 /* block size */)\n"
" {\n"
" gl_Position = position;\n"
" }\n"
" else\n"
" {\n"
" bool represents_top_edge = (position.z == 0.0);\n"
" float delta = position.w;\n"
" float y = 0.0;\n"
"\n"
" if (represents_top_edge)\n"
" {\n"
/* top vertices */
" y = position.y + float(gl_InstanceID) * delta * 2.0 - 1.0;\n"
" }\n"
" else\n"
" {\n"
" y = position.y + float(gl_InstanceID) * delta * 2.0 - 1.0 + delta * 2.0;\n"
/* bottom vertices */
" }\n"
"\n"
" gl_Position = vec4(position.x,\n"
" y,\n"
" position.z,\n"
" 1.0);\n"
" }\n"
"\n"
" vs_gs_color = vec4(0, 0, 0, 0);\n"
"\n"
" if (is_gl_line_loop_draw_call)\n"
" {\n"
/* GL_LINE_LOOP */
" if (!is_indexed_draw_call)\n"
" {\n"
" switch(gl_VertexID)\n"
" {\n"
" case 0: vs_gs_color = vec4(1, 0, 0, 0); break;\n"
" case 1: vs_gs_color = vec4(0, 1, 0, 0); break;\n"
" case 2: vs_gs_color = vec4(0, 0, 1, 0); break;\n"
" case 3: vs_gs_color = vec4(0, 0, 0, 1); break;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" switch(gl_VertexID)\n"
" {\n"
" case 3: vs_gs_color = vec4(1, 0, 0, 0); break;\n"
" case 0: vs_gs_color = vec4(0, 0, 0, 1); break;\n"
" case 1: vs_gs_color = vec4(0, 0, 1, 0); break;\n"
" case 2: vs_gs_color = vec4(0, 1, 0, 0); break;\n"
" }\n"
" }\n"
" }\n"
" else\n"
" if (is_gl_line_strip_adjacency_draw_call)\n"
" {\n"
/* GL_LINE_STRIP_ADJACENCY_EXT */
" if (!is_indexed_draw_call)\n"
" {\n"
" switch(gl_VertexID)\n"
" {\n"
" case 1:\n"
" case 5: vs_gs_color = vec4(1, 0, 0, 0); break;\n"
" case 2: vs_gs_color = vec4(0, 1, 0, 0); break;\n"
" case 3: vs_gs_color = vec4(0, 0, 1, 0); break;\n"
" case 0:\n"
" case 4: vs_gs_color = vec4(0, 0, 0, 1); break;\n"
" }\n"
" }\n"
" else\n"