1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2014 The Android Open Source Project
6  * Copyright (c) 2016 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Tessellation Geometry Interaction - Grid render (limits, scatter)
23 *//*--------------------------------------------------------------------*/
24 
25 #include "vktTessellationGeometryGridRenderTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28 
29 #include "tcuTestLog.hpp"
30 #include "tcuTextureUtil.hpp"
31 #include "tcuSurface.hpp"
32 #include "tcuRGBA.hpp"
33 
34 #include "vkDefs.hpp"
35 #include "vkBarrierUtil.hpp"
36 #include "vkQueryUtil.hpp"
37 #include "vkBuilderUtil.hpp"
38 #include "vkTypeUtil.hpp"
39 #include "vkImageUtil.hpp"
40 #include "vkCmdUtil.hpp"
41 #include "vkObjUtil.hpp"
42 #include "vkBufferWithMemory.hpp"
43 #include "vkImageWithMemory.hpp"
44 
45 #include "deUniquePtr.hpp"
46 
47 #include <string>
48 #include <vector>
49 
50 namespace vkt
51 {
52 namespace tessellation
53 {
54 
55 using namespace vk;
56 
57 namespace
58 {
59 
60 enum Constants
61 {
62     RENDER_SIZE = 256,
63 };
64 
65 enum FlagBits
66 {
67     FLAG_TESSELLATION_MAX_SPEC         = 1u << 0,
68     FLAG_GEOMETRY_MAX_SPEC             = 1u << 1,
69     FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC = 1u << 2,
70 
71     FLAG_GEOMETRY_SCATTER_INSTANCES  = 1u << 3,
72     FLAG_GEOMETRY_SCATTER_PRIMITIVES = 1u << 4,
73     FLAG_GEOMETRY_SEPARATE_PRIMITIVES =
74         1u << 5, //!< if set, geometry shader outputs separate grid cells and not continuous slices
75     FLAG_GEOMETRY_SCATTER_LAYERS = 1u << 6,
76 };
77 typedef uint32_t Flags;
78 
79 class GridRenderTestCase : public TestCase
80 {
81 public:
82     void initPrograms(vk::SourceCollections &programCollection) const;
83     TestInstance *createInstance(Context &context) const;
84 
85     GridRenderTestCase(tcu::TestContext &testCtx, const std::string &name, const Flags flags);
86 
87 private:
88     const Flags m_flags;
89     const int m_tessGenLevel;
90     const int m_numGeometryInvocations;
91     const int m_numLayers;
92     int m_numGeometryPrimitivesPerInvocation;
93 };
94 
GridRenderTestCase(tcu::TestContext & testCtx,const std::string & name,const Flags flags)95 GridRenderTestCase::GridRenderTestCase(tcu::TestContext &testCtx, const std::string &name, const Flags flags)
96     : TestCase(testCtx, name)
97     , m_flags(flags)
98     , m_tessGenLevel((m_flags & FLAG_TESSELLATION_MAX_SPEC) ? 64 : 5)
99     , m_numGeometryInvocations((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) ? 32 : 4)
100     , m_numLayers((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) ? 8 : 1)
101 {
102     DE_ASSERT(((flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) ==
103               ((flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
104 
105     int geometryOutputVertices        = 0;
106     int geometryTotalOutputComponents = 0;
107 
108     if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
109     {
110         geometryOutputVertices        = 256;
111         geometryTotalOutputComponents = 1024;
112     }
113     else
114     {
115         geometryOutputVertices        = 16;
116         geometryTotalOutputComponents = 1024;
117     }
118 
119     const bool separatePrimitives    = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
120     const int numComponentsPerVertex = 8; // vec4 pos, vec4 color
121 
122     if (separatePrimitives)
123     {
124         const int numComponentLimit = geometryTotalOutputComponents / (4 * numComponentsPerVertex);
125         const int numOutputLimit    = geometryOutputVertices / 4;
126 
127         m_numGeometryPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit);
128     }
129     else
130     {
131         // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
132         // Each slice is a triangle strip and is generated by a single shader invocation.
133         // One slice with 4 segment ends (nodes) and 3 segments:
134         //    .__.__.__.
135         //    |\ |\ |\ |
136         //    |_\|_\|_\|
137 
138         const int numSliceNodesComponentLimit =
139             geometryTotalOutputComponents / (2 * numComponentsPerVertex + 2); // each node 2 vertices
140         const int numSliceNodesOutputLimit = geometryOutputVertices / 2;      // each node 2 vertices
141         const int numSliceNodes            = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
142 
143         m_numGeometryPrimitivesPerInvocation = (numSliceNodes - 1) * 2;
144     }
145 }
146 
initPrograms(SourceCollections & programCollection) const147 void GridRenderTestCase::initPrograms(SourceCollections &programCollection) const
148 {
149     // Vertex shader
150     {
151         std::ostringstream src;
152         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
153             << "\n"
154             << "void main (void)\n"
155             << "{\n"
156             << "    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
157             << "}\n";
158 
159         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
160     }
161 
162     // Fragment shader
163     {
164         std::ostringstream src;
165         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
166             << "layout(location = 0) flat in highp   vec4 v_color;\n"
167             << "layout(location = 0) out     mediump vec4 fragColor;\n"
168             << "\n"
169             << "void main (void)\n"
170             << "{\n"
171             << "    fragColor = v_color;\n"
172             << "}\n";
173 
174         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
175     }
176 
177     // Tessellation control
178     {
179         std::ostringstream src;
180         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES)
181             << "\n"
182                "#extension GL_EXT_tessellation_shader : require\n"
183                "layout(vertices = 1) out;\n"
184                "\n"
185                "void main (void)\n"
186                "{\n"
187                "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
188                "    gl_TessLevelInner[0] = float("
189             << m_tessGenLevel
190             << ");\n"
191                "    gl_TessLevelInner[1] = float("
192             << m_tessGenLevel
193             << ");\n"
194                "    gl_TessLevelOuter[0] = float("
195             << m_tessGenLevel
196             << ");\n"
197                "    gl_TessLevelOuter[1] = float("
198             << m_tessGenLevel
199             << ");\n"
200                "    gl_TessLevelOuter[2] = float("
201             << m_tessGenLevel
202             << ");\n"
203                "    gl_TessLevelOuter[3] = float("
204             << m_tessGenLevel
205             << ");\n"
206                "}\n";
207 
208         programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
209     }
210 
211     // Tessellation evaluation
212     {
213         std::ostringstream src;
214         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
215             << "#extension GL_EXT_tessellation_shader : require\n"
216             << "layout(quads) in;\n"
217             << "\n"
218             << "layout(location = 0) out mediump ivec2 v_tessellationGridPosition;\n"
219             << "\n"
220             << "// note: No need to use precise gl_Position since position does not depend on order\n"
221             << "void main (void)\n"
222             << "{\n";
223 
224         if (m_flags &
225             (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
226             src << "    // Cover only a small area in a corner. The area will be expanded in geometry shader to cover "
227                    "whole viewport\n"
228                 << "    gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
229         else
230             src << "    // Fill the whole viewport\n"
231                 << "    gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
232 
233         src << "    // Calculate position in tessellation grid\n"
234             << "    v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << m_tessGenLevel << ")));\n"
235             << "}\n";
236 
237         programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
238     }
239 
240     // Geometry shader
241     {
242         const int numInvocations = m_numGeometryInvocations;
243         const int numPrimitives  = m_numGeometryPrimitivesPerInvocation;
244 
245         std::ostringstream src;
246 
247         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
248             << "#extension GL_EXT_geometry_shader : require\n"
249             << "layout(triangles, invocations = " << numInvocations << ") in;\n"
250             << "layout(triangle_strip, max_vertices = "
251             << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n"
252             << "\n"
253             << "layout(location = 0) in       mediump ivec2 v_tessellationGridPosition[];\n"
254             << "layout(location = 0) flat out highp   vec4  v_color;\n"
255             << "\n"
256             << "void main (void)\n"
257             << "{\n"
258             << "    const float equalThreshold = 0.001;\n"
259             << "    const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce "
260                "gaps. Fill potential gaps by enlarging the output slice a little.\n"
261             << "\n"
262             << "    // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
263             << "    // Original rectangle can be found by finding the bounding AABB of the triangle\n"
264             << "    vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, "
265                "gl_in[2].gl_Position.x)),\n"
266             << "                     min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, "
267                "gl_in[2].gl_Position.y)),\n"
268             << "                     max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, "
269                "gl_in[2].gl_Position.x)),\n"
270             << "                     max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, "
271                "gl_in[2].gl_Position.y)));\n"
272             << "\n"
273             << "    // Location in tessellation grid\n"
274             << "    ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], "
275                "v_tessellationGridPosition[2])));\n"
276             << "\n"
277             << "    // Which triangle of the two that split the grid cell\n"
278             << "    int numVerticesOnBottomEdge = 0;\n"
279             << "    for (int ndx = 0; ndx < 3; ++ndx)\n"
280             << "        if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
281             << "            ++numVerticesOnBottomEdge;\n"
282             << "    bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
283             << "\n";
284 
285         if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
286         {
287             // scatter primitives
288             src << "    // Draw grid cells\n"
289                 << "    int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
290                 << "    for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
291                 << "    {\n"
292                 << "        ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", 2 * "
293                 << m_tessGenLevel << " * " << numInvocations << ");\n"
294                 << "        ivec2 dstGridNdx = ivec2(" << m_tessGenLevel << " * ndx + gridPosition.x, "
295                 << m_tessGenLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
296                 << "        vec4 dstArea;\n"
297                 << "        dstArea.x = float(dstGridNdx.x)   / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
298                 << "        dstArea.y = float(dstGridNdx.y)   / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
299                 << "        dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
300                 << "        dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
301                 << "\n"
302                 << "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
303                 << "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
304                 << "        vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
305                 << "\n"
306                 << "        gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
307                 << "        v_color = outputColor;\n"
308                 << "        EmitVertex();\n"
309                 << "\n"
310                 << "        gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
311                 << "        v_color = outputColor;\n"
312                 << "        EmitVertex();\n"
313                 << "\n"
314                 << "        gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
315                 << "        v_color = outputColor;\n"
316                 << "        EmitVertex();\n"
317                 << "\n"
318                 << "        gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
319                 << "        v_color = outputColor;\n"
320                 << "        EmitVertex();\n"
321                 << "        EndPrimitive();\n"
322                 << "    }\n";
323         }
324         else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
325         {
326             // Number of subrectangle instances = num layers
327             DE_ASSERT(m_numLayers == numInvocations * 2);
328 
329             src << "    // Draw grid cells, send each primitive to a separate layer\n"
330                 << "    int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
331                 << "    for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
332                 << "    {\n"
333                 << "        ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", "
334                 << m_tessGenLevel << ");\n"
335                 << "        ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives
336                 << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
337                 << "        vec4 dstArea;\n"
338                 << "        dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
339                 << "        dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
340                 << "        dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
341                 << "        dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
342                 << "\n"
343                 << "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
344                 << "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
345                 << "        vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
346                 << "\n"
347                 << "        gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
348                 << "        v_color = outputColor;\n"
349                 << "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
350                 << "        EmitVertex();\n"
351                 << "\n"
352                 << "        gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
353                 << "        v_color = outputColor;\n"
354                 << "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
355                 << "        EmitVertex();\n"
356                 << "\n"
357                 << "        gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
358                 << "        v_color = outputColor;\n"
359                 << "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
360                 << "        EmitVertex();\n"
361                 << "\n"
362                 << "        gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
363                 << "        v_color = outputColor;\n"
364                 << "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
365                 << "        EmitVertex();\n"
366                 << "        EndPrimitive();\n"
367                 << "    }\n";
368         }
369         else
370         {
371             if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
372             {
373                 src << "    // Scatter slices\n"
374                     << "    int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
375                     << "    ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInvocations * 2)
376                     << " + inputTriangleNdx);\n"
377                     << "    ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2("
378                     << m_tessGenLevel << ", " << m_tessGenLevel << " * " << (numInvocations * 2) << ");\n"
379                     << "\n"
380                     << "    // Draw slice to the dstSlice slot\n"
381                     << "    vec4 outputSliceArea;\n"
382                     << "    outputSliceArea.x = float(dstSliceNdx.x)   / float(" << m_tessGenLevel
383                     << ") * 2.0 - 1.0 - gapOffset;\n"
384                     << "    outputSliceArea.y = float(dstSliceNdx.y)   / float("
385                     << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 - gapOffset;\n"
386                     << "    outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << m_tessGenLevel
387                     << ") * 2.0 - 1.0 + gapOffset;\n"
388                     << "    outputSliceArea.w = float(dstSliceNdx.y+1) / float("
389                     << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
390             }
391             else
392             {
393                 src << "    // Fill the input area with slices\n"
394                     << "    // Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
395                     << "    float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
396                     << "    // Each slice is a invocation\n"
397                     << "    float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInvocations << ");\n"
398                     << "    float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
399                     << "\n"
400                     << "    vec4 outputSliceArea;\n"
401                     << "    outputSliceArea.x = aabb.x - gapOffset;\n"
402                     << "    outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
403                     << "    outputSliceArea.z = aabb.z + gapOffset;\n"
404                     << "    outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
405             }
406 
407             src << "\n"
408                 << "    // Draw slice\n"
409                 << "    for (int ndx = 0; ndx < " << ((numPrimitives + 2) / 2) << "; ++ndx)\n"
410                 << "    {\n"
411                 << "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
412                 << "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
413                 << "        vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
414                 << "        float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float("
415                 << (numPrimitives / 2) << "));\n"
416                 << "\n"
417                 << "        gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
418                 << "        v_color = outputColor;\n"
419                 << "        EmitVertex();\n"
420                 << "\n"
421                 << "        gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
422                 << "        v_color = outputColor;\n"
423                 << "        EmitVertex();\n"
424                 << "    }\n";
425         }
426 
427         src << "}\n";
428 
429         programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
430     }
431 }
432 
433 class GridRenderTestInstance : public TestInstance
434 {
435 public:
436     struct Params
437     {
438         tcu::TestContext &testCtx;
439         Flags flags;
440         int tessGenLevel;
441         int numGeometryInvocations;
442         int numLayers;
443         int numGeometryPrimitivesPerInvocation;
444 
Paramsvkt::tessellation::__anonc8d5ac4c0111::GridRenderTestInstance::Params445         Params(tcu::TestContext &testContext)
446             : testCtx(testContext)
447             , flags()
448             , tessGenLevel()
449             , numGeometryInvocations()
450             , numLayers()
451             , numGeometryPrimitivesPerInvocation()
452         {
453         }
454     };
455     GridRenderTestInstance(Context &context, const Params &params);
456     tcu::TestStatus iterate(void);
457 
458 private:
459     Params m_params;
460 };
461 
GridRenderTestInstance(Context & context,const Params & params)462 GridRenderTestInstance::GridRenderTestInstance(Context &context, const Params &params)
463     : TestInstance(context)
464     , m_params(params)
465 {
466     tcu::TestContext &testCtx = m_params.testCtx;
467     testCtx.getLog() << tcu::TestLog::Message
468                      << "Testing tessellation and geometry shaders that output a large number of primitives.\n"
469                      << tcu::TestLog::EndMessage;
470 
471     if (m_params.flags & FLAG_GEOMETRY_SCATTER_LAYERS)
472         testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_params.numLayers
473                          << tcu::TestLog::EndMessage;
474 
475     testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: " << m_params.tessGenLevel << ", mode = quad.\n"
476                      << "\tEach input patch produces " << (m_params.tessGenLevel * m_params.tessGenLevel) << " ("
477                      << (m_params.tessGenLevel * m_params.tessGenLevel * 2) << " triangles)\n"
478                      << tcu::TestLog::EndMessage;
479 
480     int geometryOutputComponents      = 0;
481     int geometryOutputVertices        = 0;
482     int geometryTotalOutputComponents = 0;
483 
484     if (m_params.flags & FLAG_GEOMETRY_MAX_SPEC)
485     {
486         testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader minimum maximum output limits."
487                          << tcu::TestLog::EndMessage;
488 
489         geometryOutputComponents      = 64;
490         geometryOutputVertices        = 256;
491         geometryTotalOutputComponents = 1024;
492     }
493     else
494     {
495         geometryOutputComponents      = 64;
496         geometryOutputVertices        = 16;
497         geometryTotalOutputComponents = 1024;
498     }
499 
500     if ((m_params.flags & FLAG_GEOMETRY_MAX_SPEC) || (m_params.flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC))
501     {
502         tcu::MessageBuilder msg(&testCtx.getLog());
503 
504         msg << "Geometry shader, targeting following limits:\n";
505 
506         if (m_params.flags & FLAG_GEOMETRY_MAX_SPEC)
507             msg << "\tmaxGeometryOutputComponents = " << geometryOutputComponents << "\n"
508                 << "\tmaxGeometryOutputVertices = " << geometryOutputVertices << "\n"
509                 << "\tmaxGeometryTotalOutputComponents = " << geometryTotalOutputComponents << "\n";
510 
511         if (m_params.flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
512             msg << "\tmaxGeometryShaderInvocations = " << m_params.numGeometryInvocations;
513 
514         msg << tcu::TestLog::EndMessage;
515     }
516 
517     const bool separatePrimitives         = (m_params.flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
518     const int numComponentsPerVertex      = 8; // vec4 pos, vec4 color
519     int numVerticesPerInvocation          = 0;
520     int geometryVerticesPerPrimitive      = 0;
521     int geometryPrimitivesOutPerPrimitive = 0;
522 
523     if (separatePrimitives)
524     {
525         numVerticesPerInvocation = m_params.numGeometryPrimitivesPerInvocation * 4;
526     }
527     else
528     {
529         // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
530         // Each slice is a triangle strip and is generated by a single shader invocation.
531         // One slice with 4 segment ends (nodes) and 3 segments:
532         //    .__.__.__.
533         //    |\ |\ |\ |
534         //    |_\|_\|_\|
535 
536         const int numSliceNodesComponentLimit =
537             geometryTotalOutputComponents / (2 * numComponentsPerVertex); // each node 2 vertices
538         const int numSliceNodesOutputLimit = geometryOutputVertices / 2;  // each node 2 vertices
539         const int numSliceNodes            = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
540 
541         numVerticesPerInvocation = numSliceNodes * 2;
542     }
543 
544     geometryVerticesPerPrimitive      = numVerticesPerInvocation * m_params.numGeometryInvocations;
545     geometryPrimitivesOutPerPrimitive = m_params.numGeometryPrimitivesPerInvocation * m_params.numGeometryInvocations;
546 
547     testCtx.getLog() << tcu::TestLog::Message << "Geometry shader:\n"
548                      << "\tTotal output vertex count per invocation: " << numVerticesPerInvocation << "\n"
549                      << "\tTotal output primitive count per invocation: " << m_params.numGeometryPrimitivesPerInvocation
550                      << "\n"
551                      << "\tNumber of invocations per primitive: " << m_params.numGeometryInvocations << "\n"
552                      << "\tTotal output vertex count per input primitive: " << geometryVerticesPerPrimitive << "\n"
553                      << "\tTotal output primitive count per input primitive: " << geometryPrimitivesOutPerPrimitive
554                      << "\n"
555                      << tcu::TestLog::EndMessage;
556 
557     testCtx.getLog() << tcu::TestLog::Message << "Program:\n"
558                      << "\tTotal program output vertices count per input patch: "
559                      << (m_params.tessGenLevel * m_params.tessGenLevel * 2 * geometryVerticesPerPrimitive) << "\n"
560                      << "\tTotal program output primitive count per input patch: "
561                      << (m_params.tessGenLevel * m_params.tessGenLevel * 2 * geometryPrimitivesOutPerPrimitive) << "\n"
562                      << tcu::TestLog::EndMessage;
563 }
564 
createInstance(Context & context) const565 TestInstance *GridRenderTestCase::createInstance(Context &context) const
566 {
567     GridRenderTestInstance::Params params(m_testCtx);
568 
569     params.flags                              = m_flags;
570     params.tessGenLevel                       = m_tessGenLevel;
571     params.numGeometryInvocations             = m_numGeometryInvocations;
572     params.numLayers                          = m_numLayers;
573     params.numGeometryPrimitivesPerInvocation = m_numGeometryPrimitivesPerInvocation;
574 
575     return new GridRenderTestInstance(context, params);
576 }
577 
verifyResultLayer(tcu::TestLog & log,const tcu::ConstPixelBufferAccess & image,const int layerNdx)578 bool verifyResultLayer(tcu::TestLog &log, const tcu::ConstPixelBufferAccess &image, const int layerNdx)
579 {
580     tcu::Surface errorMask(image.getWidth(), image.getHeight());
581     bool foundError = false;
582 
583     tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
584 
585     log << tcu::TestLog::Message << "Verifying output layer " << layerNdx << tcu::TestLog::EndMessage;
586 
587     for (int y = 0; y < image.getHeight(); ++y)
588         for (int x = 0; x < image.getWidth(); ++x)
589         {
590             const int threshold = 8;
591             const tcu::RGBA color(image.getPixel(x, y));
592 
593             // Color must be a linear combination of green and yellow
594             if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
595             {
596                 errorMask.setPixel(x, y, tcu::RGBA::red());
597                 foundError = true;
598             }
599         }
600 
601     if (!foundError)
602     {
603         log << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
604             << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
605             << tcu::TestLog::Image("Result", "Rendered result", image) << tcu::TestLog::EndImageSet;
606         return true;
607     }
608     else
609     {
610         log << tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage
611             << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
612             << tcu::TestLog::Image("Result", "Rendered result", image)
613             << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) << tcu::TestLog::EndImageSet;
614         return false;
615     }
616 }
617 
iterate(void)618 tcu::TestStatus GridRenderTestInstance::iterate(void)
619 {
620     requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(),
621                     FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER);
622 
623     m_context.getTestContext().getLog() << tcu::TestLog::Message
624                                         << "Rendering single point at the origin. Expecting yellow and green colored "
625                                            "grid-like image. (High-frequency grid may appear unicolored)."
626                                         << tcu::TestLog::EndMessage;
627 
628     const DeviceInterface &vk       = m_context.getDeviceInterface();
629     const VkDevice device           = m_context.getDevice();
630     const VkQueue queue             = m_context.getUniversalQueue();
631     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
632     Allocator &allocator            = m_context.getDefaultAllocator();
633 
634     // Color attachment
635 
636     const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
637     const VkFormat colorFormat  = VK_FORMAT_R8G8B8A8_UNORM;
638     const VkImageSubresourceRange colorImageAllLayersRange =
639         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
640     const VkImageCreateInfo colorImageCreateInfo =
641         makeImageCreateInfo(renderSize, colorFormat,
642                             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, m_params.numLayers);
643     const VkImageViewType colorAttachmentViewType =
644         (m_params.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY);
645     const ImageWithMemory colorAttachmentImage(vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any);
646 
647     // Color output buffer: image will be copied here for verification (big enough for all layers).
648 
649     const VkDeviceSize colorBufferSizeBytes =
650         renderSize.x() * renderSize.y() * m_params.numLayers * tcu::getPixelSize(mapVkFormat(colorFormat));
651     const BufferWithMemory colorBuffer(vk, device, allocator,
652                                        makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
653                                        MemoryRequirement::HostVisible);
654 
655     // Pipeline: no vertex input attributes nor descriptors.
656 
657     const Unique<VkImageView> colorAttachmentView(makeImageView(
658         vk, device, *colorAttachmentImage, colorAttachmentViewType, colorFormat, colorImageAllLayersRange));
659     const Unique<VkRenderPass> renderPass(makeRenderPass(vk, device, colorFormat));
660     const Unique<VkFramebuffer> framebuffer(makeFramebuffer(vk, device, *renderPass, *colorAttachmentView,
661                                                             renderSize.x(), renderSize.y(), m_params.numLayers));
662     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device));
663     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
664     const Unique<VkCommandBuffer> cmdBuffer(
665         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
666 
667     const Unique<VkPipeline> pipeline(
668         GraphicsPipelineBuilder()
669             .setRenderSize(renderSize)
670             .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
671             .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
672             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
673                        m_context.getBinaryCollection().get("tesc"), DE_NULL)
674             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
675                        m_context.getBinaryCollection().get("tese"), DE_NULL)
676             .setShader(vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get("geom"), DE_NULL)
677             .build(vk, device, *pipelineLayout, *renderPass));
678 
679     beginCommandBuffer(vk, *cmdBuffer);
680 
681     // Change color attachment image layout
682     {
683         const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
684             (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
685             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, *colorAttachmentImage, colorImageAllLayersRange);
686 
687         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
688                               VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
689                               &colorAttachmentLayoutBarrier);
690     }
691 
692     // Begin render pass
693     {
694         const VkRect2D renderArea = makeRect2D(renderSize);
695         const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
696 
697         beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
698     }
699 
700     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
701 
702     vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
703     endRenderPass(vk, *cmdBuffer);
704 
705     // Copy render result to a host-visible buffer
706     copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize,
707                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
708                       m_params.numLayers);
709 
710     endCommandBuffer(vk, *cmdBuffer);
711     submitCommandsAndWait(vk, device, queue, *cmdBuffer);
712 
713     // Verify results
714     {
715         const Allocation &alloc(colorBuffer.getAllocation());
716 
717         invalidateAlloc(vk, device, alloc);
718 
719         const tcu::ConstPixelBufferAccess imageAllLayers(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(),
720                                                          m_params.numLayers, alloc.getHostPtr());
721         bool allOk(true);
722 
723         for (int ndx = 0; ndx < m_params.numLayers; ++ndx)
724             allOk = allOk && verifyResultLayer(
725                                  m_context.getTestContext().getLog(),
726                                  tcu::getSubregion(imageAllLayers, 0, 0, ndx, renderSize.x(), renderSize.y(), 1), ndx);
727 
728         return (allOk ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
729     }
730 }
731 
732 struct TestCaseDescription
733 {
734     const char *name;
735     Flags flags;
736 };
737 
738 } // namespace
739 
740 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.limits.*
741 //! \note Tests that check implementation defined limits were omitted, because they rely on runtime shader source generation
742 //!       (e.g. changing the number of vertices output from geometry shader). CTS currently doesn't support that,
743 //!       because some platforms require precompiled shaders.
createGeometryGridRenderLimitsTests(tcu::TestContext & testCtx)744 tcu::TestCaseGroup *createGeometryGridRenderLimitsTests(tcu::TestContext &testCtx)
745 {
746     // Render with properties near their limits
747     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "limits"));
748 
749     static const TestCaseDescription cases[] = {
750         // Minimum maximum tessellation level
751         {"output_required_max_tessellation", FLAG_TESSELLATION_MAX_SPEC},
752         // Output minimum maximum number of vertices the geometry shader
753         {"output_required_max_geometry", FLAG_GEOMETRY_MAX_SPEC},
754         // Minimum maximum number of geometry shader invocations
755         {"output_required_max_invocations", FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC},
756     };
757 
758     for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
759         group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].flags));
760 
761     return group.release();
762 }
763 
764 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.scatter.*
createGeometryGridRenderScatterTests(tcu::TestContext & testCtx)765 tcu::TestCaseGroup *createGeometryGridRenderScatterTests(tcu::TestContext &testCtx)
766 {
767     // Scatter output primitives
768     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "scatter"));
769 
770     static const TestCaseDescription cases[] = {
771         // Each geometry shader instance outputs its primitives far from other instances of the same execution
772         {"geometry_scatter_instances", FLAG_GEOMETRY_SCATTER_INSTANCES},
773         // Each geometry shader instance outputs its primitives far from other primitives of the same instance
774         {"geometry_scatter_primitives", FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SEPARATE_PRIMITIVES},
775         // Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance
776         {"geometry_scatter_layers", FLAG_GEOMETRY_SCATTER_LAYERS | FLAG_GEOMETRY_SEPARATE_PRIMITIVES},
777     };
778 
779     for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
780         group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].flags));
781 
782     return group.release();
783 }
784 
785 } // namespace tessellation
786 } // namespace vkt
787