HLSL 转 Metal
为了方便游戏移植到iOS和Mac,苹果近年推出了一个Metal shader converter工具,这个工具可以搭配微软的DXC工具,实现HLSL转化为Metal
- 编写HLSL
- DXC将
.hlsl
转化为.dxil
- MSC将
.dxil
转化为.metallib
原生Metal
在了解HLSL转Metal之前,我们需要先知道原生Metal长什么样,我感觉和HLSL还是有不少区别的
桥接文件
首先Metal是有一个桥接的.h
文件,这个文件连接了shader和程序(OC、Swift、C++),在这个文件中定义的枚举和结构,在两者中都可以使用
TODO:研究一下能不能使用enum class,这样写枚举污染命名空间
// Common.h |
// 某个swift中,使得枚举可以直接转为Int |
比如我在这个Common.h
中定义了一个枚举,每个枚举值都有其对应的整数,我们可以在管线和shader都使用这个枚举
// 这是基于swift的管线代码,我们将UBO设置到UniformsBuffer.index的位置 |
// 这是Metal shader,将ubo设置到UniformsBuffer所对应的位置 |
HLSL
静态成员
HLSL可以在shader中创建并初始化一个静态成员,但是需要加static
static const float3x2 _positions = { -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f }; |
Resource binding
HLSL的资产绑定是基于寄存器的
符号 | 含义 | 示例 |
---|---|---|
t | SRV | Texture2D _BaseMap : register(t1); |
s | samplers | SamplerState _BaseMap_ST : register(s1); |
u | UAV | |
b | CBV | cbuffer ubo : register(b0) { UBO ubo; } |
SPIR-V拓展
[[vk::push_constant]]
HLSL中Push Constants其实也是一个cbuffer,只是不绑定任何寄存器,是一个$Global cbuffer,你可以直接
cbuffer Constants
{
float4x4 g_WorldViewProj;
};
用于标记常量数据,使得可以被vkCmdPushConstants
推送
struct PushConstant |
环境搭建
DXC on Mac
git clone
git clone https://github.com/microsoft/DirectXShaderCompiler.git |
submodule init
cd DirectXShaderCompiler |
build(话说这一步还挺慢的,感觉我的M1该退役了)
mkdir build |
我们会得到一大堆生成内容,我们只需要build/bin/dxc-3.7
和build/lib/libdxcompiler.dylib
- 将
build/bin/dxc-3.7
拷贝到/usr/local/bin/
- 将
build/lib/libdxcompiler.dylib
拷贝到/usr/local/lib
打开命令行,发现可以使用dxc命令了
dxc-3.7 -E MainVS -T vs_6_0 -Fo "grass.v.dxil" "grass.hlsl" |
MSC
下载MSC后,双击安装即可
安装完毕你可以在
/usr/local/lib
找到对应的库,如果你的程序需要运行时生成 Metal Shader,可以将这个库嵌入项目中
你可以使用命令行编译了
metal-shaderconverter grass.v.dxil -o ./grass.metallib |
使用参数--output-reflection-file
还可以生成反射信息
metal-shaderconverter triangle.v.dxil -o ./triangle.metallib --output-reflection-file a.json |
{ |
SPIRV
除了官方的MSC,还可以使用SPIRV
我不太清楚MSC要如何加入调试信息,但SPIRV可以很容易加入
dxc-3.7 -E MainPS -spirv -Zi -Qembed_debug -O0 -T ps_6_0 -Fo "triangle.frag.spirv" "triangle.hlsl" |
通过安装Metal Developer Tools For Windows,可以在Window下使用metal和metallib工具
MSC
片元着色器
片元着色器比较好实现
struct v2f |
使用DXC和MSC编译后,得到triangle.f.metallib
,并放在路径Metal-Tutorial/triangle.p.metallib
MTL::Library* lib = metalDevice->newLibrary(NS::String::string("Metal-Tutorial/triangle.p.metallib", NS::ASCIIStringEncoding), nullptr); |
顶点着色器
metal有两种传入顶点信息的方式
vertex descriptor
一种是使用VertexDescriptor描述VertexBuffer,shader大概长这样
struct VertexIn { |
用HLSL写大概是
PSInput VSMain(float4 position : POSITION, float4 uv : TEXCOORD) |
binding buffer
另一种是binding一个buffer和vertexID,类似于instance
struct Vertex { |
用HLSL写,大概是
struct VertexData |