/*------------------------------------------------------------------------- * 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 specializationMap; specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion); /* Create separate programs - start from vertex stage to catch varying names */ std::vector 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 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 */