けいブログ

端くれ描画プログラマによるNote

VulkanでHLSL

わりと久々にブログ更新。

最近、昔にほんのちょろっと触ったVulkanを再入門してみています。

Vulkanは、C APIだと少しツライところがあるので、C++ APIを使います。 (GitHub - KhronosGroup/Vulkan-Hpp: Open-Source Vulkan C++ API)

シェーダ言語どうするか

はじめはなんとなくGLSLでシェーダを記述していたのですが、やっぱりHLSLで書きたいよね!ということで。

VulkanSDK付属の glslangValidator でも変換できるようですが、今回 DirectX Shader Compiler (DXC) を使ってみることにします。 ( DirectXShaderCompiler/SPIR-V.rst at master · microsoft/DirectXShaderCompiler · GitHub)

まずはHLSLを用意する

例として、テクスチャと頂点カラーを乗算して出力するなんの変哲もないピクセルシェーダを用意します。

Texture2D    tex  : register(t0);
SamplerState samp : register(s0);

struct PSInput
{
    float4 color : TEXCOORD0;
    float2 uv : TEXCOORD1;
    float4 position : SV_POSITION;
};

float4 main(PSInput input) : SV_TARGET
{
    float4 output;;
    output = tex.Sample(samp, input.uv) * input.color;
    return output;
}

HLSL→SPIR-V

dxc に SPIR-V のオプションを渡しつつ、プロファイルやエントリポイントなどを指定します。

dxc -spirv -T ps_6_0 -E main "shader.hlsl" -Fo "shader.spv" -fvk-s-shift 10 0

少し気を付けないといけないのが、-fvk-s-shift の指定。

HLSLだとTextureやSampler,Bufferなど、タイプ別にregister指定が独立していますが、GLSL/Vulkanで同じような役割を担うbindingは各タイプで共通になります。

dxcではコンバート時にregister指定の数値をシフトしてbindingに割り当てることができるようです。

今回は s0 を binding = 10 にシフトさせています。

Descriptor

GLSLで実装している際は特に気にせず sampler2D を使用していました。

HLSLで Texture2D, SamplerState を使用しているので、C++側も変更します。

DescriptorType::eCombinedImageSampler が eSampledImage + eSampler になります。

std::array<vk::WriteDescriptorSet, 2> writeSets;

auto& tex = writeSets[0];
vk::DescriptorImageInfo imageInfo;
imageInfo.setImageView(m_textureView)
    .setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
tex.setDstSet(m_descriptorSet[0])
    .setDstBinding(0)
    .setDescriptorCount(1)
    .setDescriptorType(vk::DescriptorType::eSampledImage)
    .setImageInfo(imageInfo);

auto& smp = writeSets[1];
vk::DescriptorImageInfo samplerInfo;
samplerInfo.setSampler(m_sampler);
smp.setDstSet(m_descriptorSet[0])
    .setDstBinding(10)
    .setDescriptorCount(1)
    .setDescriptorType(vk::DescriptorType::eSampler)
    .setImageInfo(samplerInfo);

m_device.updateDescriptorSets(writeSets, nullptr);

NsightGraphicsでの見え方

Nsight Graphics でシェーダを確認すると、以下のようにGLSLに変換された状態で確認ができました。 (SPIRV-Cross によって生成されていそうなコメントがついていました)

// GLSL generated using SPIRV-Cross and might not be representative
// of original shader source

#version 450

layout(set = 0, binding = 0) uniform texture2D tex;
layout(set = 0, binding = 10) uniform sampler samp;

layout(location = 0) out vec4 out_var_SV_TARGET;
layout(location = 1) in vec2 in_var_TEXCOORD1;
layout(location = 0) in vec4 in_var_TEXCOORD0;

void main()
{
    out_var_SV_TARGET = texture(sampler2D(tex, samp), in_var_TEXCOORD1) * in_var_TEXCOORD0;
}

GLSLにはなってしまいますが、Nsight上でのコードの確認や編集がひとまずできました。