Search code examples
glslshadervulkan

How can I read a 3D image with R8_UINT format and SAMPLED_IMAGE descriptor type in a glsl shader?


I'm using vulkan to create a 3D image (VK_IMAGE_TYPE_3D) with an unsigned 8-bit integer per texel format (VK_FORMAT_R8_UINT). This image is attached to a descriptor set with the type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE which is given to the glsl shader. The reason I need to do this is because the 3D image represents voxel data and the 8 bits for each texel represent an index in a material array for that voxel.

The problem is that I cant find a way to get a 8bit unsigned integer that corresponds to an image coordinate (read data from the image). I have tried the following:

Attaching to a uimage3D

layout (binding = 2, r8ui) uniform uimage3D scene; and imageLoad(scene, ivec3(0,0,0)).r

But this results in the validation layer error

Type mismatch on descriptor slot 0.2 (expected VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) but descriptor of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE The Vulkan spec states: layout must be consistent with the layout of the compute shader specified in stage (https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#VUID-VkComputePipelineCreateInfo-layout-00703)

I don't want to use a storage image though because I need the image to be optimized for fast read-only access.

Attaching to texture3D

layout (binding = 2, r8ui) uniform texture3D scene; results in the shader compile error:

error: 'r8ui' : only apply to images


Solution

  • I don't want to use a storage image though because I need the image to be optimized for fast read-only access.

    That's not what using a sampled image does.

    If you want to use an image as a sampled image, that means you want to employ the capabilities of samplers. You want to access pixels via normalized coordinates, or you want to do filtering, or texture coordinate wrapping, or automatic mipmap selection, or something like that.

    You don't use a sampled image because you want read-only accesses to be faster. That's why storage images have the readonly layout qualifier.

    If all you want is to have read-only access to a single mipmap level of an image, using integer texel coordinates with no filtering or wrapping, then what you want is a storage image.

    However, if you do want to use a sampled image, then you have to follow the rules of sampled images. In particular, you don't specify the format of them in the shader. You specify the general category of the format (floating-point vs. signed integer vs. unsigned integer), but that is specified by the type of the variable (sampler3D vs. isampler3D vs. usampler3D, respectively).

    You also need a sampler, either via a combined sampler/image pair specified in the descriptor (which map to the GLSL samplerX types) or via two separate variables: a texture type and a sampler type, which the shader will combine itself.