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.
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;
}