10 WGSL 入口点

入口点的定义

入口点(entry point)是 一个用户自定义函数,在特定的 着色器阶段 (shader stage)执行相应的工作。

着色器阶段 Shader Stage

WebGPU 将工作以绘制(draw)或 调度(dispatch)命令的形式发送给GPU,这些命令在一组着色器阶段的输入、输出和附加资源的上下文上执行渲染管线(pipeline)。

渲染管线(pipeline)将在GPU上执行的工作描述为一系列的阶段,其中一些阶段是可编程。在WebGPU中,渲染管线在执行 绘制(draw)和 调度(dispatch)命令之前创建。有两种渲染管线:

GPUComputePipeline 和 GPURenderPipeline。

调度命令(dispatch)使用GPUComputePipeline在并行度可控的逻辑网格上运行计算着色阶段,同时读取并可能更新缓冲区和图像资源。

绘制命令(draw)使用GPURenderPipeline 和其它固定功能阶段一起在两个可编程阶段运行多阶段进程(multi-stage process)(有点绕口, multi-stage process 翻译的可能不准确):

  1. vertex shader stage

将单个顶点的输入属性映射为输出属性。

  1. fixed-function stages

将顶点映射为光栅化需要的图元(graphic primitives), 例如三角形。

  1. fragment shader stage

处理每个片元,可能会有产生新的片元输出。

  1. fixed-function stages

使用片元更新颜色、深度、模板缓存。

WGSL中 定义三个着色器阶段(shader stages)对应于管线可编程部分:

  • computer

  • vertex

  • fragment

入口点函数的声明

为创建入口点函数, 我们使用 着色器阶段属性来声明一个用户自定义函数。

当在WebGPU API中配置管道时,入口点的函数名称映射到WebGPU GPUProgrammableStage对象的entryPoint属性。

入口点函数的形参表示着色器阶段的输入,入口点函数如果有返回值,表示着色器阶段的输出。

入口点函数的形参和返回值必须是以下类型:

  • bool 布尔类型

  • a numeric scalar 数值型标量

  • a numeric vector 数值型向量

  • a structure 结构体,其中结构体成员也是 bool, numeric scalar , numeric vector

结构类型可用于将用户定义的输入分组,也可用于将内置输入分组。可以使用结构类型作为返回类型,将用户定义的输出相互组合,也可以将内置输出组合。

NOTE: The bool case is forbidden for user-defined inputs and outputs. It is only permitted for the front_facing builtin value.

NOTE: Compute entry points never have a return type.

入口点函数示例:

@vertex
fn vert_main() -> @builtin(position) vec4 {
  return vec4(0.0, 0.0, 0.0, 1.0);
}

@fragment
fn frag_main(@builtin(position) coord_in: vec4) -> @location(0) vec4 {
  return vec4(coord_in.x, coord_in.y, 0.0, 1.0);
}

@compute @workgroup_size(1)
fn comp_main() { }

入口点函数的函数属性:

  • shader stage attributes: vertex, fragment and compute

  • workgroup_size

@compute @workgroup_size(8,4,1)
fn sorter() { }

@compute @workgroup_size(8u)
fn reverser() { }

// Using an pipeline-overridable constant.
@id(42) override block_width = 12u;
@compute @workgroup_size(block_width)
fn shuffler() { }

// Error: workgroup_size must be specified on compute shader
@compute
fn bad_shader() { }

着色器接口(shader interface)

着色器接口(shader interface)是一组对象的集合,着色器通过这些对象访问外部数据(用于读取或写入),以及用于配置着色器的可被管道覆盖的常量(pipeline-overridable constants)。该接口包括:

  • Shader stage inputs 着色器阶段的输入

  • Shader stage outputs 着色器阶段的输出

  • Override-declarations 覆盖声明

  • Attached resources, which include: 附加资源

  • Uniform buffers 统一缓存

  • Storage buffers 存储缓存

  • Texture resources 纹理资源

  • Sampler resources 采样资源

我们可以精确地定义着色器的接口,包括:

  • 入口点函数的形参,这些表示着色器阶段的输入;

  • 入口点函数的返回值,这表示着色器阶段的输出;

  • 由着色器静态访问的uniform buffer, storage buffer, texture resource和sampler resource变量;

  • 着色器可以静态访问覆盖声明(override-declarations);

阶段内(inter-stage)输入输出接口

着色器阶段的输入是管道上游提供给着色器阶段的数据。数据要么是内置的输入值,要么是用户定义的输入值

着色器阶段的输出是着色器提供给管道下游进一步处理的数据。每个数据要么是内置输出值,要么是用户定义的输出值

IO属性用于将对象构建为着色器阶段的输入或输出,或者进一步描述输入或输出的属性。IO属性有:

  • builtin

  • location

  • interpolate

  • invariant

内置输入输出接口

内置的输入值提供了对系统生成的控制信息的访问,入口点不能包含重复的内置输入

阶段S的内置输入名为X,类型为TX,可以通过形式参数访问着色器阶段S的入口点,有两种方式之一:

  • 这个参数有属性builtin(X),类型是TX。

  • 形参具有结构类型,其中一个结构成员具有属性builtin(X),类型为TX。

(也就是说入口点函数的参数可以是单独的内置输入,也可以用结构体封装后作为输入)

相反,当入口点的参数或参数的成员具有内置属性时,相应的内置必须是入口点的着色阶段的输入。

着色器使用一个内置的输出值来向管道中的后续处理步骤传递控制信息,入口点不能包含重复的内置输出。

阶段S的内置输出名为Y,类型为TY,可以通过着色器阶段S入口点的返回值设置,有两种方式:

  • 入口点返回类型具有属性builtin(Y),类型为TY。

  • 入口点返回类型具有结构类型,其中一个结构成员具有属性builtin(Y),类型为TY。

(入口点函数的输出形式上跟输入一致,都可以用单独的 内置属性或结构体,结构体中必须包含内置属性)

相反,当入口点的返回类型或返回类型的成员具有builtin属性时,相应的内置元素必须是入口点着色阶段的输出。

NOTE: The position built-in is both an output of a vertex shader, and an input to the fragment shader.

用户自定义输入输出接口

用户定义的数据可以作为管道开始时的输入,可以在管道的各个阶段之间传递,也可以作为管道结束时的输出。

每个用户自定义输入数据和用户自定义输出数据必须:

  • 是数值型标量或数值型向量;

  • 分配一个IO location;

计算着色器不能有用户自定义的输入或输出。

输入输出位置(Input - output locations)

每个输入输出位置最大可以存储16字节的值,例如一个4个元素的浮点向量占一个位置。(这里涉及到对齐规则,WGSL 与 GLSL 差不多)。

IO locations 使用 location 属性 描述 , 用户自定义的输入和输出一定要有 IO location, 入口点函数结构体中的成员是用户自定义输入或输出的也要精确描述 location。

在以下每一组中,位置不得重叠: (也就是不能有相同的 location)

  • 结构体成员

  • 入口点函数形参

NOTE: Location numbering is distinct between inputs and outputs: Location numbers for an entry point’s shader stage inputs do not conflict with location numbers for the entry point’s shader stage outputs.
输入\输出 的 location 是两个体系,它们的location 不会冲突。
NOTE: The number of available locations for an entry point is defined by the WebGPU API.

LOCATION ATTRIBUTES 示例:

struct A {
  @location(0) x: f32,
  // Despite locations being 16-bytes, x and y cannot share a location
  @location(1) y: f32
}

// in1 occupies locations 0 and 1.
// in2 occupies location 2.
// The return value occupies location 0.
@fragment
fn fragShader(in1: A, @location(2) in2: f32) -> @location(0) vec4 {
 // ...
}

在结构体中,用户自定义输入输出与内置输入输出可以混合用:

// Mixed builtins and user-defined inputs.
struct MyInputs {
  @location(0) x: vec4,
  @builtin(front_facing) y: bool,
  @location(1) @interpolate(flat) z: u32
}

struct MyOutputs {
  @builtin(frag_depth) x: f32,
  @location(0) y: vec4
}

@fragment
fn fragShader(in1: MyInputs) -> MyOutputs {
  // ...
}

无效的位置分配代码:

struct A {
  @location(0) x: f32,
  // Invalid, x and y cannot share a location.
  @location(0) y: f32
}

struct B {
  @location(0) x: f32
}

struct C {
  // Invalid, structures with user-defined IO cannot be nested.
  b: B
}

struct D {
  x: vec4
}

@fragment
// Invalid, location cannot be applied to a structure type.
fn fragShader1(@location(0) in1: D) {
  // ...
}

@fragment
// Invalid, in1 and in2 cannot share a location.
fn fragShader2(@location(0) in1: f32, @location(0) in2: f32) {
  // ...
}

@fragment
// Invalid, location cannot be applied to a structure.
fn fragShader3(@location(0) in1: vec4) -> @location(0) D {
  // ...
}

插值

作者可以通过使用interpolate属性来控制如何插值用户定义的IO数据,WGSL提供了两个方面的插值控制:插值方式和插值采样方式。

插值类型必须是以下之一:

  • perspective - Values are interpolated in a perspective correct manner.

  • linear - Values are interpolated in a linear, non-perspective correct manner.

  • flat - Values are not interpolated. Interpolation sampling is not used with flat interpolation.

插值采样方式必须是以下之一:

  • center - Interpolation is performed at the center of the pixel. (像素中心)

  • centroid - Interpolation is performed at a point that lies within all the samples covered by the fragment within the current primitive. This value is the same for all samples in the primitive. (质心)

  • sample - Interpolation is performed per sample. The fragment shader is invoked once per sample when this attribute is applied. (逐采样)

不同数据类型的插值情况:

对于用户自定义的浮点类型的标量或向量类型:
  • 如果插值属性没有指定, 默认是 @interpolate(perspective, center) ;

  • 如果指定了插值属性:

如果插值类型是 flat ,就不要指定插值采样类型;

如果插值类型是 perspective 或者 linear时,插值采样类型没有指定, 默认是 center;

用户自定义的顶点着色器输出或片元着色器输入,如果是整型标量或者整型向量,插值类型一定指定为@interpolate(flat)。( 也就是整型类型的输入输出不做插值处理!)

资源接口

资源是一个对象,它提供对着色器阶段外部数据的访问,并且它不是覆盖声明(override-declaration),也不是着色器阶段的输入或输出,资源被着色器的所有调用(invocations)共享。

有四种资源接口:

  • Uniform buffers

  • Storage buffers

  • Texture resources

  • Sampler resources

每个资源变量必须同时使用组属性(group)和绑定属性(binding)声明,它们和着色器的阶段一起确定了着色器流水线中资源的绑定地址。

两个不同的资源变量不能具有相同的 组属性(group) 和 绑定属性(binding)。

资源布局的兼容性

WebGPU要求着色器的资源接口与使用着色器的管道布局匹配,如果资源接口中的WGSL变量绑定到不兼容的WebGPU绑定资源类型或绑定类型,其中兼容性由下表定义,则会出现流水线创建错误。

WebGPU binding type compatibility

WGSL resource

WebGPU

resource type

WebGPU binding member

WebGPU binding type

uniform buffer

GPUBufferBinding

buffer

GPUBufferBindingType

"uniform"

storage buffer with read_write access

"storage"

storage buffer with read access

"read-only-storage"

sampler

GPUSampler

sampler

GPUSamplerBindingType

"filtering"

"non-filtering"

sampler_comparison

"comparison"

sampled texture

GPUTextureView

texture

GPUTextureSampleType

"float"

"unfilterable-float"

"sint"

"uint"

"depth"

write-only storage texture

GPUTextureView

storageTexture

GPUStorageTextureAccess

"write-only"

external sampled texture

GPUExternalTexture

externalTexture

(not applicable)

你可能感兴趣的:(WebGPU,webgpu,wgsl)