HLSL for Vulkan

DXC's SPIR-V back end (-spirv) makes HLSL a first-class Vulkan shading language; it's used by many engines and by the Vulkan samples.

DXC's SPIR-V back end (-spirv) makes HLSL a first-class Vulkan shading language; it's used by many engines and by the Vulkan samples. The __spirv__ macro is defined when targeting SPIR-V.

36.1 Compiling

dxc -spirv -T vs_6_0 -E VSMain -fspv-target-env=vulkan1.3 shader.hlsl -Fo shader.vert.spv
dxc -spirv -T ps_6_0 -E PSMain -fspv-target-env=vulkan1.3 shader.hlsl -Fo shader.frag.spv
:: Debug info for RenderDoc source-level debugging
dxc -spirv -T ps_6_0 -E PSMain -fspv-debug=vulkan-with-source -Od shader.hlsl -Fo shader.frag.spv
:: Validate / disassemble with SPIRV-Tools
spirv-val shader.frag.spv
spirv-dis shader.frag.spv

36.2 Binding resources

Vulkan uses (set, binding) pairs instead of registers. Either annotate explicitly or let DXC map registers:

Without attributes, DXC maps register(xN, spaceM) → binding N, set M. Since b0, t0, s0, u0 would collide in the same set, shift them:

// Explicit Vulkan attributes (ignored when compiling to DXIL)
[[vk::binding(0, 0)]] ConstantBuffer<FrameCB> gFrameVK;            // set 0, binding 0
[[vk::binding(1, 0)]] Texture2D<float4>       gAlbedoVK;
[[vk::binding(2, 0)]] SamplerState            gSamplerVK;
[[vk::combinedImageSampler]] [[vk::binding(3, 0)]] Texture2D<float4> gCombinedTex;
[[vk::combinedImageSampler]] [[vk::binding(3, 0)]] SamplerState      gCombinedSamp;

struct PushData { float4x4 model; uint materialIndex; };
[[vk::push_constant]] PushData gPush;                              // push constants

[[vk::constant_id(0)]] const bool  kUseShadows = true;            // specialization constants
[[vk::constant_id(1)]] const int   kSampleCount = 16;

struct VkVSIn
{
    [[vk::location(0)]] float3 pos    : POSITION;                  // vertex input locations
    [[vk::location(1)]] float2 uv     : TEXCOORD0;
};

// Render-pass input attachments (subpasses)
[[vk::input_attachment_index(0)]] [[vk::binding(4, 0)]] SubpassInput<float4> gGBufferAlbedo;
float4 ReadSubpass() { return gGBufferAlbedo.SubpassLoad(); }
dxc -spirv -T ps_6_0 -E main -fvk-b-shift 0 0 -fvk-t-shift 100 0 -fvk-s-shift 200 0 -fvk-u-shift 300 0 shader.hlsl -Fo shader.spv

36.3 Differences & useful flags

Vulkan (via DXC) -fvk-invert-y (VS/DS/GS/MS) or negative viewport height (Vulkan 1.1+) SV_Position.w in PS 1/w (FragCoord.w) cbuffer 16-byte rules std140/std430 by default

-fvk-use-dx-layout, -fvk-use-scalar-layout, -fvk-use-gl-layout Append/consume counters separate counter buffer Combined image samplers

-fspv-reflect adds semantic decorations -fspv-extension=SPV_KHR_..., [[vk::ext_instruction]] inline SPIR-V

VK_KHR_ray_tracing_pipeline / ray_query -fspv-target-env=vulkan1.2+, [[vk::shader_record_ext]] for SBT data

printf("x = %f", x) via NonSemantic.DebugPrintf enable in validation layers