2
votes

I have a compute shader that reads a signed normalized integer image using imageLoad.

The image itself (which contains both positive and negative values) is created as a R16G16_SNORM and is written by a fragment shader in a previous gpass. The imageview bound to the descriptorsetlayout binding in the compute shader is also created with the same R16G16_SNORM format.

Everything works as expected.

Yesterday I realized that in the compute shader I used the wrong image format qualifier rg16. A bit puzzled (I could not understand how it could work properly reading an unsigned normalized value) I corrected to rg16_snorm, and.. nothing changed.

I performed several tests (I even specified a rg16f) and always had the same (correct, [-1,1] signed) result.

It seems like Vulkan (at least my implementation) silently ignores any image format qualifier, and falls back (I guess) to the imageview format bound to the descriptorset.

This seems to be in line with the spec regarding format in imageview creation

format is a VkFormat describing the format and type used to interpret texel blocks in the image

but then in Appendix A (Vulkan Environment for SPIR-V - "Compatibility Between SPIR-V Image Formats And Vulkan Formats") there is a clear distinction between Rg16 and Rg16Snorm.. so:

is it a bug or a feature?

I am working with an Nvidia 2070 Super under ubuntu 20.04

UPDATE

The initial image writing operation happens as the result of a fragment shader color attachment output, and as such, there is no descriptorsetlayout binding declaration. The fragment shader outputs a vec2 to the R16G16_SNORM color attachment as specified by the active framebuffer and renderpass.

The resulting image (after the relevant barriers) is then read (correctly, despite the wrong layout qualifier) by a compute shader as an image/imageLoad operation.

Note that validation layers are enabled and silent.

Note also that the resulting values are far from random, and exactly match the expected values (both positive and negative), using either rg16, rg16f or rg16_snorm.

1

1 Answers

1
votes

What you're getting is undefined behavior.

There is a validation check on Image Write Operations that prevents the OpTypeImage's format (equivalent to the layout format specifier in GLSL) from being incompatible with the backing VkImageView's format:

If the image format of the OpTypeImage is not compatible with the VkImageView’s format, the write causes the contents of the image’s memory to become undefined.

Note that when it says "compatible", it doesn't mean image view compatibility; it means "exactly match". Your OpTypeImage format did not exactly match that of the shader, so your writes were undefined. And "undefined" can mean "works as if you had specified the correct format".