DXC ile Vulkan'da HLSL shader'ları: derleme, eşleme ve tuzaklar

Vulkan GLSL değil SPIR-V kabul eder — ve SPIR-V onu hangi dilin ürettiğini umursamaz. Microsoft’un dxc‘si (DirectX Shader Compiler) doğrudan SPIR-V üretir, yani mevcut bir HLSL kod tabanı yeniden yazmadan bir Vulkan renderer’ı besleyebilir. Boşluklar eşlemede: register annotation’ları vs descriptor binding’leri, CBV vs UBO ve her daim var olan row_major vs column_major varsayılanı.

Derleme

build.sh
dxc -spirv -T ps_6_0 -E main shader.hlsl -Fo shader.spv
#   ^SPIR-V çıktısı  ^profil   ^entry

# Genelde istediğiniz Vulkan hedefli düğmeler:
dxc -spirv -fvk-use-gl-layout        # std430/GL layout kuralları
         -fvk-use-dx-layout          # veya DX offset'lerini koru
         -fvk-b-shift 0 0            # register b* → binding'e kaydır
         -fvk-t-shift 0 0            # t* kaydırma
         -fvk-s-shift 0 0            # s* kaydırma
         -fvk-u-shift 0 0            # u* (UAV) kaydırma
         -fspv-target-env=vulkan1.3  # SPIR-V env hedefi
         -Zi                          # debug bilgisi → RenderDoc okunabilir

-fvk-*-shift flag’leri HLSL register space’lerini (b0, t0, u0, s0) Vulkan’ın düz binding = N numaralandırmasına yeniden eşler — aynı shader iki API’yi de beslerken şarttır.

Isıran eşleme

mapping.txt
HLSL: cbuffer Params : register(b0)   → VK: layout(set=0,binding=0) UBO
HLSL: Texture2D t : register(t0)      → VK: layout(set=0,binding=1) texture2D
HLSL: SamplerState s : register(s0)   → VK: layout(set=0,binding=2) sampler
HLSL: RWTexture2D u : register(u0)    → VK: layout(set=0,binding=3) image2D
HLSL: register(b0, space1)            → VK: set=1, binding=0

space → set, register → binding — -fvk-*-shift flag’leri onları offset’ler, böylece birden çok space tek Vulkan set’inde bir arada olabilir.

Gerçek tuzaklar

pitfalls.txt
row_major vs column_major:
    HLSL cbuffer varsayılanı column_major, GLSL std140 row_major —
    -fvk-use-gl-layout veya #pragma pack_matrix ile BİRİNİ seçin —
    sessizce transpoz edilen matrix'ler 1 numaralı porting bug'ıdır

push constants:
    HLSL'de push constant kavramı yoktur — dxc, [[vk::push_constant]]
    işaretli özel bir cbuffer'ı VK'nin push_constant bloğuna eşler —
    veya -fvk-use-dx-layout ile normal UBO gibi davranın

vertex inputs:
    HLSL semantik isimler (POSITION, TEXCOORD0) kullanır; Vulkan
    numaralı location'lar ister — [[vk::location(N)]] kullanın veya
    pipeline oluşturmada attribute'ları eşleyin

combined sampler:
    HLSL t0 (texture) ve s0 (sampler)'ı ayırır — Vulkan
    texture2D+sample() tek descriptor kullanır; dxc bunu halleder
    ama binding numaraları farklıdır
shader.hlsl
// minimal Vulkan-dostu HLSL:
[[vk::binding(0,0)]] cbuffer Params { row_major float4x4 mvp; };
[[vk::binding(1,0)]] Texture2D    tex;
[[vk::binding(2,0)]] SamplerState smp;
[[vk::location(0)]] float4 main([[vk::location(0)]] float3 pos : POSITION,
                              [[vk::location(1)]] float2 uv : TEXCOORD0)
    : SV_Position { return mul(mvp, float4(pos,1)); }

Ne zaman değer

  • Mevcut bir D3D renderer’ı port edin — HLSL kaynağını koruyun, SPIR-V üretin, çift-kaynak bakımı yok.
  • Shader lib’i çapraz derleyin — tek .hlsl → D3D12 için DXIL + Vulkan için SPIR-V; reflection için spirv-cross (diğer yazı).
  • DXIL özellikleri — dxc’de wave op’ları, WaveMultiPrefixOp ve glslang’in GLSL’inin yalnızca uzantılarla sunduğu yeni HLSL intrinsic’leri var.

Pratik kurallar

  • -fspv-target-env=vulkan1.3 önemlidir — varsayılan daha az kabiliyetli eski bir SPIR-V sürümünü hedefler.
  • CI’da -Zi + spirv-val — DXC’nin SPIR-V’si geçerlidir ama attribute eşlemesi -fvk-* flag’lerinin açıkça yazılmasını ister.
  • [[vk::binding(N,S)]] + [[vk::location(N)]] DXC’ye özgü attribute’lardır — register/space heuristiklerini temiz şekilde ezer.
  • row_major uyumsuzluğu sessiz katildir — layout’lar uyuşmazsa shader’a verilen her matrix yanlıştır; pipeline build’de bir kez assert edin.

Check out similar posts by category: Vulkan C++