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