Skip to content

Instantly share code, notes, and snippets.

@zeux
Last active July 16, 2021 01:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zeux/c83001968e06fe0b789fa4bd513860c6 to your computer and use it in GitHub Desktop.
Save zeux/c83001968e06fe0b789fa4bd513860c6 to your computer and use it in GitHub Desktop.
Notes on supporting half-precision computation in SPIRV based shader pipelines

The following assumes that HLSL source is used as an input for shader compilation, glslang is used to compile it to SPIRV, and then SPIRV-Cross is optionally used to compile the result to MSL/GLSL.

First, make sure you use min16floatN type instead of halfN type. This is necessary because glslang treats halfN type (with default compilation options) as floatN because unfortunately that's what DX10 does as well.

#define half min16float
#define half2 min16float2
#define half3 min16float3
#define half4 min16float4

Now, you have two options when compiling HLSL to SPIRV: default settings, and --hlsl-enable-16bit-types (aka EShMsgHlslEnable16BitTypes if you invoke glslang via C++).

By default, min16floatN types compile to floatN types in SPIRV, but the variables, including temporaries, will have RelaxedPrecision annotation in SPIRV:

OpDecorate %27 RelaxedPrecision

This is the behavior that you probably want when compiling to SPIRV directly for use in Vulkan! The decoration is optional, so devices that don't support 16-bit floating point computation are free to disregard this and use 32-bit floating point math instead.

You can also use SPIRV-Cross to cross-compile this to GLSL:

#version 450 es
precision mediump float;
precision highp int;

layout(location = 0) in vec4 input_color;
layout(location = 0) out vec4 _entryPointOutput;

(vec4 in the code above refers to mediump vec4; ordinarily SPIRV-Cross would insert highp vec4)

Unfortunately, SPIRV-Cross doesn't support RelaxedPrecision for MSL. To get around that, you need to enable 16-bit type support when building SPIRV for use with SPIRV-Cross, which will result in the actual half-precision types being emitted:

%half = OpTypeFloat 16
%v4half = OpTypeVector %half 4

Note that you can't use the resulting SPIRV in Vulkan if the device doesn't support VK_KHR_shader_float16_int8! Most older drivers don't. However, we don't care about this - we care about feeding this to SPIRV-Cross, which will happily translate this to halfN types when targeting Metal:

half4 _entryPointOutput [[color(0)]];

The danger of this approach is that using half in constant buffers will result in actual half-precision types (with sizeof=2) emitted for Metal but not for other APIs, which will result in a different constant buffer layout. This can be prevented by querying SPIRV-Cross reflection information and making sure half types aren't used for uniform data.

@greg-lunarg
Copy link

Yes, sorry we have not advertised it yet. Right now we are in the proof-of-concept phase to see if such a thing might be profitable. One concern is is if the cost of conversions outweighs the savings in computation.

We hope to have some data in the next week or so.

@greg-lunarg
Copy link

Is the issue that Texture2D or something along these lines is supported but Texture2D isn't?

Typo?

@zeux
Copy link
Author

zeux commented Jun 13, 2019

Tags eaten by HTML :( I meant Texture2D<float4> vs Texture2D<min16float4>

@greg-lunarg
Copy link

Texture2D<min16float4> is supported (probably as Texture2D<float4>).

Texture2D<half4> is not supported by glslang, dxc, or apparently Vulkan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment