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 &params)
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 &params) : vkt::TestInstance(context), m_params(&params)
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, &copyRegion);
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