共计 18868 个字符,预计需要花费 48 分钟才能阅读完成。
系列博客总目录:https://segmentfault.com/a/1190000040716735
https://www.w3.org/TR/webgpu/…
GPUPipelineBase
├ GPURenderPipeline
└ GPUComputePipeline
管线
管线代表某种计算的过程,在 WebGPU 中,有渲染管线和计算管线两种。
这一过程须要用到绑定组、VBO、着色器等对象或资源,而后最终能输入一些内容,譬如渲染管线输入色彩值(以色彩附件模式),计算管线输入到其指定的中央,此处就不列举太具体了。
管线在结构上看,由一系列 可编程阶段 和一些固定的状态组合而成。
留神,依据操作系统、显卡驱动不同,有局部固定的状态会编译到着色器代码中,因而将他们组合成一个管线对象里是不错的抉择。
两种管线对象均可由设施对象创立。
在对应的通道编码器中,能够切换管线以进行不同的计算过程。
1 根底管线
dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {GPUPipelineLayout layout;};
interface mixin GPUPipelineBase {GPUBindGroupLayout getBindGroupLayout(unsigned long index);
};
两种管线在创立时均须要参数对象,参数对象各自有不同的具体类型,但均继承自这里的 GPUPipelineDescriptorBase
类型。
两种管线也均继承自根底管线类型 GPUPipelineBase
。
1.1 根底管线的 getBindGroupLayout 办法
每个管线对象均有此办法,它承受一个 unsigned long 类型的数字作为参数,返回一个管线布局对象中对应地位的绑定组布局对象。
译者注:若创立管线时没有传递布局对象,这个办法会依据着色器代码内的 group 个性主动结构出绑定组布局对象。
有一个须要留神的,那就是这个数字参数要小于设施限度列表中的 maxBindGroups
值。
1.2 默认管线布局
如果创立一个管线时,没有设置管线布局对象,那么会主动在外部创立一个默认的布局对象。这个过程在文档中尚未详尽解释,只说了是一种“反射”技术,而后一步一步结构进去。
具体过程要参考文档中 WebGPU Spec 10.1.1 默认管线布局 的创立步骤。
默认的管线布局对象,其 bindGroupLayouts
数组是空数组。
1.3 可编程阶段:GPUProgrammableStage
在创立管线对象时,须要用到参数对象,这个参数对象有不同可编程阶段能够设置,其中每一个阶段都是一个 GPUProgrammableStage 对象。这一点在下文管线创立局部会具体列举进去。
GPUProgrammableStage
类型的对象组织起了管线中具体的一个阶段用到什么 GPUShaderModule,其 WGSL 入口点函数名是什么,须要传递哪些常量值这些信息。
会有具体介绍。
dictionary GPUProgrammableStage {
required GPUShaderModule module;
required USVString entryPoint;
record<USVString, GPUPipelineConstantValue> constants;
};
typedef double GPUPipelineConstantValue;
每一个这样的对象,有两个必选参数:
module
,GPUShaderModule 类型的变量,着色器模块,参考着色器模块章节entryPoint
,字符串,指定module
着色器代码中的入口函数
还有一个可选参数 constants
,是一个值是 GPUPipelineConstantValue 类型的 JavaScript 对象,它用来传递着色器代码中具备 override
个性的常量值。
这个对象的键能够是 WGSL 中个性 override(i)
括号里的 i,也能够是常量名。例如(以顶点阶段为例):
const pipeline = device.createRenderPipeline({
vertex: {
/* ... */
constants: {
1300: 2.0, // 将传递给着色器代码中第 1300 个,也即 gain 常量
depth: -1, // 将传递给着色器代码中的 depth 常量
}
}
})
对应着色器代码:
[[override(1300)]] let gain: f32; // 将收到 2.0f
[[override]] let depth: f32; // 将收到 -1
[[override(0)]] let has_point_light: bool = true;
[[override(1200)]] let specular_param: f32 = 2.3;
[[override]] let width: f32 = 0.0;
// 其余代码
如果你想把这几个 WGSL 常量中带默认值的也一并笼罩掉,能够传递这样的对象:
const pipeline = device.createRenderPipeline({
vertex: {
/* ... */
constants: {
1300: 2.0, // 将传递给着色器代码中第 1300 个,也即 gain 常量
depth: -1, // 将传递给着色器代码中的 depth 常量
0: false,
1200: 3.0,
width: 20,
}
}
})
JavaScript 的数值类型转到 WGSL 时,会主动依据常量在 WGSL 中具体的类型(bool,i32,u32,f32)进行转换。
1.4 补充材料:管线和着色阶段
WebGPU 通过收回绘制命令或调度命令的形式向 GPU 下达指令。
管线在 GPU 执行的计算的行为,被形容为一系列的阶段,其中有一些是可编程的。在 WebGPU 中,执行绘制或调度之前须要创立管线,执行绘制的叫渲染管线,执行调度的叫计算管线。
1.5 如何验证可编程阶段对象的合规性
创立管线时,对某个阶段对象(设为 stage)及管线布局对象(设为 layout)是有要求的:
- stage.module 必须是一个无效的
GPUShaderModule
对象 - stage.module 的着色器代码中必须有一个入口函数,名字与 stage.entryPoint 要统一
- 对入口函数中用到的每个被绑定的变量,验证绑定
- 对入口函数中用到的每个采样纹理,设纹理为 texture,设采样器为 sampler,sampler.type 如果是 “filtering”,那么 texture.sampleType 不能是 “unfilterable-float”
- 对于 stage.constants 中的所有常量,在着色器中必须有对应的 override 常量;如果着色器中的常量没有应用初始化语法给定默认值,那么 stage.constants 中必须给定值。
1.6 如何验证某个着色器中被绑定的变量的合规性
和题目用意一样,这一大节能够领导着色器中被绑定变量的语法书写、组织。
对于着色器中某个被绑定的变量,记作 variable
,设其 group
和 bind
的数字别离是 bindGroupId
和 bindId
,另外设管线布局对象为 pipelineLayout
,绑定组布局可由 pipelineLayout.bindGroupLayouts[bindGroupId]
失去并记作 bindGroupLayout
:
- bindGroupLayout 必须有一个 binding 值与
bindId
一样的 entry 对象; -
遍历 bindGroupLayout 中的 entries,设被遍历到的 GPUBindGroupLayoutEntry 变量为 entry:
-
如果是 buffer 类型,且 entry.buffer.type 是
- “uniform”,那么着色器代码中此 variable 应用
var<uniform>
申明 - “storage”,那么着色器代码中此 variable 应用
var<storage, read_write>
申明 - “read-only-storage”,那么着色器代码中此 variable 应用
var<storage, read>
申明
- “uniform”,那么着色器代码中此 variable 应用
-
如果是 buffer 类型且 entry.buffer.minBindingSize 不是 0:
- 如果着色器代码中某个构造体的最初一个字段是无界数组,那么 entry.buffer.minBindingSize 必须大于等于数组的偏移量与步幅的和
- 如果着色器代码中某个构造体最初一个字段并不是无界数组,那么 entry.buffer.minBindingSize 必须大于等于构造体的大小
-
如果是 sampler 类型,且 entry.sampler.type 是
- “filtering”,那么 variable 应用
sampler
类型 - “comparison”,那么 variable 应用
comparison_sampler
类型
- “filtering”,那么 variable 应用
- 如果是 texture 类型,当且仅当 entry.texture.multisampled 是 true 时,variable 要应用
texture_multisampled_2d<T>
或texture_depth_multisampled_2d<T>
类型; -
如果是 texture 类型,当 entry.texture.type 是
- “float”,”unfilterable-float”,”sint”,”uint” 时,variable 要应用
texture_1d<T>
,texture_2d<T>
,texture_2d_array<T>
,texture_cube<T>
,texture_cube_array<T>
,texture_3d<T>
, 或texture_multisampled_2d<T>
类型来申明;对于泛型参数 T,entry.texture.sampleType 是 “float”、”unfilterable-float” 时,T 是 f32,是 “sint” 时,T 是 i32,是 “uint” 时,T 是 u32; - “depth”,variable 要应用
texture_depth_2d
,texture_depth_2d_array
,texture_depth_cube
,texture_depth_cube_array
或texture_depth_multisampled_2d
类型来申明
- “float”,”unfilterable-float”,”sint”,”uint” 时,variable 要应用
-
如果是 texture 类型,当 entry.texture.viewDimension 是
- “1d”,variable 要应用
texture_1d<T>
申明; - “2d”,variable 要应用
texture_2d<T>
或texture_multisampled_2d<T>
申明; - “2d-array”,variable 要应用
texture_2d_array<T>
申明; - “cube”,variable 要应用
texture_cube<T>
申明; - “cube-array”,variable 要应用
texture_cube_array<T>
申明; - “3d”,variable 要应用
texture_3d<T>
申明;
- “1d”,variable 要应用
-
如果是 storageTexture 类型,当 entry.storageTexture.viewDimension 是
- “1d”,variable 要应用
texture_storage_1d<T, A>
类型来申明; - “2d”,variable 要应用
texture_storage_2d<T, A>
类型来申明; - “2d-array”,variable 要应用
texture_storage_2d_array<T, A>
类型来申明; -
“3d”,variable 要应用
texture_storage_3d<T, A>
类型来申明;entry.storageTexture.access 是 “write-only” 时,上述泛型中的 A 要应用
write
,泛型类型 T 要跟 entry.storageTexture.format 一样。
- “1d”,variable 要应用
-
2 渲染管线
渲染管线 GPURenderPipeline
是一种管制着顶点着色和片元着色阶段的管线。
它能够被渲染通道编码器 GPURenderPassEncoder
和 渲染打包编码器 GPURenderBundleEncoder
应用。
渲染管线的输出有:
- 通过
GPUPipelineLayout
提供的绑定组所绑定的资源 - 顶点和索引数据,由上面 顶点着色阶段 应用
- 色彩附件,由色彩指标状态形容
- 一个可选的深度模板附件,由深度模板状态形容
渲染管线的输入有:
- 绑定组中类型为
"storage"
的 GPUBuffer 资源 - 绑定组中
access
属性为"write-only"
的 GPUStorageTexture 资源 - 色彩附件
- 可选的深度模板附件
渲染管线有如下几个 渲染阶段 :
- 顶点提取(Vertex Fetch),由
GPUVertexState
对象的 buffers 属性来形容、管制 - 顶点着色(Vertex Shader),由
GPUVertexState
对象形容、管制 - 图元拆卸(Primitive Assembly),由
GPUPrimitiveState
对象形容、管制 - 光栅化(Rasterization),由
GPUPrimitiveState
、GPUDepthStencilState
和GPUMultisampleState
对象来形容、管制 - 片元着色(Fragment Shader),由
GPUFragmentState
对象来管制 - 顺次模板测试及操作(StencilTest)和深度测试并写入(DepthTest),由
GPUDepthStencilState
对象形容、管制 - 交融输入,由
GPUFragmentState
对象的 targets 属性来形容、管制
上述的 GPUVertexState
、GPUPrimitiveState
、GPUFragmentState
、GPUDepthStencilState
、GPUMultisampleState
类型在下文的 顶点着色阶段、图元拼装阶段、片元着色阶段、深度模板测试阶段、多重采样阶段 几个大节均有介绍,对应着接下来 渲染管线创立 大节中介绍的 GPURenderPipelineDescriptor
对象中的 vertex
、primitive
、fragment
、depthStencil
、fragment
字段。
这 5 个阶段中,只有顶点着色阶段和片元着色阶段是 可编程阶段。
渲染管线的 WebIDL 定义如下:
[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPipeline {
};
GPURenderPipeline includes GPUObjectBase;
GPURenderPipeline includes GPUPipelineBase;
2.1 管线的创立
渲染管线是通过设施对象的 createRenderPipeline
办法来创立的。
创立渲染管线须要一个 GPURenderPipelineDescriptor
类型的对象作为参数。
dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
required GPUVertexState vertex;
GPUPrimitiveState primitive = {};
GPUDepthStencilState depthStencil;
GPUMultisampleState multisample = {};
GPUFragmentState fragment;
};
在此对象中,有若干个阶段状态,在此简略形容每个字段的用处,前面大节会具体介绍它们对应的类。
vertex
字段,GPUVertexState
类型的 JavaScript 对象,是一个可编程阶段,除了可编程阶段对象的属性外,还额定形容了顶点着色器的入口点函数、输出到管线中的顶点数据的布局;primitive
字段,GPUPrimitiveState
类型的 JavaScript 对象,形容图元拆卸的信息,默认是空对象;depthStencil
字段,GPUDepthStencilState
类型的 JavaScript 对象,是一个可选对象,形容深度模板测试信息;multisample
字段,GPUMultisampleState
类型的 JavaScript 对象,默认是一个空对象,形容管线的多重采样信息;fragment
字段,GPUFragmentState
类型的 JavaScript 对象,是一个可编程阶段,除了可编程阶段对象的属性外,还额定形容了片元着色器的入口点函数及如何输入片元色彩。如果它是 null,那么渲染管线就不会输入色彩,仅仅作为无色彩输入模式,个别这种管线只拿来作深度模板测试。
2.1.1 异步创立管线
除了通过设施对象的 createRenderPipeline
办法同步创立外,还能够通过设施对象的 createRenderPipelineAsync
对象来异步创立,它返回的是 resolve 值是渲染管线对象的一个 Promise,你能够在异步函数中调用 await 来获取它的 resolve 值,异步创立函数同样也用到 GPURenderPipelineDescriptor
参数对象。
2.1.2 如何验证 GPURenderPipelineDescriptor 合规性
满足下列所有条件即可:
- 验证顶点着色阶段对象的可编程属性是否有问题
- 验证顶点着色阶段对象本人是否有问题
-
如果存在 descriptor.fragment 字段(不为 null):
- 验证片元着色阶段对象的可编程属性是否有问题
- 验证片元着色阶段对象本人是否有问题
- 如果片元着色器中用到了 “sample_mask” 内置变量,那么 descriptor.multisample.alphaToCoverageEnable 要设为 false
- 验证图元拼装阶段对象是否有问题
- 如果 descriptor.depthStencil 不是 null,那么 验证深度模板测试阶段对象是否有问题
- 验证多重采样阶段对象是否有问题
- 对于顶点着色器中用户自定义的进口,片元着色器必然要有与之对应的入口,到 wgsl 会介绍
- 对于片元着色器和片元着色器中自定义的组件(应该指的是通过 location 在两个着色器之间传递的总个数)的数量,要小于设施限度列表中的
maxInterStageShaderComponents
值。
2.2 图元拼装阶段
GPURenderPipelineDescriptor
对象里的 primitive
字段值,是一个 JavaScript Object。
enum GPUPrimitiveTopology {
"point-list",
"line-list",
"line-strip",
"triangle-list",
"triangle-strip"
};
enum GPUFrontFace {
"ccw",
"cw"
};
enum GPUCullMode {
"none",
"front",
"back"
};
dictionary GPUPrimitiveState {
GPUPrimitiveTopology topology = "triangle-list";
GPUIndexFormat stripIndexFormat;
GPUFrontFace frontFace = "ccw";
GPUCullMode cullMode = "none";
boolean clampDepth = false;
};
图元拆卸阶段对象是 GPUPrimitiveState
类型的,所有的参数均可选,它自身也是可选的。
- 参数
topology
,GPUPrimitiveTopology
字符串枚举类型的值,示意顶点如何拆卸,默认是三角形列表"triangle-list"
,其余还有 三角带"triangle-strip"
、线列表"line-list"
、线带"line-strip"
和单纯绘制 点"piont-list"
,与 WebGL 中 drawArray 的 mode 参数相似; - 参数
stripIndexFormat
,可选参数,若为索引三角形,则指定索引数值的数字类型,是一种字符串枚举类型GPUIndexFormat
,具体见顶点着色阶段中的 WebIDL 定义; - 参数
frontFace
,可选参数,默认值是"ccw"
,是一种字符串枚举类型GPUFrontFace
,指的是点形成三角形的缭绕方向,ccw 即逆时针,cw 即顺时针。 - 参数
cullMode
,可选参数,默认值是"none"
,是一种字符串枚举类型GPUCullMode
,指的是剔除模式,除了 none 之外还有前剔除、背剔除; - 参数
clampDepth
,可选参数,默认值是 false,布尔类型,若启用则示意深度值会被截取;须要在申请设施对象时启用clamp-depth
性能。
2.2.1 顶点索引格局 GPUIndexFormat
enum GPUIndexFormat {
"uint16",
"uint32"
};
顶点索引格局,GPUIndexFormat
,它决定了 VBO 中索引值的数据类型以及当图元拓扑为 “line-strip” 和 “triangle-strip” 时图元的 从新起算值 。
图元的从新起算值 (Primitive Restart Value),批示应从哪里从新开始算一个图元,而不是持续应用先前索引过的顶点来结构三角带。
GPUPrimitiveState,如果其 "topology"
字段指定了 “line-strip” 或 “triangle-strip”,那么它的 "stripIndexFormat"
字段也须要相应地设置,以便管线创立时能够用到图元从新起算值。
如果用的是 “triangle-list”、”line-list” 和 “point-list”,那么 "stripIndexFormat"
要设为 undefined,并要应用渲染通道编码器(GPURenderPassEncoder)的 setIndexBuffer
办法来设置索引。
译者注
原文介绍 GPUIndexFormat 类型是在顶点着色阶段(Vertex State),笔者感觉此局部应摆在用到它的类型下更适合。
uint16
代表从新起算值是 2byte(0xFFFF),即 2byte 才取一个索引数字;uint32
示意从新起算值是 4byte(0xFFFFFFFF),即 4byte 才取一个索引数字。
2.2.2 如何验证图元拼装阶段对象合规性
如果下列条件都满足,那么图元拼装阶段对象就是没问题的:
- 图元拼装阶段对象的 topology 字段是 “line-strip” 或 “triangle-strip”,那么 stripIndexFormat 不能是 undefined;topology 是其余的,stripIndexFormat 就必须是 undefined;
- 图元拼装阶段对象的 clampDepth 是 true,那么设施的性能列表要包含 “depth-clamp” 性能
2.2.3 代码举例
const renderPipeline = device.createRenderPipeline({
/* ... */
primitive: {topology: "triangle-list",}
})
2.3 顶点着色阶段
顶点着色阶段对象,是 GPURenderPipelineDescriptor
对象中 vertex
字段的值,是一个 JavaScript Object,要满足 GPUVertexState
类型(包含其父类型 GPUProgrammableStage):
dictionary GPUVertexState: GPUProgrammableStage {sequence<GPUVertexBufferLayout?> buffers = [];
};
dictionary GPUVertexBufferLayout {
required GPUSize64 arrayStride;
GPUVertexStepMode stepMode = "vertex";
required sequence<GPUVertexAttribute> attributes;
};
enum GPUVertexStepMode {
"vertex",
"instance"
};
GPUVertexState 对象须要一个 buffers 字段,是 GPUVertexBufferLayout
对象的数组。
GPUVertexBufferLayout
对象,须要两个必选参数:
arrayStride
,unsigned longlong 类型,示意一个顶点包含的所有数据(坐标、色彩、法线、纹理坐标等)步幅有多大;attributes
,一个数组,元素类型是GPUVertexAttribute
的对象,形容这块顶点数据中有多少个 顶点属性;
还有一个可选参数,GPUVertexStepMode
字符枚举类型的字段 stepMode
,默认值是 "vertex"
;它示意如何拜访顶点数据,它有两种值:
- “vertex”,示意无论渲染通道编码器收回几次绘制(draw 办法的 instanceCount 参数无论是几)指令,都不会从 VertexBuffer 的头部再从新开始读取顶点数据,而是基于第一次读取的开端持续往下读;
- “instance”,示意即便渲染通道编码器收回绘制屡次(draw 办法的 instanceCount 参数大于 1)的指令,在绘制完第一轮后(顶点着色器跑了一遍后),依然从同一个 VertexBuffer 的终点开始获取顶点数据;
2.3.1 顶点缓存(VertexBuffer)与顶点属性(GPUVertexAttribute)
概念上说,顶点缓存是显存中顶点数据的一个形容视图,具体而言是一个数组,前一个数组元素和后一个数组元素之间的间隔被称作 ArrayStride(单位:byte),也能够称之为元素的长度。
每一个元素,被称为一个顶点数据,它由若干个顶点属性(VertexAttribute)形成。每个顶点属性在顶点着色器中的 location 是举世无双的。
dictionary GPUVertexAttribute {
required GPUVertexFormat format;
required GPUSize64 offset;
required GPUIndex32 shaderLocation;
};
在 GPUVertexBufferLayout 对象的 attributes 数组中,每个元素即 GPUVertexAttribute 对象。
每个 GPUVertexAttribute 对象有三个必选参数:
format
,GPUVertexFormat
字符枚举类型,见下文 顶点格局,确定某种顶点属性的数字类型、元素组成状况;offset
,unsigned longlong 类型,指定它在每块顶点数据中的偏移量,单位 byte;shaderLocation
,unsigned long 类型,指定它在 WGSL 顶点着色器中的 location 号。
2.3.2 顶点格局 GPUVertexFormat
enum GPUVertexFormat {
"uint8x2",
"uint8x4",
"sint8x2",
"sint8x4",
"unorm8x2",
"unorm8x4",
"snorm8x2",
"snorm8x4",
"uint16x2",
"uint16x4",
"sint16x2",
"sint16x4",
"unorm16x2",
"unorm16x4",
"snorm16x2",
"snorm16x4",
"float16x2",
"float16x4",
"float32",
"float32x2",
"float32x3",
"float32x4",
"uint32",
"uint32x2",
"uint32x3",
"uint32x4",
"sint32",
"sint32x2",
"sint32x3",
"sint32x4",
};
下面 WebIDL 中顶点格局 GPUVertexFormat
枚举的每一个值中,有按顶点的组件类型(二三四维)、每个维度的数字类型两局部信息,其中有一些简写:
unorm
= 无符号归一化 u(unsigned)+ 归一化 norm(normalized)snorm
= 有符号归一化 s(signed)+ 归一化 norm(normalized)uint
= 无符号整型sint
= 有符号整型float
= 浮点数
以常见的 “float32x3” 为例(留神那个 x 是小写字母的 X),示意某个顶点属性的数值类型是三个 float32 数字(不肯定是坐标,也能够是其余的顶点属性(Vertex Attribute))。
2.3.3 代码举例
const renderPipeline = device.createRenderPipeline({
/* ... */
vertex: {
module: device.createShaderModule({code: ` /* wgsl vertex shader code */ `,}),
entryPoint: 'vertex_main',
buffers: [
{
arrayStride: 4 * 5, // 一个顶点数据占 20 bytes
attributes: [
{
// for Position VertexAttribute
shaderLocation: 0,
offset: 0,
format: "float32x3" // 其中顶点的坐标属性占 12 字节,三个 float32 数字
},
{
// for UV0 VertexAttribute
shaderLocation: 1,
offset: 3 * 4,
format: "float32x2" // 顶点的纹理坐标占 8 字节,两个 float32 数字
}
]
}
]
}
})
下面例子:
- 用到的
GPUVertexFormat
有 “float32x3″、”float32x2″; - 一个顶点数据(块)长 20 bytes,即从一个顶点数据的头部返回下一个顶点数据的头部,须要 “stride” 20 个字节;
- 对于排列在这块 VertexBuffer 中的某个顶点数据块中,有两个
GPUVertexAttribute
,一个是坐标,紧接着第二个是二维纹理坐标,所以纹理坐标的数据应在顶点数据块的第 12 个字节开始起算(0~11 字节是坐标的三个 float32 数字)
2.3.4 如何验证顶点缓存布局对象 GPUVertexBufferLayout 的合规性
设某个 GPUVertexBufferLayout 对象为 layout,当其合乎以下条件即可:
- layout.arrayStride ≤ 设施限度列表中的
maxVertexBufferArrayStride
,且为 4 的倍数 -
对于 layout.attributes 中的每个元素,令其为 attrib:
- 若 layout.arrayStride 是 0,则:attrib.offset + sizeof(attrib.format) ≤ 设施限度列表中的
maxVertexBufferArrayStride
- 否则 attrib.offset + sizeof(attrib.format) ≤ layout.arrayStride
- attrib.offset 至多是 min(4, sizeof(attrib.format)) 的倍数
- attrib.shaderLocation < 设施限度列表中的
maxVertexAttributes
- 若 layout.arrayStride 是 0,则:attrib.offset + sizeof(attrib.format) ≤ 设施限度列表中的
-
对于顶点着色器代码中入口函数的每个顶点属性,layout.attributes 中的每个 GPUVertexAttribute 元素对象均要满足:
-
着色器代码中的顶点属性变量类型,与 attrib.format 相比:
- 当 attrib.format 是 “unorm”,”snorm” 或 “float” 其中的一种时,着色器代码中变量类型要是
vecN<f32>
或f32
- 当 attrib.format 是 “uint” 其中的一种时,着色器代码中变量类型要是
vecN<u32>
或u32
- 当 attrib.format 是 “sint” 其中的一种时,着色器代码中变量类型要是
vecN<i32>
或i32
- 当 attrib.format 是 “unorm”,”snorm” 或 “float” 其中的一种时,着色器代码中变量类型要是
- 着色器中顶点属性的 location 个性值要等于 attrib.shaderLocation
-
2.3.5 如何验证顶点着色阶段对象 GPUVertexState 的合规性
设某个 GPUVertexState 对象为 state,当其合乎以下条件即可:
- state.buffers.length ≤ 设施限度列表中的
maxVertexBuffers
- state.buffers 中每个元素均要通过 顶点缓存布局对象的合规性验证
- state.buffers 数组的所有元素的 attributes 个数之和 ≤ 设施限度列表中
maxVertexAttributes
- 所有 GPUVertexAttribute 顶点属性对象的 shaderLocation 要惟一
2.4 片元着色阶段
片元着色阶段对象,是 GPURenderPipelineDescriptor
对象中 fragment
字段的值,是一个 JavaScript Object,要满足 GPUFragmentState
类型(包含其父类型 GPUProgrammableStage):
dictionary GPUFragmentState: GPUProgrammableStage {required sequence<GPUColorTargetState> targets;};
- 参数
targets
是元素为GPUColorTargetState
类型的对象的数组,是一个必须存在的参数,见 2.6 色彩输入阶段
如何验证片元着色阶段对象的合规性
设片元着色阶段 GPUFragmentState 对象为 state,其满足如下要求即可:
- state.targets.length ≤ 8
-
state.targets 中每个元素,称之为 colorState:
- colorState.format 必须是 WebGPU Spec 24.1.1 纯色格局(Plain color formats)中标为
RENDER_ATTACHMENT
的一种 -
若 colorState.blend 是 undefined:
- colorState.format 要在 WebGPU Spec 24.1.1 纯色格局(Plain color formats)当选
- colorState.blend.color、colorState.blend.alpha 必须是无效的
- colorState.writeMask ≤ 16
- 片元着色器入口函数有与 state.targets 中相匹配的输入 location,管线输入对象的类型要与 colorState.format 绝对应,否则 colorState.writeMask 必须是 0
- colorState.format 必须是 WebGPU Spec 24.1.1 纯色格局(Plain color formats)中标为
colorState.blend.color、colorState.blend.alpha 如何说是无效的?令它俩为 component,若 component.operation 是 “min” 或 “max”,那么 component.srcFactor 和 component.dstFactor 都必须是 “one” 即可。
举例
const renderPipeline = device.createRenderPipeline({
/* ... */
fragment: {
module: device.createShaderModule({code: `/* wgsl fragment shader source code */`,}),
entryPoint: "fragment_main",
targets: [{format: "bgra8unorm"}]
}
})
2.5 深度模板测试阶段
深度模板测试阶段对象,是 GPURenderPipelineDescriptor
对象中 depthStencil
字段的值,是一个 JavaScript Object,要满足 GPUDepthStencilState
类型:
dictionary GPUDepthStencilState {
required GPUTextureFormat format;
boolean depthWriteEnabled = false;
GPUCompareFunction depthCompare = "always";
GPUStencilFaceState stencilFront = {};
GPUStencilFaceState stencilBack = {};
GPUStencilValue stencilReadMask = 0xFFFFFFFF;
GPUStencilValue stencilWriteMask = 0xFFFFFFFF;
GPUDepthBias depthBias = 0;
float depthBiasSlopeScale = 0;
float depthBiasClamp = 0;
};
dictionary GPUStencilFaceState {
GPUCompareFunction compare = "always";
GPUStencilOperation failOp = "keep";
GPUStencilOperation depthFailOp = "keep";
GPUStencilOperation passOp = "keep";
};
enum GPUStencilOperation {
"keep",
"zero",
"replace",
"invert",
"increment-clamp",
"decrement-clamp",
"increment-wrap",
"decrement-wrap"
};
因为官网这部分没有文字介绍,到后续有例子学习时再专门补充。
如何验证深度模板测试阶段对象的合规性
对于深度模板测试阶段 GPUDepthStencilState
对象,令其为 state,只需满足:
- state.format 是 WebGPU Spec 24.1.2 深度模板格局(Depth/stencil format)中的一个;
- 若 state.depthWriteEnabled 是 true,或者 state.depthCompare 不是 “always”,那么 state.format 必须是有深度局部的格局
- 若 state.stencilFront 或 state.stencilBack 不是默认值,那么 state.format 必须是有模板局部的格局
2.6 色彩输入阶段
色彩输入阶段对象,GPUColorTargeState
类型,是 GPURenderPipelineDescriptor
对象中 fragment
字段中 targets
这个数组字段中元素的类型。
dictionary GPUColorTargetState {
required GPUTextureFormat format;
GPUBlendState blend;
GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL
};
子类型:
dictionary GPUBlendState {
required GPUBlendComponent color;
required GPUBlendComponent alpha;
};
typedef [EnforceRange] unsigned long GPUColorWriteFlags;
[Exposed=(Window, DedicatedWorker)]
namespace GPUColorWrite {
const GPUFlagsConstant RED = 0x1;
const GPUFlagsConstant GREEN = 0x2;
const GPUFlagsConstant BLUE = 0x4;
const GPUFlagsConstant ALPHA = 0x8;
const GPUFlagsConstant ALL = 0xF;
};
混合模式
dictionary GPUBlendComponent {
GPUBlendOperation operation = "add";
GPUBlendFactor srcFactor = "one";
GPUBlendFactor dstFactor = "zero";
};
enum GPUBlendFactor {
"zero",
"one",
"src",
"one-minus-src",
"src-alpha",
"one-minus-src-alpha",
"dst",
"one-minus-dst",
"dst-alpha",
"one-minus-dst-alpha",
"src-alpha-saturated",
"constant",
"one-minus-constant"
};
enum GPUBlendOperation {
"add",
"subtract",
"reverse-subtract",
"min",
"max"
};
因为官网这部分没有文字介绍,到后续有例子学习时再专门补充。
2.7 多重采样阶段
色彩输入阶段对象,GPUMultisampleState
类型,是 GPURenderPipelineDescriptor
对象中 multisample
字段的值。
dictionary GPUMultisampleState {
GPUSize32 count = 1;
GPUSampleMask mask = 0xFFFFFFFF;
boolean alphaToCoverageEnabled = false;
};
- 参数
count
,采样次数,只能是 1 或者 4,默认值是 1; - 参数
mask
,采样遮罩值,类型是 unsigned long,默认值是0xFFFFFFFF
; - 参数
alphaToCoverageEnabled
,默认值是 false。
前面两个值,在 WebGPU Spec 21.3.10 采样遮罩 中会介绍它们的用处,采样遮罩值是 0 的片元将不会输入色彩,它由三个遮罩变量取逻辑与形成,其中一个 shader-output-mask
由片元着色器中的内置变量 sample_mask
决定,而这个内置变量又由此处的 alphaToCoverageEnabled、mask 值独特作用而成,此处为简略形容。
如何验证多重采样阶段对象的合规性
alphaToCoverageEnabled 若为 true,count 要大于 1.
代码举例
const renderPipeline = device.createRenderPipeline({
/* ... */
multisample: {count: 4}
})
2.8 综合代码举例
const renderPipeline = device.createRenderPipeline({layout: [/* GPURenderPipelineLayout[] */],
vertex: {/* {}: GPUVertexState */ },
fragment: {/* {}: GPUVertexState */ },
primitive: {/* {}: GPUPrimitiveState */ },
multisample: {/* {}: GPUMultisampleState */ }
})
3 计算管线
计算管线,GPUComputePipeline
类型,是一种能在计算通道编码器 GPUComputePassEncoder
中应用的管线,实现某个通用计算的过程。
计算管线的输出和输入在绑定组中绑定好了,并通过 GPUPipelineLayout
提供(详见资源绑定章节)。
它不像渲染管线一样有特地具体的输入后果,它的后果,将写入绑定组中 type 是 "storage"
的 buffer 和 type 是 "write-only"
的 storageTexture 的两种资源。
计算管线的阶段只有一个:计算着色器。
上面是计算管线的接口定义:
[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePipeline {
};
GPUComputePipeline includes GPUObjectBase;
GPUComputePipeline includes GPUPipelineBase;
计算管线的创立
设施对象的 createComputePipeline
能够创立一个计算管线,它须要一个 GPUComputePipelineDescriptor
类型的 JavaScript 对象作为参数。
dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {required GPUProgrammableStage compute;};
GPUComputePipelineDescriptor 类型继承自 GPUPipelineDescriptorBase
类型,意味着也要遵循父类型的字段规定。
也能够通过设施对象的 createComputePipelineAsync
办法异步创立计算管线,语法与异步创立渲染管线一样,参数与同步创立计算管线一样。
如何验证 GPUComputePipelineDescriptor 参数对象的合规性
令 GPUComputePipelineDescriptor 对象名为 descriptor。
- descriptor.layout 与设施对象必须是无效的
- 验证计算着色阶段对象的可编程属性是否有问题
- descriptor.compute 中计算着色器用到的工作组大小要不大于设施对象限度列表中的
maxComputeWorkgroupStorageSize
值,每个工作组用到的 invocation 要不大于设施对象限度列表中maxComputeInvocationsPerWorkgroup
值; - descriptor.compute 中计算着色器的
workgroup_size
个性的三个重量值都不能大于设施对象限度列表中的maxComputeWorkgroupSizeX
、maxComputeWorkgroupSizeY
、maxComputeWorkgroupSizeZ
的值;
以上均通过的话,则能够返回一个正确的计算管线对象。
代码举例
const computePipeline = device.createComputePipeline({
compute: {
module: device.createShaderModule({code: `/* wgsl compute shader source code */`}),
entryPoint: "main",
}
})
4 译者注
这部分较长,有一部分我还没有实际,学习、翻译起来比拟吃力。
然而稀释起来,提炼骨干看,只讲了上面四件事:
- 管线是什么,输出和输入是什么货色,如何创立的,有什么类型的管线
- 管线是由阶段形成的,其中有三个阶段(顶点着色、片元着色、通用计算)是可编程阶段
- 管线配套的绑定组布局、顶点缓存布局与着色器代码之间的坑位的连贯规定
- 顶点着色阶段中十分重要的概念:VertexBuffer 和顶点属性
管线是 WebGPU 中执行一个残缺的计算(或渲染,或通用计算)的最小单元,他有着本人的输出和输入。
你能够通过下一篇文章的通道编码器向其传递不同的 VertexBuffer 和 资源绑定组,然而通道编码器的作用可不仅仅是向管线设置数据那么简略,下文见。