From 5a4fd158a6669609d50dac8c2b7a042241cb4bf3 Mon Sep 17 00:00:00 2001 From: Piotr Byszewski Date: Mon, 31 Jul 2017 16:59:32 +0200 Subject: [PATCH] Test TF in separable program objects Enforce that only attributes taken from the program object active on the upstream shader are captured by transform feedback. Components: OpenGL VK-GL-CTS issue: 38 Affects: KHR-GL45.separable_programs_tf.* KHR-GLES32.core.separable_programs_tf.* Change-Id: Ia9a0da88a4a4e110893d4b928b8c847a69047df1 --- .../gl/khronos_mustpass/4.5.5.x/gl45-master.txt | 2 + .../khronos_mustpass/3.2.4.x/gles32-khr-master.txt | 2 + .../khronos_mustpass/master/gles32-khr-master.txt | 2 + external/openglcts/modules/common/CMakeLists.txt | 2 + .../glcSeparableProgramsTransformFeedbackTests.cpp | 344 +++++++++++++++++++++ .../glcSeparableProgramsTransformFeedbackTests.hpp | 52 ++++ external/openglcts/modules/gl/gl4cTestPackages.cpp | 2 + .../openglcts/modules/gles32/es32cTestPackage.cpp | 2 + 8 files changed, 408 insertions(+) create mode 100644 external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.cpp create mode 100644 external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp diff --git a/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt b/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt index 789dd0d..dd98f21 100644 --- a/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt +++ b/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt @@ -6511,3 +6511,5 @@ KHR-GL45.limits.max_compute_work_group_size KHR-GL45.polygon_offset_clamp.PolygonOffsetClampAvailability KHR-GL45.polygon_offset_clamp.PolygonOffsetClampMinMax KHR-GL45.polygon_offset_clamp.PolygonOffsetClampZeroInfinity +KHR-GL45.separable_programs_tf.tessellation_active +KHR-GL45.separable_programs_tf.geometry_active diff --git a/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles32-khr-master.txt b/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles32-khr-master.txt index c371fa9..500c67b 100644 --- a/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles32-khr-master.txt +++ b/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles32-khr-master.txt @@ -1001,3 +1001,5 @@ KHR-GLES32.core.constant_expressions.array_normalize_vec3_tess_eval KHR-GLES32.core.constant_expressions.array_normalize_vec4_geometry KHR-GLES32.core.constant_expressions.array_normalize_vec4_tess_control KHR-GLES32.core.constant_expressions.array_normalize_vec4_tess_eval +KHR-GLES32.core.separable_programs_tf.tessellation_active +KHR-GLES32.core.separable_programs_tf.geometry_active diff --git a/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles32-khr-master.txt b/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles32-khr-master.txt index ee0ea9e..a128526 100644 --- a/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles32-khr-master.txt +++ b/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles32-khr-master.txt @@ -1001,6 +1001,8 @@ KHR-GLES32.core.constant_expressions.array_normalize_vec3_tess_eval KHR-GLES32.core.constant_expressions.array_normalize_vec4_geometry KHR-GLES32.core.constant_expressions.array_normalize_vec4_tess_control KHR-GLES32.core.constant_expressions.array_normalize_vec4_tess_eval +KHR-GLES32.core.separable_programs_tf.tessellation_active +KHR-GLES32.core.separable_programs_tf.geometry_active KHR-GLES32.robust.robust_buffer_access_behavior.vertex_buffer_objects KHR-GLES32.robust.robust_buffer_access_behavior.texel_fetch KHR-GLES32.robust.robust_buffer_access_behavior.image_load_store diff --git a/external/openglcts/modules/common/CMakeLists.txt b/external/openglcts/modules/common/CMakeLists.txt index b734738..f1953ab 100644 --- a/external/openglcts/modules/common/CMakeLists.txt +++ b/external/openglcts/modules/common/CMakeLists.txt @@ -40,6 +40,8 @@ set(GLCTS_COMMON_SRCS glcPolygonOffsetClampTests.hpp glcRobustBufferAccessBehaviorTests.cpp glcRobustBufferAccessBehaviorTests.hpp + glcSeparableProgramsTransformFeedbackTests.cpp + glcSeparableProgramsTransformFeedbackTests.hpp glcShaderConstExprTests.hpp glcShaderConstExprTests.cpp glcShaderIndexingTests.cpp diff --git a/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.cpp b/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.cpp new file mode 100644 index 0000000..64cc809 --- /dev/null +++ b/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.cpp @@ -0,0 +1,344 @@ +/*------------------------------------------------------------------------- + * 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 */ diff --git a/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp b/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp new file mode 100644 index 0000000..9ab31ba --- /dev/null +++ b/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp @@ -0,0 +1,52 @@ +#ifndef _GLCSEPARABLEPROGRAMSTRANSFORMFEEDBACKTESTS_HPP +#define _GLCSEPARABLEPROGRAMSTRANSFORMFEEDBACKTESTS_HPP +/*------------------------------------------------------------------------- + * 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.hpp + * \brief + */ /*--------------------------------------------------------------------*/ + +#include "glcTestCase.hpp" +#include "glwDefs.hpp" +#include "tcuDefs.hpp" + +namespace glcts +{ + +/** Test group which encapsulates conformance tests that verify if the set of + * attributes captured in transform feedback mode is taken from the program object + * active on the upstream shader when the separable program objects are in use. +*/ +class SeparableProgramsTransformFeedbackTests : public deqp::TestCaseGroup +{ +public: + /* Public methods */ + SeparableProgramsTransformFeedbackTests(deqp::Context& context); + + void init(void); + +private: + SeparableProgramsTransformFeedbackTests(const SeparableProgramsTransformFeedbackTests& other); + SeparableProgramsTransformFeedbackTests& operator=(const SeparableProgramsTransformFeedbackTests& other); +}; + +} /* glcts namespace */ + +#endif // _GLCSEPARABLEPROGRAMSTRANSFORMFEEDBACKTESTS_HPP diff --git a/external/openglcts/modules/gl/gl4cTestPackages.cpp b/external/openglcts/modules/gl/gl4cTestPackages.cpp index 6367725..1453d7f 100644 --- a/external/openglcts/modules/gl/gl4cTestPackages.cpp +++ b/external/openglcts/modules/gl/gl4cTestPackages.cpp @@ -74,6 +74,7 @@ #include "glcPolygonOffsetClampTests.hpp" #include "glcRobustBufferAccessBehaviorTests.hpp" #include "glcSampleVariablesTests.hpp" +#include "glcSeparableProgramsTransformFeedbackTests.hpp" #include "glcShaderConstExprTests.hpp" #include "glcShaderIntegerMixTests.hpp" #include "glcShaderLibrary.hpp" @@ -368,6 +369,7 @@ void GL45TestPackage::init(void) addChild(new gl4cts::ShaderViewportLayerArray(getContext())); addChild(new gl4cts::LimitsTests(getContext())); addChild(new glcts::PolygonOffsetClamp(getContext())); + addChild(new glcts::SeparableProgramsTransformFeedbackTests(getContext())); } catch (...) { diff --git a/external/openglcts/modules/gles32/es32cTestPackage.cpp b/external/openglcts/modules/gles32/es32cTestPackage.cpp index d8a2456..2a9a5a9 100644 --- a/external/openglcts/modules/gles32/es32cTestPackage.cpp +++ b/external/openglcts/modules/gles32/es32cTestPackage.cpp @@ -27,6 +27,7 @@ #include "esextcTestPackage.hpp" #include "glcFragDepthTests.hpp" #include "glcInfoTests.hpp" +#include "glcSeparableProgramsTransformFeedbackTests.hpp" #include "glcShaderConstExprTests.hpp" #include "glcShaderIndexingTests.hpp" #include "glcShaderIntegerMixTests.hpp" @@ -156,6 +157,7 @@ void ES32TestPackage::init(void) coreGroup->addChild(new glcts::TextureBufferTests(getContext(), extParams)); coreGroup->addChild(new glcts::DrawBuffersIndexedTests(getContext(), extParams)); coreGroup->addChild(new glcts::ShaderConstExprTests(getContext())); + coreGroup->addChild(new glcts::SeparableProgramsTransformFeedbackTests(getContext())); addChild(coreGroup); tcu::TestCaseGroup* robustGroup = new tcu::TestCaseGroup(getTestContext(), "robust", ""); robustGroup->addChild(new es32cts::RobustBufferAccessBehaviorTests(getContext())); -- 2.7.4