--- /dev/null
- constexpr auto Message = tcu::TestLog::Message;
- constexpr auto EndMessage = tcu::TestLog::EndMessage;
-
+ /*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2021 The Khronos Group Inc.
+ * Copyright (c) 2021 Valve Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Mesh Shader Misc Tests
+ *//*--------------------------------------------------------------------*/
+
+ #include "vktMeshShaderMiscTests.hpp"
+ #include "vktTestCase.hpp"
+
+ #include "vkBuilderUtil.hpp"
+ #include "vkImageWithMemory.hpp"
+ #include "vkBufferWithMemory.hpp"
+ #include "vkObjUtil.hpp"
+ #include "vkTypeUtil.hpp"
+ #include "vkCmdUtil.hpp"
+ #include "vkImageUtil.hpp"
+ #include "vkBarrierUtil.hpp"
+
+ #include "tcuImageCompare.hpp"
+ #include "tcuTexture.hpp"
+ #include "tcuTextureUtil.hpp"
+ #include "tcuMaybe.hpp"
+ #include "tcuStringTemplate.hpp"
+ #include "tcuTestLog.hpp"
+
+ #include <memory>
+ #include <utility>
+ #include <vector>
+ #include <string>
+ #include <sstream>
+ #include <map>
+
+ namespace vkt
+ {
+ namespace MeshShader
+ {
+
+ namespace
+ {
+
+ using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
+
+ using namespace vk;
+
+ // Output images will use this format.
+ VkFormat getOutputFormat ()
+ {
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ }
+
+ // Threshold that's reasonable for the previous format.
+ float getCompareThreshold ()
+ {
+ return 0.005f; // 1/256 < 0.005 < 2/256
+ }
+
+ // Check mesh shader support.
+ void genericCheckSupport (Context& context, bool requireTaskShader, bool requireVertexStores)
+ {
+ context.requireDeviceFunctionality("VK_NV_mesh_shader");
+
+ const auto& meshFeatures = context.getMeshShaderFeatures();
+
+ if (!meshFeatures.meshShader)
+ TCU_THROW(NotSupportedError, "Mesh shader not supported");
+
+ if (requireTaskShader && !meshFeatures.taskShader)
+ TCU_THROW(NotSupportedError, "Task shader not supported");
+
+ if (requireVertexStores)
+ {
+ const auto& features = context.getDeviceFeatures();
+ if (!features.vertexPipelineStoresAndAtomics)
+ TCU_THROW(NotSupportedError, "Vertex pieline stores and atomics not supported");
+ }
+ }
+
+ struct MiscTestParams
+ {
+ tcu::Maybe<uint32_t> taskCount;
+ uint32_t meshCount;
+
+ uint32_t width;
+ uint32_t height;
+
+ // Makes the class polymorphic and allows the right destructor to be used for subclasses.
+ virtual ~MiscTestParams () {}
+
+ bool needsTaskShader () const
+ {
+ return static_cast<bool>(taskCount);
+ }
+
+ uint32_t drawCount () const
+ {
+ if (needsTaskShader())
+ return taskCount.get();
+ return meshCount;
+ }
+ };
+
+ using ParamsPtr = std::unique_ptr<MiscTestParams>;
+
+ class MeshShaderMiscCase : public vkt::TestCase
+ {
+ public:
+ MeshShaderMiscCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params);
+ virtual ~MeshShaderMiscCase (void) {}
+
+ void checkSupport (Context& context) const override;
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+
+ protected:
+ std::unique_ptr<MiscTestParams> m_params;
+ };
+
+ MeshShaderMiscCase::MeshShaderMiscCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : vkt::TestCase (testCtx, name, description)
+ , m_params (params.release())
+ {}
+
+ void MeshShaderMiscCase::checkSupport (Context& context) const
+ {
+ genericCheckSupport(context, m_params->needsTaskShader(), /*requireVertexStores*/false);
+ }
+
+ // Adds the generic fragment shader. To be called by subclasses.
+ void MeshShaderMiscCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ std::string frag =
+ "#version 450\n"
+ "#extension GL_NV_mesh_shader : enable\n"
+ "\n"
+ "layout (location=0) in perprimitiveNV vec4 primitiveColor;\n"
+ "layout (location=0) out vec4 outColor;\n"
+ "\n"
+ "void main ()\n"
+ "{\n"
+ " outColor = primitiveColor;\n"
+ "}\n"
+ ;
+ programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
+ }
+
+ class MeshShaderMiscInstance : public vkt::TestInstance
+ {
+ public:
+ MeshShaderMiscInstance (Context& context, const MiscTestParams* params)
+ : vkt::TestInstance (context)
+ , m_params (params)
+ , m_referenceLevel ()
+ {
+ }
+
+ void generateSolidRefLevel (const tcu::Vec4& color, std::unique_ptr<tcu::TextureLevel>& output);
+ virtual void generateReferenceLevel () = 0;
+
+ virtual bool verifyResult (const tcu::ConstPixelBufferAccess& resultAccess, const tcu::TextureLevel& referenceLevel) const;
+ virtual bool verifyResult (const tcu::ConstPixelBufferAccess& resultAccess) const;
+ tcu::TestStatus iterate () override;
+
+ protected:
+ const MiscTestParams* m_params;
+ std::unique_ptr<tcu::TextureLevel> m_referenceLevel;
+ };
+
+ void MeshShaderMiscInstance::generateSolidRefLevel (const tcu::Vec4& color, std::unique_ptr<tcu::TextureLevel>& output)
+ {
+ const auto format = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(format);
+
+ const auto iWidth = static_cast<int>(m_params->width);
+ const auto iHeight = static_cast<int>(m_params->height);
+
+ output.reset(new tcu::TextureLevel(tcuFormat, iWidth, iHeight));
+
+ const auto access = output->getAccess();
+
+ // Fill with solid color.
+ tcu::clear(access, color);
+ }
+
+ bool MeshShaderMiscInstance::verifyResult (const tcu::ConstPixelBufferAccess& resultAccess) const
+ {
+ return verifyResult(resultAccess, *m_referenceLevel);
+ }
+
+ bool MeshShaderMiscInstance::verifyResult (const tcu::ConstPixelBufferAccess& resultAccess, const tcu::TextureLevel& referenceLevel) const
+ {
+ const auto referenceAccess = referenceLevel.getAccess();
+
+ const auto refWidth = referenceAccess.getWidth();
+ const auto refHeight = referenceAccess.getHeight();
+ const auto refDepth = referenceAccess.getDepth();
+
+ const auto resWidth = resultAccess.getWidth();
+ const auto resHeight = resultAccess.getHeight();
+ const auto resDepth = resultAccess.getDepth();
+
+ DE_ASSERT(resWidth == refWidth || resHeight == refHeight || resDepth == refDepth);
+
+ // For release builds.
+ DE_UNREF(refWidth);
+ DE_UNREF(refHeight);
+ DE_UNREF(refDepth);
+ DE_UNREF(resWidth);
+ DE_UNREF(resHeight);
+ DE_UNREF(resDepth);
+
+ const auto outputFormat = getOutputFormat();
+ const auto expectedFormat = mapVkFormat(outputFormat);
+ const auto resFormat = resultAccess.getFormat();
+ const auto refFormat = referenceAccess.getFormat();
+
+ DE_ASSERT(resFormat == expectedFormat && refFormat == expectedFormat);
+
+ // For release builds
+ DE_UNREF(expectedFormat);
+ DE_UNREF(resFormat);
+ DE_UNREF(refFormat);
+
+ auto& log = m_context.getTestContext().getLog();
+ const auto threshold = getCompareThreshold();
+ const tcu::Vec4 thresholdVec (threshold, threshold, threshold, threshold);
+
+ return tcu::floatThresholdCompare(log, "Result", "", referenceAccess, resultAccess, thresholdVec, tcu::COMPARE_LOG_ON_ERROR);
+ }
+
+ tcu::TestStatus MeshShaderMiscInstance::iterate ()
+ {
+ const auto& vkd = m_context.getDeviceInterface();
+ const auto device = m_context.getDevice();
+ auto& alloc = m_context.getDefaultAllocator();
+ const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
+ const auto queue = m_context.getUniversalQueue();
+
+ const auto imageFormat = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(imageFormat);
+ const auto imageExtent = makeExtent3D(m_params->width, m_params->height, 1u);
+ const auto imageUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+
+ const VkImageCreateInfo colorBufferInfo =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ imageFormat, // VkFormat format;
+ imageExtent, // VkExtent3D extent;
+ 1u, // uint32_t mipLevels;
+ 1u, // uint32_t arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ imageUsage, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // uint32_t queueFamilyIndexCount;
+ nullptr, // const uint32_t* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+
+ // Create color image and view.
+ ImageWithMemory colorImage (vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
+ const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
+ const auto colorView = makeImageView(vkd, device, colorImage.get(), VK_IMAGE_VIEW_TYPE_2D, imageFormat, colorSRR);
+
+ // Create a memory buffer for verification.
+ const auto verificationBufferSize = static_cast<VkDeviceSize>(imageExtent.width * imageExtent.height * tcu::getPixelSize(tcuFormat));
+ const auto verificationBufferUsage = (VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ const auto verificationBufferInfo = makeBufferCreateInfo(verificationBufferSize, verificationBufferUsage);
+
+ BufferWithMemory verificationBuffer (vkd, device, alloc, verificationBufferInfo, MemoryRequirement::HostVisible);
+ auto& verificationBufferAlloc = verificationBuffer.getAllocation();
+ void* verificationBufferData = verificationBufferAlloc.getHostPtr();
+
+ // Pipeline layout.
+ const auto pipelineLayout = makePipelineLayout(vkd, device);
+
+ // Shader modules.
+ const auto& binaries = m_context.getBinaryCollection();
+ const auto hasTask = binaries.contains("task");
+
+ const auto meshShader = createShaderModule(vkd, device, binaries.get("mesh"));
+ const auto fragShader = createShaderModule(vkd, device, binaries.get("frag"));
+
+ Move<VkShaderModule> taskShader;
+ if (hasTask)
+ taskShader = createShaderModule(vkd, device, binaries.get("task"));
+
+ // Render pass.
+ const auto renderPass = makeRenderPass(vkd, device, imageFormat);
+
+ // Framebuffer.
+ const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorView.get(), imageExtent.width, imageExtent.height);
+
+ // Viewport and scissor.
+ const std::vector<VkViewport> viewports (1u, makeViewport(imageExtent));
+ const std::vector<VkRect2D> scissors (1u, makeRect2D(imageExtent));
+
+ const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
+ taskShader.get(), meshShader.get(), fragShader.get(),
+ renderPass.get(), viewports, scissors);
+
+ // Command pool and buffer.
+ const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
+ const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+ const auto cmdBuffer = cmdBufferPtr.get();
+
+ beginCommandBuffer(vkd, cmdBuffer);
+
+ // Run pipeline.
+ const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 0.0f);
+ beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor);
+ vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
+ vkd.cmdDrawMeshTasksNV(cmdBuffer, m_params->drawCount(), 0u);
+ endRenderPass(vkd, cmdBuffer);
+
+ // Copy color buffer to verification buffer.
+ const auto colorAccess = (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT);
+ const auto transferRead = VK_ACCESS_TRANSFER_READ_BIT;
+ const auto transferWrite = VK_ACCESS_TRANSFER_WRITE_BIT;
+ const auto hostRead = VK_ACCESS_HOST_READ_BIT;
+
+ const auto preCopyBarrier = makeImageMemoryBarrier(colorAccess, transferRead, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImage.get(), colorSRR);
+ const auto postCopyBarrier = makeMemoryBarrier(transferWrite, hostRead);
+ const auto copyRegion = makeBufferImageCopy(imageExtent, colorSRL);
+
+ vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preCopyBarrier);
+ vkd.cmdCopyImageToBuffer(cmdBuffer, colorImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verificationBuffer.get(), 1u, ©Region);
+ vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postCopyBarrier, 0u, nullptr, 0u, nullptr);
+
+ endCommandBuffer(vkd, cmdBuffer);
+ submitCommandsAndWait(vkd, device, queue, cmdBuffer);
+
+ // Generate reference image and compare results.
+ const tcu::IVec3 iExtent (static_cast<int>(imageExtent.width), static_cast<int>(imageExtent.height), 1);
+ const tcu::ConstPixelBufferAccess verificationAccess (tcuFormat, iExtent, verificationBufferData);
+
+ generateReferenceLevel();
+ invalidateAlloc(vkd, device, verificationBufferAlloc);
+ if (!verifyResult(verificationAccess))
+ TCU_FAIL("Result does not match reference; check log for details");
+
+ return tcu::TestStatus::pass("Pass");
+ }
+
+ // Verify passing more complex data between the task and mesh shaders.
+ class ComplexTaskDataCase : public MeshShaderMiscCase
+ {
+ public:
+ ComplexTaskDataCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class ComplexTaskDataInstance : public MeshShaderMiscInstance
+ {
+ public:
+ ComplexTaskDataInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ void ComplexTaskDataInstance::generateReferenceLevel ()
+ {
+ const auto format = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(format);
+
+ const auto iWidth = static_cast<int>(m_params->width);
+ const auto iHeight = static_cast<int>(m_params->height);
+
+ const auto halfWidth = iWidth / 2;
+ const auto halfHeight = iHeight / 2;
+
+ m_referenceLevel.reset(new tcu::TextureLevel(tcuFormat, iWidth, iHeight));
+
+ const auto access = m_referenceLevel->getAccess();
+
+ // Each image quadrant gets a different color.
+ for (int y = 0; y < iHeight; ++y)
+ for (int x = 0; x < iWidth; ++x)
+ {
+ const float red = ((y < halfHeight) ? 0.0f : 1.0f);
+ const float green = ((x < halfWidth) ? 0.0f : 1.0f);
+ const auto refColor = tcu::Vec4(red, green, 1.0f, 1.0f);
+ access.setPixel(refColor, x, y);
+ }
+ }
+
+ void ComplexTaskDataCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ // Add the generic fragment shader.
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ const std::string taskDataDeclTemplate =
+ "struct RowId {\n"
+ " uint id;\n"
+ "};\n"
+ "\n"
+ "struct WorkGroupData {\n"
+ " float WorkGroupIdPlusOnex1000Iota[10];\n"
+ " RowId rowId;\n"
+ " uvec3 WorkGroupIdPlusOnex2000Iota;\n"
+ " vec2 WorkGroupIdPlusOnex3000Iota;\n"
+ "};\n"
+ "\n"
+ "struct ExternalData {\n"
+ " float OneMillion;\n"
+ " uint TwoMillion;\n"
+ " WorkGroupData workGroupData;\n"
+ "};\n"
+ "\n"
+ "${INOUT} taskNV TaskData {\n"
+ " uint yes;\n"
+ " ExternalData externalData;\n"
+ "} td;\n"
+ ;
+ const tcu::StringTemplate taskDataDecl(taskDataDeclTemplate);
+
+ {
+ std::map<std::string, std::string> taskMap;
+ taskMap["INOUT"] = "out";
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=1) in;\n"
+ << "\n"
+ << taskDataDecl.specialize(taskMap)
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_TaskCountNV = 2u;\n"
+ << " td.yes = 1u;\n"
+ << " td.externalData.OneMillion = 1000000.0;\n"
+ << " td.externalData.TwoMillion = 2000000u;\n"
+ << " for (uint i = 0; i < 10; i++) {\n"
+ << " td.externalData.workGroupData.WorkGroupIdPlusOnex1000Iota[i] = float((gl_WorkGroupID.x + 1u) * 1000 + i);\n"
+ << " }\n"
+ << " {\n"
+ << " uint baseVal = (gl_WorkGroupID.x + 1u) * 2000;\n"
+ << " td.externalData.workGroupData.WorkGroupIdPlusOnex2000Iota = uvec3(baseVal, baseVal + 1, baseVal + 2);\n"
+ << " }\n"
+ << " {\n"
+ << " uint baseVal = (gl_WorkGroupID.x + 1u) * 3000;\n"
+ << " td.externalData.workGroupData.WorkGroupIdPlusOnex3000Iota = vec2(baseVal, baseVal + 1);\n"
+ << " }\n"
+ << " td.externalData.workGroupData.rowId.id = gl_WorkGroupID.x;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+ }
+
+ {
+ std::map<std::string, std::string> meshMap;
+ meshMap["INOUT"] = "in";
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=2) in;\n"
+ << "layout(triangles) out;\n"
+ << "layout(max_vertices=4, max_primitives=2) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 triangleColor[];\n"
+ << "\n"
+ << taskDataDecl.specialize(meshMap)
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " bool dataOK = true;\n"
+ << " dataOK = (dataOK && (td.yes == 1u));\n"
+ << " dataOK = (dataOK && (td.externalData.OneMillion == 1000000.0 && td.externalData.TwoMillion == 2000000u));\n"
+ << " uint rowId = td.externalData.workGroupData.rowId.id;\n"
+ << " dataOK = (dataOK && (rowId == 0u || rowId == 1u));\n"
+ << "\n"
+ << " {\n"
+ << " uint baseVal = (rowId + 1u) * 1000u;\n"
+ << " for (uint i = 0; i < 10; i++) {\n"
+ << " if (td.externalData.workGroupData.WorkGroupIdPlusOnex1000Iota[i] != float(baseVal + i)) {\n"
+ << " dataOK = false;\n"
+ << " break;\n"
+ << " }\n"
+ << " }\n"
+ << " }\n"
+ << "\n"
+ << " {\n"
+ << " uint baseVal = (rowId + 1u) * 2000;\n"
+ << " uvec3 expected = uvec3(baseVal, baseVal + 1, baseVal + 2);\n"
+ << " if (td.externalData.workGroupData.WorkGroupIdPlusOnex2000Iota != expected) {\n"
+ << " dataOK = false;\n"
+ << " }\n"
+ << " }\n"
+ << "\n"
+ << " {\n"
+ << " uint baseVal = (rowId + 1u) * 3000;\n"
+ << " vec2 expected = vec2(baseVal, baseVal + 1);\n"
+ << " if (td.externalData.workGroupData.WorkGroupIdPlusOnex3000Iota != expected) {\n"
+ << " dataOK = false;\n"
+ << " }\n"
+ << " }\n"
+ << "\n"
+ << " uint columnId = gl_WorkGroupID.x;\n"
+ << "\n"
+ << " if (dataOK) {\n"
+ << " gl_PrimitiveCountNV = 2u;\n"
+ << " }\n"
+ << " else {\n"
+ << " gl_PrimitiveCountNV = 0u;\n"
+ << " return;\n"
+ << " }\n"
+ << "\n"
+ << " const vec4 outColor = vec4(rowId, columnId, 1.0f, 1.0f);\n"
+ << " triangleColor[0] = outColor;\n"
+ << " triangleColor[1] = outColor;\n"
+ << "\n"
+ << " // Each local invocation will generate two points and one triangle from the quad.\n"
+ << " // The first local invocation will generate the top quad vertices.\n"
+ << " // The second invocation will generate the two bottom vertices.\n"
+ << " vec4 left = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ << " vec4 right = vec4(1.0, 0.0, 0.0, 1.0);\n"
+ << "\n"
+ << " float localInvocationOffsetY = float(gl_LocalInvocationID.x);\n"
+ << " left.y += localInvocationOffsetY;\n"
+ << " right.y += localInvocationOffsetY;\n"
+ << "\n"
+ << " // The code above creates a quad from (0, 0) to (1, 1) but we need to offset it\n"
+ << " // in X and/or Y depending on the row and column, to place it in other quadrants.\n"
+ << " float quadrantOffsetX = float(int(columnId) - 1);\n"
+ << " float quadrantOffsetY = float(int(rowId) - 1);\n"
+ << "\n"
+ << " left.x += quadrantOffsetX;\n"
+ << " right.x += quadrantOffsetX;\n"
+ << "\n"
+ << " left.y += quadrantOffsetY;\n"
+ << " right.y += quadrantOffsetY;\n"
+ << "\n"
+ << " uint baseVertexId = 2*gl_LocalInvocationID.x;\n"
+ << " gl_MeshVerticesNV[baseVertexId + 0].gl_Position = left;\n"
+ << " gl_MeshVerticesNV[baseVertexId + 1].gl_Position = right;\n"
+ << "\n"
+ << " uint baseIndexId = 3*gl_LocalInvocationID.x;\n"
+ << " // 0,1,2 or 1,2,3 (note: triangles alternate front face this way)\n"
+ << " gl_PrimitiveIndicesNV[baseIndexId + 0] = 0 + gl_LocalInvocationID.x;\n"
+ << " gl_PrimitiveIndicesNV[baseIndexId + 1] = 1 + gl_LocalInvocationID.x;\n"
+ << " gl_PrimitiveIndicesNV[baseIndexId + 2] = 2 + gl_LocalInvocationID.x;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+ }
+
+ TestInstance* ComplexTaskDataCase::createInstance (Context& context) const
+ {
+ return new ComplexTaskDataInstance(context, m_params.get());
+ }
+
+ // Verify drawing a single point.
+ class SinglePointCase : public MeshShaderMiscCase
+ {
+ public:
+ SinglePointCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class SinglePointInstance : public MeshShaderMiscInstance
+ {
+ public:
+ SinglePointInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* SinglePointCase::createInstance (Context& context) const
+ {
+ return new SinglePointInstance (context, m_params.get());
+ }
+
+ void SinglePointCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ DE_ASSERT(!m_params->needsTaskShader());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=1) in;\n"
+ << "layout(points) out;\n"
+ << "layout(max_vertices=256, max_primitives=256) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 pointColor[];\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 1u;\n"
+ << " pointColor[0] = vec4(0.0f, 1.0f, 1.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[0].gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[0].gl_PointSize = 1.0f;\n"
+ << " gl_PrimitiveIndicesNV[0] = 0;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ void SinglePointInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), m_referenceLevel);
+
+ const auto halfWidth = static_cast<int>(m_params->width / 2u);
+ const auto halfHeight = static_cast<int>(m_params->height / 2u);
+ const auto access = m_referenceLevel->getAccess();
+
+ access.setPixel(tcu::Vec4(0.0f, 1.0f, 1.0f, 1.0f), halfWidth, halfHeight);
+ }
+
+ // Verify drawing a single line.
+ class SingleLineCase : public MeshShaderMiscCase
+ {
+ public:
+ SingleLineCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class SingleLineInstance : public MeshShaderMiscInstance
+ {
+ public:
+ SingleLineInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* SingleLineCase::createInstance (Context& context) const
+ {
+ return new SingleLineInstance (context, m_params.get());
+ }
+
+ void SingleLineCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ DE_ASSERT(!m_params->needsTaskShader());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=1) in;\n"
+ << "layout(lines) out;\n"
+ << "layout(max_vertices=256, max_primitives=256) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 lineColor[];\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 1u;\n"
+ << " lineColor[0] = vec4(0.0f, 1.0f, 1.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0f, 0.0f, 0.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[1].gl_Position = vec4( 1.0f, 0.0f, 0.0f, 1.0f);\n"
+ << " gl_PrimitiveIndicesNV[0] = 0;\n"
+ << " gl_PrimitiveIndicesNV[1] = 1;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ void SingleLineInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), m_referenceLevel);
+
+ const auto iWidth = static_cast<int>(m_params->width);
+ const auto halfHeight = static_cast<int>(m_params->height / 2u);
+ const auto access = m_referenceLevel->getAccess();
+
+ // Center row.
+ for (int x = 0; x < iWidth; ++x)
+ access.setPixel(tcu::Vec4(0.0f, 1.0f, 1.0f, 1.0f), x, halfHeight);
+ }
+
+ // Verify drawing a single triangle.
+ class SingleTriangleCase : public MeshShaderMiscCase
+ {
+ public:
+ SingleTriangleCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class SingleTriangleInstance : public MeshShaderMiscInstance
+ {
+ public:
+ SingleTriangleInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* SingleTriangleCase::createInstance (Context& context) const
+ {
+ return new SingleTriangleInstance (context, m_params.get());
+ }
+
+ void SingleTriangleCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ DE_ASSERT(!m_params->needsTaskShader());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ const float halfPixelX = 2.0f / static_cast<float>(m_params->width);
+ const float halfPixelY = 2.0f / static_cast<float>(m_params->height);
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=1) in;\n"
+ << "layout(triangles) out;\n"
+ << "layout(max_vertices=256, max_primitives=256) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 triangleColor[];\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 1u;\n"
+ << " triangleColor[0] = vec4(0.0f, 1.0f, 1.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[0].gl_Position = vec4(" << halfPixelY << ", " << -halfPixelX << ", 0.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[1].gl_Position = vec4(" << halfPixelY << ", " << halfPixelX << ", 0.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[2].gl_Position = vec4(" << -halfPixelY << ", 0.0f, 0.0f, 1.0f);\n"
+ << " gl_PrimitiveIndicesNV[0] = 0;\n"
+ << " gl_PrimitiveIndicesNV[1] = 1;\n"
+ << " gl_PrimitiveIndicesNV[2] = 2;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ void SingleTriangleInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), m_referenceLevel);
+
+ const auto halfWidth = static_cast<int>(m_params->width / 2u);
+ const auto halfHeight = static_cast<int>(m_params->height / 2u);
+ const auto access = m_referenceLevel->getAccess();
+
+ // Single pixel in the center.
+ access.setPixel(tcu::Vec4(0.0f, 1.0f, 1.0f, 1.0f), halfWidth, halfHeight);
+ }
+
+ // Verify drawing the maximum number of points.
+ class MaxPointsCase : public MeshShaderMiscCase
+ {
+ public:
+ MaxPointsCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class MaxPointsInstance : public MeshShaderMiscInstance
+ {
+ public:
+ MaxPointsInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* MaxPointsCase::createInstance (Context& context) const
+ {
+ return new MaxPointsInstance (context, m_params.get());
+ }
+
+ void MaxPointsCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ DE_ASSERT(!m_params->needsTaskShader());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ // Fill a 16x16 image with 256 points. Each of the 32 local invocations will handle a segment of 8 pixels. Two segments per row.
+ DE_ASSERT(m_params->width == 16u && m_params->height == 16u);
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=32) in;\n"
+ << "layout(points) out;\n"
+ << "layout(max_vertices=256, max_primitives=256) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 pointColor[];\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 256u;\n"
+ << " uint firstPixel = 8u * gl_LocalInvocationID.x;\n"
+ << " uint row = firstPixel / 16u;\n"
+ << " uint col = firstPixel % 16u;\n"
+ << " float pixSize = 2.0f / 16.0f;\n"
+ << " float yCoord = pixSize * (float(row) + 0.5f) - 1.0f;\n"
+ << " float baseXCoord = pixSize * (float(col) + 0.5f) - 1.0f;\n"
+ << " for (uint i = 0; i < 8u; i++) {\n"
+ << " float xCoord = baseXCoord + pixSize * float(i);\n"
+ << " uint pixId = firstPixel + i;\n"
+ << " gl_MeshVerticesNV[pixId].gl_Position = vec4(xCoord, yCoord, 0.0f, 1.0f);\n"
+ << " gl_MeshVerticesNV[pixId].gl_PointSize = 1.0f;\n"
+ << " gl_PrimitiveIndicesNV[pixId] = pixId;\n"
+ << " pointColor[pixId] = vec4(((xCoord + 1.0f) / 2.0f), ((yCoord + 1.0f) / 2.0f), 0.0f, 1.0f);\n"
+ << " }\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ void MaxPointsInstance::generateReferenceLevel ()
+ {
+ const auto format = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(format);
+
+ const auto iWidth = static_cast<int>(m_params->width);
+ const auto iHeight = static_cast<int>(m_params->height);
+ const auto fWidth = static_cast<float>(m_params->width);
+ const auto fHeight = static_cast<float>(m_params->height);
+
+ m_referenceLevel.reset(new tcu::TextureLevel(tcuFormat, iWidth, iHeight));
+
+ const auto access = m_referenceLevel->getAccess();
+
+ // Fill with gradient like the shader does.
+ for (int y = 0; y < iHeight; ++y)
+ for (int x = 0; x < iWidth; ++x)
+ {
+ const tcu::Vec4 color (
+ ((static_cast<float>(x) + 0.5f) / fWidth),
+ ((static_cast<float>(y) + 0.5f) / fHeight),
+ 0.0f, 1.0f);
+ access.setPixel(color, x, y);
+ }
+ }
+
+ // Verify drawing the maximum number of lines.
+ class MaxLinesCase : public MeshShaderMiscCase
+ {
+ public:
+ MaxLinesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class MaxLinesInstance : public MeshShaderMiscInstance
+ {
+ public:
+ MaxLinesInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* MaxLinesCase::createInstance (Context& context) const
+ {
+ return new MaxLinesInstance (context, m_params.get());
+ }
+
+ void MaxLinesCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ DE_ASSERT(!m_params->needsTaskShader());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ // Fill a 1x1020 image with 255 lines, each line being 4 pixels tall. Each invocation will generate ~8 lines.
+ DE_ASSERT(m_params->width == 1u && m_params->height == 1020u);
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=32) in;\n"
+ << "layout(lines) out;\n"
+ << "layout(max_vertices=256, max_primitives=255) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 lineColor[];\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 255u;\n"
+ << " uint firstLine = 8u * gl_LocalInvocationID.x;\n"
+ << " for (uint i = 0u; i < 8u; i++) {\n"
+ << " uint lineId = firstLine + i;\n"
+ << " uint topPixel = 4u * lineId;\n"
+ << " uint bottomPixel = 3u + topPixel;\n"
+ << " if (bottomPixel < 1020u) {\n"
+ << " float bottomCoord = ((float(bottomPixel) + 1.0f) / 1020.0) * 2.0 - 1.0;\n"
+ << " gl_MeshVerticesNV[lineId + 1u].gl_Position = vec4(0.0, bottomCoord, 0.0f, 1.0f);\n"
+ << " gl_PrimitiveIndicesNV[lineId * 2u] = lineId;\n"
+ << " gl_PrimitiveIndicesNV[lineId * 2u + 1u] = lineId + 1u;\n"
+ << " lineColor[lineId] = vec4(0.0f, 1.0f, float(lineId) / 255.0f, 1.0f);\n"
+ << " } else {\n"
+ << " // The last iteration of the last invocation emits the first point\n"
+ << " gl_MeshVerticesNV[0].gl_Position = vec4(0.0, -1.0, 0.0f, 1.0f);\n"
+ << " }\n"
+ << " }\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ void MaxLinesInstance::generateReferenceLevel ()
+ {
+ const auto format = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(format);
+
+ const auto iWidth = static_cast<int>(m_params->width);
+ const auto iHeight = static_cast<int>(m_params->height);
+
+ m_referenceLevel.reset(new tcu::TextureLevel(tcuFormat, iWidth, iHeight));
+
+ const auto access = m_referenceLevel->getAccess();
+
+ // Fill lines, 4 pixels per line.
+ const uint32_t kNumLines = 255u;
+ const uint32_t kLineHeight = 4u;
+
+ for (uint32_t i = 0u; i < kNumLines; ++i)
+ {
+ const tcu::Vec4 color (0.0f, 1.0f, static_cast<float>(i) / static_cast<float>(kNumLines), 1.0f);
+ for (uint32_t j = 0u; j < kLineHeight; ++j)
+ access.setPixel(color, 0, i*kLineHeight + j);
+ }
+ }
+
+ // Verify drawing the maximum number of triangles.
+ class MaxTrianglesCase : public MeshShaderMiscCase
+ {
+ public:
+ MaxTrianglesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class MaxTrianglesInstance : public MeshShaderMiscInstance
+ {
+ public:
+ MaxTrianglesInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* MaxTrianglesCase::createInstance (Context& context) const
+ {
+ return new MaxTrianglesInstance (context, m_params.get());
+ }
+
+ void MaxTrianglesCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ DE_ASSERT(!m_params->needsTaskShader());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ // Fill a sufficiently large image with solid color. Generate a quarter of a circle with the center in the top left corner,
+ // using a triangle fan that advances from top to bottom. Each invocation will generate ~8 triangles.
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=32) in;\n"
+ << "layout(triangles) out;\n"
+ << "layout(max_vertices=256, max_primitives=254) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 triangleColor[];\n"
+ << "\n"
+ << "const float PI_2 = 1.57079632679489661923;\n"
+ << "const float RADIUS = 4.5;\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 254u;\n"
+ << " uint firstTriangle = 8u * gl_LocalInvocationID.x;\n"
+ << " for (uint i = 0u; i < 8u; i++) {\n"
+ << " uint triangleId = firstTriangle + i;\n"
+ << " if (triangleId < 254u) {\n"
+ << " uint vertexId = triangleId + 2u;\n"
+ << " float angleProportion = float(vertexId - 1u) / 254.0f;\n"
+ << " float angle = PI_2 * angleProportion;\n"
+ << " float xCoord = cos(angle) * RADIUS - 1.0;\n"
+ << " float yCoord = sin(angle) * RADIUS - 1.0;\n"
+ << " gl_MeshVerticesNV[vertexId].gl_Position = vec4(xCoord, yCoord, 0.0, 1.0);\n"
+ << " gl_PrimitiveIndicesNV[triangleId * 3u + 0u] = 0u;\n"
+ << " gl_PrimitiveIndicesNV[triangleId * 3u + 1u] = triangleId + 1u;\n"
+ << " gl_PrimitiveIndicesNV[triangleId * 3u + 2u] = triangleId + 2u;\n"
+ << " triangleColor[triangleId] = vec4(0.0f, 0.0f, 1.0f, 1.0f);\n"
+ << " } else {\n"
+ << " // The last iterations of the last invocation emit the first two vertices\n"
+ << " uint vertexId = triangleId - 254u;\n"
+ << " if (vertexId == 0u) {\n"
+ << " gl_MeshVerticesNV[0u].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
+ << " } else {\n"
+ << " gl_MeshVerticesNV[1u].gl_Position = vec4(RADIUS, -1.0, 0.0, 1.0);\n"
+ << " }\n"
+ << " }\n"
+ << " }\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ void MaxTrianglesInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), m_referenceLevel);
+ }
+
+ // Large work groups with many threads.
+ class LargeWorkGroupCase : public MeshShaderMiscCase
+ {
+ public:
+ LargeWorkGroupCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+
+ static constexpr uint32_t kLocalInvocations = 32u;
+ };
+
+ class LargeWorkGroupInstance : public MeshShaderMiscInstance
+ {
+ public:
+ LargeWorkGroupInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* LargeWorkGroupCase::createInstance (Context& context) const
+ {
+ return new LargeWorkGroupInstance(context, m_params.get());
+ }
+
+ void LargeWorkGroupInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), m_referenceLevel);
+ }
+
+ void LargeWorkGroupCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ const auto useTaskShader = m_params->needsTaskShader();
+ const auto taskMultiplier = (useTaskShader ? m_params->taskCount.get() : 1u);
+
+ // Add the frag shader.
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ std::ostringstream taskData;
+ taskData
+ << "taskNV TaskData {\n"
+ << " uint parentTask[" << kLocalInvocations << "];\n"
+ << "} td;\n"
+ ;
+ const auto taskDataStr = taskData.str();
+
+ if (useTaskShader)
+ {
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=" << kLocalInvocations << ") in;\n"
+ << "\n"
+ << "out " << taskDataStr
+ << "\n"
+ << "void main () {\n"
+ << " gl_TaskCountNV = " << m_params->meshCount << ";\n"
+ << " td.parentTask[gl_LocalInvocationID.x] = gl_WorkGroupID.x;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+ }
+
+ // Needed for the code below to work.
+ DE_ASSERT(m_params->width * m_params->height == taskMultiplier * m_params->meshCount * kLocalInvocations);
+ DE_UNREF(taskMultiplier); // For release builds.
+
+ // Emit one point per framebuffer pixel. The number of jobs (kLocalInvocations in each mesh shader work group, multiplied by the
+ // number of mesh work groups emitted by each task work group) must be the same as the total framebuffer size. Calculate a job
+ // ID corresponding to the current mesh shader invocation, and assign a pixel position to it. Draw a point at that position.
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=" << kLocalInvocations << ") in;\n"
+ << "layout (points) out;\n"
+ << "layout (max_vertices=" << kLocalInvocations << ", max_primitives=" << kLocalInvocations << ") out;\n"
+ << "\n"
+ << (useTaskShader ? "in " + taskDataStr : "")
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 pointColor[];\n"
+ << "\n"
+ << "void main () {\n"
+ ;
+
+ if (useTaskShader)
+ {
+ mesh
+ << " uint parentTask = td.parentTask[0];\n"
+ << " if (td.parentTask[gl_LocalInvocationID.x] != parentTask) {\n"
+ << " return;\n"
+ << " }\n"
+ ;
+ }
+ else
+ {
+ mesh << " uint parentTask = 0;\n";
+ }
+
+ mesh
+ << " gl_PrimitiveCountNV = " << kLocalInvocations << ";\n"
+ << " uint jobId = ((parentTask * " << m_params->meshCount << ") + gl_WorkGroupID.x) * " << kLocalInvocations << " + gl_LocalInvocationID.x;\n"
+ << " uint row = jobId / " << m_params->width << ";\n"
+ << " uint col = jobId % " << m_params->width << ";\n"
+ << " float yCoord = (float(row + 0.5) / " << m_params->height << ".0) * 2.0 - 1.0;\n"
+ << " float xCoord = (float(col + 0.5) / " << m_params->width << ".0) * 2.0 - 1.0;\n"
+ << " gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_Position = vec4(xCoord, yCoord, 0.0, 1.0);\n"
+ << " gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_PointSize = 1.0;\n"
+ << " gl_PrimitiveIndicesNV[gl_LocalInvocationID.x] = gl_LocalInvocationID.x;\n"
+ << " pointColor[gl_LocalInvocationID.x] = vec4(0.0, 0.0, 1.0, 1.0);\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ // Tests that generate no primitives of a given type.
+ enum class PrimitiveType { POINTS=0, LINES, TRIANGLES };
+
+ std::string primitiveTypeName (PrimitiveType primitiveType)
+ {
+ std::string primitiveName;
+
+ switch (primitiveType)
+ {
+ case PrimitiveType::POINTS: primitiveName = "points"; break;
+ case PrimitiveType::LINES: primitiveName = "lines"; break;
+ case PrimitiveType::TRIANGLES: primitiveName = "triangles"; break;
+ default: DE_ASSERT(false); break;
+ }
+
+ return primitiveName;
+ }
+
+ struct NoPrimitivesParams : public MiscTestParams
+ {
+ PrimitiveType primitiveType;
+ };
+
+ class NoPrimitivesCase : public MeshShaderMiscCase
+ {
+ public:
+ NoPrimitivesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class NoPrimitivesInstance : public MeshShaderMiscInstance
+ {
+ public:
+ NoPrimitivesInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ void NoPrimitivesInstance::generateReferenceLevel ()
+ {
+ // No primitives: clear color.
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), m_referenceLevel);
+ }
+
+ TestInstance* NoPrimitivesCase::createInstance (Context& context) const
+ {
+ return new NoPrimitivesInstance(context, m_params.get());
+ }
+
+ void NoPrimitivesCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ const auto params = dynamic_cast<NoPrimitivesParams*>(m_params.get());
+
+ DE_ASSERT(params);
+ DE_ASSERT(!params->needsTaskShader());
+
+ const auto primitiveName = primitiveTypeName(params->primitiveType);
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=32) in;\n"
+ << "layout (" << primitiveName << ") out;\n"
+ << "layout (max_vertices=256, max_primitives=256) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 primitiveColor[];\n"
+ << "\n"
+ << "void main () {\n"
+ << " gl_PrimitiveCountNV = 0u;\n"
+ << "}\n"
+ ;
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ class NoPrimitivesExtraWritesCase : public NoPrimitivesCase
+ {
+ public:
+ NoPrimitivesExtraWritesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : NoPrimitivesCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+
+ static constexpr uint32_t kLocalInvocations = 32u;
+ };
+
+ void NoPrimitivesExtraWritesCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ const auto params = dynamic_cast<NoPrimitivesParams*>(m_params.get());
+
+ DE_ASSERT(params);
+ DE_ASSERT(m_params->needsTaskShader());
+
+ std::ostringstream taskData;
+ taskData
+ << "taskNV TaskData {\n"
+ << " uint localInvocations[" << kLocalInvocations << "];\n"
+ << "} td;\n"
+ ;
+ const auto taskDataStr = taskData.str();
+
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=" << kLocalInvocations << ") in;\n"
+ << "\n"
+ << "out " << taskDataStr
+ << "\n"
+ << "void main () {\n"
+ << " gl_TaskCountNV = " << params->meshCount << ";\n"
+ << " td.localInvocations[gl_LocalInvocationID.x] = gl_LocalInvocationID.x;\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+
+ const auto primitiveName = primitiveTypeName(params->primitiveType);
+
+ // Otherwise the shader would be illegal.
+ DE_ASSERT(kLocalInvocations > 2u);
+
+ uint32_t maxPrimitives = 0u;
+ switch (params->primitiveType)
+ {
+ case PrimitiveType::POINTS: maxPrimitives = kLocalInvocations - 0u; break;
+ case PrimitiveType::LINES: maxPrimitives = kLocalInvocations - 1u; break;
+ case PrimitiveType::TRIANGLES: maxPrimitives = kLocalInvocations - 2u; break;
+ default: DE_ASSERT(false); break;
+ }
+
+ const std::string pointSizeDecl = ((params->primitiveType == PrimitiveType::POINTS)
+ ? " gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_PointSize = 1.0;\n"
+ : "");
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=" << kLocalInvocations << ") in;\n"
+ << "layout (" << primitiveName << ") out;\n"
+ << "layout (max_vertices=" << kLocalInvocations << ", max_primitives=" << maxPrimitives << ") out;\n"
+ << "\n"
+ << "in " << taskDataStr
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 primitiveColor[];\n"
+ << "\n"
+ << "shared uint sumOfIds;\n"
+ << "\n"
+ << "const float PI_2 = 1.57079632679489661923;\n"
+ << "const float RADIUS = 1.0f;\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " sumOfIds = 0u;\n"
+ << " barrier();\n"
+ << " atomicAdd(sumOfIds, td.localInvocations[gl_LocalInvocationID.x]);\n"
+ << " barrier();\n"
+ << " // This should dynamically give 0\n"
+ << " gl_PrimitiveCountNV = sumOfIds - (" << kLocalInvocations * (kLocalInvocations - 1u) / 2u << ");\n"
+ << "\n"
+ << " // Emit points and primitives to the arrays in any case\n"
+ << " if (gl_LocalInvocationID.x > 0u) {\n"
+ << " float proportion = (float(gl_LocalInvocationID.x - 1u) + 0.5f) / float(" << kLocalInvocations << " - 1u);\n"
+ << " float angle = PI_2 * proportion;\n"
+ << " float xCoord = cos(angle) * RADIUS - 1.0;\n"
+ << " float yCoord = sin(angle) * RADIUS - 1.0;\n"
+ << " gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_Position = vec4(xCoord, yCoord, 0.0, 1.0);\n"
+ << pointSizeDecl
+ << " } else {\n"
+ << " gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ << pointSizeDecl
+ << " }\n"
+ << " uint primitiveId = max(gl_LocalInvocationID.x, " << (maxPrimitives - 1u) << ");\n"
+ << " primitiveColor[primitiveId] = vec4(0.0, 0.0, 1.0, 1.0);\n"
+ ;
+
+ if (params->primitiveType == PrimitiveType::POINTS)
+ {
+ mesh
+ << " gl_PrimitiveIndicesNV[primitiveId] = primitiveId;\n"
+ ;
+ }
+ else if (params->primitiveType == PrimitiveType::LINES)
+ {
+ mesh
+ << " gl_PrimitiveIndicesNV[primitiveId * 2u + 0u] = primitiveId + 0u;\n"
+ << " gl_PrimitiveIndicesNV[primitiveId * 2u + 1u] = primitiveId + 1u;\n"
+ ;
+ }
+ else if (params->primitiveType == PrimitiveType::TRIANGLES)
+ {
+ mesh
+ << " gl_PrimitiveIndicesNV[primitiveId * 3u + 0u] = 0u;\n"
+ << " gl_PrimitiveIndicesNV[primitiveId * 3u + 1u] = primitiveId + 1u;\n"
+ << " gl_PrimitiveIndicesNV[primitiveId * 3u + 2u] = primitiveId + 3u;\n"
+ ;
+ }
+ else
+ DE_ASSERT(false);
+
+ mesh
+ << "}\n"
+ ;
+
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+
+ MeshShaderMiscCase::initPrograms(programCollection);
+ }
+
+ // Case testing barrier().
+ class SimpleBarrierCase : public MeshShaderMiscCase
+ {
+ public:
+ SimpleBarrierCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+
+ static constexpr uint32_t kLocalInvocations = 32u;
+ };
+
+ class SimpleBarrierInstance : public MeshShaderMiscInstance
+ {
+ public:
+ SimpleBarrierInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ };
+
+ TestInstance* SimpleBarrierCase::createInstance (Context& context) const
+ {
+ return new SimpleBarrierInstance(context, m_params.get());
+ }
+
+ void SimpleBarrierInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), m_referenceLevel);
+ }
+
+ void SimpleBarrierCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ // Generate frag shader.
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ DE_ASSERT(m_params->meshCount == 1u);
+ DE_ASSERT(m_params->width == 1u && m_params->height == 1u);
+
+ std::ostringstream meshPrimData;
+ meshPrimData
+ << "gl_PrimitiveCountNV = 1u;\n"
+ << "gl_MeshVerticesNV[0].gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ << "gl_MeshVerticesNV[0].gl_PointSize = 1.0;\n"
+ << "primitiveColor[0] = vec4(0.0, 0.0, 1.0, 1.0);\n"
+ << "gl_PrimitiveIndicesNV[0] = 0;\n"
+ ;
+ const std::string meshPrimStr = meshPrimData.str();
+
+ const std::string taskOK = "gl_TaskCountNV = 1u;\n";
+ const std::string taskFAIL = "gl_TaskCountNV = 0u;\n";
+
+ const std::string meshOK = meshPrimStr;
+ const std::string meshFAIL = "gl_PrimitiveCountNV = 0u;\n";
+
+ const std::string okStatement = (m_params->needsTaskShader() ? taskOK : meshOK);
+ const std::string failStatement = (m_params->needsTaskShader() ? taskFAIL : meshFAIL);
+
+ const std::string sharedDecl = "shared uint counter;\n\n";
+ std::ostringstream verification;
+ verification
+ << "counter = 0;\n"
+ << "barrier();\n"
+ << "atomicAdd(counter, 1u);\n"
+ << "barrier();\n"
+ << "if (gl_LocalInvocationID.x == 0u) {\n"
+ << " if (counter == " << kLocalInvocations << ") {\n"
+ << "\n"
+ << okStatement
+ << "\n"
+ << " } else {\n"
+ << "\n"
+ << failStatement
+ << "\n"
+ << " }\n"
+ << "}\n"
+ ;
+
+ // The mesh shader is very similar in both cases, so we use a template.
+ std::ostringstream meshTemplateStr;
+ meshTemplateStr
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=${LOCAL_SIZE}) in;\n"
+ << "layout (points) out;\n"
+ << "layout (max_vertices=1, max_primitives=1) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 primitiveColor[];\n"
+ << "\n"
+ << "${GLOBALS:opt}"
+ << "void main ()\n"
+ << "{\n"
+ << "${BODY}"
+ << "}\n"
+ ;
+ const tcu::StringTemplate meshTemplate = meshTemplateStr.str();
+
+ if (m_params->needsTaskShader())
+ {
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=" << kLocalInvocations << ") in;\n"
+ << "\n"
+ << sharedDecl
+ << "void main ()\n"
+ << "{\n"
+ << verification.str()
+ << "}\n"
+ ;
+
+ std::map<std::string, std::string> replacements;
+ replacements["LOCAL_SIZE"] = "1";
+ replacements["BODY"] = meshPrimStr;
+
+ const auto meshStr = meshTemplate.specialize(replacements);
+
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+ programCollection.glslSources.add("mesh") << glu::MeshSource(meshStr);
+ }
+ else
+ {
+ std::map<std::string, std::string> replacements;
+ replacements["LOCAL_SIZE"] = std::to_string(kLocalInvocations);
+ replacements["BODY"] = verification.str();
+ replacements["GLOBALS"] = sharedDecl;
+
+ const auto meshStr = meshTemplate.specialize(replacements);
+
+ programCollection.glslSources.add("mesh") << glu::MeshSource(meshStr);
+ }
+ }
+
+ // Case testing memoryBarrierShared() and groupMemoryBarrier().
+ enum class MemoryBarrierType { SHARED = 0, GROUP };
+
+ struct MemoryBarrierParams : public MiscTestParams
+ {
+ MemoryBarrierType memBarrierType;
+
+ std::string glslFunc () const
+ {
+ std::string funcName;
+
+ switch (memBarrierType)
+ {
+ case MemoryBarrierType::SHARED: funcName = "memoryBarrierShared"; break;
+ case MemoryBarrierType::GROUP: funcName = "groupMemoryBarrier"; break;
+ default: DE_ASSERT(false); break;
+ }
+
+ return funcName;
+ }
+
+ };
+
+ class MemoryBarrierCase : public MeshShaderMiscCase
+ {
+ public:
+ MemoryBarrierCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+
+ static constexpr uint32_t kLocalInvocations = 2u;
+ };
+
+ class MemoryBarrierInstance : public MeshShaderMiscInstance
+ {
+ public:
+ MemoryBarrierInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ bool verifyResult (const tcu::ConstPixelBufferAccess& resultAccess) const override;
+
+ protected:
+ // Allow two possible outcomes.
+ std::unique_ptr<tcu::TextureLevel> m_referenceLevel2;
+ };
+
+ TestInstance* MemoryBarrierCase::createInstance (Context& context) const
+ {
+ return new MemoryBarrierInstance(context, m_params.get());
+ }
+
+ void MemoryBarrierInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), m_referenceLevel);
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), m_referenceLevel2);
+ }
+
+ bool MemoryBarrierInstance::verifyResult (const tcu::ConstPixelBufferAccess& resultAccess) const
+ {
+ // Any of the two results is considered valid.
- log << Message << "Comparing result with reference " << i << "..." << EndMessage;
+ // Clarify what we are checking in the logs; otherwise, they could be confusing.
+ auto& log = m_context.getTestContext().getLog();
+ const std::vector<tcu::TextureLevel*> levels = { m_referenceLevel.get(), m_referenceLevel2.get() };
+
+ bool good = false;
+ for (size_t i = 0; i < levels.size(); ++i)
+ {
- log << Message << "Match! The test has passed" << EndMessage;
++ log << tcu::TestLog::Message << "Comparing result with reference " << i << "..." << tcu::TestLog::EndMessage;
+ const auto success = MeshShaderMiscInstance::verifyResult(resultAccess, *levels[i]);
+ if (success)
+ {
++ log << tcu::TestLog::Message << "Match! The test has passed" << tcu::TestLog::EndMessage;
+ good = true;
+ break;
+ }
+ }
+
+ return good;
+ }
+
+ void MemoryBarrierCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ const auto params = dynamic_cast<MemoryBarrierParams*>(m_params.get());
+ DE_ASSERT(params);
+
+ // Generate frag shader.
+ MeshShaderMiscCase::initPrograms(programCollection);
+
+ DE_ASSERT(params->meshCount == 1u);
+ DE_ASSERT(params->width == 1u && params->height == 1u);
+
+ const bool taskShader = params->needsTaskShader();
+
+ const std::string taskDataDecl = "taskNV TaskData { float blue; } td;\n\n";
+ const std::string inTaskData = "in " + taskDataDecl;
+ const std::string outTaskData = "out " + taskDataDecl;
+ const auto barrierFunc = params->glslFunc();
+
+ std::ostringstream meshPrimData;
+ meshPrimData
+ << "gl_PrimitiveCountNV = 1u;\n"
+ << "gl_MeshVerticesNV[0].gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ << "gl_MeshVerticesNV[0].gl_PointSize = 1.0;\n"
+ << "primitiveColor[0] = vec4(0.0, 0.0, " << (taskShader ? "td.blue" : "float(iterations % 2u)") << ", 1.0);\n"
+ << "gl_PrimitiveIndicesNV[0] = 0;\n"
+ ;
+ const std::string meshPrimStr = meshPrimData.str();
+
+ const std::string taskAction = "gl_TaskCountNV = 1u;\ntd.blue = float(iterations % 2u);\n";
+ const std::string meshAction = meshPrimStr;
+ const std::string action = (taskShader ? taskAction : meshAction);
+
+ const std::string sharedDecl = "shared uint flags[2];\n\n";
+ std::ostringstream verification;
+ verification
+ << "flags[gl_LocalInvocationID.x] = 0u;\n"
+ << "barrier();\n"
+ << "flags[gl_LocalInvocationID.x] = 1u;\n"
+ << barrierFunc << "();\n"
+ << "uint otherInvocation = 1u - gl_LocalInvocationID.x;\n"
+ << "uint iterations = 0u;\n"
+ << "while (flags[otherInvocation] != 1u) {\n"
+ << " iterations++;\n"
+ << "}\n"
+ << "if (gl_LocalInvocationID.x == 0u) {\n"
+ << "\n"
+ << action
+ << "\n"
+ << "}\n"
+ ;
+
+ // The mesh shader is very similar in both cases, so we use a template.
+ std::ostringstream meshTemplateStr;
+ meshTemplateStr
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=${LOCAL_SIZE}) in;\n"
+ << "layout (points) out;\n"
+ << "layout (max_vertices=1, max_primitives=1) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 primitiveColor[];\n"
+ << "\n"
+ << "${GLOBALS}"
+ << "void main ()\n"
+ << "{\n"
+ << "${BODY}"
+ << "}\n"
+ ;
+ const tcu::StringTemplate meshTemplate = meshTemplateStr.str();
+
+ if (params->needsTaskShader())
+ {
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=" << kLocalInvocations << ") in;\n"
+ << "\n"
+ << sharedDecl
+ << outTaskData
+ << "void main ()\n"
+ << "{\n"
+ << verification.str()
+ << "}\n"
+ ;
+
+ std::map<std::string, std::string> replacements;
+ replacements["LOCAL_SIZE"] = "1";
+ replacements["BODY"] = meshPrimStr;
+ replacements["GLOBALS"] = inTaskData;
+
+ const auto meshStr = meshTemplate.specialize(replacements);
+
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+ programCollection.glslSources.add("mesh") << glu::MeshSource(meshStr);
+ }
+ else
+ {
+ std::map<std::string, std::string> replacements;
+ replacements["LOCAL_SIZE"] = std::to_string(kLocalInvocations);
+ replacements["BODY"] = verification.str();
+ replacements["GLOBALS"] = sharedDecl;
+
+ const auto meshStr = meshTemplate.specialize(replacements);
+
+ programCollection.glslSources.add("mesh") << glu::MeshSource(meshStr);
+ }
+ }
+
+ class CustomAttributesCase : public MeshShaderMiscCase
+ {
+ public:
+ CustomAttributesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase(testCtx, name, description, std::move(params)) {}
+ virtual ~CustomAttributesCase (void) {}
+
+ TestInstance* createInstance (Context& context) const override;
+ void checkSupport (Context& context) const override;
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ };
+
+ class CustomAttributesInstance : public MeshShaderMiscInstance
+ {
+ public:
+ CustomAttributesInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance(context, params) {}
+ virtual ~CustomAttributesInstance (void) {}
+
+ void generateReferenceLevel () override;
+ tcu::TestStatus iterate (void) override;
+ };
+
+ TestInstance* CustomAttributesCase::createInstance (Context& context) const
+ {
+ return new CustomAttributesInstance(context, m_params.get());
+ }
+
+ void CustomAttributesCase::checkSupport (Context& context) const
+ {
+ MeshShaderMiscCase::checkSupport(context);
+
+ context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
+ context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
+ }
+
+ void CustomAttributesCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ std::ostringstream frag;
+ frag
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (location=0) in vec4 customAttribute1;\n"
+ << "layout (location=1) in flat float customAttribute2;\n"
+ << "layout (location=2) in flat int customAttribute3;\n"
+ << "\n"
+ << "layout (location=3) in perprimitiveNV flat uvec4 customAttribute4;\n"
+ << "layout (location=4) in perprimitiveNV float customAttribute5;\n"
+ << "\n"
+ << "layout (location=0) out vec4 outColor;\n"
+ << "\n"
+ << "void main ()\n"
+ << "{\n"
+ << " bool goodPrimitiveID = (gl_PrimitiveID == 1000 || gl_PrimitiveID == 1001);\n"
+ << " bool goodViewportIndex = (gl_ViewportIndex == 1);\n"
+ << " bool goodCustom1 = (customAttribute1.x >= 0.25 && customAttribute1.x <= 0.5 &&\n"
+ << " customAttribute1.y >= 0.5 && customAttribute1.y <= 1.0 &&\n"
+ << " customAttribute1.z >= 10.0 && customAttribute1.z <= 20.0 &&\n"
+ << " customAttribute1.w == 3.0);\n"
+ << " bool goodCustom2 = (customAttribute2 == 1.0 || customAttribute2 == 2.0);\n"
+ << " bool goodCustom3 = (customAttribute3 == 3 || customAttribute3 == 4);\n"
+ << " bool goodCustom4 = ((gl_PrimitiveID == 1000 && customAttribute4 == uvec4(100, 101, 102, 103)) ||\n"
+ << " (gl_PrimitiveID == 1001 && customAttribute4 == uvec4(200, 201, 202, 203)));\n"
+ << " bool goodCustom5 = ((gl_PrimitiveID == 1000 && customAttribute5 == 6.0) ||\n"
+ << " (gl_PrimitiveID == 1001 && customAttribute5 == 7.0));\n"
+ << " \n"
+ << " if (goodPrimitiveID && goodViewportIndex && goodCustom1 && goodCustom2 && goodCustom3 && goodCustom4 && goodCustom5) {\n"
+ << " outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
+ << " } else {\n"
+ << " outColor = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ << " }\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
+
+ std::ostringstream pvdDataDeclStream;
+ pvdDataDeclStream
+ << " vec4 positions[4];\n"
+ << " float pointSizes[4];\n"
+ << " float clipDistances[4];\n"
+ << " vec4 custom1[4];\n"
+ << " float custom2[4];\n"
+ << " int custom3[4];\n"
+ ;
+ const auto pvdDataDecl = pvdDataDeclStream.str();
+
+ std::ostringstream ppdDataDeclStream;
+ ppdDataDeclStream
+ << " int primitiveIds[2];\n"
+ << " int viewportIndices[2];\n"
+ << " uvec4 custom4[2];\n"
+ << " float custom5[2];\n"
+ ;
+ const auto ppdDataDecl = ppdDataDeclStream.str();
+
+ std::ostringstream bindingsDeclStream;
+ bindingsDeclStream
+ << "layout (set=0, binding=0, std430) buffer PerVertexData {\n"
+ << pvdDataDecl
+ << "} pvd;\n"
+ << "layout (set=0, binding=1) uniform PerPrimitiveData {\n"
+ << ppdDataDecl
+ << "} ppd;\n"
+ << "\n"
+ ;
+ const auto bindingsDecl = bindingsDeclStream.str();
+
+ std::ostringstream taskDataStream;
+ taskDataStream
+ << "taskNV TaskData {\n"
+ << pvdDataDecl
+ << ppdDataDecl
+ << "} td;\n"
+ << "\n"
+ ;
+ const auto taskDataDecl = taskDataStream.str();
+
+ const auto taskShader = m_params->needsTaskShader();
+
+ const auto meshPvdPrefix = (taskShader ? "td" : "pvd");
+ const auto meshPpdPrefix = (taskShader ? "td" : "ppd");
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout (local_size_x=1) in;\n"
+ << "layout (max_primitives=2, max_vertices=4) out;\n"
+ << "layout (triangles) out;\n"
+ << "\n"
+ << "out gl_MeshPerVertexNV {\n"
+ << " vec4 gl_Position;\n"
+ << " float gl_PointSize;\n"
+ << " float gl_ClipDistance[1];\n"
+ << "} gl_MeshVerticesNV[];\n"
+ << "\n"
+ << "layout (location=0) out vec4 customAttribute1[];\n"
+ << "layout (location=1) out flat float customAttribute2[];\n"
+ << "layout (location=2) out int customAttribute3[];\n"
+ << "\n"
+ << "layout (location=3) out perprimitiveNV uvec4 customAttribute4[];\n"
+ << "layout (location=4) out perprimitiveNV float customAttribute5[];\n"
+ << "\n"
+ << "out perprimitiveNV gl_MeshPerPrimitiveNV {\n"
+ << " int gl_PrimitiveID;\n"
+ << " int gl_ViewportIndex;\n"
+ << "} gl_MeshPrimitivesNV[];\n"
+ << "\n"
+ << (taskShader ? "in " + taskDataDecl : bindingsDecl)
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 2u;\n"
+ << "\n"
+ << " gl_MeshVerticesNV[0].gl_Position = " << meshPvdPrefix << ".positions[0]; //vec4(-1.0, -1.0, 0.0, 1.0)\n"
+ << " gl_MeshVerticesNV[1].gl_Position = " << meshPvdPrefix << ".positions[1]; //vec4( 1.0, -1.0, 0.0, 1.0)\n"
+ << " gl_MeshVerticesNV[2].gl_Position = " << meshPvdPrefix << ".positions[2]; //vec4(-1.0, 1.0, 0.0, 1.0)\n"
+ << " gl_MeshVerticesNV[3].gl_Position = " << meshPvdPrefix << ".positions[3]; //vec4( 1.0, 1.0, 0.0, 1.0)\n"
+ << "\n"
+ << " gl_MeshVerticesNV[0].gl_PointSize = " << meshPvdPrefix << ".pointSizes[0]; //1.0\n"
+ << " gl_MeshVerticesNV[1].gl_PointSize = " << meshPvdPrefix << ".pointSizes[1]; //1.0\n"
+ << " gl_MeshVerticesNV[2].gl_PointSize = " << meshPvdPrefix << ".pointSizes[2]; //1.0\n"
+ << " gl_MeshVerticesNV[3].gl_PointSize = " << meshPvdPrefix << ".pointSizes[3]; //1.0\n"
+ << "\n"
+ << " // Remove geometry on the right side.\n"
+ << " gl_MeshVerticesNV[0].gl_ClipDistance[0] = " << meshPvdPrefix << ".clipDistances[0]; // 1.0\n"
+ << " gl_MeshVerticesNV[1].gl_ClipDistance[0] = " << meshPvdPrefix << ".clipDistances[1]; //-1.0\n"
+ << " gl_MeshVerticesNV[2].gl_ClipDistance[0] = " << meshPvdPrefix << ".clipDistances[2]; // 1.0\n"
+ << " gl_MeshVerticesNV[3].gl_ClipDistance[0] = " << meshPvdPrefix << ".clipDistances[3]; //-1.0\n"
+ << " \n"
+ << " gl_PrimitiveIndicesNV[0] = 0;\n"
+ << " gl_PrimitiveIndicesNV[1] = 2;\n"
+ << " gl_PrimitiveIndicesNV[2] = 1;\n"
+ << "\n"
+ << " gl_PrimitiveIndicesNV[3] = 2;\n"
+ << " gl_PrimitiveIndicesNV[4] = 3;\n"
+ << " gl_PrimitiveIndicesNV[5] = 1;\n"
+ << "\n"
+ << " gl_MeshPrimitivesNV[0].gl_PrimitiveID = " << meshPpdPrefix << ".primitiveIds[0]; //1000\n"
+ << " gl_MeshPrimitivesNV[1].gl_PrimitiveID = " << meshPpdPrefix << ".primitiveIds[1]; //1001\n"
+ << "\n"
+ << " gl_MeshPrimitivesNV[0].gl_ViewportIndex = " << meshPpdPrefix << ".viewportIndices[0]; //1\n"
+ << " gl_MeshPrimitivesNV[1].gl_ViewportIndex = " << meshPpdPrefix << ".viewportIndices[1]; //1\n"
+ << "\n"
+ << " // Custom per-vertex attributes\n"
+ << " customAttribute1[0] = " << meshPvdPrefix << ".custom1[0]; //vec4(0.25, 0.5, 10.0, 3.0)\n"
+ << " customAttribute1[1] = " << meshPvdPrefix << ".custom1[1]; //vec4(0.25, 1.0, 20.0, 3.0)\n"
+ << " customAttribute1[2] = " << meshPvdPrefix << ".custom1[2]; //vec4( 0.5, 0.5, 20.0, 3.0)\n"
+ << " customAttribute1[3] = " << meshPvdPrefix << ".custom1[3]; //vec4( 0.5, 1.0, 10.0, 3.0)\n"
+ << "\n"
+ << " customAttribute2[0] = " << meshPvdPrefix << ".custom2[0]; //1.0f\n"
+ << " customAttribute2[1] = " << meshPvdPrefix << ".custom2[1]; //1.0f\n"
+ << " customAttribute2[2] = " << meshPvdPrefix << ".custom2[2]; //2.0f\n"
+ << " customAttribute2[3] = " << meshPvdPrefix << ".custom2[3]; //2.0f\n"
+ << "\n"
+ << " customAttribute3[0] = " << meshPvdPrefix << ".custom3[0]; //3\n"
+ << " customAttribute3[1] = " << meshPvdPrefix << ".custom3[1]; //3\n"
+ << " customAttribute3[2] = " << meshPvdPrefix << ".custom3[2]; //4\n"
+ << " customAttribute3[3] = " << meshPvdPrefix << ".custom3[3]; //4\n"
+ << "\n"
+ << " // Custom per-primitive attributes.\n"
+ << " customAttribute4[0] = " << meshPpdPrefix << ".custom4[0]; //uvec4(100, 101, 102, 103)\n"
+ << " customAttribute4[1] = " << meshPpdPrefix << ".custom4[1]; //uvec4(200, 201, 202, 203)\n"
+ << "\n"
+ << " customAttribute5[0] = " << meshPpdPrefix << ".custom5[0]; //6.0\n"
+ << " customAttribute5[1] = " << meshPpdPrefix << ".custom5[1]; //7.0\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+
+ if (taskShader)
+ {
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "out " << taskDataDecl
+ << bindingsDecl
+ << "void main ()\n"
+ << "{\n"
+ << " gl_TaskCountNV = " << m_params->meshCount << ";\n"
+ << "\n"
+ << " td.positions[0] = pvd.positions[0];\n"
+ << " td.positions[1] = pvd.positions[1];\n"
+ << " td.positions[2] = pvd.positions[2];\n"
+ << " td.positions[3] = pvd.positions[3];\n"
+ << "\n"
+ << " td.pointSizes[0] = pvd.pointSizes[0];\n"
+ << " td.pointSizes[1] = pvd.pointSizes[1];\n"
+ << " td.pointSizes[2] = pvd.pointSizes[2];\n"
+ << " td.pointSizes[3] = pvd.pointSizes[3];\n"
+ << "\n"
+ << " td.clipDistances[0] = pvd.clipDistances[0];\n"
+ << " td.clipDistances[1] = pvd.clipDistances[1];\n"
+ << " td.clipDistances[2] = pvd.clipDistances[2];\n"
+ << " td.clipDistances[3] = pvd.clipDistances[3];\n"
+ << "\n"
+ << " td.custom1[0] = pvd.custom1[0];\n"
+ << " td.custom1[1] = pvd.custom1[1];\n"
+ << " td.custom1[2] = pvd.custom1[2];\n"
+ << " td.custom1[3] = pvd.custom1[3];\n"
+ << "\n"
+ << " td.custom2[0] = pvd.custom2[0];\n"
+ << " td.custom2[1] = pvd.custom2[1];\n"
+ << " td.custom2[2] = pvd.custom2[2];\n"
+ << " td.custom2[3] = pvd.custom2[3];\n"
+ << "\n"
+ << " td.custom3[0] = pvd.custom3[0];\n"
+ << " td.custom3[1] = pvd.custom3[1];\n"
+ << " td.custom3[2] = pvd.custom3[2];\n"
+ << " td.custom3[3] = pvd.custom3[3];\n"
+ << "\n"
+ << " td.primitiveIds[0] = ppd.primitiveIds[0];\n"
+ << " td.primitiveIds[1] = ppd.primitiveIds[1];\n"
+ << "\n"
+ << " td.viewportIndices[0] = ppd.viewportIndices[0];\n"
+ << " td.viewportIndices[1] = ppd.viewportIndices[1];\n"
+ << "\n"
+ << " td.custom4[0] = ppd.custom4[0];\n"
+ << " td.custom4[1] = ppd.custom4[1];\n"
+ << "\n"
+ << " td.custom5[0] = ppd.custom5[0];\n"
+ << " td.custom5[1] = ppd.custom5[1];\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+ }
+ }
+
+ void CustomAttributesInstance::generateReferenceLevel ()
+ {
+ const auto format = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(format);
+
+ const auto iWidth = static_cast<int>(m_params->width);
+ const auto iHeight = static_cast<int>(m_params->height);
+
+ const auto halfWidth = iWidth / 2;
+ const auto halfHeight = iHeight / 2;
+
+ m_referenceLevel.reset(new tcu::TextureLevel(tcuFormat, iWidth, iHeight));
+
+ const auto access = m_referenceLevel->getAccess();
+ const auto clearColor = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
+ const auto blueColor = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
+
+ tcu::clear(access, clearColor);
+
+ // Fill the top left quarter.
+ for (int y = 0; y < halfWidth; ++y)
+ for (int x = 0; x < halfHeight; ++x)
+ {
+ access.setPixel(blueColor, x, y);
+ }
+ }
+
+ tcu::TestStatus CustomAttributesInstance::iterate ()
+ {
+ struct PerVertexData
+ {
+ tcu::Vec4 positions[4];
+ float pointSizes[4];
+ float clipDistances[4];
+ tcu::Vec4 custom1[4];
+ float custom2[4];
+ int32_t custom3[4];
+ };
+
+ struct PerPrimitiveData
+ {
+ // Note some of these are declared as vectors to match the std140 layout.
+ tcu::IVec4 primitiveIds[2];
+ tcu::IVec4 viewportIndices[2];
+ tcu::UVec4 custom4[2];
+ tcu::Vec4 custom5[2];
+ };
+
+ const auto& vkd = m_context.getDeviceInterface();
+ const auto device = m_context.getDevice();
+ auto& alloc = m_context.getDefaultAllocator();
+ const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
+ const auto queue = m_context.getUniversalQueue();
+
+ const auto imageFormat = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(imageFormat);
+ const auto imageExtent = makeExtent3D(m_params->width, m_params->height, 1u);
+ const auto imageUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+
+ const auto& binaries = m_context.getBinaryCollection();
+ const auto hasTask = binaries.contains("task");
+ const auto bufStages = (hasTask ? VK_SHADER_STAGE_TASK_BIT_NV : VK_SHADER_STAGE_MESH_BIT_NV);
+
+ const VkImageCreateInfo colorBufferInfo =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ imageFormat, // VkFormat format;
+ imageExtent, // VkExtent3D extent;
+ 1u, // uint32_t mipLevels;
+ 1u, // uint32_t arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ imageUsage, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // uint32_t queueFamilyIndexCount;
+ nullptr, // const uint32_t* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+
+ // Create color image and view.
+ ImageWithMemory colorImage (vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
+ const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
+ const auto colorView = makeImageView(vkd, device, colorImage.get(), VK_IMAGE_VIEW_TYPE_2D, imageFormat, colorSRR);
+
+ // Create a memory buffer for verification.
+ const auto verificationBufferSize = static_cast<VkDeviceSize>(imageExtent.width * imageExtent.height * tcu::getPixelSize(tcuFormat));
+ const auto verificationBufferUsage = (VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ const auto verificationBufferInfo = makeBufferCreateInfo(verificationBufferSize, verificationBufferUsage);
+
+ BufferWithMemory verificationBuffer (vkd, device, alloc, verificationBufferInfo, MemoryRequirement::HostVisible);
+ auto& verificationBufferAlloc = verificationBuffer.getAllocation();
+ void* verificationBufferData = verificationBufferAlloc.getHostPtr();
+
+ // This needs to match what the fragment shader will expect.
+ const PerVertexData perVertexData =
+ {
+ // tcu::Vec4 positions[4];
+ {
+ tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
+ tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
+ tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
+ tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
+ },
+ // float pointSizes[4];
+ { 1.0f, 1.0f, 1.0f, 1.0f, },
+ // float clipDistances[4];
+ {
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ -1.0f,
+ },
+ // tcu::Vec4 custom1[4];
+ {
+ tcu::Vec4(0.25, 0.5, 10.0, 3.0),
+ tcu::Vec4(0.25, 1.0, 20.0, 3.0),
+ tcu::Vec4( 0.5, 0.5, 20.0, 3.0),
+ tcu::Vec4( 0.5, 1.0, 10.0, 3.0),
+ },
+ // float custom2[4];
+ { 1.0f, 1.0f, 2.0f, 2.0f, },
+ // int32_t custom3[4];
+ { 3, 3, 4, 4 },
+ };
+
+ // This needs to match what the fragment shader will expect. Reminder: some of these are declared as gvec4 to match the std140
+ // layout, but only the first component is actually used.
+ const PerPrimitiveData perPrimitiveData =
+ {
+ // int primitiveIds[2];
+ {
+ tcu::IVec4(1000, 0, 0, 0),
+ tcu::IVec4(1001, 0, 0, 0),
+ },
+ // int viewportIndices[2];
+ {
+ tcu::IVec4(1, 0, 0, 0),
+ tcu::IVec4(1, 0, 0, 0),
+ },
+ // uvec4 custom4[2];
+ {
+ tcu::UVec4(100u, 101u, 102u, 103u),
+ tcu::UVec4(200u, 201u, 202u, 203u),
+ },
+ // float custom5[2];
+ {
+ tcu::Vec4(6.0f, 0.0f, 0.0f, 0.0f),
+ tcu::Vec4(7.0f, 0.0f, 0.0f, 0.0f),
+ },
+ };
+
+ // Create and fill buffers with this data.
+ const auto pvdSize = static_cast<VkDeviceSize>(sizeof(perVertexData));
+ const auto pvdInfo = makeBufferCreateInfo(pvdSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+ BufferWithMemory pvdData (vkd, device, alloc, pvdInfo, MemoryRequirement::HostVisible);
+ auto& pvdAlloc = pvdData.getAllocation();
+ void* pvdPtr = pvdAlloc.getHostPtr();
+
+ const auto ppdSize = static_cast<VkDeviceSize>(sizeof(perPrimitiveData));
+ const auto ppdInfo = makeBufferCreateInfo(ppdSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
+ BufferWithMemory ppdData (vkd, device, alloc, ppdInfo, MemoryRequirement::HostVisible);
+ auto& ppdAlloc = ppdData.getAllocation();
+ void* ppdPtr = ppdAlloc.getHostPtr();
+
+ deMemcpy(pvdPtr, &perVertexData, sizeof(perVertexData));
+ deMemcpy(ppdPtr, &perPrimitiveData, sizeof(perPrimitiveData));
+
+ flushAlloc(vkd, device, pvdAlloc);
+ flushAlloc(vkd, device, ppdAlloc);
+
+ // Descriptor set layout.
+ DescriptorSetLayoutBuilder setLayoutBuilder;
+ setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, bufStages);
+ setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bufStages);
+ const auto setLayout = setLayoutBuilder.build(vkd, device);
+
+ // Create and update descriptor set.
+ DescriptorPoolBuilder descriptorPoolBuilder;
+ descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+ descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+ const auto descriptorPool = descriptorPoolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+ const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
+
+ DescriptorSetUpdateBuilder updateBuilder;
+ const auto storageBufferInfo = makeDescriptorBufferInfo(pvdData.get(), 0ull, pvdSize);
+ const auto uniformBufferInfo = makeDescriptorBufferInfo(ppdData.get(), 0ull, ppdSize);
+ updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &storageBufferInfo);
+ updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &uniformBufferInfo);
+ updateBuilder.update(vkd, device);
+
+ // Pipeline layout.
+ const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
+
+ // Shader modules.
+ const auto meshShader = createShaderModule(vkd, device, binaries.get("mesh"));
+ const auto fragShader = createShaderModule(vkd, device, binaries.get("frag"));
+
+ Move<VkShaderModule> taskShader;
+ if (hasTask)
+ taskShader = createShaderModule(vkd, device, binaries.get("task"));
+
+ // Render pass.
+ const auto renderPass = makeRenderPass(vkd, device, imageFormat);
+
+ // Framebuffer.
+ const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorView.get(), imageExtent.width, imageExtent.height);
+
+ // Viewport and scissor.
+ const auto topHalf = makeViewport(imageExtent.width, imageExtent.height / 2u);
+ const std::vector<VkViewport> viewports { makeViewport(imageExtent), topHalf };
+ const std::vector<VkRect2D> scissors (2u, makeRect2D(imageExtent));
+
+ const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
+ taskShader.get(), meshShader.get(), fragShader.get(),
+ renderPass.get(), viewports, scissors);
+
+ // Command pool and buffer.
+ const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
+ const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+ const auto cmdBuffer = cmdBufferPtr.get();
+
+ beginCommandBuffer(vkd, cmdBuffer);
+
+ // Run pipeline.
+ const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 0.0f);
+ beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor);
+ vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
+ vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
+ vkd.cmdDrawMeshTasksNV(cmdBuffer, m_params->drawCount(), 0u);
+ endRenderPass(vkd, cmdBuffer);
+
+ // Copy color buffer to verification buffer.
+ const auto colorAccess = (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT);
+ const auto transferRead = VK_ACCESS_TRANSFER_READ_BIT;
+ const auto transferWrite = VK_ACCESS_TRANSFER_WRITE_BIT;
+ const auto hostRead = VK_ACCESS_HOST_READ_BIT;
+
+ const auto preCopyBarrier = makeImageMemoryBarrier(colorAccess, transferRead, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImage.get(), colorSRR);
+ const auto postCopyBarrier = makeMemoryBarrier(transferWrite, hostRead);
+ const auto copyRegion = makeBufferImageCopy(imageExtent, colorSRL);
+
+ vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preCopyBarrier);
+ vkd.cmdCopyImageToBuffer(cmdBuffer, colorImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verificationBuffer.get(), 1u, ©Region);
+ vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postCopyBarrier, 0u, nullptr, 0u, nullptr);
+
+ endCommandBuffer(vkd, cmdBuffer);
+ submitCommandsAndWait(vkd, device, queue, cmdBuffer);
+
+ // Generate reference image and compare results.
+ const tcu::IVec3 iExtent (static_cast<int>(imageExtent.width), static_cast<int>(imageExtent.height), 1);
+ const tcu::ConstPixelBufferAccess verificationAccess (tcuFormat, iExtent, verificationBufferData);
+
+ generateReferenceLevel();
+ invalidateAlloc(vkd, device, verificationBufferAlloc);
+ if (!verifyResult(verificationAccess))
+ TCU_FAIL("Result does not match reference; check log for details");
+
+ return tcu::TestStatus::pass("Pass");
+ }
+
+ // Tests that use push constants in the new stages.
+ class PushConstantCase : public MeshShaderMiscCase
+ {
+ public:
+ PushConstantCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr params)
+ : MeshShaderMiscCase (testCtx, name, description, std::move(params))
+ {}
+
+ void initPrograms (vk::SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ };
+
+ class PushConstantInstance : public MeshShaderMiscInstance
+ {
+ public:
+ PushConstantInstance (Context& context, const MiscTestParams* params)
+ : MeshShaderMiscInstance (context, params)
+ {}
+
+ void generateReferenceLevel () override;
+ tcu::TestStatus iterate () override;
+ };
+
+ TestInstance* PushConstantCase::createInstance (Context& context) const
+ {
+ return new PushConstantInstance(context, m_params.get());
+ }
+
+ void PushConstantInstance::generateReferenceLevel ()
+ {
+ generateSolidRefLevel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), m_referenceLevel);
+ }
+
+ void PushConstantCase::initPrograms (vk::SourceCollections& programCollection) const
+ {
+ const auto useTaskShader = m_params->needsTaskShader();
+ const auto pcNumFloats = (useTaskShader ? 2u : 4u);
+
+ std::ostringstream pushConstantStream;
+ pushConstantStream
+ << "layout (push_constant, std430) uniform PushConstantBlock {\n"
+ << " layout (offset=${PCOFFSET}) float values[" << pcNumFloats << "];\n"
+ << "} pc;\n"
+ << "\n"
+ ;
+ const tcu::StringTemplate pushConstantsTemplate (pushConstantStream.str());
+ using TemplateMap = std::map<std::string, std::string>;
+
+ std::ostringstream taskDataStream;
+ taskDataStream
+ << "taskNV TaskData {\n"
+ << " float values[2];\n"
+ << "} td;\n"
+ << "\n"
+ ;
+ const auto taskDataDecl = taskDataStream.str();
+
+ if (useTaskShader)
+ {
+ TemplateMap taskMap;
+ taskMap["PCOFFSET"] = std::to_string(2u * sizeof(float));
+
+ std::ostringstream task;
+ task
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=1) in;\n"
+ << "\n"
+ << "out " << taskDataDecl
+ << pushConstantsTemplate.specialize(taskMap)
+ << "void main ()\n"
+ << "{\n"
+ << " gl_TaskCountNV = " << m_params->meshCount << ";\n"
+ << "\n"
+ << " td.values[0] = pc.values[0];\n"
+ << " td.values[1] = pc.values[1];\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("task") << glu::TaskSource(task.str());
+ }
+
+ {
+ const std::string blue = (useTaskShader ? "td.values[0] + pc.values[0]" : "pc.values[0] + pc.values[2]");
+ const std::string alpha = (useTaskShader ? "td.values[1] + pc.values[1]" : "pc.values[1] + pc.values[3]");
+
+ TemplateMap meshMap;
+ meshMap["PCOFFSET"] = "0";
+
+ std::ostringstream mesh;
+ mesh
+ << "#version 450\n"
+ << "#extension GL_NV_mesh_shader : enable\n"
+ << "\n"
+ << "layout(local_size_x=1) in;\n"
+ << "layout(triangles) out;\n"
+ << "layout(max_vertices=3, max_primitives=1) out;\n"
+ << "\n"
+ << "layout (location=0) out perprimitiveNV vec4 triangleColor[];\n"
+ << "\n"
+ << pushConstantsTemplate.specialize(meshMap)
+ << (useTaskShader ? "in " + taskDataDecl : "")
+ << "void main ()\n"
+ << "{\n"
+ << " gl_PrimitiveCountNV = 1;\n"
+ << "\n"
+ << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
+ << " gl_MeshVerticesNV[1].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
+ << " gl_MeshVerticesNV[2].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n"
+ << "\n"
+ << " gl_PrimitiveIndicesNV[0] = 0;\n"
+ << " gl_PrimitiveIndicesNV[1] = 1;\n"
+ << " gl_PrimitiveIndicesNV[2] = 2;\n"
+ << "\n"
+ << " triangleColor[0] = vec4(0.0, 0.0, " << blue << ", " << alpha << ");\n"
+ << "}\n"
+ ;
+ programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
+ }
+
+ // Add default fragment shader.
+ MeshShaderMiscCase::initPrograms(programCollection);
+ }
+
+ tcu::TestStatus PushConstantInstance::iterate ()
+ {
+ const auto& vkd = m_context.getDeviceInterface();
+ const auto device = m_context.getDevice();
+ auto& alloc = m_context.getDefaultAllocator();
+ const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
+ const auto queue = m_context.getUniversalQueue();
+
+ const auto imageFormat = getOutputFormat();
+ const auto tcuFormat = mapVkFormat(imageFormat);
+ const auto imageExtent = makeExtent3D(m_params->width, m_params->height, 1u);
+ const auto imageUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+
+ const auto& binaries = m_context.getBinaryCollection();
+ const auto hasTask = binaries.contains("task");
+
+ const VkImageCreateInfo colorBufferInfo =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ imageFormat, // VkFormat format;
+ imageExtent, // VkExtent3D extent;
+ 1u, // uint32_t mipLevels;
+ 1u, // uint32_t arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ imageUsage, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // uint32_t queueFamilyIndexCount;
+ nullptr, // const uint32_t* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+
+ // Create color image and view.
+ ImageWithMemory colorImage (vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
+ const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
+ const auto colorView = makeImageView(vkd, device, colorImage.get(), VK_IMAGE_VIEW_TYPE_2D, imageFormat, colorSRR);
+
+ // Create a memory buffer for verification.
+ const auto verificationBufferSize = static_cast<VkDeviceSize>(imageExtent.width * imageExtent.height * tcu::getPixelSize(tcuFormat));
+ const auto verificationBufferUsage = (VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ const auto verificationBufferInfo = makeBufferCreateInfo(verificationBufferSize, verificationBufferUsage);
+
+ BufferWithMemory verificationBuffer (vkd, device, alloc, verificationBufferInfo, MemoryRequirement::HostVisible);
+ auto& verificationBufferAlloc = verificationBuffer.getAllocation();
+ void* verificationBufferData = verificationBufferAlloc.getHostPtr();
+
+ // Push constant ranges.
+ std::vector<float> pcData { 0.25f, 0.25f, 0.75f, 0.75f };
+ const auto pcSize = static_cast<uint32_t>(de::dataSize(pcData));
+ const auto pcHalfSize = pcSize / 2u;
+
+ std::vector<VkPushConstantRange> pcRanges;
+ if (hasTask)
+ {
+ pcRanges.push_back(makePushConstantRange(VK_SHADER_STAGE_MESH_BIT_NV, 0u, pcHalfSize));
+ pcRanges.push_back(makePushConstantRange(VK_SHADER_STAGE_TASK_BIT_NV, pcHalfSize, pcHalfSize));
+ }
+ else
+ {
+ pcRanges.push_back(makePushConstantRange(VK_SHADER_STAGE_MESH_BIT_NV, 0u, pcSize));
+ }
+
+ // Pipeline layout.
+ const auto pipelineLayout = makePipelineLayout(vkd, device, 0u, nullptr, static_cast<uint32_t>(pcRanges.size()), de::dataOrNull(pcRanges));
+
+ // Shader modules.
+ const auto meshShader = createShaderModule(vkd, device, binaries.get("mesh"));
+ const auto fragShader = createShaderModule(vkd, device, binaries.get("frag"));
+
+ Move<VkShaderModule> taskShader;
+ if (hasTask)
+ taskShader = createShaderModule(vkd, device, binaries.get("task"));
+
+ // Render pass.
+ const auto renderPass = makeRenderPass(vkd, device, imageFormat);
+
+ // Framebuffer.
+ const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorView.get(), imageExtent.width, imageExtent.height);
+
+ // Viewport and scissor.
+ const std::vector<VkViewport> viewports (1u, makeViewport(imageExtent));
+ const std::vector<VkRect2D> scissors (1u, makeRect2D(imageExtent));
+
+ const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
+ taskShader.get(), meshShader.get(), fragShader.get(),
+ renderPass.get(), viewports, scissors);
+
+ // Command pool and buffer.
+ const auto cmdPool = makeCommandPool(vkd, device, queueIndex);
+ const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+ const auto cmdBuffer = cmdBufferPtr.get();
+
+ beginCommandBuffer(vkd, cmdBuffer);
+
+ // Run pipeline.
+ const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 0.0f);
+ beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor);
+ vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
+ for (const auto& range : pcRanges)
+ vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), range.stageFlags, range.offset, range.size, reinterpret_cast<const char*>(pcData.data()) + range.offset);
+ vkd.cmdDrawMeshTasksNV(cmdBuffer, m_params->drawCount(), 0u);
+ endRenderPass(vkd, cmdBuffer);
+
+ // Copy color buffer to verification buffer.
+ const auto colorAccess = (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT);
+ const auto transferRead = VK_ACCESS_TRANSFER_READ_BIT;
+ const auto transferWrite = VK_ACCESS_TRANSFER_WRITE_BIT;
+ const auto hostRead = VK_ACCESS_HOST_READ_BIT;
+
+ const auto preCopyBarrier = makeImageMemoryBarrier(colorAccess, transferRead, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImage.get(), colorSRR);
+ const auto postCopyBarrier = makeMemoryBarrier(transferWrite, hostRead);
+ const auto copyRegion = makeBufferImageCopy(imageExtent, colorSRL);
+
+ vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preCopyBarrier);
+ vkd.cmdCopyImageToBuffer(cmdBuffer, colorImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verificationBuffer.get(), 1u, ©Region);
+ vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postCopyBarrier, 0u, nullptr, 0u, nullptr);
+
+ endCommandBuffer(vkd, cmdBuffer);
+ submitCommandsAndWait(vkd, device, queue, cmdBuffer);
+
+ // Generate reference image and compare results.
+ const tcu::IVec3 iExtent (static_cast<int>(imageExtent.width), static_cast<int>(imageExtent.height), 1);
+ const tcu::ConstPixelBufferAccess verificationAccess (tcuFormat, iExtent, verificationBufferData);
+
+ generateReferenceLevel();
+ invalidateAlloc(vkd, device, verificationBufferAlloc);
+ if (!verifyResult(verificationAccess))
+ TCU_FAIL("Result does not match reference; check log for details");
+
+ return tcu::TestStatus::pass("Pass");
+ }
+
+ }
+
+ tcu::TestCaseGroup* createMeshShaderMiscTests (tcu::TestContext& testCtx)
+ {
+ GroupPtr miscTests (new tcu::TestCaseGroup(testCtx, "misc", "Mesh Shader Misc Tests"));
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::just(2u);
+ paramsPtr->meshCount = 2u;
+ paramsPtr->width = 8u;
+ paramsPtr->height = 8u;
+
+ miscTests->addChild(new ComplexTaskDataCase(testCtx, "complex_task_data", "Pass a complex structure from the task to the mesh shader", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 5u; // Use an odd value so there's a pixel in the exact center.
+ paramsPtr->height = 7u; // Idem.
+
+ miscTests->addChild(new SinglePointCase(testCtx, "single_point", "Draw a single point", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 8u;
+ paramsPtr->height = 5u; // Use an odd value so there's a center line.
+
+ miscTests->addChild(new SingleLineCase(testCtx, "single_line", "Draw a single line", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 5u; // Use an odd value so there's a pixel in the exact center.
+ paramsPtr->height = 7u; // Idem.
+
+ miscTests->addChild(new SingleTriangleCase(testCtx, "single_triangle", "Draw a single triangle", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 16u;
+ paramsPtr->height = 16u;
+
+ miscTests->addChild(new MaxPointsCase(testCtx, "max_points", "Draw the maximum number of points", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 1u;
+ paramsPtr->height = 1020u;
+
+ miscTests->addChild(new MaxLinesCase(testCtx, "max_lines", "Draw the maximum number of lines", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 512u;
+ paramsPtr->height = 512u;
+
+ miscTests->addChild(new MaxTrianglesCase(testCtx, "max_triangles", "Draw the maximum number of triangles", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::just(65535u);
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 1360u;
+ paramsPtr->height = 1542u;
+
+ miscTests->addChild(new LargeWorkGroupCase(testCtx, "many_task_work_groups", "Generate a large number of task work groups", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::nothing<uint32_t>();
+ paramsPtr->meshCount = 65535u;
+ paramsPtr->width = 1360u;
+ paramsPtr->height = 1542u;
+
+ miscTests->addChild(new LargeWorkGroupCase(testCtx, "many_mesh_work_groups", "Generate a large number of mesh work groups", std::move(paramsPtr)));
+ }
+
+ {
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = tcu::just(512u);
+ paramsPtr->meshCount = 512u;
+ paramsPtr->width = 4096u;
+ paramsPtr->height = 2048u;
+
+ miscTests->addChild(new LargeWorkGroupCase(testCtx, "many_task_mesh_work_groups", "Generate a large number of task and mesh work groups", std::move(paramsPtr)));
+ }
+
+ {
+ const PrimitiveType types[] = {
+ PrimitiveType::POINTS,
+ PrimitiveType::LINES,
+ PrimitiveType::TRIANGLES,
+ };
+
+ for (int i = 0; i < 2; ++i)
+ {
+ const bool extraWrites = (i > 0);
+
+ for (const auto primType : types)
+ {
+ std::unique_ptr<NoPrimitivesParams> params (new NoPrimitivesParams);
+ params->taskCount = (extraWrites ? tcu::just(1u) : tcu::nothing<uint32_t>());
+ params->meshCount = 1u;
+ params->width = 16u;
+ params->height = 16u;
+ params->primitiveType = primType;
+
+ ParamsPtr paramsPtr (params.release());
+ const auto primName = primitiveTypeName(primType);
+ const std::string name = "no_" + primName + (extraWrites ? "_extra_writes" : "");
+ const std::string desc = "Run a pipeline that generates no " + primName + (extraWrites ? " but generates primitive data" : "");
+
+ miscTests->addChild(extraWrites
+ ? (new NoPrimitivesExtraWritesCase(testCtx, name, desc, std::move(paramsPtr)))
+ : (new NoPrimitivesCase(testCtx, name, desc, std::move(paramsPtr))));
+ }
+ }
+ }
+
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ const bool useTaskShader = (i == 0);
+
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = (useTaskShader ? tcu::just(1u) : tcu::nothing<uint32_t>());
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 1u;
+ paramsPtr->height = 1u;
+
+ const std::string shader = (useTaskShader ? "task" : "mesh");
+ const std::string name = "barrier_in_" + shader;
+ const std::string desc = "Use a control barrier in the " + shader + " shader";
+
+ miscTests->addChild(new SimpleBarrierCase(testCtx, name, desc, std::move(paramsPtr)));
+ }
+ }
+
+ {
+ const struct
+ {
+ MemoryBarrierType memBarrierType;
+ std::string caseName;
+ } barrierTypes[] =
+ {
+ { MemoryBarrierType::SHARED, "memory_barrier_shared" },
+ { MemoryBarrierType::GROUP, "group_memory_barrier" },
+ };
+
+ for (const auto& barrierCase : barrierTypes)
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ const bool useTaskShader = (i == 0);
+
+ std::unique_ptr<MemoryBarrierParams> paramsPtr (new MemoryBarrierParams);
+
+ paramsPtr->taskCount = (useTaskShader ? tcu::just(1u) : tcu::nothing<uint32_t>());
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 1u;
+ paramsPtr->height = 1u;
+ paramsPtr->memBarrierType = barrierCase.memBarrierType;
+
+ const std::string shader = (useTaskShader ? "task" : "mesh");
+ const std::string name = barrierCase.caseName + "_in_" + shader;
+ const std::string desc = "Use " + paramsPtr->glslFunc() + "() in the " + shader + " shader";
+
+ miscTests->addChild(new MemoryBarrierCase(testCtx, name, desc, std::move(paramsPtr)));
+ }
+ }
+ }
+
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ const bool useTaskShader = (i > 0);
+ const auto name = std::string("custom_attributes") + (useTaskShader ? "_and_task_shader" : "");
+ const auto desc = std::string("Use several custom vertex and primitive attributes") + (useTaskShader ? " and also a task shader" : "");
+
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = (useTaskShader ? tcu::just(1u) : tcu::nothing<uint32_t>());
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 32u;
+ paramsPtr->height = 32u;
+
+ miscTests->addChild(new CustomAttributesCase(testCtx, name, desc, std::move(paramsPtr)));
+ }
+ }
+
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ const bool useTaskShader = (i > 0);
+ const auto name = std::string("push_constant") + (useTaskShader ? "_and_task_shader" : "");
+ const auto desc = std::string("Use push constants in the mesh shader stage") + (useTaskShader ? " and also in the task shader stage" : "");
+
+ ParamsPtr paramsPtr (new MiscTestParams);
+
+ paramsPtr->taskCount = (useTaskShader ? tcu::just(1u) : tcu::nothing<uint32_t>());
+ paramsPtr->meshCount = 1u;
+ paramsPtr->width = 16u;
+ paramsPtr->height = 16u;
+
+ miscTests->addChild(new PushConstantCase(testCtx, name, desc, std::move(paramsPtr)));
+ }
+ }
+
+ return miscTests.release();
+ }
+
+ } // MeshShader
+ } // vkt