Disable conflicting fragment shading rate features
authorPiotr Byszewski <piotr.byszewski@mobica.com>
Mon, 23 Nov 2020 09:48:06 +0000 (10:48 +0100)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Mon, 7 Dec 2020 16:01:19 +0000 (11:01 -0500)
Features of VK_EXT_fragment_density_map and VK_NV_shading_rate_image cannot
be enabled at the same time as features of VK_KHR_fragment_shading_rate.

Instead if the VK_KHR_fragment_shading_rate features are enabled then
disable the VK_EXT_fragment_density_map and VK_NV_shading_rate_image features
and use a temporary device with them enabled for testing.

Components: Vulkan, Framework

VK-GL-CTS Issue: 2665

Affects: *

Change-Id: I544c021ed963bd453635283282a6a272383e448e

external/vulkancts/framework/vulkan/vkDeviceFeatures.cpp
external/vulkancts/modules/vulkan/fragment_shader_interlock/vktFragmentShaderInterlockBasic.cpp
external/vulkancts/modules/vulkan/fragment_shader_interlock/vktFragmentShaderInterlockBasic.hpp
external/vulkancts/modules/vulkan/fragment_shader_interlock/vktFragmentShaderInterlockTests.cpp
external/vulkancts/modules/vulkan/renderpass/vktRenderPassFragmentDensityMapTests.cpp

index 2a5c8cc..3e51299 100644 (file)
@@ -32,8 +32,11 @@ DeviceFeatures::DeviceFeatures       (const InstanceInterface&                       vki,
                                                                 const std::vector<std::string>&        instanceExtensions,
                                                                 const std::vector<std::string>&        deviceExtensions)
 {
-       VkPhysicalDeviceRobustness2FeaturesEXT*         robustness2Features             = nullptr;
-       VkPhysicalDeviceImageRobustnessFeaturesEXT*     imageRobustnessFeatures = nullptr;
+       VkPhysicalDeviceRobustness2FeaturesEXT*                 robustness2Features                     = nullptr;
+       VkPhysicalDeviceImageRobustnessFeaturesEXT*             imageRobustnessFeatures         = nullptr;
+       VkPhysicalDeviceFragmentShadingRateFeaturesKHR* fragmentShadingRateFeatures     = nullptr;
+       VkPhysicalDeviceShadingRateImageFeaturesNV*             shadingRateImageFeatures        = nullptr;
+       VkPhysicalDeviceFragmentDensityMapFeaturesEXT*  fragmentDensityMapFeatures      = nullptr;
 
        m_coreFeatures2         = initVulkanStructure();
        m_vulkan11Features      = initVulkanStructure();
@@ -77,14 +80,23 @@ DeviceFeatures::DeviceFeatures      (const InstanceInterface&                       vki,
                                        featuresToFillFromBlob.push_back(p);
                                else
                                {
-                                       if (p->getFeatureDesc().sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT)
-                                               robustness2Features = reinterpret_cast<VkPhysicalDeviceRobustness2FeaturesEXT*>(p->getFeatureTypeRaw());
-                                       else if (p->getFeatureDesc().sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT)
-                                               imageRobustnessFeatures = reinterpret_cast<VkPhysicalDeviceImageRobustnessFeaturesEXT*>(p->getFeatureTypeRaw());
+                                       VkStructureType structType              = p->getFeatureDesc().sType;
+                                       void*                   rawStructPtr    = p->getFeatureTypeRaw();
+
+                                       if (structType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT)
+                                               robustness2Features = reinterpret_cast<VkPhysicalDeviceRobustness2FeaturesEXT*>(rawStructPtr);
+                                       else if (structType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT)
+                                               imageRobustnessFeatures = reinterpret_cast<VkPhysicalDeviceImageRobustnessFeaturesEXT*>(rawStructPtr);
+                                       else if (structType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR)
+                                               fragmentShadingRateFeatures = reinterpret_cast<VkPhysicalDeviceFragmentShadingRateFeaturesKHR*>(rawStructPtr);
+                                       else if (structType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV)
+                                               shadingRateImageFeatures = reinterpret_cast<VkPhysicalDeviceShadingRateImageFeaturesNV*>(rawStructPtr);
+                                       else if (structType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT)
+                                               fragmentDensityMapFeatures = reinterpret_cast<VkPhysicalDeviceFragmentDensityMapFeaturesEXT*>(rawStructPtr);
 
                                        // add to chain
-                                       *nextPtr = p->getFeatureTypeRaw();
-                                       nextPtr = p->getFeatureTypeNext();
+                                       *nextPtr        = rawStructPtr;
+                                       nextPtr         = p->getFeatureTypeNext();
                                }
                                m_features.push_back(p);
                        }
@@ -121,6 +133,19 @@ DeviceFeatures::DeviceFeatures     (const InstanceInterface&                       vki,
                imageRobustnessFeatures->robustImageAccess      = false;
        }
        m_coreFeatures2.features.robustBufferAccess = false;
+
+       // Disable VK_EXT_fragment_density_map and VK_NV_shading_rate_image features
+       // that must: not be enabled if KHR fragment shading rate features are enabled.
+       if (fragmentShadingRateFeatures &&
+               (fragmentShadingRateFeatures->pipelineFragmentShadingRate ||
+                       fragmentShadingRateFeatures->primitiveFragmentShadingRate ||
+                       fragmentShadingRateFeatures->attachmentFragmentShadingRate))
+       {
+               if (shadingRateImageFeatures)
+                       shadingRateImageFeatures->shadingRateImage = false;
+               if (fragmentDensityMapFeatures)
+                       fragmentDensityMapFeatures->fragmentDensityMap = false;
+       }
 }
 
 bool DeviceFeatures::verifyFeatureAddCriteria (const FeatureStructCreationData& item, const std::vector<VkExtensionProperties>& properties)
index cd215af..14806a7 100644 (file)
@@ -48,6 +48,7 @@
 
 #include "vktTestGroupUtil.hpp"
 #include "vktTestCase.hpp"
+#include "vktCustomInstancesDevices.hpp"
 
 #include "deDefs.h"
 #include "deMath.h"
@@ -57,6 +58,7 @@
 
 #include "tcuTestCase.hpp"
 #include "tcuTestLog.hpp"
+#include "tcuCommandLine.hpp"
 
 #include <string>
 #include <sstream>
@@ -86,6 +88,63 @@ typedef enum
        INT_SHADING_RATE_UNORDERED,
 } Interlock;
 
+de::SharedPtr<Move<vk::VkDevice>>      g_singletonDevice;
+
+VkDevice getDevice(Context& context, Interlock interlock)
+{
+       if (interlock == INT_SHADING_RATE_ORDERED || interlock == INT_SHADING_RATE_UNORDERED)
+       {
+               if (!g_singletonDevice)
+               {
+                       const float queuePriority = 1.0f;
+
+                       // Create a universal queue that supports graphics and compute
+                       const VkDeviceQueueCreateInfo   queueParams =
+                       {
+                               VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,     // VkStructureType                              sType;
+                               DE_NULL,                                                                        // const void*                                  pNext;
+                               0u,                                                                                     // VkDeviceQueueCreateFlags             flags;
+                               context.getUniversalQueueFamilyIndex(),         // deUint32                                             queueFamilyIndex;
+                               1u,                                                                                     // deUint32                                             queueCount;
+                               &queuePriority                                                          // const float*                                 pQueuePriorities;
+                       };
+
+                       const char * extensions[] =
+                       {
+                               "VK_EXT_fragment_shader_interlock",
+                               "VK_NV_shading_rate_image",
+                       };
+
+                       VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT      fragmentShaderInterlockFeatures = initVulkanStructure();
+                       VkPhysicalDeviceShadingRateImageFeaturesNV                      shadingRateImageFeatures                = initVulkanStructure(&fragmentShaderInterlockFeatures);
+                       VkPhysicalDeviceFeatures2                                                       features2                                               = initVulkanStructure(&shadingRateImageFeatures);
+
+                       context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &features2);
+
+                       const VkDeviceCreateInfo                                        deviceCreateInfo =
+                       {
+                               VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,                                                   //sType;
+                               &features2,                                                                                                             //pNext;
+                               (VkDeviceCreateFlags)0u,                                                                                //flags
+                               1,                                                                                                                              //queueRecordCount;
+                               &queueParams,                                                                                                   //pRequestedQueues;
+                               0,                                                                                                                              //layerCount;
+                               DE_NULL,                                                                                                                //ppEnabledLayerNames;
+                               DE_LENGTH_OF_ARRAY(extensions),                                                                 // deUint32                                                     enabledExtensionCount;
+                               extensions,                                                                                                             // const char* const*                           ppEnabledExtensionNames;
+                               DE_NULL,                                                                                                                //pEnabledFeatures;
+                       };
+
+                       Move<VkDevice> device = createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), context.getPlatformInterface(), context.getInstance(), context.getInstanceInterface(), context.getPhysicalDevice(), &deviceCreateInfo);
+                       g_singletonDevice = de::SharedPtr<Move<VkDevice>>(new Move<VkDevice>(device));
+               }
+
+               return g_singletonDevice->get();
+       }
+
+       return context.getDevice();
+}
+
 struct CaseDef
 {
        deUint32 dim;
@@ -165,16 +224,22 @@ void FSITestCase::checkSupport(Context& context) const
                TCU_THROW(NotSupportedError, "Fragment shader pixel interlock not supported");
        }
 
-       if ((m_data.interlock == INT_SHADING_RATE_ORDERED || m_data.interlock == INT_SHADING_RATE_UNORDERED) &&
-               !context.getFragmentShaderInterlockFeaturesEXT().fragmentShaderShadingRateInterlock)
+       if ((m_data.interlock == INT_SHADING_RATE_ORDERED || m_data.interlock == INT_SHADING_RATE_UNORDERED))
        {
-               TCU_THROW(NotSupportedError, "Fragment shader shading rate interlock not supported");
-       }
+               if (!context.getFragmentShaderInterlockFeaturesEXT().fragmentShaderShadingRateInterlock)
+                       TCU_THROW(NotSupportedError, "Fragment shader shading rate interlock not supported");
 
-       if ((m_data.interlock == INT_SHADING_RATE_ORDERED || m_data.interlock == INT_SHADING_RATE_UNORDERED) &&
-               !context.getShadingRateImageFeatures().shadingRateImage)
-       {
-               TCU_THROW(NotSupportedError, "Shading rate image not supported");
+               context.requireDeviceFunctionality("VK_NV_shading_rate_image");
+
+               // We need to query the VK_NV_shading_rate_image features because they might be disabled
+               // in the default context due to a conflict with VK_KHR_fragment_shading_rate.
+               VkPhysicalDeviceShadingRateImageFeaturesNV      shadingRateImageFeatures        = initVulkanStructure();
+               VkPhysicalDeviceFeatures2KHR                            features2                                       = initVulkanStructure(&shadingRateImageFeatures);
+
+               context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &features2);
+
+               if (!shadingRateImageFeatures.shadingRateImage)
+                       TCU_THROW(NotSupportedError, "Shading rate image not supported");
        }
 }
 
@@ -321,7 +386,7 @@ TestInstance* FSITestCase::createInstance (Context& context) const
 tcu::TestStatus FSITestInstance::iterate (void)
 {
        const DeviceInterface&  vk                                              = m_context.getDeviceInterface();
-       const VkDevice                  device                                  = m_context.getDevice();
+       const VkDevice                  device                                  = getDevice(m_context, m_data.interlock);
        Allocator&                              allocator                               = m_context.getDefaultAllocator();
        VkFlags                                 allShaderStages                 = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
        VkFlags                                 allPipelineStages               = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
@@ -387,7 +452,7 @@ tcu::TestStatus FSITestInstance::iterate (void)
 
        flushAlloc(vk, device, buffer->getAllocation());
 
-       const VkQueue                                   queue                                   = m_context.getUniversalQueue();
+       const VkQueue                                   queue                                   = getDeviceQueue(vk, device, m_context.getUniversalQueueFamilyIndex(), 0);
        Move<VkCommandPool>                             cmdPool                                 = createCommandPool(vk, device, 0, m_context.getUniversalQueueFamilyIndex());
        Move<VkCommandBuffer>                   cmdBuffer                               = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
 
@@ -896,5 +961,10 @@ tcu::TestCaseGroup*        createBasicTests (tcu::TestContext& testCtx)
        return group.release();
 }
 
+void cleanupDevice()
+{
+       g_singletonDevice.clear();
+}
+
 }      // FragmentShaderInterlock
 }      // vkt
index 4709414..c986ede 100644 (file)
@@ -34,18 +34,25 @@ namespace FragmentShaderInterlock
 namespace
 {
 
-void createChildren (tcu::TestCaseGroup* group)
+static void createChildren (tcu::TestCaseGroup* group)
 {
        tcu::TestContext&       testCtx         = group->getTestContext();
 
        group->addChild(createBasicTests(testCtx));
 }
 
+static void cleanupGroup (tcu::TestCaseGroup* group)
+{
+       DE_UNREF(group);
+       // Destroy singleton objects.
+       cleanupDevice();
+}
+
 } // anonymous
 
 tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
 {
-       return createTestGroup(testCtx, "fragment_shader_interlock", "Fragment shader interlock tests", createChildren);
+       return createTestGroup(testCtx, "fragment_shader_interlock", "Fragment shader interlock tests", createChildren, cleanupGroup);
 }
 
 } // FragmentShaderInterlock
index b25ee17..9959612 100644 (file)
@@ -25,6 +25,8 @@
 #include "pipeline/vktPipelineImageUtil.hpp"
 #include "deMath.h"
 #include "vktTestCase.hpp"
+#include "vktTestGroupUtil.hpp"
+#include "vktCustomInstancesDevices.hpp"
 #include "vkImageUtil.hpp"
 #include "vkQueryUtil.hpp"
 #include "vkCmdUtil.hpp"
 #include "vkObjUtil.hpp"
 #include "vkBarrierUtil.hpp"
 #include "vkBuilderUtil.hpp"
+#include "tcuCommandLine.hpp"
 #include "tcuStringTemplate.hpp"
 #include "tcuTextureUtil.hpp"
 #include "tcuTestLog.hpp"
 #include <sstream>
 #include <vector>
+#include <set>
 
 // Each test generates an image with a color gradient where all colors should be unique when rendered without density map
 // ( and for multi_view tests - the quantity of each color in a histogram should be 2 instead of 1 ).
@@ -104,6 +108,79 @@ struct Vertex4RGBA
        tcu::Vec4       color;
 };
 
+de::SharedPtr<Move<vk::VkDevice>>      g_singletonDevice;
+
+static std::vector<std::string> removeExtensions (const std::vector<std::string>& a, const std::vector<const char*>& b)
+{
+       std::vector<std::string>        res;
+       std::set<std::string>           removeExts      (b.begin(), b.end());
+
+       for (std::vector<std::string>::const_iterator aIter = a.begin(); aIter != a.end(); ++aIter)
+       {
+               if (!de::contains(removeExts, *aIter))
+                       res.push_back(*aIter);
+       }
+
+       return res;
+}
+
+VkDevice getDevice(Context& context)
+{
+       if (!g_singletonDevice)
+       {
+               const float queuePriority = 1.0f;
+
+               // Create a universal queue that supports graphics and compute
+               const VkDeviceQueueCreateInfo   queueParams =
+               {
+                       VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,     // VkStructureType                              sType;
+                       DE_NULL,                                                                        // const void*                                  pNext;
+                       0u,                                                                                     // VkDeviceQueueCreateFlags             flags;
+                       context.getUniversalQueueFamilyIndex(),         // deUint32                                             queueFamilyIndex;
+                       1u,                                                                                     // deUint32                                             queueCount;
+                       &queuePriority                                                          // const float*                                 pQueuePriorities;
+               };
+
+               // \note Extensions in core are not explicitly enabled even though
+               //               they are in the extension list advertised to tests.
+               std::vector<const char*>        extensionPtrs;
+               std::vector<const char*>        coreExtensions;
+               getCoreDeviceExtensions(context.getUsedApiVersion(), coreExtensions);
+               std::vector<std::string>        nonCoreExtensions(removeExtensions(context.getDeviceExtensions(), coreExtensions));
+
+               extensionPtrs.resize(nonCoreExtensions.size());
+
+               for (size_t ndx = 0; ndx < nonCoreExtensions.size(); ++ndx)
+                       extensionPtrs[ndx] = nonCoreExtensions[ndx].c_str();
+
+               VkPhysicalDeviceFragmentDensityMapFeaturesEXT   fragmentDensityMapFeatures      = initVulkanStructure();
+               VkPhysicalDeviceFragmentDensityMap2FeaturesEXT  fragmentDensityMap2Features     = initVulkanStructure(&fragmentDensityMapFeatures);
+               VkPhysicalDeviceFeatures2                                               features2                                       = initVulkanStructure(&fragmentDensityMap2Features);
+
+               context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &features2);
+               const VkPhysicalDeviceFeatures2 & feature2ptr = context.getDeviceFeatures2();
+
+               const VkDeviceCreateInfo                                        deviceCreateInfo =
+               {
+                       VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,                                                   //sType;
+                       &feature2ptr,                                                                                                   //pNext;
+                       (VkDeviceCreateFlags)0u,                                                                                //flags
+                       1,                                                                                                                              //queueRecordCount;
+                       &queueParams,                                                                                                   //pRequestedQueues;
+                       0,                                                                                                                              //layerCount;
+                       DE_NULL,                                                                                                                //ppEnabledLayerNames;
+                       (deUint32)extensionPtrs.size(),                 // deUint32                             enabledExtensionCount;
+                       (extensionPtrs.empty() ? DE_NULL : &extensionPtrs[0]),                  // const char* const*                           ppEnabledExtensionNames;
+                       DE_NULL,                                                                                                                //pEnabledFeatures;
+               };
+
+               Move<VkDevice> device = createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), context.getPlatformInterface(), context.getInstance(), context.getInstanceInterface(), context.getPhysicalDevice(), &deviceCreateInfo);
+               g_singletonDevice = de::SharedPtr<Move<VkDevice>>(new Move<VkDevice>(device));
+       }
+
+       return g_singletonDevice->get();
+}
+
 std::vector<Vertex4RGBA> createFullscreenMesh(deUint32 viewCount, tcu::Vec2 redGradient, tcu::Vec2 greenGradient)
 {
        DE_ASSERT(viewCount > 0);
@@ -1073,7 +1150,14 @@ void FragmentDensityMapTest::checkSupport(Context& context) const
 #else
        context.requireDeviceFunctionality("VK_EXT_fragment_density_map");
 
-       const auto& fragmentDensityMapFeatures          = context.getFragmentDensityMapFeaturesEXT();
+
+
+       VkPhysicalDeviceFragmentDensityMapFeaturesEXT           fragmentDensityMapFeatures              = initVulkanStructure();
+       VkPhysicalDeviceFragmentDensityMap2FeaturesEXT          fragmentDensityMap2Features             = initVulkanStructure(&fragmentDensityMapFeatures);
+       VkPhysicalDeviceFeatures2KHR                                            features2                                               = initVulkanStructure(&fragmentDensityMap2Features);
+
+       context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &features2);
+
        const auto& fragmentDensityMap2Properties       = context.getFragmentDensityMap2PropertiesEXT();
 
        if (!fragmentDensityMapFeatures.fragmentDensityMap)
@@ -1086,7 +1170,7 @@ void FragmentDensityMapTest::checkSupport(Context& context) const
        if (m_testParams.deferredDensityMap)
        {
                context.requireDeviceFunctionality("VK_EXT_fragment_density_map2");
-               if (!context.getFragmentDensityMap2FeaturesEXT().fragmentDensityMapDeferred)
+               if (!fragmentDensityMap2Features.fragmentDensityMapDeferred)
                        TCU_THROW(NotSupportedError, "fragmentDensityMapDeferred feature is not supported");
        }
        if (m_testParams.subsampledLoads)
@@ -1147,9 +1231,10 @@ FragmentDensityMapTestInstance::FragmentDensityMapTestInstance(Context&                          conte
        m_viewMask                      = (m_testParams.viewCount > 1) ? ((1u << m_testParams.viewCount) - 1u) : 0u;
 
        const DeviceInterface&          vk                                                      = m_context.getDeviceInterface();
-       const VkDevice                          vkDevice                                        = m_context.getDevice();
+       const VkDevice                          vkDevice                                        = getDevice(m_context);
        const VkPhysicalDevice          vkPhysicalDevice                        = m_context.getPhysicalDevice();
        const deUint32                          queueFamilyIndex                        = m_context.getUniversalQueueFamilyIndex();
+       const VkQueue                           queue                                           = getDeviceQueue(vk, vkDevice, queueFamilyIndex, 0);
        SimpleAllocator                         memAlloc                                        (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), vkPhysicalDevice));
        const VkComponentMapping        componentMappingRGBA            = makeComponentMappingRGBA();
 
@@ -1271,7 +1356,7 @@ FragmentDensityMapTestInstance::FragmentDensityMapTestInstance(Context&                           conte
 
                        copyBufferToImage
                        (
-                               vk, vkDevice, m_context.getUniversalQueue(), queueFamilyIndex,
+                               vk, vkDevice, queue, queueFamilyIndex,
                                *stagingBuffer, stagingBufferSize,
                                densityMapImageSize, densityMapImageLayers, **m_densityMapImages[mapIndex]
                        );
@@ -1714,8 +1799,8 @@ FragmentDensityMapTestInstance::FragmentDensityMapTestInstance(Context&                           conte
 tcu::TestStatus FragmentDensityMapTestInstance::iterate (void)
 {
        const DeviceInterface&  vk                      = m_context.getDeviceInterface();
-       const VkDevice                  vkDevice        = m_context.getDevice();
-       const VkQueue                   queue           = m_context.getUniversalQueue();
+       const VkDevice                  vkDevice        = getDevice(m_context);
+       const VkQueue                   queue           = getDeviceQueue(vk, vkDevice, m_context.getUniversalQueueFamilyIndex(), 0);
 
        submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
 
@@ -1743,9 +1828,9 @@ struct Vec4Sorter
 tcu::TestStatus FragmentDensityMapTestInstance::verifyImage (void)
 {
        const DeviceInterface&                          vk                                      = m_context.getDeviceInterface();
-       const VkDevice                                          vkDevice                        = m_context.getDevice();
-       const VkQueue                                           queue                           = m_context.getUniversalQueue();
+       const VkDevice                                          vkDevice                        = getDevice(m_context);
        const deUint32                                          queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       const VkQueue                                           queue                           = getDeviceQueue(vk, vkDevice, queueFamilyIndex, 0);
        SimpleAllocator                                         memAlloc                        (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
        tcu::UVec2                                                      renderSize                      (m_renderSize.x(), m_renderSize.y());
        de::UniquePtr<tcu::TextureLevel>        outputImage                     (pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, memAlloc, *m_outputImage, VK_FORMAT_R8G8B8A8_UNORM, renderSize).release());
@@ -1796,9 +1881,9 @@ tcu::TestStatus FragmentDensityMapTestInstance::verifyImage (void)
 
 } // anonymous
 
-tcu::TestCaseGroup* createFragmentDensityMapTests (tcu::TestContext& testCtx)
+static void createChildren (tcu::TestCaseGroup* fdmTests)
 {
-       de::MovePtr<tcu::TestCaseGroup> fdmTests(new tcu::TestCaseGroup(testCtx, "fragment_density_map", "VK_EXT_fragment_density_map and VK_EXT_fragment_density_map2 extensions tests"));
+       tcu::TestContext&       testCtx         = fdmTests->getTestContext();
 
        const struct
        {
@@ -1967,8 +2052,18 @@ tcu::TestCaseGroup* createFragmentDensityMapTests (tcu::TestContext& testCtx)
        params.coarseReconstruction     = true;
        propertiesGroup->addChild(new FragmentDensityMapTest(testCtx, "subsampled_coarse_reconstruction", "", params));
        fdmTests->addChild(propertiesGroup.release());
+}
 
-       return fdmTests.release();
+static void cleanupGroup (tcu::TestCaseGroup* group)
+{
+       DE_UNREF(group);
+       // Destroy singleton objects.
+       g_singletonDevice.clear();
+}
+
+tcu::TestCaseGroup* createFragmentDensityMapTests (tcu::TestContext& testCtx)
+{
+       return createTestGroup(testCtx, "fragment_density_map", "VK_EXT_fragment_density_map and VK_EXT_fragment_density_map2 extensions tests", createChildren, cleanupGroup);
 }
 
 } // renderpass