1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 The Khronos Group Inc.
6 * Copyright (c) 2021 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 Tests mixing VK_EXT_mesh_shader and VK_EXT_provoking_vertex
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktMeshShaderProvokingVertexTestsEXT.hpp"
26 #include "vktTestCase.hpp"
27 #include "vktMeshShaderUtil.hpp"
28 #include "vkImageWithMemory.hpp"
29 #include "vkTypeUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkObjUtil.hpp"
32 #include "vkBufferWithMemory.hpp"
33 #include "vkCmdUtil.hpp"
34 #include "vkBarrierUtil.hpp"
35
36 #include "deUniquePtr.hpp"
37
38 #include <sstream>
39 #include <vector>
40 #include <string>
41
42 namespace vkt
43 {
44 namespace MeshShader
45 {
46
47 namespace
48 {
49
50 using namespace vk;
51
52 enum class Geometry
53 {
54 LINES = 0,
55 TRIANGLES,
56 };
57
58 using ProvokingVertexModeVec = std::vector<VkProvokingVertexModeEXT>;
59
getLineColors(void)60 std::vector<tcu::UVec4> getLineColors (void)
61 {
62 return std::vector<tcu::UVec4>{
63 tcu::UVec4(1, 1, 0, 1),
64 tcu::UVec4(1, 0, 1, 1),
65 };
66 }
67
getTriangleColors(void)68 std::vector<tcu::UVec4> getTriangleColors (void)
69 {
70 return std::vector<tcu::UVec4>{
71 tcu::UVec4(1, 1, 0, 1),
72 tcu::UVec4(0, 1, 1, 1),
73 tcu::UVec4(1, 0, 1, 1),
74 };
75 }
76
getLinePositions(void)77 std::vector<tcu::Vec4> getLinePositions (void)
78 {
79 return std::vector<tcu::Vec4>{
80 tcu::Vec4(-1.0, 0.0, 0.0, 1.0),
81 tcu::Vec4( 1.0, 0.0, 0.0, 1.0),
82 };
83 }
84
getTrianglePositions(void)85 std::vector<tcu::Vec4> getTrianglePositions (void)
86 {
87 return std::vector<tcu::Vec4>{
88 tcu::Vec4(-1.0, -1.0, 0.0, 1.0),
89 tcu::Vec4(-1.0, 1.0, 0.0, 1.0),
90 tcu::Vec4( 3.0, -1.0, 0.0, 1.0),
91 };
92 }
93
getClearColor(void)94 tcu::Vec4 getClearColor (void)
95 {
96 return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
97 }
98
getCaseName(Geometry geometry)99 std::string getCaseName (Geometry geometry)
100 {
101 switch (geometry)
102 {
103 case Geometry::LINES: return "lines";
104 case Geometry::TRIANGLES: return "triangles";
105 default:
106 DE_ASSERT(false);
107 break;
108 }
109 // Unreachable.
110 return "";
111 }
112
getCaseName(const ProvokingVertexModeVec & modes)113 std::string getCaseName (const ProvokingVertexModeVec& modes)
114 {
115 std::string caseName;
116
117 for (const auto& mode : modes)
118 {
119 caseName += (caseName.empty() ? "" : "_");
120 switch (mode)
121 {
122 case VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT: caseName += "first"; break;
123 case VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT: caseName += "last"; break;
124 default:
125 DE_ASSERT(false);
126 break;
127 }
128 }
129
130 return caseName;
131 }
132
133 struct TestParams
134 {
135 ProvokingVertexModeVec provokingVertices; // In the same render pass. In practice 1 or 2 elements.
136 Geometry geometryType;
137 };
138
139 class ProvokingVertexCase : public vkt::TestCase
140 {
141 public:
ProvokingVertexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)142 ProvokingVertexCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
143 : vkt::TestCase (testCtx, name, description)
144 , m_params (params)
145 {
146 DE_ASSERT(m_params.provokingVertices.size() <= 2);
147 }
148
~ProvokingVertexCase(void)149 virtual ~ProvokingVertexCase (void) {}
150
151 void initPrograms (vk::SourceCollections& programCollection) const override;
152 TestInstance* createInstance (Context& context) const override;
153 void checkSupport (Context& context) const override;
154
155 protected:
156 TestParams m_params;
157 };
158
159 class ProvokingVertexInstance : public vkt::TestInstance
160 {
161 public:
ProvokingVertexInstance(Context & context,const TestParams & params)162 ProvokingVertexInstance (Context& context, const TestParams& params)
163 : vkt::TestInstance (context)
164 , m_params (¶ms)
165 {}
~ProvokingVertexInstance(void)166 virtual ~ProvokingVertexInstance (void) {}
167
168 tcu::TestStatus iterate (void) override;
169
170 protected:
171 const TestParams* m_params;
172 };
173
createInstance(Context & context) const174 TestInstance* ProvokingVertexCase::createInstance (Context& context) const
175 {
176 return new ProvokingVertexInstance(context, m_params);
177 }
178
initPrograms(vk::SourceCollections & programCollection) const179 void ProvokingVertexCase::initPrograms (vk::SourceCollections& programCollection) const
180 {
181 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
182
183 std::ostringstream frag;
184 frag
185 << "#version 460\n"
186 << "layout (location=0) flat in uvec4 inColor;\n"
187 << "layout (location=0) out vec4 outColor;\n"
188 << "void main ()\n"
189 << "{\n"
190 << " outColor = vec4(inColor);\n"
191 << "}\n"
192 ;
193 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
194
195 const auto isLines = (m_params.geometryType == Geometry::LINES);
196 const auto vertCount = (isLines ? 2u : 3u);
197 const auto geometryName = (isLines ? "lines" : "triangles");
198 const auto primIndices = (isLines
199 ? "gl_PrimitiveLineIndicesEXT[0] = uvec2(0, 1);"
200 : "gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);");
201 const auto colors = (isLines ? getLineColors() : getTriangleColors());
202 const auto positions = (isLines ? getLinePositions() : getTrianglePositions());
203
204 std::ostringstream mesh;
205 mesh
206 << "#version 460\n"
207 << "#extension GL_EXT_mesh_shader : enable\n"
208 << "\n"
209 << "layout (local_size_x=" << vertCount << ", local_size_y=1, local_size_z=1) in;\n"
210 << "layout (" << geometryName << ") out;\n"
211 << "layout (max_vertices=" << vertCount << ", max_primitives=1) out;\n"
212 << "\n"
213 << "layout (push_constant, std430) uniform PushConstantBlock {\n"
214 << " int layer;\n"
215 << "} pc;\n"
216 << "\n"
217 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
218 << " int gl_Layer;\n"
219 << "} gl_MeshPrimitivesEXT[];\n"
220 << "\n"
221 << "uvec4 colors[] = uvec4[](\n"
222 ;
223
224 for (size_t i = 0; i < colors.size(); ++i)
225 mesh << " uvec4" << colors[i] << ((i < colors.size() - 1) ? "," : "") << "\n";
226
227 mesh
228 << ");\n"
229 << "\n"
230 << "vec4 vertices[] = vec4[](\n"
231 ;
232
233 for (size_t i = 0; i < positions.size(); ++i)
234 mesh << " vec4" << positions[i] << ((i < positions.size() - 1) ? "," : "") << "\n";
235
236 mesh
237 << ");\n"
238 << "\n"
239 << "layout (location=0) flat out uvec4 vtxColor[];\n"
240 << "\n"
241 << "void main ()\n"
242 << "{\n"
243 << " SetMeshOutputsEXT(" << vertCount << ", 1);\n"
244 << " gl_MeshVerticesEXT[gl_LocalInvocationIndex].gl_Position = vertices[gl_LocalInvocationIndex];\n"
245 << " vtxColor[gl_LocalInvocationIndex] = colors[gl_LocalInvocationIndex];\n"
246 << "\n"
247 << " if (gl_LocalInvocationIndex == 0u) {\n"
248 << " " << primIndices << "\n"
249 << " gl_MeshPrimitivesEXT[0].gl_Layer = pc.layer;\n"
250 << " }\n"
251 << "}\n"
252 ;
253 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
254 }
255
checkSupport(Context & context) const256 void ProvokingVertexCase::checkSupport (Context& context) const
257 {
258 checkTaskMeshShaderSupportEXT(context, false/*requireTask*/, true/*requireMesh*/);
259
260 context.requireDeviceFunctionality("VK_EXT_provoking_vertex");
261
262 if (m_params.provokingVertices.size() > 1)
263 {
264 const auto& pvProperties = context.getProvokingVertexPropertiesEXT();
265 if (!pvProperties.provokingVertexModePerPipeline)
266 TCU_THROW(NotSupportedError, "Switching provoking vertex modes in the same render pass not supported");
267 }
268 }
269
iterate(void)270 tcu::TestStatus ProvokingVertexInstance::iterate (void)
271 {
272 const auto& vkd = m_context.getDeviceInterface();
273 const auto device = m_context.getDevice();
274 auto& alloc = m_context.getDefaultAllocator();
275 const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
276 const auto queue = m_context.getUniversalQueue();
277 const auto colorExtent = makeExtent3D(1u, 1u, 1u);
278 const auto colorLayers = static_cast<uint32_t>(m_params->provokingVertices.size());
279 const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
280 const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
281 const auto tcuFormat = mapVkFormat(colorFormat);
282 const auto pixelSize = static_cast<uint32_t>(tcu::getPixelSize(tcuFormat));
283 const auto viewType = ((colorLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
284 const auto clearColor = getClearColor();
285
286 // Color attachment.
287 const VkImageCreateInfo colorBufferInfo =
288 {
289 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
290 nullptr, // const void* pNext;
291 0u, // VkImageCreateFlags flags;
292 VK_IMAGE_TYPE_2D, // VkImageType imageType;
293 colorFormat, // VkFormat format;
294 colorExtent, // VkExtent3D extent;
295 1u, // uint32_t mipLevels;
296 colorLayers, // uint32_t arrayLayers;
297 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
298 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
299 colorUsage, // VkImageUsageFlags usage;
300 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
301 0u, // uint32_t queueFamilyIndexCount;
302 nullptr, // const uint32_t* pQueueFamilyIndices;
303 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
304 };
305 ImageWithMemory colorBuffer (vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
306 const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, colorLayers);
307 const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, colorLayers);
308 const auto colorView = makeImageView(vkd, device, colorBuffer.get(), viewType, colorFormat, colorSRR);
309
310 // Verification buffer.
311 const auto verificationBufferSize = (pixelSize * colorExtent.width * colorExtent.height * colorLayers);
312 const auto verificationBufferInfo = makeBufferCreateInfo(verificationBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
313 BufferWithMemory verificationBuffer (vkd, device, alloc, verificationBufferInfo, MemoryRequirement::HostVisible);
314
315 // Push constant range.
316 const auto pcSize = static_cast<uint32_t>(sizeof(int32_t));
317 const auto pcStages = VK_SHADER_STAGE_MESH_BIT_EXT;
318 const auto pcRange = makePushConstantRange(pcStages, 0u, pcSize);
319
320 // Pipeline layout.
321 const auto pipelineLayout = makePipelineLayout(vkd, device, DE_NULL, &pcRange);
322
323 // Modules.
324 const auto& binaries = m_context.getBinaryCollection();
325 const auto meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
326 const auto fragModule = createShaderModule(vkd, device, binaries.get("frag"));
327
328 // Render pass and framebuffer.
329 const auto renderPass = makeRenderPass(vkd, device, colorFormat);
330 const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorView.get(), colorExtent.width, colorExtent.height, colorLayers);
331
332 // Viewports and scissors.
333 const std::vector<VkViewport> viewports (1u, makeViewport(colorExtent));
334 const std::vector<VkRect2D> scissors (1u, makeRect2D(colorExtent));
335
336 // Pipelines with different provoking vertex modes.
337 std::vector<Move<VkPipeline>> pipelines;
338
339 VkPipelineRasterizationProvokingVertexStateCreateInfoEXT pvInfo = initVulkanStructure();
340
341 const VkPipelineRasterizationStateCreateInfo rasterState =
342 {
343 VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
344 &pvInfo, // const void* pNext;
345 0u, // VkPipelineRasterizationStateCreateFlags flags;
346 VK_FALSE, // VkBool32 depthClampEnable;
347 VK_FALSE, // VkBool32 rasterizerDiscardEnable;
348 VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
349 VK_CULL_MODE_BACK_BIT, // VkCullModeFlags cullMode;
350 VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
351 VK_FALSE, // VkBool32 depthBiasEnable;
352 0.0f, // float depthBiasConstantFactor;
353 0.0f, // float depthBiasClamp;
354 0.0f, // float depthBiasSlopeFactor;
355 1.0f, // float lineWidth;
356 };
357
358 for (const auto& pvMode : m_params->provokingVertices)
359 {
360 pvInfo.provokingVertexMode = pvMode;
361
362 pipelines.push_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
363 DE_NULL, meshModule.get(), fragModule.get(),
364 renderPass.get(), viewports, scissors, 0u, &rasterState));
365 }
366
367 // Command pool and buffer.
368 const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
369 const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
370 const auto cmdBuffer = cmdBufferPtr.get();
371
372 beginCommandBuffer(vkd, cmdBuffer);
373 beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
374 for (int32_t layer = 0; layer < static_cast<int32_t>(pipelines.size()); ++layer)
375 {
376 const auto& pipeline = pipelines.at(static_cast<size_t>(layer));
377 vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
378 vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), pcStages, 0u, pcSize, &layer);
379 vkd.cmdDrawMeshTasksEXT(cmdBuffer, 1u, 1u, 1u);
380 }
381 endRenderPass(vkd, cmdBuffer);
382 {
383 // Copy data to verification buffer.
384 const auto preTransferBarrier = makeImageMemoryBarrier(
385 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
386 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
387 colorBuffer.get(), colorSRR);
388
389 cmdPipelineImageMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &preTransferBarrier);
390
391 const auto copyRegion = makeBufferImageCopy(colorExtent, colorSRL);
392 vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verificationBuffer.get(), 1u, ©Region);
393
394 const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
395
396 cmdPipelineMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &postTransferBarrier);
397 }
398 endCommandBuffer(vkd, cmdBuffer);
399 submitCommandsAndWait(vkd, device, queue, cmdBuffer);
400
401 // Verify colors.
402 auto& verificationBufferAlloc = verificationBuffer.getAllocation();
403 void* verificationBufferData = verificationBufferAlloc.getHostPtr();
404 invalidateAlloc(vkd, device, verificationBufferAlloc);
405
406 const tcu::IVec3 iExtent (static_cast<int>(colorExtent.width), static_cast<int>(colorExtent.height), static_cast<int>(colorLayers));
407 const tcu::ConstPixelBufferAccess resultAccess (tcuFormat, iExtent, verificationBufferData);
408
409 const auto isLines = (m_params->geometryType == Geometry::LINES);
410 const auto colors = (isLines ? getLineColors() : getTriangleColors());
411
412 bool fail = false;
413 auto& log = m_context.getTestContext().getLog();
414
415 for (int z = 0; z < iExtent.z(); ++z)
416 {
417 const auto pvMode = m_params->provokingVertices.at(static_cast<size_t>(z));
418 const auto expectedIntColor = (pvMode == VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT ? colors.front() : colors.back());
419 const tcu::Vec4 expectedColor (static_cast<float>(expectedIntColor.x()),
420 static_cast<float>(expectedIntColor.y()),
421 static_cast<float>(expectedIntColor.z()),
422 static_cast<float>(expectedIntColor.w()));
423
424 for (int y = 0; y < iExtent.y(); ++y)
425 for (int x = 0; x < iExtent.x(); ++x)
426 {
427 const auto resultColor = resultAccess.getPixel(x, y, z);
428 if (resultColor != expectedColor)
429 {
430 fail = true;
431 std::ostringstream msg;
432 msg << "Unexpected color found at layer " << z << " coordinates (" << x << ", " << y << "): expected " << expectedColor << " found " << resultColor;
433 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
434 }
435 }
436 }
437
438 if (fail)
439 return tcu::TestStatus::fail("Failed -- check log for details");
440 return tcu::TestStatus::pass("Pass");
441 }
442
443 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
444
445 } // anonymous namespace
446
createMeshShaderProvokingVertexTestsEXT(tcu::TestContext & testCtx)447 tcu::TestCaseGroup* createMeshShaderProvokingVertexTestsEXT (tcu::TestContext& testCtx)
448 {
449 const std::vector<Geometry> geometries { Geometry::LINES, Geometry::TRIANGLES };
450
451 const std::vector<ProvokingVertexModeVec> testModeCases
452 {
453 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT},
454 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT},
455 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT, VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT},
456 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT, VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT},
457 };
458
459 GroupPtr provokingVertexGroup (new tcu::TestCaseGroup(testCtx, "provoking_vertex", ""));
460
461 for (const auto& geometry : geometries)
462 {
463 const auto geometryName = getCaseName(geometry);
464 GroupPtr geometryGroup (new tcu::TestCaseGroup(testCtx, geometryName.c_str(), ""));
465
466 for (const auto& testModes : testModeCases)
467 {
468 const auto modeName = getCaseName(testModes);
469 TestParams params
470 {
471 testModes, // ProvokingVertexModeVec provokingVertices;
472 geometry, // Geometry geometryType;
473 };
474
475 geometryGroup->addChild(new ProvokingVertexCase(testCtx, modeName, "", params));
476 }
477
478 provokingVertexGroup->addChild(geometryGroup.release());
479 }
480
481 return provokingVertexGroup.release();
482 }
483
484 } // MeshShader
485 } // vkt
486