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:
104 return "lines";
105 case Geometry::TRIANGLES:
106 return "triangles";
107 default:
108 DE_ASSERT(false);
109 break;
110 }
111 // Unreachable.
112 return "";
113 }
114
getCaseName(const ProvokingVertexModeVec & modes)115 std::string getCaseName(const ProvokingVertexModeVec &modes)
116 {
117 std::string caseName;
118
119 for (const auto &mode : modes)
120 {
121 caseName += (caseName.empty() ? "" : "_");
122 switch (mode)
123 {
124 case VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT:
125 caseName += "first";
126 break;
127 case VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT:
128 caseName += "last";
129 break;
130 default:
131 DE_ASSERT(false);
132 break;
133 }
134 }
135
136 return caseName;
137 }
138
139 struct TestParams
140 {
141 ProvokingVertexModeVec provokingVertices; // In the same render pass. In practice 1 or 2 elements.
142 Geometry geometryType;
143 };
144
145 class ProvokingVertexCase : public vkt::TestCase
146 {
147 public:
ProvokingVertexCase(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)148 ProvokingVertexCase(tcu::TestContext &testCtx, const std::string &name, const TestParams ¶ms)
149 : vkt::TestCase(testCtx, name)
150 , m_params(params)
151 {
152 DE_ASSERT(m_params.provokingVertices.size() <= 2);
153 }
154
~ProvokingVertexCase(void)155 virtual ~ProvokingVertexCase(void)
156 {
157 }
158
159 void initPrograms(vk::SourceCollections &programCollection) const override;
160 TestInstance *createInstance(Context &context) const override;
161 void checkSupport(Context &context) const override;
162
163 protected:
164 TestParams m_params;
165 };
166
167 class ProvokingVertexInstance : public vkt::TestInstance
168 {
169 public:
ProvokingVertexInstance(Context & context,const TestParams & params)170 ProvokingVertexInstance(Context &context, const TestParams ¶ms) : vkt::TestInstance(context), m_params(¶ms)
171 {
172 }
~ProvokingVertexInstance(void)173 virtual ~ProvokingVertexInstance(void)
174 {
175 }
176
177 tcu::TestStatus iterate(void) override;
178
179 protected:
180 const TestParams *m_params;
181 };
182
createInstance(Context & context) const183 TestInstance *ProvokingVertexCase::createInstance(Context &context) const
184 {
185 return new ProvokingVertexInstance(context, m_params);
186 }
187
initPrograms(vk::SourceCollections & programCollection) const188 void ProvokingVertexCase::initPrograms(vk::SourceCollections &programCollection) const
189 {
190 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
191
192 std::ostringstream frag;
193 frag << "#version 460\n"
194 << "layout (location=0) flat in uvec4 inColor;\n"
195 << "layout (location=0) out vec4 outColor;\n"
196 << "void main ()\n"
197 << "{\n"
198 << " outColor = vec4(inColor);\n"
199 << "}\n";
200 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
201
202 const auto isLines = (m_params.geometryType == Geometry::LINES);
203 const auto vertCount = (isLines ? 2u : 3u);
204 const auto geometryName = (isLines ? "lines" : "triangles");
205 const auto primIndices = (isLines ? "gl_PrimitiveLineIndicesEXT[0] = uvec2(0, 1);" :
206 "gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);");
207 const auto colors = (isLines ? getLineColors() : getTriangleColors());
208 const auto positions = (isLines ? getLinePositions() : getTrianglePositions());
209
210 std::ostringstream mesh;
211 mesh << "#version 460\n"
212 << "#extension GL_EXT_mesh_shader : enable\n"
213 << "\n"
214 << "layout (local_size_x=" << vertCount << ", local_size_y=1, local_size_z=1) in;\n"
215 << "layout (" << geometryName << ") out;\n"
216 << "layout (max_vertices=" << vertCount << ", max_primitives=1) out;\n"
217 << "\n"
218 << "layout (push_constant, std430) uniform PushConstantBlock {\n"
219 << " int layer;\n"
220 << "} pc;\n"
221 << "\n"
222 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
223 << " int gl_Layer;\n"
224 << "} gl_MeshPrimitivesEXT[];\n"
225 << "\n"
226 << "uvec4 colors[] = uvec4[](\n";
227
228 for (size_t i = 0; i < colors.size(); ++i)
229 mesh << " uvec4" << colors[i] << ((i < colors.size() - 1) ? "," : "") << "\n";
230
231 mesh << ");\n"
232 << "\n"
233 << "vec4 vertices[] = vec4[](\n";
234
235 for (size_t i = 0; i < positions.size(); ++i)
236 mesh << " vec4" << positions[i] << ((i < positions.size() - 1) ? "," : "") << "\n";
237
238 mesh << ");\n"
239 << "\n"
240 << "layout (location=0) flat out uvec4 vtxColor[];\n"
241 << "\n"
242 << "void main ()\n"
243 << "{\n"
244 << " SetMeshOutputsEXT(" << vertCount << ", 1);\n"
245 << " gl_MeshVerticesEXT[gl_LocalInvocationIndex].gl_Position = vertices[gl_LocalInvocationIndex];\n"
246 << " vtxColor[gl_LocalInvocationIndex] = colors[gl_LocalInvocationIndex];\n"
247 << "\n"
248 << " if (gl_LocalInvocationIndex == 0u) {\n"
249 << " " << primIndices << "\n"
250 << " gl_MeshPrimitivesEXT[0].gl_Layer = pc.layer;\n"
251 << " }\n"
252 << "}\n";
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 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
289 nullptr, // const void* pNext;
290 0u, // VkImageCreateFlags flags;
291 VK_IMAGE_TYPE_2D, // VkImageType imageType;
292 colorFormat, // VkFormat format;
293 colorExtent, // VkExtent3D extent;
294 1u, // uint32_t mipLevels;
295 colorLayers, // uint32_t arrayLayers;
296 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
297 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
298 colorUsage, // VkImageUsageFlags usage;
299 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
300 0u, // uint32_t queueFamilyIndexCount;
301 nullptr, // const uint32_t* pQueueFamilyIndices;
302 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
303 };
304 ImageWithMemory colorBuffer(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
305 const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, colorLayers);
306 const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, colorLayers);
307 const auto colorView = makeImageView(vkd, device, colorBuffer.get(), viewType, colorFormat, colorSRR);
308
309 // Verification buffer.
310 const auto verificationBufferSize = (pixelSize * colorExtent.width * colorExtent.height * colorLayers);
311 const auto verificationBufferInfo = makeBufferCreateInfo(verificationBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
312 BufferWithMemory verificationBuffer(vkd, device, alloc, verificationBufferInfo, MemoryRequirement::HostVisible);
313
314 // Push constant range.
315 const auto pcSize = static_cast<uint32_t>(sizeof(int32_t));
316 const auto pcStages = VK_SHADER_STAGE_MESH_BIT_EXT;
317 const auto pcRange = makePushConstantRange(pcStages, 0u, pcSize);
318
319 // Pipeline layout.
320 const auto pipelineLayout = makePipelineLayout(vkd, device, DE_NULL, &pcRange);
321
322 // Modules.
323 const auto &binaries = m_context.getBinaryCollection();
324 const auto meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
325 const auto fragModule = createShaderModule(vkd, device, binaries.get("frag"));
326
327 // Render pass and framebuffer.
328 const auto renderPass = makeRenderPass(vkd, device, colorFormat);
329 const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorView.get(), colorExtent.width,
330 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 VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
343 &pvInfo, // const void* pNext;
344 0u, // VkPipelineRasterizationStateCreateFlags flags;
345 VK_FALSE, // VkBool32 depthClampEnable;
346 VK_FALSE, // VkBool32 rasterizerDiscardEnable;
347 VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
348 VK_CULL_MODE_BACK_BIT, // VkCullModeFlags cullMode;
349 VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
350 VK_FALSE, // VkBool32 depthBiasEnable;
351 0.0f, // float depthBiasConstantFactor;
352 0.0f, // float depthBiasClamp;
353 0.0f, // float depthBiasSlopeFactor;
354 1.0f, // float lineWidth;
355 };
356
357 for (const auto &pvMode : m_params->provokingVertices)
358 {
359 pvInfo.provokingVertexMode = pvMode;
360
361 pipelines.push_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(), DE_NULL, meshModule.get(),
362 fragModule.get(), renderPass.get(), viewports, scissors, 0u,
363 &rasterState));
364 }
365
366 // Command pool and buffer.
367 const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
368 const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
369 const auto cmdBuffer = cmdBufferPtr.get();
370
371 beginCommandBuffer(vkd, cmdBuffer);
372 beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
373 for (int32_t layer = 0; layer < static_cast<int32_t>(pipelines.size()); ++layer)
374 {
375 const auto &pipeline = pipelines.at(static_cast<size_t>(layer));
376 vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
377 vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), pcStages, 0u, pcSize, &layer);
378 vkd.cmdDrawMeshTasksEXT(cmdBuffer, 1u, 1u, 1u);
379 }
380 endRenderPass(vkd, cmdBuffer);
381 {
382 // Copy data to verification buffer.
383 const auto preTransferBarrier = makeImageMemoryBarrier(
384 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
385 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer.get(), colorSRR);
386
387 cmdPipelineImageMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
388 VK_PIPELINE_STAGE_TRANSFER_BIT, &preTransferBarrier);
389
390 const auto copyRegion = makeBufferImageCopy(colorExtent, colorSRL);
391 vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
392 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,
397 &postTransferBarrier);
398 }
399 endCommandBuffer(vkd, cmdBuffer);
400 submitCommandsAndWait(vkd, device, queue, cmdBuffer);
401
402 // Verify colors.
403 auto &verificationBufferAlloc = verificationBuffer.getAllocation();
404 void *verificationBufferData = verificationBufferAlloc.getHostPtr();
405 invalidateAlloc(vkd, device, verificationBufferAlloc);
406
407 const tcu::IVec3 iExtent(static_cast<int>(colorExtent.width), static_cast<int>(colorExtent.height),
408 static_cast<int>(colorLayers));
409 const tcu::ConstPixelBufferAccess resultAccess(tcuFormat, iExtent, verificationBufferData);
410
411 const auto isLines = (m_params->geometryType == Geometry::LINES);
412 const auto colors = (isLines ? getLineColors() : getTriangleColors());
413
414 bool fail = false;
415 auto &log = m_context.getTestContext().getLog();
416
417 for (int z = 0; z < iExtent.z(); ++z)
418 {
419 const auto pvMode = m_params->provokingVertices.at(static_cast<size_t>(z));
420 const auto expectedIntColor =
421 (pvMode == VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT ? colors.front() : colors.back());
422 const tcu::Vec4 expectedColor(
423 static_cast<float>(expectedIntColor.x()), static_cast<float>(expectedIntColor.y()),
424 static_cast<float>(expectedIntColor.z()), static_cast<float>(expectedIntColor.w()));
425
426 for (int y = 0; y < iExtent.y(); ++y)
427 for (int x = 0; x < iExtent.x(); ++x)
428 {
429 const auto resultColor = resultAccess.getPixel(x, y, z);
430 if (resultColor != expectedColor)
431 {
432 fail = true;
433 std::ostringstream msg;
434 msg << "Unexpected color found at layer " << z << " coordinates (" << x << ", " << y
435 << "): expected " << expectedColor << " found " << resultColor;
436 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
437 }
438 }
439 }
440
441 if (fail)
442 return tcu::TestStatus::fail("Failed -- check log for details");
443 return tcu::TestStatus::pass("Pass");
444 }
445
446 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
447
448 } // anonymous namespace
449
createMeshShaderProvokingVertexTestsEXT(tcu::TestContext & testCtx)450 tcu::TestCaseGroup *createMeshShaderProvokingVertexTestsEXT(tcu::TestContext &testCtx)
451 {
452 const std::vector<Geometry> geometries{Geometry::LINES, Geometry::TRIANGLES};
453
454 const std::vector<ProvokingVertexModeVec> testModeCases{
455 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT},
456 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT},
457 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT, VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT},
458 ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT, VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT},
459 };
460
461 GroupPtr provokingVertexGroup(new tcu::TestCaseGroup(testCtx, "provoking_vertex"));
462
463 for (const auto &geometry : geometries)
464 {
465 const auto geometryName = getCaseName(geometry);
466 GroupPtr geometryGroup(new tcu::TestCaseGroup(testCtx, geometryName.c_str()));
467
468 for (const auto &testModes : testModeCases)
469 {
470 const auto modeName = getCaseName(testModes);
471 TestParams params{
472 testModes, // ProvokingVertexModeVec provokingVertices;
473 geometry, // Geometry geometryType;
474 };
475
476 geometryGroup->addChild(new ProvokingVertexCase(testCtx, modeName, params));
477 }
478
479 provokingVertexGroup->addChild(geometryGroup.release());
480 }
481
482 return provokingVertexGroup.release();
483 }
484
485 } // namespace MeshShader
486 } // namespace vkt
487