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 Mesh Shader Builtin Tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktMeshShaderBuiltinTests.hpp"
26 #include "vktTestCase.hpp"
27
28 #include "vkTypeUtil.hpp"
29 #include "vkImageUtil.hpp"
30 #include "vkObjUtil.hpp"
31 #include "vkBuilderUtil.hpp"
32 #include "vkImageWithMemory.hpp"
33 #include "vkBufferWithMemory.hpp"
34 #include "vkCmdUtil.hpp"
35 #include "vkBarrierUtil.hpp"
36
37 #include "tcuTexture.hpp"
38 #include "tcuTestLog.hpp"
39
40 #include <vector>
41 #include <algorithm>
42 #include <sstream>
43 #include <map>
44 #include <utility>
45 #include <sstream>
46
47 namespace tcu
48 {
49 // Needed for PixelMap below.
operator <(const IVec2 & a,const IVec2 & b)50 bool operator<(const IVec2& a, const IVec2& b)
51 {
52 return (a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()));
53 }
54 }
55
56 namespace vkt
57 {
58 namespace MeshShader
59 {
60
61 namespace
62 {
63
64 using namespace vk;
65
66 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
67 using DrawCommandVec = std::vector<VkDrawMeshTasksIndirectCommandNV>;
68 using ImageWithMemoryPtr = de::MovePtr<ImageWithMemory>;
69 using BufferWithMemoryPtr = de::MovePtr<BufferWithMemory>;
70 using ViewportVec = std::vector<VkViewport>;
71 using ColorVec = std::vector<tcu::Vec4>;
72 using PixelMap = std::map<tcu::IVec2, tcu::Vec4>; // Coordinates to color.
73
getDefaultExtent()74 VkExtent2D getDefaultExtent ()
75 {
76 return makeExtent2D(8u, 8u);
77 }
78
getLinearExtent()79 VkExtent2D getLinearExtent ()
80 {
81 return makeExtent2D(8u, 1u);
82 }
83
84 struct JobSize
85 {
86 uint32_t numTasks;
87 uint32_t localSize;
88 };
89
getLargeJobSize()90 JobSize getLargeJobSize ()
91 {
92 return JobSize{8u, 8u};
93 }
94
95 // Single draw command with the given number of tasks, 1 by default.
getDefaultDrawCommands(uint32_t taskCount=1u)96 DrawCommandVec getDefaultDrawCommands (uint32_t taskCount = 1u)
97 {
98 return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandNV(taskCount, 0u));
99 }
100
101 // Basic fragment shader that draws fragments in blue.
getBasicFragShader()102 std::string getBasicFragShader ()
103 {
104 return
105 "#version 460\n"
106 "#extension GL_NV_mesh_shader : enable\n"
107 "\n"
108 "layout (location=0) out vec4 outColor;\n"
109 "\n"
110 "void main ()\n"
111 "{\n"
112 " outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
113 "}\n"
114 ;
115 }
116
117 struct IterationParams
118 {
119 VkExtent2D colorExtent;
120 uint32_t numLayers;
121 DrawCommandVec drawArgs;
122 bool indirect;
123 ViewportVec viewports; // If empty, a single default viewport is used.
124 };
125
126 class MeshShaderBuiltinInstance : public vkt::TestInstance
127 {
128 public:
MeshShaderBuiltinInstance(Context & context,const IterationParams & params)129 MeshShaderBuiltinInstance (Context& context, const IterationParams& params)
130 : vkt::TestInstance (context)
131 , m_params (params)
132 {}
~MeshShaderBuiltinInstance(void)133 virtual ~MeshShaderBuiltinInstance (void) {}
134
135 tcu::TestStatus iterate () override;
136 virtual void verifyResults (const tcu::ConstPixelBufferAccess& result) = 0;
137
138 protected:
139 IterationParams m_params;
140 };
141
iterate()142 tcu::TestStatus MeshShaderBuiltinInstance::iterate ()
143 {
144 const auto& vkd = m_context.getDeviceInterface();
145 const auto device = m_context.getDevice();
146 auto& alloc = m_context.getDefaultAllocator();
147 const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
148 const auto queue = m_context.getUniversalQueue();
149 const auto& binaries = m_context.getBinaryCollection();
150
151 const auto useTask = binaries.contains("task");
152 const auto useFrag = binaries.contains("frag");
153 const auto extent = makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u);
154 const auto iExtent3D = tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height), static_cast<int>(m_params.numLayers));
155 const auto format = VK_FORMAT_R8G8B8A8_UNORM;
156 const auto tcuFormat = mapVkFormat(format);
157 const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
158 const auto viewType = ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
159 const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
160 const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
161 const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 1.0f);
162
163 ImageWithMemoryPtr colorBuffer;
164 Move<VkImageView> colorBufferView;
165 {
166 const VkImageCreateInfo colorBufferInfo =
167 {
168 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
169 nullptr, // const void* pNext;
170 0u, // VkImageCreateFlags flags;
171 VK_IMAGE_TYPE_2D, // VkImageType imageType;
172 format, // VkFormat format;
173 extent, // VkExtent3D extent;
174 1u, // uint32_t mipLevels;
175 m_params.numLayers, // uint32_t arrayLayers;
176 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
177 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
178 colorUsage, // VkImageUsageFlags usage;
179 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
180 0u, // uint32_t queueFamilyIndexCount;
181 nullptr, // const uint32_t* pQueueFamilyIndices;
182 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
183 };
184 colorBuffer = ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any));
185 colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR);
186 }
187
188 // Empty descriptor set layout.
189 DescriptorSetLayoutBuilder layoutBuilder;
190 const auto setLayout = layoutBuilder.build(vkd, device);
191
192 // Pipeline layout.
193 const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
194
195 // Render pass and framebuffer.
196 const auto renderPass = makeRenderPass(vkd, device, format);
197 const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width, extent.height, m_params.numLayers);
198
199 // Pipeline.
200 Move<VkShaderModule> taskModule;
201 Move<VkShaderModule> meshModule;
202 Move<VkShaderModule> fragModule;
203
204 if (useTask)
205 taskModule = createShaderModule(vkd, device, binaries.get("task"));
206 if (useFrag)
207 fragModule = createShaderModule(vkd, device, binaries.get("frag"));
208 meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
209
210 std::vector<VkViewport> viewports;
211 std::vector<VkRect2D> scissors;
212 if (m_params.viewports.empty())
213 {
214 // Default ones.
215 viewports.push_back(makeViewport(extent));
216 scissors.push_back(makeRect2D(extent));
217 }
218 else
219 {
220 // The desired viewports and the same number of default scissors.
221 viewports.reserve(m_params.viewports.size());
222 std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports));
223 scissors.resize(viewports.size(), makeRect2D(extent));
224 }
225
226 const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
227 taskModule.get(), meshModule.get(), fragModule.get(),
228 renderPass.get(), viewports, scissors);
229
230 // Command pool and buffer.
231 const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
232 const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
233 const auto cmdBuffer = cmdBufferPtr.get();
234
235 // Indirect buffer if needed.
236 BufferWithMemoryPtr indirectBuffer;
237
238 DE_ASSERT(!m_params.drawArgs.empty());
239 if (m_params.indirect)
240 {
241 // Indirect draws.
242 const auto indirectBufferSize = static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs));
243 const auto indirectBufferUsage = (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
244 const auto indirectBufferInfo = makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage);
245 indirectBuffer = BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible));
246 auto& indirectBufferAlloc = indirectBuffer->getAllocation();
247 void* indirectBufferData = indirectBufferAlloc.getHostPtr();
248
249 deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize));
250 flushAlloc(vkd, device, indirectBufferAlloc);
251 }
252
253 // Submit commands.
254 beginCommandBuffer(vkd, cmdBuffer);
255 beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
256 vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
257
258 if (!m_params.indirect)
259 {
260 for (const auto& command : m_params.drawArgs)
261 vkd.cmdDrawMeshTasksNV(cmdBuffer, command.taskCount, command.firstTask);
262 }
263 else
264 {
265 const auto numDraws = static_cast<uint32_t>(m_params.drawArgs.size());
266 const auto stride = static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type));
267 vkd.cmdDrawMeshTasksIndirectNV(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride);
268 }
269
270 endRenderPass(vkd, cmdBuffer);
271
272 // Output buffer to extract the color buffer contents.
273 BufferWithMemoryPtr outBuffer;
274 void* outBufferData = nullptr;
275 {
276 const auto layerSize = static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * extent.width * extent.height);
277 const auto outBufferSize = layerSize * m_params.numLayers;
278 const auto outBufferUsage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
279 const auto outBufferInfo = makeBufferCreateInfo(outBufferSize, outBufferUsage);
280
281 outBuffer = BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible));
282 outBufferData = outBuffer->getAllocation().getHostPtr();
283 }
284
285 // Transition image layout.
286 const auto preTransferBarrier = makeImageMemoryBarrier(
287 (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT,
288 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
289 colorBuffer->get(), colorSRR);
290
291 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier);
292
293 // Copy image to output buffer.
294 const std::vector<VkBufferImageCopy> regions (1u, makeBufferImageCopy(extent, colorSRL));
295 vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(), static_cast<uint32_t>(regions.size()), de::dataOrNull(regions));
296
297 // Transfer to host barrier.
298 const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
299 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postTransferBarrier, 0u, nullptr, 0u, nullptr);
300
301 endCommandBuffer(vkd, cmdBuffer);
302 submitCommandsAndWait(vkd, device, queue, cmdBuffer);
303
304 // Invalidate alloc and verify result.
305 {
306 auto& outBufferAlloc = outBuffer->getAllocation();
307 invalidateAlloc(vkd, device, outBufferAlloc);
308
309 tcu::ConstPixelBufferAccess result (tcuFormat, iExtent3D, outBufferData);
310 verifyResults(result);
311 }
312
313 return tcu::TestStatus::pass("Pass");
314 }
315
316 // Abstract case that implements the generic checkSupport method.
317 class MeshShaderBuiltinCase : public vkt::TestCase
318 {
319 public:
MeshShaderBuiltinCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)320 MeshShaderBuiltinCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
321 : vkt::TestCase (testCtx, name, description)
322 , m_taskNeeded (taskNeeded)
323 {}
~MeshShaderBuiltinCase(void)324 virtual ~MeshShaderBuiltinCase (void) {}
325
326 void checkSupport (Context& context) const override;
327
328 protected:
329 const bool m_taskNeeded;
330 };
331
checkSupport(Context & context) const332 void MeshShaderBuiltinCase::checkSupport (Context& context) const
333 {
334 context.requireDeviceFunctionality("VK_NV_mesh_shader");
335
336 const auto& meshFeatures = context.getMeshShaderFeatures();
337
338 if (!meshFeatures.meshShader)
339 TCU_THROW(NotSupportedError, "Mesh shader not supported");
340
341 if (m_taskNeeded && !meshFeatures.taskShader)
342 TCU_THROW(NotSupportedError, "Task shader not supported");
343 }
344
345 // Instance that verifies color layers.
346 class FullScreenColorInstance : public MeshShaderBuiltinInstance
347 {
348 public:
FullScreenColorInstance(Context & context,const IterationParams & params,const ColorVec & expectedColors)349 FullScreenColorInstance (Context& context, const IterationParams& params, const ColorVec& expectedColors)
350 : MeshShaderBuiltinInstance (context, params)
351 , m_expectedColors (expectedColors)
352 {}
~FullScreenColorInstance(void)353 virtual ~FullScreenColorInstance (void) {}
354
355 void verifyResults (const tcu::ConstPixelBufferAccess& result) override;
356
357 protected:
358 const ColorVec m_expectedColors;
359 };
360
verifyResults(const tcu::ConstPixelBufferAccess & result)361 void FullScreenColorInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
362 {
363 auto& log = m_context.getTestContext().getLog();
364 bool fail = false;
365 const auto width = result.getWidth();
366 const auto height = result.getHeight();
367 const auto depth = result.getDepth();
368
369 for (int z = 0; z < depth; ++z)
370 {
371 const auto& expected = m_expectedColors.at(z);
372
373 for (int y = 0; y < height; ++y)
374 for (int x = 0; x < width; ++x)
375 {
376 const auto resultColor = result.getPixel(x, y, z);
377 if (resultColor != expected)
378 {
379 std::ostringstream msg;
380 msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected << " and found " << resultColor;
381 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
382 fail = true;
383 }
384 }
385 }
386
387 if (fail)
388 {
389 log << tcu::TestLog::Image("Result", "", result);
390 TCU_FAIL("Check log for details");
391 }
392 }
393
394 // Instance that verifies single-layer framebuffers divided into 4 quadrants.
395 class QuadrantsInstance : public MeshShaderBuiltinInstance
396 {
397 public:
QuadrantsInstance(Context & context,const IterationParams & params,const tcu::Vec4 topLeft,const tcu::Vec4 topRight,const tcu::Vec4 bottomLeft,const tcu::Vec4 bottomRight)398 QuadrantsInstance (Context& context, const IterationParams& params,
399 const tcu::Vec4 topLeft,
400 const tcu::Vec4 topRight,
401 const tcu::Vec4 bottomLeft,
402 const tcu::Vec4 bottomRight)
403 : MeshShaderBuiltinInstance (context, params)
404 , m_topLeft (topLeft)
405 , m_topRight (topRight)
406 , m_bottomLeft (bottomLeft)
407 , m_bottomRight (bottomRight)
408 {}
~QuadrantsInstance(void)409 virtual ~QuadrantsInstance (void) {}
410
411 void verifyResults (const tcu::ConstPixelBufferAccess& result) override;
412
413 protected:
414 const tcu::Vec4 m_topLeft;
415 const tcu::Vec4 m_topRight;
416 const tcu::Vec4 m_bottomLeft;
417 const tcu::Vec4 m_bottomRight;
418 };
419
verifyResults(const tcu::ConstPixelBufferAccess & result)420 void QuadrantsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
421 {
422 const auto width = result.getWidth();
423 const auto height = result.getHeight();
424 const auto depth = result.getDepth();
425
426 DE_ASSERT(depth == 1);
427 DE_ASSERT(width > 0 && width % 2 == 0);
428 DE_ASSERT(height > 0 && height % 2 == 0);
429 DE_UNREF(depth); // For release builds.
430
431 const auto halfWidth = width / 2;
432 const auto halfHeight = height / 2;
433 tcu::Vec4 expected;
434
435 for (int y = 0; y < height; ++y)
436 for (int x = 0; x < width; ++x)
437 {
438 // Choose the right quadrant
439 if (y < halfHeight)
440 expected = ((x < halfWidth) ? m_topLeft : m_topRight);
441 else
442 expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight);
443
444 const auto resultColor = result.getPixel(x, y);
445 if (resultColor != expected)
446 {
447 std::ostringstream msg;
448 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
449 TCU_FAIL(msg.str());
450 }
451 }
452 }
453
454 // Instance that verifies single-layer framebuffers with specific pixels set to some color.
455 struct PixelVerifierParams
456 {
457 const tcu::Vec4 background;
458 const PixelMap pixelMap;
459 };
460
461 class PixelsInstance : public MeshShaderBuiltinInstance
462 {
463 public:
PixelsInstance(Context & context,const IterationParams & params,const PixelVerifierParams & pixelParams)464 PixelsInstance (Context& context, const IterationParams& params, const PixelVerifierParams& pixelParams)
465 : MeshShaderBuiltinInstance (context, params)
466 , m_pixelParams (pixelParams)
467 {}
~PixelsInstance(void)468 virtual ~PixelsInstance (void) {}
469
470 void verifyResults (const tcu::ConstPixelBufferAccess& result) override;
471
472 protected:
473 const PixelVerifierParams m_pixelParams;
474 };
475
verifyResults(const tcu::ConstPixelBufferAccess & result)476 void PixelsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
477 {
478 const auto width = result.getWidth();
479 const auto height = result.getHeight();
480 const auto depth = result.getDepth();
481
482 DE_ASSERT(depth == 1);
483 DE_UNREF(depth); // For release builds.
484
485 for (int y = 0; y < height; ++y)
486 for (int x = 0; x < width; ++x)
487 {
488 const tcu::IVec2 coords (x, y);
489 const auto iter = m_pixelParams.pixelMap.find(coords);
490 const auto expected = ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second);
491 const auto resultColor = result.getPixel(x, y);
492
493 if (resultColor != expected)
494 {
495 std::ostringstream msg;
496 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
497 TCU_FAIL(msg.str());
498 }
499 }
500 }
501
502 // Primitive ID cases.
503 class PrimitiveIdCase : public MeshShaderBuiltinCase
504 {
505 public:
PrimitiveIdCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool glslFrag)506 PrimitiveIdCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool glslFrag)
507 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
508 , m_glslFrag (glslFrag)
509 {}
~PrimitiveIdCase(void)510 virtual ~PrimitiveIdCase (void) {}
511
512 void initPrograms (vk::SourceCollections& programCollection) const override;
513 void checkSupport (Context& context) const override;
514 TestInstance* createInstance (Context& context) const override;
515
516 protected:
517 // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
518 const bool m_glslFrag;
519 };
520
initPrograms(vk::SourceCollections & programCollection) const521 void PrimitiveIdCase::initPrograms (vk::SourceCollections& programCollection) const
522 {
523 // Mesh shader.
524 {
525 std::ostringstream mesh;
526 mesh
527 << "#version 460\n"
528 << "#extension GL_NV_mesh_shader : enable\n"
529 << "\n"
530 << "layout (local_size_x=1) in;\n"
531 << "layout (triangles) out;\n"
532 << "layout (max_vertices=3, max_primitives=1) out;\n"
533 << "\n"
534 << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
535 << " int gl_PrimitiveID;\n"
536 << "} gl_MeshPrimitivesNV[];\n"
537 << "\n"
538 << "void main ()\n"
539 << "{\n"
540 << " gl_PrimitiveCountNV = 1u;\n"
541 << "\n"
542 << " gl_PrimitiveIndicesNV[0] = 0;\n"
543 << " gl_PrimitiveIndicesNV[1] = 1;\n"
544 << " gl_PrimitiveIndicesNV[2] = 2;\n"
545 << "\n"
546 << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
547 << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
548 << " gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
549 << "\n"
550 // Sets an arbitrary primitive id.
551 << " gl_MeshPrimitivesNV[0].gl_PrimitiveID = 1629198956;\n"
552 << "}\n"
553 ;
554 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
555 }
556
557 // Frag shader.
558 if (m_glslFrag)
559 {
560 std::ostringstream frag;
561 frag
562 << "#version 460\n"
563 << "#extension GL_NV_mesh_shader : enable\n"
564 << "\n"
565 << "layout (location=0) out vec4 outColor;\n"
566 << "\n"
567 << "void main ()\n"
568 << "{\n"
569 // Checks the primitive id matches.
570 << " outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0));\n"
571 << "}\n"
572 ;
573 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
574 }
575 else
576 {
577 // This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingNV in order to
578 // access gl_PrimitiveID. This also needs the SPV_NV_mesh_shader extension.
579 std::ostringstream frag;
580 frag
581 << "; Version: 1.0\n"
582 << "; Generator: Khronos Glslang Reference Front End; 10\n"
583 << "; Bound: 24\n"
584 << "; Schema: 0\n"
585 << " OpCapability Shader\n"
586
587 // Manual change in these lines.
588 //<< " OpCapability Geometry\n"
589 << " OpCapability MeshShadingNV\n"
590 << " OpExtension \"SPV_NV_mesh_shader\"\n"
591
592 << " %1 = OpExtInstImport \"GLSL.std.450\"\n"
593 << " OpMemoryModel Logical GLSL450\n"
594 << " OpEntryPoint Fragment %4 \"main\" %9 %12\n"
595 << " OpExecutionMode %4 OriginUpperLeft\n"
596 << " OpDecorate %9 Location 0\n"
597 << " OpDecorate %12 Flat\n"
598 << " OpDecorate %12 BuiltIn PrimitiveId\n"
599 << " %2 = OpTypeVoid\n"
600 << " %3 = OpTypeFunction %2\n"
601 << " %6 = OpTypeFloat 32\n"
602 << " %7 = OpTypeVector %6 4\n"
603 << " %8 = OpTypePointer Output %7\n"
604 << " %9 = OpVariable %8 Output\n"
605 << "%10 = OpTypeInt 32 1\n"
606 << "%11 = OpTypePointer Input %10\n"
607 << "%12 = OpVariable %11 Input\n"
608 << "%14 = OpConstant %10 1629198956\n"
609 << "%15 = OpTypeBool\n"
610 << "%17 = OpConstant %6 0\n"
611 << "%18 = OpConstant %6 1\n"
612 << "%19 = OpConstantComposite %7 %17 %17 %18 %18\n"
613 << "%20 = OpConstantComposite %7 %17 %17 %17 %18\n"
614 << "%21 = OpTypeVector %15 4\n"
615 << " %4 = OpFunction %2 None %3\n"
616 << " %5 = OpLabel\n"
617 << "%13 = OpLoad %10 %12\n"
618 << "%16 = OpIEqual %15 %13 %14\n"
619 << "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n"
620 << "%23 = OpSelect %7 %22 %19 %20\n"
621 << " OpStore %9 %23\n"
622 << " OpReturn\n"
623 << " OpFunctionEnd\n"
624 ;
625 programCollection.spirvAsmSources.add("frag") << frag.str();
626 }
627 }
628
checkSupport(Context & context) const629 void PrimitiveIdCase::checkSupport (Context& context) const
630 {
631 MeshShaderBuiltinCase::checkSupport(context);
632
633 // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
634 if (m_glslFrag)
635 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
636 }
637
createInstance(Context & context) const638 TestInstance* PrimitiveIdCase::createInstance (Context& context) const
639 {
640 const ColorVec expectedColors (1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
641 const IterationParams iterationParams =
642 {
643 getDefaultExtent(), // VkExtent2D colorExtent;
644 1u, // uint32_t numLayers;
645 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
646 false, // bool indirect;
647 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
648 };
649 return new FullScreenColorInstance(context, iterationParams, expectedColors);
650 }
651
652 // Layer builtin case.
653 class LayerCase : public MeshShaderBuiltinCase
654 {
655 public:
LayerCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool shareVertices)656 LayerCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool shareVertices)
657 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
658 , m_shareVertices (shareVertices)
659 {}
~LayerCase(void)660 virtual ~LayerCase (void) {}
661
662 void initPrograms (vk::SourceCollections& programCollection) const override;
663 void checkSupport (Context& context) const override;
664 TestInstance* createInstance (Context& context) const override;
665
666 static constexpr uint32_t kNumLayers = 4u;
667
668 protected:
669 const bool m_shareVertices;
670 };
671
initPrograms(vk::SourceCollections & programCollection) const672 void LayerCase::initPrograms (vk::SourceCollections& programCollection) const
673 {
674 const auto localSize = (m_shareVertices ? kNumLayers : 1u);
675 const auto numPrimitives = (m_shareVertices ? kNumLayers : 1u);
676 const auto layerNumber = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
677
678 // One layer per local invocation or work group (shared vertices or not, respectively).
679 {
680 std::ostringstream mesh;
681 mesh
682 << "#version 460\n"
683 << "#extension GL_NV_mesh_shader : enable\n"
684 << "\n"
685 << "layout (local_size_x=" << localSize << ") in;\n"
686 << "layout (triangles) out;\n"
687 << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
688 << "\n"
689 << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
690 << " int gl_Layer;\n"
691 << "} gl_MeshPrimitivesNV[];\n"
692 << "\n"
693 << "void main ()\n"
694 << "{\n"
695 << " gl_PrimitiveCountNV = " << numPrimitives << ";\n"
696 << "\n"
697 << " if (gl_LocalInvocationIndex == 0u)\n"
698 << " {\n"
699 << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
700 << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
701 << " gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
702 << " }\n"
703 << "\n"
704 << " const uint baseIndex = gl_LocalInvocationIndex * 3u;\n"
705 << " gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n"
706 << " gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n"
707 << " gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n"
708 << "\n"
709 << " gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n"
710 << "}\n"
711 ;
712 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
713 }
714
715 // Fragment shader chooses one color per layer.
716 {
717 std::ostringstream frag;
718 frag
719 << "#version 460\n"
720 << "#extension GL_NV_mesh_shader : enable\n"
721 << "\n"
722 << "layout (location=0) out vec4 outColor;\n"
723 << "\n"
724 << "vec4 colors[" << kNumLayers << "] = vec4[](\n"
725 << " vec4(0.0, 0.0, 1.0, 1.0),\n"
726 << " vec4(1.0, 0.0, 1.0, 1.0),\n"
727 << " vec4(0.0, 1.0, 1.0, 1.0),\n"
728 << " vec4(1.0, 1.0, 0.0, 1.0)\n"
729 << ");\n"
730 << "\n"
731 << "void main ()\n"
732 << "{\n"
733 << " outColor = colors[gl_Layer];\n"
734 << "}\n"
735 ;
736 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
737 }
738 }
739
checkSupport(Context & context) const740 void LayerCase::checkSupport (Context& context) const
741 {
742 MeshShaderBuiltinCase::checkSupport(context);
743
744 if (!context.contextSupports(vk::ApiVersion(1u, 2u, 0u)))
745 context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
746 else
747 {
748 const auto& features = context.getDeviceVulkan12Features();
749 if (!features.shaderOutputLayer)
750 TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported");
751 }
752 }
753
createInstance(Context & context) const754 TestInstance* LayerCase::createInstance (Context& context) const
755 {
756 ColorVec expectedColors;
757
758 expectedColors.reserve(kNumLayers);
759 expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
760 expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
761 expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
762 expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
763
764 const auto numWorkGroups = (m_shareVertices ? 1u : kNumLayers);
765 const IterationParams iterationParams =
766 {
767 getDefaultExtent(), // VkExtent2D colorExtent;
768 kNumLayers, // uint32_t numLayers;
769 getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
770 false, // bool indirect;
771 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
772 };
773 return new FullScreenColorInstance(context, iterationParams, expectedColors);
774 }
775
776 // ViewportIndex builtin case.
777 class ViewportIndexCase : public MeshShaderBuiltinCase
778 {
779 public:
ViewportIndexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool shareVertices)780 ViewportIndexCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool shareVertices)
781 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
782 , m_shareVertices (shareVertices)
783 {}
~ViewportIndexCase(void)784 virtual ~ViewportIndexCase (void) {}
785
786 void initPrograms (vk::SourceCollections& programCollection) const override;
787 void checkSupport (Context& context) const override;
788 TestInstance* createInstance (Context& context) const override;
789
790 static constexpr uint32_t kQuadrants = 4u;
791
792 protected:
793 const bool m_shareVertices;
794 };
795
initPrograms(vk::SourceCollections & programCollection) const796 void ViewportIndexCase::initPrograms (vk::SourceCollections& programCollection) const
797 {
798 const auto localSize = (m_shareVertices ? kQuadrants : 1u);
799 const auto numPrimitives = (m_shareVertices ? kQuadrants : 1u);
800 const auto viewportIndex = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
801
802 // One viewport per local invocation or work group (sharing vertices or not, respectively).
803 {
804 std::ostringstream mesh;
805 mesh
806 << "#version 460\n"
807 << "#extension GL_NV_mesh_shader : enable\n"
808 << "\n"
809 << "layout (local_size_x=" << localSize << ") in;\n"
810 << "layout (triangles) out;\n"
811 << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
812 << "\n"
813 << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
814 << " int gl_ViewportIndex;\n"
815 << "} gl_MeshPrimitivesNV[];\n"
816 << "\n"
817 << "void main ()\n"
818 << "{\n"
819 << " gl_PrimitiveCountNV = " << numPrimitives << ";\n"
820 << "\n"
821 << " if (gl_LocalInvocationIndex == 0u)\n"
822 << " {\n"
823 << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
824 << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
825 << " gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
826 << " }\n"
827 << "\n"
828 << " const uint baseIndex = gl_LocalInvocationIndex * 3u;\n"
829 << " gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n"
830 << " gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n"
831 << " gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n"
832 << "\n"
833 << " gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex << ");\n"
834 << "}\n"
835 ;
836 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
837 }
838
839 // Fragment shader chooses one color per viewport.
840 {
841 std::ostringstream frag;
842 frag
843 << "#version 460\n"
844 << "#extension GL_NV_mesh_shader : enable\n"
845 << "\n"
846 << "layout (location=0) out vec4 outColor;\n"
847 << "\n"
848 << "vec4 colors[" << kQuadrants << "] = vec4[](\n"
849 << " vec4(0.0, 0.0, 1.0, 1.0),\n"
850 << " vec4(1.0, 0.0, 1.0, 1.0),\n"
851 << " vec4(0.0, 1.0, 1.0, 1.0),\n"
852 << " vec4(1.0, 1.0, 0.0, 1.0)\n"
853 << ");\n"
854 << "\n"
855 << "void main ()\n"
856 << "{\n"
857 << " outColor = colors[gl_ViewportIndex];\n"
858 << "}\n"
859 ;
860 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
861 }
862 }
863
checkSupport(Context & context) const864 void ViewportIndexCase::checkSupport (Context& context) const
865 {
866 MeshShaderBuiltinCase::checkSupport(context);
867 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
868
869 if (!context.contextSupports(vk::ApiVersion(1u, 2u, 0u)))
870 context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
871 else
872 {
873 const auto& features = context.getDeviceVulkan12Features();
874 if (!features.shaderOutputViewportIndex)
875 TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported");
876 }
877 }
878
createInstance(Context & context) const879 TestInstance* ViewportIndexCase::createInstance (Context& context) const
880 {
881 const auto extent = getDefaultExtent();
882
883 DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u);
884 DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u);
885
886 const auto halfWidth = static_cast<float>(extent.width / 2u);
887 const auto halfHeight = static_cast<float>(extent.height / 2u);
888
889 const auto topLeft = tcu::Vec4(0.0, 0.0, 1.0, 1.0);
890 const auto topRight = tcu::Vec4(1.0, 0.0, 1.0, 1.0);
891 const auto bottomLeft = tcu::Vec4(0.0, 1.0, 1.0, 1.0);
892 const auto bottomRight = tcu::Vec4(1.0, 1.0, 0.0, 1.0);
893
894 ViewportVec viewports;
895 viewports.reserve(kQuadrants);
896 viewports.emplace_back(makeViewport(0.0f, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
897 viewports.emplace_back(makeViewport(halfWidth, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
898 viewports.emplace_back(makeViewport(0.0f, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
899 viewports.emplace_back(makeViewport(halfWidth, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
900
901 const auto numWorkGroups = (m_shareVertices ? 1u : kQuadrants);
902 const IterationParams iterationParams =
903 {
904 getDefaultExtent(), // VkExtent2D colorExtent;
905 1u, // uint32_t numLayers;
906 getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
907 false, // bool indirect;
908 std::move(viewports), // ViewportVec viewports;
909 };
910 return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight);
911 }
912
913 // Position builtin case.
914 class PositionCase : public MeshShaderBuiltinCase
915 {
916 public:
PositionCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)917 PositionCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
918 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
919 {}
~PositionCase(void)920 virtual ~PositionCase (void) {}
921
922 void initPrograms (vk::SourceCollections& programCollection) const override;
923 TestInstance* createInstance (Context& context) const override;
924 };
925
initPrograms(vk::SourceCollections & programCollection) const926 void PositionCase::initPrograms (vk::SourceCollections& programCollection) const
927 {
928 // Mesh shader: emit single triangle around the center of the top left pixel.
929 {
930 const auto extent = getDefaultExtent();
931 const auto fWidth = static_cast<float>(extent.width);
932 const auto fHeight = static_cast<float>(extent.height);
933
934 const auto pxWidth = 2.0f / fWidth;
935 const auto pxHeight = 2.0f / fHeight;
936
937 const auto halfXPix = pxWidth / 2.0f;
938 const auto halfYPix = pxHeight / 2.0f;
939
940 // Center of top left pixel.
941 const auto x = -1.0f + halfXPix;
942 const auto y = -1.0f + halfYPix;
943
944 std::ostringstream mesh;
945 mesh
946 << "#version 460\n"
947 << "#extension GL_NV_mesh_shader : enable\n"
948 << "\n"
949 << "layout (local_size_x=1) in;\n"
950 << "layout (triangles) out;\n"
951 << "layout (max_vertices=3, max_primitives=1) out;\n"
952 << "\n"
953 << "void main ()\n"
954 << "{\n"
955 << " gl_PrimitiveCountNV = 1u;\n"
956 << "\n"
957 << " gl_PrimitiveIndicesNV[0] = 0;\n"
958 << " gl_PrimitiveIndicesNV[1] = 1;\n"
959 << " gl_PrimitiveIndicesNV[2] = 2;\n"
960 << "\n"
961 << " gl_MeshVerticesNV[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n"
962 << " gl_MeshVerticesNV[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n"
963 << " gl_MeshVerticesNV[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n"
964 << "}\n"
965 ;
966 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
967 }
968
969 // Basic fragment shader.
970 {
971 const auto frag = getBasicFragShader();
972 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
973 }
974 }
975
createInstance(Context & context) const976 TestInstance* PositionCase::createInstance (Context& context) const
977 {
978 const IterationParams iterationParams =
979 {
980 getDefaultExtent(), // VkExtent2D colorExtent;
981 1u, // uint32_t numLayers;
982 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
983 false, // bool indirect;
984 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
985 };
986
987 // Must match the shader.
988 PixelMap pixelMap;
989 pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
990
991 const PixelVerifierParams verifierParams =
992 {
993 tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), // const tcu::Vec4 background;
994 std::move(pixelMap), // const PixelMap pixelMap;
995 };
996 return new PixelsInstance(context, iterationParams, verifierParams);
997 }
998
999 // PointSize builtin case.
1000 class PointSizeCase : public MeshShaderBuiltinCase
1001 {
1002 public:
PointSizeCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1003 PointSizeCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1004 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1005 {}
~PointSizeCase(void)1006 virtual ~PointSizeCase (void) {}
1007
1008 void initPrograms (vk::SourceCollections& programCollection) const override;
1009 TestInstance* createInstance (Context& context) const override;
1010 void checkSupport (Context& context) const override;
1011
1012 static constexpr float kPointSize = 4.0f;
1013 };
1014
initPrograms(vk::SourceCollections & programCollection) const1015 void PointSizeCase::initPrograms (vk::SourceCollections& programCollection) const
1016 {
1017 // Mesh shader: large point covering the top left quadrant.
1018 {
1019 std::ostringstream mesh;
1020 mesh
1021 << "#version 460\n"
1022 << "#extension GL_NV_mesh_shader : enable\n"
1023 << "\n"
1024 << "layout (local_size_x=1) in;\n"
1025 << "layout (points) out;\n"
1026 << "layout (max_vertices=1, max_primitives=1) out;\n"
1027 << "\n"
1028 << "void main ()\n"
1029 << "{\n"
1030 << " gl_PrimitiveCountNV = 1u;\n"
1031 << " gl_PrimitiveIndicesNV[0] = 0;\n"
1032 << " gl_MeshVerticesNV[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n"
1033 << " gl_MeshVerticesNV[0].gl_PointSize = " << kPointSize << ";\n"
1034 << "}\n"
1035 ;
1036 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1037 }
1038
1039 // Basic fragment shader.
1040 {
1041 const auto frag = getBasicFragShader();
1042 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1043 }
1044 }
1045
createInstance(Context & context) const1046 TestInstance* PointSizeCase::createInstance (Context& context) const
1047 {
1048 const IterationParams iterationParams =
1049 {
1050 getDefaultExtent(), // VkExtent2D colorExtent;
1051 1u, // uint32_t numLayers;
1052 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1053 false, // bool indirect;
1054 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1055 };
1056
1057 // Must match the shader.
1058 const tcu::Vec4 black (0.0f, 0.0f, 0.0f, 1.0f);
1059 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
1060
1061 return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1062 }
1063
checkSupport(Context & context) const1064 void PointSizeCase::checkSupport (Context& context) const
1065 {
1066 MeshShaderBuiltinCase::checkSupport(context);
1067 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
1068
1069 const auto& properties = context.getDeviceProperties();
1070 if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1])
1071 TCU_THROW(NotSupportedError, "Required point size outside point size range");
1072 }
1073
1074 // ClipDistance builtin case.
1075 class ClipDistanceCase : public MeshShaderBuiltinCase
1076 {
1077 public:
ClipDistanceCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1078 ClipDistanceCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1079 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1080 {}
~ClipDistanceCase(void)1081 virtual ~ClipDistanceCase (void) {}
1082
1083 void initPrograms (vk::SourceCollections& programCollection) const override;
1084 TestInstance* createInstance (Context& context) const override;
1085 void checkSupport (Context& context) const override;
1086 };
1087
initPrograms(vk::SourceCollections & programCollection) const1088 void ClipDistanceCase::initPrograms (vk::SourceCollections& programCollection) const
1089 {
1090 // Mesh shader: full-screen quad using different clip distances.
1091 {
1092 std::ostringstream mesh;
1093 mesh
1094 << "#version 460\n"
1095 << "#extension GL_NV_mesh_shader : enable\n"
1096 << "\n"
1097 << "layout (local_size_x=1) in;\n"
1098 << "layout (triangles) out;\n"
1099 << "layout (max_vertices=4, max_primitives=2) out;\n"
1100 << "\n"
1101 << "out gl_MeshPerVertexNV {\n"
1102 << " vec4 gl_Position;\n"
1103 << " float gl_ClipDistance[2];\n"
1104 << "} gl_MeshVerticesNV[];\n"
1105 << "\n"
1106 << "void main ()\n"
1107 << "{\n"
1108 << " gl_PrimitiveCountNV = 2u;\n"
1109 << "\n"
1110 << " gl_PrimitiveIndicesNV[0] = 0;\n"
1111 << " gl_PrimitiveIndicesNV[1] = 1;\n"
1112 << " gl_PrimitiveIndicesNV[2] = 2;\n"
1113 << " gl_PrimitiveIndicesNV[3] = 1;\n"
1114 << " gl_PrimitiveIndicesNV[4] = 3;\n"
1115 << " gl_PrimitiveIndicesNV[5] = 2;\n"
1116 << "\n"
1117 << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1118 << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
1119 << " gl_MeshVerticesNV[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1120 << " gl_MeshVerticesNV[3].gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n"
1121 << "\n"
1122 // The first clip plane keeps the left half of the frame buffer.
1123 << " gl_MeshVerticesNV[0].gl_ClipDistance[0] = 1.0;\n"
1124 << " gl_MeshVerticesNV[1].gl_ClipDistance[0] = 1.0;\n"
1125 << " gl_MeshVerticesNV[2].gl_ClipDistance[0] = -1.0;\n"
1126 << " gl_MeshVerticesNV[3].gl_ClipDistance[0] = -1.0;\n"
1127 << "\n"
1128 // The second clip plane keeps the top half of the frame buffer.
1129 << " gl_MeshVerticesNV[0].gl_ClipDistance[1] = 1.0;\n"
1130 << " gl_MeshVerticesNV[1].gl_ClipDistance[1] = -1.0;\n"
1131 << " gl_MeshVerticesNV[2].gl_ClipDistance[1] = 1.0;\n"
1132 << " gl_MeshVerticesNV[3].gl_ClipDistance[1] = -1.0;\n"
1133 << "}\n"
1134 ;
1135 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1136 }
1137
1138 // Fragment shader chooses a constant color.
1139 {
1140 std::ostringstream frag;
1141 frag
1142 << "#version 460\n"
1143 << "#extension GL_NV_mesh_shader : enable\n"
1144 << "\n"
1145 << "layout (location=0) out vec4 outColor;\n"
1146 << "\n"
1147 << "void main ()\n"
1148 << "{\n"
1149 // White color should not actually be used, as those fragments are supposed to be discarded.
1150 << " outColor = ((gl_ClipDistance[0] >= 0.0 && gl_ClipDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n"
1151 << "}\n"
1152 ;
1153 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
1154 }
1155 }
1156
createInstance(Context & context) const1157 TestInstance* ClipDistanceCase::createInstance (Context& context) const
1158 {
1159 const IterationParams iterationParams =
1160 {
1161 getDefaultExtent(), // VkExtent2D colorExtent;
1162 1u, // uint32_t numLayers;
1163 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1164 false, // bool indirect;
1165 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1166 };
1167
1168 // Must match the shader.
1169 const tcu::Vec4 black (0.0f, 0.0f, 0.0f, 1.0f);
1170 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
1171
1172 return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1173 }
1174
checkSupport(Context & context) const1175 void ClipDistanceCase::checkSupport (Context& context) const
1176 {
1177 MeshShaderBuiltinCase::checkSupport(context);
1178 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
1179 }
1180
1181 // CullDistance builtin case.
1182 class CullDistanceCase : public MeshShaderBuiltinCase
1183 {
1184 public:
CullDistanceCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1185 CullDistanceCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1186 : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1187 {}
~CullDistanceCase(void)1188 virtual ~CullDistanceCase (void) {}
1189
1190 void initPrograms (vk::SourceCollections& programCollection) const override;
1191 TestInstance* createInstance (Context& context) const override;
1192 void checkSupport (Context& context) const override;
1193 };
1194
initPrograms(vk::SourceCollections & programCollection) const1195 void CullDistanceCase::initPrograms (vk::SourceCollections& programCollection) const
1196 {
1197 // Mesh shader: two quads covering the whole screen, one on top of the other.
1198 // Use cull distances to discard the bottom quad.
1199 // Use cull distances to paint the top one in two colors: blue on the left, white on the right.
1200 {
1201 std::ostringstream mesh;
1202 mesh
1203 << "#version 460\n"
1204 << "#extension GL_NV_mesh_shader : enable\n"
1205 << "\n"
1206 << "layout (local_size_x=1) in;\n"
1207 << "layout (triangles) out;\n"
1208 << "layout (max_vertices=6, max_primitives=4) out;\n"
1209 << "\n"
1210 << "out gl_MeshPerVertexNV {\n"
1211 << " vec4 gl_Position;\n"
1212 << " float gl_CullDistance[2];\n"
1213 << "} gl_MeshVerticesNV[];\n"
1214 << "\n"
1215 << "void main ()\n"
1216 << "{\n"
1217 << " gl_PrimitiveCountNV = 4u;\n"
1218 << "\n"
1219 << " gl_PrimitiveIndicesNV[0] = 0;\n"
1220 << " gl_PrimitiveIndicesNV[1] = 1;\n"
1221 << " gl_PrimitiveIndicesNV[2] = 3;\n"
1222 << " gl_PrimitiveIndicesNV[3] = 1;\n"
1223 << " gl_PrimitiveIndicesNV[4] = 4;\n"
1224 << " gl_PrimitiveIndicesNV[5] = 3;\n"
1225 << " gl_PrimitiveIndicesNV[6] = 1;\n"
1226 << " gl_PrimitiveIndicesNV[7] = 2;\n"
1227 << " gl_PrimitiveIndicesNV[8] = 4;\n"
1228 << " gl_PrimitiveIndicesNV[9] = 2;\n"
1229 << " gl_PrimitiveIndicesNV[10] = 5;\n"
1230 << " gl_PrimitiveIndicesNV[11] = 4;\n"
1231 << "\n"
1232 << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1233 << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 0.0, 0.0, 1.0);\n"
1234 << " gl_MeshVerticesNV[2].gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
1235 << " gl_MeshVerticesNV[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1236 << " gl_MeshVerticesNV[4].gl_Position = vec4( 1.0, 0.0, 0.0, 1.0);\n"
1237 << " gl_MeshVerticesNV[5].gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n"
1238 << "\n"
1239 // The first cull plane discards the bottom quad
1240 << " gl_MeshVerticesNV[0].gl_CullDistance[0] = 1.0;\n"
1241 << " gl_MeshVerticesNV[1].gl_CullDistance[0] = -1.0;\n"
1242 << " gl_MeshVerticesNV[2].gl_CullDistance[0] = -2.0;\n"
1243 << " gl_MeshVerticesNV[3].gl_CullDistance[0] = 1.0;\n"
1244 << " gl_MeshVerticesNV[4].gl_CullDistance[0] = -1.0;\n"
1245 << " gl_MeshVerticesNV[5].gl_CullDistance[0] = -2.0;\n"
1246 << "\n"
1247 // The second cull plane helps paint left and right different.
1248 << " gl_MeshVerticesNV[0].gl_CullDistance[1] = 1.0;\n"
1249 << " gl_MeshVerticesNV[1].gl_CullDistance[1] = 1.0;\n"
1250 << " gl_MeshVerticesNV[2].gl_CullDistance[1] = 1.0;\n"
1251 << " gl_MeshVerticesNV[3].gl_CullDistance[1] = -1.0;\n"
1252 << " gl_MeshVerticesNV[4].gl_CullDistance[1] = -1.0;\n"
1253 << " gl_MeshVerticesNV[5].gl_CullDistance[1] = -1.0;\n"
1254 << "}\n"
1255 ;
1256 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1257 }
1258
1259 // Fragment shader chooses color based on the second cull distance.
1260 {
1261 std::ostringstream frag;
1262 frag
1263 << "#version 460\n"
1264 << "#extension GL_NV_mesh_shader : enable\n"
1265 << "\n"
1266 << "layout (location=0) out vec4 outColor;\n"
1267 << "\n"
1268 << "void main ()\n"
1269 << "{\n"
1270 << " outColor = ((gl_CullDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n"
1271 << "}\n"
1272 ;
1273 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
1274 }
1275 }
1276
createInstance(Context & context) const1277 TestInstance* CullDistanceCase::createInstance (Context& context) const
1278 {
1279 const IterationParams iterationParams =
1280 {
1281 getDefaultExtent(), // VkExtent2D colorExtent;
1282 1u, // uint32_t numLayers;
1283 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1284 false, // bool indirect;
1285 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1286 };
1287
1288 // Must match the shader.
1289 const tcu::Vec4 black (0.0f, 0.0f, 0.0f, 1.0f);
1290 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
1291 const tcu::Vec4 white (1.0f, 1.0f, 1.0f, 1.0f);
1292
1293 return new QuadrantsInstance(context, iterationParams, blue, white, black, black);
1294 }
1295
checkSupport(Context & context) const1296 void CullDistanceCase::checkSupport (Context& context) const
1297 {
1298 MeshShaderBuiltinCase::checkSupport(context);
1299 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE);
1300 }
1301
1302 // Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len).
1303 // Supposes the height of the framebuffer is 1.
triangleForPixel(const std::string & pixel,const std::string & len,const std::string & baseIndex)1304 std::string triangleForPixel(const std::string& pixel, const std::string& len, const std::string& baseIndex)
1305 {
1306 std::ostringstream statements;
1307 statements
1308 << " const float imgWidth = float(" << len << ");\n"
1309 << " const float pixWidth = (2.0 / imgWidth);\n"
1310 << " const float halfPix = (pixWidth / 2.0);\n"
1311 << " const float xCenter = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n"
1312 << " const float xLeft = (xCenter - halfPix);\n"
1313 << " const float xRight = (xCenter + halfPix);\n"
1314 << " const uvec3 indices = uvec3(" << baseIndex << " + 0, " << baseIndex << " + 1, " << baseIndex << " + 2);\n"
1315 << "\n"
1316 << " gl_PrimitiveIndicesNV[indices.x] = indices.x;\n"
1317 << " gl_PrimitiveIndicesNV[indices.y] = indices.y;\n"
1318 << " gl_PrimitiveIndicesNV[indices.z] = indices.z;\n"
1319 << "\n"
1320 << " gl_MeshVerticesNV[indices.x].gl_Position = vec4(xLeft, 0.5, 0.0, 1.0);\n"
1321 << " gl_MeshVerticesNV[indices.y].gl_Position = vec4(xRight, 0.5, 0.0, 1.0);\n"
1322 << " gl_MeshVerticesNV[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n"
1323 ;
1324 return statements.str();
1325 }
1326
1327 // WorkGroupID builtin case.
1328 class WorkGroupIdCase : public MeshShaderBuiltinCase
1329 {
1330 public:
WorkGroupIdCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)1331 WorkGroupIdCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
1332 : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1333 , m_extent (getLinearExtent())
1334 {}
~WorkGroupIdCase(void)1335 virtual ~WorkGroupIdCase (void) {}
1336
1337 void initPrograms (vk::SourceCollections& programCollection) const override;
1338 TestInstance* createInstance (Context& context) const override;
1339
1340 protected:
1341 const VkExtent2D m_extent;
1342 };
1343
initPrograms(vk::SourceCollections & programCollection) const1344 void WorkGroupIdCase::initPrograms (vk::SourceCollections& programCollection) const
1345 {
1346 const std::string taskDataDecl =
1347 "taskNV TaskData {\n"
1348 " uint id;\n"
1349 " uint size;\n"
1350 "} td;\n"
1351 ;
1352
1353 // Mesh shader: each work group fills one pixel.
1354 {
1355 const std::string pixel = (m_taskNeeded ? "td.id" : "gl_WorkGroupID.x" );
1356 const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width) );
1357
1358 std::ostringstream mesh;
1359 mesh
1360 << "#version 460\n"
1361 << "#extension GL_NV_mesh_shader : enable\n"
1362 << "\n"
1363 << "layout (local_size_x=1) in;\n"
1364 << "layout (triangles) out;\n"
1365 << "layout (max_vertices=3, max_primitives=1) out;\n"
1366 << "\n"
1367 << (m_taskNeeded ? ("in " + taskDataDecl) : "")
1368 << "\n"
1369 << "void main ()\n"
1370 << "{\n"
1371 << " gl_PrimitiveCountNV = 1u;\n"
1372 << "\n"
1373 << triangleForPixel(pixel, len, "0")
1374 << "}\n"
1375 ;
1376 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1377 }
1378
1379 if (m_taskNeeded)
1380 {
1381 std::ostringstream task;
1382 task
1383 << "#version 460\n"
1384 << "#extension GL_NV_mesh_shader : enable\n"
1385 << "\n"
1386 << "layout (local_size_x=1) in;\n"
1387 << "\n"
1388 << "out " << taskDataDecl
1389 << "\n"
1390 << "void main ()\n"
1391 << "{\n"
1392 << " gl_TaskCountNV = 1u;\n"
1393 << " td.id = gl_WorkGroupID.x;\n"
1394 << " td.size = " << m_extent.width << ";\n"
1395 << "}\n"
1396 ;
1397 programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1398 }
1399
1400 // Basic fragment shader.
1401 {
1402 const auto frag = getBasicFragShader();
1403 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1404 }
1405 }
1406
createInstance(Context & context) const1407 TestInstance* WorkGroupIdCase::createInstance (Context& context) const
1408 {
1409 // Must match the shader.
1410 const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1411 const IterationParams iterationParams =
1412 {
1413 m_extent, // VkExtent2D colorExtent;
1414 1u, // uint32_t numLayers;
1415 getDefaultDrawCommands(m_extent.width), // DrawCommandVec drawArgs;
1416 false, // bool indirect;
1417 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1418 };
1419 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1420 }
1421
1422 // Variable to use.
1423 enum class LocalInvocation { ID=0, INDEX };
1424
1425 // LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize.
1426 class LocalInvocationCase : public MeshShaderBuiltinCase
1427 {
1428 public:
LocalInvocationCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded,LocalInvocation variable)1429 LocalInvocationCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded, LocalInvocation variable)
1430 : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1431 , m_extent (getLinearExtent())
1432 , m_variable (variable)
1433 {}
~LocalInvocationCase(void)1434 virtual ~LocalInvocationCase (void) {}
1435
1436 void initPrograms (vk::SourceCollections& programCollection) const override;
1437 TestInstance* createInstance (Context& context) const override;
1438
1439 protected:
1440 const VkExtent2D m_extent;
1441 const LocalInvocation m_variable;
1442 };
1443
initPrograms(vk::SourceCollections & programCollection) const1444 void LocalInvocationCase::initPrograms (vk::SourceCollections& programCollection) const
1445 {
1446 // Invocation index to use.
1447 const std::string localIndex = ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex");
1448
1449 // Task data.
1450 std::ostringstream taskDataDecl;
1451 taskDataDecl
1452 << "taskNV TaskData {\n"
1453 // indexNumber[x] == x
1454 << " uint indexNumber[" << m_extent.width << "];\n"
1455 << " uint size;\n"
1456 << "} td;\n"
1457 ;
1458 const auto taskDataDeclStr = taskDataDecl.str();
1459
1460 // Mesh shader: each work group fills one pixel.
1461 {
1462 const std::string pixel = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex);
1463 const std::string len = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x");
1464 const auto localSize = (m_taskNeeded ? 1u : m_extent.width);
1465 const auto maxVert = localSize * 3u;
1466 const std::string baseIndex = (m_taskNeeded ? "0" : "(" + localIndex + " * 3u)");
1467
1468 std::ostringstream mesh;
1469 mesh
1470 << "#version 460\n"
1471 << "#extension GL_NV_mesh_shader : enable\n"
1472 << "\n"
1473 << "layout (local_size_x=" << localSize << ") in;\n"
1474 << "layout (triangles) out;\n"
1475 << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1476 << "\n"
1477 << (m_taskNeeded ? ("in " + taskDataDeclStr) : "")
1478 << "\n"
1479 << "void main ()\n"
1480 << "{\n"
1481 << " gl_PrimitiveCountNV = " << localSize << ";\n"
1482 << "\n"
1483 << triangleForPixel(pixel, len, baseIndex)
1484 << "}\n"
1485 ;
1486 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1487 }
1488
1489 if (m_taskNeeded)
1490 {
1491 std::ostringstream task;
1492 task
1493 << "#version 460\n"
1494 << "#extension GL_NV_mesh_shader : enable\n"
1495 << "\n"
1496 << "layout (local_size_x=" << m_extent.width << ") in;\n"
1497 << "\n"
1498 << "out " << taskDataDeclStr
1499 << "\n"
1500 << "void main ()\n"
1501 << "{\n"
1502 << " gl_TaskCountNV = " << m_extent.width << ";\n"
1503 << " td.indexNumber[" << localIndex << "] = " << localIndex << ";\n"
1504 << " td.size = gl_WorkGroupSize.x;\n"
1505 << "}\n"
1506 ;
1507 programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1508 }
1509
1510 // Basic fragment shader.
1511 {
1512 const auto frag = getBasicFragShader();
1513 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1514 }
1515 }
1516
createInstance(Context & context) const1517 TestInstance* LocalInvocationCase::createInstance (Context& context) const
1518 {
1519 // Must match the shader.
1520 const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1521 const IterationParams iterationParams =
1522 {
1523 m_extent, // VkExtent2D colorExtent;
1524 1u, // uint32_t numLayers;
1525 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1526 false, // bool indirect;
1527 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1528 };
1529 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1530 }
1531
1532 // GlobalInvocationId builtin case.
1533 class GlobalInvocationIdCase : public MeshShaderBuiltinCase
1534 {
1535 public:
GlobalInvocationIdCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)1536 GlobalInvocationIdCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
1537 : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1538 , m_jobSize (getLargeJobSize())
1539 , m_extent {m_jobSize.numTasks * m_jobSize.localSize, 1u}
1540 {}
~GlobalInvocationIdCase(void)1541 virtual ~GlobalInvocationIdCase (void) {}
1542
1543 void initPrograms (vk::SourceCollections& programCollection) const override;
1544 TestInstance* createInstance (Context& context) const override;
1545
1546 protected:
1547 const JobSize m_jobSize;
1548 const VkExtent2D m_extent;
1549 };
1550
initPrograms(vk::SourceCollections & programCollection) const1551 void GlobalInvocationIdCase::initPrograms (vk::SourceCollections& programCollection) const
1552 {
1553 const auto& localSize = m_jobSize.localSize;
1554
1555 // Task data.
1556 std::ostringstream taskDataDecl;
1557 taskDataDecl
1558 << "taskNV TaskData {\n"
1559 << " uint pixelId[" << localSize << "];\n"
1560 << " uint size;\n"
1561 << "} td;\n"
1562 ;
1563 const auto taskDataDeclStr = taskDataDecl.str();
1564
1565 // Mesh shader: each work group fills one pixel.
1566 {
1567 const std::string pixel = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x");
1568 const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1569 const std::string baseIndex = "(gl_LocalInvocationIndex * 3u)";
1570 const auto maxVert = localSize * 3u;
1571
1572 std::ostringstream mesh;
1573 mesh
1574 << "#version 460\n"
1575 << "#extension GL_NV_mesh_shader : enable\n"
1576 << "\n"
1577 << "layout (local_size_x=" << localSize << ") in;\n"
1578 << "layout (triangles) out;\n"
1579 << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1580 << "\n"
1581 << (m_taskNeeded ? ("in " + taskDataDeclStr) : "")
1582 << "\n"
1583 << "void main ()\n"
1584 << "{\n"
1585 << " gl_PrimitiveCountNV = " << localSize << ";\n"
1586 << "\n"
1587 << triangleForPixel(pixel, len, baseIndex)
1588 << "}\n"
1589 ;
1590 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1591 }
1592
1593 if (m_taskNeeded)
1594 {
1595 std::ostringstream task;
1596 task
1597 << "#version 460\n"
1598 << "#extension GL_NV_mesh_shader : enable\n"
1599 << "\n"
1600 << "layout (local_size_x=" << localSize << ") in;\n"
1601 << "\n"
1602 << "out " << taskDataDeclStr
1603 << "\n"
1604 << "void main ()\n"
1605 << "{\n"
1606 << " gl_TaskCountNV = 1;\n"
1607 << " td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n"
1608 << " td.size = " << m_extent.width << ";\n"
1609 << "}\n"
1610 ;
1611 programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1612 }
1613
1614 // Basic fragment shader.
1615 {
1616 const auto frag = getBasicFragShader();
1617 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1618 }
1619 }
1620
createInstance(Context & context) const1621 TestInstance* GlobalInvocationIdCase::createInstance (Context& context) const
1622 {
1623 // Must match the shader.
1624 const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1625 const IterationParams iterationParams =
1626 {
1627 m_extent, // VkExtent2D colorExtent;
1628 1u, // uint32_t numLayers;
1629 getDefaultDrawCommands(m_jobSize.numTasks), // DrawCommandVec drawArgs;
1630 false, // bool indirect;
1631 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1632 };
1633 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1634 }
1635
1636 // DrawIndex builtin case.
1637 class DrawIndexCase : public MeshShaderBuiltinCase
1638 {
1639 public:
DrawIndexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)1640 DrawIndexCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
1641 : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1642 , m_extent (getLinearExtent())
1643 {}
~DrawIndexCase(void)1644 virtual ~DrawIndexCase (void) {}
1645
1646 void initPrograms (vk::SourceCollections& programCollection) const override;
1647 TestInstance* createInstance (Context& context) const override;
1648
1649 protected:
1650 const VkExtent2D m_extent;
1651 };
1652
initPrograms(vk::SourceCollections & programCollection) const1653 void DrawIndexCase::initPrograms (vk::SourceCollections& programCollection) const
1654 {
1655 const std::string taskDataDecl =
1656 "taskNV TaskData {\n"
1657 " uint id;\n"
1658 " uint size;\n"
1659 "} td;\n"
1660 ;
1661
1662 const auto drawIndex = "uint(gl_DrawID)";
1663
1664 // Mesh shader: each work group fills one pixel.
1665 {
1666 const std::string pixel = (m_taskNeeded ? "td.id" : drawIndex);
1667 const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1668
1669 std::ostringstream mesh;
1670 mesh
1671 << "#version 460\n"
1672 << "#extension GL_NV_mesh_shader : enable\n"
1673 << "\n"
1674 << "layout (local_size_x=1) in;\n"
1675 << "layout (triangles) out;\n"
1676 << "layout (max_vertices=3, max_primitives=1) out;\n"
1677 << "\n"
1678 << (m_taskNeeded ? ("in " + taskDataDecl) : "")
1679 << "\n"
1680 << "void main ()\n"
1681 << "{\n"
1682 << " gl_PrimitiveCountNV = 1u;\n"
1683 << "\n"
1684 << triangleForPixel(pixel, len, "0")
1685 << "}\n"
1686 ;
1687 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1688 }
1689
1690 if (m_taskNeeded)
1691 {
1692 std::ostringstream task;
1693 task
1694 << "#version 460\n"
1695 << "#extension GL_NV_mesh_shader : enable\n"
1696 << "\n"
1697 << "layout (local_size_x=1) in;\n"
1698 << "\n"
1699 << "out " << taskDataDecl
1700 << "\n"
1701 << "void main ()\n"
1702 << "{\n"
1703 << " gl_TaskCountNV = 1u;\n"
1704 << " td.id = " << drawIndex << ";\n"
1705 << " td.size = " << m_extent.width << ";\n"
1706 << "}\n"
1707 ;
1708 programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1709 }
1710
1711 // Basic fragment shader.
1712 {
1713 const auto frag = getBasicFragShader();
1714 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1715 }
1716 }
1717
createInstance(Context & context) const1718 TestInstance* DrawIndexCase::createInstance (Context& context) const
1719 {
1720 // Must match the shader.
1721 const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1722 const DrawCommandVec commands (m_extent.width, makeDrawMeshTasksIndirectCommandNV(1u, 0u));
1723 const IterationParams iterationParams =
1724 {
1725 m_extent, // VkExtent2D colorExtent;
1726 1u, // uint32_t numLayers;
1727 commands, // DrawCommandVec drawArgs;
1728 true, // bool indirect;
1729 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1730 };
1731 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1732 }
1733
1734 } // anonymous
1735
createMeshShaderBuiltinTests(tcu::TestContext & testCtx)1736 tcu::TestCaseGroup* createMeshShaderBuiltinTests (tcu::TestContext& testCtx)
1737 {
1738 GroupPtr mainGroup (new tcu::TestCaseGroup(testCtx, "builtin", "Mesh Shader Builtin Tests"));
1739
1740 mainGroup->addChild(new PositionCase (testCtx, "position", ""));
1741 mainGroup->addChild(new PointSizeCase (testCtx, "point_size", ""));
1742 mainGroup->addChild(new ClipDistanceCase (testCtx, "clip_distance", ""));
1743 mainGroup->addChild(new CullDistanceCase (testCtx, "cull_distance", ""));
1744 mainGroup->addChild(new PrimitiveIdCase (testCtx, "primitive_id_glsl", "", true/*glslFrag*/));
1745 mainGroup->addChild(new PrimitiveIdCase (testCtx, "primitive_id_spirv", "", false/*glslFrag*/));
1746 mainGroup->addChild(new LayerCase (testCtx, "layer", "", false/*shareVertices*/));
1747 mainGroup->addChild(new LayerCase (testCtx, "layer_shared", "", true/*shareVertices*/));
1748 mainGroup->addChild(new ViewportIndexCase (testCtx, "viewport_index", "", false/*shareVertices*/));
1749 mainGroup->addChild(new ViewportIndexCase (testCtx, "viewport_index_shared", "", true/*shareVertices*/));
1750 mainGroup->addChild(new WorkGroupIdCase (testCtx, "work_group_id_in_mesh", "", false/*taskNeeded*/));
1751 mainGroup->addChild(new WorkGroupIdCase (testCtx, "work_group_id_in_task", "", true/*taskNeeded*/));
1752 mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_id_in_mesh", "", false/*taskNeeded*/, LocalInvocation::ID));
1753 mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_id_in_task", "", true/*taskNeeded*/, LocalInvocation::ID));
1754 mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_index_in_task", "", true/*taskNeeded*/, LocalInvocation::INDEX));
1755 mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_index_in_mesh", "", false/*taskNeeded*/, LocalInvocation::INDEX));
1756 mainGroup->addChild(new GlobalInvocationIdCase (testCtx, "global_invocation_id_in_mesh", "", false/*taskNeeded*/));
1757 mainGroup->addChild(new GlobalInvocationIdCase (testCtx, "global_invocation_id_in_task", "", true/*taskNeeded*/));
1758 mainGroup->addChild(new DrawIndexCase (testCtx, "draw_index_in_mesh", "", false/*taskNeeded*/));
1759 mainGroup->addChild(new DrawIndexCase (testCtx, "draw_index_in_task", "", true/*taskNeeded*/));
1760
1761 return mainGroup.release();
1762 }
1763
1764 } // MeshShader
1765 } // vkt
1766