| /*------------------------------------------------------------------------ |
| * 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 Test for VK_EXT_multi_draw |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktDrawMultiExtTests.hpp" |
| |
| #include "vkTypeUtil.hpp" |
| #include "vkImageWithMemory.hpp" |
| #include "vkObjUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkBufferWithMemory.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkBarrierUtil.hpp" |
| |
| #include "tcuTexture.hpp" |
| #include "tcuMaybe.hpp" |
| #include "tcuImageCompare.hpp" |
| |
| #include "deUniquePtr.hpp" |
| #include "deMath.h" |
| #include "deRandom.hpp" |
| |
| #include <vector> |
| #include <sstream> |
| #include <algorithm> |
| #include <iterator> |
| #include <limits> |
| |
| using namespace vk; |
| |
| namespace vkt |
| { |
| namespace Draw |
| { |
| |
| namespace |
| { |
| |
| // Normal or indexed draws. |
| enum class DrawType { NORMAL = 0, INDEXED }; |
| |
| // How to apply the vertex offset in indexed draws. |
| enum class VertexOffsetType |
| { |
| MIXED = 0, // Do not use pVertexOffset and mix values in struct-indicated offsets. |
| CONSTANT_RANDOM, // Use a constant value for pVertexOffset and fill offset struct members with random values. |
| CONSTANT_PACK, // Use a constant value for pVertexOffset and a stride that removes the vertex offset member in structs. |
| }; |
| |
| // Triangle mesh type. |
| enum class MeshType { MOSAIC = 0, OVERLAPPING }; |
| |
| // Vertex offset parameters. |
| struct VertexOffsetParams |
| { |
| VertexOffsetType offsetType; |
| deUint32 offset; |
| }; |
| |
| // Test parameters. |
| struct TestParams |
| { |
| MeshType meshType; |
| DrawType drawType; |
| deUint32 drawCount; |
| deUint32 instanceCount; |
| deUint32 firstInstance; |
| deUint32 stride; |
| tcu::Maybe<VertexOffsetParams> vertexOffset; // Only used for indexed draws. |
| deUint32 seed; |
| bool useTessellation; |
| bool useGeometry; |
| bool multiview; |
| bool useDynamicRendering; |
| |
| deUint32 maxInstanceIndex () const |
| { |
| if (instanceCount == 0u) |
| return 0u; |
| return (firstInstance + instanceCount - 1u); |
| } |
| }; |
| |
| // For the color attachment. Must match what the fragment shader expects. |
| VkFormat getColorFormat () |
| { |
| return VK_FORMAT_R8G8B8A8_UINT; |
| } |
| |
| // Compatible with getColorFormat() but better when used with the image logging facilities. |
| VkFormat getVerificationFormat () |
| { |
| return VK_FORMAT_R8G8B8A8_UNORM; |
| } |
| |
| // Find a suitable format for the depth/stencil buffer. |
| VkFormat chooseDepthStencilFormat (const InstanceInterface& vki, VkPhysicalDevice physDev) |
| { |
| // The spec mandates support for one of these two formats. |
| const VkFormat candidates[] = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }; |
| |
| for (const auto& format : candidates) |
| { |
| const auto properties = getPhysicalDeviceFormatProperties(vki, physDev, format); |
| if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0u) |
| return format; |
| } |
| |
| TCU_FAIL("No suitable depth/stencil format found"); |
| return VK_FORMAT_UNDEFINED; // Unreachable. |
| } |
| |
| // Format used when verifying the stencil aspect. |
| VkFormat getStencilVerificationFormat () |
| { |
| return VK_FORMAT_S8_UINT; |
| } |
| |
| deUint32 getTriangleCount () |
| { |
| return 1024u; // This matches the minumum allowed limit for maxMultiDrawCount, so we can submit a single triangle per draw call. |
| } |
| |
| // Base class for creating triangles. |
| class TriangleGenerator |
| { |
| public: |
| // Append a new triangle for ID (x, y). |
| virtual void appendTriangle(deUint32 x, deUint32 y, std::vector<tcu::Vec4>& vertices) = 0; |
| }; |
| |
| // Class that helps creating triangle vertices for each framebuffer pixel, forming a mosaic of triangles. |
| class TriangleMosaicGenerator : public TriangleGenerator |
| { |
| private: |
| // Normalized width and height taking into account the framebuffer's width and height are two units (from -1 to 1). |
| float m_pixelWidth; |
| float m_pixelHeight; |
| |
| float m_deltaX; |
| float m_deltaY; |
| |
| public: |
| TriangleMosaicGenerator (deUint32 width, deUint32 height) |
| : m_pixelWidth (2.0f / static_cast<float>(width)) |
| , m_pixelHeight (2.0f / static_cast<float>(height)) |
| , m_deltaX (m_pixelWidth * 0.25f) |
| , m_deltaY (m_pixelHeight * 0.25f) |
| {} |
| |
| // Creates a triangle for framebuffer pixel (x, y) around its center. Appends the triangle vertices to the given list. |
| void appendTriangle(deUint32 x, deUint32 y, std::vector<tcu::Vec4>& vertices) override |
| { |
| // Pixel center. |
| const float coordX = (static_cast<float>(x) + 0.5f) * m_pixelWidth - 1.0f; |
| const float coordY = (static_cast<float>(y) + 0.5f) * m_pixelHeight - 1.0f; |
| |
| // Triangle around it. |
| const float topY = coordY - m_deltaY; |
| const float bottomY = coordY + m_deltaY; |
| |
| const float leftX = coordX - m_deltaX; |
| const float rightX = coordX + m_deltaX; |
| |
| // Note: clockwise. |
| vertices.emplace_back(leftX, bottomY, 0.0f, 1.0f); |
| vertices.emplace_back(coordX, topY, 0.0f, 1.0f); |
| vertices.emplace_back(rightX, bottomY, 0.0f, 1.0f); |
| } |
| }; |
| |
| // Class that helps create full-screen triangles that overlap each other. |
| // This generator will generate width*height full-screen triangles with decreasing depth from 0.75 to 0.25. |
| class TriangleOverlapGenerator : public TriangleGenerator |
| { |
| private: |
| // Normalized width and height taking into account the framebuffer's width and height are two units (from -1 to 1). |
| deUint32 m_width; |
| deUint32 m_totalPixels; |
| float m_depthStep; |
| |
| static constexpr float kMinDepth = 0.25f; |
| static constexpr float kMaxDepth = 0.75f; |
| static constexpr float kDepthRange = kMaxDepth - kMinDepth; |
| |
| public: |
| TriangleOverlapGenerator (deUint32 width, deUint32 height) |
| : m_width (width) |
| , m_totalPixels (width * height) |
| , m_depthStep (kDepthRange / static_cast<float>(m_totalPixels)) |
| {} |
| |
| // Creates full-screen triangle with 2D id (x, y) and decreasing depth with increasing ids. |
| void appendTriangle(deUint32 x, deUint32 y, std::vector<tcu::Vec4>& vertices) override |
| { |
| const auto pixelId = static_cast<float>(y * m_width + x); |
| const auto depth = kMaxDepth - m_depthStep * pixelId; |
| |
| // Note: clockwise. |
| vertices.emplace_back(-1.0f, -1.0f, depth, 1.0f); |
| vertices.emplace_back(4.0f, -1.0f, depth, 1.0f); |
| vertices.emplace_back(-1.0f, 4.0f, depth, 1.0f); |
| } |
| }; |
| |
| // Class that helps creating a suitable draw info vector. |
| class DrawInfoPacker |
| { |
| private: |
| DrawType m_drawType; |
| tcu::Maybe<VertexOffsetType> m_offsetType; // Offset type when m_drawType is DrawType::INDEXED. |
| deUint32 m_stride; // Desired stride. Must be zero or at least as big as the needed VkMultiDraw*InfoExt. |
| deUint32 m_extraBytes; // Used to match the desired stride. |
| de::Random m_random; // Used to generate random offsets. |
| deUint32 m_infoCount; // How many infos have we appended so far? |
| std::vector<deUint8> m_dataVec; // Data vector in generic form. |
| |
| // Are draws indexed and using the offset member of VkMultiDrawIndexedInfoEXT? |
| static bool indexedWithOffset (DrawType drawType, const tcu::Maybe<VertexOffsetType>& offsetType) |
| { |
| return (drawType == DrawType::INDEXED && *offsetType != VertexOffsetType::CONSTANT_PACK); |
| } |
| |
| // Size in bytes for the base structure used with the given draw type. |
| static deUint32 baseSize (DrawType drawType, const tcu::Maybe<VertexOffsetType>& offsetType) |
| { |
| return static_cast<deUint32>(indexedWithOffset(drawType, offsetType) ? sizeof(VkMultiDrawIndexedInfoEXT) : sizeof(VkMultiDrawInfoEXT)); |
| } |
| |
| // Number of extra bytes per entry according to the given stride. |
| static deUint32 calcExtraBytes (DrawType drawType, const tcu::Maybe<VertexOffsetType>& offsetType, deUint32 stride) |
| { |
| // Stride 0 is a special allowed case. |
| if (stride == 0u) |
| return 0u; |
| |
| const auto minStride = baseSize(drawType, offsetType); |
| DE_ASSERT(stride >= minStride); |
| return (stride - minStride); |
| } |
| |
| // Entry size in bytes taking into account the number of extra bytes due to stride. |
| deUint32 entrySize () const |
| { |
| return baseSize(m_drawType, m_offsetType) + m_extraBytes; |
| } |
| |
| public: |
| DrawInfoPacker (DrawType drawType, const tcu::Maybe<VertexOffsetType>& offsetType, deUint32 stride, deUint32 estimatedInfoCount, deUint32 seed) |
| : m_drawType (drawType) |
| , m_offsetType (offsetType) |
| , m_stride (stride) |
| , m_extraBytes (calcExtraBytes(drawType, offsetType, stride)) |
| , m_random (seed) |
| , m_infoCount (0u) |
| , m_dataVec () |
| { |
| // estimatedInfoCount is used to avoid excessive reallocation. |
| if (estimatedInfoCount > 0u) |
| m_dataVec.reserve(estimatedInfoCount * entrySize()); |
| } |
| |
| void addDrawInfo (deUint32 first, deUint32 count, deInt32 offset) |
| { |
| std::vector<deUint8> entry(entrySize(), 0); |
| |
| if (indexedWithOffset(m_drawType, m_offsetType)) |
| { |
| const auto usedOffset = ((*m_offsetType == VertexOffsetType::CONSTANT_RANDOM) ? m_random.getInt32() : offset); |
| const VkMultiDrawIndexedInfoEXT info = { first, count, usedOffset }; |
| deMemcpy(entry.data(), &info, sizeof(info)); |
| } |
| else |
| { |
| const VkMultiDrawInfoEXT info = { first, count }; |
| deMemcpy(entry.data(), &info, sizeof(info)); |
| } |
| |
| std::copy(begin(entry), end(entry), std::back_inserter(m_dataVec)); |
| ++m_infoCount; |
| } |
| |
| deUint32 drawInfoCount () const |
| { |
| return m_infoCount; |
| } |
| |
| const void* drawInfoData () const |
| { |
| return m_dataVec.data(); |
| } |
| |
| deUint32 stride () const |
| { |
| return m_stride; |
| } |
| }; |
| |
| class MultiDrawTest : public vkt::TestCase |
| { |
| public: |
| MultiDrawTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params); |
| virtual ~MultiDrawTest (void) {} |
| |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override; |
| void checkSupport (Context& context) const override; |
| |
| private: |
| TestParams m_params; |
| }; |
| |
| class MultiDrawInstance : public vkt::TestInstance |
| { |
| public: |
| MultiDrawInstance (Context& context, const TestParams& params); |
| virtual ~MultiDrawInstance (void) {} |
| |
| tcu::TestStatus iterate (void) override; |
| |
| private: |
| TestParams m_params; |
| }; |
| |
| MultiDrawTest::MultiDrawTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) |
| : vkt::TestCase (testCtx, name, description) |
| , m_params (params) |
| {} |
| |
| TestInstance* MultiDrawTest::createInstance (Context& context) const |
| { |
| return new MultiDrawInstance(context, m_params); |
| } |
| |
| void MultiDrawTest::checkSupport (Context& context) const |
| { |
| context.requireDeviceFunctionality("VK_EXT_multi_draw"); |
| |
| if (m_params.useTessellation) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER); |
| |
| if (m_params.useGeometry) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER); |
| |
| if (m_params.multiview) |
| { |
| const auto& multiviewFeatures = context.getMultiviewFeatures(); |
| |
| if (!multiviewFeatures.multiview) |
| TCU_THROW(NotSupportedError, "Multiview not supported"); |
| |
| if (m_params.useTessellation && !multiviewFeatures.multiviewTessellationShader) |
| TCU_THROW(NotSupportedError, "Multiview not supported with tesellation shaders"); |
| |
| if (m_params.useGeometry && !multiviewFeatures.multiviewGeometryShader) |
| TCU_THROW(NotSupportedError, "Multiview not supported with geometry shaders"); |
| } |
| |
| if (m_params.useDynamicRendering) |
| context.requireDeviceFunctionality("VK_KHR_dynamic_rendering"); |
| } |
| |
| void MultiDrawTest::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| // The general idea behind these tests is to have a 32x32 framebuffer with 1024 pixels and 1024 triangles to draw. |
| // |
| // When using a mosaic mesh, the tests will generally draw a single triangle around the center of each of these pixels. When |
| // using an overlapping mesh, each single triangle will cover the whole framebuffer using a different depth value, and the depth |
| // test will be enabled. |
| // |
| // The color of each triangle will depend on the instance index, the draw index and, when using multiview, the view index. This |
| // way, it's possible to draw those 1024 triangles with a single draw call or to draw each triangle with a separate draw call, |
| // with up to 1024 draw calls. Combinations in between are possible. |
| // |
| // With overlapping meshes, the resulting color buffer will be uniform in color. With mosaic meshes, it depends on the submitted |
| // draw count. In some cases, all pixels will be slightly different in color. |
| // |
| // The color buffer will be cleared to transparent black when beginning the render pass, and in some special cases some or all |
| // pixels will preserve that clear color because they will not be drawn into. This happens, for example, if the instance count |
| // or draw count is zero and in some cases of meshed geometry with stride zero. |
| // |
| // The output color for each pixel will: |
| // - Have the draw index split into the R and G components. |
| // - Have the instance index I stored into the B component as 255-I. |
| // |
| // In addition, the tests will use a depth/stencil buffer. The stencil buffer will be cleared to zero and the depth buffer to an |
| // appropriate initial value (0.0 or 1.0, depending on triangle order). The stencil component will be increased with each draw |
| // on each pixel. This will allow us to verify that not only the last draw for the last instance has set the proper color, but |
| // that all draw operations have taken place. |
| |
| // Make sure the blue channel can be calculated without issues. |
| DE_ASSERT(m_params.maxInstanceIndex() <= 255u); |
| |
| std::ostringstream vert; |
| vert |
| << "#version 460\n" |
| << (m_params.multiview ? "#extension GL_EXT_multiview : enable\n" : "") |
| << "\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << "\n" |
| << "layout (location=0) in vec4 inPos;\n" |
| << "layout (location=0) out uvec4 outColor;\n" |
| << "\n" |
| << "void main()\n" |
| << "{\n" |
| << " gl_Position = inPos;\n" |
| << " const uint uDrawIndex = uint(gl_DrawID);\n" |
| << " outColor.r = ((uDrawIndex >> 8u) & 0xFFu);\n" |
| << " outColor.g = ((uDrawIndex ) & 0xFFu);\n" |
| << " outColor.b = 255u - uint(gl_InstanceIndex);\n" |
| << " outColor.a = 255u" << (m_params.multiview ? " - uint(gl_ViewIndex)" : "") << ";\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("vert") << glu::VertexSource(vert.str()); |
| |
| std::ostringstream frag; |
| frag |
| << "#version 460\n" |
| << "\n" |
| << "layout (location=0) flat in uvec4 inColor;\n" |
| << "layout (location=0) out uvec4 outColor;\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " outColor = inColor;\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| |
| if (m_params.useTessellation) |
| { |
| std::ostringstream tesc; |
| tesc |
| << "#version 460\n" |
| << "\n" |
| << "layout (vertices=3) out;\n" |
| << "in gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_in[gl_MaxPatchVertices];\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_out[];\n" |
| << "\n" |
| << "layout (location=0) in uvec4 inColor[gl_MaxPatchVertices];\n" |
| << "layout (location=0) out uvec4 outColor[];\n" |
| << "\n" |
| << "void main (void)\n" |
| << "{\n" |
| << " gl_TessLevelInner[0] = 1.0;\n" |
| << " gl_TessLevelInner[1] = 1.0;\n" |
| << " gl_TessLevelOuter[0] = 1.0;\n" |
| << " gl_TessLevelOuter[1] = 1.0;\n" |
| << " gl_TessLevelOuter[2] = 1.0;\n" |
| << " gl_TessLevelOuter[3] = 1.0;\n" |
| << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| << " outColor[gl_InvocationID] = inColor[gl_InvocationID];\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("tesc") << glu::TessellationControlSource(tesc.str()); |
| |
| std::ostringstream tese; |
| tese |
| << "#version 460\n" |
| << "\n" |
| << "layout (triangles, fractional_odd_spacing, cw) in;\n" |
| << "in gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_in[gl_MaxPatchVertices];\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << "\n" |
| << "layout (location=0) in uvec4 inColor[gl_MaxPatchVertices];\n" |
| << "layout (location=0) out uvec4 outColor;\n" |
| << "\n" |
| << "void main (void)\n" |
| << "{\n" |
| << " gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +\n" |
| << " (gl_TessCoord.y * gl_in[1].gl_Position) +\n" |
| << " (gl_TessCoord.z * gl_in[2].gl_Position);\n" |
| << " outColor = inColor[0];\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(tese.str()); |
| } |
| |
| if (m_params.useGeometry) |
| { |
| std::ostringstream geom; |
| geom |
| << "#version 460\n" |
| << "\n" |
| << "layout (triangles) in;\n" |
| << "layout (triangle_strip, max_vertices=3) out;\n" |
| << "in gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_in[3];\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << "\n" |
| << "layout (location=0) in uvec4 inColor[3];\n" |
| << "layout (location=0) out uvec4 outColor;\n" |
| << "\n" |
| << "void main ()\n" |
| << "{\n" |
| << " gl_Position = gl_in[0].gl_Position; outColor = inColor[0]; EmitVertex();\n" |
| << " gl_Position = gl_in[1].gl_Position; outColor = inColor[1]; EmitVertex();\n" |
| << " gl_Position = gl_in[2].gl_Position; outColor = inColor[2]; EmitVertex();\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add("geom") << glu::GeometrySource(geom.str()); |
| } |
| } |
| |
| MultiDrawInstance::MultiDrawInstance (Context& context, const TestParams& params) |
| : vkt::TestInstance (context) |
| , m_params (params) |
| {} |
| |
| void appendPaddingVertices (std::vector<tcu::Vec4>& vertices, deUint32 count) |
| { |
| for (deUint32 i = 0u; i < count; ++i) |
| vertices.emplace_back(0.0f, 0.0f, 0.0f, 1.0f); |
| } |
| |
| // Creates a render pass with multiple subpasses, one per layer. |
| Move<VkRenderPass> makeMultidrawRenderPass (const DeviceInterface& vk, |
| VkDevice device, |
| VkFormat colorFormat, |
| VkFormat depthStencilFormat, |
| deUint32 layerCount) |
| { |
| const VkAttachmentDescription colorAttachmentDescription = |
| { |
| 0u, // VkAttachmentDescriptionFlags flags |
| colorFormat, // VkFormat format |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples |
| VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp |
| VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout |
| }; |
| |
| const VkAttachmentDescription depthStencilAttachmentDescription = |
| { |
| 0u, // VkAttachmentDescriptionFlags flags |
| depthStencilFormat, // VkFormat format |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples |
| VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp |
| VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp |
| VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp stencilLoadOp |
| VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp stencilStoreOp |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout |
| }; |
| |
| const std::vector<VkAttachmentDescription> attachmentDescriptions = { colorAttachmentDescription, depthStencilAttachmentDescription }; |
| const VkAttachmentReference colorAttachmentRef = makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| const VkAttachmentReference depthStencilAttachmentRef = makeAttachmentReference(1u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); |
| |
| const VkSubpassDescription subpassDescription = |
| { |
| 0u, // VkSubpassDescriptionFlags flags |
| VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint |
| 0u, // deUint32 inputAttachmentCount |
| nullptr, // const VkAttachmentReference* pInputAttachments |
| 1u, // deUint32 colorAttachmentCount |
| &colorAttachmentRef, // const VkAttachmentReference* pColorAttachments |
| nullptr, // const VkAttachmentReference* pResolveAttachments |
| &depthStencilAttachmentRef, // const VkAttachmentReference* pDepthStencilAttachment |
| 0u, // deUint32 preserveAttachmentCount |
| nullptr // const deUint32* pPreserveAttachments |
| }; |
| |
| std::vector<VkSubpassDescription> subpassDescriptions; |
| |
| subpassDescriptions.reserve(layerCount); |
| for (deUint32 subpassIdx = 0u; subpassIdx < layerCount; ++subpassIdx) |
| subpassDescriptions.push_back(subpassDescription); |
| |
| using MultiviewInfoPtr = de::MovePtr<VkRenderPassMultiviewCreateInfo>; |
| |
| MultiviewInfoPtr multiviewCreateInfo; |
| std::vector<deUint32> viewMasks; |
| |
| if (layerCount > 1u) |
| { |
| multiviewCreateInfo = MultiviewInfoPtr(new VkRenderPassMultiviewCreateInfo); |
| *multiviewCreateInfo = initVulkanStructure(); |
| |
| viewMasks.resize(subpassDescriptions.size()); |
| for (deUint32 subpassIdx = 0u; subpassIdx < static_cast<deUint32>(viewMasks.size()); ++subpassIdx) |
| viewMasks[subpassIdx] = (1u << subpassIdx); |
| |
| multiviewCreateInfo->subpassCount = static_cast<deUint32>(viewMasks.size()); |
| multiviewCreateInfo->pViewMasks = de::dataOrNull(viewMasks); |
| } |
| |
| // Dependencies between subpasses for color and depth/stencil read/writes. |
| std::vector<VkSubpassDependency> dependencies; |
| if (layerCount > 1u) |
| dependencies.reserve((layerCount - 1u) * 2u); |
| |
| const auto fragmentTestStages = (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); |
| const auto dsWrites = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
| const auto dsReadWrites = (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); |
| const auto colorStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| const auto colorWrites = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| const auto colorReadWrites = (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); |
| |
| for (deUint32 subpassIdx = 1u; subpassIdx < layerCount; ++subpassIdx) |
| { |
| const auto prev = subpassIdx - 1u; |
| |
| const VkSubpassDependency dsDep = |
| { |
| prev, // deUint32 srcSubpass; |
| subpassIdx, // deUint32 dstSubpass; |
| fragmentTestStages, // VkPipelineStageFlags srcStageMask; |
| fragmentTestStages, // VkPipelineStageFlags dstStageMask; |
| dsWrites, // VkAccessFlags srcAccessMask; |
| dsReadWrites, // VkAccessFlags dstAccessMask; |
| VK_DEPENDENCY_BY_REGION_BIT, // VkDependencyFlags dependencyFlags; |
| }; |
| dependencies.push_back(dsDep); |
| |
| const VkSubpassDependency colorDep = |
| { |
| prev, // deUint32 srcSubpass; |
| subpassIdx, // deUint32 dstSubpass; |
| colorStage, // VkPipelineStageFlags srcStageMask; |
| colorStage, // VkPipelineStageFlags dstStageMask; |
| colorWrites, // VkAccessFlags srcAccessMask; |
| colorReadWrites, // VkAccessFlags dstAccessMask; |
| VK_DEPENDENCY_BY_REGION_BIT, // VkDependencyFlags dependencyFlags; |
| }; |
| dependencies.push_back(colorDep); |
| } |
| |
| const VkRenderPassCreateInfo renderPassInfo = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType |
| multiviewCreateInfo.get(), // const void* pNext |
| 0u, // VkRenderPassCreateFlags flags |
| static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount |
| de::dataOrNull(attachmentDescriptions), // const VkAttachmentDescription* pAttachments |
| static_cast<deUint32>(subpassDescriptions.size()), // deUint32 subpassCount |
| de::dataOrNull(subpassDescriptions), // const VkSubpassDescription* pSubpasses |
| static_cast<deUint32>(dependencies.size()), // deUint32 dependencyCount |
| de::dataOrNull(dependencies), // const VkSubpassDependency* pDependencies |
| }; |
| |
| return createRenderPass(vk, device, &renderPassInfo, nullptr); |
| } |
| |
| tcu::TestStatus MultiDrawInstance::iterate (void) |
| { |
| const auto& vki = m_context.getInstanceInterface(); |
| const auto physDev = m_context.getPhysicalDevice(); |
| const auto& vkd = m_context.getDeviceInterface(); |
| const auto device = m_context.getDevice(); |
| auto& alloc = m_context.getDefaultAllocator(); |
| const auto queue = m_context.getUniversalQueue(); |
| const auto qIndex = m_context.getUniversalQueueFamilyIndex(); |
| |
| const auto colorFormat = getColorFormat(); |
| const auto dsFormat = chooseDepthStencilFormat(vki, physDev); |
| const auto tcuColorFormat = mapVkFormat(colorFormat); |
| const auto triangleCount = getTriangleCount(); |
| const auto imageDim = static_cast<deUint32>(deSqrt(static_cast<double>(triangleCount))); |
| const auto imageExtent = makeExtent3D(imageDim, imageDim, 1u); |
| const auto imageLayers = (m_params.multiview ? 2u : 1u); |
| const auto imageViewType = ((imageLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D); |
| const auto colorUsage = (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| const auto dsUsage = (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); |
| const auto pixelCount = imageExtent.width * imageExtent.height; |
| const auto vertexCount = pixelCount * 3u; // Triangle list. |
| const auto isIndexed = (m_params.drawType == DrawType::INDEXED); |
| const auto isMixedMode = (isIndexed && m_params.vertexOffset && m_params.vertexOffset->offsetType == VertexOffsetType::MIXED); |
| const auto extraVertices = (m_params.vertexOffset ? m_params.vertexOffset->offset : 0u); |
| const auto isMosaic = (m_params.meshType == MeshType::MOSAIC); |
| |
| // Make sure we're providing a vertex offset for indexed cases. |
| DE_ASSERT(!isIndexed || static_cast<bool>(m_params.vertexOffset)); |
| |
| // Make sure overlapping draws use a single instance. |
| DE_ASSERT(isMosaic || m_params.instanceCount <= 1u); |
| |
| // Color buffer. |
| const VkImageCreateInfo imageCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkImageCreateFlags flags; |
| VK_IMAGE_TYPE_2D, // VkImageType imageType; |
| colorFormat, // VkFormat format; |
| imageExtent, // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| imageLayers, // deUint32 arrayLayers; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| colorUsage, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 0u, // deUint32 queueFamilyIndexCount; |
| nullptr, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| ImageWithMemory colorBuffer (vkd, device, alloc, imageCreateInfo, MemoryRequirement::Any); |
| const auto colorSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, imageLayers); |
| const auto colorBufferView = makeImageView(vkd, device, colorBuffer.get(), imageViewType, colorFormat, colorSubresourceRange); |
| |
| // Depth/stencil buffer. |
| const VkImageCreateInfo dsCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkImageCreateFlags flags; |
| VK_IMAGE_TYPE_2D, // VkImageType imageType; |
| dsFormat, // VkFormat format; |
| imageExtent, // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| imageLayers, // deUint32 arrayLayers; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| dsUsage, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 0u, // deUint32 queueFamilyIndexCount; |
| nullptr, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| ImageWithMemory dsBuffer (vkd, device, alloc, dsCreateInfo, MemoryRequirement::Any); |
| const auto dsSubresourceRange = makeImageSubresourceRange((VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT), 0u, 1u, 0u, imageLayers); |
| const auto dsBufferView = makeImageView(vkd, device, dsBuffer.get(), imageViewType, dsFormat, dsSubresourceRange); |
| |
| // Output buffers to verify attachments. |
| using BufferWithMemoryPtr = de::MovePtr<BufferWithMemory>; |
| |
| // Buffers to read color attachment. |
| const auto outputBufferSize = pixelCount * static_cast<VkDeviceSize>(tcu::getPixelSize(tcuColorFormat)); |
| const auto bufferCreateInfo = makeBufferCreateInfo(outputBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| std::vector<BufferWithMemoryPtr> outputBuffers; |
| for (deUint32 i = 0u; i < imageLayers; ++i) |
| outputBuffers.push_back(BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible))); |
| |
| // Buffer to read depth/stencil attachment. Note: this supposes we'll only copy the stencil aspect. See below. |
| const auto tcuStencilFmt = mapVkFormat(getStencilVerificationFormat()); |
| const auto stencilOutBufferSize = pixelCount * static_cast<VkDeviceSize>(tcu::getPixelSize(tcuStencilFmt)); |
| const auto stencilOutCreateInfo = makeBufferCreateInfo(stencilOutBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| std::vector<BufferWithMemoryPtr> stencilOutBuffers; |
| for (deUint32 i = 0u; i < imageLayers; ++i) |
| stencilOutBuffers.push_back(BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, stencilOutCreateInfo, MemoryRequirement::HostVisible))); |
| |
| // Shaders. |
| const auto vertModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u); |
| const auto fragModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"), 0u); |
| Move<VkShaderModule> tescModule; |
| Move<VkShaderModule> teseModule; |
| Move<VkShaderModule> geomModule; |
| |
| if (m_params.useGeometry) |
| geomModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("geom"), 0u); |
| |
| if (m_params.useTessellation) |
| { |
| tescModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("tesc"), 0u); |
| teseModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("tese"), 0u); |
| } |
| |
| DescriptorSetLayoutBuilder layoutBuilder; |
| const auto descriptorSetLayout = layoutBuilder.build(vkd, device); |
| const auto pipelineLayout = makePipelineLayout(vkd, device, descriptorSetLayout.get()); |
| |
| Move<VkRenderPass> renderPass; |
| Move<VkFramebuffer> framebuffer; |
| |
| // Render pass and Framebuffer (note layers is always 1 as required by the spec). |
| if (!m_params.useDynamicRendering) |
| { |
| renderPass = makeMultidrawRenderPass(vkd, device, colorFormat, dsFormat, imageLayers); |
| const std::vector<VkImageView> attachments { colorBufferView.get(), dsBufferView.get() }; |
| framebuffer = makeFramebuffer(vkd, device, renderPass.get(), static_cast<deUint32>(attachments.size()), de::dataOrNull(attachments), imageExtent.width, imageExtent.height, 1u); |
| } |
| |
| // Viewports and scissors. |
| const auto viewport = makeViewport(imageExtent); |
| const std::vector<VkViewport> viewports (1u, viewport); |
| const auto scissor = makeRect2D(imageExtent); |
| const std::vector<VkRect2D> scissors (1u, scissor); |
| |
| // Indexed draws will have triangle vertices in reverse order. See index buffer creation below. |
| const auto frontFace = (isIndexed ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE); |
| const VkPipelineRasterizationStateCreateInfo rasterizationInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkPipelineRasterizationStateCreateFlags flags; |
| VK_FALSE, // VkBool32 depthClampEnable; |
| VK_FALSE, // VkBool32 rasterizerDiscardEnable; |
| VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode; |
| VK_CULL_MODE_BACK_BIT, // VkCullModeFlags cullMode; |
| frontFace, // VkFrontFace frontFace; |
| VK_FALSE, // VkBool32 depthBiasEnable; |
| 0.0f, // float depthBiasConstantFactor; |
| 0.0f, // float depthBiasClamp; |
| 0.0f, // float depthBiasSlopeFactor; |
| 1.0f, // float lineWidth; |
| }; |
| |
| const auto frontStencilState = makeStencilOpState(VK_STENCIL_OP_KEEP, VK_STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, 0xFFu, 0xFFu, 0u); |
| const auto backStencilState = makeStencilOpState(VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_NEVER, 0xFFu, 0xFFu, 0u); |
| const auto depthTestEnable = (isMosaic ? VK_FALSE : VK_TRUE); |
| const auto depthWriteEnable = depthTestEnable; |
| const auto depthCompareOp = (isMosaic ? VK_COMPARE_OP_ALWAYS : (isIndexed ? VK_COMPARE_OP_GREATER : VK_COMPARE_OP_LESS)); |
| |
| const VkPipelineDepthStencilStateCreateInfo depthStencilInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkPipelineDepthStencilStateCreateFlags flags; |
| depthTestEnable, // VkBool32 depthTestEnable; |
| depthWriteEnable, // VkBool32 depthWriteEnable; |
| depthCompareOp, // VkCompareOp depthCompareOp; |
| VK_FALSE, // VkBool32 depthBoundsTestEnable; |
| VK_TRUE, // VkBool32 stencilTestEnable; |
| frontStencilState, // VkStencilOpState front; |
| backStencilState, // VkStencilOpState back; |
| 0.0f, // float minDepthBounds; |
| 1.0f, // float maxDepthBounds; |
| }; |
| |
| vk::VkPipelineRenderingCreateInfoKHR renderingCreateInfo |
| { |
| vk::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, |
| DE_NULL, |
| 0u, |
| 1u, |
| &colorFormat, |
| dsFormat, |
| dsFormat |
| }; |
| |
| vk::VkPipelineRenderingCreateInfoKHR* nextPtr = nullptr; |
| if (m_params.useDynamicRendering) |
| nextPtr = &renderingCreateInfo; |
| |
| const auto primitiveTopology = (m_params.useTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); |
| const auto patchControlPoints = (m_params.useTessellation ? 3u : 0u); |
| |
| // Pipelines. |
| std::vector<Move<VkPipeline>> pipelines; |
| pipelines.reserve(imageLayers); |
| for (deUint32 subpassIdx = 0u; subpassIdx < imageLayers; ++subpassIdx) |
| { |
| pipelines.emplace_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(), |
| vertModule.get(), tescModule.get(), teseModule.get(), geomModule.get(), fragModule.get(), |
| renderPass.get(), viewports, scissors, primitiveTopology, subpassIdx, patchControlPoints, |
| nullptr/*vertexInputStateCreateInfo*/, &rasterizationInfo, nullptr/*multisampleStateCreateInfo*/, &depthStencilInfo, |
| nullptr/*colorBlendStateCreateInfo*/, nullptr/*dynamicStateCreateInfo*/, nextPtr)); |
| } |
| |
| // Command pool and buffer. |
| const auto cmdPool = makeCommandPool(vkd, device, qIndex); |
| const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| const auto cmdBuffer = cmdBufferPtr.get(); |
| |
| // Create vertex buffer. |
| std::vector<tcu::Vec4> triangleVertices; |
| triangleVertices.reserve(vertexCount + extraVertices); |
| |
| // Vertex count per draw call. |
| const bool atLeastOneDraw = (m_params.drawCount > 0u); |
| const bool moreThanOneDraw = (m_params.drawCount > 1u); |
| const auto trianglesPerDraw = (atLeastOneDraw ? pixelCount / m_params.drawCount : 0u); |
| const auto verticesPerDraw = trianglesPerDraw * 3u; |
| |
| if (atLeastOneDraw) |
| DE_ASSERT(pixelCount % m_params.drawCount == 0u); |
| |
| { |
| using TriangleGeneratorPtr = de::MovePtr<TriangleGenerator>; |
| TriangleGeneratorPtr triangleGen; |
| |
| if (m_params.meshType == MeshType::MOSAIC) |
| triangleGen = TriangleGeneratorPtr(new TriangleMosaicGenerator(imageExtent.width, imageExtent.height)); |
| else if (m_params.meshType == MeshType::OVERLAPPING) |
| triangleGen = TriangleGeneratorPtr(new TriangleOverlapGenerator(imageExtent.width, imageExtent.height)); |
| else |
| DE_ASSERT(false); |
| |
| // When applying a vertex offset in nonmixed modes, there will be a few extra vertices at the start of the vertex buffer. |
| if (isIndexed && !isMixedMode) |
| appendPaddingVertices(triangleVertices, extraVertices); |
| |
| for (deUint32 y = 0u; y < imageExtent.height; ++y) |
| for (deUint32 x = 0u; x < imageExtent.width; ++x) |
| { |
| // When applying a vertex offset in mixed mode, there will be some extra padding between the triangles for the first |
| // block and the rest, so that the vertex offset will not be constant in all draw info structures. This way, the first |
| // triangles will always have offset zero, and the number of them depends on the given draw count. |
| const auto pixelIndex = y * imageExtent.width + x; |
| if (isIndexed && isMixedMode && moreThanOneDraw && pixelIndex == trianglesPerDraw) |
| appendPaddingVertices(triangleVertices, extraVertices); |
| |
| triangleGen->appendTriangle(x, y, triangleVertices); |
| } |
| } |
| |
| const auto vertexBufferSize = static_cast<VkDeviceSize>(de::dataSize(triangleVertices)); |
| const auto vertexBufferInfo = makeBufferCreateInfo(vertexBufferSize, (VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); |
| BufferWithMemory vertexBuffer (vkd, device, alloc, vertexBufferInfo, MemoryRequirement::HostVisible); |
| auto& vertexBufferAlloc = vertexBuffer.getAllocation(); |
| const auto vertexBufferOffset = vertexBufferAlloc.getOffset(); |
| void* vertexBufferData = vertexBufferAlloc.getHostPtr(); |
| |
| deMemcpy(vertexBufferData, triangleVertices.data(), de::dataSize(triangleVertices)); |
| flushAlloc(vkd, device, vertexBufferAlloc); |
| |
| // Index buffer if needed. |
| de::MovePtr<BufferWithMemory> indexBuffer; |
| VkDeviceSize indexBufferOffset = 0ull; |
| |
| if (isIndexed) |
| { |
| // Indices will be given in reverse order, so they effectively also make the triangles have reverse winding order. |
| std::vector<deUint32> indices; |
| indices.reserve(vertexCount); |
| for (deUint32 i = 0u; i < vertexCount; ++i) |
| indices.push_back(vertexCount - i - 1u); |
| |
| const auto indexBufferSize = static_cast<VkDeviceSize>(de::dataSize(indices)); |
| const auto indexBufferInfo = makeBufferCreateInfo(indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); |
| indexBuffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vkd, device, alloc, indexBufferInfo, MemoryRequirement::HostVisible)); |
| auto& indexBufferAlloc = indexBuffer->getAllocation(); |
| indexBufferOffset = indexBufferAlloc.getOffset(); |
| void* indexBufferData = indexBufferAlloc.getHostPtr(); |
| |
| deMemcpy(indexBufferData, indices.data(), de::dataSize(indices)); |
| flushAlloc(vkd, device, indexBufferAlloc); |
| } |
| |
| // Prepare draw information. |
| const auto offsetType = (m_params.vertexOffset ? m_params.vertexOffset->offsetType : tcu::nothing<VertexOffsetType>()); |
| const auto vertexOffset = static_cast<deInt32>(extraVertices); |
| |
| DrawInfoPacker drawInfos(m_params.drawType, offsetType, m_params.stride, m_params.drawCount, m_params.seed); |
| |
| if (m_params.drawCount > 0u) |
| { |
| deUint32 vertexIndex = 0u; |
| for (deUint32 drawIdx = 0u; drawIdx < m_params.drawCount; ++drawIdx) |
| { |
| // For indexed draws in mixed offset mode, taking into account vertex indices have been stored in reversed order and |
| // there may be a padding in the vertex buffer after the first verticesPerDraw vertices, we need to use offset 0 in the |
| // last draw call. That draw will contain the indices for the first verticesPerDraw vertices, which are stored without |
| // any offset, while other draw calls will use indices which are off by extraVertices vertices. This will make sure not |
| // every draw call will use the same offset and the implementation handles that. |
| const auto drawOffset = ((isIndexed && (!isMixedMode || (moreThanOneDraw && drawIdx < m_params.drawCount - 1u))) ? vertexOffset : 0); |
| drawInfos.addDrawInfo(vertexIndex, verticesPerDraw, drawOffset); |
| vertexIndex += verticesPerDraw; |
| } |
| } |
| |
| beginCommandBuffer(vkd, cmdBuffer); |
| |
| // Draw stuff. |
| std::vector<VkClearValue> clearValues; |
| clearValues.reserve(2u); |
| clearValues.push_back(makeClearValueColorU32(0u, 0u, 0u, 0u)); |
| clearValues.push_back(makeClearValueDepthStencil(((isMosaic || isIndexed) ? 0.0f : 1.0f), 0u)); |
| |
| if (m_params.useDynamicRendering) |
| { |
| // Transition color and depth stencil attachment to the proper initial layout for dynamic rendering |
| const auto colorPreBarrier = makeImageMemoryBarrier( |
| 0u, |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| colorBuffer.get(), colorSubresourceRange); |
| |
| vkd.cmdPipelineBarrier( |
| cmdBuffer, |
| VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| 0u, 0u, nullptr, 0u, nullptr, 1u, &colorPreBarrier); |
| |
| const auto dsPreBarrier = makeImageMemoryBarrier( |
| 0u, |
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, |
| dsBuffer.get(), dsSubresourceRange); |
| |
| vkd.cmdPipelineBarrier( |
| cmdBuffer, |
| VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, |
| (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT), |
| 0u, 0u, nullptr, 0u, nullptr, 1u, &dsPreBarrier); |
| |
| beginRendering(vkd, cmdBuffer, *colorBufferView, *dsBufferView, true, scissor, clearValues[0], clearValues[1], |
| vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, |
| VK_ATTACHMENT_LOAD_OP_CLEAR); |
| } |
| else |
| beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissor, static_cast<deUint32>(clearValues.size()), de::dataOrNull(clearValues)); |
| |
| for (deUint32 layerIdx = 0u; layerIdx < imageLayers; ++layerIdx) |
| { |
| if (layerIdx > 0u) |
| vkd.cmdNextSubpass(cmdBuffer, VK_SUBPASS_CONTENTS_INLINE); |
| |
| vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[layerIdx].get()); |
| vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset); |
| if (isIndexed) |
| vkd.cmdBindIndexBuffer(cmdBuffer, indexBuffer->get(), indexBufferOffset, VK_INDEX_TYPE_UINT32); |
| |
| if (isIndexed) |
| { |
| const auto drawInfoPtr = reinterpret_cast<const VkMultiDrawIndexedInfoEXT*>(drawInfos.drawInfoData()); |
| const auto offsetPtr = (isMixedMode ? nullptr : &vertexOffset); |
| vkd.cmdDrawMultiIndexedEXT(cmdBuffer, drawInfos.drawInfoCount(), drawInfoPtr, m_params.instanceCount, m_params.firstInstance, drawInfos.stride(), offsetPtr); |
| } |
| else |
| { |
| const auto drawInfoPtr = reinterpret_cast<const VkMultiDrawInfoEXT*>(drawInfos.drawInfoData()); |
| vkd.cmdDrawMultiEXT(cmdBuffer, drawInfos.drawInfoCount(), drawInfoPtr, m_params.instanceCount, m_params.firstInstance, drawInfos.stride()); |
| } |
| } |
| |
| if (m_params.useDynamicRendering) |
| endRendering(vkd, cmdBuffer); |
| else |
| endRenderPass(vkd, cmdBuffer); |
| |
| // Prepare images for copying. |
| const auto colorBufferBarrier = makeImageMemoryBarrier( |
| 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(), colorSubresourceRange); |
| vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &colorBufferBarrier); |
| |
| const auto dsBufferBarrier = makeImageMemoryBarrier( |
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| dsBuffer.get(), dsSubresourceRange); |
| vkd.cmdPipelineBarrier(cmdBuffer, (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT), VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &dsBufferBarrier); |
| |
| // Copy images to output buffers. |
| for (deUint32 layerIdx = 0u; layerIdx < imageLayers; ++layerIdx) |
| { |
| const auto colorSubresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, layerIdx, 1u); |
| const auto colorCopyRegion = makeBufferImageCopy(imageExtent, colorSubresourceLayers); |
| vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outputBuffers[layerIdx]->get(), 1u, &colorCopyRegion); |
| } |
| |
| // Note: this only copies the stencil aspect. See stencilOutBuffer creation. |
| for (deUint32 layerIdx = 0u; layerIdx < imageLayers; ++layerIdx) |
| { |
| const auto stencilSubresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, layerIdx, 1u); |
| const auto stencilCopyRegion = makeBufferImageCopy(imageExtent, stencilSubresourceLayers); |
| vkd.cmdCopyImageToBuffer(cmdBuffer, dsBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stencilOutBuffers[layerIdx]->get(), 1u, &stencilCopyRegion); |
| } |
| |
| // Prepare buffers for host reading. |
| const auto outputBufferBarrier = 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, &outputBufferBarrier, 0u, nullptr, 0u, nullptr); |
| |
| endCommandBuffer(vkd, cmdBuffer); |
| submitCommandsAndWait(vkd, device, queue, cmdBuffer); |
| |
| // Read output buffers and verify their contents. |
| |
| // With stride zero, mosaic meshes increment the stencil buffer as many times as draw operations for affected pixels and |
| // overlapping meshes increment the stencil buffer only in the first draw operation (the rest fail the depth test) as many times |
| // as triangles per draw. |
| // |
| // With nonzero stride, mosaic meshes increment the stencil buffer once per pixel. Overlapping meshes increment it once per |
| // triangle. |
| const auto stencilIncrements = ((m_params.stride == 0u) |
| ? (isMosaic ? drawInfos.drawInfoCount() : trianglesPerDraw) |
| : (isMosaic ? 1u : triangleCount)); |
| const auto maxInstanceIndex = m_params.maxInstanceIndex(); |
| const auto colorVerificationFormat = mapVkFormat(getVerificationFormat()); |
| const auto iWidth = static_cast<int>(imageExtent.width); |
| const auto iHeight = static_cast<int>(imageExtent.height); |
| auto& log = m_context.getTestContext().getLog(); |
| const auto logMode = tcu::CompareLogMode::COMPARE_LOG_ON_ERROR; |
| |
| for (deUint32 layerIdx = 0u; layerIdx < imageLayers; ++layerIdx) |
| { |
| auto& outputBufferAlloc = outputBuffers[layerIdx]->getAllocation(); |
| invalidateAlloc(vkd, device, outputBufferAlloc); |
| const void* outputBufferData = outputBufferAlloc.getHostPtr(); |
| |
| auto& stencilOutBufferAlloc = stencilOutBuffers[layerIdx]->getAllocation(); |
| invalidateAlloc(vkd, device, stencilOutBufferAlloc); |
| const void* stencilOutBufferData = stencilOutBufferAlloc.getHostPtr(); |
| |
| tcu::ConstPixelBufferAccess colorAccess (colorVerificationFormat, iWidth, iHeight, 1, outputBufferData); |
| tcu::ConstPixelBufferAccess stencilAccess (tcuStencilFmt, iWidth, iHeight, 1, stencilOutBufferData); |
| |
| // Generate reference images. |
| tcu::TextureLevel refColorLevel (colorVerificationFormat, iWidth, iHeight); |
| tcu::PixelBufferAccess refColorAccess = refColorLevel.getAccess(); |
| tcu::TextureLevel refStencilLevel (tcuStencilFmt, iWidth, iHeight); |
| tcu::PixelBufferAccess refStencilAccess = refStencilLevel.getAccess(); |
| tcu::IVec4 referenceColor; |
| int referenceStencil; |
| |
| for (int y = 0; y < iHeight; ++y) |
| for (int x = 0; x < iWidth; ++x) |
| { |
| const auto pixelNumber = static_cast<deUint32>(y * iWidth + x); |
| const auto triangleIndex = (isIndexed ? (pixelCount - 1u - pixelNumber) : pixelNumber); // Reverse order for indexed draws. |
| |
| if (m_params.instanceCount == 0u || drawInfos.drawInfoCount() == 0u || |
| (m_params.stride == 0u && triangleIndex >= trianglesPerDraw && isMosaic)) |
| { |
| // Some pixels may not be drawn into when there are no instances or draws, or when the stride is zero in mosaic mode. |
| referenceColor = tcu::IVec4(0, 0, 0, 0); |
| referenceStencil = 0; |
| } |
| else |
| { |
| // This must match the vertex shader. |
| // |
| // With stride zero, the same block is drawn over and over again in each draw call. This affects both the draw index and |
| // the values in the depth/stencil buffer and, with overlapping meshes, only the first draw passes the depth test. |
| // |
| // With nonzero stride, the draw index depends on the triangle index and the number of triangles per draw and, for |
| // overlapping meshes, the draw index is always the last one. |
| const auto drawIndex = (m_params.stride == 0u |
| ? (isMosaic ? (drawInfos.drawInfoCount() - 1u) : 0u) |
| : (isMosaic ? (triangleIndex / trianglesPerDraw) : (drawInfos.drawInfoCount() - 1u))); |
| referenceColor = tcu::IVec4( |
| static_cast<int>((drawIndex >> 8) & 0xFFu), |
| static_cast<int>((drawIndex ) & 0xFFu), |
| static_cast<int>(255u - maxInstanceIndex), |
| static_cast<int>(255u - layerIdx)); |
| |
| referenceStencil = static_cast<int>((m_params.instanceCount * stencilIncrements) % 256u); // VK_STENCIL_OP_INCREMENT_AND_WRAP. |
| } |
| |
| refColorAccess.setPixel(referenceColor, x, y); |
| refStencilAccess.setPixStencil(referenceStencil, x, y); |
| } |
| |
| const auto layerIdxStr = de::toString(layerIdx); |
| const auto colorSetName = "ColorTestResultLayer" + layerIdxStr; |
| const auto stencilSetName = "StencilTestResultLayer" + layerIdxStr; |
| |
| if (!tcu::intThresholdCompare(log, colorSetName.c_str(), "", refColorAccess, colorAccess, tcu::UVec4(0u, 0u, 0u, 0u), logMode)) |
| return tcu::TestStatus::fail("Color image comparison failed; check log for more details"); |
| |
| if (!tcu::dsThresholdCompare(log, stencilSetName.c_str(), "", refStencilAccess, stencilAccess, 0.0f, logMode)) |
| return tcu::TestStatus::fail("Stencil image comparison failed; check log for more details"); |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| } // anonymous |
| |
| tcu::TestCaseGroup* createDrawMultiExtTests (tcu::TestContext& testCtx, bool useDynamicRendering) |
| { |
| using GroupPtr = de::MovePtr<tcu::TestCaseGroup>; |
| |
| GroupPtr drawMultiGroup (new tcu::TestCaseGroup(testCtx, "multi_draw", "VK_EXT_multi_draw tests")); |
| |
| const struct |
| { |
| MeshType meshType; |
| const char* name; |
| } meshTypeCases[] = |
| { |
| { MeshType::MOSAIC, "mosaic" }, |
| { MeshType::OVERLAPPING, "overlapping" }, |
| }; |
| |
| const struct |
| { |
| DrawType drawType; |
| const char* name; |
| } drawTypeCases[] = |
| { |
| { DrawType::NORMAL, "normal" }, |
| { DrawType::INDEXED, "indexed" }, |
| }; |
| |
| const struct |
| { |
| tcu::Maybe<VertexOffsetType> vertexOffsetType; |
| const char* name; |
| } offsetTypeCases[] = |
| { |
| { tcu::nothing<VertexOffsetType>(), "" }, |
| { VertexOffsetType::MIXED, "mixed" }, |
| { VertexOffsetType::CONSTANT_RANDOM, "random" }, |
| { VertexOffsetType::CONSTANT_PACK, "packed" }, |
| }; |
| |
| const struct |
| { |
| deUint32 drawCount; |
| const char* name; |
| } drawCountCases[] = |
| { |
| { 0u, "no_draws" }, |
| { 1u, "one_draw" }, |
| { 16u, "16_draws" }, |
| { getTriangleCount(), "max_draws" }, |
| }; |
| |
| const struct |
| { |
| int extraBytes; |
| const char* name; |
| } strideCases[] = |
| { |
| { -1, "stride_zero" }, |
| { 0, "standard_stride" }, |
| { 4, "stride_extra_4" }, |
| { 12, "stride_extra_12" }, |
| }; |
| |
| const struct |
| { |
| deUint32 firstInstance; |
| deUint32 instanceCount; |
| const char* name; |
| } instanceCases[] = |
| { |
| { 0u, 0u, "no_instances" }, |
| { 0u, 1u, "1_instance" }, |
| { 0u, 10u, "10_instances" }, |
| { 3u, 2u, "2_instances_base_3" }, |
| }; |
| |
| const struct |
| { |
| bool useTessellation; |
| bool useGeometry; |
| const char* name; |
| } shaderCases[] = |
| { |
| { false, false, "vert_only" }, |
| { false, true, "with_geom" }, |
| { true, false, "with_tess" }, |
| { true, true, "tess_geom" }, |
| }; |
| |
| const struct |
| { |
| bool multiview; |
| const char* name; |
| } multiviewCases[] = |
| { |
| { false, "single_view" }, |
| { true, "multiview" }, |
| }; |
| |
| constexpr deUint32 kSeed = 1621260419u; |
| |
| for (const auto& meshTypeCase : meshTypeCases) |
| { |
| GroupPtr meshTypeGroup(new tcu::TestCaseGroup(testCtx, meshTypeCase.name, "")); |
| |
| for (const auto& drawTypeCase : drawTypeCases) |
| { |
| for (const auto& offsetTypeCase : offsetTypeCases) |
| { |
| const auto hasOffsetType = static_cast<bool>(offsetTypeCase.vertexOffsetType); |
| if ((drawTypeCase.drawType == DrawType::NORMAL && hasOffsetType) || |
| (drawTypeCase.drawType == DrawType::INDEXED && !hasOffsetType)) |
| { |
| continue; |
| } |
| |
| std::string drawGroupName = drawTypeCase.name; |
| if (hasOffsetType) |
| drawGroupName += std::string("_") + offsetTypeCase.name; |
| |
| GroupPtr drawTypeGroup(new tcu::TestCaseGroup(testCtx, drawGroupName.c_str(), "")); |
| |
| for (const auto& drawCountCase : drawCountCases) |
| { |
| GroupPtr drawCountGroup(new tcu::TestCaseGroup(testCtx, drawCountCase.name, "")); |
| |
| for (const auto& strideCase : strideCases) |
| { |
| GroupPtr strideGroup(new tcu::TestCaseGroup(testCtx, strideCase.name, "")); |
| |
| for (const auto& instanceCase : instanceCases) |
| { |
| GroupPtr instanceGroup(new tcu::TestCaseGroup(testCtx, instanceCase.name, "")); |
| |
| for (const auto& shaderCase : shaderCases) |
| { |
| GroupPtr shaderGroup(new tcu::TestCaseGroup(testCtx, shaderCase.name, "")); |
| |
| for (const auto& multiviewCase : multiviewCases) |
| { |
| if (useDynamicRendering && multiviewCase.multiview) |
| continue; |
| |
| GroupPtr multiviewGroup(new tcu::TestCaseGroup(testCtx, multiviewCase.name, "")); |
| |
| const auto isIndexed = (drawTypeCase.drawType == DrawType::INDEXED); |
| const auto isPacked = (offsetTypeCase.vertexOffsetType && *offsetTypeCase.vertexOffsetType == VertexOffsetType::CONSTANT_PACK); |
| const auto baseStride = ((isIndexed && !isPacked) ? sizeof(VkMultiDrawIndexedInfoEXT) : sizeof(VkMultiDrawInfoEXT)); |
| const auto& extraBytes = strideCase.extraBytes; |
| const auto testOffset = (isIndexed ? VertexOffsetParams{*offsetTypeCase.vertexOffsetType, 0u } : tcu::nothing<VertexOffsetParams>()); |
| deUint32 testStride = 0u; |
| |
| if (extraBytes >= 0) |
| testStride = static_cast<deUint32>(baseStride) + static_cast<deUint32>(extraBytes); |
| |
| // For overlapping triangles we will skip instanced drawing. |
| if (instanceCase.instanceCount > 1u && meshTypeCase.meshType == MeshType::OVERLAPPING) |
| continue; |
| |
| TestParams params = |
| { |
| meshTypeCase.meshType, // MeshType meshType; |
| drawTypeCase.drawType, // DrawType drawType; |
| drawCountCase.drawCount, // deUint32 drawCount; |
| instanceCase.instanceCount, // deUint32 instanceCount; |
| instanceCase.firstInstance, // deUint32 firstInstance; |
| testStride, // deUint32 stride; |
| testOffset, // tcu::Maybe<VertexOffsetParams>> vertexOffset; // Only used for indexed draws. |
| kSeed, // deUint32 seed; |
| shaderCase.useTessellation, // bool useTessellation; |
| shaderCase.useGeometry, // bool useGeometry; |
| multiviewCase.multiview, // bool multiview; |
| useDynamicRendering, // bool useDynamicRendering; |
| }; |
| |
| multiviewGroup->addChild(new MultiDrawTest(testCtx, "no_offset", "", params)); |
| |
| if (isIndexed) |
| { |
| params.vertexOffset->offset = 6u; |
| multiviewGroup->addChild(new MultiDrawTest(testCtx, "offset_6", "", params)); |
| } |
| |
| shaderGroup->addChild(multiviewGroup.release()); |
| } |
| |
| instanceGroup->addChild(shaderGroup.release()); |
| } |
| |
| strideGroup->addChild(instanceGroup.release()); |
| } |
| |
| drawCountGroup->addChild(strideGroup.release()); |
| } |
| |
| drawTypeGroup->addChild(drawCountGroup.release()); |
| } |
| |
| meshTypeGroup->addChild(drawTypeGroup.release()); |
| } |
| } |
| |
| drawMultiGroup->addChild(meshTypeGroup.release()); |
| } |
| |
| return drawMultiGroup.release(); |
| } |
| |
| } // Draw |
| } // vkt |