备胎计划002 跨平台shader编译方案实践

Unity 和 Unreal 作为流行的商业引擎,引擎这个东西,最主要的特点就是抽象。其中关于Shader部分,他们都做了比较好的抽象,使用者不需关注每个平台的shader不同,写一次,build各个平台。

这是当今游戏引擎的一个重要标配,Unity 和 Unreal 都自己做了大量的工作,网上也有人分析过Unity和Unreal各自的方案。


替代方案

现如今,开源世界也有一些方案来处理这个问题了


主要是两条线

1.DirectXShaderCompiler + spirvcross


2.glsllang +spirvcross


方案一,DirectXShaderCompiler 是微软开源的hlsl编译器,最新的版本增加了一个编译开关,重编译后可获取将hlsl 编译为 spirv 的能力,然后通过spirvcross项目编译成各种各样的东西


方案二,glsllang 是 Khronos 官方的 for vulkan 的 glsl编译器,他负责将glsl编译为spirv,然后通过spirvcross项目编译成各种各样的东西


这两个方案使用的编写语言分别是hlsl 和 glsl(方案二其实也支持hlsl,但是资料比较少,我还没有实验出来),都是用sprivcross来跨平台

这里就要解释一下什么是spirv

爷爷Khronos: 是图形标准化组织,OPENGL就是他们制定的,然后在微软搞dx12,苹果搞metal的时候,他们也推出了新一代图形API标准,就是Vulkan。

爹Vulkan:大伯OPENGL,二伯OPENGL ES

儿子spirv:而vulkan使用的shader二进制格式就是spirv

因为opengl 和opengles 的时代,都没有发展二进制格式,爷爷制定标准的时候就想把他打造成一个通用的格式,所以spirv实际上是个很通用的执行格式(想象一下c#背后的IL)


而spirvcross也是爷爷开发的一个反编译项目,专门把spirv反编译成各种shader源文件

https://github.com/KhronosGroup/SPIRV-Cross

官网上说他的功能是,(反编译spirv)生成可读的GLSL,苹果MSL,HLSL,c++,json好了,天下大同了

  • Convert SPIR-V to readable, usable and efficient GLSL
  • Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL)
  • Convert SPIR-V to readable, usable and efficient HLSL
  • Convert SPIR-V to debuggable C++ [DEPRECATED]
  • Convert SPIR-V to a JSON reflection format [EXPERIMENTAL]


废话不多说,咱们试试

实践方案一

DirectXShaderCompiler 还要自己重编译,麻烦(其实是我没编译通过…build.py写的太烂了,辣鸡微软)

好在微软直接给了个整合好的方案一

ShaderConductor

https://github.com/microsoft/ShaderConductor

根据文档指导,直接下个最新的预发行版本就好

https://dev.azure.com/msft-ShaderConductor/public/_build/results?buildId=205&view=artifacts&type=publishedArtifacts

image

下份windowsx64的回来,其它平台的我没测试,都是命令行工具,大差不差。

写一个test.hlsl

image

然后执行命令行来编译成glsl

image

ShaderConductorCmd –I test.hlsl –E vs_main –S vs –T glsl –V 200

一长串的参数

-I test.hlsl 输入文件

-E vs_main 要编译的函数

-S vs   编译那种shader

-T glsl 输出为glsl格式

-V 110 glsl版本号 ,glsl版本号

副GLSL版本号表

2.0 110

2.1 120

3.0 130

3.1 140

3.2 150

3.3 330

4.0 400

4.1 410

4.2 420

4.3 430

4.5 450


产生的ios_matal文件

image

产生的glsl文件

image

问题,因为这个方案里的DXshadercompiler 是dx11的,所以这有个问题

image

我单独写的uniform matView也被放进了cbuffer里,观看glsl就会发现有了语义变化,这就像卡了一根刺。


实验方案二

下载glsllang

https://github.com/KhronosGroup/glslang

有release下载

https://github.com/KhronosGroup/glslang/releases

也下载个x64的

下载spirvcross

https://github.com/KhronosGroup/SPIRV-Cross

也有release下载

https://github.com/KhronosGroup/SPIRV-Cross/releases

也下载个x64的

解压出来就能用


这次写glsl

image

glsllang 默认使用文件扩展名识别功能

vertexshader就要.vert结尾


image

调用上述命令编译test.vert 和 test.frag 分别是 vertexshader 和 fragmentshader

分别得到frag.spv 和 vert.spv文件


这就是spirv的二进制格式shader


然后调用spirvcross 可以反编译他们到不同的shader

image

没有参数就反编译为glsl

加--hlsl就反编译为hlsl

加--msl就反编译为苹果metal

还有一堆参数就得你自己研究了

生成的hlsl比较啰嗦

image

imageimage

生成的glsl就比较干爽了

最后说两句

目前我想要做的备胎,第一服务对象是Android(gl +vulkan)

第二才考虑 IOS \PC \web 这些

方案二明显更加合适,使用glsl作为基准,对gl和vulkan环境支持良好,在第二考虑时才需要spirvcross上马,可以支持dx 和 metal。


原理通了,接下去就是整合了。

posted @ 2020-07-24 15:26  疯光无线  阅读(146)  评论(0编辑  收藏