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