Why Vulkan SPIR-V OpImageRead, OpImageFetch, OpImageWrite always use %v4float?

But OpenCL OpImageRead can return %v4half, when I enable extension “cl_khr_fp16”.
How can I change Vulkan GLSL shading kernel to load %v4half?
Or I can only change it in SPIR-V? I tried to change it in SPIR-V, but no performance improvement on Adreno 6xx GPUs? Does anybody know why?

GL_EXT_shader_16bit_storage GLSL extension, and Vulkan 1.1 or VK_KHR_16bit_storage extension. Adds loading a float16_t, f16vec*.

VK_KHR_shader_float16_int8 for float16 arithmetic. GL_EXT_shader_explicit_arithmetic_types_float16 in GLSL.

Thanks for reply.

I enabled GL_EXT_shader_16bit_storage and GL_EXT_shader_explicit_arithmetic_types_float16 extensions in GLSL.

And I compiled GLSL to spir-v file with --target-env=vulkan1.1 --target-spv=spv1.3. Then use spirv-dis to disassemble spir-v file. But OpImageFetch instruction still return %v4float.

GLSL code as follows:

     #version 450 core
     #extension GL_EXT_shader_16bit_storage: enable
     #extension GL_EXT_shader_explicit_arithmetic_types_float16: enable
     layout(set = 0, binding = 0) uniform sampler2D A; // array parameters
 
     void main() {
           f16vec4 a0 = f16vec4(texelFetch(A, ivec2(0, 0), 0));
     }

spir-v

 %115 = OpImageFetch %v4float %114 %113 Lod %int_0
 
 %116 = OpFConvert %v4half %115
 
 OpStore %b1 %116

That means texelFetch always returns the pixels to v4float, then convert to v4half. So how can I fetch v4half from a sampler2d(if it is a rgba16f format) image directly, not by casting v4float to v4half?

Thanks.

Ah sorry, did not realize this is an Image.
Well, there’s f16sampler2D and f16image2D. It would compile to what you want, but requires GL_AMD_gpu_shader_half_float_fetch. Can’t find anything that consumes SPIR-V with SPV_AMD_gpu_shader_half_float_fetch; might be a spec bug.

PS: if you can use Storage Buffers instead:

#version 450 core
#extension GL_EXT_shader_16bit_storage: enable
#extension GL_EXT_shader_explicit_arithmetic_types_float16: enable

layout (set=0, binding=0) buffer myStorageBuffer{
	f16vec4 data[];
} A;

void main(){
	f16vec4 a0 = A.data[0];
}

PPS: It seems your Adreno does not support any of the Float16 extensions in Vulkan though.

It should be noted that if your image format uses 16-bit floats, the system automatically converts them to 32-bit floats. Indeed, it may need to do this just to do filtering operations, in cases where the filtering unit or whatever is responsible for it can only interpolate 32-bit float values.

So it is entirely possible that there would be no performance gain from being able to get the 16-bit float values from the texture directly.

Thanks a alot. I see that. :+1:

Thanks. The register number of one thread may be smaller if using 16-bit float values. But I’m not sure.

I also tried image_load in GLSL, using same SPIR-V instruction(OpImageRead) with OpenCL read_imageh. But OpenCL kernel SPIR-V returned v4half by OpImageRead when extension cl_khr_fp16 enabled. GLSL kernel SPIR-V returned v4float when extension GL_EXT_shader_16bit_storage enabled (image2D, rgba16f).

I just donot know why. what are the differencies (extension)?

Difference is GL_EXT_shader_16bit_storage does not support Samplers, nor Storage Images (except the conversion way). It supports Uniforms and Storage Buffers:

This extension adds minimal support to allow loading/storing 8/16-bit
integer and floating-point scalar and vector types from/to uniform and
storage buffers, and to convert those values to and from 32-bit types.

Also should work in Push Constants and Shader Interfaces.

Ok, I asked the Khronos Group: https://github.com/KhronosGroup/Vulkan-Docs/issues/1140
But they are currently off for holidays.

Still, your Adreno does not seem to support the other float16 extensions neither, so you need to stick to OpenCL I guess. And maybe you need to petition those driver makers, why they support it in one API and not the other.

Thanks. I’ll try it and update. :+1: