vktTessellationInvarianceTests.cpp
vktTessellationUserDefinedIO.hpp
vktTessellationUserDefinedIO.cpp
+ vktTessellationGeometryPassthroughTests.hpp
+ vktTessellationGeometryPassthroughTests.cpp
+ vktTessellationGeometryPointSizeTests.hpp
+ vktTessellationGeometryPointSizeTests.cpp
+ vktTessellationGeometryGridRenderTests.hpp
+ vktTessellationGeometryGridRenderTests.cpp
)
set(DEQP_VK_TESSELLATION_LIBS
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 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 Tessellation Geometry Interaction - Grid render (limits, scatter)
+*//*--------------------------------------------------------------------*/
+
+#include "vktTessellationGeometryGridRenderTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuSurface.hpp"
+#include "tcuRGBA.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkImageUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+ RENDER_SIZE = 256,
+};
+
+enum FlagBits
+{
+ FLAG_TESSELLATION_MAX_SPEC = 1u << 0,
+ FLAG_GEOMETRY_MAX_SPEC = 1u << 1,
+ FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC = 1u << 2,
+
+ FLAG_GEOMETRY_SCATTER_INSTANCES = 1u << 3,
+ FLAG_GEOMETRY_SCATTER_PRIMITIVES = 1u << 4,
+ FLAG_GEOMETRY_SEPARATE_PRIMITIVES = 1u << 5, //!< if set, geometry shader outputs separate grid cells and not continuous slices
+ FLAG_GEOMETRY_SCATTER_LAYERS = 1u << 6,
+};
+typedef deUint32 Flags;
+
+class GridRenderTestCase : public TestCase
+{
+public:
+ void initPrograms (vk::SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+ GridRenderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Flags flags);
+
+private:
+ const Flags m_flags;
+ const int m_tessGenLevel;
+ const int m_numGeometryInvocations;
+ const int m_numLayers;
+ int m_numGeometryPrimitivesPerInvocation;
+};
+
+GridRenderTestCase::GridRenderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Flags flags)
+ : TestCase (testCtx, name, description)
+ , m_flags (flags)
+ , m_tessGenLevel ((m_flags & FLAG_TESSELLATION_MAX_SPEC) ? 64 : 5)
+ , m_numGeometryInvocations ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) ? 32 : 4)
+ , m_numLayers ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) ? 8 : 1)
+{
+ DE_ASSERT(((flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) == ((flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
+
+ testCtx.getLog()
+ << tcu::TestLog::Message
+ << "Testing tessellation and geometry shaders that output a large number of primitives.\n"
+ << getDescription()
+ << tcu::TestLog::EndMessage;
+
+ if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
+ m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers << tcu::TestLog::EndMessage;
+
+ m_testCtx.getLog()
+ << tcu::TestLog::Message
+ << "Tessellation level: " << m_tessGenLevel << ", mode = quad.\n"
+ << "\tEach input patch produces " << (m_tessGenLevel*m_tessGenLevel) << " (" << (m_tessGenLevel*m_tessGenLevel*2) << " triangles)\n"
+ << tcu::TestLog::EndMessage;
+
+ int geometryOutputComponents = 0;
+ int geometryOutputVertices = 0;
+ int geometryTotalOutputComponents = 0;
+
+ if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
+ {
+ m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader minimum maximum output limits." << tcu::TestLog::EndMessage;
+
+ geometryOutputComponents = 64;
+ geometryOutputVertices = 256;
+ geometryTotalOutputComponents = 1024;
+ }
+ else
+ {
+ geometryOutputComponents = 64;
+ geometryOutputVertices = 16;
+ geometryTotalOutputComponents = 1024;
+ }
+
+ if ((m_flags & FLAG_GEOMETRY_MAX_SPEC) || (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC))
+ {
+ tcu::MessageBuilder msg(&m_testCtx.getLog());
+
+ msg << "Geometry shader, targeting following limits:\n";
+
+ if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
+ msg << "\tmaxGeometryOutputComponents = " << geometryOutputComponents << "\n"
+ << "\tmaxGeometryOutputVertices = " << geometryOutputVertices << "\n"
+ << "\tmaxGeometryTotalOutputComponents = " << geometryTotalOutputComponents << "\n";
+
+ if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
+ msg << "\tmaxGeometryShaderInvocations = " << m_numGeometryInvocations;
+
+ msg << tcu::TestLog::EndMessage;
+ }
+
+ const bool separatePrimitives = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
+ const int numComponentsPerVertex = 8; // vec4 pos, vec4 color
+ int numVerticesPerInvocation = 0;
+ int geometryVerticesPerPrimitive = 0;
+ int geometryPrimitivesOutPerPrimitive = 0;
+
+ if (separatePrimitives)
+ {
+ const int numComponentLimit = geometryTotalOutputComponents / (4 * numComponentsPerVertex);
+ const int numOutputLimit = geometryOutputVertices / 4;
+
+ m_numGeometryPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit);
+ numVerticesPerInvocation = m_numGeometryPrimitivesPerInvocation * 4;
+ }
+ else
+ {
+ // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
+ // Each slice is a triangle strip and is generated by a single shader invocation.
+ // One slice with 4 segment ends (nodes) and 3 segments:
+ // .__.__.__.
+ // |\ |\ |\ |
+ // |_\|_\|_\|
+
+ const int numSliceNodesComponentLimit = geometryTotalOutputComponents / (2 * numComponentsPerVertex); // each node 2 vertices
+ const int numSliceNodesOutputLimit = geometryOutputVertices / 2; // each node 2 vertices
+ const int numSliceNodes = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
+
+ numVerticesPerInvocation = numSliceNodes * 2;
+ m_numGeometryPrimitivesPerInvocation = (numSliceNodes - 1) * 2;
+ }
+
+ geometryVerticesPerPrimitive = numVerticesPerInvocation * m_numGeometryInvocations;
+ geometryPrimitivesOutPerPrimitive = m_numGeometryPrimitivesPerInvocation * m_numGeometryInvocations;
+
+ m_testCtx.getLog()
+ << tcu::TestLog::Message
+ << "Geometry shader:\n"
+ << "\tTotal output vertex count per invocation: " << numVerticesPerInvocation << "\n"
+ << "\tTotal output primitive count per invocation: " << m_numGeometryPrimitivesPerInvocation << "\n"
+ << "\tNumber of invocations per primitive: " << m_numGeometryInvocations << "\n"
+ << "\tTotal output vertex count per input primitive: " << geometryVerticesPerPrimitive << "\n"
+ << "\tTotal output primitive count per input primitive: " << geometryPrimitivesOutPerPrimitive << "\n"
+ << tcu::TestLog::EndMessage;
+
+ m_testCtx.getLog()
+ << tcu::TestLog::Message
+ << "Program:\n"
+ << "\tTotal program output vertices count per input patch: " << (m_tessGenLevel*m_tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n"
+ << "\tTotal program output primitive count per input patch: " << (m_tessGenLevel*m_tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n"
+ << tcu::TestLog::EndMessage;
+}
+
+void GridRenderTestCase::initPrograms (SourceCollections& programCollection) const
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "layout(location = 0) flat in mediump vec4 v_color;\n"
+ << "layout(location = 0) out mediump vec4 fragColor;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " fragColor = v_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+
+ // Tessellation control
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ "#extension GL_EXT_tessellation_shader : require\n"
+ "layout(vertices = 1) out;\n"
+ "\n"
+ "void main (void)\n"
+ "{\n"
+ " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
+ " gl_TessLevelInner[0] = float(" << m_tessGenLevel << ");\n"
+ " gl_TessLevelInner[1] = float(" << m_tessGenLevel << ");\n"
+ " gl_TessLevelOuter[0] = float(" << m_tessGenLevel << ");\n"
+ " gl_TessLevelOuter[1] = float(" << m_tessGenLevel << ");\n"
+ " gl_TessLevelOuter[2] = float(" << m_tessGenLevel << ");\n"
+ " gl_TessLevelOuter[3] = float(" << m_tessGenLevel << ");\n"
+ "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "layout(quads) in;\n"
+ << "\n"
+ << "layout(location = 0) out mediump ivec2 v_tessellationGridPosition;\n"
+ << "\n"
+ << "// note: No need to use precise gl_Position since position does not depend on order\n"
+ << "void main (void)\n"
+ << "{\n";
+
+ if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
+ src << " // Cover only a small area in a corner. The area will be expanded in geometry shader to cover whole viewport\n"
+ << " gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
+ else
+ src << " // Fill the whole viewport\n"
+ << " gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
+
+ src << " // Calculate position in tessellation grid\n"
+ << " v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << m_tessGenLevel << ")));\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Geometry shader
+ {
+ const int numInvocations = m_numGeometryInvocations;
+ const int numPrimitives = m_numGeometryPrimitivesPerInvocation;
+
+ std::ostringstream src;
+
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_geometry_shader : require\n"
+ << "layout(triangles, invocations = " << numInvocations << ") in;\n"
+ << "layout(triangle_strip, max_vertices = " << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in mediump ivec2 v_tessellationGridPosition[];\n"
+ << "layout(location = 0) flat out highp vec4 v_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " const float equalThreshold = 0.001;\n"
+ << " const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce gaps. Fill potential gaps by enlarging the output slice a little.\n"
+ << "\n"
+ << " // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
+ << " // Original rectangle can be found by finding the bounding AABB of the triangle\n"
+ << " vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
+ << " min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
+ << " max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
+ << " max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
+ << "\n"
+ << " // Location in tessellation grid\n"
+ << " ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n"
+ << "\n"
+ << " // Which triangle of the two that split the grid cell\n"
+ << " int numVerticesOnBottomEdge = 0;\n"
+ << " for (int ndx = 0; ndx < 3; ++ndx)\n"
+ << " if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
+ << " ++numVerticesOnBottomEdge;\n"
+ << " bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
+ << "\n";
+
+ if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
+ {
+ // scatter primitives
+ src << " // Draw grid cells\n"
+ << " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
+ << " for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
+ << " {\n"
+ << " ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", 2 * " << m_tessGenLevel << " * " << numInvocations << ");\n"
+ << " ivec2 dstGridNdx = ivec2(" << m_tessGenLevel << " * ndx + gridPosition.x, " << m_tessGenLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
+ << " vec4 dstArea;\n"
+ << " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
+ << " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
+ << " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
+ << " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
+ << "\n"
+ << " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
+ << " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
+ << " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " EmitVertex();\n"
+ << " EndPrimitive();\n"
+ << " }\n";
+ }
+ else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
+ {
+ // Number of subrectangle instances = num layers
+ DE_ASSERT(m_numLayers == numInvocations * 2);
+
+ src << " // Draw grid cells, send each primitive to a separate layer\n"
+ << " int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
+ << " for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
+ << " {\n"
+ << " ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", " << m_tessGenLevel << ");\n"
+ << " ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
+ << " vec4 dstArea;\n"
+ << " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
+ << " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
+ << " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
+ << " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
+ << "\n"
+ << " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
+ << " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
+ << " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
+ << " EmitVertex();\n"
+ << " EndPrimitive();\n"
+ << " }\n";
+ }
+ else
+ {
+ if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
+ {
+ src << " // Scatter slices\n"
+ << " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
+ << " ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInvocations*2) << " + inputTriangleNdx);\n"
+ << " ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2(" << m_tessGenLevel << ", " << m_tessGenLevel << " * " << (numInvocations*2) << ");\n"
+ << "\n"
+ << " // Draw slice to the dstSlice slot\n"
+ << " vec4 outputSliceArea;\n"
+ << " outputSliceArea.x = float(dstSliceNdx.x) / float(" << m_tessGenLevel << ") * 2.0 - 1.0 - gapOffset;\n"
+ << " outputSliceArea.y = float(dstSliceNdx.y) / float(" << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 - gapOffset;\n"
+ << " outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << m_tessGenLevel << ") * 2.0 - 1.0 + gapOffset;\n"
+ << " outputSliceArea.w = float(dstSliceNdx.y+1) / float(" << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
+ }
+ else
+ {
+ src << " // Fill the input area with slices\n"
+ << " // Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
+ << " float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
+ << " // Each slice is a invocation\n"
+ << " float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInvocations << ");\n"
+ << " float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
+ << "\n"
+ << " vec4 outputSliceArea;\n"
+ << " outputSliceArea.x = aabb.x - gapOffset;\n"
+ << " outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
+ << " outputSliceArea.z = aabb.z + gapOffset;\n"
+ << " outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
+ }
+
+ src << "\n"
+ << " // Draw slice\n"
+ << " for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n"
+ << " {\n"
+ << " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
+ << " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
+ << " vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
+ << " float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n"
+ << "\n"
+ << " gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
+ << " v_color = outputColor;\n"
+ << " EmitVertex();\n"
+ << " }\n";
+ }
+
+ src << "}\n";
+
+ programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
+ }
+}
+
+class GridRenderTestInstance : public TestInstance
+{
+public:
+ struct Params
+ {
+ Flags flags;
+ int numLayers;
+
+ Params (void) : flags(), numLayers() {}
+ };
+ GridRenderTestInstance (Context& context, const Params& params) : TestInstance(context), m_params(params) {}
+ tcu::TestStatus iterate (void);
+
+private:
+ Params m_params;
+};
+
+TestInstance* GridRenderTestCase::createInstance (Context& context) const
+{
+ GridRenderTestInstance::Params params;
+
+ params.flags = m_flags;
+ params.numLayers = m_numLayers;
+
+ return new GridRenderTestInstance(context, params);
+}
+
+bool verifyResultLayer (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& image, const int layerNdx)
+{
+ tcu::Surface errorMask (image.getWidth(), image.getHeight());
+ bool foundError = false;
+
+ tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
+
+ log << tcu::TestLog::Message << "Verifying output layer " << layerNdx << tcu::TestLog::EndMessage;
+
+ for (int y = 0; y < image.getHeight(); ++y)
+ for (int x = 0; x < image.getWidth(); ++x)
+ {
+ const int threshold = 8;
+ const tcu::RGBA color (image.getPixel(x, y));
+
+ // Color must be a linear combination of green and yellow
+ if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
+ {
+ errorMask.setPixel(x, y, tcu::RGBA::red());
+ foundError = true;
+ }
+ }
+
+ if (!foundError)
+ {
+ log << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
+ << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
+ << tcu::TestLog::Image("Result", "Rendered result", image)
+ << tcu::TestLog::EndImageSet;
+ return true;
+ }
+ else
+ {
+ log << tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage
+ << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
+ << tcu::TestLog::Image("Result", "Rendered result", image)
+ << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
+ << tcu::TestLog::EndImageSet;
+ return false;
+ }
+}
+
+tcu::TestStatus GridRenderTestInstance::iterate (void)
+{
+ requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER);
+
+ m_context.getTestContext().getLog()
+ << tcu::TestLog::Message
+ << "Rendering single point at the origin. Expecting yellow and green colored grid-like image. (High-frequency grid may appear unicolored)."
+ << tcu::TestLog::EndMessage;
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = m_context.getDefaultAllocator();
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageAllLayersRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
+ const VkImageCreateInfo colorImageCreateInfo = makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, m_params.numLayers);
+ const VkImageViewType colorAttachmentViewType = (m_params.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY);
+ const Image colorAttachmentImage (vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification (big enough for all layers).
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * m_params.numLayers * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Pipeline: no vertex input attributes nor descriptors.
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, colorAttachmentViewType, colorFormat, colorImageAllLayersRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), m_params.numLayers));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline (GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get("geom"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageAllLayersRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+
+ vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageAllLayersRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkImageSubresourceLayers subresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), subresourceLayers);
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ // Verify results
+ {
+ const Allocation& alloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
+
+ const tcu::ConstPixelBufferAccess imageAllLayers(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), m_params.numLayers, alloc.getHostPtr());
+
+ bool allOk = true;
+ for (int ndx = 0; ndx < m_params.numLayers; ++ndx)
+ allOk = allOk && verifyResultLayer(m_context.getTestContext().getLog(),
+ tcu::getSubregion(imageAllLayers, 0, 0, ndx, renderSize.x(), renderSize.y(), 1),
+ ndx);
+
+ return (allOk ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
+ }
+}
+
+struct TestCaseDescription
+{
+ const char* name;
+ const char* desc;
+ Flags flags;
+};
+
+} // anonymous
+
+//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.limits.*
+//! \note Tests that check implementation defined limits were omitted, because they rely on runtime shader source generation
+//! (e.g. changing the number of vertices output from geometry shader). CTS currently doesn't support that,
+//! because some platforms require precompiled shaders.
+tcu::TestCaseGroup* createGeometryGridRenderLimitsTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "limits", "Render with properties near their limits"));
+
+ static const TestCaseDescription cases[] =
+ {
+ {
+ "output_required_max_tessellation",
+ "Minimum maximum tessellation level",
+ FLAG_TESSELLATION_MAX_SPEC
+ },
+ {
+ "output_required_max_geometry",
+ "Output minimum maximum number of vertices the geometry shader",
+ FLAG_GEOMETRY_MAX_SPEC
+ },
+ {
+ "output_required_max_invocations",
+ "Minimum maximum number of geometry shader invocations",
+ FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
+ },
+ };
+
+ for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
+ group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
+
+ return group.release();
+}
+
+//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.scatter.*
+tcu::TestCaseGroup* createGeometryGridRenderScatterTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "scatter", "Scatter output primitives"));
+
+ static const TestCaseDescription cases[] =
+ {
+ {
+ "geometry_scatter_instances",
+ "Each geometry shader instance outputs its primitives far from other instances of the same execution",
+ FLAG_GEOMETRY_SCATTER_INSTANCES
+ },
+ {
+ "geometry_scatter_primitives",
+ "Each geometry shader instance outputs its primitives far from other primitives of the same instance",
+ FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SEPARATE_PRIMITIVES
+ },
+ {
+ "geometry_scatter_layers",
+ "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance",
+ FLAG_GEOMETRY_SCATTER_LAYERS | FLAG_GEOMETRY_SEPARATE_PRIMITIVES
+ },
+ };
+
+ for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
+ group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONGEOMETRYGRIDRENDERTESTS_HPP
+#define _VKTTESSELLATIONGEOMETRYGRIDRENDERTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 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 Tessellation Geometry Interaction - Grid render (limits, scatter)
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createGeometryGridRenderLimitsTests (tcu::TestContext& testCtx);
+tcu::TestCaseGroup* createGeometryGridRenderScatterTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONGEOMETRYGRIDRENDERTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 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 Tessellation Geometry Interaction - Passthrough
+*//*--------------------------------------------------------------------*/
+
+#include "vktTessellationGeometryPassthroughTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageCompare.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkImageUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+void addVertexAndFragmentShaders (vk::SourceCollections& programCollection)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 a_position;\n"
+ << "layout(location = 0) out highp vec4 v_vertex_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_Position = a_position;\n"
+ << " v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in mediump vec4 v_fragment_color;\n"
+ << "layout(location = 0) out mediump vec4 fragColor;\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " fragColor = v_fragment_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+//! Tessellation evaluation shader used in passthrough geometry shader case.
+std::string generateTessellationEvaluationShader (const TessPrimitiveType primitiveType, const std::string& colorOutputName)
+{
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "layout(" << getTessPrimitiveTypeShaderName(primitiveType) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 v_patch_color[];\n"
+ << "layout(location = 0) out highp vec4 " << colorOutputName << ";\n"
+ << "\n"
+ << "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
+ << "void main (void)\n"
+ << "{\n";
+
+ if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ src << " vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, 1.3));\n"
+ << " vec3 cweights = gl_TessCoord;\n"
+ << " gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + weights.z * gl_in[2].gl_Position.xyz, 1.0);\n"
+ << " " << colorOutputName << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n";
+ else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+ src << " vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n"
+ << " vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n"
+ << " vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n"
+ << " vec2 cweights = gl_TessCoord.xy;\n"
+ << " gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n"
+ << " " << colorOutputName << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], cweights.y), cweights.x);\n";
+ else
+ DE_ASSERT(false);
+
+ src << "}\n";
+
+ return src.str();
+}
+
+class IdentityGeometryShaderTestCase : public TestCase
+{
+public:
+ void initPrograms (vk::SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+ IdentityGeometryShaderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType)
+ : TestCase (testCtx, name, description)
+ , m_primitiveType (primitiveType)
+ {
+ }
+
+private:
+ const TessPrimitiveType m_primitiveType;
+};
+
+void IdentityGeometryShaderTestCase::initPrograms (vk::SourceCollections& programCollection) const
+{
+ addVertexAndFragmentShaders(programCollection);
+
+ // Tessellation control
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "layout(vertices = 4) out;\n"
+ << "\n"
+ << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
+ << " float inner0;\n"
+ << " float inner1;\n"
+ << " float outer0;\n"
+ << " float outer1;\n"
+ << " float outer2;\n"
+ << " float outer3;\n"
+ << "} sb_levels;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 v_vertex_color[];\n"
+ << "layout(location = 0) out highp vec4 v_patch_color[];\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
+ << " v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = sb_levels.inner0;\n"
+ << " gl_TessLevelInner[1] = sb_levels.inner1;\n"
+ << " gl_TessLevelOuter[0] = sb_levels.outer0;\n"
+ << " gl_TessLevelOuter[1] = sb_levels.outer1;\n"
+ << " gl_TessLevelOuter[2] = sb_levels.outer2;\n"
+ << " gl_TessLevelOuter[3] = sb_levels.outer3;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ programCollection.glslSources.add("tese_to_frag")
+ << glu::TessellationEvaluationSource(generateTessellationEvaluationShader(m_primitiveType, "v_fragment_color"));
+ programCollection.glslSources.add("tese_to_geom")
+ << glu::TessellationEvaluationSource(generateTessellationEvaluationShader(m_primitiveType, "v_evaluated_color"));
+ }
+
+ // Geometry shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_geometry_shader : require\n"
+ << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(m_primitiveType, false) << ") in;\n"
+ << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(m_primitiveType, false)
+ << ", max_vertices=" << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 v_evaluated_color[];\n"
+ << "layout(location = 0) out highp vec4 v_fragment_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
+ << " {\n"
+ << " gl_Position = gl_in[ndx].gl_Position;\n"
+ << " v_fragment_color = v_evaluated_color[ndx];\n"
+ << " EmitVertex();\n"
+ << " }\n"
+ << "}\n";
+
+ programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
+ }
+}
+
+class IdentityTessellationShaderTestCase : public TestCase
+{
+public:
+ void initPrograms (vk::SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+ IdentityTessellationShaderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType)
+ : TestCase (testCtx, name, description)
+ , m_primitiveType (primitiveType)
+ {
+ }
+
+private:
+ const TessPrimitiveType m_primitiveType;
+};
+
+//! Geometry shader used in passthrough tessellation shader case.
+std::string generateGeometryShader (const TessPrimitiveType primitiveType, const std::string& colorSourceName)
+{
+ const int numEmitVertices = (primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 11 : 8);
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_geometry_shader : require\n"
+ << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, false) << ") in;\n"
+ << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, false)
+ << ", max_vertices=" << numEmitVertices << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 " << colorSourceName << "[];\n"
+ << "layout(location = 0) out highp vec4 v_fragment_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n";
+
+ if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ src << " vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n"
+ << "\n"
+ << " for (int ndx = 0; ndx < 4; ++ndx)\n"
+ << " {\n"
+ << " gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n"
+ << " v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
+ << " EmitVertex();\n"
+ << "\n"
+ << " gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n"
+ << " v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
+ << " EmitVertex();\n"
+ << " }\n";
+ }
+ else if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+ {
+ src << " vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - gl_in[0].gl_Position.x, 0.0, 0.0);\n"
+ << " for (int i = 0; i <= 10; ++i)\n"
+ << " {\n"
+ << " float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n"
+ << " float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n"
+ << " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n"
+ << " v_fragment_color = mix(" << colorSourceName << "[0], " << colorSourceName << "[1], xweight);\n"
+ << " EmitVertex();\n"
+ << " }\n";
+ }
+ else
+ DE_ASSERT(false);
+
+ src << "}\n";
+
+ return src.str();
+}
+
+void IdentityTessellationShaderTestCase::initPrograms (vk::SourceCollections& programCollection) const
+{
+ addVertexAndFragmentShaders(programCollection);
+
+ // Tessellation control
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "layout(vertices = " << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 v_vertex_color[];\n"
+ << "layout(location = 0) out highp vec4 v_control_color[];\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
+ << " v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\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";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 v_control_color[];\n"
+ << "layout(location = 0) out highp vec4 v_evaluated_color;\n"
+ << "\n"
+ << "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
+ << "void main (void)\n"
+ << "{\n";
+
+ if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ src << " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n"
+ << " v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + gl_TessCoord.z * v_control_color[2];\n";
+ else if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+ src << " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
+ << " v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n";
+ else
+ DE_ASSERT(false);
+
+ src << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Geometry shader
+ {
+ programCollection.glslSources.add("geom_from_tese") << glu::GeometrySource(
+ generateGeometryShader(m_primitiveType, "v_evaluated_color"));
+ programCollection.glslSources.add("geom_from_vert") << glu::GeometrySource(
+ generateGeometryShader(m_primitiveType, "v_vertex_color"));
+ }
+}
+
+inline tcu::ConstPixelBufferAccess getPixelBufferAccess (const DeviceInterface& vk,
+ const VkDevice device,
+ const Buffer& colorBuffer,
+ const VkFormat colorFormat,
+ const VkDeviceSize colorBufferSizeBytes,
+ const tcu::IVec2& renderSize)
+{
+ const Allocation& alloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
+ return tcu::ConstPixelBufferAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr());
+}
+
+//! When a test case disables tessellation stage and we need to derive a primitive type.
+VkPrimitiveTopology getPrimitiveTopology (const TessPrimitiveType primitiveType)
+{
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ case TESSPRIMITIVETYPE_QUADS:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ case TESSPRIMITIVETYPE_ISOLINES:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
+
+ default:
+ DE_ASSERT(false);
+ return VK_PRIMITIVE_TOPOLOGY_LAST;
+ }
+}
+
+enum Constants
+{
+ PIPELINE_CASES = 2,
+ RENDER_SIZE = 256,
+};
+
+class PassthroughTestInstance : public TestInstance
+{
+public:
+ struct PipelineDescription
+ {
+ bool useTessellation;
+ bool useGeometry;
+ std::string tessEvalShaderName;
+ std::string geomShaderName;
+ std::string description;
+
+ PipelineDescription (void) : useTessellation(), useGeometry() {}
+ };
+
+ struct Params
+ {
+ bool useTessLevels;
+ TessLevels tessLevels;
+ TessPrimitiveType primitiveType;
+ int inputPatchVertices;
+ std::vector<tcu::Vec4> vertices;
+ PipelineDescription pipelineCases[PIPELINE_CASES]; //!< Each test case renders with two pipelines and compares results
+ std::string message;
+
+ Params (void) : useTessLevels(), tessLevels(), primitiveType(), inputPatchVertices() {}
+ };
+
+ PassthroughTestInstance (Context& context, const Params& params) : TestInstance(context), m_params(params) {}
+ tcu::TestStatus iterate (void);
+
+private:
+ const Params m_params;
+};
+
+tcu::TestStatus PassthroughTestInstance::iterate (void)
+{
+ requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER);
+ DE_STATIC_ASSERT(PIPELINE_CASES == 2);
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = m_context.getDefaultAllocator();
+
+ // Tessellation levels
+ const Buffer tessLevelsBuffer (vk, device, allocator, makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ if (m_params.useTessLevels)
+ {
+ const Allocation& alloc = tessLevelsBuffer.getAllocation();
+ TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
+ *bufferTessLevels = m_params.tessLevels;
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+ }
+
+ // Vertex attributes
+
+ const VkDeviceSize vertexDataSizeBytes = sizeInBytes(m_params.vertices);
+ const VkFormat vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
+ const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), &m_params.vertices[0], static_cast<std::size_t>(vertexDataSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+ }
+
+ // Descriptors - make descriptor for tessellation levels, even if we don't use them, to simplify code
+
+ const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+ .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
+ .build(vk, device));
+
+ const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+ .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+ const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+ const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(*tessLevelsBuffer, 0ull, sizeof(TessLevels));
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
+ .update(vk, device);
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification.
+ // We use two buffers, one for each case.
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer1 (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+ const Buffer colorBuffer2 (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+ const Buffer* const colorBuffer[PIPELINE_CASES] = { &colorBuffer1, &colorBuffer2 };
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout (vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ // Message explaining the test
+ {
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ log << tcu::TestLog::Message << m_params.message << tcu::TestLog::EndMessage;
+
+ if (m_params.useTessLevels)
+ log << tcu::TestLog::Message << "Tessellation levels: " << getTessellationLevelsString(m_params.tessLevels, m_params.primitiveType) << tcu::TestLog::EndMessage;
+ }
+
+ for (int pipelineNdx = 0; pipelineNdx < PIPELINE_CASES; ++pipelineNdx)
+ {
+ const PipelineDescription& pipelineDescription = m_params.pipelineCases[pipelineNdx];
+ GraphicsPipelineBuilder pipelineBuilder;
+
+ pipelineBuilder
+ .setPrimitiveTopology (getPrimitiveTopology(m_params.primitiveType))
+ .setRenderSize (renderSize)
+ .setBlend (true)
+ .setVertexInputSingleAttribute(vertexFormat, tcu::getPixelSize(mapVkFormat(vertexFormat)))
+ .setPatchControlPoints (m_params.inputPatchVertices)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL);
+
+ if (pipelineDescription.useTessellation)
+ pipelineBuilder
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(pipelineDescription.tessEvalShaderName), DE_NULL);
+
+ if (pipelineDescription.useGeometry)
+ pipelineBuilder
+ .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get(pipelineDescription.geomShaderName), DE_NULL);
+
+ const Unique<VkPipeline> pipeline (pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
+
+ // Draw commands
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ // State is slightly different on the first iteration.
+ const VkImageLayout currentLayout = (pipelineNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+ const VkAccessFlags srcFlags = (pipelineNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
+
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ if (m_params.useTessLevels)
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+
+ vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(m_params.vertices.size()), 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer[pipelineNdx]->get(), 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, colorBuffer[pipelineNdx]->get(), 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+ }
+
+ // Verify results
+
+ tcu::ConstPixelBufferAccess image0 = getPixelBufferAccess(vk, device, *colorBuffer[0], colorFormat, colorBufferSizeBytes, renderSize);
+ tcu::ConstPixelBufferAccess image1 = getPixelBufferAccess(vk, device, *colorBuffer[1], colorFormat, colorBufferSizeBytes, renderSize);
+
+ const tcu::UVec4 colorThreshold (8, 8, 8, 255);
+ const tcu::IVec3 positionDeviation (1, 1, 0); // 3x3 search kernel
+ const bool ignoreOutOfBounds = true;
+
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ log << tcu::TestLog::Message
+ << "In image comparison:\n"
+ << " Reference - " << m_params.pipelineCases[0].description << "\n"
+ << " Result - " << m_params.pipelineCases[1].description << "\n"
+ << tcu::TestLog::EndMessage;
+
+ const bool ok = tcu::intThresholdPositionDeviationCompare(
+ log, "ImageCompare", "Image comparison", image0, image1, colorThreshold, positionDeviation, ignoreOutOfBounds, tcu::COMPARE_LOG_RESULT);
+
+ return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
+}
+
+TestInstance* IdentityGeometryShaderTestCase::createInstance (Context& context) const
+{
+ PassthroughTestInstance::Params params;
+
+ const float level = 14.0;
+ params.useTessLevels = true;
+ params.tessLevels.inner[0] = level;
+ params.tessLevels.inner[1] = level;
+ params.tessLevels.outer[0] = level;
+ params.tessLevels.outer[1] = level;
+ params.tessLevels.outer[2] = level;
+ params.tessLevels.outer[3] = level;
+
+ params.primitiveType = m_primitiveType;
+ params.inputPatchVertices = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+ params.vertices.push_back(tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f ));
+ params.vertices.push_back(tcu::Vec4( -0.9f, 0.9f, 0.0f, 1.0f ));
+ params.vertices.push_back(tcu::Vec4( 0.9f, -0.9f, 0.0f, 1.0f ));
+ params.vertices.push_back(tcu::Vec4( 0.9f, 0.9f, 0.0f, 1.0f ));
+
+ params.pipelineCases[0].useTessellation = true;
+ params.pipelineCases[0].useGeometry = true;
+ params.pipelineCases[0].tessEvalShaderName = "tese_to_geom";
+ params.pipelineCases[0].geomShaderName = "geom";
+ params.pipelineCases[0].description = "passthrough geometry shader";
+
+ params.pipelineCases[1].useTessellation = true;
+ params.pipelineCases[1].useGeometry = false;
+ params.pipelineCases[1].tessEvalShaderName = "tese_to_frag";
+ params.pipelineCases[1].geomShaderName = "geom";
+ params.pipelineCases[1].description = "no geometry shader in the pipeline";
+
+ params.message = "Testing tessellating shader program output does not change when a passthrough geometry shader is attached.\n"
+ "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n"
+ "Using additive blending to detect overlap.\n";
+
+ return new PassthroughTestInstance(context, params);
+};
+
+TestInstance* IdentityTessellationShaderTestCase::createInstance (Context& context) const
+{
+ PassthroughTestInstance::Params params;
+
+ params.useTessLevels = false;
+ params.primitiveType = m_primitiveType;
+ params.inputPatchVertices = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
+
+ params.vertices.push_back( tcu::Vec4( -0.4f, 0.4f, 0.0f, 1.0f ));
+ params.vertices.push_back( tcu::Vec4( 0.0f, -0.5f, 0.0f, 1.0f ));
+ if (params.inputPatchVertices == 3)
+ params.vertices.push_back(tcu::Vec4( 0.4f, 0.4f, 0.0f, 1.0f ));
+
+ params.pipelineCases[0].useTessellation = true;
+ params.pipelineCases[0].useGeometry = true;
+ params.pipelineCases[0].tessEvalShaderName = "tese";
+ params.pipelineCases[0].geomShaderName = "geom_from_tese";
+ params.pipelineCases[0].description = "passthrough tessellation shaders";
+
+ params.pipelineCases[1].useTessellation = false;
+ params.pipelineCases[1].useGeometry = true;
+ params.pipelineCases[1].tessEvalShaderName = "tese";
+ params.pipelineCases[1].geomShaderName = "geom_from_vert";
+ params.pipelineCases[1].description = "no tessellation shaders in the pipeline";
+
+ params.message = "Testing geometry shading shader program output does not change when a passthrough tessellation shader is attached.\n"
+ "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n"
+ "Using additive blending to detect overlap.\n";
+
+ return new PassthroughTestInstance(context, params);
+};
+
+inline TestCase* makeIdentityGeometryShaderCase (tcu::TestContext& testCtx, const TessPrimitiveType primitiveType)
+{
+ return new IdentityGeometryShaderTestCase(
+ testCtx,
+ "tessellate_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_passthrough_geometry_no_change",
+ "Passthrough geometry shader has no effect",
+ primitiveType);
+}
+
+inline TestCase* makeIdentityTessellationShaderCase (tcu::TestContext& testCtx, const TessPrimitiveType primitiveType)
+{
+ return new IdentityTessellationShaderTestCase(
+ testCtx,
+ "passthrough_tessellation_geometry_shade_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_no_change",
+ "Passthrough tessellation shader has no effect",
+ primitiveType);
+}
+
+} // anonymous
+
+
+//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.passthrough.*
+tcu::TestCaseGroup* createGeometryPassthroughTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "passthrough", "Render various types with either passthrough geometry or tessellation shader"));
+
+ // Passthrough geometry shader
+ group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES));
+ group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_QUADS));
+ group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES));
+
+ // Passthrough tessellation shader
+ group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES));
+ group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES));
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONGEOMETRYPASSTHROUGHTESTS_HPP
+#define _VKTTESSELLATIONGEOMETRYPASSTHROUGHTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 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 Tessellation Geometry Interaction - Passthrough
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createGeometryPassthroughTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONGEOMETRYPASSTHROUGHTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 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 Tessellation Geometry Interaction - Point Size
+*//*--------------------------------------------------------------------*/
+
+#include "vktTessellationGeometryPassthroughTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkImageUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+ RENDER_SIZE = 32,
+};
+
+enum FlagBits
+{
+ FLAG_VERTEX_SET = 1u << 0, // !< set gl_PointSize in vertex shader
+ FLAG_TESSELLATION_CONTROL_SET = 1u << 1, // !< set gl_PointSize in tessellation evaluation shader
+ FLAG_TESSELLATION_EVALUATION_SET = 1u << 2, // !< set gl_PointSize in tessellation control shader
+ FLAG_TESSELLATION_ADD = 1u << 3, // !< read and add to gl_PointSize in tessellation shader pair
+ FLAG_TESSELLATION_DONT_SET = 1u << 4, // !< don't set gl_PointSize in tessellation shader
+ FLAG_GEOMETRY_SET = 1u << 5, // !< set gl_PointSize in geometry shader
+ FLAG_GEOMETRY_ADD = 1u << 6, // !< read and add to gl_PointSize in geometry shader
+ FLAG_GEOMETRY_DONT_SET = 1u << 7, // !< don't set gl_PointSize in geometry shader
+};
+typedef deUint32 Flags;
+
+void checkPointSizeRequirements (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const int maxPointSize)
+{
+ const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice);
+ if (maxPointSize > static_cast<int>(properties.limits.pointSizeRange[1]))
+ throw tcu::NotSupportedError("Test requires point size " + de::toString(maxPointSize));
+ // Point size granularity must be 1.0 at most, so no need to check it for this test.
+}
+
+int getExpectedPointSize (const Flags flags)
+{
+ int addition = 0;
+
+ // geometry
+ if (flags & FLAG_GEOMETRY_DONT_SET)
+ return 1;
+ else if (flags & FLAG_GEOMETRY_SET)
+ return 6;
+ else if (flags & FLAG_GEOMETRY_ADD)
+ addition += 2;
+
+ // tessellation
+ if (flags & FLAG_TESSELLATION_EVALUATION_SET)
+ return 4 + addition;
+ else if (flags & FLAG_TESSELLATION_ADD)
+ addition += 2;
+ else if (flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_DONT_SET))
+ {
+ DE_ASSERT((flags & FLAG_GEOMETRY_ADD) == 0); // reading pointSize undefined
+ return 1;
+ }
+
+ // vertex
+ if (flags & FLAG_VERTEX_SET)
+ return 2 + addition;
+
+ // undefined
+ DE_ASSERT(false);
+ return -1;
+}
+
+inline bool isTessellationStage (const Flags flags)
+{
+ return (flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) != 0;
+}
+
+inline bool isGeometryStage (const Flags flags)
+{
+ return (flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET)) != 0;
+}
+
+bool verifyImage (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image, const int expectedSize)
+{
+ log << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels." << tcu::TestLog::EndMessage;
+
+ bool resultAreaFound = false;
+ tcu::IVec4 resultArea;
+ const tcu::Vec4 black(0.0, 0.0, 0.0, 1.0);
+
+ // Find rasterization output area
+
+ for (int y = 0; y < image.getHeight(); ++y)
+ for (int x = 0; x < image.getWidth(); ++x)
+ if (image.getPixel(x, y) != black)
+ {
+ if (!resultAreaFound)
+ {
+ // first fragment
+ resultArea = tcu::IVec4(x, y, x + 1, y + 1);
+ resultAreaFound = true;
+ }
+ else
+ {
+ // union area
+ resultArea.x() = de::min(resultArea.x(), x);
+ resultArea.y() = de::min(resultArea.y(), y);
+ resultArea.z() = de::max(resultArea.z(), x+1);
+ resultArea.w() = de::max(resultArea.w(), y+1);
+ }
+ }
+
+ if (!resultAreaFound)
+ {
+ log << tcu::TestLog::Message << "Verification failed, could not find any point fragments." << tcu::TestLog::EndMessage;
+ return false;
+ }
+
+ const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1);
+
+ if (pointSize.x() != pointSize.y())
+ {
+ log << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize << tcu::TestLog::EndMessage;
+ return false;
+ }
+
+ if (pointSize.x() != expectedSize)
+ {
+ log << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << pointSize.x() << tcu::TestLog::EndMessage;
+ return false;
+ }
+
+ return true;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const Flags flags)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n";
+
+ if (flags & FLAG_VERTEX_SET)
+ src << " gl_PointSize = 2.0;\n";
+
+ src << "}\n";
+
+ programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "layout(location = 0) out mediump vec4 fragColor;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " fragColor = vec4(1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+
+ if (isTessellationStage(flags))
+ {
+ // Tessellation control shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << ((flags & FLAG_TESSELLATION_DONT_SET) ? "" : "#extension GL_EXT_tessellation_point_size : require\n")
+ << "layout(vertices = 1) out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_TessLevelOuter[0] = 3.0;\n"
+ << " gl_TessLevelOuter[1] = 3.0;\n"
+ << " gl_TessLevelOuter[2] = 3.0;\n"
+ << " gl_TessLevelInner[0] = 3.0;\n"
+ << "\n"
+ << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
+
+ if (flags & FLAG_TESSELLATION_ADD)
+ src << " // pass as is to eval\n"
+ << " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
+ else if (flags & FLAG_TESSELLATION_CONTROL_SET)
+ src << " // thrown away\n"
+ << " gl_out[gl_InvocationID].gl_PointSize = 4.0;\n";
+
+ src << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << (flags & FLAG_TESSELLATION_DONT_SET ? "" : "#extension GL_EXT_tessellation_point_size : require\n")
+ << "layout(triangles, point_mode) in;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " // hide all but one vertex\n"
+ << " if (gl_TessCoord.x < 0.99)\n"
+ << " gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
+ << " else\n"
+ << " gl_Position = gl_in[0].gl_Position;\n";
+
+ if (flags & FLAG_TESSELLATION_ADD)
+ src << "\n"
+ << " // add to point size\n"
+ << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
+ else if (flags & FLAG_TESSELLATION_EVALUATION_SET)
+ src << "\n"
+ << " // set point size\n"
+ << " gl_PointSize = 4.0;\n";
+
+ src << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+ }
+
+ if (isGeometryStage(flags))
+ {
+ // Geometry shader
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_geometry_shader : require\n"
+ << (flags & FLAG_GEOMETRY_DONT_SET ? "" : "#extension GL_EXT_geometry_point_size : require\n")
+ << "layout(points) in;\n"
+ << "layout(points, max_vertices = 1) out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n";
+
+ if (flags & FLAG_GEOMETRY_SET)
+ src << " gl_Position = gl_in[0].gl_Position;\n"
+ << " gl_PointSize = 6.0;\n";
+ else if (flags & FLAG_GEOMETRY_ADD)
+ src << " gl_Position = gl_in[0].gl_Position;\n"
+ << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
+ else if (flags & FLAG_GEOMETRY_DONT_SET)
+ src << " gl_Position = gl_in[0].gl_Position;\n";
+
+ src << "\n"
+ << " EmitVertex();\n"
+ << "}\n";
+
+ programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
+ }
+}
+
+tcu::TestStatus test (Context& context, const Flags flags)
+{
+ const int expectedPointSize = getExpectedPointSize(flags);
+ {
+ const InstanceInterface& vki = context.getInstanceInterface();
+ const VkPhysicalDevice physDevice = context.getPhysicalDevice();
+
+ requireFeatures (vki, physDevice, FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE);
+ checkPointSizeRequirements(vki, physDevice, expectedPointSize);
+ }
+ {
+ tcu::TestLog& log = context.getTestContext().getLog();
+
+ if (flags & FLAG_VERTEX_SET)
+ log << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_TESSELLATION_CONTROL_SET)
+ log << tcu::TestLog::Message << "Setting point size in tessellation control shader to 4.0. (And ignoring it in evaluation)." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_TESSELLATION_EVALUATION_SET)
+ log << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_TESSELLATION_ADD)
+ log << tcu::TestLog::Message << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_TESSELLATION_DONT_SET)
+ log << tcu::TestLog::Message << "Not setting point size in tessellation evaluation shader (resulting in the default point size)." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_GEOMETRY_SET)
+ log << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_GEOMETRY_ADD)
+ log << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0." << tcu::TestLog::EndMessage;
+ if (flags & FLAG_GEOMETRY_DONT_SET)
+ log << tcu::TestLog::Message << "Not setting point size in geometry shader (resulting in the default point size)." << tcu::TestLog::EndMessage;
+ }
+
+ const DeviceInterface& vk = context.getDeviceInterface();
+ const VkDevice device = context.getDevice();
+ const VkQueue queue = context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = context.getDefaultAllocator();
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ GraphicsPipelineBuilder pipelineBuilder;
+
+ pipelineBuilder
+ .setPrimitiveTopology (VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
+ .setRenderSize (renderSize)
+ .setPatchControlPoints (1)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL);
+
+ if (isTessellationStage(flags))
+ pipelineBuilder
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL);
+
+ if (isGeometryStage(flags))
+ pipelineBuilder
+ .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom"), DE_NULL);
+
+ const Unique<VkPipeline> pipeline(pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
+
+ // Draw commands
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ {
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+
+ vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ // Verify results
+ {
+ const Allocation& alloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
+ tcu::ConstPixelBufferAccess image(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr());
+
+ tcu::TestLog& log = context.getTestContext().getLog();
+ log << tcu::LogImage("color0", "", image);
+
+ if (verifyImage(log, image, expectedPointSize))
+ return tcu::TestStatus::pass("OK");
+ else
+ return tcu::TestStatus::fail("Didn't render expected point");
+ }
+}
+
+std::string getTestCaseName (const Flags flags)
+{
+ std::ostringstream buf;
+
+ // join per-bit descriptions into a single string with '_' separator
+ if (flags & FLAG_VERTEX_SET) buf << "vertex_set";
+ if (flags & FLAG_TESSELLATION_CONTROL_SET) buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET-1)) ? ("_") : ("")) << "control_set";
+ if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? ("_") : ("")) << "evaluation_set";
+ if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? ("_") : ("")) << "control_pass_eval_add";
+ if (flags & FLAG_TESSELLATION_DONT_SET) buf << ((flags & (FLAG_TESSELLATION_DONT_SET-1)) ? ("_") : ("")) << "eval_default";
+ if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? ("_") : ("")) << "geometry_set";
+ if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? ("_") : ("")) << "geometry_add";
+ if (flags & FLAG_GEOMETRY_DONT_SET) buf << ((flags & (FLAG_GEOMETRY_DONT_SET-1)) ? ("_") : ("")) << "geometry_default";
+
+ return buf.str();
+}
+
+std::string getTestCaseDescription (const Flags flags)
+{
+ std::ostringstream buf;
+
+ // join per-bit descriptions into a single string with ", " separator
+ if (flags & FLAG_VERTEX_SET) buf << "set point size in vertex shader";
+ if (flags & FLAG_TESSELLATION_CONTROL_SET) buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET-1)) ? (", ") : ("")) << "set point size in tessellation control shader";
+ if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? (", ") : ("")) << "set point size in tessellation evaluation shader";
+ if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? (", ") : ("")) << "add to point size in tessellation shader";
+ if (flags & FLAG_TESSELLATION_DONT_SET) buf << ((flags & (FLAG_TESSELLATION_DONT_SET-1)) ? (", ") : ("")) << "don't set point size in tessellation evaluation shader";
+ if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? (", ") : ("")) << "set point size in geometry shader";
+ if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? (", ") : ("")) << "add to point size in geometry shader";
+ if (flags & FLAG_GEOMETRY_DONT_SET) buf << ((flags & (FLAG_GEOMETRY_DONT_SET-1)) ? (", ") : ("")) << "don't set point size in geometry shader";
+
+ return buf.str();
+}
+
+} // anonymous
+
+//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.point_size.*
+tcu::TestCaseGroup* createGeometryPointSizeTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "point_size", "Test point size"));
+
+ static const Flags caseFlags[] =
+ {
+ FLAG_VERTEX_SET,
+ FLAG_TESSELLATION_EVALUATION_SET,
+ FLAG_GEOMETRY_SET,
+ FLAG_VERTEX_SET | FLAG_TESSELLATION_CONTROL_SET,
+ FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET,
+ FLAG_VERTEX_SET | FLAG_TESSELLATION_DONT_SET,
+ FLAG_VERTEX_SET | FLAG_GEOMETRY_SET,
+ FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_GEOMETRY_SET,
+ FLAG_VERTEX_SET | FLAG_TESSELLATION_ADD | FLAG_GEOMETRY_ADD,
+ FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_GEOMETRY_DONT_SET,
+ };
+
+ for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx)
+ {
+ const std::string name = getTestCaseName (caseFlags[ndx]);
+ const std::string desc = getTestCaseDescription(caseFlags[ndx]);
+
+ addFunctionCaseWithPrograms(group.get(), name, desc, initPrograms, test, caseFlags[ndx]);
+ }
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONGEOMETRYPOINTSIZETESTS_HPP
+#define _VKTTESSELLATIONGEOMETRYPOINTSIZETESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 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 Tessellation Geometry Interaction - Point Size
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createGeometryPointSizeTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONGEOMETRYPOINTSIZETESTS_HPP
#include "vktTessellationPrimitiveDiscardTests.hpp"
#include "vktTessellationInvarianceTests.hpp"
#include "vktTessellationUserDefinedIO.hpp"
+#include "vktTessellationGeometryPassthroughTests.hpp"
+#include "vktTessellationGeometryPointSizeTests.hpp"
+#include "vktTessellationGeometryGridRenderTests.hpp"
+
+#include "deUniquePtr.hpp"
namespace vkt
{
namespace
{
+tcu::TestCaseGroup* createGeometryInteractionTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "geometry_interaction", "Tessellation and geometry shader interaction tests"));
+
+ group->addChild(createGeometryPassthroughTests (testCtx));
+ group->addChild(createGeometryGridRenderLimitsTests (testCtx));
+ group->addChild(createGeometryGridRenderScatterTests(testCtx));
+ group->addChild(createGeometryPointSizeTests (testCtx));
+
+ return group.release();
+}
+
void createChildren (tcu::TestCaseGroup* tessellationTests)
{
tcu::TestContext& testCtx = tessellationTests->getTestContext();
- tessellationTests->addChild(createLimitsTests (testCtx));
- tessellationTests->addChild(createCoordinatesTests (testCtx));
- tessellationTests->addChild(createWindingTests (testCtx));
- tessellationTests->addChild(createShaderInputOutputTests(testCtx));
- tessellationTests->addChild(createMiscDrawTests (testCtx));
- tessellationTests->addChild(createCommonEdgeTests (testCtx));
- tessellationTests->addChild(createFractionalSpacingTests(testCtx));
- tessellationTests->addChild(createPrimitiveDiscardTests (testCtx));
- tessellationTests->addChild(createInvarianceTests (testCtx));
- tessellationTests->addChild(createUserDefinedIOTests (testCtx));
+ tessellationTests->addChild(createLimitsTests (testCtx));
+ tessellationTests->addChild(createCoordinatesTests (testCtx));
+ tessellationTests->addChild(createWindingTests (testCtx));
+ tessellationTests->addChild(createShaderInputOutputTests (testCtx));
+ tessellationTests->addChild(createMiscDrawTests (testCtx));
+ tessellationTests->addChild(createCommonEdgeTests (testCtx));
+ tessellationTests->addChild(createFractionalSpacingTests (testCtx));
+ tessellationTests->addChild(createPrimitiveDiscardTests (testCtx));
+ tessellationTests->addChild(createInvarianceTests (testCtx));
+ tessellationTests->addChild(createUserDefinedIOTests (testCtx));
+ tessellationTests->addChild(createGeometryInteractionTests (testCtx));
}
} // anonymous
dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_ccw_point_mode
dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_cw
dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.geometry_interaction.passthrough.tessellate_triangles_passthrough_geometry_no_change
+dEQP-VK.tessellation.geometry_interaction.passthrough.tessellate_quads_passthrough_geometry_no_change
+dEQP-VK.tessellation.geometry_interaction.passthrough.tessellate_isolines_passthrough_geometry_no_change
+dEQP-VK.tessellation.geometry_interaction.passthrough.passthrough_tessellation_geometry_shade_triangles_no_change
+dEQP-VK.tessellation.geometry_interaction.passthrough.passthrough_tessellation_geometry_shade_isolines_no_change
+dEQP-VK.tessellation.geometry_interaction.limits.output_required_max_tessellation
+dEQP-VK.tessellation.geometry_interaction.limits.output_required_max_geometry
+dEQP-VK.tessellation.geometry_interaction.limits.output_required_max_invocations
+dEQP-VK.tessellation.geometry_interaction.scatter.geometry_scatter_instances
+dEQP-VK.tessellation.geometry_interaction.scatter.geometry_scatter_primitives
+dEQP-VK.tessellation.geometry_interaction.scatter.geometry_scatter_layers
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set
+dEQP-VK.tessellation.geometry_interaction.point_size.evaluation_set
+dEQP-VK.tessellation.geometry_interaction.point_size.geometry_set
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_control_set
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_evaluation_set
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_eval_default
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_geometry_set
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_evaluation_set_geometry_set
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_control_pass_eval_add_geometry_add
+dEQP-VK.tessellation.geometry_interaction.point_size.vertex_set_evaluation_set_geometry_default