--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2019 The Khronos Group Inc.
+ *
+ * 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 Tests fragment density map extension ( VK_EXT_fragment_density_map )
+ *//*--------------------------------------------------------------------*/
+
+#include "vktRenderPassFragmentDensityMapTests.hpp"
+#include "pipeline/vktPipelineImageUtil.hpp"
+#include "deMath.h"
+#include "vktTestCase.hpp"
+#include "vkImageUtil.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkCmdUtil.hpp"
+#include "vkRefUtil.hpp"
+#include "vkObjUtil.hpp"
+#include "tcuTestLog.hpp"
+#include <sstream>
+#include <vector>
+
+// Each test generates an image with a color gradient where all colors should be unique when rendered without density map
+// ( the number of each color in a histogram should be 1 ).
+// The whole density map has the same values defined by input fragment area ( one of the test input parameters ).
+// With density map enabled - the number of each color in a histogram should be [ fragmentArea.x * fragmentArea.y ].
+//
+// Additionally test checks if gl_FragSizeEXT shader variable has proper value ( as defined by fragmentArea input parameter ).
+//
+// static_* tests use density map loaded from CPU.
+// dynamic_* tests use density map rendered on a GPU in a separate render pass
+// *_nonsubsampled tests check if it's possible to use nonsubsampled images instead of subsampled ones
+// There are 3 render passes performed during the test:
+// - render pass that produces density map ( this rp is skipped when density map is static )
+// - render pass that produces subsampled image using density map
+// - render pass that copies subsampled image to traditional image using sampler with VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT flag.
+// ( because subsampled images cannot be retrieved to CPU in any other way ).
+
+namespace vkt
+{
+
+namespace renderpass
+{
+
+using namespace vk;
+
+namespace
+{
+
+// set value of DRY_RUN_WITHOUT_FDM_EXTENSION to 1 if you want to check the correctness of the code without using VK_EXT_fragment_density_map extension
+#define DRY_RUN_WITHOUT_FDM_EXTENSION 0
+
+struct TestParams
+{
+ TestParams(bool dynamicDensity, bool nonSubsampled, const tcu::UVec2& area)
+ : dynamicDensityMap{ dynamicDensity }, nonSubsampledImages{ nonSubsampled }, fragmentArea{ area }, densityMapFormat{ VK_FORMAT_R8G8_UNORM }
+ {}
+ bool dynamicDensityMap;
+ bool nonSubsampledImages;
+ tcu::UVec2 fragmentArea;
+ VkFormat densityMapFormat;
+};
+
+struct Vertex4RGBA
+{
+ tcu::Vec4 position;
+ tcu::Vec4 color;
+};
+
+std::vector<Vertex4RGBA> createFullscreenQuadRG(void)
+{
+ const Vertex4RGBA lowerLeftVertex = { tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f) };
+ const Vertex4RGBA upperLeftVertex = { tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f) };
+ const Vertex4RGBA lowerRightVertex = { tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f) };
+ const Vertex4RGBA upperRightVertex = { tcu::Vec4(1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f) };
+
+ return
+ {
+ lowerLeftVertex, lowerRightVertex, upperLeftVertex,
+ upperLeftVertex, lowerRightVertex, upperRightVertex
+ };
+}
+
+std::vector<Vertex4RGBA> createFullscreenQuadDensity(float densityX, float densityY)
+{
+ const Vertex4RGBA lowerLeftVertex = { tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(densityX, densityY, 0.0f, 1.0f) };
+ const Vertex4RGBA upperLeftVertex = { tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4(densityX, densityY, 0.0f, 1.0f) };
+ const Vertex4RGBA lowerRightVertex = { tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(densityX, densityY, 0.0f, 1.0f) };
+ const Vertex4RGBA upperRightVertex = { tcu::Vec4(1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4(densityX, densityY, 0.0f, 1.0f) };
+
+ return
+ {
+ lowerLeftVertex, lowerRightVertex, upperLeftVertex,
+ upperLeftVertex, lowerRightVertex, upperRightVertex
+ };
+};
+
+template <typename T>
+void createVertexBuffer(const DeviceInterface& vk,
+ VkDevice vkDevice,
+ const deUint32& queueFamilyIndex,
+ SimpleAllocator& memAlloc,
+ const std::vector<T>& vertices,
+ Move<VkBuffer>& vertexBuffer,
+ de::MovePtr<Allocation>& vertexAlloc)
+{
+ const VkBufferCreateInfo vertexBufferParams =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkBufferCreateFlags flags;
+ (VkDeviceSize)(sizeof(T) * vertices.size()), // VkDeviceSize size;
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyIndexCount;
+ &queueFamilyIndex // const deUint32* pQueueFamilyIndices;
+ };
+
+ vertexBuffer = createBuffer(vk, vkDevice, &vertexBufferParams);
+ vertexAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible);
+ VK_CHECK(vk.bindBufferMemory(vkDevice, *vertexBuffer, vertexAlloc->getMemory(), vertexAlloc->getOffset()));
+
+ // Upload vertex data
+ deMemcpy(vertexAlloc->getHostPtr(), vertices.data(), vertices.size() * sizeof(T));
+ flushAlloc(vk, vkDevice, *vertexAlloc);
+}
+
+template<typename AttachmentDesc, typename AttachmentRef, typename SubpassDesc, typename SubpassDep, typename RenderPassCreateInfo>
+Move<VkRenderPass> createRenderPassProduceDynamicDensityMap(const DeviceInterface& vk,
+ VkDevice vkDevice,
+ const TestParams& testParams)
+{
+ VkImageLayout densityPassFinalLayout = testParams.dynamicDensityMap ? VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ std::vector<AttachmentDesc> attachmentDescriptions =
+ {
+ {
+ DE_NULL, // const void* pNext
+ (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
+ testParams.densityMapFormat, // 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
+ densityPassFinalLayout // VkImageLayout finalLayout
+ }
+ };
+
+ std::vector<AttachmentRef> colorAttachmentRefs
+ {
+ { DE_NULL, 0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT }
+ };
+
+ std::vector<SubpassDesc> subpassDescriptions
+ {
+ {
+ DE_NULL,
+ (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
+ 0u, // deUint32 viewMask
+ 0u, // deUint32 inputAttachmentCount
+ DE_NULL, // const VkAttachmentReference* pInputAttachments
+ static_cast<deUint32>(colorAttachmentRefs.size()), // deUint32 colorAttachmentCount
+ colorAttachmentRefs.data(), // const VkAttachmentReference* pColorAttachments
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments
+ DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment
+ 0u, // deUint32 preserveAttachmentCount
+ DE_NULL // const deUint32* pPreserveAttachments
+ }
+ };
+
+ std::vector<SubpassDep> subpassDependencies;
+ if ( testParams.dynamicDensityMap )
+ {
+ subpassDependencies.emplace_back(
+ SubpassDep(
+ DE_NULL, // const void* pNext
+ 0u, // uint32_t srcSubpass
+ VK_SUBPASS_EXTERNAL, // uint32_t dstSubpass
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask
+ VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT, // VkPipelineStageFlags dstStageMask
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask
+ VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT, // VkAccessFlags dstAccessMask
+ VK_DEPENDENCY_BY_REGION_BIT, // VkDependencyFlags dependencyFlags
+ 0u // deInt32 viewOffset
+ )
+ );
+ };
+
+ const RenderPassCreateInfo renderPassInfo(
+ DE_NULL, // const void* pNext
+ (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags
+ static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount
+ attachmentDescriptions.data(), // const VkAttachmentDescription* pAttachments
+ static_cast<deUint32>(subpassDescriptions.size()), // deUint32 subpassCount
+ subpassDescriptions.data(), // const VkSubpassDescription* pSubpasses
+ static_cast<deUint32>(subpassDependencies.size()), // deUint32 dependencyCount
+ (!testParams.dynamicDensityMap) ? DE_NULL : subpassDependencies.data(), // const VkSubpassDependency* pDependencies
+ 0u, // deUint32 correlatedViewMaskCount
+ DE_NULL // const deUint32* pCorrelatedViewMasks
+ );
+
+ return renderPassInfo.createRenderPass(vk, vkDevice);
+}
+
+template<typename AttachmentDesc, typename AttachmentRef, typename SubpassDesc, typename SubpassDep, typename RenderPassCreateInfo>
+Move<VkRenderPass> createRenderPassProduceSubsampledImage(const DeviceInterface& vk,
+ VkDevice vkDevice,
+ const TestParams& testParams)
+{
+ DE_UNREF(testParams);
+ std::vector<AttachmentDesc> attachmentDescriptions
+ {
+ // Output color attachment
+ {
+ DE_NULL, // const void* pNext
+ (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
+ VK_FORMAT_R8G8B8A8_UNORM, // 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_SHADER_READ_ONLY_OPTIMAL // VkImageLayout finalLayout
+ }
+ };
+
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ {
+ attachmentDescriptions.emplace_back(
+ AttachmentDesc(
+ DE_NULL, // const void* pNext
+ (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
+ testParams.densityMapFormat, // VkFormat format
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
+ VK_ATTACHMENT_LOAD_OP_LOAD, // VkAttachmentLoadOp loadOp
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
+ VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT, // VkImageLayout initialLayout
+ VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT // VkImageLayout finalLayout
+ )
+ );
+ }
+#endif
+
+ std::vector<AttachmentRef> colorAttachmentRefs
+ {
+ { DE_NULL, 0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT }
+ };
+
+ std::vector<SubpassDesc> subpassDescriptions
+ {
+ {
+ DE_NULL,
+ (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
+ 0u, // deUint32 viewMask
+ 0u, // deUint32 inputAttachmentCount
+ DE_NULL, // const VkAttachmentReference* pInputAttachments
+ static_cast<deUint32>(colorAttachmentRefs.size()), // deUint32 colorAttachmentCount
+ colorAttachmentRefs.data(), // const VkAttachmentReference* pColorAttachments
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments
+ DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment
+ 0u, // deUint32 preserveAttachmentCount
+ DE_NULL // const deUint32* pPreserveAttachments
+ }
+ };
+
+ std::vector<SubpassDep> subpassDependencies
+ {
+ {
+ DE_NULL, // const void* pNext
+ 0u, // uint32_t srcSubpass
+ VK_SUBPASS_EXTERNAL, // uint32_t dstSubpass
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // VkPipelineStageFlags dstStageMask
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask
+ VK_ACCESS_SHADER_READ_BIT, // VkAccessFlags dstAccessMask
+ VK_DEPENDENCY_BY_REGION_BIT, // VkDependencyFlags dependencyFlags
+ 0u // deInt32 viewOffset
+ }
+ };
+
+ VkRenderPassFragmentDensityMapCreateInfoEXT renderPassFragmentDensityMap;
+ renderPassFragmentDensityMap.sType = VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT;
+ renderPassFragmentDensityMap.pNext = DE_NULL;
+ renderPassFragmentDensityMap.fragmentDensityMapAttachment = { 1, VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT };
+
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ const void* renderPassInfoPNext = (const void*)&renderPassFragmentDensityMap;
+#else
+ const void* renderPassInfoPNext = DE_NULL;
+#endif
+ const RenderPassCreateInfo renderPassInfo(
+ renderPassInfoPNext, // const void* pNext
+ (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags
+ static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount
+ attachmentDescriptions.data(), // const VkAttachmentDescription* pAttachments
+ static_cast<deUint32>(subpassDescriptions.size()), // deUint32 subpassCount
+ subpassDescriptions.data(), // const VkSubpassDescription* pSubpasses
+ static_cast<deUint32>(subpassDependencies.size()), // deUint32 dependencyCount
+ subpassDependencies.data(), // const VkSubpassDependency* pDependencies
+ 0u, // deUint32 correlatedViewMaskCount
+ DE_NULL // const deUint32* pCorrelatedViewMasks
+ );
+
+ return renderPassInfo.createRenderPass(vk, vkDevice);
+}
+
+template<typename AttachmentDesc, typename AttachmentRef, typename SubpassDesc, typename SubpassDep, typename RenderPassCreateInfo>
+Move<VkRenderPass> createRenderPassOutputSubsampledImage(const DeviceInterface& vk,
+ VkDevice vkDevice,
+ const TestParams& testParams)
+{
+ DE_UNREF(testParams);
+ // copy subsampled image to ordinary image - you cannot retrieve subsampled image to CPU in any way. You must first convert it into plain image through rendering
+ std::vector<AttachmentDesc> attachmentDescriptions =
+ {
+ // output attachment
+ AttachmentDesc(
+ DE_NULL, // const void* pNext
+ (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
+ VK_FORMAT_R8G8B8A8_UNORM, // 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
+ ),
+ };
+
+ std::vector<AttachmentRef> colorAttachmentRefs
+ {
+ { DE_NULL, 0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT }
+ };
+
+ std::vector<SubpassDesc> subpassDescriptions =
+ {
+ {
+ DE_NULL,
+ (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
+ 0u, // deUint32 viewMask
+ 0u, // deUint32 inputAttachmentCount
+ DE_NULL, // const VkAttachmentReference* pInputAttachments
+ static_cast<deUint32>(colorAttachmentRefs.size()), // deUint32 colorAttachmentCount
+ colorAttachmentRefs.data(), // const VkAttachmentReference* pColorAttachments
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments
+ DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment
+ 0u, // deUint32 preserveAttachmentCount
+ DE_NULL // const deUint32* pPreserveAttachments
+ }
+ };
+
+ const RenderPassCreateInfo renderPassInfo(
+ DE_NULL, // const void* pNext
+ (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags
+ static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount
+ attachmentDescriptions.data(), // const VkAttachmentDescription* pAttachments
+ static_cast<deUint32>(subpassDescriptions.size()), // deUint32 subpassCount
+ subpassDescriptions.data(), // const VkSubpassDescription* pSubpasses
+ 0, // deUint32 dependencyCount
+ DE_NULL, // const VkSubpassDependency* pDependencies
+ 0u, // deUint32 correlatedViewMaskCount
+ DE_NULL // const deUint32* pCorrelatedViewMasks
+ );
+
+ return renderPassInfo.createRenderPass(vk, vkDevice);
+}
+
+Move<VkFramebuffer> createFrameBuffer( const DeviceInterface& vk, VkDevice vkDevice, VkRenderPass renderPass, const tcu::UVec2& renderSize, const std::vector<VkImageView>& imageViews)
+{
+ const VkFramebufferCreateInfo framebufferParams =
+ {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkFramebufferCreateFlags flags;
+ renderPass, // VkRenderPass renderPass;
+ static_cast<deUint32>(imageViews.size()), // deUint32 attachmentCount;
+ imageViews.data(), // const VkImageView* pAttachments;
+ renderSize.x(), // deUint32 width;
+ renderSize.y(), // deUint32 height;
+ 1u // deUint32 layers;
+ };
+
+ return createFramebuffer(vk, vkDevice, &framebufferParams);
+}
+
+class FragmentDensityMapTest : public vkt::TestCase
+{
+public:
+ FragmentDensityMapTest (tcu::TestContext& testContext,
+ const std::string& name,
+ const std::string& description,
+ const TestParams& testParams);
+ virtual void initPrograms (SourceCollections& sourceCollections) const;
+ virtual TestInstance* createInstance (Context& context) const;
+ virtual void checkSupport (Context& context) const;
+
+private:
+ const TestParams m_testParams;
+};
+
+class FragmentDensityMapTestInstance : public vkt::TestInstance
+{
+public:
+ FragmentDensityMapTestInstance (Context& context,
+ const TestParams& testParams);
+ virtual tcu::TestStatus iterate (void);
+private:
+ tcu::TestStatus verifyImage (void);
+
+ TestParams m_testParams;
+ const tcu::UVec2 m_renderSize;
+ const tcu::UVec2 m_densityMapSize;
+
+ VkPhysicalDeviceFragmentDensityMapPropertiesEXT m_fragmentDensityMapProperties;
+
+ Move<VkCommandPool> m_cmdPool;
+
+ Move<VkImage> m_densityMapImage;
+ de::MovePtr<Allocation> m_densityMapImageAlloc;
+ Move<VkImageView> m_densityMapImageView;
+
+ Move<VkImage> m_colorImage;
+ de::MovePtr<Allocation> m_colorImageAlloc;
+ Move<VkImageView> m_colorImageView;
+
+ Move<VkImage> m_outputImage;
+ de::MovePtr<Allocation> m_outputImageAlloc;
+ Move<VkImageView> m_outputImageView;
+
+ Move<VkSampler> m_colorSampler;
+
+ Move<VkRenderPass> m_renderPassProduceDynamicDensityMap;
+ Move<VkRenderPass> m_renderPassProduceSubsampledImage;
+ Move<VkRenderPass> m_renderPassOutputSubsampledImage;
+ Move<VkFramebuffer> m_framebufferProduceDynamicDensityMap;
+ Move<VkFramebuffer> m_framebufferProduceSubsampledImage;
+ Move<VkFramebuffer> m_framebufferOutputSubsampledImage;
+
+ Move<VkDescriptorSetLayout> m_descriptorSetLayoutProduceSubsampled;
+ Move<VkDescriptorSetLayout> m_descriptorSetLayoutOutputSubsampledImage;
+ Move<VkDescriptorPool> m_descriptorPoolOutputSubsampledImage;
+ Move<VkDescriptorSet> m_descriptorSetOutputSubsampledImage;
+
+ Move<VkShaderModule> m_vertexCommonShaderModule;
+ Move<VkShaderModule> m_fragmentShaderModuleProduceSubsampledImage;
+ Move<VkShaderModule> m_fragmentShaderModuleOutputSubsampledImage;
+
+ Move<VkBuffer> m_vertexBuffer;
+ std::vector<Vertex4RGBA> m_vertices;
+ de::MovePtr<Allocation> m_vertexBufferAlloc;
+
+ Move<VkBuffer> m_vertexBufferDDM;
+ std::vector<Vertex4RGBA> m_verticesDDM;
+ de::MovePtr<Allocation> m_vertexBufferAllocDDM;
+
+ Move<VkPipelineLayout> m_pipelineLayoutProduceSubsampledImage;
+ Move<VkPipelineLayout> m_pipelineLayoutOutputSubsampledImage;
+ Move<VkPipeline> m_graphicsPipelineProduceDynamicDensityMap;
+ Move<VkPipeline> m_graphicsPipelineProduceSubsampledImage;
+ Move<VkPipeline> m_graphicsPipelineOutputSubsampledImage;
+
+ Move<VkCommandBuffer> m_cmdBuffer;
+};
+
+FragmentDensityMapTest::FragmentDensityMapTest (tcu::TestContext& testContext,
+ const std::string& name,
+ const std::string& description,
+ const TestParams& testParams)
+ : vkt::TestCase (testContext, name, description)
+ , m_testParams (testParams)
+{
+}
+
+void FragmentDensityMapTest::initPrograms(SourceCollections& sourceCollections) const
+{
+ std::ostringstream densityVertexGLSL;
+ densityVertexGLSL <<
+ "#version 450\n"
+ "layout(location = 0) in vec4 inPosition;\n"
+ "layout(location = 1) in vec4 inColor;\n"
+ "layout(location = 0) out vec4 outColor;\n"
+ "layout(location = 1) out vec2 outUV;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = inPosition;\n"
+ " outColor = inColor;\n"
+ " outUV = 0.5 * inPosition.xy + vec2(0.5);\n"
+ "}\n";
+ sourceCollections.glslSources.add("densitymap_vert") << glu::VertexSource(densityVertexGLSL.str());
+
+ std::ostringstream densityFragmentProduceGLSL;
+ densityFragmentProduceGLSL <<
+ "#version 450\n"
+ "#extension GL_EXT_fragment_invocation_density : enable\n"
+ "layout(location = 0) in vec4 inColor;\n"
+ "layout(location = 1) in vec2 inUV;\n"
+ "layout(location = 0) out vec4 fragColor;\n"
+ "void main(void)\n"
+ "{\n"
+ " fragColor = vec4(inColor.x, inColor.y, 1.0/float(gl_FragSizeEXT.x), 1.0/(gl_FragSizeEXT.y));\n"
+ "}\n";
+ sourceCollections.glslSources.add("densitymap_frag_produce") << glu::FragmentSource(densityFragmentProduceGLSL.str());
+
+ std::ostringstream densityFragmentOutputGLSL;
+ densityFragmentOutputGLSL <<
+ "#version 450\n"
+ "layout(location = 0) in vec4 inColor;\n"
+ "layout(location = 1) in vec2 inUV;\n"
+ "layout(binding = 0) uniform sampler2D subsampledImage;\n"
+ "layout(location = 0) out vec4 fragColor;\n"
+ "void main(void)\n"
+ "{\n"
+ " fragColor = texture(subsampledImage, inUV);\n"
+ "}\n";
+ sourceCollections.glslSources.add("densitymap_frag_output") << glu::FragmentSource(densityFragmentOutputGLSL.str());
+}
+
+TestInstance* FragmentDensityMapTest::createInstance(Context& context) const
+{
+ return new FragmentDensityMapTestInstance(context, m_testParams);
+}
+
+void FragmentDensityMapTest::checkSupport(Context& context) const
+{
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ context.requireDeviceFunctionality("VK_EXT_fragment_density_map");
+
+ VkPhysicalDeviceFeatures2 features;
+ deMemset(&features, 0, sizeof(VkPhysicalDeviceFeatures2));
+ features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+
+ VkPhysicalDeviceFragmentDensityMapFeaturesEXT fragmentDensityMapFeatures;
+ deMemset(&fragmentDensityMapFeatures, 0, sizeof(VkPhysicalDeviceFragmentDensityMapFeaturesEXT));
+ fragmentDensityMapFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT;
+ features.pNext = &fragmentDensityMapFeatures;
+
+ context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &features);
+
+ if (!fragmentDensityMapFeatures.fragmentDensityMap)
+ TCU_THROW(NotSupportedError, "fragmentDensityMap feature is not supported");
+ if (m_testParams.dynamicDensityMap && !fragmentDensityMapFeatures.fragmentDensityMapDynamic)
+ TCU_THROW(NotSupportedError, "fragmentDensityMapDynamic feature is not supported");
+ if (m_testParams.nonSubsampledImages && !fragmentDensityMapFeatures.fragmentDensityMapNonSubsampledImages)
+ TCU_THROW(NotSupportedError, "fragmentDensityMapNonSubsampledImages feature is not supported");
+#else
+ DE_UNREF(context);
+#endif
+}
+
+FragmentDensityMapTestInstance::FragmentDensityMapTestInstance(Context& context,
+ const TestParams& testParams)
+ : vkt::TestInstance ( context )
+ , m_testParams ( testParams )
+ , m_renderSize ( 32u, 32u )
+ , m_densityMapSize ( 16u, 16u )
+ , m_vertices ( createFullscreenQuadRG() )
+ , m_verticesDDM ( createFullscreenQuadDensity(1.0f / static_cast<float>(testParams.fragmentArea.x()), 1.0f / static_cast<float>(testParams.fragmentArea.y())) )
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice vkDevice = m_context.getDevice();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ SimpleAllocator memAlloc (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
+ const VkComponentMapping componentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
+
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ {
+ VkPhysicalDeviceProperties2 properties;
+ deMemset(&properties, 0, sizeof(VkPhysicalDeviceProperties2));
+ properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+
+ deMemset(&m_fragmentDensityMapProperties, 0, sizeof(VkPhysicalDeviceFragmentDensityMapPropertiesEXT));
+ m_fragmentDensityMapProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT;
+ properties.pNext = &m_fragmentDensityMapProperties;
+
+ context.getInstanceInterface().getPhysicalDeviceProperties2(context.getPhysicalDevice(), &properties);
+ }
+#else
+ {
+ m_fragmentDensityMapProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT;
+ m_fragmentDensityMapProperties.minFragmentDensityTexelSize.width = 1u;
+ m_fragmentDensityMapProperties.maxFragmentDensityTexelSize.width = 1u;
+ m_fragmentDensityMapProperties.minFragmentDensityTexelSize.height = 1u;
+ m_fragmentDensityMapProperties.maxFragmentDensityTexelSize.height = 1u;
+ m_fragmentDensityMapProperties.fragmentDensityInvocations = DE_FALSE;
+ m_testParams.fragmentArea.x() = 1u;
+ m_testParams.fragmentArea.y() = 1u;
+ }
+#endif
+
+ // Create density map image
+ {
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ vk::VkImageUsageFlags densityMapImageUsage = m_testParams.dynamicDensityMap ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT : VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT;
+#else
+ vk::VkImageUsageFlags densityMapImageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+#endif
+ const VkImageCreateInfo densityMapImageParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ m_testParams.densityMapFormat, // VkFormat format;
+ { m_densityMapSize.x(), m_densityMapSize.y(), 1u }, // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ densityMapImageUsage, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyIndexCount;
+ &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
+ };
+
+ m_densityMapImage = createImage(vk, vkDevice, &densityMapImageParams);
+
+ // Allocate and bind density map image memory
+ VkMemoryRequirements memoryRequirements = getImageMemoryRequirements(vk, vkDevice, *m_densityMapImage);
+
+ m_densityMapImageAlloc = memAlloc.allocate(memoryRequirements, MemoryRequirement::Any);
+ VK_CHECK(vk.bindImageMemory(vkDevice, *m_densityMapImage, m_densityMapImageAlloc->getMemory(), m_densityMapImageAlloc->getOffset()));
+
+ // create and fill staging buffer, copy its data to density map image
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ tcu::TextureFormat densityMapTextureFormat = vk::mapVkFormat(m_testParams.densityMapFormat);
+
+ if ( !m_testParams.dynamicDensityMap )
+ {
+ VkDeviceSize stagingBufferSize = tcu::getPixelSize(densityMapTextureFormat) * m_densityMapSize.x() * m_densityMapSize.y() * 1;
+ const vk::VkBufferCreateInfo stagingBufferCreateInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ DE_NULL,
+ 0u, // flags
+ stagingBufferSize, // size
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // usage
+ vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode
+ 0u, // queueFamilyCount
+ DE_NULL, // pQueueFamilyIndices
+ };
+ vk::Move<vk::VkBuffer> stagingBuffer = vk::createBuffer(vk, vkDevice, &stagingBufferCreateInfo);
+ const vk::VkMemoryRequirements stagingRequirements = vk::getBufferMemoryRequirements(vk, vkDevice, *stagingBuffer);
+ de::MovePtr<vk::Allocation> stagingAllocation = memAlloc.allocate(stagingRequirements, MemoryRequirement::HostVisible);
+ VK_CHECK(vk.bindBufferMemory(vkDevice, *stagingBuffer, stagingAllocation->getMemory(), stagingAllocation->getOffset()));
+ tcu::PixelBufferAccess stagingBufferAccess = tcu::PixelBufferAccess(densityMapTextureFormat, m_densityMapSize.x(), m_densityMapSize.y(), 1, stagingAllocation->getHostPtr());
+
+ tcu::Vec4 fragmentArea { 1.0f / static_cast<float>(testParams.fragmentArea.x()), 1.0f / static_cast<float>(testParams.fragmentArea.y()), 0.0f, 1.0f };
+ for (int y = 0; y < stagingBufferAccess.getHeight(); y++)
+ for (int x = 0; x < stagingBufferAccess.getWidth(); x++)
+ stagingBufferAccess.setPixel(fragmentArea, x, y);
+ flushAlloc(vk, vkDevice, *stagingAllocation);
+
+ std::vector<VkBufferImageCopy> copyRegions =
+ {
+ {
+ 0, // VkDeviceSize bufferOffset
+ 0, // deUint32 bufferRowLength
+ 0, // deUint32 bufferImageHeight
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, // VkImageSubresourceLayers imageSubresource
+ { 0, 0, 0 }, // VkOffset3D imageOffset
+ { m_densityMapSize.x(), m_densityMapSize.y(), 1u } // VkExtent3D imageExtent
+ }
+ };
+
+ vk::copyBufferToImage
+ (
+ vk,
+ vkDevice,
+ m_context.getUniversalQueue(),
+ queueFamilyIndex,
+ *stagingBuffer,
+ stagingBufferSize,
+ copyRegions,
+ DE_NULL,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 1,
+ 1,
+ *m_densityMapImage,
+ VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT,
+ VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT
+ );
+ }
+#endif
+
+ //create image view for fragment density map
+ deUint32 densityMapImageViewCreateFlags = m_testParams.dynamicDensityMap ? (deUint32)VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT : 0u;
+ const VkImageViewCreateInfo densityMapImageViewParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkImageViewCreateFlags)densityMapImageViewCreateFlags, // VkImageViewCreateFlags flags;
+ *m_densityMapImage, // VkImage image;
+ VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
+ m_testParams.densityMapFormat, // VkFormat format;
+ componentMappingRGBA, // VkChannelMapping channels;
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
+ };
+
+ m_densityMapImageView = createImageView(vk, vkDevice, &densityMapImageViewParams);
+ }
+
+ // Create subsampled color image
+ {
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ deUint32 colorImageCreateFlags = m_testParams.nonSubsampledImages ? 0u : (deUint32)VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT;
+#else
+ deUint32 colorImageCreateFlags = 0u;
+#endif
+ const VkImageCreateInfo colorImageParams
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkImageCreateFlags)colorImageCreateFlags, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ VK_FORMAT_R8G8B8A8_UNORM, // VkFormat format;
+ { m_renderSize.x(), m_renderSize.y(), 1u }, // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyIndexCount;
+ &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
+ };
+
+ m_colorImage = createImage(vk, vkDevice, &colorImageParams);
+
+ // Allocate and bind color image memory
+ m_colorImageAlloc = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *m_colorImage), MemoryRequirement::Any);
+ VK_CHECK(vk.bindImageMemory(vkDevice, *m_colorImage, m_colorImageAlloc->getMemory(), m_colorImageAlloc->getOffset()));
+
+ // create image view for subsampled image
+ const VkImageViewCreateInfo colorImageViewParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageViewCreateFlags flags;
+ *m_colorImage, // VkImage image;
+ VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
+ VK_FORMAT_R8G8B8A8_UNORM, // VkFormat format;
+ componentMappingRGBA, // VkChannelMapping channels;
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
+ };
+
+ m_colorImageView = createImageView(vk, vkDevice, &colorImageViewParams);
+ }
+
+ // Create output image ( data from subsampled color image will be copied into it using sampler with VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT )
+ {
+ const VkImageCreateInfo outputImageParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ VK_FORMAT_R8G8B8A8_UNORM, // VkFormat format;
+ { m_renderSize.x(), m_renderSize.y(), 1u }, // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyIndexCount;
+ &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
+ };
+
+ m_outputImage = createImage(vk, vkDevice, &outputImageParams);
+
+ // Allocate and bind input image memory
+ m_outputImageAlloc = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *m_outputImage), MemoryRequirement::Any);
+ VK_CHECK(vk.bindImageMemory(vkDevice, *m_outputImage, m_outputImageAlloc->getMemory(), m_outputImageAlloc->getOffset()));
+
+ // create image view for output image
+ const VkImageViewCreateInfo outputImageViewParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageViewCreateFlags flags;
+ *m_outputImage, // VkImage image;
+ VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
+ VK_FORMAT_R8G8B8A8_UNORM, // VkFormat format;
+ componentMappingRGBA, // VkChannelMapping channels;
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
+ };
+
+ m_outputImageView = createImageView(vk, vkDevice, &outputImageViewParams);
+ }
+
+ // create a sampler that is able to read from subsampled image
+ {
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ deUint32 samplerCreateFlags = (deUint32)VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT;
+#else
+ deUint32 samplerCreateFlags = 0u;
+#endif
+ const struct VkSamplerCreateInfo samplerInfo
+ {
+ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // sType
+ DE_NULL, // pNext
+ (VkSamplerCreateFlags)samplerCreateFlags, // flags
+ VK_FILTER_NEAREST, // magFilter
+ VK_FILTER_NEAREST, // minFilter
+ VK_SAMPLER_MIPMAP_MODE_NEAREST, // mipmapMode
+ VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeU
+ VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeV
+ VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeW
+ 0.0f, // mipLodBias
+ VK_FALSE, // anisotropyEnable
+ 1.0f, // maxAnisotropy
+ DE_FALSE, // compareEnable
+ VK_COMPARE_OP_ALWAYS, // compareOp
+ 0.0f, // minLod
+ 0.0f, // maxLod
+ VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // borderColor
+ VK_FALSE, // unnormalizedCoords
+ };
+ m_colorSampler = createSampler(vk, vkDevice, &samplerInfo);
+ }
+
+ // Create render passes
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ if ( testParams.dynamicDensityMap )
+#endif
+ m_renderPassProduceDynamicDensityMap = createRenderPassProduceDynamicDensityMap<AttachmentDescription2, AttachmentReference2, SubpassDescription2, SubpassDependency2, RenderPassCreateInfo2>(vk, vkDevice, testParams);
+ m_renderPassProduceSubsampledImage = createRenderPassProduceSubsampledImage<AttachmentDescription2, AttachmentReference2, SubpassDescription2, SubpassDependency2, RenderPassCreateInfo2>(vk, vkDevice, testParams);
+ m_renderPassOutputSubsampledImage = createRenderPassOutputSubsampledImage<AttachmentDescription2, AttachmentReference2, SubpassDescription2, SubpassDependency2, RenderPassCreateInfo2>(vk, vkDevice, testParams);
+
+ // Create framebuffers
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ if ( testParams.dynamicDensityMap )
+#endif
+ m_framebufferProduceDynamicDensityMap = createFrameBuffer(vk, vkDevice, *m_renderPassProduceDynamicDensityMap, m_densityMapSize, { *m_densityMapImageView });
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ m_framebufferProduceSubsampledImage = createFrameBuffer(vk, vkDevice, *m_renderPassProduceSubsampledImage, m_renderSize, { *m_colorImageView, *m_densityMapImageView });
+#else
+ m_framebufferProduceSubsampledImage = createFrameBuffer(vk, vkDevice, *m_renderPassProduceSubsampledImage, m_renderSize, { *m_colorImageView });
+#endif
+ m_framebufferOutputSubsampledImage = createFrameBuffer( vk, vkDevice, *m_renderPassOutputSubsampledImage, m_renderSize, { *m_outputImageView } );
+
+ // Create pipeline layout for first two render passes that do not use any descriptors
+ {
+ const VkPipelineLayoutCreateInfo pipelineLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineLayoutCreateFlags flags;
+ 0u, // deUint32 setLayoutCount;
+ DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL // const VkPushConstantRange* pPushConstantRanges;
+ };
+
+ m_pipelineLayoutProduceSubsampledImage = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+ }
+
+ // Create pipeline layout for last render pass ( output subsampled image )
+ {
+ std::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings =
+ {
+ {
+ 0, // deUint32 binding;
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // VkDescriptorType descriptorType;
+ 1, // deUint32 descriptorCount;
+ VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags;
+ &(m_colorSampler.get()) // const VkSampler* pImmutableSamplers;
+ },
+ };
+
+ const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkDescriptorSetLayoutCreateFlags flags
+ static_cast<deUint32>(descriptorSetLayoutBindings.size()), // deUint32 bindingCount
+ descriptorSetLayoutBindings.data() // const VkDescriptorSetLayoutBinding* pBindings
+ };
+ m_descriptorSetLayoutOutputSubsampledImage = createDescriptorSetLayout(vk, vkDevice, &descriptorSetLayoutParams);
+
+ const VkPipelineLayoutCreateInfo pipelineLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineLayoutCreateFlags flags;
+ 1u, // deUint32 setLayoutCount;
+ &m_descriptorSetLayoutOutputSubsampledImage.get(), // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL // const VkPushConstantRange* pPushConstantRanges;
+ };
+ m_pipelineLayoutOutputSubsampledImage = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+ }
+
+ // Update descriptor set
+ {
+ {
+ std::vector<VkDescriptorPoolSize> poolSizes =
+ {
+ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1u }
+ };
+
+ const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, // VkDescriptorPoolCreateFlags flags
+ 1u, // deUint32 maxSets
+ static_cast<deUint32>(poolSizes.size()), // deUint32 poolSizeCount
+ poolSizes.data() // const VkDescriptorPoolSize* pPoolSizes
+ };
+ m_descriptorPoolOutputSubsampledImage = createDescriptorPool(vk, vkDevice, &descriptorPoolCreateInfo);
+ }
+
+ {
+ const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ *m_descriptorPoolOutputSubsampledImage, // VkDescriptorPool descriptorPool
+ 1u, // deUint32 descriptorSetCount
+ &m_descriptorSetLayoutOutputSubsampledImage.get(), // const VkDescriptorSetLayout* pSetLayouts
+ };
+ m_descriptorSetOutputSubsampledImage = allocateDescriptorSet(vk, vkDevice, &descriptorSetAllocateInfo);
+
+ const VkDescriptorImageInfo inputImageInfo =
+ {
+ DE_NULL, // VkSampleri sampler;
+ *m_colorImageView, // VkImageView imageView;
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL // VkImageLayout imageLayout;
+ };
+
+ std::vector<VkWriteDescriptorSet> descriptorWrite =
+ {
+ {
+ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *m_descriptorSetOutputSubsampledImage, // VkDescriptorSet dstSet;
+ 0u, // deUint32 dstBinding;
+ 0u, // deUint32 dstArrayElement;
+ 1u, // deUint32 descriptorCount;
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // VkDescriptorType descriptorType;
+ &inputImageInfo, // const VkDescriptorImageInfo* pImageInfo;
+ DE_NULL, // const VkDescriptorBufferInfo* pBufferInfo;
+ DE_NULL // const VkBufferView* pTexelBufferView;
+ }
+ };
+ vk.updateDescriptorSets(vkDevice, static_cast<deUint32>(descriptorWrite.size()), descriptorWrite.data(), 0u, DE_NULL);
+ }
+ }
+
+ m_vertexCommonShaderModule = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("densitymap_vert"), 0);
+ m_fragmentShaderModuleProduceSubsampledImage = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("densitymap_frag_produce"), 0);
+ m_fragmentShaderModuleOutputSubsampledImage = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("densitymap_frag_output"), 0);
+
+ // Create pipelines
+ {
+ const VkVertexInputBindingDescription vertexInputBindingDescription =
+ {
+ 0u, // deUint32 binding;
+ sizeof(Vertex4RGBA), // deUint32 strideInBytes;
+ VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
+ };
+
+ std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions =
+ {
+ { 0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u },
+ { 1u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, (deUint32)(sizeof(float) * 4) }
+ };
+
+ const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineVertexInputStateCreateFlags flags;
+ 1u, // deUint32 vertexBindingDescriptionCount;
+ &vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
+ static_cast<deUint32>(vertexInputAttributeDescriptions.size()), // deUint32 vertexAttributeDescriptionCount;
+ vertexInputAttributeDescriptions.data() // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
+ };
+
+ const std::vector<VkViewport> viewportsDDM { makeViewport(m_densityMapSize) };
+ const std::vector<VkRect2D> scissorsDDM { makeRect2D(m_densityMapSize) };
+ const std::vector<VkViewport> viewports { makeViewport(m_renderSize) };
+ const std::vector<VkRect2D> scissors { makeRect2D(m_renderSize) };
+
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ if (testParams.dynamicDensityMap)
+#endif
+ m_graphicsPipelineProduceDynamicDensityMap = makeGraphicsPipeline(vk, // const DeviceInterface& vk
+ vkDevice, // const VkDevice device
+ *m_pipelineLayoutProduceSubsampledImage, // const VkPipelineLayout pipelineLayout
+ *m_vertexCommonShaderModule, // const VkShaderModule vertexShaderModule
+ DE_NULL, // const VkShaderModule tessellationControlModule
+ DE_NULL, // const VkShaderModule tessellationEvalModule
+ DE_NULL, // const VkShaderModule geometryShaderModule
+ *m_fragmentShaderModuleProduceSubsampledImage, // const VkShaderModule fragmentShaderModule
+ *m_renderPassProduceDynamicDensityMap, // const VkRenderPass renderPass
+ viewportsDDM, // const std::vector<VkViewport>& viewports
+ scissorsDDM, // const std::vector<VkRect2D>& scissors
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology
+ 0u, // const deUint32 subpass
+ 0u, // const deUint32 patchControlPoints
+ &vertexInputStateParams); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
+
+ m_graphicsPipelineProduceSubsampledImage = makeGraphicsPipeline(vk, // const DeviceInterface& vk
+ vkDevice, // const VkDevice device
+ *m_pipelineLayoutProduceSubsampledImage, // const VkPipelineLayout pipelineLayout
+ *m_vertexCommonShaderModule, // const VkShaderModule vertexShaderModule
+ DE_NULL, // const VkShaderModule tessellationControlModule
+ DE_NULL, // const VkShaderModule tessellationEvalModule
+ DE_NULL, // const VkShaderModule geometryShaderModule
+ *m_fragmentShaderModuleProduceSubsampledImage, // const VkShaderModule fragmentShaderModule
+ *m_renderPassProduceSubsampledImage, // const VkRenderPass renderPass
+ viewports, // const std::vector<VkViewport>& viewports
+ scissors, // const std::vector<VkRect2D>& scissors
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology
+ 0u, // const deUint32 subpass
+ 0u, // const deUint32 patchControlPoints
+ &vertexInputStateParams); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
+
+ m_graphicsPipelineOutputSubsampledImage = makeGraphicsPipeline(vk, // const DeviceInterface& vk
+ vkDevice, // const VkDevice device
+ *m_pipelineLayoutOutputSubsampledImage, // const VkPipelineLayout pipelineLayout
+ *m_vertexCommonShaderModule, // const VkShaderModule vertexShaderModule
+ DE_NULL, // const VkShaderModule tessellationControlModule
+ DE_NULL, // const VkShaderModule tessellationEvalModule
+ DE_NULL, // const VkShaderModule geometryShaderModule
+ *m_fragmentShaderModuleOutputSubsampledImage, // const VkShaderModule fragmentShaderModule
+ *m_renderPassOutputSubsampledImage, // const VkRenderPass renderPass
+ viewports, // const std::vector<VkViewport>& viewports
+ scissors, // const std::vector<VkRect2D>& scissors
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology
+ 0u, // const deUint32 subpass
+ 0u, // const deUint32 patchControlPoints
+ &vertexInputStateParams); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
+ }
+
+ // Create vertex buffers
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ if (testParams.dynamicDensityMap)
+#endif
+ createVertexBuffer(vk, vkDevice, queueFamilyIndex, memAlloc, m_verticesDDM, m_vertexBufferDDM, m_vertexBufferAllocDDM);
+ createVertexBuffer(vk, vkDevice, queueFamilyIndex, memAlloc, m_vertices, m_vertexBuffer, m_vertexBufferAlloc);
+
+ // Create command pool and command buffer
+ m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex);
+ m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ const typename RenderpassSubpass2::SubpassBeginInfo subpassBeginInfo(DE_NULL, VK_SUBPASS_CONTENTS_INLINE);
+ const typename RenderpassSubpass2::SubpassEndInfo subpassEndInfo(DE_NULL);
+ const VkDeviceSize vertexBufferOffset = 0;
+ std::vector<VkClearValue> attachmentClearValuesDDM = { makeClearValueColorF32(1.0f, 1.0f, 1.0f, 1.0f) };
+ std::vector<VkClearValue> attachmentClearValues = { makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f) };
+
+ beginCommandBuffer(vk, *m_cmdBuffer, 0u);
+
+ // first render pass - render dynamic density map
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ if ( testParams.dynamicDensityMap )
+#endif
+ {
+ const VkRenderPassBeginInfo renderPassBeginInfoProduceDynamicDensityMap =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *m_renderPassProduceDynamicDensityMap, // VkRenderPass renderPass;
+ *m_framebufferProduceDynamicDensityMap, // VkFramebuffer framebuffer;
+ makeRect2D(m_densityMapSize), // VkRect2D renderArea;
+ static_cast<deUint32>(attachmentClearValuesDDM.size()), // uint32_t clearValueCount;
+ attachmentClearValuesDDM.data() // const VkClearValue* pClearValues;
+ };
+ RenderpassSubpass2::cmdBeginRenderPass(vk, *m_cmdBuffer, &renderPassBeginInfoProduceDynamicDensityMap, &subpassBeginInfo);
+ vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipelineProduceDynamicDensityMap);
+ vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &m_vertexBufferDDM.get(), &vertexBufferOffset);
+ vk.cmdDraw(*m_cmdBuffer, (deUint32)m_verticesDDM.size(), 1, 0, 0);
+ RenderpassSubpass2::cmdEndRenderPass(vk, *m_cmdBuffer, &subpassEndInfo);
+ }
+
+ // render subsampled image
+ const VkRenderPassBeginInfo renderPassBeginInfoProduceSubsampledImage =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *m_renderPassProduceSubsampledImage, // VkRenderPass renderPass;
+ *m_framebufferProduceSubsampledImage, // VkFramebuffer framebuffer;
+ makeRect2D(m_renderSize), // VkRect2D renderArea;
+ static_cast<deUint32>(attachmentClearValues.size()), // uint32_t clearValueCount;
+ attachmentClearValues.data() // const VkClearValue* pClearValues;
+ };
+ RenderpassSubpass2::cmdBeginRenderPass(vk, *m_cmdBuffer, &renderPassBeginInfoProduceSubsampledImage, &subpassBeginInfo);
+ vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipelineProduceSubsampledImage);
+ vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &m_vertexBuffer.get(), &vertexBufferOffset);
+ vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1, 0, 0);
+ RenderpassSubpass2::cmdEndRenderPass(vk, *m_cmdBuffer, &subpassEndInfo);
+
+ // copy subsampled image to ordinary image using sampler that is able to read from subsampled images( subsampled image cannot be copied using vkCmdCopyImageToBuffer )
+ const VkRenderPassBeginInfo renderPassBeginInfoOutputSubsampledImage =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *m_renderPassOutputSubsampledImage, // VkRenderPass renderPass;
+ *m_framebufferOutputSubsampledImage, // VkFramebuffer framebuffer;
+ makeRect2D(m_renderSize), // VkRect2D renderArea;
+ static_cast<deUint32>(attachmentClearValues.size()), // uint32_t clearValueCount;
+ attachmentClearValues.data() // const VkClearValue* pClearValues;
+ };
+ RenderpassSubpass2::cmdBeginRenderPass(vk, *m_cmdBuffer, &renderPassBeginInfoOutputSubsampledImage, &subpassBeginInfo);
+ vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipelineOutputSubsampledImage);
+ vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayoutOutputSubsampledImage, 0, 1, &m_descriptorSetOutputSubsampledImage.get(), 0, DE_NULL);
+ vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1, 0, 0);
+ RenderpassSubpass2::cmdEndRenderPass(vk, *m_cmdBuffer, &subpassEndInfo);
+
+ endCommandBuffer(vk, *m_cmdBuffer);
+}
+
+tcu::TestStatus FragmentDensityMapTestInstance::iterate (void)
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice vkDevice = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+
+ submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
+
+ return verifyImage();
+}
+
+struct Vec4Sorter
+{
+ bool operator()(const tcu::Vec4& lhs, const tcu::Vec4& rhs) const
+ {
+ if (lhs.x() != rhs.x())
+ return lhs.x() < rhs.x();
+ if (lhs.y() != rhs.y())
+ return lhs.y() < rhs.y();
+ if (lhs.z() != rhs.z())
+ return lhs.z() < rhs.z();
+ return lhs.w() < rhs.w();
+ }
+};
+
+tcu::TestStatus FragmentDensityMapTestInstance::verifyImage (void)
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice vkDevice = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ SimpleAllocator memAlloc (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
+ de::UniquePtr<tcu::TextureLevel> outputImage (pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, memAlloc, *m_outputImage, VK_FORMAT_R8G8B8A8_UNORM, m_renderSize).release());
+ const tcu::ConstPixelBufferAccess& outputAccess = outputImage->getAccess();
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+
+ // log images
+ log << tcu::TestLog::ImageSet("Result", "Result images")
+ << tcu::TestLog::Image("Rendered", "Rendered output image", outputAccess)
+ << tcu::TestLog::EndImageSet;
+
+#if !DRY_RUN_WITHOUT_FDM_EXTENSION
+ deUint32 estimatedColorCount = m_testParams.fragmentArea.x() * m_testParams.fragmentArea.y();
+#else
+ deUint32 estimatedColorCount = 1u;
+#endif
+ tcu::Vec2 density{
+ 1.0f / static_cast<float>(m_testParams.fragmentArea.x()),
+ 1.0f / static_cast<float>(m_testParams.fragmentArea.y())
+ };
+ float densityMult = density.x() * density.y();
+
+ // create histogram of all image colors, check the value of inverted FragSizeEXT
+ std::map<tcu::Vec4, deUint32, Vec4Sorter> colorCount;
+ for (int y = 0; y < outputAccess.getHeight(); y++)
+ {
+ for (int x = 0; x < outputAccess.getWidth(); x++)
+ {
+ tcu::Vec4 outputColor = outputAccess.getPixel(x, y);
+ float densityClamped = outputColor.z() * outputColor.w();
+ if ((densityClamped + 0.01) < densityMult)
+ return tcu::TestStatus::fail("Wrong value of FragSizeEXT variable");
+ auto it = colorCount.find(outputColor);
+ if (it == end(colorCount))
+ it = colorCount.insert({ outputColor, 0u }).first;
+ it->second++;
+ }
+ }
+
+ // check if color count is the same as estimated one
+ for (const auto& color : colorCount)
+ {
+ if (color.second > estimatedColorCount)
+ return tcu::TestStatus::fail("Wrong color count");
+ }
+
+ return tcu::TestStatus::pass("Pass");
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createFragmentDensityMapTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> fdmTests (new tcu::TestCaseGroup(testCtx, "fragment_density_map", "VK_EXT_fragment_density_map extension tests"));
+
+ std::vector<tcu::UVec2> fragmentArea
+ {
+ { 1, 2 },
+ { 2, 1 },
+ { 2, 2 }
+ };
+
+ for (const auto& area : fragmentArea)
+ {
+ std::stringstream str;
+ str << "_" << area.x() << "_" << area.y();
+ fdmTests->addChild(new FragmentDensityMapTest(testCtx, std::string("static_subsampled") + str.str(), "", TestParams(false, false, area)));
+ fdmTests->addChild(new FragmentDensityMapTest(testCtx, std::string("dynamic_subsampled") + str.str(), "", TestParams(true, false, area)));
+ fdmTests->addChild(new FragmentDensityMapTest(testCtx, std::string("static_nonsubsampled") + str.str(), "", TestParams(false, true, area)));
+ fdmTests->addChild(new FragmentDensityMapTest(testCtx, std::string("dynamic_nonsubsampled") + str.str(), "", TestParams(true, true, area)));
+ }
+
+ return fdmTests.release();
+}
+
+} // renderpass
+
+} // vkt