Search code examples
c++graphicsshadervulkancompute-shader

Non uniform texture access in vulkan glsl


I am trying to write a compute shader that raytraces an image, pixels on the right of the yz plane sample from image A, those on the left from image B.

I don't want to have to sample both images so I am trying to use non uniform access by doing:

texture(textures[nonuniformEXT(sampler_id)], vec2(0.5));

and enabling the relevant extension in the shader. This triggers the following validaiton layer error:

Message: Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01091 ] Object 0: handle = 0x55a1c21315d0, name = Logical device: AMD RADV RAVEN2, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xa7bb8db6 | vkCreateShaderModule(): The SPIR-V Capability (SampledImageArrayNonUniformIndexing) was declared, but none of the requirements were met to use it. The Vulkan spec states: If pCode declares any of the capabilities listed in the SPIR-V Environment appendix, one of the corresponding requirements must be satisfied (https://vulkan.lunarg.com/doc/view/1.2.182.0/linux/1.2-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01091)

If I read the docs it would seem this is a hardware feature, but someone said I can still have non uniform access if create the correct extension object. But I am not entirely sure how to do that.


Solution

  • You have to enable the feature at device creation.

    You can check for support of the feature by calling vkGetPhysicalDeviceFeatures2 archive and following the pNext chain through to a VkPhysicalDeviceVulkan12Features archive, and checking that shaderSampledImageArrayNonUniformIndexing member is to VK_TRUE.

    After that when creating the device with vkCreateDevice archive, inside the pCreateInfo structure, in the pNext chain you have to have a VkPhysicalDeviceVulkan12Features archive with shaderSampledImageArrayNonUniformIndexing set to VK_TRUE.

    bool checkForNonUniformIndexing(VkPhysicalDevice physicalDevice)
    {
        VkPhysicalDeviceFeatures2 features;
        vkGetPhysicalDeviceFeatures2(physicalDevice, &features);
    
        if(features.sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
        {
            return false;
        }
    
        const VkPhysicalDeviceFeatures2* next = &features;
    
        do
        {
            // We know the type of the struct based on the `sType` member, but the first 
            // two fields are the same in all of these structs. There may be a more appropriate 
            // generic structure to use, but as long as we don't access any further members
            // we should be mostly fine.
            next = reinterpret_cast<const VkPhysicalDeviceFeatures*>(next->pNext);
            if(next.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES)
            {
                const VkPhysicalDeviceVulkan12Features* pVk12Features = reinterpret_cast<const VkPhysicalDeviceVulkan12Features*>(next);
                return next.shaderSampledImageArrayNonUniformIndexing == VK_TRUE;
            }
        } while(next);
    
        return false;
    }
    
    VkDevice* createDevice(VkPhysicalDevice physicalDevice, const VkAllocationCallbacks* pAllocator)
    {
        VkPhysicalDeviceVulkan12Features features;
        features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
        features.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
    
        VkDeviceCreateInfo createInfo;
        createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
        createInfo.pNext = &features;
        // Setting other create data
    
        VkDevice device;
        vkCreateDevice(physicalDevice, &createInfo, pAllocator, &device);
    
        // Error checking
    
        return device;
    }