blob: 64cc809554a4ea118320288e437adba2ee36ec9f [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2017 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 glcSeparableProgramXFBTests.cpp
* \brief
*/ /*-------------------------------------------------------------------*/
#include "glcSeparableProgramsTransformFeedbackTests.hpp"
#include "glcViewportArrayTests.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuCommandLine.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuTestLog.hpp"
using namespace tcu;
using namespace glu;
using namespace glw;
using namespace glcts::ViewportArray;
namespace glcts
{
/**
* @brief The StageIndex enum. Stages order coresponds to order
* in which shader sources are specified in Utils::program::build.
*/
enum StageIndex
{
FRAGMENT_STAGE_INDEX = 0,
GEOMETRY_STAGE_INDEX,
TESSELLATION_CONTROL_STAGE,
TESSELLATION_EVALUATION_STAGE,
VERTEX_STAGE,
STAGES_COUNT
};
/**
* @brief The StageTokens array. Stages order coresponds to order
* in which shader sources are specified in Utils::program::build.
*/
static const GLenum StageTokens[STAGES_COUNT] = { GL_FRAGMENT_SHADER_BIT, GL_GEOMETRY_SHADER_BIT,
GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
GL_VERTEX_SHADER_BIT };
/**
* @brief The StageData structure.
*/
struct StageData
{
const GLchar* source;
const GLchar* const* tfVaryings;
const GLuint tfVaryingsCount;
};
/**
* @brief The PerStageData structure containimg shader data per all stages.
*/
struct PerStageData
{
StageData stage[STAGES_COUNT];
};
static const GLchar* vs_code = "${VERSION}\n"
"flat out highp int o_vert;\n"
"${PERVERTEX_BLOCK}\n"
"void main()\n"
"{\n"
" o_vert = 1;\n"
" gl_Position = vec4(1, 0, 0, 1);\n"
"}\n";
static const GLchar* vs_tf_varyings[] = { "o_vert" };
static const GLchar* tcs_code = "${VERSION}\n"
"layout(vertices = 1) out;\n"
"flat in highp int o_vert[];\n"
"${PERVERTEX_BLOCK}\n"
"void main()\n"
"{\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
" gl_TessLevelOuter[3] = 1.0;\n"
"}\n";
static const GLchar* tes_code = "${VERSION}\n"
"layout (triangles, point_mode) in;\n"
"flat out highp int o_tess;\n"
"${PERVERTEX_BLOCK}\n"
"void main()\n"
"{\n"
" o_tess = 2;\n"
" gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
"}\n";
static const GLchar* tes_tf_varyings[] = { "o_tess" };
static const GLchar* gs_code = "${VERSION}\n"
"layout (points) in;\n"
"layout (points, max_vertices = 3) out;\n"
"${PERVERTEX_BLOCK}\n"
"flat in highp int ${IN_VARYING_NAME}[];\n"
"flat out highp int o_geom;\n"
"void main()\n"
"{\n"
" o_geom = 3;\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" EmitVertex();\n"
" o_geom = 3;\n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" EmitVertex();\n"
" o_geom = 3;\n"
" gl_Position = vec4(1, -1, 0, 1);\n"
" EmitVertex();\n"
"}\n";
static const GLchar* gs_tf_varyings[] = { "o_geom" };
static const GLchar* fs_code = "${VERSION}\n"
"flat in highp int ${IN_VARYING_NAME};"
"out highp vec4 o_color;\n"
"void main()\n"
"{\n"
" o_color = vec4(1.0);\n"
"}\n";
class SeparableProgramTFTestCase : public deqp::TestCase
{
public:
/* Public methods */
SeparableProgramTFTestCase(deqp::Context& context, const char* name, PerStageData shaderData, GLint expectedValue);
tcu::TestNode::IterateResult iterate(void);
protected:
/* Protected attributes */
PerStageData m_shaderData;
GLint m_expectedValue;
};
/** Constructor.
*
* @param context Rendering context
* @param name Test name
* @param description Test description
*/
SeparableProgramTFTestCase::SeparableProgramTFTestCase(deqp::Context& context, const char* name,
PerStageData shaderData, GLint expectedValue)
: deqp::TestCase(context, name, ""), m_shaderData(shaderData), m_expectedValue(expectedValue)
{
}
tcu::TestNode::IterateResult SeparableProgramTFTestCase::iterate(void)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
ContextType contextType = m_context.getRenderContext().getType();
GLSLVersion glslVersion = getContextTypeGLSLVersion(contextType);
/* For core GL gl_PerVertex interface block is combined from two parts.
* First part contains definition and the second part name, which is
* only specified for tess control stage (arrays are used here to avoid
* three branches in a loop). For ES both parts are empty string */
const char* blockName[STAGES_COUNT] = { "", ";\n", " gl_out[];\n", ";\n", ";\n" };
const char* blockEmptyName[STAGES_COUNT] = { "", "", "", "", "" };
std::string vertexBlock("");
const char** vertexBlockPostfix = blockEmptyName;
if (isContextTypeGLCore(contextType))
{
vertexBlock = "out gl_PerVertex"
"{\n"
" vec4 gl_Position;\n"
"}";
vertexBlockPostfix = blockName;
}
/* Construct specialization map - some specializations differ per stage */
std::map<std::string, std::string> specializationMap;
specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion);
/* Create separate programs - start from vertex stage to catch varying names */
std::vector<Utils::program> programs(STAGES_COUNT, Utils::program(m_context));
const char* code[STAGES_COUNT] = { 0, 0, 0, 0, 0 };
for (int stageIndex = VERTEX_STAGE; stageIndex > -1; --stageIndex)
{
StageData* stageData = m_shaderData.stage + stageIndex;
std::string source = stageData->source;
if (source.empty())
continue;
specializationMap["PERVERTEX_BLOCK"] = vertexBlock + vertexBlockPostfix[stageIndex];
std::string specializedShader = StringTemplate(source).specialize(specializationMap);
code[stageIndex] = specializedShader.c_str();
programs[stageIndex].build(0, code[0], code[1], code[2], code[3], code[4], stageData->tfVaryings,
stageData->tfVaryingsCount, true);
code[stageIndex] = 0;
/* Use varying name from current stage to specialize next stage */
if (stageData->tfVaryings)
specializationMap["IN_VARYING_NAME"] = stageData->tfVaryings[0];
}
/* Create program pipeline */
GLuint pipelineId;
gl.genProgramPipelines(1, &pipelineId);
gl.bindProgramPipeline(pipelineId);
for (int stageIndex = 0; stageIndex < STAGES_COUNT; ++stageIndex)
{
if (!programs[stageIndex].m_program_object_id)
continue;
gl.useProgramStages(pipelineId, StageTokens[stageIndex], programs[stageIndex].m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
}
/* Validate the pipeline */
GLint validateStatus = GL_FALSE;
gl.validateProgramPipeline(pipelineId);
GLU_EXPECT_NO_ERROR(gl.getError(), "glValidateProgramPipeline() call failed.");
gl.getProgramPipelineiv(pipelineId, GL_VALIDATE_STATUS, &validateStatus);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineiv() call failed.");
if (validateStatus != GL_TRUE)
{
GLint logLength;
gl.getProgramPipelineiv(pipelineId, GL_INFO_LOG_LENGTH, &logLength);
if (logLength)
{
std::vector<GLchar> logBuffer(logLength + 1);
gl.getProgramPipelineInfoLog(pipelineId, logLength + 1, NULL, &logBuffer[0]);
m_context.getTestContext().getLog() << tcu::TestLog::Message << &logBuffer[0] << tcu::TestLog::EndMessage;
}
TCU_FAIL("Program pipeline has not been validated successfully.");
}
/* Generate buffer object to hold result XFB data */
Utils::buffer tfb(m_context);
GLsizeiptr tfbSize = 100;
tfb.generate(GL_TRANSFORM_FEEDBACK_BUFFER);
tfb.update(tfbSize, 0 /* data */, GL_DYNAMIC_COPY);
tfb.bindRange(0, 0, tfbSize);
/* Generate VAO to use for the draw calls */
Utils::vertexArray vao(m_context);
vao.generate();
vao.bind();
/* Generate query object */
GLuint queryId;
gl.genQueries(1, &queryId);
/* Check if tessellation stage is active */
GLenum drawMode = GL_POINTS;
if (strlen(m_shaderData.stage[TESSELLATION_CONTROL_STAGE].source) > 0)
drawMode = GL_PATCHES;
/* Draw and capture data */
gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, queryId);
gl.beginTransformFeedback(GL_POINTS);
gl.patchParameteri(GL_PATCH_VERTICES, 1);
gl.drawArrays(drawMode, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
gl.endTransformFeedback();
gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
/* Get TF results */
GLuint writtenPrimitives = 0;
gl.getQueryObjectuiv(queryId, GL_QUERY_RESULT, &writtenPrimitives);
GLint* feedbackData = (GLint*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfbSize, GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer");
/* Verify if only values from upstream shader were captured */
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
if (writtenPrimitives != 0)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
for (GLuint dataIndex = 0; dataIndex < writtenPrimitives; ++dataIndex)
{
if (feedbackData[dataIndex] == m_expectedValue)
continue;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
break;
}
}
/* Cleanup */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
gl.deleteQueries(1, &queryId);
gl.bindProgramPipeline(0);
gl.deleteProgramPipelines(1, &pipelineId);
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
*/
SeparableProgramsTransformFeedbackTests::SeparableProgramsTransformFeedbackTests(deqp::Context& context)
: deqp::TestCaseGroup(context, "separable_programs_tf", "")
{
}
/** Initializes the test group contents. */
void SeparableProgramsTransformFeedbackTests::init(void)
{
PerStageData tessellation_active = { {
{ fs_code, NULL, 0 }, // fragment stage
{ "", NULL, 0 }, // geometry stage
{ tcs_code, NULL, 0 }, // tesselation control stage
{ tes_code, tes_tf_varyings, 1 }, // tesselation evaluation stage
{ vs_code, vs_tf_varyings, 1 } // vertex_stage
} };
PerStageData geometry_active = { {
{ fs_code, NULL, 0 }, // fragment stage
{ gs_code, gs_tf_varyings, 1 }, // geometry stage
{ tcs_code, NULL, 0 }, // tesselation control stage
{ tes_code, tes_tf_varyings, 1 }, // tesselation evaluation stage
{ vs_code, vs_tf_varyings, 1 } // vertex_stage
} };
addChild(new SeparableProgramTFTestCase(m_context, "tessellation_active", tessellation_active, 2));
addChild(new SeparableProgramTFTestCase(m_context, "geometry_active", geometry_active, 3));
}
} /* glcts namespace */