microsoft / DirectXShaderCompiler

This repo hosts the source for the DirectX Shader Compiler which is based on LLVM/Clang.
Other
3k stars 671 forks source link

[SPIR-V] Feature request: add annotations for images readonly, writeonly #4530

Open bsekura opened 2 years ago

bsekura commented 2 years ago

Currently in spirv backend, Texture objects are always translated to sampled image. OpTypeImage will have sampled set to "will be used with sampler" (https://www.khronos.org/registry/SPIR-V/specs/1.0/SPIRV.html#OpTypeImage).

There are many cases where we want to use readonly Texture without sampling (e.g. the format does not support sampling - for example uint64). In this case we want it to be a readonly storage image and GLSL provides correct decorations to do that:

// 1.comp
// glslangValidator -H -V -o 1.spv 1.comp
#version 450
layout(set = 0, binding = 0, rgba8) uniform highp readonly image2D image0;
layout(set = 0, binding = 1, rgba8) uniform highp writeonly image2D image1;
void main() {
    vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID));
    imageStore(image1, ivec2(gl_LocalInvocationID), pixel);
}

The result contains "NonWritable" and "NonReadable":

Decorate 12(image0) DescriptorSet 0
Decorate 12(image0) Binding 0
Decorate 12(image0) NonWritable
Decorate 17(gl_LocalInvocationID) BuiltIn LocalInvocationId
Decorate 27(image1) DescriptorSet 0
Decorate 27(image1) Binding 1
Decorate 27(image1) NonReadable

Using HLSL with Vulkan makes it difficult to handle these cases, because read-only images will still be treated as UAVs but we want them to be SRVs.

Somewhat related discussion: https://github.com/KhronosGroup/SPIRV-Cross/issues/1306

To illustrate the problem, let me provide a more detailed use case.

Consider the following HLSL shader, which is pretty much identical to GLSL sample above:

// 1.hlsl
Texture2D<uint64_t> image0 : register(t0, space0);
RWTexture2D<uint64_t> image1 : register(u0, space0);

[numthreads (16, 16, 1)]
void main(uint3 dispatchThreadId : SV_DispatchThreadID) {
    uint64_t pixel = image0[dispatchThreadId.xy];
    image1[dispatchThreadId.xy] = pixel;
}

Here we are using uint64_t format, which does not support sampling. On DirectX 12, we can use SRV descriptor for image0.

For Vulkan, DXC spirv codegen will generate "sampled image" for image0 and reflection tools (e.g. spirv_reflect) will treat it as SRV and descriptor type DESCRIPTOR_TYPE_SAMPLED_IMAGE. What we really want is SRV and DESCRIPTOR_TYPE_STORAGE_IMAGE. The only way to make it work is to change image0 to RWTexture2D, which will result in it being an UAV now (and register specification must change as well).

I believe providing "readony", "writeonly" annotations and emitting corresponding decorations would make it easy to tools to correctly map to Vulkan descriptor types.

pow2clk commented 2 years ago

Hi @sudonatalie @kuhar! SPIRV bug for you