antvis / g-webgl-compute

A GPGPU implementation based on WebGL.
MIT License
144 stars 15 forks source link

在 Safari 中支持转译成 WHLSL #25

Open xiaoiver opened 4 years ago

xiaoiver commented 4 years ago

问题背景

目前各大浏览器的预览版本对于 WebGPU API 的支持度都不错,但是在 Shader 语言的选择上存在分歧: https://github.com/gpuweb/gpuweb/wiki/Implementation-Status

因此目前 WebGPU 的 DEMO 都会注明是 Safari only 或者 Chrome/Edge only: https://github.com/gpuweb/gpuweb/wiki/Implementation-Status#samples

对于我们的 DSL -> GLSL #17 方案来说,也可以支持 WHLSL,也算是“一套代码,多处运行”。

WebGPU API 差异

Chrome/Edge 和 Safari 绝大部分 API 都是相同的,目前发现的不同点如下:

创建 ShaderModule

创建 ShaderModule 的 WebGPU API 略有不同:

  1. isWHLSL flag
  2. 可以指定 entryPoint,意味着一个 Shader 里可以包含多个 main 函数
    const shaderModule = device.createShaderModule({
    code: '...', // Shader 代码
    isWHLSL: true // 表明使用的是 WHLSL
    });
    device.createComputePipeline({ 
    layout: pipelineLayout, 
    computeStage: {
    module: shaderModule,
    entryPoint: "horizontal" // 可以指定 entryPoint
    }
    });

获取队列

https://gpuweb.github.io/gpuweb/#gpuqueue

// safari
this.device.getQueue().submit(this.commandBuffers);
// chrome
this.device.defaultQueue.submit(this.commandBuffers);

context

const context = (canvas.getContext(
  isSafari ? 'gpu' : 'gpupresent',
) as unknown) as GPUCanvasContext;

获取纹理视图

// safari
mainTexture.createDefaultView();
// chrome
mainTexture.createView();

depthTextureDescriptor

format: isSafari
        ? 'depth32float-stencil8'
        : WebGPUConstants.TextureFormat.Depth24PlusStencil8,

GPUBindGroupDescriptor 键名

isSafari ?
  { bindings: bindGroupLayoutEntries }
  : { entries: bindGroupLayoutEntries },

WHLSL 转译

Safari 使用了 WHLSL: https://webkit.org/blog/8482/web-high-level-shading-language/ 中文版翻译:https://zhuanlan.zhihu.com/p/50344162

下面来看 Shader 语法层面 WHLSL 与 GLSL 的差异。

声明线程组

声明线程组语法和 GLSL 4.5 差不多,另外 Compute Shader 中 main 函数前需要加 compute 修饰符:

[numthreads(100, 1, 1)]
compute void horizontal() {}

线程组变量

GLSL 中线程组变量都是全局的,而在 WHLSL 中需要使用 Semantics 声明:

float3 dispatchThreadID : SV_DispatchThreadID

数据类型

WSL 并不支持隐式类型转换。

考虑到与 GLSL 1.0 统一,标量也不支持 uint: https://gpuweb.github.io/WSL/#built-in-scalars

向量: vec3 -> float3

定义常量

GLSL 中可以通过 define 定义常量,但 WHLSL 中似乎不支持:

GPUDevice.createComputePipeline(): WHLSL compile error: Parse error at line 4: Unexpected token (expected ( got =)

本质上是不支持在全局作用域声明常量导致的:

https://gpuweb.github.io/WSL/#top-level-declarations

topLevelDecl ::=  ";" | typedef | structDef | enumDef | funcDef

因此只能采取向每个函数中添加的方式来模拟。