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