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 ¶ms);
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 ¶ms)
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