| /*------------------------------------------------------------------------ |
| * 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 Builtin Tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktMeshShaderBuiltinTests.hpp" |
| #include "vktTestCase.hpp" |
| |
| #include "vkTypeUtil.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkObjUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkImageWithMemory.hpp" |
| #include "vkBufferWithMemory.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkBarrierUtil.hpp" |
| |
| #include "tcuTexture.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include <vector> |
| #include <algorithm> |
| #include <sstream> |
| #include <map> |
| #include <utility> |
| #include <sstream> |
| |
| namespace tcu |
| { |
| // Needed for PixelMap below. |
| bool operator<(const IVec2& a, const IVec2& b) |
| { |
| return (a.x() < b.x() || (a.x() == b.x() && a.y() < b.y())); |
| } |
| } |
| |
| namespace vkt |
| { |
| namespace MeshShader |
| { |
| |
| namespace |
| { |
| |
| using namespace vk; |
| |
| using GroupPtr = de::MovePtr<tcu::TestCaseGroup>; |
| using DrawCommandVec = std::vector<VkDrawMeshTasksIndirectCommandNV>; |
| using ImageWithMemoryPtr = de::MovePtr<ImageWithMemory>; |
| using BufferWithMemoryPtr = de::MovePtr<BufferWithMemory>; |
| using ViewportVec = std::vector<VkViewport>; |
| using ColorVec = std::vector<tcu::Vec4>; |
| using PixelMap = std::map<tcu::IVec2, tcu::Vec4>; // Coordinates to color. |
| |
| VkExtent2D getDefaultExtent () |
| { |
| return makeExtent2D(8u, 8u); |
| } |
| |
| VkExtent2D getLinearExtent () |
| { |
| return makeExtent2D(8u, 1u); |
| } |
| |
| struct JobSize |
| { |
| uint32_t numTasks; |
| uint32_t localSize; |
| }; |
| |
| JobSize getLargeJobSize () |
| { |
| return JobSize{8u, 8u}; |
| } |
| |
| // Single draw command with the given number of tasks, 1 by default. |
| DrawCommandVec getDefaultDrawCommands (uint32_t taskCount = 1u) |
| { |
| return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandNV(taskCount, 0u)); |
| } |
| |
| // Basic fragment shader that draws fragments in blue. |
| std::string getBasicFragShader () |
| { |
| return |
| "#version 460\n" |
| "#extension GL_NV_mesh_shader : enable\n" |
| "\n" |
| "layout (location=0) out vec4 outColor;\n" |
| "\n" |
| "void main ()\n" |
| "{\n" |
| " outColor = vec4(0.0, 0.0, 1.0, 1.0);\n" |
| "}\n" |
| ; |
| } |
| |
| struct IterationParams |
| { |
| VkExtent2D colorExtent; |
| uint32_t numLayers; |
| DrawCommandVec drawArgs; |
| bool indirect; |
| ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| |
| class MeshShaderBuiltinInstance : public vkt::TestInstance |
| { |
| public: |
| MeshShaderBuiltinInstance (Context& context, const IterationParams& params) |
| : vkt::TestInstance (context) |
| , m_params (params) |
| {} |
| virtual ~MeshShaderBuiltinInstance (void) {} |
| |
| tcu::TestStatus iterate () override; |
| virtual void verifyResults (const tcu::ConstPixelBufferAccess& result) = 0; |
| |
| protected: |
| IterationParams m_params; |
| }; |
| |
| tcu::TestStatus MeshShaderBuiltinInstance::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& binaries = m_context.getBinaryCollection(); |
| |
| const auto useTask = binaries.contains("task"); |
| const auto useFrag = binaries.contains("frag"); |
| const auto extent = makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u); |
| const auto iExtent3D = tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height), static_cast<int>(m_params.numLayers)); |
| const auto format = VK_FORMAT_R8G8B8A8_UNORM; |
| const auto tcuFormat = mapVkFormat(format); |
| const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); |
| const auto viewType = ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D); |
| const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers); |
| const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers); |
| const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 1.0f); |
| |
| ImageWithMemoryPtr colorBuffer; |
| Move<VkImageView> colorBufferView; |
| { |
| const VkImageCreateInfo colorBufferInfo = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkImageCreateFlags flags; |
| VK_IMAGE_TYPE_2D, // VkImageType imageType; |
| format, // VkFormat format; |
| extent, // VkExtent3D extent; |
| 1u, // uint32_t mipLevels; |
| m_params.numLayers, // uint32_t arrayLayers; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| colorUsage, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 0u, // uint32_t queueFamilyIndexCount; |
| nullptr, // const uint32_t* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| colorBuffer = ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any)); |
| colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR); |
| } |
| |
| // Empty descriptor set layout. |
| DescriptorSetLayoutBuilder layoutBuilder; |
| const auto setLayout = layoutBuilder.build(vkd, device); |
| |
| // Pipeline layout. |
| const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get()); |
| |
| // Render pass and framebuffer. |
| const auto renderPass = makeRenderPass(vkd, device, format); |
| const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width, extent.height, m_params.numLayers); |
| |
| // Pipeline. |
| Move<VkShaderModule> taskModule; |
| Move<VkShaderModule> meshModule; |
| Move<VkShaderModule> fragModule; |
| |
| if (useTask) |
| taskModule = createShaderModule(vkd, device, binaries.get("task")); |
| if (useFrag) |
| fragModule = createShaderModule(vkd, device, binaries.get("frag")); |
| meshModule = createShaderModule(vkd, device, binaries.get("mesh")); |
| |
| std::vector<VkViewport> viewports; |
| std::vector<VkRect2D> scissors; |
| if (m_params.viewports.empty()) |
| { |
| // Default ones. |
| viewports.push_back(makeViewport(extent)); |
| scissors.push_back(makeRect2D(extent)); |
| } |
| else |
| { |
| // The desired viewports and the same number of default scissors. |
| viewports.reserve(m_params.viewports.size()); |
| std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports)); |
| scissors.resize(viewports.size(), makeRect2D(extent)); |
| } |
| |
| const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(), |
| taskModule.get(), meshModule.get(), fragModule.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(); |
| |
| // Indirect buffer if needed. |
| BufferWithMemoryPtr indirectBuffer; |
| |
| DE_ASSERT(!m_params.drawArgs.empty()); |
| if (m_params.indirect) |
| { |
| // Indirect draws. |
| const auto indirectBufferSize = static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs)); |
| const auto indirectBufferUsage = (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT); |
| const auto indirectBufferInfo = makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage); |
| indirectBuffer = BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible)); |
| auto& indirectBufferAlloc = indirectBuffer->getAllocation(); |
| void* indirectBufferData = indirectBufferAlloc.getHostPtr(); |
| |
| deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize)); |
| flushAlloc(vkd, device, indirectBufferAlloc); |
| } |
| |
| // Submit commands. |
| beginCommandBuffer(vkd, cmdBuffer); |
| beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor); |
| vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get()); |
| |
| if (!m_params.indirect) |
| { |
| for (const auto& command : m_params.drawArgs) |
| vkd.cmdDrawMeshTasksNV(cmdBuffer, command.taskCount, command.firstTask); |
| } |
| else |
| { |
| const auto numDraws = static_cast<uint32_t>(m_params.drawArgs.size()); |
| const auto stride = static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type)); |
| vkd.cmdDrawMeshTasksIndirectNV(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride); |
| } |
| |
| endRenderPass(vkd, cmdBuffer); |
| |
| // Output buffer to extract the color buffer contents. |
| BufferWithMemoryPtr outBuffer; |
| void* outBufferData = nullptr; |
| { |
| const auto layerSize = static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * extent.width * extent.height); |
| const auto outBufferSize = layerSize * m_params.numLayers; |
| const auto outBufferUsage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| const auto outBufferInfo = makeBufferCreateInfo(outBufferSize, outBufferUsage); |
| |
| outBuffer = BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible)); |
| outBufferData = outBuffer->getAllocation().getHostPtr(); |
| } |
| |
| // Transition image layout. |
| const auto preTransferBarrier = makeImageMemoryBarrier( |
| (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| colorBuffer->get(), colorSRR); |
| |
| vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier); |
| |
| // Copy image to output buffer. |
| const std::vector<VkBufferImageCopy> regions (1u, makeBufferImageCopy(extent, colorSRL)); |
| vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(), static_cast<uint32_t>(regions.size()), de::dataOrNull(regions)); |
| |
| // Transfer to host barrier. |
| const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); |
| vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postTransferBarrier, 0u, nullptr, 0u, nullptr); |
| |
| endCommandBuffer(vkd, cmdBuffer); |
| submitCommandsAndWait(vkd, device, queue, cmdBuffer); |
| |
| // Invalidate alloc and verify result. |
| { |
| auto& outBufferAlloc = outBuffer->getAllocation(); |
| invalidateAlloc(vkd, device, outBufferAlloc); |
| |
| tcu::ConstPixelBufferAccess result (tcuFormat, iExtent3D, outBufferData); |
| verifyResults(result); |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| // Abstract case that implements the generic checkSupport method. |
| class MeshShaderBuiltinCase : public vkt::TestCase |
| { |
| public: |
| MeshShaderBuiltinCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded) |
| : vkt::TestCase (testCtx, name, description) |
| , m_taskNeeded (taskNeeded) |
| {} |
| virtual ~MeshShaderBuiltinCase (void) {} |
| |
| void checkSupport (Context& context) const override; |
| |
| protected: |
| const bool m_taskNeeded; |
| }; |
| |
| void MeshShaderBuiltinCase::checkSupport (Context& context) const |
| { |
| context.requireDeviceFunctionality("VK_NV_mesh_shader"); |
| |
| const auto& meshFeatures = context.getMeshShaderFeatures(); |
| |
| if (!meshFeatures.meshShader) |
| TCU_THROW(NotSupportedError, "Mesh shader not supported"); |
| |
| if (m_taskNeeded && !meshFeatures.taskShader) |
| TCU_THROW(NotSupportedError, "Task shader not supported"); |
| } |
| |
| // Instance that verifies color layers. |
| class FullScreenColorInstance : public MeshShaderBuiltinInstance |
| { |
| public: |
| FullScreenColorInstance (Context& context, const IterationParams& params, const ColorVec& expectedColors) |
| : MeshShaderBuiltinInstance (context, params) |
| , m_expectedColors (expectedColors) |
| {} |
| virtual ~FullScreenColorInstance (void) {} |
| |
| void verifyResults (const tcu::ConstPixelBufferAccess& result) override; |
| |
| protected: |
| const ColorVec m_expectedColors; |
| }; |
| |
| void FullScreenColorInstance::verifyResults (const tcu::ConstPixelBufferAccess& result) |
| { |
| auto& log = m_context.getTestContext().getLog(); |
| bool fail = false; |
| const auto width = result.getWidth(); |
| const auto height = result.getHeight(); |
| const auto depth = result.getDepth(); |
| |
| for (int z = 0; z < depth; ++z) |
| { |
| const auto& expected = m_expectedColors.at(z); |
| |
| for (int y = 0; y < height; ++y) |
| for (int x = 0; x < width; ++x) |
| { |
| const auto resultColor = result.getPixel(x, y, z); |
| if (resultColor != expected) |
| { |
| std::ostringstream msg; |
| msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected << " and found " << resultColor; |
| log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage; |
| fail = true; |
| } |
| } |
| } |
| |
| if (fail) |
| { |
| log << tcu::TestLog::Image("Result", "", result); |
| TCU_FAIL("Check log for details"); |
| } |
| } |
| |
| // Instance that verifies single-layer framebuffers divided into 4 quadrants. |
| class QuadrantsInstance : public MeshShaderBuiltinInstance |
| { |
| public: |
| QuadrantsInstance (Context& context, const IterationParams& params, |
| const tcu::Vec4 topLeft, |
| const tcu::Vec4 topRight, |
| const tcu::Vec4 bottomLeft, |
| const tcu::Vec4 bottomRight) |
| : MeshShaderBuiltinInstance (context, params) |
| , m_topLeft (topLeft) |
| , m_topRight (topRight) |
| , m_bottomLeft (bottomLeft) |
| , m_bottomRight (bottomRight) |
| {} |
| virtual ~QuadrantsInstance (void) {} |
| |
| void verifyResults (const tcu::ConstPixelBufferAccess& result) override; |
| |
| protected: |
| const tcu::Vec4 m_topLeft; |
| const tcu::Vec4 m_topRight; |
| const tcu::Vec4 m_bottomLeft; |
| const tcu::Vec4 m_bottomRight; |
| }; |
| |
| void QuadrantsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result) |
| { |
| const auto width = result.getWidth(); |
| const auto height = result.getHeight(); |
| const auto depth = result.getDepth(); |
| |
| DE_ASSERT(depth == 1); |
| DE_ASSERT(width > 0 && width % 2 == 0); |
| DE_ASSERT(height > 0 && height % 2 == 0); |
| DE_UNREF(depth); // For release builds. |
| |
| const auto halfWidth = width / 2; |
| const auto halfHeight = height / 2; |
| tcu::Vec4 expected; |
| |
| for (int y = 0; y < height; ++y) |
| for (int x = 0; x < width; ++x) |
| { |
| // Choose the right quadrant |
| if (y < halfHeight) |
| expected = ((x < halfWidth) ? m_topLeft : m_topRight); |
| else |
| expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight); |
| |
| const auto resultColor = result.getPixel(x, y); |
| if (resultColor != expected) |
| { |
| std::ostringstream msg; |
| msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor; |
| TCU_FAIL(msg.str()); |
| } |
| } |
| } |
| |
| // Instance that verifies single-layer framebuffers with specific pixels set to some color. |
| struct PixelVerifierParams |
| { |
| const tcu::Vec4 background; |
| const PixelMap pixelMap; |
| }; |
| |
| class PixelsInstance : public MeshShaderBuiltinInstance |
| { |
| public: |
| PixelsInstance (Context& context, const IterationParams& params, const PixelVerifierParams& pixelParams) |
| : MeshShaderBuiltinInstance (context, params) |
| , m_pixelParams (pixelParams) |
| {} |
| virtual ~PixelsInstance (void) {} |
| |
| void verifyResults (const tcu::ConstPixelBufferAccess& result) override; |
| |
| protected: |
| const PixelVerifierParams m_pixelParams; |
| }; |
| |
| void PixelsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result) |
| { |
| const auto width = result.getWidth(); |
| const auto height = result.getHeight(); |
| const auto depth = result.getDepth(); |
| |
| DE_ASSERT(depth == 1); |
| DE_UNREF(depth); // For release builds. |
| |
| for (int y = 0; y < height; ++y) |
| for (int x = 0; x < width; ++x) |
| { |
| const tcu::IVec2 coords (x, y); |
| const auto iter = m_pixelParams.pixelMap.find(coords); |
| const auto expected = ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second); |
| const auto resultColor = result.getPixel(x, y); |
| |
| if (resultColor != expected) |
| { |
| std::ostringstream msg; |
| msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor; |
| TCU_FAIL(msg.str()); |
| } |
| } |
| } |
| |
| // Primitive ID cases. |
| class PrimitiveIdCase : public MeshShaderBuiltinCase |
| { |
| public: |
| PrimitiveIdCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool glslFrag) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| , m_glslFrag (glslFrag) |
| {} |
| virtual ~PrimitiveIdCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| void checkSupport (Context& context) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| protected: |
| // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID. |
| const bool m_glslFrag; |
| }; |
| |
| void PrimitiveIdCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // Mesh shader. |
| { |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\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" |
| << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n" |
| << " int gl_PrimitiveID;\n" |
| << "} gl_MeshPrimitivesNV[];\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 1u;\n" |
| << "\n" |
| << " gl_PrimitiveIndicesNV[0] = 0;\n" |
| << " gl_PrimitiveIndicesNV[1] = 1;\n" |
| << " gl_PrimitiveIndicesNV[2] = 2;\n" |
| << "\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n" |
| << "\n" |
| // Sets an arbitrary primitive id. |
| << " gl_MeshPrimitivesNV[0].gl_PrimitiveID = 1629198956;\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Frag shader. |
| if (m_glslFrag) |
| { |
| std::ostringstream frag; |
| frag |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| // Checks the primitive id matches. |
| << " outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0));\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| } |
| else |
| { |
| // This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingNV in order to |
| // access gl_PrimitiveID. This also needs the SPV_NV_mesh_shader extension. |
| std::ostringstream frag; |
| frag |
| << "; Version: 1.0\n" |
| << "; Generator: Khronos Glslang Reference Front End; 10\n" |
| << "; Bound: 24\n" |
| << "; Schema: 0\n" |
| << " OpCapability Shader\n" |
| |
| // Manual change in these lines. |
| //<< " OpCapability Geometry\n" |
| << " OpCapability MeshShadingNV\n" |
| << " OpExtension \"SPV_NV_mesh_shader\"\n" |
| |
| << " %1 = OpExtInstImport \"GLSL.std.450\"\n" |
| << " OpMemoryModel Logical GLSL450\n" |
| << " OpEntryPoint Fragment %4 \"main\" %9 %12\n" |
| << " OpExecutionMode %4 OriginUpperLeft\n" |
| << " OpDecorate %9 Location 0\n" |
| << " OpDecorate %12 Flat\n" |
| << " OpDecorate %12 BuiltIn PrimitiveId\n" |
| << " %2 = OpTypeVoid\n" |
| << " %3 = OpTypeFunction %2\n" |
| << " %6 = OpTypeFloat 32\n" |
| << " %7 = OpTypeVector %6 4\n" |
| << " %8 = OpTypePointer Output %7\n" |
| << " %9 = OpVariable %8 Output\n" |
| << "%10 = OpTypeInt 32 1\n" |
| << "%11 = OpTypePointer Input %10\n" |
| << "%12 = OpVariable %11 Input\n" |
| << "%14 = OpConstant %10 1629198956\n" |
| << "%15 = OpTypeBool\n" |
| << "%17 = OpConstant %6 0\n" |
| << "%18 = OpConstant %6 1\n" |
| << "%19 = OpConstantComposite %7 %17 %17 %18 %18\n" |
| << "%20 = OpConstantComposite %7 %17 %17 %17 %18\n" |
| << "%21 = OpTypeVector %15 4\n" |
| << " %4 = OpFunction %2 None %3\n" |
| << " %5 = OpLabel\n" |
| << "%13 = OpLoad %10 %12\n" |
| << "%16 = OpIEqual %15 %13 %14\n" |
| << "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n" |
| << "%23 = OpSelect %7 %22 %19 %20\n" |
| << " OpStore %9 %23\n" |
| << " OpReturn\n" |
| << " OpFunctionEnd\n" |
| ; |
| programCollection.spirvAsmSources.add("frag") << frag.str(); |
| } |
| } |
| |
| void PrimitiveIdCase::checkSupport (Context& context) const |
| { |
| MeshShaderBuiltinCase::checkSupport(context); |
| |
| // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID. |
| if (m_glslFrag) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER); |
| } |
| |
| TestInstance* PrimitiveIdCase::createInstance (Context& context) const |
| { |
| const ColorVec expectedColors (1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| return new FullScreenColorInstance(context, iterationParams, expectedColors); |
| } |
| |
| // Layer builtin case. |
| class LayerCase : public MeshShaderBuiltinCase |
| { |
| public: |
| LayerCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool shareVertices) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| , m_shareVertices (shareVertices) |
| {} |
| virtual ~LayerCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| void checkSupport (Context& context) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| static constexpr uint32_t kNumLayers = 4u; |
| |
| protected: |
| const bool m_shareVertices; |
| }; |
| |
| void LayerCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| const auto localSize = (m_shareVertices ? kNumLayers : 1u); |
| const auto numPrimitives = (m_shareVertices ? kNumLayers : 1u); |
| const auto layerNumber = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x"); |
| |
| // One layer per local invocation or work group (shared vertices or not, respectively). |
| { |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=" << localSize << ") in;\n" |
| << "layout (triangles) out;\n" |
| << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n" |
| << "\n" |
| << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n" |
| << " int gl_Layer;\n" |
| << "} gl_MeshPrimitivesNV[];\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = " << numPrimitives << ";\n" |
| << "\n" |
| << " if (gl_LocalInvocationIndex == 0u)\n" |
| << " {\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n" |
| << " }\n" |
| << "\n" |
| << " const uint baseIndex = gl_LocalInvocationIndex * 3u;\n" |
| << " gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n" |
| << " gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n" |
| << " gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n" |
| << "\n" |
| << " gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Fragment shader chooses one color per layer. |
| { |
| std::ostringstream frag; |
| frag |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << "\n" |
| << "vec4 colors[" << kNumLayers << "] = vec4[](\n" |
| << " vec4(0.0, 0.0, 1.0, 1.0),\n" |
| << " vec4(1.0, 0.0, 1.0, 1.0),\n" |
| << " vec4(0.0, 1.0, 1.0, 1.0),\n" |
| << " vec4(1.0, 1.0, 0.0, 1.0)\n" |
| << ");\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " outColor = colors[gl_Layer];\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| } |
| } |
| |
| void LayerCase::checkSupport (Context& context) const |
| { |
| MeshShaderBuiltinCase::checkSupport(context); |
| |
| if (!context.contextSupports(vk::ApiVersion(1u, 2u, 0u))) |
| context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer"); |
| else |
| { |
| const auto& features = context.getDeviceVulkan12Features(); |
| if (!features.shaderOutputLayer) |
| TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported"); |
| } |
| } |
| |
| TestInstance* LayerCase::createInstance (Context& context) const |
| { |
| ColorVec expectedColors; |
| |
| expectedColors.reserve(kNumLayers); |
| expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0)); |
| expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0)); |
| expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0)); |
| expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0)); |
| |
| const auto numWorkGroups = (m_shareVertices ? 1u : kNumLayers); |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| kNumLayers, // uint32_t numLayers; |
| getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| return new FullScreenColorInstance(context, iterationParams, expectedColors); |
| } |
| |
| // ViewportIndex builtin case. |
| class ViewportIndexCase : public MeshShaderBuiltinCase |
| { |
| public: |
| ViewportIndexCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool shareVertices) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| , m_shareVertices (shareVertices) |
| {} |
| virtual ~ViewportIndexCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| void checkSupport (Context& context) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| static constexpr uint32_t kQuadrants = 4u; |
| |
| protected: |
| const bool m_shareVertices; |
| }; |
| |
| void ViewportIndexCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| const auto localSize = (m_shareVertices ? kQuadrants : 1u); |
| const auto numPrimitives = (m_shareVertices ? kQuadrants : 1u); |
| const auto viewportIndex = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x"); |
| |
| // One viewport per local invocation or work group (sharing vertices or not, respectively). |
| { |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=" << localSize << ") in;\n" |
| << "layout (triangles) out;\n" |
| << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n" |
| << "\n" |
| << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n" |
| << " int gl_ViewportIndex;\n" |
| << "} gl_MeshPrimitivesNV[];\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = " << numPrimitives << ";\n" |
| << "\n" |
| << " if (gl_LocalInvocationIndex == 0u)\n" |
| << " {\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n" |
| << " }\n" |
| << "\n" |
| << " const uint baseIndex = gl_LocalInvocationIndex * 3u;\n" |
| << " gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n" |
| << " gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n" |
| << " gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n" |
| << "\n" |
| << " gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex << ");\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Fragment shader chooses one color per viewport. |
| { |
| std::ostringstream frag; |
| frag |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << "\n" |
| << "vec4 colors[" << kQuadrants << "] = vec4[](\n" |
| << " vec4(0.0, 0.0, 1.0, 1.0),\n" |
| << " vec4(1.0, 0.0, 1.0, 1.0),\n" |
| << " vec4(0.0, 1.0, 1.0, 1.0),\n" |
| << " vec4(1.0, 1.0, 0.0, 1.0)\n" |
| << ");\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " outColor = colors[gl_ViewportIndex];\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| } |
| } |
| |
| void ViewportIndexCase::checkSupport (Context& context) const |
| { |
| MeshShaderBuiltinCase::checkSupport(context); |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT); |
| |
| if (!context.contextSupports(vk::ApiVersion(1u, 2u, 0u))) |
| context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer"); |
| else |
| { |
| const auto& features = context.getDeviceVulkan12Features(); |
| if (!features.shaderOutputViewportIndex) |
| TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported"); |
| } |
| } |
| |
| TestInstance* ViewportIndexCase::createInstance (Context& context) const |
| { |
| const auto extent = getDefaultExtent(); |
| |
| DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u); |
| DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u); |
| |
| const auto halfWidth = static_cast<float>(extent.width / 2u); |
| const auto halfHeight = static_cast<float>(extent.height / 2u); |
| |
| const auto topLeft = tcu::Vec4(0.0, 0.0, 1.0, 1.0); |
| const auto topRight = tcu::Vec4(1.0, 0.0, 1.0, 1.0); |
| const auto bottomLeft = tcu::Vec4(0.0, 1.0, 1.0, 1.0); |
| const auto bottomRight = tcu::Vec4(1.0, 1.0, 0.0, 1.0); |
| |
| ViewportVec viewports; |
| viewports.reserve(kQuadrants); |
| viewports.emplace_back(makeViewport(0.0f, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f)); |
| viewports.emplace_back(makeViewport(halfWidth, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f)); |
| viewports.emplace_back(makeViewport(0.0f, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f)); |
| viewports.emplace_back(makeViewport(halfWidth, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f)); |
| |
| const auto numWorkGroups = (m_shareVertices ? 1u : kQuadrants); |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| std::move(viewports), // ViewportVec viewports; |
| }; |
| return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight); |
| } |
| |
| // Position builtin case. |
| class PositionCase : public MeshShaderBuiltinCase |
| { |
| public: |
| PositionCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| {} |
| virtual ~PositionCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| }; |
| |
| void PositionCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // Mesh shader: emit single triangle around the center of the top left pixel. |
| { |
| const auto extent = getDefaultExtent(); |
| const auto fWidth = static_cast<float>(extent.width); |
| const auto fHeight = static_cast<float>(extent.height); |
| |
| const auto pxWidth = 2.0f / fWidth; |
| const auto pxHeight = 2.0f / fHeight; |
| |
| const auto halfXPix = pxWidth / 2.0f; |
| const auto halfYPix = pxHeight / 2.0f; |
| |
| // Center of top left pixel. |
| const auto x = -1.0f + halfXPix; |
| const auto y = -1.0f + halfYPix; |
| |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\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" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 1u;\n" |
| << "\n" |
| << " gl_PrimitiveIndicesNV[0] = 0;\n" |
| << " gl_PrimitiveIndicesNV[1] = 1;\n" |
| << " gl_PrimitiveIndicesNV[2] = 2;\n" |
| << "\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Basic fragment shader. |
| { |
| const auto frag = getBasicFragShader(); |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag); |
| } |
| } |
| |
| TestInstance* PositionCase::createInstance (Context& context) const |
| { |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| |
| // Must match the shader. |
| PixelMap pixelMap; |
| pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f); |
| |
| const PixelVerifierParams verifierParams = |
| { |
| tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), // const tcu::Vec4 background; |
| std::move(pixelMap), // const PixelMap pixelMap; |
| }; |
| return new PixelsInstance(context, iterationParams, verifierParams); |
| } |
| |
| // PointSize builtin case. |
| class PointSizeCase : public MeshShaderBuiltinCase |
| { |
| public: |
| PointSizeCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| {} |
| virtual ~PointSizeCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| void checkSupport (Context& context) const override; |
| |
| static constexpr float kPointSize = 4.0f; |
| }; |
| |
| void PointSizeCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // Mesh shader: large point covering the top left quadrant. |
| { |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=1) in;\n" |
| << "layout (points) out;\n" |
| << "layout (max_vertices=1, max_primitives=1) out;\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 1u;\n" |
| << " gl_PrimitiveIndicesNV[0] = 0;\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[0].gl_PointSize = " << kPointSize << ";\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Basic fragment shader. |
| { |
| const auto frag = getBasicFragShader(); |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag); |
| } |
| } |
| |
| TestInstance* PointSizeCase::createInstance (Context& context) const |
| { |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| |
| // Must match the shader. |
| const tcu::Vec4 black (0.0f, 0.0f, 0.0f, 1.0f); |
| const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); |
| |
| return new QuadrantsInstance(context, iterationParams, blue, black, black, black); |
| } |
| |
| void PointSizeCase::checkSupport (Context& context) const |
| { |
| MeshShaderBuiltinCase::checkSupport(context); |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS); |
| |
| const auto& properties = context.getDeviceProperties(); |
| if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1]) |
| TCU_THROW(NotSupportedError, "Required point size outside point size range"); |
| } |
| |
| // ClipDistance builtin case. |
| class ClipDistanceCase : public MeshShaderBuiltinCase |
| { |
| public: |
| ClipDistanceCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| {} |
| virtual ~ClipDistanceCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| void checkSupport (Context& context) const override; |
| }; |
| |
| void ClipDistanceCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // Mesh shader: full-screen quad using different clip distances. |
| { |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=1) in;\n" |
| << "layout (triangles) out;\n" |
| << "layout (max_vertices=4, max_primitives=2) out;\n" |
| << "\n" |
| << "out gl_MeshPerVertexNV {\n" |
| << " vec4 gl_Position;\n" |
| << " float gl_ClipDistance[2];\n" |
| << "} gl_MeshVerticesNV[];\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 2u;\n" |
| << "\n" |
| << " gl_PrimitiveIndicesNV[0] = 0;\n" |
| << " gl_PrimitiveIndicesNV[1] = 1;\n" |
| << " gl_PrimitiveIndicesNV[2] = 2;\n" |
| << " gl_PrimitiveIndicesNV[3] = 1;\n" |
| << " gl_PrimitiveIndicesNV[4] = 3;\n" |
| << " gl_PrimitiveIndicesNV[5] = 2;\n" |
| << "\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[3].gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n" |
| << "\n" |
| // The first clip plane keeps the left half of the frame buffer. |
| << " gl_MeshVerticesNV[0].gl_ClipDistance[0] = 1.0;\n" |
| << " gl_MeshVerticesNV[1].gl_ClipDistance[0] = 1.0;\n" |
| << " gl_MeshVerticesNV[2].gl_ClipDistance[0] = -1.0;\n" |
| << " gl_MeshVerticesNV[3].gl_ClipDistance[0] = -1.0;\n" |
| << "\n" |
| // The second clip plane keeps the top half of the frame buffer. |
| << " gl_MeshVerticesNV[0].gl_ClipDistance[1] = 1.0;\n" |
| << " gl_MeshVerticesNV[1].gl_ClipDistance[1] = -1.0;\n" |
| << " gl_MeshVerticesNV[2].gl_ClipDistance[1] = 1.0;\n" |
| << " gl_MeshVerticesNV[3].gl_ClipDistance[1] = -1.0;\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Fragment shader chooses a constant color. |
| { |
| std::ostringstream frag; |
| frag |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| // White color should not actually be used, as those fragments are supposed to be discarded. |
| << " outColor = ((gl_ClipDistance[0] >= 0.0 && gl_ClipDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| } |
| } |
| |
| TestInstance* ClipDistanceCase::createInstance (Context& context) const |
| { |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| |
| // Must match the shader. |
| const tcu::Vec4 black (0.0f, 0.0f, 0.0f, 1.0f); |
| const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); |
| |
| return new QuadrantsInstance(context, iterationParams, blue, black, black, black); |
| } |
| |
| void ClipDistanceCase::checkSupport (Context& context) const |
| { |
| MeshShaderBuiltinCase::checkSupport(context); |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE); |
| } |
| |
| // CullDistance builtin case. |
| class CullDistanceCase : public MeshShaderBuiltinCase |
| { |
| public: |
| CullDistanceCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description) |
| : MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/) |
| {} |
| virtual ~CullDistanceCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| void checkSupport (Context& context) const override; |
| }; |
| |
| void CullDistanceCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // Mesh shader: two quads covering the whole screen, one on top of the other. |
| // Use cull distances to discard the bottom quad. |
| // Use cull distances to paint the top one in two colors: blue on the left, white on the right. |
| { |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=1) in;\n" |
| << "layout (triangles) out;\n" |
| << "layout (max_vertices=6, max_primitives=4) out;\n" |
| << "\n" |
| << "out gl_MeshPerVertexNV {\n" |
| << " vec4 gl_Position;\n" |
| << " float gl_CullDistance[2];\n" |
| << "} gl_MeshVerticesNV[];\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 4u;\n" |
| << "\n" |
| << " gl_PrimitiveIndicesNV[0] = 0;\n" |
| << " gl_PrimitiveIndicesNV[1] = 1;\n" |
| << " gl_PrimitiveIndicesNV[2] = 3;\n" |
| << " gl_PrimitiveIndicesNV[3] = 1;\n" |
| << " gl_PrimitiveIndicesNV[4] = 4;\n" |
| << " gl_PrimitiveIndicesNV[5] = 3;\n" |
| << " gl_PrimitiveIndicesNV[6] = 1;\n" |
| << " gl_PrimitiveIndicesNV[7] = 2;\n" |
| << " gl_PrimitiveIndicesNV[8] = 4;\n" |
| << " gl_PrimitiveIndicesNV[9] = 2;\n" |
| << " gl_PrimitiveIndicesNV[10] = 5;\n" |
| << " gl_PrimitiveIndicesNV[11] = 4;\n" |
| << "\n" |
| << " gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[1].gl_Position = vec4(-1.0, 0.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[2].gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[4].gl_Position = vec4( 1.0, 0.0, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[5].gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n" |
| << "\n" |
| // The first cull plane discards the bottom quad |
| << " gl_MeshVerticesNV[0].gl_CullDistance[0] = 1.0;\n" |
| << " gl_MeshVerticesNV[1].gl_CullDistance[0] = -1.0;\n" |
| << " gl_MeshVerticesNV[2].gl_CullDistance[0] = -2.0;\n" |
| << " gl_MeshVerticesNV[3].gl_CullDistance[0] = 1.0;\n" |
| << " gl_MeshVerticesNV[4].gl_CullDistance[0] = -1.0;\n" |
| << " gl_MeshVerticesNV[5].gl_CullDistance[0] = -2.0;\n" |
| << "\n" |
| // The second cull plane helps paint left and right different. |
| << " gl_MeshVerticesNV[0].gl_CullDistance[1] = 1.0;\n" |
| << " gl_MeshVerticesNV[1].gl_CullDistance[1] = 1.0;\n" |
| << " gl_MeshVerticesNV[2].gl_CullDistance[1] = 1.0;\n" |
| << " gl_MeshVerticesNV[3].gl_CullDistance[1] = -1.0;\n" |
| << " gl_MeshVerticesNV[4].gl_CullDistance[1] = -1.0;\n" |
| << " gl_MeshVerticesNV[5].gl_CullDistance[1] = -1.0;\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| // Fragment shader chooses color based on the second cull distance. |
| { |
| std::ostringstream frag; |
| frag |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " outColor = ((gl_CullDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| } |
| } |
| |
| TestInstance* CullDistanceCase::createInstance (Context& context) const |
| { |
| const IterationParams iterationParams = |
| { |
| getDefaultExtent(), // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| |
| // Must match the shader. |
| const tcu::Vec4 black (0.0f, 0.0f, 0.0f, 1.0f); |
| const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); |
| const tcu::Vec4 white (1.0f, 1.0f, 1.0f, 1.0f); |
| |
| return new QuadrantsInstance(context, iterationParams, blue, white, black, black); |
| } |
| |
| void CullDistanceCase::checkSupport (Context& context) const |
| { |
| MeshShaderBuiltinCase::checkSupport(context); |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE); |
| } |
| |
| // Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len). |
| // Supposes the height of the framebuffer is 1. |
| std::string triangleForPixel(const std::string& pixel, const std::string& len, const std::string& baseIndex) |
| { |
| std::ostringstream statements; |
| statements |
| << " const float imgWidth = float(" << len << ");\n" |
| << " const float pixWidth = (2.0 / imgWidth);\n" |
| << " const float halfPix = (pixWidth / 2.0);\n" |
| << " const float xCenter = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n" |
| << " const float xLeft = (xCenter - halfPix);\n" |
| << " const float xRight = (xCenter + halfPix);\n" |
| << " const uvec3 indices = uvec3(" << baseIndex << " + 0, " << baseIndex << " + 1, " << baseIndex << " + 2);\n" |
| << "\n" |
| << " gl_PrimitiveIndicesNV[indices.x] = indices.x;\n" |
| << " gl_PrimitiveIndicesNV[indices.y] = indices.y;\n" |
| << " gl_PrimitiveIndicesNV[indices.z] = indices.z;\n" |
| << "\n" |
| << " gl_MeshVerticesNV[indices.x].gl_Position = vec4(xLeft, 0.5, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[indices.y].gl_Position = vec4(xRight, 0.5, 0.0, 1.0);\n" |
| << " gl_MeshVerticesNV[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n" |
| ; |
| return statements.str(); |
| } |
| |
| // WorkGroupID builtin case. |
| class WorkGroupIdCase : public MeshShaderBuiltinCase |
| { |
| public: |
| WorkGroupIdCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded) |
| : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded) |
| , m_extent (getLinearExtent()) |
| {} |
| virtual ~WorkGroupIdCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| protected: |
| const VkExtent2D m_extent; |
| }; |
| |
| void WorkGroupIdCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| const std::string taskDataDecl = |
| "taskNV TaskData {\n" |
| " uint id;\n" |
| " uint size;\n" |
| "} td;\n" |
| ; |
| |
| // Mesh shader: each work group fills one pixel. |
| { |
| const std::string pixel = (m_taskNeeded ? "td.id" : "gl_WorkGroupID.x" ); |
| const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width) ); |
| |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\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" |
| << (m_taskNeeded ? ("in " + taskDataDecl) : "") |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 1u;\n" |
| << "\n" |
| << triangleForPixel(pixel, len, "0") |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| if (m_taskNeeded) |
| { |
| std::ostringstream task; |
| task |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=1) in;\n" |
| << "\n" |
| << "out " << taskDataDecl |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_TaskCountNV = 1u;\n" |
| << " td.id = gl_WorkGroupID.x;\n" |
| << " td.size = " << m_extent.width << ";\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("task") << glu::TaskSource(task.str()); |
| } |
| |
| // Basic fragment shader. |
| { |
| const auto frag = getBasicFragShader(); |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag); |
| } |
| } |
| |
| TestInstance* WorkGroupIdCase::createInstance (Context& context) const |
| { |
| // Must match the shader. |
| const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0)); |
| const IterationParams iterationParams = |
| { |
| m_extent, // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(m_extent.width), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| return new FullScreenColorInstance(context, iterationParams, expectedColors); |
| } |
| |
| // Variable to use. |
| enum class LocalInvocation { ID=0, INDEX }; |
| |
| // LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize. |
| class LocalInvocationCase : public MeshShaderBuiltinCase |
| { |
| public: |
| LocalInvocationCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded, LocalInvocation variable) |
| : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded) |
| , m_extent (getLinearExtent()) |
| , m_variable (variable) |
| {} |
| virtual ~LocalInvocationCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| protected: |
| const VkExtent2D m_extent; |
| const LocalInvocation m_variable; |
| }; |
| |
| void LocalInvocationCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // Invocation index to use. |
| const std::string localIndex = ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex"); |
| |
| // Task data. |
| std::ostringstream taskDataDecl; |
| taskDataDecl |
| << "taskNV TaskData {\n" |
| // indexNumber[x] == x |
| << " uint indexNumber[" << m_extent.width << "];\n" |
| << " uint size;\n" |
| << "} td;\n" |
| ; |
| const auto taskDataDeclStr = taskDataDecl.str(); |
| |
| // Mesh shader: each work group fills one pixel. |
| { |
| const std::string pixel = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex); |
| const std::string len = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x"); |
| const auto localSize = (m_taskNeeded ? 1u : m_extent.width); |
| const auto maxVert = localSize * 3u; |
| const std::string baseIndex = (m_taskNeeded ? "0" : "(" + localIndex + " * 3u)"); |
| |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=" << localSize << ") in;\n" |
| << "layout (triangles) out;\n" |
| << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n" |
| << "\n" |
| << (m_taskNeeded ? ("in " + taskDataDeclStr) : "") |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = " << localSize << ";\n" |
| << "\n" |
| << triangleForPixel(pixel, len, baseIndex) |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| if (m_taskNeeded) |
| { |
| std::ostringstream task; |
| task |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=" << m_extent.width << ") in;\n" |
| << "\n" |
| << "out " << taskDataDeclStr |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_TaskCountNV = " << m_extent.width << ";\n" |
| << " td.indexNumber[" << localIndex << "] = " << localIndex << ";\n" |
| << " td.size = gl_WorkGroupSize.x;\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("task") << glu::TaskSource(task.str()); |
| } |
| |
| // Basic fragment shader. |
| { |
| const auto frag = getBasicFragShader(); |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag); |
| } |
| } |
| |
| TestInstance* LocalInvocationCase::createInstance (Context& context) const |
| { |
| // Must match the shader. |
| const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0)); |
| const IterationParams iterationParams = |
| { |
| m_extent, // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| return new FullScreenColorInstance(context, iterationParams, expectedColors); |
| } |
| |
| // GlobalInvocationId builtin case. |
| class GlobalInvocationIdCase : public MeshShaderBuiltinCase |
| { |
| public: |
| GlobalInvocationIdCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded) |
| : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded) |
| , m_jobSize (getLargeJobSize()) |
| , m_extent {m_jobSize.numTasks * m_jobSize.localSize, 1u} |
| {} |
| virtual ~GlobalInvocationIdCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| protected: |
| const JobSize m_jobSize; |
| const VkExtent2D m_extent; |
| }; |
| |
| void GlobalInvocationIdCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| const auto& localSize = m_jobSize.localSize; |
| |
| // Task data. |
| std::ostringstream taskDataDecl; |
| taskDataDecl |
| << "taskNV TaskData {\n" |
| << " uint pixelId[" << localSize << "];\n" |
| << " uint size;\n" |
| << "} td;\n" |
| ; |
| const auto taskDataDeclStr = taskDataDecl.str(); |
| |
| // Mesh shader: each work group fills one pixel. |
| { |
| const std::string pixel = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x"); |
| const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width)); |
| const std::string baseIndex = "(gl_LocalInvocationIndex * 3u)"; |
| const auto maxVert = localSize * 3u; |
| |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=" << localSize << ") in;\n" |
| << "layout (triangles) out;\n" |
| << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n" |
| << "\n" |
| << (m_taskNeeded ? ("in " + taskDataDeclStr) : "") |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = " << localSize << ";\n" |
| << "\n" |
| << triangleForPixel(pixel, len, baseIndex) |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| if (m_taskNeeded) |
| { |
| std::ostringstream task; |
| task |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=" << localSize << ") in;\n" |
| << "\n" |
| << "out " << taskDataDeclStr |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_TaskCountNV = 1;\n" |
| << " td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n" |
| << " td.size = " << m_extent.width << ";\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("task") << glu::TaskSource(task.str()); |
| } |
| |
| // Basic fragment shader. |
| { |
| const auto frag = getBasicFragShader(); |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag); |
| } |
| } |
| |
| TestInstance* GlobalInvocationIdCase::createInstance (Context& context) const |
| { |
| // Must match the shader. |
| const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0)); |
| const IterationParams iterationParams = |
| { |
| m_extent, // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| getDefaultDrawCommands(m_jobSize.numTasks), // DrawCommandVec drawArgs; |
| false, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| return new FullScreenColorInstance(context, iterationParams, expectedColors); |
| } |
| |
| // DrawIndex builtin case. |
| class DrawIndexCase : public MeshShaderBuiltinCase |
| { |
| public: |
| DrawIndexCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded) |
| : MeshShaderBuiltinCase (testCtx, name, description, taskNeeded) |
| , m_extent (getLinearExtent()) |
| {} |
| virtual ~DrawIndexCase (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| protected: |
| const VkExtent2D m_extent; |
| }; |
| |
| void DrawIndexCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| const std::string taskDataDecl = |
| "taskNV TaskData {\n" |
| " uint id;\n" |
| " uint size;\n" |
| "} td;\n" |
| ; |
| |
| const auto drawIndex = "uint(gl_DrawID)"; |
| |
| // Mesh shader: each work group fills one pixel. |
| { |
| const std::string pixel = (m_taskNeeded ? "td.id" : drawIndex); |
| const std::string len = (m_taskNeeded ? "td.size" : de::toString(m_extent.width)); |
| |
| std::ostringstream mesh; |
| mesh |
| << "#version 460\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" |
| << (m_taskNeeded ? ("in " + taskDataDecl) : "") |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_PrimitiveCountNV = 1u;\n" |
| << "\n" |
| << triangleForPixel(pixel, len, "0") |
| << "}\n" |
| ; |
| programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()); |
| } |
| |
| if (m_taskNeeded) |
| { |
| std::ostringstream task; |
| task |
| << "#version 460\n" |
| << "#extension GL_NV_mesh_shader : enable\n" |
| << "\n" |
| << "layout (local_size_x=1) in;\n" |
| << "\n" |
| << "out " << taskDataDecl |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_TaskCountNV = 1u;\n" |
| << " td.id = " << drawIndex << ";\n" |
| << " td.size = " << m_extent.width << ";\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("task") << glu::TaskSource(task.str()); |
| } |
| |
| // Basic fragment shader. |
| { |
| const auto frag = getBasicFragShader(); |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag); |
| } |
| } |
| |
| TestInstance* DrawIndexCase::createInstance (Context& context) const |
| { |
| // Must match the shader. |
| const ColorVec expectedColors (1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0)); |
| const DrawCommandVec commands (m_extent.width, makeDrawMeshTasksIndirectCommandNV(1u, 0u)); |
| const IterationParams iterationParams = |
| { |
| m_extent, // VkExtent2D colorExtent; |
| 1u, // uint32_t numLayers; |
| commands, // DrawCommandVec drawArgs; |
| true, // bool indirect; |
| {}, // ViewportVec viewports; // If empty, a single default viewport is used. |
| }; |
| return new FullScreenColorInstance(context, iterationParams, expectedColors); |
| } |
| |
| } // anonymous |
| |
| tcu::TestCaseGroup* createMeshShaderBuiltinTests (tcu::TestContext& testCtx) |
| { |
| GroupPtr mainGroup (new tcu::TestCaseGroup(testCtx, "builtin", "Mesh Shader Builtin Tests")); |
| |
| mainGroup->addChild(new PositionCase (testCtx, "position", "")); |
| mainGroup->addChild(new PointSizeCase (testCtx, "point_size", "")); |
| mainGroup->addChild(new ClipDistanceCase (testCtx, "clip_distance", "")); |
| mainGroup->addChild(new CullDistanceCase (testCtx, "cull_distance", "")); |
| mainGroup->addChild(new PrimitiveIdCase (testCtx, "primitive_id_glsl", "", true/*glslFrag*/)); |
| mainGroup->addChild(new PrimitiveIdCase (testCtx, "primitive_id_spirv", "", false/*glslFrag*/)); |
| mainGroup->addChild(new LayerCase (testCtx, "layer", "", false/*shareVertices*/)); |
| mainGroup->addChild(new LayerCase (testCtx, "layer_shared", "", true/*shareVertices*/)); |
| mainGroup->addChild(new ViewportIndexCase (testCtx, "viewport_index", "", false/*shareVertices*/)); |
| mainGroup->addChild(new ViewportIndexCase (testCtx, "viewport_index_shared", "", true/*shareVertices*/)); |
| mainGroup->addChild(new WorkGroupIdCase (testCtx, "work_group_id_in_mesh", "", false/*taskNeeded*/)); |
| mainGroup->addChild(new WorkGroupIdCase (testCtx, "work_group_id_in_task", "", true/*taskNeeded*/)); |
| mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_id_in_mesh", "", false/*taskNeeded*/, LocalInvocation::ID)); |
| mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_id_in_task", "", true/*taskNeeded*/, LocalInvocation::ID)); |
| mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_index_in_task", "", true/*taskNeeded*/, LocalInvocation::INDEX)); |
| mainGroup->addChild(new LocalInvocationCase (testCtx, "local_invocation_index_in_mesh", "", false/*taskNeeded*/, LocalInvocation::INDEX)); |
| mainGroup->addChild(new GlobalInvocationIdCase (testCtx, "global_invocation_id_in_mesh", "", false/*taskNeeded*/)); |
| mainGroup->addChild(new GlobalInvocationIdCase (testCtx, "global_invocation_id_in_task", "", true/*taskNeeded*/)); |
| mainGroup->addChild(new DrawIndexCase (testCtx, "draw_index_in_mesh", "", false/*taskNeeded*/)); |
| mainGroup->addChild(new DrawIndexCase (testCtx, "draw_index_in_task", "", true/*taskNeeded*/)); |
| |
| return mainGroup.release(); |
| } |
| |
| } // MeshShader |
| } // vkt |