Test TF in separable program objects
authorPiotr Byszewski <piotr.byszewski@mobica.com>
Mon, 31 Jul 2017 14:59:32 +0000 (16:59 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Fri, 11 Aug 2017 13:57:01 +0000 (09:57 -0400)
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

external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt
external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles32-khr-master.txt
external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles32-khr-master.txt
external/openglcts/modules/common/CMakeLists.txt
external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.cpp [new file with mode: 0644]
external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp [new file with mode: 0644]
external/openglcts/modules/gl/gl4cTestPackages.cpp
external/openglcts/modules/gles32/es32cTestPackage.cpp

index 789dd0d..dd98f21 100644 (file)
@@ -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
index c371fa9..500c67b 100644 (file)
@@ -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
index ee0ea9e..a128526 100644 (file)
@@ -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
index b734738..f1953ab 100644 (file)
@@ -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 (file)
index 0000000..64cc809
--- /dev/null
@@ -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<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 */
diff --git a/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp b/external/openglcts/modules/common/glcSeparableProgramsTransformFeedbackTests.hpp
new file mode 100644 (file)
index 0000000..9ab31ba
--- /dev/null
@@ -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
index 6367725..1453d7f 100644 (file)
@@ -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 (...)
        {
index d8a2456..2a9a5a9 100644 (file)
@@ -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()));