KhronosGroup / KhronosGroup/SPIRV-Cross

HLSL: Using half in constant buffer may result in exception for SM 6.2 with native 16bits types

Open
#1,809 2 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
GLSL
Stars
2.5k
Forks
713
Avg merge
2d 18h
Merged PRs (30d)
16

Description

When using `half` in constant buffer in HLSL, SPIRV-Cross may failed to translate the spirv generated by dxc back to HLSL, but throwing an exception read `Cannot pack on tighter bounds than 4 bytes in HLSL`.

For example, I use the simple HLSL shader:
```
struct CBData
{
half Scalar0; // This half cause the exception
half4 Color;
};

ConstantBuffer CB;

float4 PSMain() : SV_Target0
{
return pow(CB.Color, 2.2h);
}
```
under SM 6.2 with native fp16 enabled. I use dxc to generate the SPIRV code like `dxc -T ps_6_2 -E PSMain -enable-16bit-types -spirv -fspv-target-env="vulkan1.2" .\test0.hlsl -Fo test0.spv` . The generated SPIRV (assembly) is something like
```
; SPIR-V
; Version: 1.5
; Generator: Google spiregg; 0
; Bound: 24
; Schema: 0
OpCapability Shader
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %PSMain "PSMain" %CB %out_var_SV_Target0
OpExecutionMode %PSMain OriginUpperLeft
OpSource HLSL 620
OpName %type_ConstantBuffer_CBData "type.ConstantBuffer.CBData"
OpMemberName %type_ConstantBuffer_CBData 0 "Scalar0"
OpMemberName %type_ConstantBuffer_CBData 1 "Color"
OpName %CB "CB"
OpName %out_var_SV_Target0 "out.var.SV_Target0"
OpName %PSMain "PSMain"
OpDecorate %out_var_SV_Target0 Location 0
OpDecorate %CB DescriptorSet 0
OpDecorate %CB Binding 0
OpMemberDecorate %type_ConstantBuffer_CBData 0 Offset 0
OpMemberDecorate %type_ConstantBuffer_CBData 1 Offset 2
OpDecorate %type_ConstantBuffer_CBData Block
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%half = OpTypeFloat 16
%half_0x1_198p_1 = OpConstant %half 0x1.198p+1
%v4half = OpTypeVector %half 4
%11 = OpConstantComposite %v4half %half_0x1_198p_1 %half_0x1_198p_1 %half_0x1_198p_1 %half_0x1_198p_1
%type_ConstantBuffer_CBData = OpTypeStruct %half %v4half
%_ptr_Uniform_type_ConstantBuffer_CBData = OpTypePointer Uniform %type_ConstantBuffer_CBData
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%void = OpTypeVoid
%17 = OpTypeFunction %void
%_ptr_Uniform_v4half = OpTypePointer Uniform %v4half
%CB = OpVariable %_ptr_Uniform_type_ConstantBuffer_CBData Uniform
%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
%PSMain = OpFunction %void None %17
%19 = OpLabel
%20 = OpAccessChain %_ptr_Uniform_v4half %CB %int_1
%21 = OpLoad %v4half %20
%22 = OpExtInst %v4half %1 Pow %21 %11
%23 = OpFConvert %v4float %22
OpStore %out_var_SV_Target0 %23
OpReturn
OpFunctionEnd
```
Then I use SPIRV-Cross to translate the generated SIPRV back to HLSL using `.\spirv-cross.exe PATH\TO\test0.spv --hlsl --shader-model 62 --hlsl-enable-16bit-types`, and I get the exception
```
SPIRV-Cross threw an exception: Cannot pack on tighter bounds than 4 bytes in HLSL.
```
On the other hand, when I comment out the `Scalar0` in HLSL source, SPIRV-Cross works well, resulting in the following HLSL
```
cbuffer type_ConstantBuffer_CBData : register(b0, space0)
{
half4 CB_Color : packoffset(c0);
};

static float4 out_var_SV_Target0;

struct SPIRV_Cross_Output
{
float4 out_var_SV_Target0 : SV_Target0;
};

void frag_main()
{
out_var_SV_Target0 = float4(pow(CB_Color, half(2.19921875).xxxx));
}

SPIRV_Cross_Output main()
{
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.out_var_SV_Target0 = out_var_SV_Target0;
return stage_output;
}
```

It seems that SPIRV-Cross will use `packoffset` in the generated HLSL to describe to constant buffer layout, but it can only describe an offset aligned to 4 bytes (like c1.y referred to offset 16+4=20 bytes), while in HLSL SM 6.2 with native 16 bits type such 16 bits data [can be packed into a single 16 bytes row in constant buffer](https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing#legacy-layout-in-native-16-bit-types-mode--enable-16bit-types), and multiple half (or vector of half) in such a row can align to 2 bytes rather than 4 bytes, which make `packoffset` invalid, and result in the exception.

Contributor guide

No contributing guide indexed for this repository

Research direction

Reproduce the issue with test0.hlsl and the shown dxc command, then run SPIRV-Cross on test0.spv with the HLSL options. Trace the HLSL translation path where the constant-buffer layout raises “Cannot pack...” and compare it with the output when Scalar0 is omitted. Done means the native 16-bit layout translates without the exception while preserving offsets.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.