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 for VK_EXT_mesh_shader
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktMeshShaderBuiltinTestsEXT.hpp"
26 #include "vktMeshShaderUtil.hpp"
27 #include "vktTestCase.hpp"
28
29 #include "vkTypeUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkObjUtil.hpp"
32 #include "vkBuilderUtil.hpp"
33 #include "vkImageWithMemory.hpp"
34 #include "vkBufferWithMemory.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkBarrierUtil.hpp"
37
38 #include "tcuTexture.hpp"
39 #include "tcuTestLog.hpp"
40
41 #include <vector>
42 #include <algorithm>
43 #include <sstream>
44 #include <map>
45 #include <utility>
46 #include <sstream>
47
48 namespace vkt
49 {
50 namespace MeshShader
51 {
52
53 namespace
54 {
55
56 // Wraps a tcu::IVec2 with a custom operator< that uses the X and Y components in component order so it can be used as a map key.
57 // Can be converted to and from a tcu::IVec2 automatically.
58 class CoordKey
59 {
60 public:
CoordKey(const tcu::IVec2 & coords)61 CoordKey(const tcu::IVec2 &coords) : m_coords(coords)
62 {
63 }
64
operator tcu::IVec2() const65 operator tcu::IVec2() const
66 {
67 return m_coords;
68 }
69
operator <(const CoordKey & other) const70 bool operator<(const CoordKey &other) const
71 {
72 const auto &a = this->m_coords;
73 const auto &b = other.m_coords;
74
75 for (int i = 0; i < tcu::IVec2::SIZE; ++i)
76 {
77 if (a[i] < b[i])
78 return true;
79 if (a[i] > b[i])
80 return false;
81 }
82
83 return false;
84 }
85
86 private:
87 const tcu::IVec2 m_coords;
88 };
89
90 using namespace vk;
91
92 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
93 using DrawCommandVec = std::vector<VkDrawMeshTasksIndirectCommandEXT>;
94 using ImageWithMemoryPtr = de::MovePtr<ImageWithMemory>;
95 using BufferWithMemoryPtr = de::MovePtr<BufferWithMemory>;
96 using ViewportVec = std::vector<VkViewport>;
97 using ColorVec = std::vector<tcu::Vec4>;
98 using PixelMap = std::map<CoordKey, tcu::Vec4>; // Coordinates to color.
99
getClearColor()100 tcu::Vec4 getClearColor()
101 {
102 return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
103 }
104
getDefaultExtent()105 VkExtent2D getDefaultExtent()
106 {
107 return makeExtent2D(8u, 8u);
108 }
109
getLinearExtent()110 VkExtent2D getLinearExtent()
111 {
112 return makeExtent2D(8u, 1u);
113 }
114
115 struct JobSize
116 {
117 uint32_t numTasks;
118 uint32_t localSize;
119 };
120
getLargeJobSize()121 JobSize getLargeJobSize()
122 {
123 return JobSize{8u, 8u};
124 }
125
126 // Single draw command with the given number of tasks, 1 by default.
getDefaultDrawCommands(uint32_t taskCount=1u)127 DrawCommandVec getDefaultDrawCommands(uint32_t taskCount = 1u)
128 {
129 return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandEXT(taskCount, 1u, 1u));
130 }
131
132 // Basic fragment shader that draws fragments in blue.
getBasicFragShader()133 std::string getBasicFragShader()
134 {
135 return "#version 460\n"
136 "layout (location=0) out vec4 outColor;\n"
137 "void main ()\n"
138 "{\n"
139 " outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
140 "}\n";
141 }
142
143 struct IterationParams
144 {
145 VkExtent2D colorExtent;
146 uint32_t numLayers;
147 bool multiview;
148 bool indirect;
149 tcu::Maybe<FragmentSize> fragmentSize;
150 DrawCommandVec drawArgs;
151 ViewportVec viewports; // If empty, a single default viewport is used.
152 };
153
154 class MeshShaderBuiltinInstance : public vkt::TestInstance
155 {
156 public:
MeshShaderBuiltinInstance(Context & context,const IterationParams & params)157 MeshShaderBuiltinInstance(Context &context, const IterationParams ¶ms)
158 : vkt::TestInstance(context)
159 , m_params(params)
160 {
161 }
~MeshShaderBuiltinInstance(void)162 virtual ~MeshShaderBuiltinInstance(void)
163 {
164 }
165
166 tcu::TestStatus iterate() override;
167 virtual void verifyResults(const tcu::ConstPixelBufferAccess &result) = 0;
168
169 protected:
170 IterationParams m_params;
171 };
172
createCustomRenderPass(const DeviceInterface & vkd,VkDevice device,VkFormat format,bool multiview,uint32_t numLayers)173 Move<VkRenderPass> createCustomRenderPass(const DeviceInterface &vkd, VkDevice device, VkFormat format, bool multiview,
174 uint32_t numLayers)
175 {
176 DE_ASSERT(numLayers > 0u);
177 const uint32_t numSubpasses = (multiview ? numLayers : 1u);
178
179 const VkAttachmentDescription colorAttachmentDescription = {
180 0u, // VkAttachmentDescriptionFlags flags
181 format, // VkFormat format
182 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
183 VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp
184 VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
185 VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
186 VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
187 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout
188 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout
189 };
190
191 const VkAttachmentReference colorAttachmentRef =
192 makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
193
194 const VkSubpassDescription subpassDescription = {
195 0u, // VkSubpassDescriptionFlags flags
196 VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
197 0u, // uint32_t inputAttachmentCount
198 nullptr, // const VkAttachmentReference* pInputAttachments
199 1u, // uint32_t colorAttachmentCount
200 &colorAttachmentRef, // const VkAttachmentReference* pColorAttachments
201 nullptr, // const VkAttachmentReference* pResolveAttachments
202 nullptr, // const VkAttachmentReference* pDepthStencilAttachment
203 0u, // uint32_t preserveAttachmentCount
204 nullptr // const uint32_t* pPreserveAttachments
205 };
206
207 std::vector<VkSubpassDescription> subpassDescriptions;
208
209 subpassDescriptions.reserve(numSubpasses);
210 for (uint32_t i = 0; i < numSubpasses; ++i)
211 subpassDescriptions.push_back(subpassDescription);
212
213 std::vector<VkSubpassDependency> dependencies;
214
215 for (uint32_t subpassIdx = 1u; subpassIdx < numSubpasses; ++subpassIdx)
216 {
217 const uint32_t prev = subpassIdx - 1u;
218 const VkSubpassDependency colorDep = {
219 prev, // uint32_t srcSubpass;
220 subpassIdx, // uint32_t dstSubpass;
221 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask;
222 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags dstStageMask;
223 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask;
224 (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
225 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask;
226 VK_DEPENDENCY_BY_REGION_BIT, // VkDependencyFlags dependencyFlags;
227 };
228 dependencies.push_back(colorDep);
229 }
230
231 using MultiviewInfoPtr = de::MovePtr<VkRenderPassMultiviewCreateInfo>;
232
233 MultiviewInfoPtr multiviewCreateInfo;
234 std::vector<uint32_t> viewMasks;
235
236 if (multiview)
237 {
238 multiviewCreateInfo = MultiviewInfoPtr(new VkRenderPassMultiviewCreateInfo);
239 *multiviewCreateInfo = initVulkanStructure();
240
241 viewMasks.resize(subpassDescriptions.size());
242 for (uint32_t subpassIdx = 0u; subpassIdx < static_cast<uint32_t>(viewMasks.size()); ++subpassIdx)
243 viewMasks[subpassIdx] = (1u << subpassIdx);
244
245 multiviewCreateInfo->subpassCount = static_cast<uint32_t>(viewMasks.size());
246 multiviewCreateInfo->pViewMasks = de::dataOrNull(viewMasks);
247 }
248
249 const VkRenderPassCreateInfo renderPassInfo = {
250 VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
251 multiviewCreateInfo.get(), // const void* pNext
252 0u, // VkRenderPassCreateFlags flags
253 1u, // uint32_t attachmentCount
254 &colorAttachmentDescription, // const VkAttachmentDescription* pAttachments
255 static_cast<uint32_t>(subpassDescriptions.size()), // uint32_t subpassCount
256 de::dataOrNull(subpassDescriptions), // const VkSubpassDescription* pSubpasses
257 static_cast<uint32_t>(dependencies.size()), // uint32_t dependencyCount
258 de::dataOrNull(dependencies), // const VkSubpassDependency* pDependencies
259 };
260
261 return createRenderPass(vkd, device, &renderPassInfo);
262 }
263
iterate()264 tcu::TestStatus MeshShaderBuiltinInstance::iterate()
265 {
266 const auto &vkd = m_context.getDeviceInterface();
267 const auto device = m_context.getDevice();
268 auto &alloc = m_context.getDefaultAllocator();
269 const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
270 const auto queue = m_context.getUniversalQueue();
271 const auto &binaries = m_context.getBinaryCollection();
272
273 const auto useTask = binaries.contains("task");
274 const auto useFrag = binaries.contains("frag");
275 const auto extent = makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u);
276 const auto iExtent3D = tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height),
277 static_cast<int>(m_params.numLayers));
278 const auto format = VK_FORMAT_R8G8B8A8_UNORM;
279 const auto tcuFormat = mapVkFormat(format);
280 const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
281 const auto viewType = ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
282 const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
283 const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
284 const auto numPasses = (m_params.multiview ? m_params.numLayers : 1u);
285 const tcu::Vec4 clearColor = getClearColor();
286
287 ImageWithMemoryPtr colorBuffer;
288 Move<VkImageView> colorBufferView;
289 {
290 const VkImageCreateInfo colorBufferInfo = {
291 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
292 nullptr, // const void* pNext;
293 0u, // VkImageCreateFlags flags;
294 VK_IMAGE_TYPE_2D, // VkImageType imageType;
295 format, // VkFormat format;
296 extent, // VkExtent3D extent;
297 1u, // uint32_t mipLevels;
298 m_params.numLayers, // uint32_t arrayLayers;
299 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
300 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
301 colorUsage, // VkImageUsageFlags usage;
302 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
303 0u, // uint32_t queueFamilyIndexCount;
304 nullptr, // const uint32_t* pQueueFamilyIndices;
305 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
306 };
307 colorBuffer =
308 ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any));
309 colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR);
310 }
311
312 // Empty descriptor set layout.
313 DescriptorSetLayoutBuilder layoutBuilder;
314 const auto setLayout = layoutBuilder.build(vkd, device);
315
316 // Pipeline layout.
317 const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
318
319 // Render pass and framebuffer.
320 const auto renderPass = createCustomRenderPass(vkd, device, format, m_params.multiview, m_params.numLayers);
321 const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width,
322 extent.height, (m_params.multiview ? 1u : m_params.numLayers));
323
324 // Pipeline.
325 Move<VkShaderModule> taskModule;
326 Move<VkShaderModule> meshModule;
327 Move<VkShaderModule> fragModule;
328
329 if (useTask)
330 taskModule = createShaderModule(vkd, device, binaries.get("task"));
331 if (useFrag)
332 fragModule = createShaderModule(vkd, device, binaries.get("frag"));
333 meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
334
335 std::vector<VkViewport> viewports;
336 std::vector<VkRect2D> scissors;
337 if (m_params.viewports.empty())
338 {
339 // Default ones.
340 viewports.push_back(makeViewport(extent));
341 scissors.push_back(makeRect2D(extent));
342 }
343 else
344 {
345 // The desired viewports and the same number of default scissors.
346 viewports.reserve(m_params.viewports.size());
347 std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports));
348 scissors.resize(viewports.size(), makeRect2D(extent));
349 }
350
351 using ShadingRateInfoPtr = de::MovePtr<VkPipelineFragmentShadingRateStateCreateInfoKHR>;
352 ShadingRateInfoPtr pNext;
353 if (static_cast<bool>(m_params.fragmentSize))
354 {
355 pNext = ShadingRateInfoPtr(new VkPipelineFragmentShadingRateStateCreateInfoKHR);
356 *pNext = initVulkanStructure();
357
358 pNext->fragmentSize = getShadingRateSize(m_params.fragmentSize.get());
359 pNext->combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
360 pNext->combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
361 }
362
363 // Pipelines.
364 std::vector<Move<VkPipeline>> pipelines;
365 pipelines.reserve(numPasses);
366 for (uint32_t subpassIdx = 0u; subpassIdx < numPasses; ++subpassIdx)
367 {
368 pipelines.emplace_back(makeGraphicsPipeline(
369 vkd, device, pipelineLayout.get(), taskModule.get(), meshModule.get(), fragModule.get(), renderPass.get(),
370 viewports, scissors, subpassIdx, nullptr, nullptr, nullptr, nullptr, nullptr, 0u, pNext.get()));
371 }
372
373 // Command pool and buffer.
374 const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
375 const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
376 const auto cmdBuffer = cmdBufferPtr.get();
377
378 // Indirect buffer if needed.
379 BufferWithMemoryPtr indirectBuffer;
380
381 DE_ASSERT(!m_params.drawArgs.empty());
382 if (m_params.indirect)
383 {
384 // Indirect draws.
385 const auto indirectBufferSize = static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs));
386 const auto indirectBufferUsage = (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
387 const auto indirectBufferInfo = makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage);
388 indirectBuffer = BufferWithMemoryPtr(
389 new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible));
390 auto &indirectBufferAlloc = indirectBuffer->getAllocation();
391 void *indirectBufferData = indirectBufferAlloc.getHostPtr();
392
393 deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize));
394 flushAlloc(vkd, device, indirectBufferAlloc);
395 }
396
397 // Submit commands.
398 beginCommandBuffer(vkd, cmdBuffer);
399 beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
400
401 for (uint32_t subpassIdx = 0u; subpassIdx < numPasses; ++subpassIdx)
402 {
403 if (subpassIdx > 0u)
404 vkd.cmdNextSubpass(cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
405
406 vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[subpassIdx].get());
407
408 if (!m_params.indirect)
409 {
410 for (const auto &command : m_params.drawArgs)
411 vkd.cmdDrawMeshTasksEXT(cmdBuffer, command.groupCountX, command.groupCountY, command.groupCountZ);
412 }
413 else
414 {
415 const auto numDraws = static_cast<uint32_t>(m_params.drawArgs.size());
416 const auto stride = static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type));
417 vkd.cmdDrawMeshTasksIndirectEXT(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride);
418 }
419 }
420
421 endRenderPass(vkd, cmdBuffer);
422
423 // Output buffer to extract the color buffer contents.
424 BufferWithMemoryPtr outBuffer;
425 void *outBufferData = nullptr;
426 {
427 const auto layerSize = static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) *
428 extent.width * extent.height);
429 const auto outBufferSize = layerSize * m_params.numLayers;
430 const auto outBufferUsage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
431 const auto outBufferInfo = makeBufferCreateInfo(outBufferSize, outBufferUsage);
432
433 outBuffer = BufferWithMemoryPtr(
434 new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible));
435 outBufferData = outBuffer->getAllocation().getHostPtr();
436 }
437
438 // Transition image layout.
439 const auto preTransferBarrier = makeImageMemoryBarrier(
440 (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT,
441 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer->get(), colorSRR);
442
443 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
444 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier);
445
446 // Copy image to output buffer.
447 const std::vector<VkBufferImageCopy> regions(1u, makeBufferImageCopy(extent, colorSRL));
448 vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(),
449 static_cast<uint32_t>(regions.size()), de::dataOrNull(regions));
450
451 // Transfer to host barrier.
452 const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
453 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u,
454 &postTransferBarrier, 0u, nullptr, 0u, nullptr);
455
456 endCommandBuffer(vkd, cmdBuffer);
457 submitCommandsAndWait(vkd, device, queue, cmdBuffer);
458
459 // Invalidate alloc and verify result.
460 {
461 auto &outBufferAlloc = outBuffer->getAllocation();
462 invalidateAlloc(vkd, device, outBufferAlloc);
463
464 tcu::ConstPixelBufferAccess result(tcuFormat, iExtent3D, outBufferData);
465 verifyResults(result);
466 }
467
468 return tcu::TestStatus::pass("Pass");
469 }
470
471 // Abstract case that implements the generic checkSupport method.
472 class MeshShaderBuiltinCase : public vkt::TestCase
473 {
474 public:
MeshShaderBuiltinCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)475 MeshShaderBuiltinCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
476 : vkt::TestCase(testCtx, name)
477 , m_taskNeeded(taskNeeded)
478 {
479 }
~MeshShaderBuiltinCase(void)480 virtual ~MeshShaderBuiltinCase(void)
481 {
482 }
483
484 void checkSupport(Context &context) const override;
485
486 protected:
487 const bool m_taskNeeded;
488 };
489
checkSupport(Context & context) const490 void MeshShaderBuiltinCase::checkSupport(Context &context) const
491 {
492 checkTaskMeshShaderSupportEXT(context, m_taskNeeded, true);
493 }
494
495 // Instance that verifies color layers.
496 class FullScreenColorInstance : public MeshShaderBuiltinInstance
497 {
498 public:
FullScreenColorInstance(Context & context,const IterationParams & params,const ColorVec & expectedColors)499 FullScreenColorInstance(Context &context, const IterationParams ¶ms, const ColorVec &expectedColors)
500 : MeshShaderBuiltinInstance(context, params)
501 , m_expectedColors(expectedColors)
502 {
503 }
~FullScreenColorInstance(void)504 virtual ~FullScreenColorInstance(void)
505 {
506 }
507
508 void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
509
510 protected:
511 const ColorVec m_expectedColors;
512 };
513
verifyResults(const tcu::ConstPixelBufferAccess & result)514 void FullScreenColorInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
515 {
516 auto &log = m_context.getTestContext().getLog();
517 bool fail = false;
518 const auto width = result.getWidth();
519 const auto height = result.getHeight();
520 const auto depth = result.getDepth();
521
522 for (int z = 0; z < depth; ++z)
523 {
524 const auto &expected = m_expectedColors.at(z);
525
526 for (int y = 0; y < height; ++y)
527 for (int x = 0; x < width; ++x)
528 {
529 const auto resultColor = result.getPixel(x, y, z);
530 if (resultColor != expected)
531 {
532 std::ostringstream msg;
533 msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected
534 << " and found " << resultColor;
535 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
536 fail = true;
537 }
538 }
539 }
540
541 if (fail)
542 {
543 log << tcu::TestLog::Image("Result", "", result);
544 TCU_FAIL("Check log for details");
545 }
546 }
547
548 // Instance that verifies single-layer framebuffers divided into 4 quadrants.
549 class QuadrantsInstance : public MeshShaderBuiltinInstance
550 {
551 public:
QuadrantsInstance(Context & context,const IterationParams & params,const tcu::Vec4 topLeft,const tcu::Vec4 topRight,const tcu::Vec4 bottomLeft,const tcu::Vec4 bottomRight)552 QuadrantsInstance(Context &context, const IterationParams ¶ms, const tcu::Vec4 topLeft,
553 const tcu::Vec4 topRight, const tcu::Vec4 bottomLeft, const tcu::Vec4 bottomRight)
554 : MeshShaderBuiltinInstance(context, params)
555 , m_topLeft(topLeft)
556 , m_topRight(topRight)
557 , m_bottomLeft(bottomLeft)
558 , m_bottomRight(bottomRight)
559 {
560 }
~QuadrantsInstance(void)561 virtual ~QuadrantsInstance(void)
562 {
563 }
564
565 void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
566
567 protected:
568 const tcu::Vec4 m_topLeft;
569 const tcu::Vec4 m_topRight;
570 const tcu::Vec4 m_bottomLeft;
571 const tcu::Vec4 m_bottomRight;
572 };
573
verifyResults(const tcu::ConstPixelBufferAccess & result)574 void QuadrantsInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
575 {
576 auto &log = m_context.getTestContext().getLog();
577 bool fail = false;
578 const auto width = result.getWidth();
579 const auto height = result.getHeight();
580 const auto depth = result.getDepth();
581
582 DE_ASSERT(depth == 1);
583 DE_ASSERT(width > 0 && width % 2 == 0);
584 DE_ASSERT(height > 0 && height % 2 == 0);
585 DE_UNREF(depth); // For release builds.
586
587 const auto halfWidth = width / 2;
588 const auto halfHeight = height / 2;
589 tcu::Vec4 expected;
590
591 for (int y = 0; y < height; ++y)
592 for (int x = 0; x < width; ++x)
593 {
594 // Choose the right quadrant
595 if (y < halfHeight)
596 expected = ((x < halfWidth) ? m_topLeft : m_topRight);
597 else
598 expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight);
599
600 const auto resultColor = result.getPixel(x, y);
601 if (resultColor != expected)
602 {
603 std::ostringstream msg;
604 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
605 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
606 fail = true;
607 }
608 }
609
610 if (fail)
611 {
612 log << tcu::TestLog::Image("Result", "", result);
613 TCU_FAIL("Check log for details");
614 }
615 }
616
617 // Instance that verifies single-layer framebuffers with specific pixels set to some color.
618 struct PixelVerifierParams
619 {
620 const tcu::Vec4 background;
621 const PixelMap pixelMap;
622 };
623
624 class PixelsInstance : public MeshShaderBuiltinInstance
625 {
626 public:
PixelsInstance(Context & context,const IterationParams & params,const PixelVerifierParams & pixelParams)627 PixelsInstance(Context &context, const IterationParams ¶ms, const PixelVerifierParams &pixelParams)
628 : MeshShaderBuiltinInstance(context, params)
629 , m_pixelParams(pixelParams)
630 {
631 }
~PixelsInstance(void)632 virtual ~PixelsInstance(void)
633 {
634 }
635
636 void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
637
638 protected:
639 const PixelVerifierParams m_pixelParams;
640 };
641
verifyResults(const tcu::ConstPixelBufferAccess & result)642 void PixelsInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
643 {
644 auto &log = m_context.getTestContext().getLog();
645 bool fail = false;
646 const auto width = result.getWidth();
647 const auto height = result.getHeight();
648 const auto depth = result.getDepth();
649
650 DE_ASSERT(depth == 1);
651 DE_UNREF(depth); // For release builds.
652
653 for (int y = 0; y < height; ++y)
654 for (int x = 0; x < width; ++x)
655 {
656 const tcu::IVec2 coords(x, y);
657 const auto iter = m_pixelParams.pixelMap.find(coords);
658 const auto expected = ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second);
659 const auto resultColor = result.getPixel(x, y);
660
661 if (resultColor != expected)
662 {
663 std::ostringstream msg;
664 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
665 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
666 fail = true;
667 }
668 }
669
670 if (fail)
671 {
672 log << tcu::TestLog::Image("Result", "", result);
673 TCU_FAIL("Check log for details");
674 }
675 }
676
677 // Primitive ID case.
678 class PrimitiveIdCase : public MeshShaderBuiltinCase
679 {
680 public:
PrimitiveIdCase(tcu::TestContext & testCtx,const std::string & name,bool glslFrag)681 PrimitiveIdCase(tcu::TestContext &testCtx, const std::string &name, bool glslFrag)
682 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
683 , m_glslFrag(glslFrag)
684 {
685 }
~PrimitiveIdCase(void)686 virtual ~PrimitiveIdCase(void)
687 {
688 }
689
690 void initPrograms(vk::SourceCollections &programCollection) const override;
691 void checkSupport(Context &context) const override;
692 TestInstance *createInstance(Context &context) const override;
693
694 protected:
695 // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
696 const bool m_glslFrag;
697 };
698
initPrograms(vk::SourceCollections & programCollection) const699 void PrimitiveIdCase::initPrograms(vk::SourceCollections &programCollection) const
700 {
701 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
702 const auto spvBuildOptions = getMinMeshEXTSpvBuildOptions(programCollection.usedVulkanVersion);
703
704 // Mesh shader.
705 {
706 std::ostringstream mesh;
707 mesh << "#version 460\n"
708 << "#extension GL_EXT_mesh_shader : enable\n"
709 << "\n"
710 << "layout (local_size_x=1) in;\n"
711 << "layout (triangles) out;\n"
712 << "layout (max_vertices=3, max_primitives=1) out;\n"
713 << "\n"
714 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
715 << " int gl_PrimitiveID;\n"
716 << "} gl_MeshPrimitivesEXT[];\n"
717 << "\n"
718 << "void main ()\n"
719 << "{\n"
720 << " SetMeshOutputsEXT(3u, 1u);\n"
721 << "\n"
722 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
723 << "\n"
724 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
725 << " gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
726 << " gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
727 << "\n"
728 // Sets an arbitrary primitive id.
729 << " gl_MeshPrimitivesEXT[0].gl_PrimitiveID = 1629198956;\n"
730 << "}\n";
731 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
732 }
733
734 // Frag shader.
735 if (m_glslFrag)
736 {
737 std::ostringstream frag;
738 frag << "#version 460\n"
739 << "#extension GL_EXT_mesh_shader : enable\n"
740 << "\n"
741 << "layout (location=0) out vec4 outColor;\n"
742 << "\n"
743 << "void main ()\n"
744 << "{\n"
745 // Checks the primitive id matches.
746 << " outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, "
747 "1.0));\n"
748 << "}\n";
749 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
750 }
751 else
752 {
753 // This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingEXT in order to
754 // access gl_PrimitiveID. This also needs the SPV_EXT_mesh_shader extension.
755 std::ostringstream frag;
756 frag << "; Version: 1.0\n"
757 << "; Generator: Khronos Glslang Reference Front End; 10\n"
758 << "; Bound: 24\n"
759 << "; Schema: 0\n"
760 << " OpCapability Shader\n"
761
762 // Manual change in these lines.
763 //<< " OpCapability Geometry\n"
764 << " OpCapability MeshShadingEXT\n"
765 << " OpExtension \"SPV_EXT_mesh_shader\"\n"
766
767 << " %1 = OpExtInstImport \"GLSL.std.450\"\n"
768 << " OpMemoryModel Logical GLSL450\n"
769 << " OpEntryPoint Fragment %4 \"main\" %9 %12\n"
770 << " OpExecutionMode %4 OriginUpperLeft\n"
771 << " OpDecorate %9 Location 0\n"
772 << " OpDecorate %12 Flat\n"
773 << " OpDecorate %12 BuiltIn PrimitiveId\n"
774 << " %2 = OpTypeVoid\n"
775 << " %3 = OpTypeFunction %2\n"
776 << " %6 = OpTypeFloat 32\n"
777 << " %7 = OpTypeVector %6 4\n"
778 << " %8 = OpTypePointer Output %7\n"
779 << " %9 = OpVariable %8 Output\n"
780 << "%10 = OpTypeInt 32 1\n"
781 << "%11 = OpTypePointer Input %10\n"
782 << "%12 = OpVariable %11 Input\n"
783 << "%14 = OpConstant %10 1629198956\n"
784 << "%15 = OpTypeBool\n"
785 << "%17 = OpConstant %6 0\n"
786 << "%18 = OpConstant %6 1\n"
787 << "%19 = OpConstantComposite %7 %17 %17 %18 %18\n"
788 << "%20 = OpConstantComposite %7 %17 %17 %17 %18\n"
789 << "%21 = OpTypeVector %15 4\n"
790 << " %4 = OpFunction %2 None %3\n"
791 << " %5 = OpLabel\n"
792 << "%13 = OpLoad %10 %12\n"
793 << "%16 = OpIEqual %15 %13 %14\n"
794 << "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n"
795 << "%23 = OpSelect %7 %22 %19 %20\n"
796 << " OpStore %9 %23\n"
797 << " OpReturn\n"
798 << " OpFunctionEnd\n";
799 programCollection.spirvAsmSources.add("frag") << frag.str() << spvBuildOptions;
800 }
801 }
802
checkSupport(Context & context) const803 void PrimitiveIdCase::checkSupport(Context &context) const
804 {
805 MeshShaderBuiltinCase::checkSupport(context);
806
807 // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
808 if (m_glslFrag)
809 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
810 }
811
createInstance(Context & context) const812 TestInstance *PrimitiveIdCase::createInstance(Context &context) const
813 {
814 const ColorVec expectedColors(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
815 const IterationParams iterationParams = {
816 getDefaultExtent(), // VkExtent2D colorExtent;
817 1u, // uint32_t numLayers;
818 false, // bool multiview;
819 false, // bool indirect;
820 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
821 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
822 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
823 };
824 return new FullScreenColorInstance(context, iterationParams, expectedColors);
825 }
826
827 // Layer builtin case.
828 class LayerCase : public MeshShaderBuiltinCase
829 {
830 public:
LayerCase(tcu::TestContext & testCtx,const std::string & name,bool writeVal,bool shareVertices)831 LayerCase(tcu::TestContext &testCtx, const std::string &name, bool writeVal, bool shareVertices)
832 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
833 , m_shareVertices(shareVertices)
834 , m_writeVal(writeVal)
835 {
836 }
~LayerCase(void)837 virtual ~LayerCase(void)
838 {
839 }
840
841 void initPrograms(vk::SourceCollections &programCollection) const override;
842 void checkSupport(Context &context) const override;
843 TestInstance *createInstance(Context &context) const override;
844
845 static constexpr uint32_t kNumLayers = 4u;
846
847 protected:
848 const bool m_shareVertices;
849 const bool m_writeVal;
850 };
851
initPrograms(vk::SourceCollections & programCollection) const852 void LayerCase::initPrograms(vk::SourceCollections &programCollection) const
853 {
854 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
855 const auto localSize = (m_shareVertices ? kNumLayers : 1u);
856 const auto numPrimitives = (m_shareVertices ? kNumLayers : 1u);
857 const auto layerNumber = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
858
859 // One layer per local invocation or work group (shared vertices or not, respectively).
860 {
861 std::ostringstream mesh;
862 mesh << "#version 460\n"
863 << "#extension GL_EXT_mesh_shader : enable\n"
864 << "\n"
865 << "layout (local_size_x=" << localSize << ") in;\n"
866 << "layout (triangles) out;\n"
867 << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
868 << "\n"
869 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
870 << " int gl_Layer;\n"
871 << "} gl_MeshPrimitivesEXT[];\n"
872 << "\n"
873 << "void main ()\n"
874 << "{\n"
875 << " SetMeshOutputsEXT(3u, " << numPrimitives << ");\n"
876 << "\n"
877 << " if (gl_LocalInvocationIndex == 0u)\n"
878 << " {\n"
879 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
880 << " gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
881 << " gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
882 << " }\n"
883 << "\n"
884 << " gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);\n";
885
886 if (m_writeVal)
887 mesh << " gl_MeshPrimitivesEXT[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n";
888
889 mesh << "}\n";
890
891 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
892 }
893
894 // Fragment shader chooses one color per layer.
895 {
896 std::ostringstream frag;
897 frag << "#version 460\n"
898 << "#extension GL_EXT_mesh_shader : enable\n"
899 << "\n"
900 << "layout (location=0) out vec4 outColor;\n"
901 << "\n"
902 << "vec4 colors[" << kNumLayers << "] = vec4[](\n"
903 << " vec4(0.0, 0.0, 1.0, 1.0),\n"
904 << " vec4(1.0, 0.0, 1.0, 1.0),\n"
905 << " vec4(0.0, 1.0, 1.0, 1.0),\n"
906 << " vec4(1.0, 1.0, 0.0, 1.0)\n"
907 << ");\n"
908 << "\n"
909 << "void main ()\n"
910 << "{\n"
911 << " outColor = colors[gl_Layer];\n"
912 << "}\n";
913 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
914 }
915 }
916
checkSupport(Context & context) const917 void LayerCase::checkSupport(Context &context) const
918 {
919 MeshShaderBuiltinCase::checkSupport(context);
920
921 if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
922 context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
923 else
924 {
925 const auto &features = context.getDeviceVulkan12Features();
926 if (!features.shaderOutputLayer)
927 TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported");
928 }
929 }
930
createInstance(Context & context) const931 TestInstance *LayerCase::createInstance(Context &context) const
932 {
933 ColorVec expectedColors;
934
935 const auto usedLayers = (m_writeVal ? kNumLayers : 1u);
936 const auto numWorkGroups = (m_shareVertices ? 1u : kNumLayers);
937
938 expectedColors.reserve(usedLayers);
939 expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
940
941 if (m_writeVal)
942 {
943 expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
944 expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
945 expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
946 }
947
948 const IterationParams iterationParams = {
949 getDefaultExtent(), // VkExtent2D colorExtent;
950 usedLayers, // uint32_t numLayers;
951 false, // bool multiview;
952 false, // bool indirect;
953 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
954 getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
955 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
956 };
957 return new FullScreenColorInstance(context, iterationParams, expectedColors);
958 }
959
960 // ViewportIndex builtin case.
961 class ViewportIndexCase : public MeshShaderBuiltinCase
962 {
963 public:
ViewportIndexCase(tcu::TestContext & testCtx,const std::string & name,bool writeVal,bool shareVertices)964 ViewportIndexCase(tcu::TestContext &testCtx, const std::string &name, bool writeVal, bool shareVertices)
965 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
966 , m_shareVertices(shareVertices)
967 , m_writeVal(writeVal)
968 {
969 }
~ViewportIndexCase(void)970 virtual ~ViewportIndexCase(void)
971 {
972 }
973
974 void initPrograms(vk::SourceCollections &programCollection) const override;
975 void checkSupport(Context &context) const override;
976 TestInstance *createInstance(Context &context) const override;
977
978 static constexpr uint32_t kQuadrants = 4u;
979
980 protected:
981 const bool m_shareVertices;
982 const bool m_writeVal;
983 };
984
initPrograms(vk::SourceCollections & programCollection) const985 void ViewportIndexCase::initPrograms(vk::SourceCollections &programCollection) const
986 {
987 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
988 const auto localSize = (m_shareVertices ? kQuadrants : 1u);
989 const auto numPrimitives = (m_shareVertices ? kQuadrants : 1u);
990 const auto viewportIndex = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
991
992 // One viewport per local invocation or work group (sharing vertices or not, respectively).
993 {
994 std::ostringstream mesh;
995 mesh << "#version 460\n"
996 << "#extension GL_EXT_mesh_shader : enable\n"
997 << "\n"
998 << "layout (local_size_x=" << localSize << ") in;\n"
999 << "layout (triangles) out;\n"
1000 << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
1001 << "\n"
1002 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
1003 << " int gl_ViewportIndex;\n"
1004 << "} gl_MeshPrimitivesEXT[];\n"
1005 << "\n"
1006 << "void main ()\n"
1007 << "{\n"
1008 << " SetMeshOutputsEXT(3u, " << numPrimitives << ");\n"
1009 << "\n"
1010 << " if (gl_LocalInvocationIndex == 0u)\n"
1011 << " {\n"
1012 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1013 << " gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
1014 << " gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
1015 << " }\n"
1016 << "\n"
1017 << " gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);\n";
1018
1019 if (m_writeVal)
1020 mesh << " gl_MeshPrimitivesEXT[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex
1021 << ");\n";
1022
1023 mesh << "}\n";
1024
1025 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1026 }
1027
1028 // Fragment shader chooses one color per viewport.
1029 {
1030 std::ostringstream frag;
1031 frag << "#version 460\n"
1032 << "#extension GL_EXT_mesh_shader : enable\n"
1033 << "\n"
1034 << "layout (location=0) out vec4 outColor;\n"
1035 << "\n"
1036 << "vec4 colors[" << kQuadrants << "] = vec4[](\n"
1037 << " vec4(0.0, 0.0, 1.0, 1.0),\n"
1038 << " vec4(1.0, 0.0, 1.0, 1.0),\n"
1039 << " vec4(0.0, 1.0, 1.0, 1.0),\n"
1040 << " vec4(1.0, 1.0, 0.0, 1.0)\n"
1041 << ");\n"
1042 << "\n"
1043 << "void main ()\n"
1044 << "{\n"
1045 << " outColor = colors[gl_ViewportIndex];\n"
1046 << "}\n";
1047 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1048 }
1049 }
1050
checkSupport(Context & context) const1051 void ViewportIndexCase::checkSupport(Context &context) const
1052 {
1053 MeshShaderBuiltinCase::checkSupport(context);
1054 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
1055
1056 if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
1057 context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
1058 else
1059 {
1060 const auto &features = context.getDeviceVulkan12Features();
1061 if (!features.shaderOutputViewportIndex)
1062 TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported");
1063 }
1064 }
1065
createInstance(Context & context) const1066 TestInstance *ViewportIndexCase::createInstance(Context &context) const
1067 {
1068 const auto extent = getDefaultExtent();
1069
1070 DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u);
1071 DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u);
1072
1073 const auto halfWidth = static_cast<float>(extent.width) / 2.0f;
1074 const auto halfHeight = static_cast<float>(extent.height) / 2.0f;
1075
1076 const auto topLeft = tcu::Vec4(0.0, 0.0, 1.0, 1.0);
1077 const auto topRight = (m_writeVal ? tcu::Vec4(1.0, 0.0, 1.0, 1.0) : getClearColor());
1078 const auto bottomLeft = (m_writeVal ? tcu::Vec4(0.0, 1.0, 1.0, 1.0) : getClearColor());
1079 const auto bottomRight = (m_writeVal ? tcu::Vec4(1.0, 1.0, 0.0, 1.0) : getClearColor());
1080
1081 ViewportVec viewports;
1082 viewports.reserve(kQuadrants);
1083 viewports.emplace_back(makeViewport(0.0f, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
1084 viewports.emplace_back(makeViewport(halfWidth, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
1085 viewports.emplace_back(makeViewport(0.0f, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
1086 viewports.emplace_back(makeViewport(halfWidth, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
1087
1088 const auto numWorkGroups = (m_shareVertices ? 1u : kQuadrants);
1089 const IterationParams iterationParams = {
1090 getDefaultExtent(), // VkExtent2D colorExtent;
1091 1u, // uint32_t numLayers;
1092 false, // bool multiview;
1093 false, // bool indirect;
1094 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1095 getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
1096 std::move(viewports), // ViewportVec viewports;
1097 };
1098 return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight);
1099 }
1100
1101 // Position builtin case.
1102 class PositionCase : public MeshShaderBuiltinCase
1103 {
1104 public:
PositionCase(tcu::TestContext & testCtx,const std::string & name)1105 PositionCase(tcu::TestContext &testCtx, const std::string &name)
1106 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1107 {
1108 }
~PositionCase(void)1109 virtual ~PositionCase(void)
1110 {
1111 }
1112
1113 void initPrograms(vk::SourceCollections &programCollection) const override;
1114 TestInstance *createInstance(Context &context) const override;
1115 };
1116
initPrograms(vk::SourceCollections & programCollection) const1117 void PositionCase::initPrograms(vk::SourceCollections &programCollection) const
1118 {
1119 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1120
1121 // Mesh shader: emit single triangle around the center of the top left pixel.
1122 {
1123 const auto extent = getDefaultExtent();
1124 const auto fWidth = static_cast<float>(extent.width);
1125 const auto fHeight = static_cast<float>(extent.height);
1126
1127 const auto pxWidth = 2.0f / fWidth;
1128 const auto pxHeight = 2.0f / fHeight;
1129
1130 const auto halfXPix = pxWidth / 2.0f;
1131 const auto halfYPix = pxHeight / 2.0f;
1132
1133 // Center of top left pixel.
1134 const auto x = -1.0f + halfXPix;
1135 const auto y = -1.0f + halfYPix;
1136
1137 std::ostringstream mesh;
1138 mesh << "#version 460\n"
1139 << "#extension GL_EXT_mesh_shader : enable\n"
1140 << "\n"
1141 << "layout (local_size_x=1) in;\n"
1142 << "layout (triangles) out;\n"
1143 << "layout (max_vertices=3, max_primitives=1) out;\n"
1144 << "\n"
1145 << "void main ()\n"
1146 << "{\n"
1147 << " SetMeshOutputsEXT(3u, 1u);\n"
1148 << "\n"
1149 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
1150 << "\n"
1151 << " gl_MeshVerticesEXT[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix)
1152 << ", 0.0, 1.0);\n"
1153 << " gl_MeshVerticesEXT[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix)
1154 << ", 0.0, 1.0);\n"
1155 << " gl_MeshVerticesEXT[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n"
1156 << "}\n";
1157 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1158 }
1159
1160 // Basic fragment shader.
1161 {
1162 const auto frag = getBasicFragShader();
1163 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1164 }
1165 }
1166
createInstance(Context & context) const1167 TestInstance *PositionCase::createInstance(Context &context) const
1168 {
1169 const IterationParams iterationParams = {
1170 getDefaultExtent(), // VkExtent2D colorExtent;
1171 1u, // uint32_t numLayers;
1172 false, // bool multiview;
1173 false, // bool indirect;
1174 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1175 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1176 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1177 };
1178
1179 // Must match the shader.
1180 PixelMap pixelMap;
1181 pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
1182
1183 const PixelVerifierParams verifierParams = {
1184 getClearColor(), // const tcu::Vec4 background;
1185 std::move(pixelMap), // const PixelMap pixelMap;
1186 };
1187 return new PixelsInstance(context, iterationParams, verifierParams);
1188 }
1189
1190 // PointSize builtin case.
1191 class PointSizeCase : public MeshShaderBuiltinCase
1192 {
1193 public:
PointSizeCase(tcu::TestContext & testCtx,const std::string & name)1194 PointSizeCase(tcu::TestContext &testCtx, const std::string &name)
1195 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1196 {
1197 }
~PointSizeCase(void)1198 virtual ~PointSizeCase(void)
1199 {
1200 }
1201
1202 void initPrograms(vk::SourceCollections &programCollection) const override;
1203 TestInstance *createInstance(Context &context) const override;
1204 void checkSupport(Context &context) const override;
1205
1206 static constexpr float kPointSize = 4.0f;
1207 };
1208
initPrograms(vk::SourceCollections & programCollection) const1209 void PointSizeCase::initPrograms(vk::SourceCollections &programCollection) const
1210 {
1211 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1212
1213 // Mesh shader: large point covering the top left quadrant.
1214 {
1215 std::ostringstream mesh;
1216 mesh << "#version 460\n"
1217 << "#extension GL_EXT_mesh_shader : enable\n"
1218 << "\n"
1219 << "layout (local_size_x=1) in;\n"
1220 << "layout (points) out;\n"
1221 << "layout (max_vertices=1, max_primitives=1) out;\n"
1222 << "\n"
1223 << "void main ()\n"
1224 << "{\n"
1225 << " SetMeshOutputsEXT(1u, 1u);\n"
1226 << "\n"
1227 << " gl_PrimitivePointIndicesEXT[0] = 0u;\n"
1228 << "\n"
1229 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n"
1230 << " gl_MeshVerticesEXT[0].gl_PointSize = " << kPointSize << ";\n"
1231 << "}\n";
1232 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1233 }
1234
1235 // Basic fragment shader.
1236 {
1237 const auto frag = getBasicFragShader();
1238 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1239 }
1240 }
1241
createInstance(Context & context) const1242 TestInstance *PointSizeCase::createInstance(Context &context) const
1243 {
1244 const IterationParams iterationParams = {
1245 getDefaultExtent(), // VkExtent2D colorExtent;
1246 1u, // uint32_t numLayers;
1247 false, // bool multiview;
1248 false, // bool indirect;
1249 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1250 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1251 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1252 };
1253
1254 // Must match the shader.
1255 const tcu::Vec4 black = getClearColor();
1256 const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1257
1258 return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1259 }
1260
checkSupport(Context & context) const1261 void PointSizeCase::checkSupport(Context &context) const
1262 {
1263 MeshShaderBuiltinCase::checkSupport(context);
1264 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
1265
1266 const auto &properties = context.getDeviceProperties();
1267 if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1])
1268 TCU_THROW(NotSupportedError, "Required point size outside point size range");
1269 }
1270
1271 // ClipDistance builtin case.
1272 class ClipDistanceCase : public MeshShaderBuiltinCase
1273 {
1274 public:
ClipDistanceCase(tcu::TestContext & testCtx,const std::string & name)1275 ClipDistanceCase(tcu::TestContext &testCtx, const std::string &name)
1276 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1277 {
1278 }
~ClipDistanceCase(void)1279 virtual ~ClipDistanceCase(void)
1280 {
1281 }
1282
1283 void initPrograms(vk::SourceCollections &programCollection) const override;
1284 TestInstance *createInstance(Context &context) const override;
1285 void checkSupport(Context &context) const override;
1286 };
1287
initPrograms(vk::SourceCollections & programCollection) const1288 void ClipDistanceCase::initPrograms(vk::SourceCollections &programCollection) const
1289 {
1290 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1291
1292 // Mesh shader: full-screen quad using different clip distances.
1293 {
1294 std::ostringstream mesh;
1295 mesh << "#version 460\n"
1296 << "#extension GL_EXT_mesh_shader : enable\n"
1297 << "\n"
1298 << "layout (local_size_x=1) in;\n"
1299 << "layout (triangles) out;\n"
1300 << "layout (max_vertices=4, max_primitives=2) out;\n"
1301 << "\n"
1302 << "out gl_MeshPerVertexEXT {\n"
1303 << " vec4 gl_Position;\n"
1304 << " float gl_ClipDistance[2];\n"
1305 << "} gl_MeshVerticesEXT[];\n"
1306 << "\n"
1307 << "void main ()\n"
1308 << "{\n"
1309 << " SetMeshOutputsEXT(4u, 2u);\n"
1310 << "\n"
1311 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
1312 << " gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 3u, 2u);\n"
1313 << "\n"
1314 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1315 << " gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
1316 << " gl_MeshVerticesEXT[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1317 << " gl_MeshVerticesEXT[3].gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n"
1318 << "\n"
1319 // The first clip plane keeps the left half of the frame buffer.
1320 << " gl_MeshVerticesEXT[0].gl_ClipDistance[0] = 1.0;\n"
1321 << " gl_MeshVerticesEXT[1].gl_ClipDistance[0] = 1.0;\n"
1322 << " gl_MeshVerticesEXT[2].gl_ClipDistance[0] = -1.0;\n"
1323 << " gl_MeshVerticesEXT[3].gl_ClipDistance[0] = -1.0;\n"
1324 << "\n"
1325 // The second clip plane keeps the top half of the frame buffer.
1326 << " gl_MeshVerticesEXT[0].gl_ClipDistance[1] = 1.0;\n"
1327 << " gl_MeshVerticesEXT[1].gl_ClipDistance[1] = -1.0;\n"
1328 << " gl_MeshVerticesEXT[2].gl_ClipDistance[1] = 1.0;\n"
1329 << " gl_MeshVerticesEXT[3].gl_ClipDistance[1] = -1.0;\n"
1330 << "}\n";
1331 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1332 }
1333
1334 // Fragment shader chooses a constant color.
1335 {
1336 std::ostringstream frag;
1337 frag << "#version 460\n"
1338 << "#extension GL_EXT_mesh_shader : enable\n"
1339 << "\n"
1340 << "layout (location=0) out vec4 outColor;\n"
1341 << "\n"
1342 << "void main ()\n"
1343 << "{\n"
1344 // White color should not actually be used, as those fragments are supposed to be discarded.
1345 << " outColor = ((gl_ClipDistance[0] >= 0.0 && gl_ClipDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : "
1346 "vec4(1.0, 1.0, 1.0, 1.0));\n"
1347 << "}\n";
1348 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1349 }
1350 }
1351
createInstance(Context & context) const1352 TestInstance *ClipDistanceCase::createInstance(Context &context) const
1353 {
1354 const IterationParams iterationParams = {
1355 getDefaultExtent(), // VkExtent2D colorExtent;
1356 1u, // uint32_t numLayers;
1357 false, // bool multiview;
1358 false, // bool indirect;
1359 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1360 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1361 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1362 };
1363
1364 // Must match the shader.
1365 const tcu::Vec4 black = getClearColor();
1366 const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1367
1368 return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1369 }
1370
checkSupport(Context & context) const1371 void ClipDistanceCase::checkSupport(Context &context) const
1372 {
1373 MeshShaderBuiltinCase::checkSupport(context);
1374 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
1375 }
1376
1377 // CullDistance builtin case.
1378 class CullDistanceCase : public MeshShaderBuiltinCase
1379 {
1380 public:
CullDistanceCase(tcu::TestContext & testCtx,const std::string & name)1381 CullDistanceCase(tcu::TestContext &testCtx, const std::string &name)
1382 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1383 {
1384 }
~CullDistanceCase(void)1385 virtual ~CullDistanceCase(void)
1386 {
1387 }
1388
1389 void initPrograms(vk::SourceCollections &programCollection) const override;
1390 TestInstance *createInstance(Context &context) const override;
1391 void checkSupport(Context &context) const override;
1392 };
1393
initPrograms(vk::SourceCollections & programCollection) const1394 void CullDistanceCase::initPrograms(vk::SourceCollections &programCollection) const
1395 {
1396 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1397
1398 // Mesh shader: two quads covering the whole screen, one on top of the other.
1399 // Use cull distances to discard the bottom quad.
1400 // Use cull distances to paint the top one in two colors: blue on the left, white on the right.
1401 {
1402 std::ostringstream mesh;
1403 mesh << "#version 460\n"
1404 << "#extension GL_EXT_mesh_shader : enable\n"
1405 << "\n"
1406 << "layout (local_size_x=1) in;\n"
1407 << "layout (triangles) out;\n"
1408 << "layout (max_vertices=6, max_primitives=4) out;\n"
1409 << "\n"
1410 << "out gl_MeshPerVertexEXT {\n"
1411 << " vec4 gl_Position;\n"
1412 << " float gl_CullDistance[2];\n"
1413 << "} gl_MeshVerticesEXT[];\n"
1414 << "\n"
1415 << "void main ()\n"
1416 << "{\n"
1417 << " SetMeshOutputsEXT(6u, 4u);\n"
1418 << "\n"
1419 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
1420 << " gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
1421 << " gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
1422 << " gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
1423 << "\n"
1424 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1425 << " gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0, 0.0, 0.0, 1.0);\n"
1426 << " gl_MeshVerticesEXT[2].gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
1427 << " gl_MeshVerticesEXT[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1428 << " gl_MeshVerticesEXT[4].gl_Position = vec4( 1.0, 0.0, 0.0, 1.0);\n"
1429 << " gl_MeshVerticesEXT[5].gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n"
1430 << "\n"
1431 // The first cull plane discards the bottom quad
1432 << " gl_MeshVerticesEXT[0].gl_CullDistance[0] = 1.0;\n"
1433 << " gl_MeshVerticesEXT[1].gl_CullDistance[0] = -1.0;\n"
1434 << " gl_MeshVerticesEXT[2].gl_CullDistance[0] = -2.0;\n"
1435 << " gl_MeshVerticesEXT[3].gl_CullDistance[0] = 1.0;\n"
1436 << " gl_MeshVerticesEXT[4].gl_CullDistance[0] = -1.0;\n"
1437 << " gl_MeshVerticesEXT[5].gl_CullDistance[0] = -2.0;\n"
1438 << "\n"
1439 // The second cull plane helps paint left and right different.
1440 << " gl_MeshVerticesEXT[0].gl_CullDistance[1] = 1.0;\n"
1441 << " gl_MeshVerticesEXT[1].gl_CullDistance[1] = 1.0;\n"
1442 << " gl_MeshVerticesEXT[2].gl_CullDistance[1] = 1.0;\n"
1443 << " gl_MeshVerticesEXT[3].gl_CullDistance[1] = -1.0;\n"
1444 << " gl_MeshVerticesEXT[4].gl_CullDistance[1] = -1.0;\n"
1445 << " gl_MeshVerticesEXT[5].gl_CullDistance[1] = -1.0;\n"
1446 << "}\n";
1447 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1448 }
1449
1450 // Fragment shader chooses color based on the second cull distance.
1451 {
1452 std::ostringstream frag;
1453 frag << "#version 460\n"
1454 << "#extension GL_EXT_mesh_shader : enable\n"
1455 << "\n"
1456 << "layout (location=0) out vec4 outColor;\n"
1457 << "\n"
1458 << "void main ()\n"
1459 << "{\n"
1460 << " 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"
1461 << "}\n";
1462 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1463 }
1464 }
1465
createInstance(Context & context) const1466 TestInstance *CullDistanceCase::createInstance(Context &context) const
1467 {
1468 const IterationParams iterationParams = {
1469 getDefaultExtent(), // VkExtent2D colorExtent;
1470 1u, // uint32_t numLayers;
1471 false, // bool multiview;
1472 false, // bool indirect;
1473 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1474 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1475 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1476 };
1477
1478 // Must match the shader.
1479 const tcu::Vec4 black = getClearColor();
1480 const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1481 const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
1482
1483 return new QuadrantsInstance(context, iterationParams, blue, white, black, black);
1484 }
1485
checkSupport(Context & context) const1486 void CullDistanceCase::checkSupport(Context &context) const
1487 {
1488 MeshShaderBuiltinCase::checkSupport(context);
1489 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE);
1490 }
1491
1492 // Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len).
1493 // Supposes the height of the framebuffer is 1.
triangleForPixel(const std::string & pixel,const std::string & len,const std::string & primitiveIndex)1494 std::string triangleForPixel(const std::string &pixel, const std::string &len, const std::string &primitiveIndex)
1495 {
1496 std::ostringstream statements;
1497 statements << " const float imgWidth = float(" << len << ");\n"
1498 << " const float pixWidth = (2.0 / imgWidth);\n"
1499 << " const float halfPix = (pixWidth / 2.0);\n"
1500 << " const float xCenter = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n"
1501 << " const float xLeft = (xCenter - halfPix);\n"
1502 << " const float xRight = (xCenter + halfPix);\n"
1503 << " const uint vindex = (" << primitiveIndex << " * 3u);\n"
1504 << " const uvec3 indices = uvec3(vindex + 0, vindex + 1, vindex + 2);\n"
1505 << "\n"
1506 << " gl_PrimitiveTriangleIndicesEXT[" << primitiveIndex << "] = indices;\n"
1507 << "\n"
1508 << " gl_MeshVerticesEXT[indices.x].gl_Position = vec4(xLeft, 0.5, 0.0, 1.0);\n"
1509 << " gl_MeshVerticesEXT[indices.y].gl_Position = vec4(xRight, 0.5, 0.0, 1.0);\n"
1510 << " gl_MeshVerticesEXT[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n";
1511 return statements.str();
1512 }
1513
1514 // WorkGroupID builtin case.
1515 class WorkGroupIdCase : public MeshShaderBuiltinCase
1516 {
1517 public:
WorkGroupIdCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1518 WorkGroupIdCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1519 : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1520 , m_extent(getLinearExtent())
1521 {
1522 }
~WorkGroupIdCase(void)1523 virtual ~WorkGroupIdCase(void)
1524 {
1525 }
1526
1527 void initPrograms(vk::SourceCollections &programCollection) const override;
1528 TestInstance *createInstance(Context &context) const override;
1529
1530 protected:
1531 const VkExtent2D m_extent;
1532 };
1533
initPrograms(vk::SourceCollections & programCollection) const1534 void WorkGroupIdCase::initPrograms(vk::SourceCollections &programCollection) const
1535 {
1536 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1537
1538 const std::string taskDataDecl = "struct TaskData {\n"
1539 " uint id;\n"
1540 " uint size;\n"
1541 "};\n"
1542 "taskPayloadSharedEXT TaskData td;\n";
1543
1544 // Mesh shader: each work group fills one pixel.
1545 {
1546 const std::string pixel = (m_taskNeeded ? "td.id" : "gl_WorkGroupID.x");
1547 const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1548
1549 std::ostringstream mesh;
1550 mesh << "#version 460\n"
1551 << "#extension GL_EXT_mesh_shader : enable\n"
1552 << "\n"
1553 << "layout (local_size_x=1) in;\n"
1554 << "layout (triangles) out;\n"
1555 << "layout (max_vertices=3, max_primitives=1) out;\n"
1556 << "\n"
1557 << (m_taskNeeded ? taskDataDecl : "") << "\n"
1558 << "void main ()\n"
1559 << "{\n"
1560 << " SetMeshOutputsEXT(3u, 1u);\n"
1561 << "\n"
1562 << triangleForPixel(pixel, len, "0") << "}\n";
1563 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1564 }
1565
1566 if (m_taskNeeded)
1567 {
1568 std::ostringstream task;
1569 task << "#version 460\n"
1570 << "#extension GL_EXT_mesh_shader : enable\n"
1571 << "\n"
1572 << "layout (local_size_x=1) in;\n"
1573 << "\n"
1574 << taskDataDecl << "\n"
1575 << "void main ()\n"
1576 << "{\n"
1577 << " td.id = gl_WorkGroupID.x;\n"
1578 << " td.size = " << m_extent.width << ";\n"
1579 << " EmitMeshTasksEXT(1u, 1u, 1u);\n"
1580 << "}\n";
1581 programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1582 }
1583
1584 // Basic fragment shader.
1585 {
1586 const auto frag = getBasicFragShader();
1587 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1588 }
1589 }
1590
createInstance(Context & context) const1591 TestInstance *WorkGroupIdCase::createInstance(Context &context) const
1592 {
1593 // Must match the shader.
1594 const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1595 const IterationParams iterationParams = {
1596 m_extent, // VkExtent2D colorExtent;
1597 1u, // uint32_t numLayers;
1598 false, // bool multiview;
1599 false, // bool indirect;
1600 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1601 getDefaultDrawCommands(m_extent.width), // DrawCommandVec drawArgs;
1602 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1603 };
1604 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1605 }
1606
1607 // Variable to use.
1608 enum class LocalInvocation
1609 {
1610 ID = 0,
1611 INDEX
1612 };
1613
1614 // LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize.
1615 class LocalInvocationCase : public MeshShaderBuiltinCase
1616 {
1617 public:
LocalInvocationCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded,LocalInvocation variable)1618 LocalInvocationCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded, LocalInvocation variable)
1619 : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1620 , m_extent(getLinearExtent())
1621 , m_variable(variable)
1622 {
1623 }
~LocalInvocationCase(void)1624 virtual ~LocalInvocationCase(void)
1625 {
1626 }
1627
1628 void initPrograms(vk::SourceCollections &programCollection) const override;
1629 TestInstance *createInstance(Context &context) const override;
1630
1631 protected:
1632 const VkExtent2D m_extent;
1633 const LocalInvocation m_variable;
1634 };
1635
initPrograms(vk::SourceCollections & programCollection) const1636 void LocalInvocationCase::initPrograms(vk::SourceCollections &programCollection) const
1637 {
1638 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1639
1640 // Invocation index to use.
1641 const std::string localIndex =
1642 ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex");
1643
1644 // Task data.
1645 std::ostringstream taskDataDecl;
1646 taskDataDecl << "struct TaskData {\n"
1647 // indexNumber[x] == x
1648 << " uint indexNumber[" << m_extent.width << "];\n"
1649 << " uint size;\n"
1650 << "};\n"
1651 << "taskPayloadSharedEXT TaskData td;\n";
1652 const auto taskDataDeclStr = taskDataDecl.str();
1653
1654 // Mesh shader: each work group fills one pixel.
1655 {
1656 const std::string pixel = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex);
1657 const std::string len = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x");
1658 const auto localSize = (m_taskNeeded ? 1u : m_extent.width);
1659 const auto maxVert = localSize * 3u;
1660 const std::string primitiveIndex = (m_taskNeeded ? "0" : localIndex);
1661
1662 std::ostringstream mesh;
1663 mesh << "#version 460\n"
1664 << "#extension GL_EXT_mesh_shader : enable\n"
1665 << "\n"
1666 << "layout (local_size_x=" << localSize << ") in;\n"
1667 << "layout (triangles) out;\n"
1668 << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1669 << "\n"
1670 << (m_taskNeeded ? taskDataDeclStr : "") << "\n"
1671 << "void main ()\n"
1672 << "{\n"
1673 << " SetMeshOutputsEXT(" << maxVert << ", " << localSize << ");\n"
1674 << "\n"
1675 << triangleForPixel(pixel, len, primitiveIndex) << "}\n";
1676 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1677 }
1678
1679 if (m_taskNeeded)
1680 {
1681 std::ostringstream task;
1682 task << "#version 460\n"
1683 << "#extension GL_EXT_mesh_shader : enable\n"
1684 << "\n"
1685 << "layout (local_size_x=" << m_extent.width << ") in;\n"
1686 << "\n"
1687 << taskDataDeclStr << "\n"
1688 << "void main ()\n"
1689 << "{\n"
1690 << " td.indexNumber[" << localIndex << "] = " << localIndex << ";\n"
1691 << " td.size = gl_WorkGroupSize.x;\n"
1692 << " EmitMeshTasksEXT(" << m_extent.width << ", 1u, 1u);\n"
1693 << "}\n";
1694 programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1695 }
1696
1697 // Basic fragment shader.
1698 {
1699 const auto frag = getBasicFragShader();
1700 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1701 }
1702 }
1703
createInstance(Context & context) const1704 TestInstance *LocalInvocationCase::createInstance(Context &context) const
1705 {
1706 // Must match the shader.
1707 const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1708 const IterationParams iterationParams = {
1709 m_extent, // VkExtent2D colorExtent;
1710 1u, // uint32_t numLayers;
1711 false, // bool multiview;
1712 false, // bool indirect;
1713 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1714 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1715 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1716 };
1717 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1718 }
1719
1720 // NumWorkgroups case.
toGLSL(const tcu::UVec3 & v)1721 std::string toGLSL(const tcu::UVec3 &v)
1722 {
1723 return "uvec3(" + std::to_string(v.x()) + ", " + std::to_string(v.y()) + ", " + std::to_string(v.z()) + ")";
1724 }
1725
1726 class NumWorkgroupsCase : public MeshShaderBuiltinCase
1727 {
1728 public:
NumWorkgroupsCase(tcu::TestContext & testCtx,const std::string & name,const tcu::Maybe<tcu::UVec3> & taskGroups,const tcu::UVec3 & meshGroups)1729 NumWorkgroupsCase(tcu::TestContext &testCtx, const std::string &name, const tcu::Maybe<tcu::UVec3> &taskGroups,
1730 const tcu::UVec3 &meshGroups)
1731 : MeshShaderBuiltinCase(testCtx, name, static_cast<bool>(taskGroups))
1732 , m_taskGroups(taskGroups)
1733 , m_meshGroups(meshGroups)
1734 {
1735 }
~NumWorkgroupsCase(void)1736 virtual ~NumWorkgroupsCase(void)
1737 {
1738 }
1739
1740 void initPrograms(vk::SourceCollections &programCollection) const override;
1741 TestInstance *createInstance(Context &context) const override;
1742
1743 protected:
1744 VkExtent2D imageExtent() const;
1745 tcu::UVec3 drawArgs() const;
1746
1747 const tcu::Maybe<tcu::UVec3> m_taskGroups;
1748 const tcu::UVec3 m_meshGroups;
1749 };
1750
imageExtent() const1751 VkExtent2D NumWorkgroupsCase::imageExtent() const
1752 {
1753 uint32_t taskMultiplier = 1u;
1754
1755 if (m_taskNeeded)
1756 {
1757 const auto &tg = m_taskGroups.get();
1758 taskMultiplier = tg.x() * tg.y() * tg.z();
1759 }
1760
1761 const uint32_t meshFactor = m_meshGroups.x() * m_meshGroups.y() * m_meshGroups.z();
1762 const uint32_t width = meshFactor * taskMultiplier;
1763
1764 return makeExtent2D(width, 1u);
1765 }
1766
drawArgs() const1767 tcu::UVec3 NumWorkgroupsCase::drawArgs() const
1768 {
1769 if (m_taskNeeded)
1770 return m_taskGroups.get();
1771 return m_meshGroups;
1772 }
1773
initPrograms(vk::SourceCollections & programCollection) const1774 void NumWorkgroupsCase::initPrograms(vk::SourceCollections &programCollection) const
1775 {
1776 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1777
1778 // Task data.
1779 std::ostringstream taskDataDecl;
1780
1781 if (m_taskNeeded)
1782 {
1783 taskDataDecl << "struct TaskData {\n"
1784 << " uvec3 parentId;\n"
1785 << " uvec3 parentSize;\n"
1786 << "};\n"
1787 << "taskPayloadSharedEXT TaskData td;\n";
1788 }
1789
1790 const auto taskDataDeclStr = taskDataDecl.str();
1791 const auto extent = imageExtent();
1792 const auto &width = extent.width;
1793 DE_ASSERT(extent.height == 1u);
1794
1795 // Mesh shader: each work group fills one pixel.
1796 {
1797 const std::string parentId = (m_taskNeeded ? "td.parentId" : "uvec3(0, 0, 0)");
1798 const std::string parentSize = (m_taskNeeded ? "td.parentSize" : "uvec3(1, 1, 1)");
1799 const std::string parentOffset = "(" + parentSize + ".x * " + parentSize + ".y * " + parentId + ".z + " +
1800 parentId + ".y * " + parentSize + ".x + " + parentId + ".x)";
1801 const std::string meshGroupsPerTask = std::to_string(m_meshGroups.x() * m_meshGroups.y() * m_meshGroups.z());
1802 const std::string meshGroupIndex = "(gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupID.z + "
1803 "gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x)";
1804 const std::string pixel = "((" + parentOffset + " * " + meshGroupsPerTask + ") + " + meshGroupIndex + ")";
1805 const std::string len = std::to_string(width);
1806
1807 std::ostringstream mesh;
1808 mesh << "#version 460\n"
1809 << "#extension GL_EXT_mesh_shader : enable\n"
1810 << "\n"
1811 << "layout (local_size_x=1) in;\n"
1812 << "layout (triangles) out;\n"
1813 << "layout (max_vertices=3, max_primitives=1) out;\n"
1814 << "\n"
1815 << taskDataDeclStr << "\n"
1816 << "void main ()\n"
1817 << "{\n"
1818 << " uint numVertices = 3u;\n"
1819 << " uint numPrimitives = 1u;\n"
1820 << " if (gl_NumWorkGroups != " << toGLSL(m_meshGroups) << ") {\n"
1821 << " numVertices = 0u;\n"
1822 << " numPrimitives = 0u;\n"
1823 << " }\n"
1824 << " SetMeshOutputsEXT(numVertices, numPrimitives);\n"
1825 << " if (numPrimitives == 0u) {\n"
1826 << " return;\n"
1827 << " }\n"
1828 << "\n"
1829 << triangleForPixel(pixel, len, "0") << "}\n";
1830 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1831 }
1832
1833 if (m_taskNeeded)
1834 {
1835 std::ostringstream task;
1836 task << "#version 460\n"
1837 << "#extension GL_EXT_mesh_shader : enable\n"
1838 << "\n"
1839 << "layout (local_size_x=1) in;\n"
1840 << "\n"
1841 << taskDataDeclStr << "\n"
1842 << "void main ()\n"
1843 << "{\n"
1844 << " uvec3 meshGroups = " << toGLSL(m_meshGroups) << ";\n"
1845 << " if (gl_NumWorkGroups != " << toGLSL(m_taskGroups.get()) << ") {\n"
1846 << " meshGroups = uvec3(0, 0, 0);\n"
1847 << " }\n"
1848 << " td.parentSize = gl_NumWorkGroups;\n"
1849 << " td.parentId = gl_WorkGroupID;\n"
1850 << " EmitMeshTasksEXT(meshGroups.x, meshGroups.y, meshGroups.z);\n"
1851 << "}\n";
1852 programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1853 }
1854
1855 // Basic fragment shader.
1856 {
1857 const auto frag = getBasicFragShader();
1858 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1859 }
1860 }
1861
createInstance(Context & context) const1862 TestInstance *NumWorkgroupsCase::createInstance(Context &context) const
1863 {
1864 // Must match the shader.
1865 const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1866 const auto extent = imageExtent();
1867 const auto drawCmdArgs = drawArgs();
1868 const DrawCommandVec drawCommands(
1869 1u, makeDrawMeshTasksIndirectCommandEXT(drawCmdArgs.x(), drawCmdArgs.y(), drawCmdArgs.z()));
1870 const IterationParams iterationParams = {
1871 extent, // VkExtent2D colorExtent;
1872 1u, // uint32_t numLayers;
1873 false, // bool multiview;
1874 false, // bool indirect;
1875 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1876 drawCommands, // DrawCommandVec drawArgs;
1877 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1878 };
1879 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1880 }
1881
1882 // GlobalInvocationId builtin case.
1883 class GlobalInvocationIdCase : public MeshShaderBuiltinCase
1884 {
1885 public:
GlobalInvocationIdCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1886 GlobalInvocationIdCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1887 : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1888 , m_jobSize(getLargeJobSize())
1889 , m_extent{m_jobSize.numTasks * m_jobSize.localSize, 1u}
1890 {
1891 }
~GlobalInvocationIdCase(void)1892 virtual ~GlobalInvocationIdCase(void)
1893 {
1894 }
1895
1896 void initPrograms(vk::SourceCollections &programCollection) const override;
1897 TestInstance *createInstance(Context &context) const override;
1898
1899 protected:
1900 const JobSize m_jobSize;
1901 const VkExtent2D m_extent;
1902 };
1903
initPrograms(vk::SourceCollections & programCollection) const1904 void GlobalInvocationIdCase::initPrograms(vk::SourceCollections &programCollection) const
1905 {
1906 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1907 const auto &localSize = m_jobSize.localSize;
1908
1909 // Task data.
1910 std::ostringstream taskDataDecl;
1911 taskDataDecl << "struct TaskData {\n"
1912 << " uint pixelId[" << localSize << "];\n"
1913 << " uint size;\n"
1914 << "};\n"
1915 << "taskPayloadSharedEXT TaskData td;\n";
1916 const auto taskDataDeclStr = taskDataDecl.str();
1917
1918 // Mesh shader: each work group fills one pixel.
1919 {
1920 const std::string pixel = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x");
1921 const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1922 const std::string primitiveIndex = "gl_LocalInvocationIndex";
1923 const auto maxVert = localSize * 3u;
1924
1925 std::ostringstream mesh;
1926 mesh << "#version 460\n"
1927 << "#extension GL_EXT_mesh_shader : enable\n"
1928 << "\n"
1929 << "layout (local_size_x=" << localSize << ") in;\n"
1930 << "layout (triangles) out;\n"
1931 << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1932 << "\n"
1933 << (m_taskNeeded ? taskDataDeclStr : "") << "\n"
1934 << "void main ()\n"
1935 << "{\n"
1936 << " SetMeshOutputsEXT(" << maxVert << ", " << localSize << ");\n"
1937 << "\n"
1938 << triangleForPixel(pixel, len, primitiveIndex) << "}\n";
1939 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1940 }
1941
1942 if (m_taskNeeded)
1943 {
1944 std::ostringstream task;
1945 task << "#version 460\n"
1946 << "#extension GL_EXT_mesh_shader : enable\n"
1947 << "\n"
1948 << "layout (local_size_x=" << localSize << ") in;\n"
1949 << "\n"
1950 << taskDataDeclStr << "\n"
1951 << "void main ()\n"
1952 << "{\n"
1953 << " td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n"
1954 << " td.size = " << m_extent.width << ";\n"
1955 << " EmitMeshTasksEXT(1u, 1u, 1u);\n"
1956 << "}\n";
1957 programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1958 }
1959
1960 // Basic fragment shader.
1961 {
1962 const auto frag = getBasicFragShader();
1963 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1964 }
1965 }
1966
createInstance(Context & context) const1967 TestInstance *GlobalInvocationIdCase::createInstance(Context &context) const
1968 {
1969 // Must match the shader.
1970 const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1971 const IterationParams iterationParams = {
1972 m_extent, // VkExtent2D colorExtent;
1973 1u, // uint32_t numLayers;
1974 false, // bool multiview;
1975 false, // bool indirect;
1976 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1977 getDefaultDrawCommands(m_jobSize.numTasks), // DrawCommandVec drawArgs;
1978 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
1979 };
1980 return new FullScreenColorInstance(context, iterationParams, expectedColors);
1981 }
1982
1983 // DrawIndex builtin case.
1984 class DrawIndexCase : public MeshShaderBuiltinCase
1985 {
1986 public:
DrawIndexCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1987 DrawIndexCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1988 : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1989 , m_extent(getLinearExtent())
1990 {
1991 }
~DrawIndexCase(void)1992 virtual ~DrawIndexCase(void)
1993 {
1994 }
1995
1996 void initPrograms(vk::SourceCollections &programCollection) const override;
1997 TestInstance *createInstance(Context &context) const override;
1998
1999 protected:
2000 const VkExtent2D m_extent;
2001 };
2002
initPrograms(vk::SourceCollections & programCollection) const2003 void DrawIndexCase::initPrograms(vk::SourceCollections &programCollection) const
2004 {
2005 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2006
2007 const std::string taskDataDecl = "struct TaskData {\n"
2008 " uint id;\n"
2009 " uint size;\n"
2010 "};\n"
2011 "taskPayloadSharedEXT TaskData td;\n";
2012
2013 const auto drawIndex = "uint(gl_DrawID)";
2014
2015 // Mesh shader: each work group fills one pixel.
2016 {
2017 const std::string pixel = (m_taskNeeded ? "td.id" : drawIndex);
2018 const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
2019
2020 std::ostringstream mesh;
2021 mesh << "#version 460\n"
2022 << "#extension GL_EXT_mesh_shader : enable\n"
2023 << "\n"
2024 << "layout (local_size_x=1) in;\n"
2025 << "layout (triangles) out;\n"
2026 << "layout (max_vertices=3, max_primitives=1) out;\n"
2027 << "\n"
2028 << (m_taskNeeded ? taskDataDecl : "") << "\n"
2029 << "void main ()\n"
2030 << "{\n"
2031 << " SetMeshOutputsEXT(3u, 1u);\n"
2032 << "\n"
2033 << triangleForPixel(pixel, len, "0") << "}\n";
2034 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2035 }
2036
2037 if (m_taskNeeded)
2038 {
2039 std::ostringstream task;
2040 task << "#version 460\n"
2041 << "#extension GL_EXT_mesh_shader : enable\n"
2042 << "\n"
2043 << "layout (local_size_x=1) in;\n"
2044 << "\n"
2045 << taskDataDecl << "\n"
2046 << "void main ()\n"
2047 << "{\n"
2048 << " td.id = " << drawIndex << ";\n"
2049 << " td.size = " << m_extent.width << ";\n"
2050 << " EmitMeshTasksEXT(1u, 1u, 1u);\n"
2051 << "}\n";
2052 programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
2053 }
2054
2055 // Basic fragment shader.
2056 {
2057 const auto frag = getBasicFragShader();
2058 programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
2059 }
2060 }
2061
createInstance(Context & context) const2062 TestInstance *DrawIndexCase::createInstance(Context &context) const
2063 {
2064 // Must match the shader.
2065 const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
2066 const DrawCommandVec commands(m_extent.width, makeDrawMeshTasksIndirectCommandEXT(1u, 1u, 1u));
2067 const IterationParams iterationParams = {
2068 m_extent, // VkExtent2D colorExtent;
2069 1u, // uint32_t numLayers;
2070 false, // bool multiview;
2071 true, // bool indirect;
2072 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
2073 commands, // DrawCommandVec drawArgs;
2074 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
2075 };
2076 return new FullScreenColorInstance(context, iterationParams, expectedColors);
2077 }
2078
2079 // ViewIndex builtin case.
2080 class ViewIndexCase : public MeshShaderBuiltinCase
2081 {
2082 public:
ViewIndexCase(tcu::TestContext & testCtx,const std::string & name)2083 ViewIndexCase(tcu::TestContext &testCtx, const std::string &name)
2084 : MeshShaderBuiltinCase(testCtx, name, false)
2085 , m_extent(getDefaultExtent())
2086 {
2087 }
~ViewIndexCase(void)2088 virtual ~ViewIndexCase(void)
2089 {
2090 }
2091
2092 void checkSupport(Context &context) const override;
2093 void initPrograms(vk::SourceCollections &programCollection) const override;
2094 TestInstance *createInstance(Context &context) const override;
2095
2096 static constexpr uint32_t kNumLayers = 4u;
2097
2098 protected:
2099 const VkExtent2D m_extent;
2100 };
2101
checkSupport(Context & context) const2102 void ViewIndexCase::checkSupport(Context &context) const
2103 {
2104 MeshShaderBuiltinCase::checkSupport(context);
2105
2106 const auto &multiviewFeatures = context.getMultiviewFeatures();
2107 if (!multiviewFeatures.multiview)
2108 TCU_THROW(NotSupportedError, "Multiview not supported");
2109
2110 const auto &meshFeatures = context.getMeshShaderFeaturesEXT();
2111 if (!meshFeatures.multiviewMeshShader)
2112 TCU_THROW(NotSupportedError, "Multiview not supported for mesh shaders");
2113
2114 const auto &meshProperties = context.getMeshShaderPropertiesEXT();
2115 if (kNumLayers > meshProperties.maxMeshMultiviewViewCount)
2116 {
2117 std::ostringstream msg;
2118 msg << "maxMeshMultiviewViewCount too low: " << meshProperties.maxMeshMultiviewViewCount
2119 << " and the test needs " << kNumLayers;
2120 TCU_THROW(NotSupportedError, msg.str());
2121 }
2122 }
2123
initPrograms(vk::SourceCollections & programCollection) const2124 void ViewIndexCase::initPrograms(vk::SourceCollections &programCollection) const
2125 {
2126 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2127
2128 DE_ASSERT(!m_taskNeeded);
2129
2130 // Mesh shader: choose output color depending on the view index.
2131 {
2132 std::ostringstream mesh;
2133 mesh << "#version 460\n"
2134 << "#extension GL_EXT_mesh_shader : enable\n"
2135 << "#extension GL_EXT_multiview : enable\n"
2136 << "\n"
2137 << "layout (local_size_x=1) in;\n"
2138 << "layout (triangles) out;\n"
2139 << "layout (max_vertices=3, max_primitives=1) out;\n"
2140 << "\n"
2141 << "vec4 colors[" << kNumLayers << "] = vec4[](\n"
2142 << " vec4(0.0, 0.0, 1.0, 1.0),\n"
2143 << " vec4(1.0, 0.0, 1.0, 1.0),\n"
2144 << " vec4(0.0, 1.0, 1.0, 1.0),\n"
2145 << " vec4(1.0, 1.0, 0.0, 1.0)\n"
2146 << ");\n"
2147 << "\n"
2148 << "layout (location=0) perprimitiveEXT out vec4 primitiveColor[];\n"
2149 << "\n"
2150 << "void main ()\n"
2151 << "{\n"
2152 << " SetMeshOutputsEXT(3u, 1u);\n"
2153 << "\n"
2154 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
2155 << " primitiveColor[0] = colors[gl_ViewIndex];\n"
2156 << "\n"
2157 << " gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2158 << " gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
2159 << " gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
2160 << "}\n";
2161 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2162 }
2163
2164 // Fragment shader writes its output using the primitive color from the mesh shader.
2165 {
2166 std::ostringstream frag;
2167 frag << "#version 460\n"
2168 << "#extension GL_EXT_mesh_shader : enable\n"
2169 << "#extension GL_EXT_multiview : enable\n"
2170 << "\n"
2171 << "layout (location=0) perprimitiveEXT in vec4 primitiveColor;\n"
2172 << "layout (location=0) out vec4 outColor;\n"
2173 << "\n"
2174 << "void main ()\n"
2175 << "{\n"
2176 << " outColor = primitiveColor;\n"
2177 << "}\n";
2178 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
2179 }
2180 }
2181
createInstance(Context & context) const2182 TestInstance *ViewIndexCase::createInstance(Context &context) const
2183 {
2184 // Must match the shader.
2185 ColorVec expectedColors;
2186
2187 expectedColors.reserve(kNumLayers);
2188 expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
2189 expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
2190 expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
2191 expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
2192
2193 const IterationParams iterationParams = {
2194 getDefaultExtent(), // VkExtent2D colorExtent;
2195 kNumLayers, // uint32_t numLayers;
2196 true, // bool multiview;
2197 false, // bool indirect;
2198 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
2199 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
2200 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
2201 };
2202 return new FullScreenColorInstance(context, iterationParams, expectedColors);
2203 }
2204
2205 // Primitive Shading Rate case.
2206 class PrimitiveShadingRateCase : public MeshShaderBuiltinCase
2207 {
2208 public:
PrimitiveShadingRateCase(tcu::TestContext & testCtx,const std::string & name,FragmentSize topSize,FragmentSize bottomSize)2209 PrimitiveShadingRateCase(tcu::TestContext &testCtx, const std::string &name, FragmentSize topSize,
2210 FragmentSize bottomSize)
2211 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
2212 , m_topSize(topSize)
2213 , m_bottomSize(bottomSize)
2214 {
2215 }
~PrimitiveShadingRateCase(void)2216 virtual ~PrimitiveShadingRateCase(void)
2217 {
2218 }
2219
2220 void initPrograms(vk::SourceCollections &programCollection) const override;
2221 void checkSupport(Context &context) const override;
2222 TestInstance *createInstance(Context &context) const override;
2223
2224 protected:
2225 const FragmentSize m_topSize;
2226 const FragmentSize m_bottomSize;
2227 };
2228
initPrograms(vk::SourceCollections & programCollection) const2229 void PrimitiveShadingRateCase::initPrograms(vk::SourceCollections &programCollection) const
2230 {
2231 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2232
2233 // Shading rate masks to use.
2234 const auto topMask = getGLSLShadingRateMask(m_topSize);
2235 const auto bottomMask = getGLSLShadingRateMask(m_bottomSize);
2236
2237 // Mesh shader.
2238 {
2239 std::ostringstream mesh;
2240 mesh << "#version 460\n"
2241 << "#extension GL_EXT_mesh_shader : enable\n"
2242 << "#extension GL_EXT_fragment_shading_rate : enable\n"
2243 << "\n"
2244 << "layout (local_size_x=1) in;\n"
2245 << "layout (triangles) out;\n"
2246 << "layout (max_vertices=6, max_primitives=4) out;\n"
2247 << "\n"
2248 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
2249 << " int gl_PrimitiveShadingRateEXT;\n"
2250 << "} gl_MeshPrimitivesEXT[];\n"
2251 << "\n"
2252 << "void main ()\n"
2253 << "{\n"
2254 << " SetMeshOutputsEXT(6u, 4u);\n"
2255 << "\n"
2256 << " const vec4 topLeft = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2257 << " const vec4 midLeft = vec4(-1.0, 0.0, 0.0, 1.0);\n"
2258 << " const vec4 botLeft = vec4(-1.0, 1.0, 0.0, 1.0);\n"
2259 << "\n"
2260 << " const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);\n"
2261 << " const vec4 midRight = vec4( 1.0, 0.0, 0.0, 1.0);\n"
2262 << " const vec4 botRight = vec4( 1.0, 1.0, 0.0, 1.0);\n"
2263 << "\n"
2264 << " gl_MeshVerticesEXT[0].gl_Position = topLeft;\n"
2265 << " gl_MeshVerticesEXT[1].gl_Position = midLeft;\n"
2266 << " gl_MeshVerticesEXT[2].gl_Position = botLeft;\n"
2267 << "\n"
2268 << " gl_MeshVerticesEXT[3].gl_Position = topRight;\n"
2269 << " gl_MeshVerticesEXT[4].gl_Position = midRight;\n"
2270 << " gl_MeshVerticesEXT[5].gl_Position = botRight;\n"
2271 << "\n"
2272 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
2273 << " gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
2274 << " gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
2275 << " gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
2276 << "\n"
2277 << " gl_MeshPrimitivesEXT[0].gl_PrimitiveShadingRateEXT = " << topMask << ";\n"
2278 << " gl_MeshPrimitivesEXT[1].gl_PrimitiveShadingRateEXT = " << topMask << ";\n"
2279 << " gl_MeshPrimitivesEXT[2].gl_PrimitiveShadingRateEXT = " << bottomMask << ";\n"
2280 << " gl_MeshPrimitivesEXT[3].gl_PrimitiveShadingRateEXT = " << bottomMask << ";\n"
2281 << "}\n";
2282 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2283 }
2284
2285 // Frag shader.
2286 {
2287 const auto extent = getDefaultExtent();
2288 const auto halfHeight = static_cast<float>(extent.height) / 2.0f;
2289
2290 std::ostringstream frag;
2291 frag << "#version 460\n"
2292 << "#extension GL_EXT_mesh_shader : enable\n"
2293 << "#extension GL_EXT_fragment_shading_rate : enable\n"
2294 << "\n"
2295 << "layout (location=0) out vec4 outColor;\n"
2296 << "\n"
2297 << "void main ()\n"
2298 << "{\n"
2299 // Checks the shading rate matches.
2300 << " const int expectedRate = ((gl_FragCoord.y < " << halfHeight << ")? " << topMask << " : "
2301 << bottomMask << ");\n"
2302 << " outColor = ((gl_ShadingRateEXT == expectedRate) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, "
2303 "1.0));\n"
2304 << "}\n";
2305 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
2306 }
2307 }
2308
checkSupport(Context & context) const2309 void PrimitiveShadingRateCase::checkSupport(Context &context) const
2310 {
2311 MeshShaderBuiltinCase::checkSupport(context);
2312
2313 context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
2314
2315 const auto &meshShaderFeatures = context.getMeshShaderFeaturesEXT();
2316 if (!meshShaderFeatures.primitiveFragmentShadingRateMeshShader)
2317 TCU_THROW(NotSupportedError, "Primitive fragment shading rate not supported in mesh shaders");
2318 }
2319
createInstance(Context & context) const2320 TestInstance *PrimitiveShadingRateCase::createInstance(Context &context) const
2321 {
2322 const ColorVec expectedColors(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
2323 FragmentSizeVector fsInUse{m_topSize, m_bottomSize};
2324 const IterationParams iterationParams = {
2325 getDefaultExtent(), // VkExtent2D colorExtent;
2326 1u, // uint32_t numLayers;
2327 false, // bool multiview;
2328 false, // bool indirect;
2329 tcu::just(getBadShadingRateSize(begin(fsInUse), end(fsInUse))), // tcu::Maybe<FragmentSize> fragmentSize;
2330 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
2331 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
2332 };
2333 return new FullScreenColorInstance(context, iterationParams, expectedColors);
2334 }
2335
2336 // Cull Primitives case.
2337 class CullPrimitivesCase : public MeshShaderBuiltinCase
2338 {
2339 public:
CullPrimitivesCase(tcu::TestContext & testCtx,const std::string & name)2340 CullPrimitivesCase(tcu::TestContext &testCtx, const std::string &name)
2341 : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
2342 {
2343 }
~CullPrimitivesCase(void)2344 virtual ~CullPrimitivesCase(void)
2345 {
2346 }
2347
2348 void initPrograms(vk::SourceCollections &programCollection) const override;
2349 TestInstance *createInstance(Context &context) const override;
2350 };
2351
initPrograms(vk::SourceCollections & programCollection) const2352 void CullPrimitivesCase::initPrograms(vk::SourceCollections &programCollection) const
2353 {
2354 const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2355
2356 // Mesh shader.
2357 {
2358 std::ostringstream mesh;
2359 mesh << "#version 460\n"
2360 << "#extension GL_EXT_mesh_shader : enable\n"
2361 << "\n"
2362 << "layout (local_size_x=1) in;\n"
2363 << "layout (triangles) out;\n"
2364 << "layout (max_vertices=6, max_primitives=4) out;\n"
2365 << "\n"
2366 << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
2367 << " bool gl_CullPrimitiveEXT;\n"
2368 << "} gl_MeshPrimitivesEXT[];\n"
2369 << "\n"
2370 << "void main ()\n"
2371 << "{\n"
2372 << " SetMeshOutputsEXT(6u, 4u);\n"
2373 << "\n"
2374 << " const vec4 topLeft = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2375 << " const vec4 midLeft = vec4(-1.0, 0.0, 0.0, 1.0);\n"
2376 << " const vec4 botLeft = vec4(-1.0, 1.0, 0.0, 1.0);\n"
2377 << "\n"
2378 << " const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);\n"
2379 << " const vec4 midRight = vec4( 1.0, 0.0, 0.0, 1.0);\n"
2380 << " const vec4 botRight = vec4( 1.0, 1.0, 0.0, 1.0);\n"
2381 << "\n"
2382 << " gl_MeshVerticesEXT[0].gl_Position = topLeft;\n"
2383 << " gl_MeshVerticesEXT[1].gl_Position = midLeft;\n"
2384 << " gl_MeshVerticesEXT[2].gl_Position = botLeft;\n"
2385 << "\n"
2386 << " gl_MeshVerticesEXT[3].gl_Position = topRight;\n"
2387 << " gl_MeshVerticesEXT[4].gl_Position = midRight;\n"
2388 << " gl_MeshVerticesEXT[5].gl_Position = botRight;\n"
2389 << "\n"
2390 << " gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
2391 << " gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
2392 << " gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
2393 << " gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
2394 << "\n"
2395 << " gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = false;\n"
2396 << " gl_MeshPrimitivesEXT[1].gl_CullPrimitiveEXT = false;\n"
2397 << " gl_MeshPrimitivesEXT[2].gl_CullPrimitiveEXT = true;\n"
2398 << " gl_MeshPrimitivesEXT[3].gl_CullPrimitiveEXT = true;\n"
2399 << "}\n";
2400 programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2401 }
2402
2403 // Frag shader.
2404 programCollection.glslSources.add("frag") << glu::FragmentSource(getBasicFragShader());
2405 }
2406
createInstance(Context & context) const2407 TestInstance *CullPrimitivesCase::createInstance(Context &context) const
2408 {
2409 const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
2410 const tcu::Vec4 black = getClearColor();
2411
2412 const IterationParams iterationParams = {
2413 getDefaultExtent(), // VkExtent2D colorExtent;
2414 1u, // uint32_t numLayers;
2415 false, // bool multiview;
2416 false, // bool indirect;
2417 tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
2418 getDefaultDrawCommands(), // DrawCommandVec drawArgs;
2419 {}, // ViewportVec viewports; // If empty, a single default viewport is used.
2420 };
2421 return new QuadrantsInstance(context, iterationParams, blue, blue, black, black);
2422 }
2423
2424 } // namespace
2425
createMeshShaderBuiltinTestsEXT(tcu::TestContext & testCtx)2426 tcu::TestCaseGroup *createMeshShaderBuiltinTestsEXT(tcu::TestContext &testCtx)
2427 {
2428 GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "builtin"));
2429
2430 mainGroup->addChild(new PositionCase(testCtx, "position"));
2431 mainGroup->addChild(new PointSizeCase(testCtx, "point_size"));
2432 mainGroup->addChild(new ClipDistanceCase(testCtx, "clip_distance"));
2433 mainGroup->addChild(new CullDistanceCase(testCtx, "cull_distance"));
2434 mainGroup->addChild(new PrimitiveIdCase(testCtx, "primitive_id_glsl", true /*glslFrag*/));
2435 mainGroup->addChild(new PrimitiveIdCase(testCtx, "primitive_id_spirv", false /*glslFrag*/));
2436 mainGroup->addChild(new LayerCase(testCtx, "layer", true /*writeval*/, false /*shareVertices*/));
2437 mainGroup->addChild(new LayerCase(testCtx, "layer_shared", true /*writeval*/, true /*shareVertices*/));
2438 mainGroup->addChild(new LayerCase(testCtx, "layer_no_write", false /*writeval*/, false /*shareVertices*/));
2439 mainGroup->addChild(new ViewportIndexCase(testCtx, "viewport_index", true /*writeVal*/, false /*shareVertices*/));
2440 mainGroup->addChild(
2441 new ViewportIndexCase(testCtx, "viewport_index_shared", true /*writeVal*/, true /*shareVertices*/));
2442 mainGroup->addChild(
2443 new ViewportIndexCase(testCtx, "viewport_index_no_write", false /*writeVal*/, false /*shareVertices*/));
2444 mainGroup->addChild(new WorkGroupIdCase(testCtx, "work_group_id_in_mesh", false /*taskNeeded*/));
2445 mainGroup->addChild(new WorkGroupIdCase(testCtx, "work_group_id_in_task", true /*taskNeeded*/));
2446 mainGroup->addChild(new NumWorkgroupsCase(testCtx, "num_work_groups_mesh", tcu::Nothing, tcu::UVec3(5u, 6u, 7u)));
2447 mainGroup->addChild(new NumWorkgroupsCase(testCtx, "num_work_groups_task_and_mesh",
2448 tcu::just(tcu::UVec3(2u, 3u, 4u)), tcu::UVec3(3u, 4u, 2u)));
2449 mainGroup->addChild(
2450 new LocalInvocationCase(testCtx, "local_invocation_id_in_mesh", false /*taskNeeded*/, LocalInvocation::ID));
2451 mainGroup->addChild(
2452 new LocalInvocationCase(testCtx, "local_invocation_id_in_task", true /*taskNeeded*/, LocalInvocation::ID));
2453 mainGroup->addChild(new LocalInvocationCase(testCtx, "local_invocation_index_in_task", true /*taskNeeded*/,
2454 LocalInvocation::INDEX));
2455 mainGroup->addChild(new LocalInvocationCase(testCtx, "local_invocation_index_in_mesh", false /*taskNeeded*/,
2456 LocalInvocation::INDEX));
2457 mainGroup->addChild(new GlobalInvocationIdCase(testCtx, "global_invocation_id_in_mesh", false /*taskNeeded*/));
2458 mainGroup->addChild(new GlobalInvocationIdCase(testCtx, "global_invocation_id_in_task", true /*taskNeeded*/));
2459 mainGroup->addChild(new DrawIndexCase(testCtx, "draw_index_in_mesh", false /*taskNeeded*/));
2460 mainGroup->addChild(new DrawIndexCase(testCtx, "draw_index_in_task", true /*taskNeeded*/));
2461 mainGroup->addChild(new ViewIndexCase(testCtx, "view_index"));
2462 mainGroup->addChild(new CullPrimitivesCase(testCtx, "cull_primitives"));
2463
2464 // Primitive shading rate tests.
2465 {
2466 const auto sizeCount = static_cast<int>(FragmentSize::SIZE_COUNT);
2467
2468 for (int i = 0; i < sizeCount; ++i)
2469 for (int j = 0; j < sizeCount; ++j)
2470 {
2471 const auto topSize = static_cast<FragmentSize>(i);
2472 const auto bottomSize = static_cast<FragmentSize>(j);
2473
2474 const auto topExtent = getShadingRateSize(topSize);
2475 const auto bottomExtent = getShadingRateSize(bottomSize);
2476
2477 const auto testName = "primitive_shading_rate_" + std::to_string(topExtent.width) + "x" +
2478 std::to_string(topExtent.height) + "_" + std::to_string(bottomExtent.width) +
2479 "x" + std::to_string(bottomExtent.height);
2480
2481 mainGroup->addChild(new PrimitiveShadingRateCase(testCtx, testName, topSize, bottomSize));
2482 }
2483 }
2484
2485 return mainGroup.release();
2486 }
2487
2488 } // namespace MeshShader
2489 } // namespace vkt
2490