shader-slang / slang

Making it easier to work with shaders
MIT License
1.97k stars 167 forks source link

SPIRV validation error with `Texture<uint8_t>` and other small int types #4748

Closed dubiousconst282 closed 1 month ago

dubiousconst282 commented 1 month ago
RWTexture2D<uint8_t> g_Image;

[numthreads(8, 8, 1)]
void ComputeMain(uint2 tid: SV_DispatchThreadID) {
    g_Image[tid] = uint8_t((tid.x + tid.y) % 2);
}

Produces:

$ slangc -target spirv test.slang | spirv-val --target-env vulkan1.3
error: line 21: [VUID-StandaloneSpirv-OpTypeImage-04656] Expected Sampled Type to be a 32-bit int, 64-bit int or 32-bit float scalar type for Vulkan environment
  %19 = OpTypeImage %uchar 2D 2 0 0 2 R8ui

On a related note, I think that integer texture types should not infer the format in OpTypeImage since the float textures are emitted as having unknown format by default, I suspect this would cause UB when the formats don't match (not sure if the validation layers can catch this, it would probably be more problematic with bindless).

jkwak-work commented 1 month ago

I am not sure what the expected behavior is. To make the validation happy, you can change the type to one of listed types like,

RWTexture2D<int> g_Image;

But it sounds like the given code is expected to work without the validation error, is it?

I am not sure if I understood the note correctly. Do you mean that when the type for RWTexture2D is an integer type, it should use float instead of the given type?

Or is the expected behavior printing an error from Slang when the type is small inter types?

dubiousconst282 commented 1 month ago

This seems like an odd limitation from the SPIRV specification but I believe normalizing the sampled type to be int instead of int8/etc would be enough to solve the validation error.

The 32-bit int types do compile and run correctly, but the format is inferred from the element type (int -> R32i, int4 -> Rgba32i, etc) whereas float textures never specify a format, so AFAIK there is currently no non-UB way to use smaller formats:

         %21 = OpTypeImage %int 2D 2 0 0 2 R32i         // RWTexture2D<int>
         %21 = OpTypeImage %float 2D 2 0 0 2 Unknown    // RWTexture2D<float>

I think always emitting unknown format would be more consistent but I'm also not sure it's the best idea since it could have performance implications?

jkwak-work commented 1 month ago

Just to be clear, the validation error is on the first argument to OpTyoeImage not the last argument. For the given repro case, OpTypeImage doesn't recognize "%uchar" and it wants it to be int or float.

OpTypeImage %uchar

I am not sure if the last argument matters regardless it is unknown or a specific type.

csyonghe commented 1 month ago

You can use [format("rgba8")] Texture2D tex;to specify the format operand of an image.