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