1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2024 The Khronos Group Inc.
6 * Copyright (c) 2024 Valve Corporation.
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 Device Generated Commands EXT Graphics Draw Tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktDGCGraphicsDrawTestsExt.hpp"
26 #include "vkCmdUtil.hpp"
27 #include "vktTestCase.hpp"
28 #include "vktDGCUtilCommon.hpp"
29 #include "vktDGCUtilExt.hpp"
30
31 #include "vkImageUtil.hpp"
32 #include "vkObjUtil.hpp"
33 #include "vkBarrierUtil.hpp"
34
35 #include "tcuImageCompare.hpp"
36
37 #include "deUniquePtr.hpp"
38 #include "deRandom.hpp"
39 #include "vktTestCaseUtil.hpp"
40
41 #include <sstream>
42 #include <map>
43 #include <memory>
44 #include <algorithm>
45 #include <iterator>
46 #include <bitset>
47 #include <utility>
48
49 namespace vkt
50 {
51 namespace DGC
52 {
53
54 namespace
55 {
56
57 using namespace vk;
58
59 /*
60 TEST MECHANISM FOR VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT
61
62 2x2 pixel framebuffer, with 3 sequences: lets call them 0, 1, 2
63
64 Each sequence will draw triangles over the following pixels:
65
66 0 1
67 1 2
68
69 The indirect commands layout will contain:
70
71 1. VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT (optional)
72 2. VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT
73 3. VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT
74 4. VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT
75
76 The order of the two intermediate elements will be chosen pseudorandomly.
77
78 The structure in the indirect commands buffer needs to be:
79
80 typedef struct VkDrawIndirectCommand {
81 uint32_t vertexCount;
82 uint32_t instanceCount;
83 uint32_t firstVertex;
84 uint32_t firstInstance;
85 } VkDrawIndirectCommand;
86
87 And we want to check if each of those 4 parameters is properly taken into account.
88
89 We'll create vertex coordinates for 4 triangles (12 vertices in total) each covering one of the framebuffer pixels.
90
91 Lets call these triangles A, B, C and D, by default drawn as:
92
93 A B
94 C D
95
96 Over the 4 pixels of the framebuffer.
97
98 To make things more interesting and use a different vertex buffer in each sequence, we'll create a vertex buffer for each of the 3
99 sequences with different configurations, so that the stride and size also varies a bit. ZZ represents Zeros.
100
101 Buffer 0: A0 A1 A2 # Smaller buffer.
102 Buffer 1: B0 ZZ B1 ZZ B2 ZZ C0 ZZ C1 ZZ C2 ZZ # Wider stride.
103 Buffer 2: ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ D0 D1 D2 # Non-zero first vertex.
104
105 The vertex count for each sequence varies: 3, 6, 3.
106
107 The instance count could change also for each sequence: 1, 1, 2.
108 - And we could pass the instance index to the fragment shader and use it as the green channel.
109
110 The first vertex will vary in each call: 0, 0, 9
111
112 The first instance can also vary per call: 0 1 0
113
114 The push constants will determine the red channel, and will vary in each sequence.
115
116 The test setup is good for cases where VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT is not used. If it is, how can we make a
117 difference there?
118
119 We can provide 2 fragment shaders: one stores 0 in the blue channel and another one stores 1. We can make the
120 sequences use pipelines 0 0 1, for example. What about the vertex shader? One could mirror coordinates in the X dimension for the
121 *first* triangle *only* and another one would not. With this, we can determine the expected color for each of the 4 pixels.
122
123 We can add variations that use tessellation or geometry, and in that case the tessellation or geometry shaders would be the ones
124 flipping coordinates in the X dimension for the first triangle. As VertexIndex can only be used in the vertex shader, the
125 determination of which triangle to flip will be done in the vertex shader, which will pass a flag to the tessellation or geometry
126 shader indicating which triangle needs flipping.
127
128 */
129
130 /*
131 TEST MECHANISM FOR VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT
132
133 Note: very similar to a non-indexed draw as explained above.
134
135 2x2 pixel framebuffer, with 3 sequences: lets call them 0, 1, 2
136
137 Each sequence will draw triangles over the following pixels:
138
139 0 1
140 1 2
141
142 The indirect commands layout will contain:
143
144 1. VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT (optional)
145 2. VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT
146 3. VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT
147 4. VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT
148
149 The order of the two intermediate elements will be chosen pseudorandomly.
150
151 The structure in the indirect commands buffer needs to be:
152
153 typedef struct VkDrawIndexedIndirectCommand {
154 uint32_t indexCount;
155 uint32_t instanceCount;
156 uint32_t firstIndex;
157 int32_t vertexOffset;
158 uint32_t firstInstance;
159 } VkDrawIndexedIndirectCommand;
160
161 And we want to check if each of those 5 parameters is properly taken into account.
162
163 We'll create vertex coordinates for 4 triangles (12 vertices in total) each covering one of the framebuffer pixels.
164
165 Lets call these triangles A, B, C and D, by default drawn as:
166
167 A B
168 C D
169
170 Over the 4 pixels of the framebuffer.
171
172 We'll use a single vertex buffer with all vertices stored consecutively for triangles A, B, C and D.
173
174 To make things more interesting and use a different index buffer in each sequence, we'll create an index buffer for each of the 3
175 sequences with different configurations, so that the size and index type also varies. ZZ represents an invalid large index value.
176
177 Buffer 0: 0 1 2 # Different sizes.
178 Buffer 1: 3 4 5 6 7 8 #
179 Buffer 2: ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ 29 30 31 # Non-zero first vertex, plus indices will be stored as 16-bit instead of 32-bit.
180
181 Parameters for each sequence:
182
183 indexCount: 3 6 3
184 instanceCount: 1 1 2
185 firstIndex: 0 0 9
186 vertexOffset: 0 0 -20
187 firstInstance: 0 1 0
188
189 The push constants will determine the red channel, and will vary in each sequence.
190
191 The test setup is good for cases where VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT is not used. If it is, wow can we make a
192 difference there?
193
194 We can provide 2 fragment shaders: one stores 0 in the blue channel and another one stores 1. We can make the
195 sequences use pipelines 0 0 1, for example. What about the vertex shader? One could mirror coordinates in the X dimension for the
196 *first* and *second* triangles and another one would not. With this, we can determine the expected color for each of the 4 pixels.
197
198 We can add variations that use tessellation or geometry, and in that case the tessellation or geometry shaders would be the ones
199 flipping coordinates in the X dimension for the first triangle. As VertexIndex can only be used in the vertex shader, the
200 determination of which triangle to flip will be done in the vertex shader, which will pass a flag to the tessellation or geometry
201 shader indicating which triangle needs flipping.
202
203 */
204
205 using DGCShaderExtPtr = std::unique_ptr<DGCShaderExt>;
206
207 constexpr int kPerTriangleVertices = 3;
208 constexpr float kVertNormalRedOffset = 0.125f;
209 constexpr float kVertFlipRedOffset = 0.25f;
210
211 enum class TestType
212 {
213 DRAW = 0,
214 DRAW_SIMPLE, // No vertex or index buffer tokens.
215 DRAW_INDEXED,
216 DRAW_INDEXED_DX, // Using VK_INDIRECT_COMMANDS_INPUT_MODE_DXGI_INDEX_BUFFER_EXT.
217 };
218
isIndexed(TestType testType)219 bool isIndexed(TestType testType)
220 {
221 return (testType == TestType::DRAW_INDEXED || testType == TestType::DRAW_INDEXED_DX);
222 }
223
224 enum class ExtraStages
225 {
226 NONE = 0,
227 TESSELLATION,
228 GEOMETRY,
229 };
230
231 enum class PipelineType
232 {
233 MONOLITHIC = 0,
234 SHADER_OBJECTS,
235 GPL_FAST,
236 GPL_OPTIMIZED,
237 GPL_MIX_BASE_FAST,
238 GPL_MIX_BASE_OPT,
239 };
240
241 enum class PreprocessType
242 {
243 NONE = 0,
244 SAME_STATE_CMD_BUFFER,
245 OTHER_STATE_CMD_BUFFER,
246 };
247
isGPL(PipelineType pipelineType)248 bool isGPL(PipelineType pipelineType)
249 {
250 return (pipelineType == PipelineType::GPL_FAST || pipelineType == PipelineType::GPL_OPTIMIZED ||
251 pipelineType == PipelineType::GPL_MIX_BASE_FAST || pipelineType == PipelineType::GPL_MIX_BASE_OPT);
252 }
253
getGeneralConstructionType(PipelineType pipelineType)254 PipelineConstructionType getGeneralConstructionType(PipelineType pipelineType)
255 {
256 PipelineConstructionType constructionType =
257 PIPELINE_CONSTRUCTION_TYPE_SHADER_OBJECT_LINKED_BINARY; // Actually invalid.
258 switch (pipelineType)
259 {
260 case PipelineType::MONOLITHIC:
261 constructionType = PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC;
262 break;
263 case PipelineType::SHADER_OBJECTS:
264 constructionType = PIPELINE_CONSTRUCTION_TYPE_SHADER_OBJECT_UNLINKED_SPIRV;
265 break;
266 case PipelineType::GPL_FAST:
267 case PipelineType::GPL_MIX_BASE_FAST:
268 constructionType = PIPELINE_CONSTRUCTION_TYPE_FAST_LINKED_LIBRARY;
269 break;
270 case PipelineType::GPL_OPTIMIZED:
271 case PipelineType::GPL_MIX_BASE_OPT:
272 constructionType = PIPELINE_CONSTRUCTION_TYPE_LINK_TIME_OPTIMIZED_LIBRARY;
273 break;
274 default:
275 break;
276 }
277 return constructionType;
278 }
279
280 struct DrawTestParams
281 {
282 TestType testType;
283 ExtraStages extraStages;
284 PipelineType pipelineType;
285 PreprocessType preprocessType;
286 bool checkDrawParams;
287 bool useExecutionSet;
288 bool unorderedSequences;
289
hasExtraStagesvkt::DGC::__anon47167f390111::DrawTestParams290 bool hasExtraStages(void) const
291 {
292 return (extraStages != ExtraStages::NONE);
293 }
294
isShaderObjectsvkt::DGC::__anon47167f390111::DrawTestParams295 bool isShaderObjects(void) const
296 {
297 return (pipelineType == PipelineType::SHADER_OBJECTS);
298 }
299
getStageFlagsvkt::DGC::__anon47167f390111::DrawTestParams300 VkShaderStageFlags getStageFlags(void) const
301 {
302 VkShaderStageFlags stages = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
303
304 if (extraStages == ExtraStages::TESSELLATION)
305 stages |= (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
306 else if (extraStages == ExtraStages::GEOMETRY)
307 stages |= (VK_SHADER_STAGE_GEOMETRY_BIT);
308 else if (extraStages == ExtraStages::NONE)
309 ;
310 else
311 DE_ASSERT(false);
312
313 return stages;
314 }
315
getInputModeFlagsvkt::DGC::__anon47167f390111::DrawTestParams316 VkIndirectCommandsInputModeFlagsEXT getInputModeFlags(void) const
317 {
318 VkIndirectCommandsInputModeFlagsEXT flags = 0u;
319
320 if (isIndexed(testType))
321 {
322 flags |= (testType == TestType::DRAW_INDEXED ? VK_INDIRECT_COMMANDS_INPUT_MODE_VULKAN_INDEX_BUFFER_EXT :
323 VK_INDIRECT_COMMANDS_INPUT_MODE_DXGI_INDEX_BUFFER_EXT);
324 }
325
326 return flags;
327 }
328
doPreprocessvkt::DGC::__anon47167f390111::DrawTestParams329 bool doPreprocess(void) const
330 {
331 return (preprocessType != PreprocessType::NONE);
332 }
333 };
334
335 class DGCDrawInstance : public vkt::TestInstance
336 {
337 public:
DGCDrawInstance(Context & context,DrawTestParams params)338 DGCDrawInstance(Context &context, DrawTestParams params) : TestInstance(context), m_params(params)
339 {
340 }
~DGCDrawInstance(void)341 virtual ~DGCDrawInstance(void)
342 {
343 }
344
345 tcu::TestStatus iterate(void) override;
346
347 protected:
348 DrawTestParams m_params;
349 };
350
351 class DGCDrawCase : public vkt::TestCase
352 {
353 public:
DGCDrawCase(tcu::TestContext & testCtx,const std::string & name,const DrawTestParams & params)354 DGCDrawCase(tcu::TestContext &testCtx, const std::string &name, const DrawTestParams ¶ms)
355 : vkt::TestCase(testCtx, name)
356 , m_params(params)
357 {
358 }
~DGCDrawCase(void)359 virtual ~DGCDrawCase(void)
360 {
361 }
362
363 void initPrograms(vk::SourceCollections &programCollection) const override;
364 TestInstance *createInstance(Context &context) const override;
365 void checkSupport(Context &context) const override;
366
367 protected:
368 DrawTestParams m_params;
369 };
370
createInstance(Context & context) const371 TestInstance *DGCDrawCase::createInstance(Context &context) const
372 {
373 return new DGCDrawInstance(context, m_params);
374 }
375
checkSupport(Context & context) const376 void DGCDrawCase::checkSupport(Context &context) const
377 {
378 const auto stages = m_params.getStageFlags();
379 const auto bindStages = (m_params.useExecutionSet ? stages : 0u);
380 const bool useESO = m_params.isShaderObjects();
381 const auto bindStagesPipeline = (useESO ? 0u : bindStages);
382 const auto bindStagesShaderObject = (useESO ? bindStages : 0u);
383 const auto modeFlags = m_params.getInputModeFlags();
384
385 checkDGCExtSupport(context, stages, bindStagesPipeline, bindStagesShaderObject, modeFlags);
386
387 if (m_params.extraStages == ExtraStages::TESSELLATION)
388 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
389
390 if (m_params.extraStages == ExtraStages::GEOMETRY)
391 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
392
393 if (m_params.checkDrawParams)
394 context.requireDeviceFunctionality("VK_KHR_shader_draw_parameters");
395
396 if (isGPL(m_params.pipelineType))
397 {
398 DE_ASSERT(m_params.useExecutionSet); // The code is not prepared otherwise.
399 context.requireDeviceFunctionality("VK_EXT_graphics_pipeline_library");
400 }
401
402 const auto &dgcProperties = context.getDeviceGeneratedCommandsPropertiesEXT();
403
404 if (useESO)
405 {
406 context.requireDeviceFunctionality("VK_EXT_shader_object");
407
408 if (m_params.useExecutionSet && dgcProperties.maxIndirectShaderObjectCount == 0u)
409 TCU_THROW(NotSupportedError, "maxIndirectShaderObjectCount is zero");
410 }
411 }
412
initPrograms(vk::SourceCollections & programCollection) const413 void DGCDrawCase::initPrograms(vk::SourceCollections &programCollection) const
414 {
415 const bool flipFirstTriangleHoriz = m_params.useExecutionSet;
416 const bool passFlipFactorDown = (flipFirstTriangleHoriz && m_params.hasExtraStages());
417 const bool checkDrawParams = m_params.checkDrawParams; // Start at location=2 for simplicity.
418
419 // Normal vertex shader, always used.
420 {
421 std::ostringstream vert;
422 vert << "#version 460\n"
423 << "out gl_PerVertex\n"
424 << "{\n"
425 << " vec4 gl_Position;\n"
426 << "};\n"
427 << "layout (location=0) in vec4 inPos;\n"
428 << "layout (location=0) out flat int instanceIndex;\n"
429 << "layout (location=1) out flat float redOffset;\n"
430 << (checkDrawParams ? "layout (location=3) out flat int drawIndex;\n" : "")
431 << (checkDrawParams ? "layout (location=4) out flat int baseVertex;\n" : "")
432 << (checkDrawParams ? "layout (location=5) out flat int baseInstance;\n" : "") << "void main (void) {\n"
433 << " gl_Position = inPos;\n"
434 << " instanceIndex = gl_InstanceIndex;\n"
435 << " redOffset = " << kVertNormalRedOffset << ";\n"
436 << (checkDrawParams ? " drawIndex = gl_DrawID;\n" : "")
437 << (checkDrawParams ? " baseVertex = gl_BaseVertex;\n" : "")
438 << (checkDrawParams ? " baseInstance = gl_BaseInstance;\n" : "") << "}\n";
439 programCollection.glslSources.add("vert_normal") << glu::VertexSource(vert.str());
440 }
441
442 // Vertex shader that flips the X coordinates of the first triangle in each draw.
443 // Used for the first two sequences when using execution sets.
444 if (flipFirstTriangleHoriz)
445 {
446 // For indexed draws, the vertex index matches the index value, so we have to flip the first 2 triangles in the list.
447 // For non-indexed draws, the vertex index is per-draw, so we have to flip the first 1 triangle in the first 2 draws.
448 const auto flippedTriangles = ((m_params.testType == TestType::DRAW) ? 1u : 2u);
449 const auto flippedVertexIndices = flippedTriangles * kPerTriangleVertices;
450
451 std::ostringstream vert;
452 vert << "#version 460\n"
453 << "out gl_PerVertex\n"
454 << "{\n"
455 << " vec4 gl_Position;\n"
456 << "};\n"
457 << "layout (location=0) in vec4 inPos;\n"
458 << "layout (location=0) out flat int instanceIndex;\n"
459 << "layout (location=1) out flat float redOffset;\n"
460 << (passFlipFactorDown ? "layout (location=2) out flat float xCoordFactor;\n" : "")
461 << (checkDrawParams ? "layout (location=3) out flat int drawIndex;\n" : "")
462 << (checkDrawParams ? "layout (location=4) out flat int baseVertex;\n" : "")
463 << (checkDrawParams ? "layout (location=5) out flat int baseInstance;\n" : "") << "void main (void) {\n"
464 << " const bool passFlipFactorDown = " << (passFlipFactorDown ? "true" : "false") << ";\n"
465 << " const bool flippedTriangle = (gl_VertexIndex < " << flippedVertexIndices << ");\n"
466 << (passFlipFactorDown ? " xCoordFactor = (flippedTriangle ? -1.0 : 1.0);\n" : "")
467 << (checkDrawParams ? " drawIndex = gl_DrawID;\n" : "")
468 << (checkDrawParams ? " baseVertex = gl_BaseVertex;\n" : "")
469 << (checkDrawParams ? " baseInstance = gl_BaseInstance;\n" : "")
470 << " const float xCoord = ((flippedTriangle && !passFlipFactorDown) ? (inPos.x * -1.0) : inPos.x);\n"
471 << " gl_Position = vec4(xCoord, inPos.yzw);\n"
472 << " instanceIndex = gl_InstanceIndex;\n"
473 << " redOffset = " << kVertFlipRedOffset << ";\n"
474 << "}\n";
475 programCollection.glslSources.add("vert_flip") << glu::VertexSource(vert.str());
476 }
477
478 // The normal fragment shader uses 0 for the blue channel and an alternative one uses 1 in the blue channel, if needed.
479 std::map<std::string, float> shaderNameBlueMap;
480 shaderNameBlueMap["frag_normal"] = 0.0f;
481 if (m_params.useExecutionSet)
482 shaderNameBlueMap["frag_alt"] = 1.0f;
483
484 for (const auto &kvPair : shaderNameBlueMap)
485 {
486 std::ostringstream frag;
487 frag << "#version 460\n"
488 << "layout (push_constant, std430) uniform PushConstantBlock {\n"
489 << " float redValue;\n"
490 << (checkDrawParams ? " int drawIndex;\n" : "") << (checkDrawParams ? " int baseVertex;\n" : "")
491 << (checkDrawParams ? " int baseInstance;\n" : "") << "} pc;\n"
492 << "layout (location=0) in flat int instanceIndex;\n"
493 << "layout (location=1) in flat float redOffset;\n"
494 << (checkDrawParams ? "layout (location=3) in flat int drawIndex;\n" : "")
495 << (checkDrawParams ? "layout (location=4) in flat int baseVertex;\n" : "")
496 << (checkDrawParams ? "layout (location=5) in flat int baseInstance;\n" : "")
497 << "layout (location=0) out vec4 outColor;\n"
498 << "void main (void) {\n"
499 << " bool drawParamsOK = true;\n"
500 << (checkDrawParams ? " drawParamsOK = (drawParamsOK && (drawIndex == pc.drawIndex));\n" : "")
501 << (checkDrawParams ? " drawParamsOK = (drawParamsOK && (baseVertex == pc.baseVertex));\n" : "")
502 << (checkDrawParams ? " drawParamsOK = (drawParamsOK && (baseInstance == pc.baseInstance));\n" : "")
503 << " const float alphaValue = (drawParamsOK ? 1.0 : 0.0);\n"
504 << " outColor = vec4(pc.redValue + redOffset, float(instanceIndex), " << kvPair.second
505 << ", alphaValue);\n"
506 << "}\n";
507 programCollection.glslSources.add(kvPair.first) << glu::FragmentSource(frag.str());
508 }
509
510 if (m_params.extraStages == ExtraStages::GEOMETRY)
511 {
512 // We have to create one or two geometry shaders, depending on if we need to flip the first triangle.
513 std::map<std::string, bool> shaderNameFlipMap;
514 shaderNameFlipMap["geom_normal"] = false;
515 if (flipFirstTriangleHoriz)
516 shaderNameFlipMap["geom_flip"] = true;
517
518 for (const auto &kvPair : shaderNameFlipMap)
519 {
520 const auto &shaderName = kvPair.first;
521 const auto flip = kvPair.second;
522
523 std::ostringstream geom;
524 geom << "#version 460\n"
525 << "layout (triangles) in;\n"
526 << "layout (triangle_strip, max_vertices=3) out;\n"
527 << "in gl_PerVertex\n"
528 << "{\n"
529 << " vec4 gl_Position;\n"
530 << "} gl_in[3];\n"
531 << "out gl_PerVertex\n"
532 << "{\n"
533 << " vec4 gl_Position;\n"
534 << "};\n"
535 << "layout (location=0) in int inInstanceIndex[3];\n"
536 << "layout (location=1) in float inRedOffset[3];\n"
537 << (checkDrawParams ? "layout (location=3) in int inDrawIndex[3];\n" : "")
538 << (checkDrawParams ? "layout (location=4) in int inBaseVertex[3];\n" : "")
539 << (checkDrawParams ? "layout (location=5) in int inBaseInstance[3];\n" : "")
540 << "layout (location=0) out flat int outInstanceIndex;\n"
541 << "layout (location=1) out flat float outRedOffset;\n"
542 << (checkDrawParams ? "layout (location=3) out flat int outDrawIndex;\n" : "")
543 << (checkDrawParams ? "layout (location=4) out flat int outBaseVertex;\n" : "")
544 << (checkDrawParams ? "layout (location=5) out flat int outBaseInstance;\n" : "")
545 << (flip ? "layout (location=2) in float inXCoordFactor[3];\n" : "") << "void main() {\n"
546 << " for (int i = 0; i < 3; ++i) {\n"
547 << " const float xCoordFactor = " << (flip ? "inXCoordFactor[i]" : "1.0") << ";\n"
548 << " gl_Position = vec4(gl_in[i].gl_Position.x * xCoordFactor, gl_in[i].gl_Position.yzw);\n"
549 << " outInstanceIndex = inInstanceIndex[i];\n"
550 << " outRedOffset = inRedOffset[i];\n"
551 << (checkDrawParams ? " outDrawIndex = inDrawIndex[i];\n" : "")
552 << (checkDrawParams ? " outBaseVertex = inBaseVertex[i];\n" : "")
553 << (checkDrawParams ? " outBaseInstance = inBaseInstance[i];\n" : "")
554 << " EmitVertex();\n"
555 << " }\n"
556 << "}\n";
557 programCollection.glslSources.add(shaderName) << glu::GeometrySource(geom.str());
558 }
559 }
560
561 if (m_params.extraStages == ExtraStages::TESSELLATION)
562 {
563 // Same as in the geometry shader case.
564 std::map<std::string, bool> shaderNameFlipMap;
565 shaderNameFlipMap["tesc_normal"] = false;
566 if (flipFirstTriangleHoriz)
567 shaderNameFlipMap["tesc_flip"] = true;
568
569 for (const auto &kvPair : shaderNameFlipMap)
570 {
571 const auto &shaderName = kvPair.first;
572 const auto flip = kvPair.second;
573
574 std::ostringstream tesc;
575 tesc << "#version 460\n"
576 << "layout (vertices=3) out;\n"
577 << "in gl_PerVertex\n"
578 << "{\n"
579 << " vec4 gl_Position;\n"
580 << "} gl_in[gl_MaxPatchVertices];\n"
581 << "out gl_PerVertex\n"
582 << "{\n"
583 << " vec4 gl_Position;\n"
584 << "} gl_out[];\n"
585 << "layout (location=0) in int inInstanceIndex[gl_MaxPatchVertices];\n"
586 << "layout (location=1) in float inRedOffset[gl_MaxPatchVertices];\n"
587 << (checkDrawParams ? "layout (location=3) in int inDrawIndex[gl_MaxPatchVertices];\n" : "")
588 << (checkDrawParams ? "layout (location=4) in int inBaseVertex[gl_MaxPatchVertices];\n" : "")
589 << (checkDrawParams ? "layout (location=5) in int inBaseInstance[gl_MaxPatchVertices];\n" : "")
590 << "layout (location=0) out int outInstanceIndex[];\n"
591 << "layout (location=1) out float outRedOffset[];\n"
592 << (checkDrawParams ? "layout (location=3) out int outDrawIndex[];\n" : "")
593 << (checkDrawParams ? "layout (location=4) out int outBaseVertex[];\n" : "")
594 << (checkDrawParams ? "layout (location=5) out int outBaseInstance[];\n" : "")
595 << (flip ? "layout (location=2) in float inXCoordFactor[gl_MaxPatchVertices];\n" : "")
596 << "void main (void)\n"
597 << "{\n"
598 << " const float xCoordFactor = " << (flip ? "inXCoordFactor[gl_InvocationID]" : "1.0") << ";\n"
599 << " gl_TessLevelInner[0] = 1.0;\n"
600 << " gl_TessLevelInner[1] = 1.0;\n"
601 << " gl_TessLevelOuter[0] = 1.0;\n"
602 << " gl_TessLevelOuter[1] = 1.0;\n"
603 << " gl_TessLevelOuter[2] = 1.0;\n"
604 << " gl_TessLevelOuter[3] = 1.0;\n"
605 << " gl_out[gl_InvocationID].gl_Position = vec4(gl_in[gl_InvocationID].gl_Position.x * "
606 "xCoordFactor, gl_in[gl_InvocationID].gl_Position.yzw);\n"
607 << " outInstanceIndex[gl_InvocationID] = inInstanceIndex[gl_InvocationID];\n"
608 << " outRedOffset[gl_InvocationID] = inRedOffset[gl_InvocationID];\n"
609 << (checkDrawParams ? " outDrawIndex[gl_InvocationID] = inDrawIndex[gl_InvocationID];\n" : "")
610 << (checkDrawParams ? " outBaseVertex[gl_InvocationID] = inBaseVertex[gl_InvocationID];\n" : "")
611 << (checkDrawParams ? " outBaseInstance[gl_InvocationID] = inBaseInstance[gl_InvocationID];\n" : "")
612 << "}\n";
613
614 programCollection.glslSources.add(shaderName) << glu::TessellationControlSource(tesc.str());
615 }
616
617 // Tessellation evaluation is always the same.
618 std::ostringstream tese;
619 tese << "#version 460\n"
620 << "layout (triangles, fractional_odd_spacing, cw) in;\n"
621 << "in gl_PerVertex\n"
622 << "{\n"
623 << " vec4 gl_Position;\n"
624 << "} gl_in[gl_MaxPatchVertices];\n"
625 << "out gl_PerVertex\n"
626 << "{\n"
627 << " vec4 gl_Position;\n"
628 << "};\n"
629 << "layout (location=0) in int inInstanceIndex[];\n"
630 << "layout (location=1) in float inRedOffset[];\n"
631 << (checkDrawParams ? "layout (location=3) in int inDrawIndex[];\n" : "")
632 << (checkDrawParams ? "layout (location=4) in int inBaseVertex[];\n" : "")
633 << (checkDrawParams ? "layout (location=5) in int inBaseInstance[];\n" : "")
634 << "layout (location=0) out flat int outInstanceIndex;\n"
635 << "layout (location=1) out flat float outRedOffset;\n"
636 << (checkDrawParams ? "layout (location=3) out flat int outDrawIndex;\n" : "")
637 << (checkDrawParams ? "layout (location=4) out flat int outBaseVertex;\n" : "")
638 << (checkDrawParams ? "layout (location=5) out flat int outBaseInstance;\n" : "") << "void main (void)\n"
639 << "{\n"
640 << " gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +\n"
641 << " (gl_TessCoord.y * gl_in[1].gl_Position) +\n"
642 << " (gl_TessCoord.z * gl_in[2].gl_Position);\n"
643 << " outInstanceIndex = inInstanceIndex[0];\n"
644 << " outRedOffset = inRedOffset[0];\n"
645 << (checkDrawParams ? " outDrawIndex = inDrawIndex[0];\n" : "")
646 << (checkDrawParams ? " outBaseVertex = inBaseVertex[0];\n" : "")
647 << (checkDrawParams ? " outBaseInstance = inBaseInstance[0];\n" : "") << "}\n";
648
649 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(tese.str());
650 }
651 }
652
653 // Generates float values for a color, given a starting point, a maximum value and a step.
654 // E.g. SequentialColorGenerator (start=128, max=255, step=2) generates 128/255, 130/255, 132/255, etc.
655 class SequentialColorGenerator
656 {
657 protected:
658 float m_current;
659 float m_max;
660 float m_step;
661
662 public:
SequentialColorGenerator(uint32_t start,uint32_t max,uint32_t step)663 SequentialColorGenerator(uint32_t start, uint32_t max, uint32_t step)
664 : m_current(static_cast<float>(start))
665 , m_max(static_cast<float>(max))
666 , m_step(static_cast<float>(step))
667 {
668 }
669
operator ()(void)670 float operator()(void)
671 {
672 const float gen = m_current / m_max;
673 m_current += m_step;
674 return gen;
675 }
676 };
677
678 // Creates a shader module if the shader exists.
679 using ShaderWrapperPtr = std::unique_ptr<ShaderWrapper>;
680
maybeCreateModule(const DeviceInterface & vkd,VkDevice device,const BinaryCollection & binaries,const std::string & name)681 ShaderWrapperPtr maybeCreateModule(const DeviceInterface &vkd, VkDevice device, const BinaryCollection &binaries,
682 const std::string &name)
683 {
684 ShaderWrapperPtr shaderPtr;
685
686 if (binaries.contains(name))
687 shaderPtr.reset(new ShaderWrapper(vkd, device, binaries.get(name)));
688 else
689 shaderPtr.reset(new ShaderWrapper());
690
691 return shaderPtr;
692 }
693
maybeCreateShader(const DeviceInterface & vkd,VkDevice device,const BinaryCollection & binaries,const std::string & name,VkShaderStageFlagBits stage,const VkPushConstantRange * pcRange,bool tessFeature,bool geomFeature)694 DGCShaderExtPtr maybeCreateShader(const DeviceInterface &vkd, VkDevice device, const BinaryCollection &binaries,
695 const std::string &name, VkShaderStageFlagBits stage,
696 const VkPushConstantRange *pcRange, bool tessFeature, bool geomFeature)
697 {
698 DGCShaderExtPtr shaderPtr;
699 if (binaries.contains(name))
700 {
701 const std::vector<VkDescriptorSetLayout> setLayouts;
702
703 std::vector<VkPushConstantRange> pcRanges;
704 if (pcRange)
705 pcRanges.push_back(*pcRange);
706
707 shaderPtr.reset(new DGCShaderExt(vkd, device, stage, 0u, binaries.get(name), setLayouts, pcRanges, tessFeature,
708 geomFeature, nullptr, nullptr));
709 }
710 return shaderPtr;
711 }
712
713 using BufferWithMemoryPtr = std::unique_ptr<BufferWithMemory>;
714
715 struct VertexBufferInfo
716 {
717 BufferWithMemoryPtr buffer;
718 VkDeviceAddress address;
719 uint32_t size;
720 uint32_t stride;
721
VertexBufferInfovkt::DGC::__anon47167f390111::VertexBufferInfo722 VertexBufferInfo() : buffer(), address(0ull), size(0u), stride(0u)
723 {
724 }
725 };
726
makeVertexBuffers(const DeviceInterface & vkd,const VkDevice device,Allocator & allocator,const std::vector<tcu::Vec4> & vertices,uint32_t sequenceCount,uint32_t pixelCount)727 std::vector<VertexBufferInfo> makeVertexBuffers(const DeviceInterface &vkd, const VkDevice device, Allocator &allocator,
728 const std::vector<tcu::Vec4> &vertices, uint32_t sequenceCount,
729 uint32_t pixelCount)
730 {
731 // Return vector.
732 std::vector<VertexBufferInfo> ret;
733
734 DE_ASSERT(sequenceCount == 1u || sequenceCount == 3u); // We don't know how to do more cases yet.
735
736 const auto vertexBufferUsage = (VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
737 const auto vertexBufferMemReqs = (MemoryRequirement::HostVisible | MemoryRequirement::DeviceAddress);
738 const auto vertexSize = static_cast<uint32_t>(sizeof(std::remove_reference<decltype(vertices)>::type::value_type));
739
740 if (sequenceCount == 1u)
741 {
742 // Flat buffer containing all vertices.
743 const auto vertexBufferSize = static_cast<VkDeviceSize>(de::dataSize(vertices));
744 const auto vertexBufferInfo = makeBufferCreateInfo(vertexBufferSize, vertexBufferUsage);
745 BufferWithMemoryPtr vertexBuffer(
746 new BufferWithMemory(vkd, device, allocator, vertexBufferInfo, vertexBufferMemReqs));
747
748 auto &vertexBufferAlloc = vertexBuffer->getAllocation();
749 void *vertexBufferData = vertexBufferAlloc.getHostPtr();
750
751 deMemcpy(vertexBufferData, de::dataOrNull(vertices), de::dataSize(vertices));
752
753 ret.resize(1u);
754 auto &bufferInfo = ret.at(0u);
755
756 bufferInfo.buffer.reset(vertexBuffer.release());
757 bufferInfo.address = getBufferDeviceAddress(vkd, device, bufferInfo.buffer->get());
758 bufferInfo.size = static_cast<uint32_t>(vertexBufferSize);
759 bufferInfo.stride = vertexSize;
760 }
761 else
762 {
763 // Vertex buffers: one per sequence, with the first one containing the
764 // first triangle and the last one containg the last triangle.
765 std::vector<BufferWithMemoryPtr> vertexBuffers(sequenceCount);
766
767 const auto vtxBufferStrideNormal = vertexSize;
768 const auto vtxBufferStrideWide = vtxBufferStrideNormal * 2u;
769
770 const std::vector<uint32_t> vertexBufferSizes{
771 vtxBufferStrideNormal * kPerTriangleVertices,
772 vtxBufferStrideWide * kPerTriangleVertices * (pixelCount - 2u), // Wider stride and 2 triangles.
773 vtxBufferStrideNormal * kPerTriangleVertices * pixelCount, // Large vertex offset.
774 };
775
776 const std::vector<uint32_t> vertexBufferStrides{
777 vtxBufferStrideNormal,
778 vtxBufferStrideWide, // The second vertex buffer has a wider stride. See long comment above.
779 vtxBufferStrideNormal,
780 };
781
782 for (uint32_t i = 0u; i < sequenceCount; ++i)
783 {
784 const auto vertexBufferCreateInfo = makeBufferCreateInfo(vertexBufferSizes.at(i), vertexBufferUsage);
785 vertexBuffers.at(i).reset(
786 new BufferWithMemory(vkd, device, allocator, vertexBufferCreateInfo, vertexBufferMemReqs));
787 }
788
789 // Device addresses for the vertex buffers.
790 std::vector<VkDeviceAddress> vertexBufferAddresses;
791 vertexBufferAddresses.reserve(vertexBuffers.size());
792 std::transform(begin(vertexBuffers), end(vertexBuffers), std::back_inserter(vertexBufferAddresses),
793 [&vkd, &device](const BufferWithMemoryPtr &vtxBuffer)
794 { return getBufferDeviceAddress(vkd, device, vtxBuffer->get()); });
795
796 std::vector<tcu::Vec4 *> vertexBufferDataPtrs;
797 vertexBufferDataPtrs.reserve(vertexBuffers.size());
798 std::transform(begin(vertexBuffers), end(vertexBuffers), std::back_inserter(vertexBufferDataPtrs),
799 [](const BufferWithMemoryPtr &bufferPtr)
800 { return reinterpret_cast<tcu::Vec4 *>(bufferPtr->getAllocation().getHostPtr()); });
801
802 const tcu::Vec4 zeroedVertex(0.0f, 0.0f, 0.0f, 0.0f);
803
804 // First vertex buffer.
805 uint32_t nextVertex = 0u;
806 for (uint32_t i = 0u; i < kPerTriangleVertices; ++i)
807 vertexBufferDataPtrs.at(0u)[i] = vertices.at(nextVertex++);
808
809 // Second vertex buffer.
810 {
811 uint32_t nextPos = 0u;
812 for (uint32_t i = 0u; i < pixelCount - 2u /*remove the first and last triangles*/; ++i)
813 for (uint32_t j = 0u; j < kPerTriangleVertices; ++j)
814 {
815 vertexBufferDataPtrs.at(1u)[nextPos++] = vertices.at(nextVertex++);
816 vertexBufferDataPtrs.at(1u)[nextPos++] =
817 zeroedVertex; // Padding between vertices for the wider stride.
818 }
819 }
820
821 // Third vertex buffer.
822 {
823 uint32_t nextPos = 0u;
824
825 // Padding at the beginning.
826 for (uint32_t i = 0u; i < nextVertex; ++i)
827 vertexBufferDataPtrs.at(2u)[nextPos++] = zeroedVertex;
828
829 // Vertices for triangle D.
830 for (uint32_t i = 0u; i < kPerTriangleVertices; ++i)
831 vertexBufferDataPtrs.at(2u)[nextPos++] = vertices.at(nextVertex++);
832 }
833
834 // Prepare return vector.
835 ret.resize(sequenceCount);
836
837 for (uint32_t i = 0u; i < sequenceCount; ++i)
838 {
839 auto &bufferInfo = ret.at(i);
840
841 bufferInfo.buffer.reset(vertexBuffers.at(i).release());
842 bufferInfo.address = vertexBufferAddresses.at(i);
843 bufferInfo.size = vertexBufferSizes.at(i);
844 bufferInfo.stride = vertexBufferStrides.at(i);
845 }
846 }
847
848 return ret;
849 }
850
851 struct IndexBufferInfo
852 {
853 BufferWithMemoryPtr buffer;
854 VkDeviceAddress address;
855 uint32_t size;
856 VkIndexType indexType;
857 int32_t vertexOffset;
858 };
859
makeIndexBuffers(const DeviceInterface & vkd,const VkDevice device,Allocator & allocator,uint32_t sequenceCount,uint32_t pixelCount)860 std::vector<IndexBufferInfo> makeIndexBuffers(const DeviceInterface &vkd, const VkDevice device, Allocator &allocator,
861 uint32_t sequenceCount, uint32_t pixelCount)
862 {
863 DE_ASSERT(sequenceCount == 0u || sequenceCount == 3u);
864
865 std::vector<IndexBufferInfo> ret;
866 if (sequenceCount == 0u)
867 return ret;
868
869 const auto indexBufferUsage = (VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
870 const auto indexBufferMemReqs = (MemoryRequirement::HostVisible | MemoryRequirement::DeviceAddress);
871
872 // Buffer contents.
873 constexpr uint16_t kInvalidIndex16 = std::numeric_limits<uint16_t>::max() / uint16_t{2};
874 const uint32_t indexCountMiddle = (pixelCount - 2u) * kPerTriangleVertices;
875
876 std::vector<uint32_t> firstBuffer;
877 firstBuffer.reserve(kPerTriangleVertices);
878 for (uint32_t i = 0u; i < kPerTriangleVertices; ++i)
879 firstBuffer.push_back(i);
880
881 std::vector<uint32_t> secondBuffer;
882 secondBuffer.reserve(indexCountMiddle);
883 for (uint32_t i = 0u; i < indexCountMiddle; ++i)
884 secondBuffer.push_back(i + kPerTriangleVertices);
885
886 std::vector<uint16_t> thirdBuffer;
887 thirdBuffer.reserve(pixelCount * kPerTriangleVertices);
888
889 const uint32_t prevVertexCount = (pixelCount - 1u) * kPerTriangleVertices;
890 const uint16_t vertexOffset = 20;
891
892 for (uint32_t i = 0u; i < prevVertexCount; ++i)
893 thirdBuffer.push_back(kInvalidIndex16);
894 for (uint32_t i = 0u; i < kPerTriangleVertices; ++i)
895 thirdBuffer.push_back(static_cast<uint16_t>(i + prevVertexCount + vertexOffset));
896
897 // Data pointers.
898 const std::vector<void *> bufferDataPtrs{
899 de::dataOrNull(firstBuffer),
900 de::dataOrNull(secondBuffer),
901 de::dataOrNull(thirdBuffer),
902 };
903
904 // Buffer sizes.
905 const std::vector<uint32_t> bufferSizes{
906 static_cast<uint32_t>(de::dataSize(firstBuffer)),
907 static_cast<uint32_t>(de::dataSize(secondBuffer)),
908 static_cast<uint32_t>(de::dataSize(thirdBuffer)),
909 };
910
911 // Index types.
912 const std::vector<VkIndexType> indexTypes{
913 VK_INDEX_TYPE_UINT32,
914 VK_INDEX_TYPE_UINT32,
915 VK_INDEX_TYPE_UINT16,
916 };
917
918 // Actual buffers.
919 std::vector<BufferWithMemoryPtr> indexBuffers;
920 indexBuffers.reserve(sequenceCount);
921 for (uint32_t i = 0u; i < sequenceCount; ++i)
922 {
923 const auto createInfo = makeBufferCreateInfo(static_cast<VkDeviceSize>(bufferSizes.at(i)), indexBufferUsage);
924 indexBuffers.emplace_back(new BufferWithMemory(vkd, device, allocator, createInfo, indexBufferMemReqs));
925
926 auto &alloc = indexBuffers.back()->getAllocation();
927 void *dataPtr = alloc.getHostPtr();
928 deMemcpy(dataPtr, bufferDataPtrs.at(i), static_cast<size_t>(bufferSizes.at(i)));
929 flushAlloc(vkd, device, alloc);
930 }
931
932 // Device addresses.
933 std::vector<VkDeviceAddress> addresses;
934 addresses.reserve(sequenceCount);
935
936 for (uint32_t i = 0u; i < sequenceCount; ++i)
937 addresses.push_back(getBufferDeviceAddress(vkd, device, indexBuffers.at(i)->get()));
938
939 // Vertex offsets.
940 const std::vector<int32_t> vertexOffsets{0, 0, -vertexOffset};
941
942 ret.resize(sequenceCount);
943
944 for (uint32_t i = 0u; i < sequenceCount; ++i)
945 {
946 auto &bufferInfo = ret.at(i);
947
948 bufferInfo.buffer.reset(indexBuffers.at(i).release());
949 bufferInfo.address = addresses.at(i);
950 bufferInfo.size = bufferSizes.at(i);
951 bufferInfo.indexType = indexTypes.at(i);
952 bufferInfo.vertexOffset = vertexOffsets.at(i);
953 }
954
955 return ret;
956 }
957
iterate(void)958 tcu::TestStatus DGCDrawInstance::iterate(void)
959 {
960 const auto ctx = m_context.getContextCommonData();
961 const tcu::IVec3 fbExtent(2, 2, 1);
962 const tcu::Vec3 floatExtent = fbExtent.asFloat();
963 const auto pixelCountI = fbExtent.x() * fbExtent.y() * fbExtent.z();
964 const auto vkExtent = makeExtent3D(fbExtent);
965 const auto pixelCountU = vkExtent.width * vkExtent.height * vkExtent.depth;
966 const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
967 const auto colorUsage =
968 (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
969 const auto sequenceCount = 3u;
970 const auto stageFlags = m_params.getStageFlags();
971 const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
972
973 // Color buffer.
974 ImageWithBuffer colorBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage,
975 VK_IMAGE_TYPE_2D);
976
977 // Vertex data: 1 triangle per pixel.
978 std::vector<tcu::Vec4> vertices;
979 vertices.reserve(pixelCountI * kPerTriangleVertices);
980
981 const float pixWidth = 2.0f / floatExtent.x();
982 const float pixHeight = 2.0f / floatExtent.y();
983 const float horMargin = pixWidth / 4.0f;
984 const float verMargin = pixHeight / 4.0f;
985
986 for (int y = 0; y < fbExtent.y(); ++y)
987 for (int x = 0; x < fbExtent.x(); ++x)
988 {
989 const float xCenter = ((static_cast<float>(x) + 0.5f) / floatExtent.x() * 2.0f) - 1.0f;
990 const float yCenter = ((static_cast<float>(y) + 0.5f) / floatExtent.y() * 2.0f) - 1.0f;
991
992 vertices.emplace_back(xCenter - horMargin, yCenter + verMargin, 0.0f, 1.0f);
993 vertices.emplace_back(xCenter + horMargin, yCenter + verMargin, 0.0f, 1.0f);
994 vertices.emplace_back(xCenter, yCenter - verMargin, 0.0f, 1.0f);
995 }
996
997 // Create vertex and index buffers.
998 const auto vertSeqCount = (m_params.testType == TestType::DRAW ? sequenceCount : 1u);
999 const auto indexSeqCount = (isIndexed(m_params.testType) ? sequenceCount : 0u);
1000
1001 const auto vertexBuffers =
1002 makeVertexBuffers(ctx.vkd, ctx.device, ctx.allocator, vertices, vertSeqCount, pixelCountU);
1003 const auto indexBuffers = makeIndexBuffers(ctx.vkd, ctx.device, ctx.allocator, indexSeqCount, pixelCountU);
1004
1005 // Push constants.
1006 const auto drawParamsCount = (m_params.checkDrawParams ? 3u /*DrawIndex, BaseVertex, BaseInstance*/ : 0u);
1007 const auto pcStages = (VK_SHADER_STAGE_FRAGMENT_BIT);
1008 const auto pcSize = DE_SIZEOF32(float) + // Must match fragment shader.
1009 drawParamsCount * DE_SIZEOF32(int32_t);
1010 const auto pcRange = makePushConstantRange(pcStages, 0u, pcSize);
1011
1012 // Pipeline layout. Note the wrapper only needs to know if it uses shader objects or not. The specific type is not
1013 // important as long as the category is correct.
1014 PipelineLayoutWrapper pipelineLayout((m_params.isShaderObjects() ?
1015 PIPELINE_CONSTRUCTION_TYPE_SHADER_OBJECT_UNLINKED_SPIRV :
1016 PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC),
1017 ctx.vkd, ctx.device, VK_NULL_HANDLE, &pcRange);
1018
1019 const uint32_t randomSeed =
1020 ((static_cast<uint32_t>(m_params.extraStages) << 24) | (static_cast<uint32_t>(m_params.checkDrawParams) << 16) |
1021 (static_cast<uint32_t>(m_params.useExecutionSet) << 15));
1022 de::Random rnd(randomSeed);
1023
1024 // Indirect commands layout.
1025 VkIndirectCommandsLayoutUsageFlagsEXT layoutFlags = 0u;
1026 if (m_params.doPreprocess())
1027 layoutFlags |= VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT;
1028 if (m_params.unorderedSequences)
1029 layoutFlags |= VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_EXT;
1030
1031 IndirectCommandsLayoutBuilderExt cmdsLayoutBuilder(layoutFlags, stageFlags, *pipelineLayout);
1032 if (m_params.useExecutionSet)
1033 {
1034 const auto infoType = (m_params.isShaderObjects() ? VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT :
1035 VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT);
1036 cmdsLayoutBuilder.addExecutionSetToken(0u, infoType, stageFlags);
1037 }
1038
1039 const bool pcFirst = rnd.getBool();
1040
1041 if (pcFirst)
1042 cmdsLayoutBuilder.addPushConstantToken(cmdsLayoutBuilder.getStreamRange(), pcRange);
1043
1044 if (m_params.testType == TestType::DRAW)
1045 {
1046 cmdsLayoutBuilder.addVertexBufferToken(cmdsLayoutBuilder.getStreamRange(), 0u);
1047 }
1048 else if (isIndexed(m_params.testType))
1049 {
1050 const auto mode = m_params.getInputModeFlags();
1051 {
1052 constexpr auto kBitCount = sizeof(VkIndirectCommandsInputModeFlagsEXT) * 8u;
1053 const std::bitset<kBitCount> bitMask(mode);
1054 DE_UNREF(bitMask); // For release mode.
1055 DE_ASSERT(bitMask.count() == 1u);
1056 }
1057 const auto modeBits = static_cast<VkIndirectCommandsInputModeFlagBitsEXT>(mode);
1058 cmdsLayoutBuilder.addIndexBufferToken(cmdsLayoutBuilder.getStreamRange(), modeBits);
1059 }
1060 else if (m_params.testType == TestType::DRAW_SIMPLE)
1061 ; // No vertex or index buffer tokens.
1062 else
1063 DE_ASSERT(false);
1064
1065 if (!pcFirst)
1066 cmdsLayoutBuilder.addPushConstantToken(cmdsLayoutBuilder.getStreamRange(), pcRange);
1067
1068 if (m_params.testType == TestType::DRAW || m_params.testType == TestType::DRAW_SIMPLE)
1069 cmdsLayoutBuilder.addDrawToken(cmdsLayoutBuilder.getStreamRange());
1070 else if (isIndexed(m_params.testType))
1071 cmdsLayoutBuilder.addDrawIndexedToken(cmdsLayoutBuilder.getStreamRange());
1072 else
1073 DE_ASSERT(false);
1074
1075 const auto cmdsLayout = cmdsLayoutBuilder.build(ctx.vkd, ctx.device);
1076
1077 // Device-generated commands data.
1078 std::vector<uint32_t> dgcData;
1079 dgcData.reserve((sequenceCount * cmdsLayoutBuilder.getStreamStride()) / sizeof(uint32_t));
1080
1081 // Red color values.
1082 SequentialColorGenerator colorGenerator(128u, 255u, 5u);
1083 std::vector<float> redValues(sequenceCount, -1.0f);
1084 std::generate(begin(redValues), end(redValues), colorGenerator);
1085
1086 // Draw commands.
1087 const auto middleVertexCount = (pixelCountU - 2u) * kPerTriangleVertices; // For the second sequence.
1088 const auto firstVertexAtEnd = de::sizeU32(vertices) - kPerTriangleVertices; // First vertex for the final sequence.
1089
1090 std::vector<VkDrawIndirectCommand> drawCmds;
1091 std::vector<VkDrawIndexedIndirectCommand> drawIndexedCmds;
1092
1093 if (m_params.testType == TestType::DRAW)
1094 {
1095 drawCmds.reserve(sequenceCount);
1096
1097 // For the rationale behind these numbers, see the explanation above. The idea is to vary every number.
1098 drawCmds.push_back(VkDrawIndirectCommand{kPerTriangleVertices, 1u, 0u, 0u});
1099 drawCmds.push_back(VkDrawIndirectCommand{middleVertexCount, 1u, 0u, 1u});
1100 drawCmds.push_back(VkDrawIndirectCommand{kPerTriangleVertices, 2u, firstVertexAtEnd, 0u});
1101 }
1102 else if (m_params.testType == TestType::DRAW_SIMPLE)
1103 {
1104 drawCmds.reserve(sequenceCount);
1105
1106 // Alternative to the one above with a single vertex buffer, so the middle draw uses a different firstVertex.
1107 drawCmds.push_back(VkDrawIndirectCommand{kPerTriangleVertices, 1u, 0u, 0u});
1108 drawCmds.push_back(VkDrawIndirectCommand{middleVertexCount, 1u, kPerTriangleVertices, 1u});
1109 drawCmds.push_back(VkDrawIndirectCommand{kPerTriangleVertices, 2u, firstVertexAtEnd, 0u});
1110 }
1111 else if (isIndexed(m_params.testType))
1112 {
1113 drawIndexedCmds.reserve(sequenceCount);
1114
1115 const std::vector<int32_t> offsets{
1116 indexBuffers.at(0).vertexOffset,
1117 indexBuffers.at(1).vertexOffset,
1118 indexBuffers.at(2).vertexOffset,
1119 };
1120
1121 // For the rationale behind these numbers, see the explanation above. The idea is to vary every number.
1122 drawIndexedCmds.push_back(VkDrawIndexedIndirectCommand{kPerTriangleVertices, 1u, 0u, offsets.at(0), 0u});
1123 drawIndexedCmds.push_back(VkDrawIndexedIndirectCommand{middleVertexCount, 1u, 0u, offsets.at(1), 1u});
1124 drawIndexedCmds.push_back(
1125 VkDrawIndexedIndirectCommand{kPerTriangleVertices, 2u, firstVertexAtEnd, offsets.at(2), 0u});
1126 }
1127 else
1128 DE_ASSERT(false);
1129
1130 std::vector<VkBindVertexBufferIndirectCommandEXT> bindVertexBufferCmds;
1131 std::vector<VkBindIndexBufferIndirectCommandEXT> bindIndexBufferCmds;
1132
1133 if (m_params.testType == TestType::DRAW)
1134 {
1135 bindVertexBufferCmds.reserve(sequenceCount);
1136
1137 for (uint32_t i = 0u; i < sequenceCount; ++i)
1138 {
1139 bindVertexBufferCmds.emplace_back(VkBindVertexBufferIndirectCommandEXT{
1140 vertexBuffers.at(i).address,
1141 vertexBuffers.at(i).size,
1142 vertexBuffers.at(i).stride,
1143 });
1144 }
1145 }
1146 else if (isIndexed(m_params.testType))
1147 {
1148 bindIndexBufferCmds.reserve(sequenceCount);
1149
1150 for (uint32_t i = 0u; i < sequenceCount; ++i)
1151 {
1152 if (m_params.testType == TestType::DRAW_INDEXED)
1153 {
1154 bindIndexBufferCmds.emplace_back(VkBindIndexBufferIndirectCommandEXT{
1155 indexBuffers.at(i).address,
1156 indexBuffers.at(i).size,
1157 indexBuffers.at(i).indexType,
1158 });
1159 }
1160 else
1161 {
1162 IndexBufferViewD3D12 cmd(indexBuffers.at(i).address, indexBuffers.at(i).size,
1163 indexBuffers.at(i).indexType);
1164 pushBackElement(bindIndexBufferCmds, cmd);
1165 }
1166 }
1167 }
1168 else if (m_params.testType == TestType::DRAW_SIMPLE)
1169 ; // No vertex or index buffer bind tokens.
1170 else
1171 DE_ASSERT(false);
1172
1173 // Closure to avoid code duplication.
1174 const auto pushPushConstants = [&dgcData, &redValues, &drawCmds, &drawIndexedCmds, this](uint32_t i)
1175 {
1176 pushBackElement(dgcData, redValues.at(i));
1177 if (this->m_params.checkDrawParams)
1178 {
1179 pushBackElement(dgcData, static_cast<int32_t>(0)); // For non-count commands, DrawIndex stays at 0.
1180
1181 const auto &testType = this->m_params.testType;
1182
1183 if (testType == TestType::DRAW || testType == TestType::DRAW_SIMPLE)
1184 {
1185 pushBackElement(dgcData, drawCmds.at(i).firstVertex);
1186 pushBackElement(dgcData, drawCmds.at(i).firstInstance);
1187 }
1188 else if (isIndexed(this->m_params.testType))
1189 {
1190 pushBackElement(dgcData, drawIndexedCmds.at(i).vertexOffset);
1191 pushBackElement(dgcData, drawIndexedCmds.at(i).firstInstance);
1192 }
1193 else
1194 DE_ASSERT(false);
1195 }
1196 };
1197
1198 // Rationale behind execution sets
1199 //
1200 // For pipelines, we'll create a different pipeline per sequence with the
1201 // right shaders, and store them in order in the execution set. This means
1202 // sequence i will use element i in the execution set.
1203 //
1204 // For shader objects, the execution set will contain 2 vertex shaders and 2
1205 // fragment shaders (plus optionally 2 tessellation control shaders and 1
1206 // tessellation evaluation shader, or 2 geometry shaders). For stages with 2
1207 // shaders, the first one will be the "normal" one and the second one will
1208 // be the "alternative" one, so in each sequence we need a different set of
1209 // numbers that will match what we'll be using for pipelines, which means,
1210 // per stage:
1211 //
1212 // vert: 1 1 0 (flip, flip, normal)
1213 // tesc: 1 1 0 (flip, flip, normal)
1214 // tese: 0 0 0 (we only have 1)
1215 // geom: 1 1 0 (flip, flip, normal)
1216 // frag: 0 1 1 (normal, alt, alt)
1217 //
1218 // However, as each shader needs to have a unique index, we'll offset those
1219 // values by a base value calculated according to the stages we will be
1220 // using.
1221 //
1222 // Also, in the indirect commands buffer, the indices for a sequence need to
1223 // appear in the order of the stages in the pipeline.
1224 //
1225
1226 // Base unique indices for each stage.
1227 const auto invalidIndex = std::numeric_limits<uint32_t>::max() / 2u; // Divided by 2 to avoid overflows.
1228 const auto vertBaseIndex = 0u;
1229 const auto fragBaseIndex = 2u;
1230 auto tescBaseIndex = invalidIndex;
1231 auto teseBaseIndex = invalidIndex;
1232 auto geomBaseIndex = invalidIndex;
1233
1234 if (m_params.extraStages == ExtraStages::TESSELLATION)
1235 {
1236 tescBaseIndex = 4u;
1237 teseBaseIndex = 6u;
1238 }
1239 if (m_params.extraStages == ExtraStages::GEOMETRY)
1240 geomBaseIndex = 4u;
1241
1242 const std::vector<uint32_t> vertIndicesESO{vertBaseIndex + 1u, vertBaseIndex + 1u, vertBaseIndex + 0u};
1243 const std::vector<uint32_t> tescIndicesESO{tescBaseIndex + 1u, tescBaseIndex + 1u, tescBaseIndex + 0u};
1244 const std::vector<uint32_t> teseIndicesESO{teseBaseIndex + 0u, teseBaseIndex + 0u, teseBaseIndex + 0u};
1245 const std::vector<uint32_t> geomIndicesESO{geomBaseIndex + 1u, geomBaseIndex + 1u, geomBaseIndex + 0u};
1246 const std::vector<uint32_t> fragIndicesESO{fragBaseIndex + 0u, fragBaseIndex + 1u, fragBaseIndex + 1u};
1247
1248 for (uint32_t i = 0u; i < sequenceCount; ++i)
1249 {
1250 if (m_params.useExecutionSet)
1251 {
1252 if (m_params.isShaderObjects())
1253 {
1254 pushBackElement(dgcData, vertIndicesESO.at(i));
1255 if (m_params.extraStages == ExtraStages::TESSELLATION)
1256 {
1257 pushBackElement(dgcData, tescIndicesESO.at(i));
1258 pushBackElement(dgcData, teseIndicesESO.at(i));
1259 }
1260 if (m_params.extraStages == ExtraStages::GEOMETRY)
1261 pushBackElement(dgcData, geomIndicesESO.at(i));
1262 pushBackElement(dgcData, fragIndicesESO.at(i));
1263 }
1264 else
1265 pushBackElement(dgcData, i);
1266 }
1267
1268 if (pcFirst)
1269 pushPushConstants(i);
1270
1271 if (m_params.testType == TestType::DRAW)
1272 pushBackElement(dgcData, bindVertexBufferCmds.at(i));
1273 else if (isIndexed(m_params.testType))
1274 pushBackElement(dgcData, bindIndexBufferCmds.at(i));
1275 else if (m_params.testType == TestType::DRAW_SIMPLE)
1276 ; // No vertex or index buffer bind tokens.
1277 else
1278 DE_ASSERT(false);
1279
1280 if (!pcFirst)
1281 pushPushConstants(i);
1282
1283 if (m_params.testType == TestType::DRAW || m_params.testType == TestType::DRAW_SIMPLE)
1284 pushBackElement(dgcData, drawCmds.at(i));
1285 else if (isIndexed(m_params.testType))
1286 pushBackElement(dgcData, drawIndexedCmds.at(i));
1287 else
1288 DE_ASSERT(false);
1289 }
1290
1291 // Buffer holding the device-generated commands.
1292 const auto dgcBufferSize = static_cast<VkDeviceSize>(de::dataSize(dgcData));
1293 DGCBuffer dgcBuffer(ctx.vkd, ctx.device, ctx.allocator, dgcBufferSize);
1294
1295 auto &dgcBufferAlloc = dgcBuffer.getAllocation();
1296 void *dgcBufferDataPtr = dgcBufferAlloc.getHostPtr();
1297
1298 deMemcpy(dgcBufferDataPtr, de::dataOrNull(dgcData), de::dataSize(dgcData));
1299 flushAlloc(ctx.vkd, ctx.device, dgcBufferAlloc);
1300
1301 // Prepare single pipeline, shaders or indirect execution set.
1302 const auto &binaries = m_context.getBinaryCollection();
1303
1304 ShaderWrapperPtr vertNormal;
1305 ShaderWrapperPtr vertFlip;
1306 ShaderWrapperPtr tescNormal;
1307 ShaderWrapperPtr tescFlip;
1308 ShaderWrapperPtr tese;
1309 ShaderWrapperPtr geomNormal;
1310 ShaderWrapperPtr geomFlip;
1311 ShaderWrapperPtr fragNormal;
1312 ShaderWrapperPtr fragAlt;
1313
1314 DGCShaderExtPtr vertNormalShader;
1315 DGCShaderExtPtr vertFlipShader;
1316 DGCShaderExtPtr tescNormalShader;
1317 DGCShaderExtPtr tescFlipShader;
1318 DGCShaderExtPtr teseShader;
1319 DGCShaderExtPtr geomNormalShader;
1320 DGCShaderExtPtr geomFlipShader;
1321 DGCShaderExtPtr fragNormalShader;
1322 DGCShaderExtPtr fragAltShader;
1323
1324 const auto &meshFeatures = m_context.getMeshShaderFeaturesEXT();
1325 const auto &features = m_context.getDeviceFeatures();
1326
1327 const auto tessFeature = (features.tessellationShader == VK_TRUE);
1328 const auto geomFeature = (features.geometryShader == VK_TRUE);
1329
1330 if (m_params.isShaderObjects())
1331 {
1332 vertNormalShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "vert_normal", VK_SHADER_STAGE_VERTEX_BIT,
1333 nullptr, tessFeature, geomFeature);
1334 vertFlipShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "vert_flip", VK_SHADER_STAGE_VERTEX_BIT,
1335 nullptr, tessFeature, geomFeature);
1336 tescNormalShader =
1337 maybeCreateShader(ctx.vkd, ctx.device, binaries, "tesc_normal", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
1338 nullptr, tessFeature, geomFeature);
1339 tescFlipShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "tesc_flip",
1340 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, nullptr, tessFeature, geomFeature);
1341 teseShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "tese",
1342 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, nullptr, tessFeature, geomFeature);
1343 geomNormalShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "geom_normal", VK_SHADER_STAGE_GEOMETRY_BIT,
1344 nullptr, tessFeature, geomFeature);
1345 geomFlipShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "geom_flip", VK_SHADER_STAGE_GEOMETRY_BIT,
1346 nullptr, tessFeature, geomFeature);
1347 fragNormalShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "frag_normal", VK_SHADER_STAGE_FRAGMENT_BIT,
1348 &pcRange, tessFeature, geomFeature);
1349 fragAltShader = maybeCreateShader(ctx.vkd, ctx.device, binaries, "frag_alt", VK_SHADER_STAGE_FRAGMENT_BIT,
1350 &pcRange, tessFeature, geomFeature);
1351 }
1352 else
1353 {
1354 vertNormal = maybeCreateModule(ctx.vkd, ctx.device, binaries, "vert_normal");
1355 vertFlip = maybeCreateModule(ctx.vkd, ctx.device, binaries, "vert_flip");
1356 tescNormal = maybeCreateModule(ctx.vkd, ctx.device, binaries, "tesc_normal");
1357 tescFlip = maybeCreateModule(ctx.vkd, ctx.device, binaries, "tesc_flip");
1358 tese = maybeCreateModule(ctx.vkd, ctx.device, binaries, "tese");
1359 geomNormal = maybeCreateModule(ctx.vkd, ctx.device, binaries, "geom_normal");
1360 geomFlip = maybeCreateModule(ctx.vkd, ctx.device, binaries, "geom_flip");
1361 fragNormal = maybeCreateModule(ctx.vkd, ctx.device, binaries, "frag_normal");
1362 fragAlt = maybeCreateModule(ctx.vkd, ctx.device, binaries, "frag_alt");
1363 }
1364
1365 Move<VkRenderPass> renderPass;
1366 Move<VkFramebuffer> framebuffer;
1367
1368 if (!m_params.isShaderObjects())
1369 {
1370 renderPass = makeRenderPass(ctx.vkd, ctx.device, colorFormat);
1371 framebuffer = makeFramebuffer(ctx.vkd, ctx.device, *renderPass, colorBuffer.getImageView(), vkExtent.width,
1372 vkExtent.height);
1373 }
1374
1375 const std::vector<VkViewport> viewports(1u, makeViewport(vkExtent));
1376 const std::vector<VkRect2D> scissors(1u, makeRect2D(vkExtent));
1377
1378 const bool hasTessellation = (m_params.extraStages == ExtraStages::TESSELLATION);
1379 const auto primitiveTopology =
1380 (hasTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
1381 const auto patchControlPoints = (hasTessellation ? 3u : 0u);
1382
1383 Move<VkPipeline> normalPipeline;
1384
1385 using GraphicsPipelineWrapperPtr = std::unique_ptr<GraphicsPipelineWrapper>;
1386 std::vector<GraphicsPipelineWrapperPtr> dgcPipelines;
1387
1388 const auto vertexBinding =
1389 makeVertexInputBindingDescription(0u, 0u /*stride will come from DGC*/, VK_VERTEX_INPUT_RATE_VERTEX);
1390 const auto vertexAttrib = makeVertexInputAttributeDescription(0u, 0u, vk::VK_FORMAT_R32G32B32A32_SFLOAT, 0u);
1391
1392 const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {
1393 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
1394 nullptr, // const void* pNext;
1395 0u, // VkPipelineVertexInputStateCreateFlags flags;
1396 1u, // uint32_t vertexBindingDescriptionCount;
1397 &vertexBinding, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
1398 1u, // uint32_t vertexAttributeDescriptionCount;
1399 &vertexAttrib, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
1400 };
1401
1402 const std::vector<VkDynamicState> dynamicStates{VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE};
1403
1404 const VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
1405 VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
1406 nullptr, // const void* pNext;
1407 0u, // VkPipelineDynamicStateCreateFlags flags;
1408 de::sizeU32(dynamicStates), // uint32_t dynamicStateCount;
1409 de::dataOrNull(dynamicStates), // const VkDynamicState* pDynamicStates;
1410 };
1411
1412 // Prepare indirect execution set at the same time as the pipelines.
1413 ExecutionSetManagerPtr executionSetManager;
1414
1415 if (m_params.useExecutionSet)
1416 {
1417 if (m_params.isShaderObjects())
1418 {
1419 const std::vector<VkDescriptorSetLayout> noSetLayouts; // No set layouts being used in these tests.
1420 std::vector<IESStageInfo> stageInfos;
1421 uint32_t maxShaderCount = 0u;
1422
1423 stageInfos.reserve(5u); // Potentially vert, tesc, tese, geom, frag.
1424
1425 const auto addStage = [&maxShaderCount, &stageInfos, &noSetLayouts](VkShaderEXT shader, uint32_t maxShaders)
1426 {
1427 stageInfos.emplace_back(IESStageInfo{shader, noSetLayouts});
1428 maxShaderCount += maxShaders;
1429 };
1430
1431 addStage(vertNormalShader->get(), 2u);
1432 addStage(fragNormalShader->get(), 2u);
1433
1434 if (m_params.extraStages == ExtraStages::TESSELLATION)
1435 {
1436 addStage(tescNormalShader->get(), 2u);
1437 addStage(teseShader->get(), 1u);
1438 }
1439
1440 if (m_params.extraStages == ExtraStages::GEOMETRY)
1441 addStage(geomNormalShader->get(), 2u);
1442
1443 const std::vector<vk::VkPushConstantRange> pcRanges(1u, pcRange);
1444
1445 // Execution set for shader objects. Note we store the normal shader
1446 // with index 0 and the alternative with index 1. This matches the
1447 // rationale we're following for shader objects described before,
1448 // and the expected contents of the indirect commands buffer.
1449 {
1450 executionSetManager =
1451 makeExecutionSetManagerShader(ctx.vkd, ctx.device, stageInfos, pcRanges, maxShaderCount);
1452
1453 executionSetManager->addShader(vertBaseIndex + 0u, vertNormalShader->get());
1454 executionSetManager->addShader(vertBaseIndex + 1u, vertFlipShader->get());
1455
1456 executionSetManager->addShader(fragBaseIndex + 0u, fragNormalShader->get());
1457 executionSetManager->addShader(fragBaseIndex + 1u, fragAltShader->get());
1458
1459 if (m_params.extraStages == ExtraStages::TESSELLATION)
1460 {
1461 executionSetManager->addShader(tescBaseIndex + 0u, tescNormalShader->get());
1462 executionSetManager->addShader(tescBaseIndex + 1u, tescFlipShader->get());
1463
1464 executionSetManager->addShader(teseBaseIndex + 0u, teseShader->get());
1465 }
1466
1467 if (m_params.extraStages == ExtraStages::GEOMETRY)
1468 {
1469 executionSetManager->addShader(geomBaseIndex + 0u, geomNormalShader->get());
1470 executionSetManager->addShader(geomBaseIndex + 1u, geomFlipShader->get());
1471 }
1472
1473 executionSetManager->update();
1474 }
1475 }
1476 else
1477 {
1478 dgcPipelines.resize(sequenceCount);
1479
1480 const auto initialValue = getGeneralConstructionType(m_params.pipelineType);
1481 std::vector<PipelineConstructionType> constructionTypes(sequenceCount, initialValue);
1482
1483 if (m_params.pipelineType == PipelineType::GPL_MIX_BASE_OPT)
1484 constructionTypes.at(1u) = PIPELINE_CONSTRUCTION_TYPE_FAST_LINKED_LIBRARY;
1485 else if (m_params.pipelineType == PipelineType::GPL_MIX_BASE_FAST)
1486 constructionTypes.at(1u) = PIPELINE_CONSTRUCTION_TYPE_LINK_TIME_OPTIMIZED_LIBRARY;
1487
1488 const VkPipelineCreateFlags2KHR creationFlags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT;
1489
1490 {
1491 auto &pipeline = dgcPipelines.at(0u);
1492 pipeline.reset(new GraphicsPipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device,
1493 m_context.getDeviceExtensions(), constructionTypes.at(0u)));
1494
1495 pipeline->setDefaultTopology(primitiveTopology)
1496 .setPipelineCreateFlags2(creationFlags)
1497 .setDefaultRasterizationState()
1498 .setDefaultColorBlendState()
1499 .setDefaultMultisampleState()
1500 .setDefaultPatchControlPoints(patchControlPoints)
1501 .setDynamicState(&dynamicStateCreateInfo)
1502 .setupVertexInputState(&vertexInputStateCreateInfo)
1503 .setupPreRasterizationShaderState2(viewports, scissors, pipelineLayout, *renderPass, 0u, *vertFlip,
1504 nullptr, *tescFlip, *tese, *geomFlip)
1505 .setupFragmentShaderState(pipelineLayout, *renderPass, 0u, *fragNormal)
1506 .setupFragmentOutputState(*renderPass, 0u)
1507 .setMonolithicPipelineLayout(pipelineLayout)
1508 .buildPipeline();
1509 }
1510
1511 {
1512 auto &pipeline = dgcPipelines.at(1u);
1513 pipeline.reset(new GraphicsPipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device,
1514 m_context.getDeviceExtensions(), constructionTypes.at(1u)));
1515
1516 pipeline->setDefaultTopology(primitiveTopology)
1517 .setPipelineCreateFlags2(creationFlags)
1518 .setDefaultRasterizationState()
1519 .setDefaultColorBlendState()
1520 .setDefaultMultisampleState()
1521 .setDefaultPatchControlPoints(patchControlPoints)
1522 .setDynamicState(&dynamicStateCreateInfo)
1523 .setupVertexInputState(&vertexInputStateCreateInfo)
1524 .setupPreRasterizationShaderState2(viewports, scissors, pipelineLayout, *renderPass, 0u, *vertFlip,
1525 nullptr, *tescFlip, *tese, *geomFlip)
1526 .setupFragmentShaderState(pipelineLayout, *renderPass, 0u, *fragAlt)
1527 .setupFragmentOutputState(*renderPass, 0u)
1528 .setMonolithicPipelineLayout(pipelineLayout)
1529 .buildPipeline();
1530 }
1531
1532 {
1533 auto &pipeline = dgcPipelines.at(2u);
1534 pipeline.reset(new GraphicsPipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device,
1535 m_context.getDeviceExtensions(), constructionTypes.at(2u)));
1536
1537 pipeline->setDefaultTopology(primitiveTopology)
1538 .setPipelineCreateFlags2(creationFlags)
1539 .setDefaultRasterizationState()
1540 .setDefaultColorBlendState()
1541 .setDefaultMultisampleState()
1542 .setDefaultPatchControlPoints(patchControlPoints)
1543 .setDynamicState(&dynamicStateCreateInfo)
1544 .setupVertexInputState(&vertexInputStateCreateInfo)
1545 .setupPreRasterizationShaderState2(viewports, scissors, pipelineLayout, *renderPass, 0u,
1546 *vertNormal, nullptr, *tescNormal, *tese, *geomNormal)
1547 .setupFragmentShaderState(pipelineLayout, *renderPass, 0u, *fragAlt)
1548 .setupFragmentOutputState(*renderPass, 0u)
1549 .setMonolithicPipelineLayout(pipelineLayout)
1550 .buildPipeline();
1551 }
1552
1553 executionSetManager =
1554 makeExecutionSetManagerPipeline(ctx.vkd, ctx.device, dgcPipelines.at(0u)->getPipeline(), sequenceCount);
1555 for (uint32_t i = 0u; i < sequenceCount; ++i)
1556 executionSetManager->addPipeline(i, dgcPipelines.at(i)->getPipeline());
1557 executionSetManager->update();
1558 }
1559 }
1560 else
1561 {
1562 if (!m_params.isShaderObjects())
1563 {
1564 normalPipeline = makeGraphicsPipeline(
1565 ctx.vkd, ctx.device, *pipelineLayout, vertNormal->getModule(), tescNormal->getModule(),
1566 tese->getModule(), geomNormal->getModule(), fragNormal->getModule(), *renderPass, viewports, scissors,
1567 primitiveTopology, 0u, patchControlPoints, &vertexInputStateCreateInfo, nullptr, nullptr, nullptr,
1568 nullptr, &dynamicStateCreateInfo);
1569 }
1570 }
1571
1572 const auto indirectExecutionSet = (executionSetManager ? executionSetManager->get() : VK_NULL_HANDLE);
1573
1574 // Preprocess buffer.
1575 std::vector<VkShaderEXT> shadersVec;
1576 if (m_params.isShaderObjects() && !m_params.useExecutionSet)
1577 {
1578 shadersVec.reserve(5u); // At most: vert, tesc, tese, geom, frag.
1579 if (vertNormalShader)
1580 shadersVec.push_back(vertNormalShader->get());
1581 if (tescNormalShader)
1582 shadersVec.push_back(tescNormalShader->get());
1583 if (teseShader)
1584 shadersVec.push_back(teseShader->get());
1585 if (geomNormalShader)
1586 shadersVec.push_back(geomNormalShader->get());
1587 if (fragNormalShader)
1588 shadersVec.push_back(fragNormalShader->get());
1589 }
1590 const std::vector<VkShaderEXT> *shadersVecPtr = (shadersVec.empty() ? nullptr : &shadersVec);
1591 PreprocessBufferExt preprocessBuffer(ctx.vkd, ctx.device, ctx.allocator, indirectExecutionSet, *cmdsLayout,
1592 sequenceCount, 0u, *normalPipeline, shadersVecPtr);
1593
1594 // Record commands.
1595 CommandPoolWithBuffer cmd(ctx.vkd, ctx.device, ctx.qfIndex);
1596 const auto cmdBuffer = *cmd.cmdBuffer;
1597
1598 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 0.0f);
1599 const auto clearValueColor = makeClearValueColor(clearColor);
1600 const auto colorSRR = makeDefaultImageSubresourceRange();
1601
1602 // Will be used for both preprocessing and execution.
1603 const DGCGenCmdsInfo generatedCommandsInfo(
1604 stageFlags, // VkShaderStageFlags shaderStages;
1605 indirectExecutionSet, // VkIndirectExecutionSetEXT indirectExecutionSet;
1606 *cmdsLayout, // VkIndirectCommandsLayoutEXT indirectCommandsLayout;
1607 dgcBuffer.getDeviceAddress(), // VkDeviceAddress indirectAddress;
1608 dgcBufferSize, // VkDeviceSize indirectAddressSize;
1609 preprocessBuffer.getDeviceAddress(), // VkDeviceAddress preprocessAddress;
1610 preprocessBuffer.getSize(), // VkDeviceSize preprocessSize;
1611 sequenceCount, // uint32_t maxSequenceCount;
1612 0ull, // VkDeviceAddress sequenceCountAddress;
1613 0u, // uint32_t maxDrawCount;
1614 *normalPipeline, shadersVecPtr);
1615
1616 // When preprocessing, we need to use a separate command buffer to record state.
1617 // The preprocessing step needs to happen outside the render pass.
1618 Move<VkCommandBuffer> separateStateCmdBuffer;
1619
1620 // A command buffer we want to record state into.
1621 // .first is the command buffer itself.
1622 // .second, if not NULL, means we'll record a preprocess command with it as the state command buffer.
1623 using StateCmdBuffer = std::pair<VkCommandBuffer, VkCommandBuffer>;
1624 const VkCommandBuffer kNullCmdBuffer = VK_NULL_HANDLE; // Workaround for emplace_back below.
1625 std::vector<StateCmdBuffer> stateCmdBuffers;
1626
1627 // Sequences and iterations for the different cases:
1628 // - PreprocessType::NONE
1629 // - Only one loop iteration.
1630 // - Iteration 0: .first = main cmd buffer, .second = NULL
1631 // - No preprocess, bind state
1632 // - Execute.
1633 // - PreprocessType::OTHER_STATE_CMD_BUFFER
1634 // - Iteration 0: .first = state cmd buffer, .second = NULL
1635 // - No preprocess, bind state
1636 // - Iteration 1: .first = main cmd buffer, .second = state cmd buffer
1637 // - Preprocess with state cmd buffer, bind state on main
1638 // - Execute.
1639 // - PreprocessType::SAME_STATE_CMD_BUFFER
1640 // - Iteration 0: .first = main cmd buffer, .second = NULL
1641 // - No preprocess, bind state
1642 // - Iteration 1: .first = main cmd buffer, .second = main cmd buffer
1643 // - Preprocess with main cmd buffer, break
1644 // - Execute.
1645 switch (m_params.preprocessType)
1646 {
1647 case PreprocessType::NONE:
1648 stateCmdBuffers.emplace_back(cmdBuffer, kNullCmdBuffer);
1649 break;
1650 case PreprocessType::SAME_STATE_CMD_BUFFER:
1651 stateCmdBuffers.emplace_back(cmdBuffer, kNullCmdBuffer);
1652 stateCmdBuffers.emplace_back(cmdBuffer, cmdBuffer);
1653 break;
1654 case PreprocessType::OTHER_STATE_CMD_BUFFER:
1655 separateStateCmdBuffer =
1656 allocateCommandBuffer(ctx.vkd, ctx.device, *cmd.cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
1657 stateCmdBuffers.emplace_back(*separateStateCmdBuffer, kNullCmdBuffer);
1658 stateCmdBuffers.emplace_back(cmdBuffer, *separateStateCmdBuffer);
1659 break;
1660 default:
1661 DE_ASSERT(false);
1662 }
1663
1664 // Record pre-execution state to all needed command buffers.
1665 VkCommandBuffer prevCmdBuffer = VK_NULL_HANDLE;
1666 for (const auto &stateCmdBufferPair : stateCmdBuffers)
1667 {
1668 const auto &recCmdBuffer = stateCmdBufferPair.first;
1669
1670 // Only begin each command buffer once.
1671 if (recCmdBuffer != prevCmdBuffer)
1672 {
1673 beginCommandBuffer(ctx.vkd, recCmdBuffer);
1674 prevCmdBuffer = recCmdBuffer;
1675 }
1676
1677 if (stateCmdBufferPair.second != VK_NULL_HANDLE)
1678 {
1679 ctx.vkd.cmdPreprocessGeneratedCommandsEXT(recCmdBuffer, &generatedCommandsInfo.get(),
1680 stateCmdBufferPair.second);
1681 separateStateCmdBuffer =
1682 Move<VkCommandBuffer>(); // Delete state command buffer right away as allowed by the spec.
1683
1684 preprocessToExecuteBarrierExt(ctx.vkd, recCmdBuffer);
1685
1686 // Break for iteration 1 of PreprocessType::SAME_STATE_CMD_BUFFER. See above.
1687 if (stateCmdBufferPair.first == stateCmdBufferPair.second)
1688 break;
1689 }
1690
1691 if (m_params.isShaderObjects())
1692 {
1693 // Bind shaders.
1694 std::map<VkShaderStageFlagBits, VkShaderEXT> shadersToBind{
1695 std::make_pair(VK_SHADER_STAGE_VERTEX_BIT, vertNormalShader->get()),
1696 std::make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, fragNormalShader->get()),
1697 };
1698
1699 if (m_params.extraStages == ExtraStages::TESSELLATION)
1700 {
1701 shadersToBind[VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT] = tescNormalShader->get();
1702 shadersToBind[VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT] = teseShader->get();
1703 }
1704 else if (features.tessellationShader)
1705 {
1706 shadersToBind[VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT] = VK_NULL_HANDLE;
1707 shadersToBind[VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT] = VK_NULL_HANDLE;
1708 }
1709
1710 if (m_params.extraStages == ExtraStages::GEOMETRY)
1711 shadersToBind[VK_SHADER_STAGE_GEOMETRY_BIT] = geomNormalShader->get();
1712 else if (features.geometryShader)
1713 shadersToBind[VK_SHADER_STAGE_GEOMETRY_BIT] = VK_NULL_HANDLE;
1714
1715 if (meshFeatures.meshShader)
1716 shadersToBind[VK_SHADER_STAGE_MESH_BIT_EXT] = VK_NULL_HANDLE;
1717 if (meshFeatures.taskShader)
1718 shadersToBind[VK_SHADER_STAGE_TASK_BIT_EXT] = VK_NULL_HANDLE;
1719
1720 for (const auto &stageAndShader : shadersToBind)
1721 ctx.vkd.cmdBindShadersEXT(recCmdBuffer, 1u, &stageAndShader.first, &stageAndShader.second);
1722 }
1723 else
1724 {
1725 if (m_params.useExecutionSet)
1726 {
1727 DE_ASSERT(!dgcPipelines.empty());
1728 ctx.vkd.cmdBindPipeline(recCmdBuffer, bindPoint, dgcPipelines.at(0u)->getPipeline());
1729 }
1730 else
1731 {
1732 // Bind shaders and state.
1733 DE_ASSERT(*normalPipeline != VK_NULL_HANDLE);
1734 ctx.vkd.cmdBindPipeline(recCmdBuffer, bindPoint, *normalPipeline);
1735 }
1736 }
1737
1738 if (m_params.isShaderObjects())
1739 {
1740 // Bind state for shader objects. This is needed with and without execution sets.
1741 bindShaderObjectState(ctx.vkd, getDeviceCreationExtensions(m_context), recCmdBuffer, viewports, scissors,
1742 primitiveTopology, patchControlPoints, &vertexInputStateCreateInfo, nullptr, nullptr,
1743 nullptr, nullptr);
1744 }
1745
1746 if (isIndexed(m_params.testType) || m_params.testType == TestType::DRAW_SIMPLE)
1747 {
1748 const VkBuffer vertexBuffer = vertexBuffers.at(0u).buffer->get();
1749 const VkDeviceSize vertexBufferOffset = 0ull;
1750 const VkDeviceSize vertexBufferSize = static_cast<VkDeviceSize>(vertexBuffers.at(0u).size);
1751 const VkDeviceSize vertexBufferStride = static_cast<VkDeviceSize>(vertexBuffers.at(0u).stride);
1752
1753 ctx.vkd.cmdBindVertexBuffers2(recCmdBuffer, 0u, 1u, &vertexBuffer, &vertexBufferOffset, &vertexBufferSize,
1754 &vertexBufferStride);
1755 }
1756 }
1757
1758 if (m_params.isShaderObjects())
1759 {
1760 const auto clearLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1761 const auto renderingLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1762
1763 const auto preClearBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
1764 clearLayout, colorBuffer.getImage(), colorSRR);
1765
1766 const auto postClearBarrier = makeImageMemoryBarrier(
1767 VK_ACCESS_TRANSFER_WRITE_BIT, (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
1768 clearLayout, renderingLayout, colorBuffer.getImage(), colorSRR);
1769
1770 cmdPipelineImageMemoryBarrier(ctx.vkd, cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1771 VK_PIPELINE_STAGE_TRANSFER_BIT, &preClearBarrier);
1772 ctx.vkd.cmdClearColorImage(cmdBuffer, colorBuffer.getImage(), clearLayout, &clearValueColor.color, 1u,
1773 &colorSRR);
1774 cmdPipelineImageMemoryBarrier(ctx.vkd, cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
1775 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, &postClearBarrier);
1776
1777 beginRendering(ctx.vkd, cmdBuffer, colorBuffer.getImageView(), scissors.at(0u), clearValueColor /*unused*/,
1778 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
1779 }
1780 else
1781 beginRenderPass(ctx.vkd, cmdBuffer, *renderPass, *framebuffer, scissors.at(0u), clearColor);
1782
1783 ctx.vkd.cmdExecuteGeneratedCommandsEXT(cmdBuffer, makeVkBool(m_params.doPreprocess()),
1784 &generatedCommandsInfo.get());
1785
1786 if (m_params.isShaderObjects())
1787 endRendering(ctx.vkd, cmdBuffer);
1788 else
1789 endRenderPass(ctx.vkd, cmdBuffer);
1790
1791 copyImageToBuffer(ctx.vkd, cmdBuffer, colorBuffer.getImage(), colorBuffer.getBuffer(), fbExtent.swizzle(0, 1));
1792 endCommandBuffer(ctx.vkd, cmdBuffer);
1793 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
1794
1795 // Verify results.
1796 auto &resultsBufferAlloc = colorBuffer.getBufferAllocation();
1797 invalidateAlloc(ctx.vkd, ctx.device, resultsBufferAlloc);
1798
1799 const auto tcuFormat = mapVkFormat(colorFormat);
1800 tcu::ConstPixelBufferAccess result(tcuFormat, fbExtent, resultsBufferAlloc.getHostPtr());
1801
1802 tcu::TextureLevel referenceLevel(tcuFormat, fbExtent.x(), fbExtent.y(), fbExtent.z());
1803 auto reference = referenceLevel.getAccess();
1804
1805 // The first and second triangles will have their pixels swapped when using execution sets, because of the X coordinate flip.
1806 // The last two sequences should have blue 1 when using execution sets.
1807 // Except for the first pixel, all others have green 1, either because of the instance count or because of the first instance value.
1808 const auto firstX = (m_params.useExecutionSet ? 1 : 0);
1809 const auto secondX = (m_params.useExecutionSet ? 0 : 1);
1810 const auto blueAlt = (m_params.useExecutionSet ? 1.0f : 0.0f);
1811 const auto redOffsetAlt = (m_params.useExecutionSet ? kVertFlipRedOffset : kVertNormalRedOffset);
1812
1813 reference.setPixel(tcu::Vec4(redValues.at(0u) + redOffsetAlt, 0.0f, 0.0f, 1.0f), firstX, 0);
1814 reference.setPixel(tcu::Vec4(redValues.at(1u) + redOffsetAlt, 1.0f, blueAlt, 1.0f), secondX, 0);
1815 reference.setPixel(tcu::Vec4(redValues.at(1u) + redOffsetAlt, 1.0f, blueAlt, 1.0f), 0, 1);
1816 reference.setPixel(tcu::Vec4(redValues.at(2u) + kVertNormalRedOffset, 1.0f, blueAlt, 1.0f), 1, 1);
1817
1818 auto &log = m_context.getTestContext().getLog();
1819 const float thresholdValue = 0.005f; // 1/255 < 0.005 < 2/255
1820 const tcu::Vec4 threshold(thresholdValue, 0.0f, 0.0f, 0.0f);
1821 if (!tcu::floatThresholdCompare(log, "Result", "", reference, result, threshold, tcu::COMPARE_LOG_EVERYTHING))
1822 TCU_FAIL("Unexpected results in color buffer; check log for details");
1823
1824 return tcu::TestStatus::pass("Pass");
1825 }
1826
checkBasicDGCGraphicsSupport(Context & context,bool)1827 void checkBasicDGCGraphicsSupport(Context &context, bool)
1828 {
1829 const VkShaderStageFlags stages = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
1830 checkDGCExtSupport(context, stages);
1831 }
1832
1833 // The fragment shader uses a push constant for the geometry color but, in addition to that, if pushConstantToken is
1834 // true we're also going to use a push constant for the point size.
basicGraphicsPrograms(SourceCollections & dst,bool pcToken)1835 void basicGraphicsPrograms(SourceCollections &dst, bool pcToken)
1836 {
1837 std::ostringstream vert;
1838 vert << "#version 460\n"
1839 << "layout (location=0) in vec4 inPos;\n"
1840 << (pcToken ? "layout (push_constant, std430) uniform PCBlock { layout(offset=16) float ptSize; } pc;\n" : "")
1841 << "void main (void) {\n"
1842 << " gl_Position = inPos;\n"
1843 << " const float pointSize = " << (pcToken ? "pc.ptSize" : "1.0") << ";\n"
1844 << " gl_PointSize = pointSize;\n"
1845 << "}\n";
1846 dst.glslSources.add("vert") << glu::VertexSource(vert.str());
1847
1848 std::ostringstream frag;
1849 frag << "#version 460\n"
1850 << "layout (push_constant, std430) uniform PCBlock { vec4 color; } pc;\n"
1851 << "layout (location=0) out vec4 outColor;\n"
1852 << "void main (void) {\n"
1853 << " outColor = pc.color;\n"
1854 << "}\n";
1855 dst.glslSources.add("frag") << glu::FragmentSource(frag.str());
1856 }
1857
1858 // indexedDrawWithoutIndexTokenRun tests indexed draws without an index buffer token.
indexedDrawWithoutIndexTokenRun(Context & context,bool pcToken)1859 tcu::TestStatus indexedDrawWithoutIndexTokenRun(Context &context, bool pcToken)
1860 {
1861 const auto &ctx = context.getContextCommonData();
1862 const tcu::IVec3 fbExtent(4, 4, 1);
1863 const auto floatExtent = fbExtent.asFloat();
1864 const auto pixelCount = static_cast<uint32_t>(fbExtent.x() * fbExtent.y() * fbExtent.z());
1865 const auto pixelCountF = static_cast<float>(pixelCount);
1866 const auto vkExtent = makeExtent3D(fbExtent);
1867 const auto fbFormat = VK_FORMAT_R8G8B8A8_UNORM;
1868 const auto tcuFormat = mapVkFormat(fbFormat);
1869 const auto fbUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
1870 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1871 const tcu::Vec4 geomColor(0.0f, 0.0f, 1.0f, 1.0f);
1872 const tcu::Vec4 solidThreshold(0.0f, 0.0f, 0.0f, 0.0f); // When using 0 and 1 only, we expect exact results.
1873 const tcu::Vec4 gradientThreshold(0.0f, 0.0f, 0.005f, 0.0f); // Allow a small mistake in the blue component.
1874 const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1875 const auto stageFlags = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
1876
1877 // Color buffer with verification buffer.
1878 ImageWithBuffer colorBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, fbFormat, fbUsage, VK_IMAGE_TYPE_2D);
1879
1880 // Vertices.
1881 std::vector<tcu::Vec4> vertices;
1882 vertices.reserve(pixelCount);
1883
1884 for (int y = 0; y < fbExtent.y(); ++y)
1885 for (int x = 0; x < fbExtent.x(); ++x)
1886 {
1887 const float xCenter = (static_cast<float>(x) + 0.5f) / floatExtent.x() * 2.0f - 1.0f;
1888 const float yCenter = (static_cast<float>(y) + 0.5f) / floatExtent.y() * 2.0f - 1.0f;
1889 vertices.emplace_back(xCenter, yCenter, 0.0f, 1.0f);
1890 }
1891
1892 // Vertex buffer
1893 const auto vbSize = static_cast<VkDeviceSize>(de::dataSize(vertices));
1894 const auto vbInfo = makeBufferCreateInfo(vbSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
1895 BufferWithMemory vertexBuffer(ctx.vkd, ctx.device, ctx.allocator, vbInfo, MemoryRequirement::HostVisible);
1896 const auto &vbAlloc = vertexBuffer.getAllocation();
1897 void *vbData = vbAlloc.getHostPtr();
1898 const auto vbOffset = static_cast<VkDeviceSize>(0);
1899
1900 deMemcpy(vbData, de::dataOrNull(vertices), de::dataSize(vertices));
1901
1902 // Indices. To make sure these are used we're going duplicate every even index and skip odd indices. And, on top
1903 // of that, we're going to apply an offset to each point.
1904 std::vector<int32_t> offsets;
1905 offsets.reserve(pixelCount);
1906 for (uint32_t i = 0u; i < pixelCount; ++i)
1907 offsets.push_back(100 + i);
1908
1909 std::vector<uint32_t> indices;
1910 indices.reserve(pixelCount);
1911 for (uint32_t i = 0u; i < pixelCount; ++i)
1912 indices.push_back((i / 2u) * 2u + offsets.at(i));
1913
1914 const auto ibSize = static_cast<VkDeviceSize>(de::dataSize(indices));
1915 const auto ibInfo = makeBufferCreateInfo(ibSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
1916 BufferWithMemory indexBuffer(ctx.vkd, ctx.device, ctx.allocator, ibInfo, MemoryRequirement::HostVisible);
1917 const auto &ibAlloc = indexBuffer.getAllocation();
1918 void *ibData = ibAlloc.getHostPtr();
1919
1920 deMemcpy(ibData, de::dataOrNull(indices), de::dataSize(indices));
1921
1922 // Pipeline, render pass, framebuffer...
1923 std::vector<VkPushConstantRange> pcRanges;
1924 if (pcToken)
1925 pcRanges.push_back(
1926 makePushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, DE_SIZEOF32(geomColor), DE_SIZEOF32(float)));
1927 pcRanges.push_back(makePushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0u, DE_SIZEOF32(geomColor)));
1928
1929 const auto pipelineLayout =
1930 makePipelineLayout(ctx.vkd, ctx.device, 0u, nullptr, de::sizeU32(pcRanges), de::dataOrNull(pcRanges));
1931 const auto renderPass = makeRenderPass(ctx.vkd, ctx.device, fbFormat);
1932 const auto framebuffer =
1933 makeFramebuffer(ctx.vkd, ctx.device, *renderPass, colorBuffer.getImageView(), vkExtent.width, vkExtent.height);
1934
1935 // Modules.
1936 const auto &binaries = context.getBinaryCollection();
1937 const auto vertModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
1938 const auto fragModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
1939
1940 const std::vector<VkViewport> viewports(1u, makeViewport(vkExtent));
1941 const std::vector<VkRect2D> scissors(1u, makeRect2D(vkExtent));
1942
1943 // The default values works for the current setup, including the vertex input data format.
1944 const auto pipeline = makeGraphicsPipeline(ctx.vkd, ctx.device, *pipelineLayout, *vertModule, VK_NULL_HANDLE,
1945 VK_NULL_HANDLE, VK_NULL_HANDLE, *fragModule, *renderPass, viewports,
1946 scissors, VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
1947
1948 // Indirect commands layout.
1949 IndirectCommandsLayoutBuilderExt cmdsLayoutBuilder(0u, stageFlags, *pipelineLayout);
1950 if (pcToken)
1951 {
1952 // The color will be provided with a push constant token.
1953 cmdsLayoutBuilder.addPushConstantToken(cmdsLayoutBuilder.getStreamRange(), pcRanges.back());
1954 }
1955 cmdsLayoutBuilder.addDrawIndexedToken(cmdsLayoutBuilder.getStreamRange());
1956 const auto cmdsLayout = cmdsLayoutBuilder.build(ctx.vkd, ctx.device);
1957
1958 // DGC Buffer.
1959 std::vector<VkDrawIndexedIndirectCommand> drawCmds;
1960 drawCmds.reserve(pixelCount);
1961 for (size_t i = 0u; i < vertices.size(); ++i)
1962 drawCmds.push_back(VkDrawIndexedIndirectCommand{1u, 1u, static_cast<uint32_t>(i), -offsets.at(i), 0u});
1963
1964 std::vector<uint32_t> dgcData;
1965 dgcData.reserve(pixelCount * (cmdsLayoutBuilder.getStreamStride() / DE_SIZEOF32(uint32_t)));
1966 for (size_t i = 0u; i < drawCmds.size(); ++i)
1967 {
1968 if (pcToken)
1969 {
1970 // Color pc token, making a gradient.
1971 const float blueComp = static_cast<float>(i) / pixelCountF;
1972 const tcu::Vec4 color(0.0f, 0.0f, blueComp, 1.0f);
1973 pushBackElement(dgcData, color);
1974 }
1975 pushBackElement(dgcData, drawCmds.at(i));
1976 }
1977
1978 const auto dgcBufferSize = static_cast<VkDeviceSize>(de::dataSize(dgcData));
1979 DGCBuffer dgcBuffer(ctx.vkd, ctx.device, ctx.allocator, dgcBufferSize);
1980 auto &dgcBufferAlloc = dgcBuffer.getAllocation();
1981 void *dgcBufferData = dgcBufferAlloc.getHostPtr();
1982 deMemcpy(dgcBufferData, de::dataOrNull(dgcData), de::dataSize(dgcData));
1983
1984 // Preprocess buffer.
1985 PreprocessBufferExt preprocessBuffer(ctx.vkd, ctx.device, ctx.allocator, VK_NULL_HANDLE, *cmdsLayout,
1986 de::sizeU32(drawCmds), 0u, *pipeline);
1987
1988 CommandPoolWithBuffer cmd(ctx.vkd, ctx.device, ctx.qfIndex);
1989 const auto cmdBuffer = *cmd.cmdBuffer;
1990
1991 beginCommandBuffer(ctx.vkd, cmdBuffer);
1992 ctx.vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vbOffset);
1993 ctx.vkd.cmdBindIndexBuffer(cmdBuffer, indexBuffer.get(), 0ull, vk::VK_INDEX_TYPE_UINT32);
1994 ctx.vkd.cmdBindPipeline(cmdBuffer, bindPoint, *pipeline);
1995 if (pcToken)
1996 {
1997 // The fixed push constant will contain the point size for the vertex shader.
1998 const auto &pcRange = pcRanges.front();
1999 const float ptSz = 1.0f;
2000 ctx.vkd.cmdPushConstants(cmdBuffer, *pipelineLayout, pcRange.stageFlags, pcRange.offset, pcRange.size, &ptSz);
2001 }
2002 else
2003 {
2004 // A fixed geometry color in this case, for the fragment shader.
2005 const auto &pcRange = pcRanges.back();
2006 ctx.vkd.cmdPushConstants(cmdBuffer, *pipelineLayout, pcRange.stageFlags, pcRange.offset, pcRange.size,
2007 &geomColor);
2008 }
2009 beginRenderPass(ctx.vkd, cmdBuffer, *renderPass, *framebuffer, scissors.at(0u), clearColor);
2010 {
2011 DGCGenCmdsInfo cmdsInfo(stageFlags, VK_NULL_HANDLE, *cmdsLayout, dgcBuffer.getDeviceAddress(),
2012 dgcBuffer.getSize(), preprocessBuffer.getDeviceAddress(), preprocessBuffer.getSize(),
2013 de::sizeU32(drawCmds), 0ull, 0u, *pipeline);
2014 ctx.vkd.cmdExecuteGeneratedCommandsEXT(cmdBuffer, VK_FALSE, &cmdsInfo.get());
2015 }
2016 endRenderPass(ctx.vkd, cmdBuffer);
2017 copyImageToBuffer(ctx.vkd, cmdBuffer, colorBuffer.getImage(), colorBuffer.getBuffer(), fbExtent.swizzle(0, 1),
2018 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1u,
2019 VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT,
2020 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
2021 endCommandBuffer(ctx.vkd, cmdBuffer);
2022 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
2023
2024 // Verify color output.
2025 invalidateAlloc(ctx.vkd, ctx.device, colorBuffer.getBufferAllocation());
2026 tcu::PixelBufferAccess resultAccess(tcuFormat, fbExtent, colorBuffer.getBufferAllocation().getHostPtr());
2027
2028 tcu::TextureLevel referenceLevel(tcuFormat, fbExtent.x(), fbExtent.y());
2029 auto referenceAccess = referenceLevel.getAccess();
2030 for (int y = 0; y < fbExtent.y(); ++y)
2031 for (int x = 0; x < fbExtent.x(); ++x)
2032 {
2033 const int pixelIdx = y * fbExtent.x() + x;
2034 const bool drawnOver = (pixelIdx % 2 == 0); // Only even pixels are drawn into.
2035
2036 tcu::Vec4 color(0.0f, 0.0f, 0.0f, 0.0f);
2037
2038 if (pcToken)
2039 {
2040 // The passed color will be in the pc token and will change with each draw, forming a gradient. See above.
2041 if (drawnOver)
2042 {
2043 // The +1 in the pixelIdx is because even points are drawn twice and the second color prevails.
2044 const float blueComp = static_cast<float>(pixelIdx + 1) / pixelCountF;
2045 color = tcu::Vec4(0.0f, 0.0f, blueComp, 1.0f);
2046 }
2047 else
2048 color = clearColor;
2049 }
2050 else
2051 {
2052 // Fixed color in this case.
2053 color = (drawnOver ? geomColor : clearColor);
2054 }
2055 referenceAccess.setPixel(color, x, y);
2056 }
2057
2058 auto &log = context.getTestContext().getLog();
2059 const auto &threshold = (pcToken ? gradientThreshold : solidThreshold);
2060 if (!tcu::floatThresholdCompare(log, "Result", "", referenceAccess, resultAccess, threshold,
2061 tcu::COMPARE_LOG_ON_ERROR))
2062 return tcu::TestStatus::fail("Unexpected color in result buffer; check log for details");
2063
2064 return tcu::TestStatus::pass("Pass");
2065 }
2066
2067 } // namespace
2068
createDGCGraphicsDrawTestsExt(tcu::TestContext & testCtx)2069 tcu::TestCaseGroup *createDGCGraphicsDrawTestsExt(tcu::TestContext &testCtx)
2070 {
2071 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
2072 GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "draw"));
2073
2074 GroupPtr drawTokenGroup(new tcu::TestCaseGroup(testCtx, "token_draw"));
2075 GroupPtr drawIndexedTokenGroup(new tcu::TestCaseGroup(testCtx, "token_draw_indexed"));
2076
2077 const struct
2078 {
2079 ExtraStages extraStages;
2080 const char *name;
2081 } extraStageCases[] = {
2082 {ExtraStages::NONE, ""},
2083 {ExtraStages::TESSELLATION, "_with_tess"},
2084 {ExtraStages::GEOMETRY, "_with_geom"},
2085 };
2086
2087 const struct
2088 {
2089 PipelineType pipelineType;
2090 const std::string name;
2091 } pipelineTypeCases[] = {
2092 {PipelineType::MONOLITHIC, "monolithic"},
2093 {PipelineType::SHADER_OBJECTS, "shader_objects"},
2094 {PipelineType::GPL_FAST, "gpl_fast"},
2095 {PipelineType::GPL_OPTIMIZED, "gpl_optimized"},
2096 {PipelineType::GPL_MIX_BASE_FAST, "gpl_mix_base_fast"},
2097 {PipelineType::GPL_MIX_BASE_OPT, "gpl_mix_base_opt"},
2098 };
2099
2100 const struct
2101 {
2102 PreprocessType preprocessType;
2103 const char *suffix;
2104 } preprocessCases[] = {
2105 {PreprocessType::NONE, ""},
2106 {PreprocessType::SAME_STATE_CMD_BUFFER, "_preprocess_same_state_cmd_buffer"},
2107 {PreprocessType::OTHER_STATE_CMD_BUFFER, "_preprocess_separate_state_cmd_buffer"},
2108 };
2109
2110 for (const auto testType :
2111 {TestType::DRAW_SIMPLE, TestType::DRAW, TestType::DRAW_INDEXED, TestType::DRAW_INDEXED_DX})
2112 for (const auto &pipelineTypeCase : pipelineTypeCases)
2113 for (const auto useExecutionSet : {false, true})
2114 {
2115 if (isGPL(pipelineTypeCase.pipelineType) && !useExecutionSet)
2116 continue;
2117
2118 for (const auto &extraStageCase : extraStageCases)
2119 for (const bool checkDrawParams : {false, true})
2120 for (const auto &preprocessCase : preprocessCases)
2121 for (const auto unordered : {false, true})
2122 {
2123 const DrawTestParams params{testType,
2124 extraStageCase.extraStages,
2125 pipelineTypeCase.pipelineType,
2126 preprocessCase.preprocessType,
2127 checkDrawParams,
2128 useExecutionSet,
2129 unordered};
2130 const std::string testName = pipelineTypeCase.name + extraStageCase.name +
2131 (useExecutionSet ? "_with_execution_set" : "") +
2132 (checkDrawParams ? "_check_draw_params" : "") +
2133 preprocessCase.suffix + (unordered ? "_unordered" : "") +
2134 (testType == TestType::DRAW_SIMPLE ? "_simple" : "") +
2135 (testType == TestType::DRAW_INDEXED_DX ? "_dx_index" : "");
2136
2137 const auto group =
2138 (isIndexed(testType) ? drawIndexedTokenGroup.get() : drawTokenGroup.get());
2139
2140 group->addChild(new DGCDrawCase(testCtx, testName, params));
2141 }
2142 }
2143
2144 for (const auto pcToken : {false, true})
2145 {
2146 const std::string testName =
2147 std::string("indexed_draw_without_index_buffer_token") + (pcToken ? "_with_pc_token" : "");
2148 addFunctionCaseWithPrograms(drawIndexedTokenGroup.get(), testName, checkBasicDGCGraphicsSupport,
2149 basicGraphicsPrograms, indexedDrawWithoutIndexTokenRun, pcToken);
2150 }
2151
2152 mainGroup->addChild(drawTokenGroup.release());
2153 mainGroup->addChild(drawIndexedTokenGroup.release());
2154
2155 return mainGroup.release();
2156 }
2157
2158 } // namespace DGC
2159 } // namespace vkt
2160