WebGPU

Editor’s Draft,

More details about this document
This version:
https://gpuweb.github.io/gpuweb/
https://github.com/gpuweb/gpuweb/blob/a63de4dff4bf001e6a4adea8b5ccd1dc7d735025/spec/index.bs
Latest published version:
https://www.w3.org/TR/webgpu/
Feedback:
GitHub
Editors:
(Google)
(Google)
(Apple Inc.)
Former Editors:
(Mozilla)
(Apple Inc.)
Participate:
File an issue (open issues)

Abstract

WebGPU API翻译文档。本翻译非官方翻译。(WebGPU 公开了一个 API,用于在图形处理单元上执行操作,例如渲染和计算。)

Status of this document

This specification was published by the GPU for the Web Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. 介绍

This section is non-normative.

图形处理单元,简称GPU,一直以来都是个人计算中实现丰富渲染和计算应用的关键部件。 WebGPU是一个将GPU硬件功能暴露给Web的API。该API从零开始设计,以便有效地映射到(2014年后的)原生GPU API。 WebGPU与WebGL无关,也没有显式地针对OpenGL ES。

WebGPU将物理GPU硬件视为GPUAdapters。它通过GPUDevice提供与适配器的连接,管理资源和设备的GPUQueues,执行命令。GPUDevice可能具有自己的内存,并可高速访问处理单元。GPUBufferGPUTexture是由GPU内存支持的物理资源GPUCommandBufferGPURenderBundle是用户记录命令的容器。GPUShaderModule包含着色器代码。其他资源,如GPUSamplerGPUBindGroup,配置了GPU使用物理资源的方式。

GPU通过将数据通过管线来执行GPUCommandBuffer中编码的命令,这是固定功能和可编程阶段的混合。可编程阶段执行着色器,这些着色器是专为在GPU硬件上运行而设计的特殊程序。 pipeline的大部分状态由GPURenderPipelineGPUComputePipeline对象定义。这些管线对象中未包含的状态在编码时通过命令设置,例如beginRenderPass()setBlendConstant()

2. 恶意使用注意事项

本节内容为非标准的 本节描述了在 Web 上公开此 API 相关的风险

2.1. 安全

WebGPU的安全性要求与Web的要求相同,同样也是不容妥协的。总体方法是在命令到达GPU之前严格验证所有的命令,确保页面只能处理其自己的数据。

2.1.1. 基于 CPU 的未定义行为

WebGPU实现将用户提出的工作负载转换为针对目标平台的特定API命令。原生API为命令指定有效用法(例如,参见vkCreateDescriptorSetLayout(https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateDescriptorSetLayout.html)),通常情况下,如果不遵循有效用法规则,则不能保证任何结果。这被称为“未定义行为”,攻击者可能利用这一行为访问他们无权访问的内存,或迫使驱动程序执行任意代码。

为了禁止不安全的使用,定义了允许的WebGPU行为范围。实现必须验证用户的所有输入,并仅在有效工作负载下达到驱动程序。这份文档详细说明了所有错误条件及处理语义。例如,在copyBufferToBuffer()的“源”和“目标”中同时指定具有相交范围的相同缓冲区,将导致GPUCommandEncoder产生错误,并且不会发生其他操作。

有关错误处理的更多信息,请参阅§ 22 错误 & 调试

2.1.2. 基于 GPU 的未定义行为

WebGPU 着色器在GPU硬件内部的计算单元中执行。在原生API中,一些着色器指令可能导致GPU上的未定义行为。为了解决这个问题,WebGPU严格定义了着色器指令集及其定义的行为。当着色器提供给createShaderModule()时,WebGPU实现必须在进行任何转换(至特定平台的着色器)或变换过程之前对其进行验证。

2.1.3. 未初始化的数据

通常,分配新内存可能暴露其他在系统上运行的应用程序的残留数据。为解决这一问题,WebGPU在概念上将所有资源初始化为零,尽管在实践中,如果实现发现开发者手动初始化内容,则可以跳过此步骤。这包括着色器内的变量和共享工作组内存。

清除工作组内存的确切机制可能因平台而异。如果原生API没有提供清除它的功能,WebGPU实现将计算着色器转换为首先在所有调用中执行清除操作,然后同步它们,并继续执行开发者的代码。

注: 在队列操作中使用的资源的初始化状态只能在操作入队时(例如,不是在编码到命令缓冲区时)获知。因此,一些实现将需要在入队时进行非优化的迟清除操作(例如,清除纹理,而不是将GPULoadOp “load”更改为“clear”)。

因此,所有实现应该在开发者控制台中发出关于这种潜在性能损失的警告,即使在该实现中没有损失。

2.1.4. 着色器中的越界访问

着色器可以直接访问物理资源(例如,作为“uniform” GPUBufferBinding),或通过纹理单元访问,纹理单元是固定功能硬件块,负责处理纹理坐标转换。WebGPU API中的验证只能保证提供了着色器的所有输入,并且它们具有正确的用法和类型。如果未涉及纹理单元,WebGPU API无法保证数据在边界内访问。

为了防止着色器访问应用程序不拥有的GPU内存,WebGPU实现可能会在驱动程序中启用一种特殊模式(称为“稳健缓冲区访问”),以保证访问仅限于缓冲区边界。

或者,实现可以通过插入手动边界检查来转换着色器代码。当采用这种方法时,越界检查仅适用于数组索引。由于主机端的minBindingSize验证,不需要对着色器结构的简单字段访问进行额外的边界检查。

如果着色器尝试在物理资源边界之外加载数据,实现可以执行以下操作:

  1. 返回资源边界内的其他位置的值

  2. 返回"(0, 0, 0, X)"的值向量,其中X可以是任何值

  3. 部分丢弃绘制或调度调用

如果着色器尝试在物理资源边界之外写入数据,实现可以执行以下操作:

  1. 将值写入资源边界内的其他位置

  2. 丢弃写入操作

  3. 部分丢弃绘制或调度调用

2.1.5. 无效数据

在将浮点数数据从CPU上传至GPU,或在GPU上生成这些数据时,我们可能得到一个并不对应于有效数字的二进制表示,例如无穷大或NaN(非数字)。在这种情况下,GPU的行为取决于GPU硬件对IEEE-754标准的实现准确性。WebGPU保证引入无效浮点数只会影响算术计算的结果,不会产生其他副作用。

2.1.6. 驱动程序错误

GPU驱动程序和其他软件一样,可能会受到错误的影响。如果出现错误,攻击者可能会利用驱动程序的错误行为来获取未授权的数据。为了降低风险,WebGPU工作组将与GPU供应商合作,将WebGPU Conformance Test Suite(CTS)集成到他们的驱动程序测试流程中,就像对WebGL所做的那样。

WebGPU实现预计会针对发现的一些错误进行修复,并在无法修复的已知错误的驱动程序上禁用WebGPU。

2.1.7. 定时攻击

WebGPU被设计为通过Web Workers支持多线程使用。因此,它旨在不让用户面临现代高精度定时攻击。一些对象,如GPUBufferGPUQueue,具有可以同时访问的共享状态。这可能导致类似于从多个Web Workers访问SharedArrayBuffer的竞态条件,从而使线程调度可观察。

WebGPU通过限制只能对代理内的代理集群进行反序列化(或共享)对象的能力来解决这个问题,并且只有在启用cross-origin isolated策略时才有效。这种限制与防止恶意 SharedArrayBuffer 使用的mitigations相匹配。同样,用户代理也可以序列化共享任何句柄的代理,以完全阻止任何并发。

最后,在WebGPU中共享状态的竞争攻击面将是 SharedArrayBuffer 攻击的一个小子集。

WebGPU还指定了“timestamp-query”功能,该功能可以提供GPU操作的高精度计时。该功能是可选的,WebGPU实现可能仅限于仅将其暴露给受信任的场景。或者,计时查询结果可以由计算着色器处理并与较低精度对齐。

2.1.8. 行锤攻击

Row hammer是一类利用DRAM单元中状态泄漏的攻击。它可以在GPU(https://www.vusec.net/projects/glitch/)上使用。WebGPU没有任何特定的缓解措施,而是依赖于平台级解决方案,例如减少内存刷新间隔。

2.1.9. 拒绝服务

WebGPU应用程序可以访问GPU内存和计算单元。WebGPU实现可能会限制应用程序可用的GPU内存,以保持其他应用程序的响应。对于GPU处理时间,WebGPU实现可以设置"看门狗"定时器,确保应用程序不会导致GPU在几秒钟内无响应。这些措施与WebGL中使用的措施类似。

2.1.10. 工作负载识别

WebGPU 提供对在同一机器上运行的不同程序(和网页)之间共享的受限全局资源的访问。应用程序可以尝试间接探测这些全局资源的约束程度,以便根据这些共享资源的使用模式推断其他打开的网页所执行的工作负载。这些问题通常类似于Javascript的问题,例如系统内存和CPU执行吞吐量。WebGPU 没有为此提供任何额外的缓解措施。

2.1.11. 内存资源

WebGPU公开了来自机器全局内存堆(如显存)的可失败分配。这允许通过尝试分配并观察分配失败来探测系统剩余可用内存的大小(对于给定的堆类型)。

内部的GPU通常具有一个或多个(通常只有两个)供所有运行应用程序共享的内存堆。当堆耗尽时,WebGPU将无法创建资源。这是可观察的,这可能允许恶意应用程序猜测其他应用程序使用哪些堆,以及它们从这些堆中分配了多少内存。

虽然这种信息可能被滥用,但要解决这个问题并不容易。WebGPU实现可能会采用一些策略来降低此问题带来的风险,如限制应用程序可用的内存和计算资源,从而保持其他应用程序的响应性。但是,这种策略并不能完全解决问题,因为恶意应用程序仍然可以通过观察资源分配失败来获取有关系统内存堆的信息。

总之,尽管WebGPU暴露了这种潜在的攻击途径,但开发者和用户仍需要保持警惕,并致力于在应用程序安全和系统资源管理方面进行持续改进。

2.1.12. 计算资源

如果一个网站与另一个网站同时使用WebGPU,它可能会注意到处理某些工作所需的时间增加。例如,如果一个网站不断提交计算工作负载并跟踪队列上的工作完成情况,它可能会注意到还有其他内容开始使用GPU。

GPU有许多可以独立测试的部分,如算术单元、纹理采样单元、原子单元等。恶意应用程序可能会感知到其中一些单元所受到的压力,并尝试通过分析压力模式来猜测另一个应用程序的工作负载。这类似于Javascript在CPU执行的现实情况。

要解决此问题并不容易,WebGPU 可能无法提供针对此类信息泄露的特定缓解措施。然而,开发者和用户仍可保持警惕并实施最佳实践,以确保应用程序安全并最大程度地减小潜在风险。这可能包括限制或屏蔽一些具有潜在危险性的GPU功能,或在系统和应用程序之间实施严格的资源隔离。

2.1.13. 功能滥用

恶意网站可能会滥用WebGPU公开的功能来运行对用户或用户体验没有益处的计算,而仅仅是为了网站自身的利益。例如,隐藏式的加密货币挖矿、密码破解或彩虹表计算。

由于浏览器无法区分有效工作负载和滥用工作负载,因此无法防范这类API使用。这是Web上所有通用计算功能的普遍问题:JavaScript,WebAssembly或WebGL。WebGPU只是使一些工作负载更容易实现或稍微比使用WebGL更高效地运行。

为了缓解这种滥用形式,浏览器可以限制对后台标签页的操作,警告某个标签页使用了大量资源,并限制允许使用WebGPU的上下文。

用户代理可以启发式地向用户发出关于高功耗使用的警告,特别是可能存在恶意使用。如果用户代理实现这种警告,在启发式中应包括WebGPU的使用,以及JavaScript、WebAssembly、WebGL等。

2.2. 隐私

There is a tracking vector here. WebGPU的隐私考虑与WebGL相似。出于使开发者能有效利用这些功能的必要性,GPU API 需要复杂地暴露设备功能的各个方面。一般的缓解方法包括归一化或将潜在的识别信息分组,并在可能的情况下强制实施统一的行为。

用户代理不得透露超过32个可区分的配置或桶。

2.2.1. 特定于机器的功能和限制

WebGPU可以揭示底层GPU架构和设备几何的许多细节。这包括可用的物理适配器,GPU和CPU资源可使用的许多限制(如最大纹理尺寸)以及可用的任何可选硬件特定功能。

用户代理没有义务公开真正的硬件限制,他们完全掌控如何公开机器特定信息。减少指纹识别的一种策略是将所有目标平台分为少数几类。总的来说,公开硬件限制对隐私的影响与WebGL相匹配。

默认限制也故意设定得足够高,以允许大多数应用程序在不请求更高限制的情况下工作。按照所请求的限制对API的所有使用情况进行验证,因此实际的硬件能力不会意外地暴露给用户。

2.2.2. 特定于机器的工件

有些机器特定的光栅化/精度伪像和性能差异可以大致以与WebGL相同的方式观察到。这适用于光栅化覆盖和模式、着色器阶段之间变化的插值精度、计算单元调度以及执行的更多方面。

通常情况下,光栅化和精度指纹在每个供应商的大部分或所有设备之间是相同的。性能差异相对难以解决,但也相对信号较低(就像JS执行性能一样)。

对隐私至关重要的应用程序和用户代理应使用软件实现来消除此类伪像。

2.2.3. 机器特定性能

另一个区分用户的因素是测量GPU上特定操作的性能。即使在低精度计时下,通过重复执行某个操作也可以显示出用户的设备在特定工作负载上的运行速度。这是一个相当常见的向量(在WebGL和Javascript中都存在),但信号较低,而且实际上很难进行真正的归一化。

WebGPU计算管线能够在不受固定功能硬件限制的情况下访问GPU。这对于唯一设备指纹识别带来了额外的风险。用户代理可以采取措施将逻辑GPU调用与实际计算单元分离,以降低这种风险。

2.2.4. 用户代理状态

本规范没有为一个源定义任何额外的用户代理状态。然而,预计用户代理将有编译缓存,用于昂贵的编译结果,如 GPUShaderModuleGPURenderPipelineGPUComputePipeline。这些缓存对于在首次访问后提高WebGPU应用程序的加载时间非常重要。

对于规范来说,这些缓存与非常快速的编译无法区分,但对于应用程序来说,很容易测量 createComputePipelineAsync() 所需的解析时间。这可能在源之间泄漏信息(例如 “用户是否访问了具有此特定着色器的站点”),所以用户代理应该遵循存储分区的最佳实践。

系统的GPU驱动程序也可能拥有自己的编译着色器和管线缓存。用户代理可能希望尽可能禁用这些功能,或者以使GPU驱动程序将它们视为不同的方式向着色器中添加每个分区的数据。

2.2.5. 驱动程序错误

除了安全中概述的问题外,驱动程序错误可能引入行为差异,这可以作为一种区分用户的方法来观察。在这里同样适用于安全性考虑中提到的缓解措施,包括与GPU供应商协调以及在用户代理中针对已知问题实施解决方案。通过这些措施,可以最大限度地降低因驱动程序错误导致的安全风险,确保WebGPU应用及相关用户的安全性。

2.2.6. 适配器标识符

根据WebGL的过往经验,开发者确实需要能识别其代码运行在哪个GPU上,以便创建和维护稳健的GPU基础内容。例如,识别具有已知驱动程序错误的适配器以解决它们,或避免在给定类别的硬件上性能低于预期的功能。

但是,公开适配器标识符也自然地扩大了可用的指纹信息量,因此有必要限制识别适配器的精度。

可以应用多种缓解措施来在实现稳健内容和保护隐私之间达到平衡。首先,用户代理可以通过识别和解决已知驱动程序问题来减轻开发者的负担,自从浏览器开始使用GPU以来就一直有这样做。

当默认公开适配器标识符时,它们应尽可能宽泛地有用。例如,可能识别适配器的供应商和一般架构,而不识别正在使用的特定适配器。同样,在某些情况下,可能会报告被认为是实际适配器合理代理的适配器标识符。

在完整且详细的适配器信息有用的情况下(例如:提交错误报告时),可以要求用户同意向页面透露有关其硬件的更多信息。

最后,如果用户代理认为适当(如在增强隐私模式下),它将始终有权自行决定是否报告适配器标识符。

3. 基本概念

3.1. 公约

3.1.1. 语法速记

在本规范中,使用以下语法简写:

. (“dot”) 语法,在编程语言中很常见。

术语 " Foo.Bar "的含义是 “值(或接口)Foo 的 Bar 成员”。 术语 " Foo.Bar 是 提供" 的含义是 " Bar 成员在 map 值 Foo 中存在"。

从 JavaScript 中采用的 ?. (“可选链”) 语法。

术语 " Foo?.Bar "的含义是 “如果 Foo 是 null 或 undefined,或者 Bar 在 Foo 中不存在,那么返回 undefined;否则,返回Foo.Bar”。

例如,在 buffer 是一个 GPUBuffer 的情况下,buffer?.[[device]].[[adapter]] 的含义是 "如果 buffer 是 null 或 undefined,那么返回 undefined;否则,返回 buffer 的 [[device]] 内部槽中的 [[adapter]] 内部槽。

从 JavaScript 中采用的 ?? (“空值合并”) 语法。

术语 " x ?? y "的含义是 “如果 x 不是 null/undefined, 则返回 x,否则返回 y”。

槽支持的属性

由相同名称的内部槽支持的 WebIDL 属性。它可能是可变的,也可能是不可变的。

3.1.2. WebGPU 接口

WebGPU接口定义了一个WebGPU对象。它可以用于:

在它被创建的内容时间线上,它是一个JavaScript公开的WebIDL接口。 在所有其他时间线上,只能访问不可变属性

以下特殊属性类型可以在WebGPU接口上定义:

不可变属性

一个在对象初始化期间设置的只读槽。它可以从任何时间线访问。

注:由于槽是不可变的,实现可以在多个时间线上根据需要拥有一个副本。不可变属性 的定义就是这样,以避免在本规范中描述多个副本。

如果名为 [[with brackets]], 它就是一个内部槽。 如果名为 withoutBrackets, 它是一个 只读 槽支持的属性

内容时间线属性

一个只能从创建对象的内容时间线上访问的属性。

如果名为 [[with brackets]],它就是一个内部槽。 如果名为 withoutBrackets,它是一个 槽支持的属性

任何包含GPUObjectBase的接口都是一个WebGPU接口

interface mixin GPUObjectBase {
    attribute USVString label;
};
创建一个新的WebGPU对象(GPUObjectBase parent, 接口 T, GPUObjectDescriptorBase descriptor)(其中 T 继承自 GPUObjectBase):
  1. device 成为 parent.[[device]]

  2. object 成为 T 的一个新实例。

  3. internals 成为 T 类型的 \[[internals]](可能会覆盖 GPUObjectBase.[[internals]])的新(未初始化)实例,且只能从 device设备时间线 上访问。

  4. 设置 object.[[device]]device

  5. 设置 object.[[internals]]internals

  6. 设置 object.labeldescriptor.label

  7. 返回 [object, internals]。

GPUObjectBase具有以下不可变属性

[[internals]], 类型为 内部对象,只读,可覆盖

内部对象

对该对象内容的操作需要断言他们正运行在 设备时间线 上,且设备是 有效的。

对于每个子类型化 GPUObjectBase 的接口,它可以使用 内部对象 的子类型进行覆盖。此槽最初设置为该类型的未初始化对象。

[[device]], 类型为 设备,只读。

拥有 内部对象设备

对此对象内容的操作需要断言它们正在 设备时间线 上运行,且设备是 有效的。

GPUObjectBase 具有以下 内容时间线属性

label, of type USVString

一个由开发者提供的标签,以实现定义的方式使用。它可以被浏览器、操作系统或其他工具用于帮助开发者识别底层的 内部对象。示例包括在 GPUError 消息、控制台警告、浏览器开发者工具和平台调试实用程序中显示标签。

注: labelGPUObjectBase 的一个属性。两个 GPUObjectBase “包装器” 对象具有完全独立的标签状态,即使它们引用相同的底层对象(例如通过 getBindGroupLayout() 返回的对象)。label 属性除了从JavaScript设置之外不会改变。

这意味着一个底层对象可以与多个标签关联。此规范未定义标签如何传播到 设备时间线 上。标签的使用完全由实现定义:错误消息可能显示最近设置的标签、所有已知标签或根本不显示标签。

该属性定义为 USVString,因为某些用户代理可能会将其提供给底层原生 API 的调试工具。

注: 理想情况下,WebGPU 接口不应阻止它们的父对象(例如拥有它们的 [[device]])被垃圾收集。然而,这不能得到保证,因为在某些实现中可能需要持有对父对象的强引用。

因此,开发者应该假设,除非该接口的所有子对象也被垃圾收集,否则 WebGPU 接口 可能不会被垃圾回收。这可能导致某些资源分配的时间比预期更长。

如果需要可预测地释放已分配的资源,应优先调用 WebGPU 接口上的 destroy 方法(例如 GPUDevice.destroy()GPUBuffer.destroy()),而不是依赖垃圾收集。

3.1.3. 内部对象

内部对象 跟踪可能仅在 设备时间线上使用的WebGPU对象的状态,在 设备时间线插槽 中,这些槽可能是可变的。

设备时间线插槽

一个仅从 设备时间线 可访问的内部插槽。

所有对 内部对象 的可变状态的读/写操作都发生在单个严格有序的 设备时间线 上执行的步骤中。这些步骤可能来自多个 代理 上的任何一个 内容时间线 算法。

注:" 代理" 是指JavaScript的 “线程”(即主线程或Web Worker)。

3.1.4. 对象描述符

一个对象描述符包含了创建对象所需的信息,这通常是通过GPUDevicecreate* 方法之一来完成的。

dictionary GPUObjectDescriptorBase {
    USVString label;
};

GPUObjectDescriptorBase has the following members:

label, of type USVString

GPUObjectBase.label的初始值。

3.2. 异步性

3.2.1. 无效的内部对象 & 具有传染性的无效性

在WebGPU中,对象创建操作不返回promise,但实际上是内部异步的。返回的对象引用的是在设备时间线上操作的内部对象。大多数发生在设备时间线上的错误不会通过异常或拒绝失败,而是通过与相关联的设备生成的GPUError进行通信。

内部对象要么有效,要么无效。 一个无效的对象将永远不会在以后变得有效,但一些有效的对象可能会变成无效

如果无法创建对象,那么对象将从创建时开始就是无效的。例如,这可能是因为对象描述符没有描述一个有效对象,或者没有足够的内存来分配资源。

绝大多数类型的内部对象在创建后不能变无效,但仍可能变得不可用,例如,如果拥有的设备变为丢失或者销毁,或者对象具有特殊的内部状态,如缓冲区状态"销毁"。

某些类型的内部对象在创建后可以变为无效;具体来说,这些类型包括设备适配器GPUCommandBuffer和命令/通道/包编码器。

给定的GPUObjectBase 对象 只有在满足以下要求时,才被认为是可用于targetObject 一起使用的:

3.2.2. Promise 排序

在WebGPU中,有几个操作会返回promises。

WebGPU不对这些promises的解决顺序(resolve或reject)做任何保证,除了以下情况:

应用程序不能依赖于其他任何promise解决顺序。

3.3. 坐标系统

注:WebGPU 的坐标系在图形管线中与 DirectX 的坐标系相匹配。

3.4. 编程模型

3.4.1. 时间线

本节为非规范性内容。

一个带有用户代理前端和GPU后端的计算机系统中,各组件在不同的时间线上并行工作:

内容时间线

与Web脚本的执行相关联。 它包括调用本规范描述的所有方法。

要从操作GPUDevice device 中向内容时间线发出步骤,请使用为GPUDevice排队全局任务 device与这些步骤。

设备时间线

与用户代理发出的GPU设备操作相关联。 它包括创建适配器、设备和GPU资源以及状态对象,这些通常是从控制GPU的用户代理部分的角度来看同步操作, 但可以在单独的操作系统进程中运行。

队列时间线

与在GPU的计算单元上执行操作相关联。它包括在GPU上运行的实际绘制、复制和计算任务。

以下展示了与每个时间线相关联的步骤和值的样式。这种样式是非规范性的;规范文本始终描述关联。
不可变值示例定义

可以在任何时间线上使用。

内容时间线示例定义

仅可在内容时间线上使用。

设备时间线示例定义

仅可在设备时间线上使用。

队列时间线示例定义

仅可在队列时间线上使用。

内容时间线上执行的步骤看起来像这样。

不可变值示例定义. 内容时间线示例定义.

设备时间线上执行的步骤看起来像这样。

不可变值示例定义. 设备时间线示例定义.

队列时间线上执行的步骤看起来像这样。

不可变值示例定义. 队列时间线示例定义.

在本规范中,当结果值取决于除 内容时间线 以外的任何时间轴上的工作时,将使用异步操作。它们在JavaScript中由回调和promises表示。

GPUComputePassEncoder.dispatchWorkgroups():
  1. 用户通过调用GPUComputePassEncoder的方法在内容时间线上编码 dispatchWorkgroups 命令。

  2. 用户执行GPUQueue.submit()GPUCommandBuffer 交给用户代理,用户代理在设备时间线上通过调用操作系统驱动程序进行底层提交来处理它。

  3. 提交由GPU调用调度器分派到实际计算单元上执行,发生在队列时间线上。

GPUDevice.createBuffer():
  1. 用户填写GPUBufferDescriptor,并用它创建一个GPUBuffer,发生在内容时间线上。

  2. 用户代理在设备时间线上创建一个低级别的缓冲区。

GPUBuffer.mapAsync():
  1. 用户在内容时间线上请求映射一个GPUBuffer,并获得一个 promise 作为返回。

  2. 用户代理检查缓冲区是否当前被GPU使用,并提醒自己在使用结束后检查。

  3. 队列时间线上的GPU完成使用缓冲区后,用户代理将其映射到内存并解析 promise。

3.4.2. 内存模型

本节为非规范性内容。

在应用程序初始化例程中获取到 GPUDevice 后,我们可以将 WebGPU平台 描述为包含以下层:

  1. 实现此规范的用户代理。

  2. 具有此设备的低级本地API驱动程序的操作系统。

  3. 实际的CPU和GPU硬件。

WebGPU平台的每一层可能有不同的内存类型,用户代理在实现规范时需要考虑:

脚本拥有的内存,例如由脚本创建的 ArrayBuffer,通常无法被GPU驱动程序访问。 用户代理可能有不同的进程负责运行内容和与GPU驱动程序通信。在这种情况下,它将使用进程间共享内存来传输数据。 独立GPU有高带宽的自己的内存,而集成GPU通常与系统共享内存。

大多数 物理资源 分配在类型为内存的GPU计算或渲染的效率上。当用户需要向GPU提供新数据时,数据可能首先需要跨越进程边界以到达与GPU驱动程序通信的用户代理部分。然后,可能需要使驱动程序可见,这有时需要将数据复制到驱动程序分配的寄存器内存中。最后,可能需要将其传输到专用GPU内存,可能将内部布局更改为GPU操作最有效的布局。

所有这些转换都由用户代理的WebGPU实现完成。

注:此示例描述了最坏情况,而实际上实现可能不需要跨越进程边界,或者可能能够将驱动程序管理的内存直接暴露给用户在 ArrayBuffer 后面,从而避免任何数据副本。

3.4.3. 资源用途

一个物理资源可以在GPU上使用内部用途

input

用于绘制或调度调用的输入数据缓冲区。保持内容不变。 允许使用缓冲区 INDEX,缓冲区 VERTEX 或 缓冲区 INDIRECT

constant

从着色器角度来看,资源绑定是不变的。保持内容不变。 允许使用缓冲区 UNIFORM 或纹理 TEXTURE_BINDING

storage

可写存储资源绑定。 允许使用缓冲区 STORAGE 或纹理 STORAGE_BINDING

storage-read

只读存储资源绑定。保持内容不变。 允许使用缓冲区 STORAGE

attachment

在渲染通道中用作输出附件的纹理。 允许使用纹理 RENDER_ATTACHMENT

attachment-read

在渲染通道中用作只读附件的纹理。 保持内容不变。 允许使用纹理 RENDER_ATTACHMENT

我们定义子资源可以是整个缓冲区,也可以是一个纹理子资源

一些 内部用途 是可以互相兼容的。 一个 子资源 可以处于将多个用途合并在一起的状态。 我们认为列表 U 是一个兼容用途列表,如果(且仅当)它满足以下任意规则:

规定用途只能组合成兼容用途列表允许API在处理内存时限制数据竞争发生的时间。具有这种性质的应用程序在针对WebGPU编写时,更有可能在不同平台上无需修改即可运行。

通常,当实现处理一个使用 子资源的操作,以与其当前用途允许的不同方式时,它会调度资源转换到新状态。在某些情况下,例如在打开的 GPURenderPassEncoder 内,由于硬件限制,此类转换是不可能的。我们将这些地方定义为 用途范围

主要用途规则 是,对于任何一个 子资源,在一个 用途范围 中的 内部用途 列表必须是一个 兼容用途列表

例如,在同一个 GPURenderPassEncoder 中将同一个缓冲区绑定为 存储输入,会使编码器以及拥有的 GPUCommandEncoder 进入错误状态。这种用途组合不能构成一个 兼容用途列表

注:在单个 用途范围 内多个可写存储缓冲区/纹理的使用之间的竞争条件是允许的。

提供给 GPURenderPassColorAttachment.viewGPURenderPassColorAttachment.resolveTarget 的纹理的 子资源 被视为在此渲染通道的 用途范围 内作为 附件 使用。

3.4.4. 同步

对于 物理资源 的每一个 子资源,其一组 内部使用 标志在 队列时间线 上进行跟踪。

队列时间线 上,有一个有序的 用途范围 序列。 在每个范围的持续时间内,任何给定的 子资源 的一组 内部用途 标志是恒定的。 在 用途范围 之间的边界处, 子资源 可能会过渡到新的用途。

本规范定义了以下 用途范围

问题:上面应该大概讲的是GPU命令。但是我们没有办法参考特定的 GPU 命令(如分派)。

注: 上述规则意味着以下示例资源使用**包含在 usage scope validation 中:

在命令编码期间,子资源的每次使用都记录在命令缓冲区的 usage scopes 之一中。 对于每个 适用范围,实现执行 适用范围验证 通过组成 [=usage scope 中使用的每个 subresource 的所有 internal usage 标志的列表 =]. 如果这些列表中的任何一个不是兼容用法列表GPUCommandEncoder.finish()生成验证错误

3.5. 核心内部对象

3.5.1. Adapters

适配器 标识系统上 WebGPU 的实现: 既是浏览器底层平台上计算/渲染功能的实例,又是浏览器在该功能之上实现 WebGPU 的实例。

适配器 不唯一表示底层实现: 多次调用 requestAdapter() 每次都会返回不同的 adapter 对象。

每个 适配器 对象只能用于创建一个 device: 在 requestDevice() 成功后,适配器变为 无效。 此外,适配器 对象可以随时 过期

注: 这确保应用程序在创建设备时使用最新的系统状态来选择适配器。 它还通过使它们看起来相似来鼓励对更多场景的鲁棒性:首次初始化、由于拔下适配器而重新初始化、由于测试 GPUDevice.destroy() 调用而重新初始化等。

如果 适配器 具有重要的性能警告以换取更广泛的兼容性、更可预测的行为或改进的隐私的某种组合,则它可以被视为 后备适配器fallback adapter 不需要在每个系统上都可用。

adapter 具有以下内部插槽:

[[features]],类型为 ordered set<GPUFeatureName>,只读

features 可用于在此适配器上创建设备。

[[limits]],类型为 supported limits,只读

可用于在此适配器上创建设备的 best 限制。

每个适配器限制必须与 supported limits 中的默认值相同或 better

[[fallback]],布尔类型

如果设置为“true”,则表示该适配器是 fallback adapter

[[unmaskedIdentifiers]],类型为 ordered set<DOMString>

用户代理已选择为此适配器报告的 GPUAdapterInfo 字段的名称列表。 最初填充了用户代理在未经用户同意的情况下选择报告的任何 GPUAdapterInfo 字段的名称。

Adapters 通过 GPUAdapter 暴露出来。

3.5.2. 设备

device适配器 的逻辑实例,通过它创建 内部对象。 它可以在多个 代理 之间共享(e.g. dedicated workers)。

设备 是从它创建的所有 内部对象 的独占所有者: 当 设备 变为 无效(是 lostdestroyed)时,它和在其上创建的所有对象(直接,例如 createTexture(),或间接地,例如 createView()) 隐式变为 unusable

device 具有以下内部插槽:

[[适配器]],类型为 适配器,只读

创建此设备的 适配器

[[特性]],类型为 ordered set<GPUFeatureName>,只读

可在此设备上使用的 特性。 不能使用附加功能,即使底层 适配器 可以支持它们。

[[限制]],类型为 支持的限制,只读

可在此设备上使用的限制。 不能使用 更好 限制,即使基础 适配器 可以支持它们。

<分区算法> 当一个新设备 deviceadapter adapter 创建 使用 GPUDeviceDescriptor 描述符:

每当用户代理需要撤销对设备的访问权限时,它都会在设备的 device timeline 上调用 lose the device(device, "unknown"),可能会提前 当前在该时间线上排队的其他操作。

如果操作失败并产生副作用,这些副作用会明显改变设备上对象的状态或可能破坏内部实现/驱动程序状态,则设备应该丢失以防止这些更改被观察到。

丢失设备设备原因):
  1. 制作设备 无效

  2. gpuDevice 是对应于 device内容时间线 GPUDevice

    问题:更严格地定义它。

  3. gpuDevice内容时间线 上执行以下步骤:

    1. 使用新的 GPUDeviceLostInfo 解决 device.lost 并将 reason 设置为 reasonmessage 设置为实现定义的值。

    注:message 不应该泄露不必要的用户/系统信息,也不应该被应用程序解析。

  4. 完成任何未完成的 mapAsync() 步骤。

  5. 完成任何未完成的 onSubmittedWorkDone() 步骤。

注:设备丢失后不会产生错误。 参见 § 22 错误 & 调试

Devices 通过 GPUDevice 暴露出来。

3.6. 可选功能

WebGPU 适配器设备 具有 功能,它描述了 WebGPU 功能在不同实现之间的差异,通常是由于硬件或系统软件限制。 功能特性限制

用户代理不得透露超过 32 个可区分的配置或桶。

适配器 的能力必须符合 § 4.2.1 Adapter Capability Guarantees

requestDevice() 中只能请求支持的功能; 请求不受支持的功能会导致失败。

设备 的功能正是在 requestDevice() 中请求的功能。 无论 适配器 的功能如何,这些功能都会被强制执行。

有关隐私方面的考虑,请参阅 § 2.2.1 特定于机器的功能和限制

3.6.1. 特性

特性 是一组可选的 WebGPU 功能,并非所有实现都支持这些功能,通常是由于硬件或系统软件限制。

只有在创建设备时请求了该功能(在 requiredFeatures 中)时,才能使用作为功能一部分的功能。 否则,以新方式使用现有 API 表面通常会导致 validation error,而使用 optional API surfaces 会导致以下结果:

<分区算法> GPUFeatureName feature 启用 GPUObjectBase object 当且仅当 object.[[device]].[[features]] 包含 feature

请参阅 Feature Index 了解每个功能启用的功能的描述。

3.6.2. 限制

每个 限制 都是对设备上 WebGPU 使用的数字限制。

每个限制都有一个 默认 值。 每个 适配器 都保证支持默认值或 更好。 如果未在 requiredLimits 中明确指定值,则使用默认值。

一个极限值可能比另一个更好更好 限制值总是放宽验证,使更多的程序严格有效。 对于每个 限制类,定义了“更好”。

不同的极限有不同的极限等级

最大

该限制对传递到 API 中的某些值强制执行最大值。

更高的值是 更好

只能设置为值 ≥ 默认。 较低的值被限制在 默认

对齐方式

该限制对传递给 API 的某些值强制执行最小对齐; 那是, 该值必须是限制的倍数。

较低的值是 更好

只能设置为 2 的幂,即 ≤ 默认。 不是 2 的幂的值无效。 2 的更高次幂被限制在 默认

注: 设置“更好”的限制可能不一定是可取的,因为它们可能会对性能产生影响。 因此,为了提高跨设备和实现的可移植性,应用程序通常应该请求适用于其内容的“最差”限制(理想情况下,默认值)。

支持的限制 对象对 WebGPU 定义的每个限制都有一个值:

限制名称 Type Limit class Default
maxTextureDimension1D GPUSize32 maximum 8192
使用 dimension "1d" 创建的 texturesize.width 的最大允许值。
maxTextureDimension2D GPUSize32 maximum 8192
size.widthdimension "2d" 创建的 纹理size.height 所允许的最大值。
maxTextureDimension3D GPUSize32 maximum 2048
使用dimension "3d" 创建的 纹理size.width, size.heightsize.depthOrArrayLayers 最大值
maxTextureArrayLayers GPUSize32 maximum 256
通过 dimension "1d""2d" 创建的纹理,所允许的 size.depthOrArrayLayers 的最大值。
maxBindGroups GPUSize32 maximum 4
创建 GPUPipelineLayout 时,bindGroupLayouts 中允许的 GPUBindGroupLayouts 最大值。
maxBindGroupsPlusVertexBuffers GPUSize32 maximum 24
同时使用的绑定组和顶点缓冲区槽的最大数量,计算最高索引以下的任何空槽。 在 createRenderPipeline()in draw calls 中验证。
maxBindingsPerBindGroup GPUSize32 maximum 1000
创建 GPUBindGroupLayout 时可用的绑定索引数。

注:这个限制是规范的,但是是任意的。 默认情况下 binding slot limits,不可能在一个绑定组中使用 1000 个绑定,但这允许 GPUBindGroupLayoutEntry.binding 值高达 999。 此限制允许实现在合理的内存空间内将绑定空间视为数组,而不是稀疏映射结构。

maxDynamicUniformBuffersPerPipelineLayout GPUSize32 maximum 8
GPUPipelineLayout 中的最大 GPUBindGroupLayoutEntry 条目数,这些条目是具有动态偏移量的统一缓冲区。 请参阅 超出绑定插槽限制
maxDynamicStorageBuffersPerPipelineLayout GPUSize32 maximum 4
GPUPipelineLayout 中的最大 GPUBindGroupLayoutEntry 条目数,这些条目是具有动态偏移量的存储缓冲区。 请参阅 超出绑定插槽限制
maxSampledTexturesPerShaderStage GPUSize32 maximum 16
对于每个可能的 GPUShaderStage stage,作为采样纹理的 GPUPipelineLayout 中的最大 GPUBindGroupLayoutEntry 条目数。 请参阅 超出绑定插槽限制
maxSamplersPerShaderStage GPUSize32 maximum 16
对于每个可能的 GPUShaderStage stageGPUPipelineLayout 中作为采样器的 GPUBindGroupLayoutEntry 条目的最大数量。 请参阅 超出绑定插槽限制
maxStorageBuffersPerShaderStage GPUSize32 maximum 8
对于每个可能的 GPUShaderStage stageGPUPipelineLayout 中作为存储缓冲区的 GPUBindGroupLayoutEntry 条目的最大数量。 请参阅 超出绑定插槽限制
maxStorageTexturesPerShaderStage GPUSize32 maximum 4
对于每个可能的 GPUShaderStage stageGPUPipelineLayout 中作为存储纹理的 GPUBindGroupLayoutEntry 条目的最大数量。 请参阅 超出绑定插槽限制
maxUniformBuffersPerShaderStage GPUSize32 maximum 12
对于每个可能的 GPUShaderStage stage,作为统一缓冲区的 GPUPipelineLayout 中的最大 GPUBindGroupLayoutEntry 条目数。 请参阅 超出绑定插槽限制
maxUniformBufferBindingSize GPUSize64 maximum 65536 bytes
GPUBindGroupLayoutEntry entry 绑定的最大 GPUBufferBinding.size entry.buffer?.type"uniform"
maxStorageBufferBindingSize GPUSize64 maximum 134217728 bytes (128 MiB)
GPUBindGroupLayoutEntry entry 绑定的最大 GPUBufferBinding.size entry.buffer?.type"storage""read-only-storage"
minUniformBufferOffsetAlignment GPUSize32 alignment 256 bytes
GPUBufferBinding 所需的对齐方式。offsetsetBindGroup() 中提供的动态偏移量,用于与 GPUBindGroupLayoutEntry entry 的绑定 entry.buffer?.type"uniform"
minStorageBufferOffsetAlignment GPUSize32 alignment 256 bytes
GPUBufferBinding 所需的对齐方式。offsetsetBindGroup() 中提供的动态偏移量,用于与 GPUBindGroupLayoutEntry entry 的绑定 entry.buffer?.type"storage""read-only-storage"
maxVertexBuffers GPUSize32 maximum 8
创建 GPURenderPipelinebuffers 的最大数量。
maxBufferSize GPUSize64 maximum 268435456 bytes (256 MiB)
创建 GPUBuffersize 的最大大小。
maxVertexAttributes GPUSize32 maximum 16
创建 GPURenderPipeline 时,跨越 buffersattributes 总数的最大数量。
maxVertexBufferArrayStride GPUSize32 maximum 2048 bytes
创建 GPURenderPipeline 时允许的最大值 arrayStride
maxInterStageShaderComponents GPUSize32 maximum 60
阶段间通信(如顶点输出或片段输入)的输入或输出变量组件的最大允许数量。
maxInterStageShaderVariables GPUSize32 maximum 16
阶段间通信(如顶点输出或片段输入)的最大允许输入或输出变量数。
maxColorAttachments GPUSize32 maximum 8
GPURenderPipelineDescriptor.fragment.targetsGPURenderPassDescriptor.colorAttachmentsGPURenderPassLayout.colorFormats 中允许的最大颜色附件数。
maxColorAttachmentBytesPerSample GPUSize32 maximum 32
在所有颜色附件中保存渲染管线输出数据的一个样本(像素或子像素)所需的最大字节数。
maxComputeWorkgroupStorageSize GPUSize32 maximum 16384 bytes
用于计算阶段 GPUShaderModule 入口点的 workgroup 存储的最大字节数。
maxComputeInvocationsPerWorkgroup GPUSize32 maximum 256
计算阶段 GPUShaderModule 入口点的“workgroup_size”维度乘积的最大值。
maxComputeWorkgroupSizeX GPUSize32 maximum 256
计算阶段 GPUShaderModule 入口点的 workgroup_size X 维度的最大值。
maxComputeWorkgroupSizeY GPUSize32 maximum 256
计算阶段 GPUShaderModule 入口点的“workgroup_size”Y 维度的最大值。
maxComputeWorkgroupSizeZ GPUSize32 maximum 64
计算阶段 GPUShaderModule 入口点的“workgroup_size”Z 维度的最大值。
maxComputeWorkgroupsPerDimension GPUSize32 maximum 65535
dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ) 参数的最大值。
3.6.2.1. GPUSupportedLimits

GPUSupportedLimits 公开适配器或设备支持的 limits。 请参见 GPUAdapter.limitsGPUDevice.limits

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSupportedLimits {
    readonly attribute unsigned long maxTextureDimension1D;
    readonly attribute unsigned long maxTextureDimension2D;
    readonly attribute unsigned long maxTextureDimension3D;
    readonly attribute unsigned long maxTextureArrayLayers;
    readonly attribute unsigned long maxBindGroups;
    readonly attribute unsigned long maxBindGroupsPlusVertexBuffers;
    readonly attribute unsigned long maxBindingsPerBindGroup;
    readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout;
    readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout;
    readonly attribute unsigned long maxSampledTexturesPerShaderStage;
    readonly attribute unsigned long maxSamplersPerShaderStage;
    readonly attribute unsigned long maxStorageBuffersPerShaderStage;
    readonly attribute unsigned long maxStorageTexturesPerShaderStage;
    readonly attribute unsigned long maxUniformBuffersPerShaderStage;
    readonly attribute unsigned long long maxUniformBufferBindingSize;
    readonly attribute unsigned long long maxStorageBufferBindingSize;
    readonly attribute unsigned long minUniformBufferOffsetAlignment;
    readonly attribute unsigned long minStorageBufferOffsetAlignment;
    readonly attribute unsigned long maxVertexBuffers;
    readonly attribute unsigned long long maxBufferSize;
    readonly attribute unsigned long maxVertexAttributes;
    readonly attribute unsigned long maxVertexBufferArrayStride;
    readonly attribute unsigned long maxInterStageShaderComponents;
    readonly attribute unsigned long maxInterStageShaderVariables;
    readonly attribute unsigned long maxColorAttachments;
    readonly attribute unsigned long maxColorAttachmentBytesPerSample;
    readonly attribute unsigned long maxComputeWorkgroupStorageSize;
    readonly attribute unsigned long maxComputeInvocationsPerWorkgroup;
    readonly attribute unsigned long maxComputeWorkgroupSizeX;
    readonly attribute unsigned long maxComputeWorkgroupSizeY;
    readonly attribute unsigned long maxComputeWorkgroupSizeZ;
    readonly attribute unsigned long maxComputeWorkgroupsPerDimension;
};
3.6.2.2. GPUSupportedFeatures

GPUSupportedFeatures 是一个 setlike 界面。 它的 set entries 是适配器或设备支持的 featuresGPUFeatureName 值。 它必须只包含来自 GPUFeatureName 枚举的字符串。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSupportedFeatures {
    readonly setlike<DOMString>;
};
注: GPUSupportedFeatures set entries 的类型是 DOMString 以允许用户代理优雅地处理有效的 GPUFeatureNames,这些是在规范的后续修订中添加的,但用户代理尚未更新以识别。 如果 set entries 类型是 GPUFeatureName,则以下代码将抛出 TypeError 而不是报告 false
Check for support of an unrecognized feature:
if (adapter.features.has('unknown-feature')) {
    // Use unknown-feature
} else {
    console.warn('unknown-feature is not supported by this adapter.');
}
3.6.2.3. WGSLLanguageFeatures

WGSLLanguageFeatures 是一个 setlike 接口。 它的 set entries 是 WGSL language extensions 支持所有适配器的字符串名称。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface WGSLLanguageFeatures {
    readonly setlike<DOMString>;
};
3.6.2.4. GPUAdapterInfo

GPUAdapterInfo 公开有关适配器的各种标识信息。

GPUAdapterInfo 中的任何成员都不能保证被填充。用户代理可以自行决定显示哪些值,并且在某些设备上很可能不会填充任何值。因此,应用程序必须能够处理任何可能的 GPUAdapterInfo 值,包括这些值的缺失。

There is a tracking vector here. 有关隐私方面的考虑,请参阅 § 2.2.6 适配器标识符

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUAdapterInfo {
    readonly attribute DOMString vendor;
    readonly attribute DOMString architecture;
    readonly attribute DOMString device;
    readonly attribute DOMString description;
};

GPUAdapterInfo 具有以下属性:

vendor, of type DOMString, readonly

适配器 供应商的名称,如果可用的话。否则为空字符串。

architecture, of type DOMString, readonly

适配器 所属的 GPU 系列或类别的名称(如果可用)。否则为空字符串。

device, of type DOMString, readonly

适配器 的供应商特定标识符(如果可用)。否则为空字符串。

注:这是一个表示适配器类型的值。 例如,它可能是 PCI 设备 ID。 它不像序列号那样唯一地标识给定的硬件。

description, of type DOMString, readonly

描述驱动程序报告的 适配器 的人类可读字符串(如果可用)。 否则为空字符串。

注:因为没有格式应用于 description ,所以不建议尝试解析此值。 根据 GPUAdapterInfo 更改其行为的应用程序,例如对已知驱动程序问题应用解决方法,应尽可能依赖其他字段。

要为给定的 适配器 adapter 创建一个 new adapter info,运行 以下步骤:
  1. adapterInfo 成为一个新的 GPUAdapterInfo

  2. unmaskedValues 成为 adapter.[[unmaskedIdentifiers]]

  3. 如果 unmaskedValues contains "vendor" 并且供应商是已知的:

    1. adapterInfo.vendor 设置为 adapter 供应商的名称作为 规范化标识符字符串

    否则:

    1. adapterInfo.vendor 设置为空字符串或供应商的合理近似值 标准化标识符字符串

  4. 如果 unmaskedValues contains "architecture" 架构是已知的:

    1. adapterInfo.architecture 设置为 normalized identifier string 表示 adapter 所属的适配器系列或类别 属于。

    否则:

    1. adapterInfo.architecture 设置为空字符串或架构的合理近似值 规范化标识符字符串

  5. 如果 unmaskedValues contains "device" 并且设备是已知的:

    1. adapterInfo.device 设置为 normalized identifier string 表示 adapter 的供应商特定标识符。

    否则:

    1. adapterInfo.device 设置为空字符串或供应商特定标识符的合理近似值 标准化标识符字符串

  6. 如果 unmaskedValues contains "description" 并且描述是已知的:

    1. adapterInfo.description 设置为 adapter 的描述 据司机报告。

    否则:

    1. adapterInfo.description 设置为空字符串或描述的合理近似值。

  7. 返回 adapterInfo

normalized identifier string遵循以下模式:

[a-z0-9]+(-[a-z0-9]+)*

有效的规范化标识符字符串的示例包括:
  • gpu

  • 3d

  • 0x3b2f

  • next-gen

  • series-x20-ultra

3.7. 扩展文档

“扩展文档”是描述新功能的附加文档,这些新功能是非规范的并且不是 WebGPU/WGSL 规范的一部分。 它们描述了基于这些规范构建的功能,通常包括一个或多个新的 API 特性 标志和/或 WGSL enable 指令,或与其他网络规范草案的交互。

WebGPU 实现不得暴露扩展功能; 这样做是违反规范的。 在将新功能集成到 WebGPU 规范(本文档)和/或 WGSL 规范中之前,它不会成为 WebGPU 标准的一部分。

3.8. 源限制

WebGPU 允许访问存储在图像、视频和画布中的图像数据。 跨域媒体的使用受到限制,因为着色器可用于间接推断已上传到 GPU 的纹理内容。

WebGPU 不允许上传is not origin-clean 的图像源。

这也意味着使用 WebGPU 渲染的画布的 origin-clean 标志永远不会设置为“false”。

有关为图像和视频元素发出 CORS 请求的更多信息,请参阅:

3.9. 任务源

3.9.1. WebGPU 任务源

WebGPU 定义了一个新的 任务源 称为 WebGPU 任务源。 它用于 uncapturederror 事件和 GPUDevice.lost

GPUDevice device 排队全局任务, 通过一系列步骤步骤:
  1. Queue a global taskWebGPU task source 上,使用用于创建 device 的全局对象,以及步骤 steps

3.9.2. 自动过期任务源

WebGPU 定义了一个名为 自动过期任务源 的新 任务源。 它用于某些对象的自动、定时到期(销毁):

使用 GPUDevice device 排队自动到期任务 和一系列步骤步骤
  1. 自动过期任务源进队一个全局任务,使用用于创建 device 的全局对象,以及步骤 steps

来自自动过期任务源的任务应该被高优先级处理; 特别是,一旦排队,它们应该在用户定义的(JavaScript)任务之前运行。

注: 这种行为更可预测,并且这种严格性有助于开发人员通过急切地检测可能难以检测的关于隐式生命周期的错误假设来编写更具可移植性的应用程序。 仍然强烈鼓励开发人员在多个实现中进行测试。

实施注: 通过在 event loop processing model 内的固定点插入额外步骤而不是运行实际任务来实现高优先级到期“任务”是有效的。

3.10. 颜色空间和编码

WebGPU 不提供颜色管理。 WebGPU 中的所有值(例如纹理元素)都是原始数值,而不是颜色管理颜色值。

WebGPU 确实与颜色管理输出(通过 GPUCanvasConfiguration)和输入(通过 copyExternalImageToTexture()importExternalTexture())进行交互。 因此,必须在 WebGPU 数值和外部颜色值之间进行颜色转换。 每个此类接口点都在本地定义一种编码(颜色空间、传递函数和 alpha 预乘),WebGPU 数值将在其中进行解释。

WebGPU 允许 PredefinedColorSpace 枚举中的所有颜色空间。 请注意,每个颜色空间都定义在扩展范围内,如引用的 CSS 定义所定义,以表示其空间之外的颜色值(色度和亮度)。

问题(gpuweb/gpuweb#1715): 考虑将 srgb 编码图像上传到线性编码纹理的路径。

色域外预乘 RGBA 值 是任何 R/G/B 通道值超过 alpha 通道值的值。 例如,预乘 sRGB RGBA 值 [1.0, 0, 0, 0.5] 表示具有 50% alpha 的(未预乘)颜色 [2, 0, 0],在 CSS 中写为 rgb(srgb 2 0 0 / 50%) . 就像 sRGB 色域之外的任何颜色值一样,这是扩展颜色空间中定义明确的点(除非 alpha 为 0,在这种情况下没有颜色)。 但是,当这些值输出到可见画布时,结果是不确定的(参见 GPUCanvasAlphaMode "premultiplied")。

3.10.1. Color Space Conversions

根据上面的定义,通过将颜色在一个空间中的表示转换为另一个空间中的表示,颜色在空间之间进行转换。

如果源值的 RGBA 通道少于 4 个,则在转换颜色空间/编码和 alpha 预乘之前,将缺少的绿色/蓝色/alpha 通道分别设置为“0、0、1”。 转换后,如果目标需要少于 4 个通道,则忽略额外的通道。

注: 灰度图像通常在其颜色空间中表示 RGB 值“(V, V, V)”或 RGBA 值“(V, V, V, A)”。

颜色在转换期间不会被有损限制:如果源颜色值在目标颜色空间的色域范围之外,则从一个颜色空间转换到另一个颜色空间将导致值超出 [0, 1] 范围。 例如,对于 sRGB 目标,如果源是 rgba16float,在更宽的色彩空间(如 Display-P3)中,或者预乘并包含 [=色域外预乘 RGBA 值|色域外值 = ].

类似地,如果源值具有高位深度(例如 PNG,每个组件 16 位)或扩展范围(例如,具有“float16”存储的画布),这些颜色将通过颜色空间转换保留,中间计算至少具有精度 的来源。

3.10.2. 色彩空间转换省略

如果颜色空间/编码转换的源和目标相同,则不需要转换。 一般来说,如果转换的任何给定步骤是恒等函数(无操作),实现应该将其省略,以提高性能。

为了获得最佳性能,应用程序应该设置它们的颜色空间和编码选项,以便在整个过程中最大限度地减少必要的转换次数。 对于 GPUImageCopyExternalImage 的各种图像源:

注:在依赖这些功能之前,检查浏览器实现是否支持这些功能。

3.11. 从 JavaScript 到 WGSL 的数字转换

WebGPU API 的几个部分(pipeline-overridable constants 和 render pass clear values)从 WebIDL(doublefloat)获取数值并将它们转换为 WGSL 值(booli32u32f32f16)。

转换 IDL 值 idlValue doublefloat 类型 到 WGSL 类型 T,可能抛出 TypeError

注:此 TypeError 是在 device timeline 中生成的,从未出现在 JavaScript 中。

1.断言 idlValue 是一个有限值,因为它不是 unrestricted doubleunrestricted float

1.使v! 转换 idlValueECMAScript value产生的 ECMAScript 数字。

  1. 如果T 是“布尔”

    返回 ! 转换 v一个 IDL value 的结果对应的 WGSL bool 值,boolean 类型。

    注: 在从 ECMAScript 值转换为 IDL doublefloat 值后调用此算法。如果原始 ECMAScript 值是非数字、非布尔值,如“[]”或“{}”,则 WGSL 的“bool”结果可能与 ECMAScript 值已直接转换为 IDL boolean

    如果Ti32

    返回?v 一个 IDL 值 的结果对应的WGSL i32值,[EnforceRange] long 类型。

    如果Tu32

    返回 ?v 一个 IDL 值 的结果对应的 WGSL u32 值,[EnforceRange] unsigned long 类型。

    如果Tf32

    返回?v 一个 IDL value 结果对应的WGSL f32值,float 类型。

    如果Tf16

    1.让wgslF32? 转换 v一个 IDL value 的结果对应的 WGSL f32 值,float 类型。 1.返回 f16(wgslF32)! 将 WGSL f32 值转换为 f16 的结果,如 WGSL 浮点转换 中所定义。

    注:只要该值在 f32 的范围内,就不会抛出错误,即使该值超出 f16 的范围。

转换 GPUColor color 到纹理格式的纹素值 format,可能会抛出一个TypeError

注:此 TypeError 是在 设备时间线 中生成的,从未出现在 JavaScript 中。

  1. 如果 format 的组件 (assert 它们都具有相同的类型)是:

    :浮点类型或规范化类型

    Tf32

    有符号整数类型

    Ti32

    无符号整数类型

    Tu32

1.让wgslColorvec4<T> 类型的 WGSL 值,其中 4 个分量是 color 的 RGBA 通道,每个 ? 转换为 to WGSL type |T |。

1.转换wgslColor格式化 使用与 § 23.3.7 输出合并 步骤相同的转换规则,并返回结果。

注: 对于非整数类型,值的确切选择是实现定义的。 对于规范化类型,值被限制在类型的范围内。

注: 换句话说,写入的值就好像是由 WGSL 着色器写入的,该着色器输出表示为 f32i32u32vec4 的值。

4. 初始化

GPU 对象分别通过 NavigatorWorkerNavigator 接口在 WindowDedicatedWorkerGlobalScope 上下文中可用,并通过 navigator.gpu 暴露出来:

interface mixin NavigatorGPU {
    [SameObject, SecureContext] readonly attribute GPU gpu;
};
Navigator includes NavigatorGPU;
WorkerNavigator includes NavigatorGPU;

4.2. GPU

GPU 是 WebGPU 的入口。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPU {
    Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {});
    GPUTextureFormat getPreferredCanvasFormat();
    [SameObject] readonly attribute WGSLLanguageFeatures wgslLanguageFeatures;
};

GPU 具有以下方法和属性:

requestAdapter(options)

从用户代理请求 适配器。 用户代理选择是否返回适配器,如果是,则根据提供的选项进行选择。

Called on: GPU this.

Arguments:

Arguments for the GPU.requestAdapter(options) method.
Parameter Type Nullable Optional Description
options GPURequestAdapterOptions 选择适配器的标准。

Returns: Promise<GPUAdapter?>

Content timeline steps:

1.让 contentTimeline 成为当前 内容时间线。 1.让 promise 成为 a new promise。 1.在 this设备时间线 上发出initialization steps

  1. 返回promise

Device timeline initialization steps:
  1. adapternull.

  2. 如果用户代理选择返回一个适配器,它应该:

    1. 设置 adapter 为一个 有效 适配器,根据 § 4.2.2 适配器选择 中的规则和 options 中的标准选择,遵守 § 4.2.1 Adapter Capability Guarantees

      适配器的 支持的限制 必须符合 § 3.6.2 限制 中定义的要求。

    2. 如果适配器 满足 fallback adapter 的标准,设置 adapter.[[fallback]] 为 true。

  3. 内容时间线 上发布后续步骤.

Content timeline steps:
  1. 如果 adapter 不为 null:

    1. 使用一个新的 GPUAdapter 去封装 adapter解析 promise

  2. 否则, 解析 promisenull.

getPreferredCanvasFormat()

返回用于在此系统上显示 8 位深度、标准动态范围内容的最佳 GPUTextureFormat。 只能返回 "rgba8unorm""bgra8unorm"

返回值可以作为 format 传递给 configure() 调用 GPUCanvasContext 以确保关联的画布能够有效地显示其内容。

注:未显示在屏幕上的画布可能会或可能不会从使用此格式中受益。

Called on: GPU this.

Returns: GPUTextureFormat

内容时间线 步骤:

  1. 返回 "rgba8unorm""bgra8unorm",具体取决于哪种格式最适合在此系统上显示 WebGPU 画布。

wgslLanguageFeatures, of type WGSLLanguageFeatures, readonly

受支持的 WGSL language extensions 的名称。 支持的语言扩展会自动启用。

适配器 可能 随时变为 无效 ("expire")。 在系统状态发生任何可能影响任何 requestAdapter() 调用结果的变化时,用户代理应该使所有先前返回的适配器过期。 例如:

注: 用户代理可能会经常选择 expire 适配器,即使没有系统状态更改(例如,创建适配器后的几秒或几分钟)。 这有助于混淆真实的系统状态变化,并让开发人员更加意识到在调用 requestDevice() 之前再次调用 requestAdapter() 总是必要的。 如果应用程序确实遇到这种情况,标准的设备丢失恢复处理应该允许它恢复。

请求一个没有提示的 GPUAdapter:
const gpuAdapter = await navigator.gpu.requestAdapter();

4.2.1. Adapter Capability Guarantees

requestAdapter() 返回的任何 GPUAdapter 必须提供以下保证:

4.2.2. 适配器选择

GPURequestAdapterOptions 向用户代理提供提示,指示哪种配置适合该应用程序。

dictionary GPURequestAdapterOptions {
    GPUPowerPreference powerPreference;
    boolean forceFallbackAdapter = false;
};
enum GPUPowerPreference {
    "low-power",
    "high-performance"
};

GPURequestAdapterOptions 拥有以下成员:

powerPreference, of type GPUPowerPreference

可选地提供提示,指示应从系统的可用适配器中选择 适配器 的类别。

此提示的值可能会影响选择哪个适配器,但不得影响是否返回适配器。

注: 此提示的主要用途是影响在多 GPU 系统中使用哪个 GPU。 例如,一些笔记本电脑具有低功耗集成 GPU 和高性能独立 GPU。 此提示还可能会影响所选 GPU 的电源配置,以匹配请求的电源首选项。

注: 根据具体的硬件配置,例如电池状态和连接的显示器或可拆卸的 GPU,用户代理可以选择不同的 适配器 给定相同的电源偏好。 通常,给定相同的硬件配置和状态以及“powerPreference”,用户代理很可能会选择相同的适配器。

它必须是以下值之一:

undefined (or not present)

没有对于用户代理的提示。

"low-power"

指将节能优先于性能的请求。

注: 通常,如果内容不太可能受到绘图性能的限制,则应该使用它; 例如,如果它每秒只渲染一帧,只使用简单的着色器绘制相对简单的几何图形,或者使用一个小的 HTML canvas 元素。 如果内容允许,鼓励开发人员使用此值,因为它可以显着延长便携式设备的电池寿命。

"high-performance"

指将性能优先于功耗的请求。

注: 通过选择这个值,开发人员应该意识到,对于在生成的适配器上创建的 设备,用户代理更有可能强制设备丢失,以便通过切换到低功率适配器来节省功率。 鼓励开发人员仅在他们认为绝对必要时才指定此值,因为它可能会显着缩短便携式设备的电池寿命。

forceFallbackAdapter, of type boolean, defaulting to false

当设置为 true 时,表示可能只返回 备用适配器。 如果用户代理不支持 备用适配器,将导致 requestAdapter() 解析为“null”。

注: 如果 forceFallbackAdapter 设置为“false”并且没有其他合适的 适配器 可用或用户代理选择,返回一个 备用适配器。 希望阻止其应用程序在 备用适配器 上运行的开发人员应在请求 GPUDevice 之前检查 GPUAdapter.isFallbackAdapter 属性。

请求一个 "high-performance" GPUAdapter:
const gpuAdapter = await navigator.gpu.requestAdapter({
    powerPreference: 'high-performance'
});

4.3. GPUAdapter

GPUAdapter 封装了一个 adapter,并描述了它的功能(特性限制)。

要获得 GPUAdapter,请使用 requestAdapter()

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUAdapter {
    [SameObject] readonly attribute GPUSupportedFeatures features;
    [SameObject] readonly attribute GPUSupportedLimits limits;
    readonly attribute boolean isFallbackAdapter;

    Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
    Promise<GPUAdapterInfo> requestAdapterInfo(optional sequence<DOMString> unmaskHints = []);
};

GPUAdapter 拥有以下属性:

features, of type GPUSupportedFeatures, readonly

this.[[adapter]].[[features]]中的值的集合。

limits, of type GPUSupportedLimits, readonly

this.[[adapter]].[[limits]]中的限制。

isFallbackAdapter, of type boolean, readonly

返回[[adapter]].[[fallback]]的值。

GPUAdapter has the following internal slots:

[[adapter]], 类型为 适配器, 只读

GPUAdapter 指向的 [= 适配器 =]。

GPUAdapter 拥有以下方法:

requestDevice(descriptor)

适配器 请求 设备

这是一次性操作:如果成功返回设备,则适配器变为 无效

Called on: GPUAdapter this.

Arguments:

Arguments for the GPUAdapter.requestDevice(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUDeviceDescriptor Description of the GPUDevice to request.

Returns: Promise<GPUDevice>

Content timeline steps:

  1. 使 contentTimeline 为当前 内容时间线.

  2. 使 promisea new promise.

  3. 使 adapterthis.[[adapter]].

  4. thisDevice timeline 发起 initialization steps.

  5. 返回 promise.

Device timeline initialization steps:
  1. 如果以下任何条件没有满足:

    然后在 contentTimeline 上执行以下步骤并返回:

    内容时间线 步骤:
    1. 以一个 TypeError 拒绝 promise

    注:这与浏览器根本不知道功能名称(在其 GPUFeatureName 定义中)时产生的错误相同。 这将浏览器不支持某项功能时的行为与特定适配器不支持某项功能时的行为融合在一起。

  2. 如果未满足以下任何要求:

    然后在 contentTimeline 上执行以下步骤并返回:

    Content timeline 步骤:
    1. 以一个 OperationError 拒绝 promise

    1.如果adapter 无效,否则用户代理无法满足请求:

    1.让device 成为一个新的 shebei1

    1. 丢失设备(device, "unknown")。

      注: 这使得 adapter invalid,如果还没有的话。

      注: 当这种情况发生时,用户代理应该考虑在大多数或所有情况下发出开发人员可见的警告。 应用程序应从 requestAdapter() 开始执行重新初始化逻辑。

    否则:

    1.让device 成为具有 descriptor 描述的功能的新设备。 1.制作adapter.[[adapter]] 无效

  3. contentTimeline 上发布后续步骤。

内容时间线 步骤:
  1. 使用新的 GPUDevice 对象 device 解析 promise

    注: 如果设备已经丢失,因为适配器无法满足请求,device.lost 已经在 promise 解析之前被解析。

requestAdapterInfo()

请求此 GPUAdapterGPUAdapterInfo

注:适配器信息值与 Promise 一起返回,使用户代理有机会在请求未屏蔽的值时执行可能长时间运行的检查,例如在返回前征求用户同意。 但是,如果未指定 unmaskHints,则不应向用户显示任何对话框。

Called on: GPUAdapter this.

Arguments:

Arguments for the GPUAdapter.requestAdapterInfo() method.
Parameter Type Nullable Optional Description
unmaskHints sequence<DOMString> GPUAdapterInfo 属性名称列表,如果可用,需要未屏蔽值。

Returns: Promise<GPUAdapterInfo>

内容时间线 步骤:

  1. 使 promisea new promise.

  2. 使 adapterthis.[[adapter]].

  3. 如果 thisrelevant global object 具有 transient activation,使 hasActivationtrue, 否则为 false

  4. in parallel 上运行以下步骤:

    1. 如果 unmaskHints.length > 0:

      1. 如果 hasActivationfalse 以一个 NotAllowedError 拒绝 promise 并停止此步骤。

      2. unmaskedKeysunmaskHints 中指定的用户代理决定取消屏蔽的字段的 list,如果有的话。

        注:用户代理可以自由使用它认为合适的任何方法来决定要取消屏蔽哪些字段。

      3. Append unmaskedKeysadapter.[[unmaskedIdentifiers]]

    2. 以一个 new adapter infoadapter 解析 promise

  5. 返回 promise.

请求具有默认特性和限制的 GPUDevice
const gpuAdapter = await navigator.gpu.requestAdapter();
const gpuDevice = await gpuAdapter.requestDevice();

4.3.1. GPUDeviceDescriptor

GPUDeviceDescriptor describes a device request.

dictionary GPUDeviceDescriptor
         : GPUObjectDescriptorBase {
    sequence<GPUFeatureName> requiredFeatures = [];
    record<DOMString, GPUSize64> requiredLimits = {};
    GPUQueueDescriptor defaultQueue = {};
};

GPUDeviceDescriptor has the following members:

requiredFeatures, of type sequence<GPUFeatureName>, defaulting to []

指定设备请求所需的 特性。 如果适配器不能提供这些功能,请求将失败。

在生成的设备上验证 API 调用时,将允许完全指定的一组功能,不多也不少。

requiredLimits, of type record<DOMString, GPUSize64>, defaulting to {}

指定设备请求所需的 限制。 如果适配器不能提供这些限制,请求将失败。

每个键必须是 支持的限制 成员的名称。 在生成的设备上验证 API 调用时,将允许完全指定的限制,并且不允许 更好 或更糟。

defaultQueue, of type GPUQueueDescriptor, defaulting to {}

默认 GPUQueue 的描述符。

Requesting a GPUDevice with the "texture-compression-astc" feature if supported:
const gpuAdapter = await navigator.gpu.requestAdapter();

const requiredFeatures = [];
if (gpuAdapter.features.has('texture-compression-astc')) {
    requiredFeatures.push('texture-compression-astc')
}

const gpuDevice = await gpuAdapter.requestDevice({
    requiredFeatures
});
4.3.1.1. GPUFeatureName

每个 GPUFeatureName 标识一组功能,如果可用,则允许 WebGPU 的其他使用,否则这些功能将无效。

enum GPUFeatureName {
    "depth-clip-control",
    "depth32float-stencil8",
    "texture-compression-bc",
    "texture-compression-etc2",
    "texture-compression-astc",
    "timestamp-query",
    "indirect-first-instance",
    "shader-f16",
    "rg11b10ufloat-renderable",
    "bgra8unorm-storage",
    "float32-filterable"
};

4.4. GPUDevice

GPUDevice 封装了 device 并公开了该设备的功能。

GPUDevice 是创建 WebGPU interfaces 的顶级接口。

要获取 GPUDevice,请使用 requestDevice()

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUDevice : EventTarget {
    [SameObject] readonly attribute GPUSupportedFeatures features;
    [SameObject] readonly attribute GPUSupportedLimits limits;

    [SameObject] readonly attribute GPUQueue queue;

    undefined destroy();

    GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
    GPUTexture createTexture(GPUTextureDescriptor descriptor);
    GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
    GPUExternalTexture importExternalTexture(GPUExternalTextureDescriptor descriptor);

    GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
    GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
    GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);

    GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
    GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
    GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
    Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
    Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor);

    GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
    GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);

    GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor);
};
GPUDevice includes GPUObjectBase;

GPUDevice has the following attributes:

features, of type GPUSupportedFeatures, readonly

包含设备支持的功能(即创建它的功能)的 GPUFeatureName 值的集合。

limits, of type GPUSupportedLimits, readonly

公开设备支持的限制(这正是创建它时所用的限制)。

queue, of type GPUQueue, readonly

此设备的主要 GPUQueue

GPUDevice[[device]]GPUDevice 引用的 设备

GPUDevice 具有上面的 WebIDL 定义中列出的方法。 此处未定义的那些在本文档的其他地方定义。

destroy()

销毁 设备,防止对其进行进一步操作。 未完成的异步操作将失败。

注:多次销毁设备有效。

Called on: GPUDevice this.

内容时间线 步骤:

  1. unmap() 所有此设备中的 GPUBuffer

  2. this设备时间线 上发起后续步骤。

设备时间线 步骤:
  1. 一旦所有当前在此设备上的任何队列上排队的操作完成后,在当前时间线上发出后续步骤。

  1. Lose the device(this.[[device]], "destroyed").

注:由于没有进一步的操作可以在此设备上排队,实现可以立即中止未完成的异步操作并释放资源分配,包括刚刚取消映射的映射内存。

A GPUDevice's allowed buffer usages are:
A GPUDevice's allowed texture usages are:

4.5. 示例

请求带有错误处理的 GPUAdapterGPUDevice 的更强大示例:
let gpuDevice = null;

async function initializeWebGPU() {
    // Check to ensure the user agent supports WebGPU.
    if (!('gpu' in navigator)) {
        console.error("User agent doesn’t support WebGPU.");
        return false;
    }

    // Request an adapter.
    const gpuAdapter = await navigator.gpu.requestAdapter();

    // requestAdapter may resolve with null if no suitable adapters are found.
    if (!gpuAdapter) {
        console.error('No WebGPU adapters found.');
        return false;
    }

    // Request a device.
    // Note that the promise will reject if invalid options are passed to the optional
    // dictionary. To avoid the promise rejecting always check any features and limits
    // against the adapters features and limits prior to calling requestDevice().
    gpuDevice = await gpuAdapter.requestDevice();

    // requestDevice will never return null, but if a valid device request can’t be
    // fulfilled for some reason it may resolve to a device which has already been lost.
    // Additionally, devices can be lost at any time after creation for a variety of reasons
    // (ie: browser resource management, driver updates), so it’s a good idea to always
    // handle lost devices gracefully.
    gpuDevice.lost.then((info) => {
        console.error(`WebGPU device was lost: ${info.message}`);

        gpuDevice = null;

        // Many causes for lost devices are transient, so applications should try getting a
        // new device once a previous one has been lost unless the loss was caused by the
        // application intentionally destroying the device. Note that any WebGPU resources
        // created with the previous device (buffers, textures, etc) will need to be
        // re-created with the new one.
        if (info.reason != 'destroyed') {
            initializeWebGPU();
        }
    });

    onWebGPUInitialized();

    return true;
}

function onWebGPUInitialized() {
    // Begin creating WebGPU resources here...
}

initializeWebGPU();

5. Buffers

5.1. GPUBuffer

GPUBuffer 表示可用于 GPU 操作的内存块。 数据以线性布局存储,这意味着分配的每个字节都可以通过它从 GPUBuffer 开始的偏移量来寻址,取决于操作的对齐限制。 一些 GPUBuffers 可以被映射,这使得内存块可以通过称为其映射的 ArrayBuffer 访问。

GPUBuffer 通过 createBuffer() 创建。 缓冲区可能是 mappedAtCreation

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBuffer {
    readonly attribute GPUSize64 size;
    readonly attribute GPUBufferUsageFlags usage;

    readonly attribute GPUBufferMapState mapState;

    Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
    ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
    undefined unmap();

    undefined destroy();
};
GPUBuffer includes GPUObjectBase;

enum GPUBufferMapState {
    "unmapped",
    "pending",
    "mapped"
};

GPUBuffer 拥有以下 不可变属性:

size, of type GPUSize64, readonly

GPUBuffer 分配的字节长度。

usage, of type GPUBufferUsageFlags, readonly

GPUBuffer 的允许用途。

[[internals]], 类型为 buffer internals, 只读, override

GPUBuffer 拥有以下 内容时间线属性:

mapState, of type GPUBufferMapState, readonly

缓冲区的当前 GPUBufferMapState

"unmapped"

缓冲区未映射供“this”使用。getMappedRange()

"pending"

已请求缓冲区的映射,但尚未完成。 它可能会在 mapAsync() 中验证成功或失败。

"mapped"

缓冲区已映射,并且可以使用 this.getMappedRange()

The getter steps are:

Content timeline 步骤:
  1. 如果 this.[[mapping]] 不是 null, 返回 "mapped"

  2. 如果 this.[[pending_map]] 不是 null, 返回 "pending"

  3. 返回"unmapped"

[[pending_map]], 类型为 Promise<void> 或 null, 初始为 null

当前未决的 mapAsync() 调用返回的 Promise

挂起的映射永远不会超过一个,因为如果请求已经在进行中,mapAsync() 将立即拒绝。

[[mapping]], 类型为 active buffer mappingnull, 初始为 null

当且仅当缓冲区当前被映射以供 getMappedRange() 使用时设置。 否则为空(即使有 [[pending_map]])。

active buffer mapping 是一个包含以下字段的结构:

data, of type Data Block

GPUBuffer 的映射。 此数据通过 ArrayBuffer 访问,ArrayBuffer 是此数据的视图,由 getMappedRange() 返回并存储在 views 中。

mode, of type GPUMapModeFlags

地图的 GPUMapModeFlags,在对 mapAsync()createBuffer() 的相应调用中指定。

range, of type tuple [unsigned long long, unsigned long long]

GPUBuffer 映射的范围。

views, of type list<ArrayBuffer>

ArrayBuffer 通过 getMappedRange() 返回给应用程序。 它们被跟踪,因此可以在调用 unmap() 时分离它们。

使用模式 mode 和范围 range初始化一个活动缓冲区映射
  1. sizerange[1] - range[0]。

  2. data? CreateByteDataBlock(size)。

    注: 这可能会导致抛出 RangeError。 为了一致性和可预测性:
    • 对于 new ArrayBuffer() 在给定时刻成功的任何大小,此分配应该在那一刻成功。

    • 对于 new ArrayBuffer() 确定性 抛出 RangeError 的任何大小,此分配也应该

  3. 返回一个 active buffer mapping 且:

GPUBufferinternal objectbuffer internals,它使用以下 device timeline slots 扩展 internal object

state

缓冲区的当前内部状态:

"available"

缓冲区可用于队列操作(除非它是 无效的)。

"unavailable"

由于被映射,缓冲区可能无法用于队列操作。

"destroyed"

由于被 destroy()ed,缓冲区可能无法用于任何操作。

Mapping and unmapping a buffer.
Failing to map a buffer.

5.1.1. GPUBufferDescriptor

dictionary GPUBufferDescriptor
         : GPUObjectDescriptorBase {
    required GPUSize64 size;
    required GPUBufferUsageFlags usage;
    boolean mappedAtCreation = false;
};

GPUBufferDescriptor 拥有以下成员:

size, of type GPUSize64

缓冲大小,以字节为单位。

usage, of type GPUBufferUsageFlags

缓冲的可用用途。

mappedAtCreation, of type boolean, defaulting to false

如果 true 以已映射状态创建缓冲区,则允许立即调用 getMappedRange()。 即使 usage 不包含 MAP_READMAP_WRITE,将 mappedAtCreation 设置为“true”也是有效的。 这可用于设置缓冲区的初始数据。

保证即使缓冲区创建最终失败,在取消映射之前,它仍然会显示为可以写入/读取映射范围。

5.1.2. 缓冲区使用

typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
[Exposed=(Window, DedicatedWorker), SecureContext]
namespace GPUBufferUsage {
    const GPUFlagsConstant MAP_READ      = 0x0001;
    const GPUFlagsConstant MAP_WRITE     = 0x0002;
    const GPUFlagsConstant COPY_SRC      = 0x0004;
    const GPUFlagsConstant COPY_DST      = 0x0008;
    const GPUFlagsConstant INDEX         = 0x0010;
    const GPUFlagsConstant VERTEX        = 0x0020;
    const GPUFlagsConstant UNIFORM       = 0x0040;
    const GPUFlagsConstant STORAGE       = 0x0080;
    const GPUFlagsConstant INDIRECT      = 0x0100;
    const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
};

GPUBufferUsage 标志决定了 GPUBuffer 在创建后如何使用:

MAP_READ

可以映射缓冲区以供读取。(示例:使用 GPUMapMode.READ 调用 mapAsync()

只能与 COPY_DST 结合使用。

MAP_WRITE

可以映射缓冲区以进行写入。(示例:使用 GPUMapMode.WRITE 调用 mapAsync()

只能与 COPY_SRC 结合使用。

COPY_SRC

缓冲区可以用作复制操作的源。(示例:作为 copyBufferToBuffer()copyBufferToTexture() 调用的 source 参数。)

COPY_DST

缓冲区可用作复制或写入操作的目标。(示例:作为 copyBufferToBuffer()copyTextureToBuffer() 调用的“目标”参数,或作为 writeBuffer() 调用的目标。)

INDEX

该缓冲区可用作索引缓冲区。(示例:传递给 setIndexBuffer()。)

VERTEX

该缓冲区可用作顶点缓冲区。(示例:传递给 setVertexBuffer()。)

UNIFORM

该缓冲区可以用作统一缓冲区。(示例:作为 GPUBufferBindingLayout 的绑定组条目,其 buffer.type"uniform"。)

STORAGE

缓冲区可以用作存储缓冲区。(示例:作为具有 bufferGPUBufferBindingLayout 的绑定组条目。type"storage""read- 仅存储“。)

INDIRECT

缓冲区可用于存储间接命令参数。(示例:作为 drawIndirect()dispatchWorkgroupsIndirect() 调用的 indirectBuffer 参数。)

QUERY_RESOLVE

缓冲区可用于捕获查询结果。(示例:作为 resolveQuerySet() 调用的“目标”参数。)

5.1.3. 缓冲区创建

createBuffer(descriptor)

创建一个 GPUBuffer.

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createBuffer(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUBufferDescriptor 要创建的 GPUBuffer 的描述。

Returns: GPUBuffer

Content timeline steps:

  1. 使 [b, bi] 为 ! create a new WebGPU object(this, GPUBuffer, descriptor).

  2. 设置 b.sizedescriptor.size.

  3. 设置 b.usagedescriptor.usage.

  4. 如果 descriptor.mappedAtCreationtrue:

    1. 设置 b.[[mapping]]? initialize an active buffer mapping,模式为 WRITE 范围为 [0, descriptor.size].

  5. this设备时间线上发起initialization steps

  6. 返回 b

Device timeline initialization steps:
  1. 如果以下任何条件没有满足, generate a validation error, 使 bi 无效, 并停止。

注:如果缓冲区创建失败,并且 descriptor.mappedAtCreation 为“false”,则对 mapAsync() 的任何调用都将被拒绝,因此分配用于启用映射的任何资源都可以而且可能是丢弃或回收。

  1. 如果 descriptor.mappedAtCreation 为“真”:

    1. bi.state 设置为“unavailable”。

    另外:

    1. bi.state 设置为“available”。

  2. bi 创建设备分配 每个字节都为零。

    如果分配失败且没有副作用,生成内存不足错误,生成 bi 无效,然后返回。

创建一个 128 字节的统一缓冲区,可以写入:
const buffer = gpuDevice.createBuffer({
    size: 128,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});

5.1.4. 缓冲区销毁

不再需要 GPUBuffer 的应用程序可以通过调用 destroy() 选择在垃圾收集之前失去对它的访问。 销毁缓冲区也会取消映射,释放为映射分配的所有内存。

注:这允许用户代理在所有先前使用它提交的操作完成后回收与 GPUBuffer 关联的 GPU 内存。

destroy()

销毁 GPUBuffer.

注:多次销毁缓冲区是有效的。

Called on: GPUBuffer this.

Returns: undefined

内容时间线 步骤:

  1. 调用 this.unmap().

  2. this.[[device]]Device timeline 上发布后续步骤。

Device timeline steps:
  1. this.[[internals]].state 设置为“destroyed”。

注:由于没有进一步的操作可以使用此缓冲区排队,因此实现可以释放资源分配,包括刚刚取消映射的映射内存。

5.2. 缓冲区映射

应用程序可以请求映射 GPUBuffer,以便它们可以通过代表 GPUBuffer 分配部分的 ArrayBuffer 访问其内容。异步请求映射 GPUBuffer mapAsync() 以便用户代理可以确保 GPU 在应用程序可以访问其内容之前完成使用 GPUBuffer。 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 取消映射,然后才能将使用它的工作提交到 队列时间线

一旦 GPUBuffer 被映射,应用程序可以使用 getMappedRange() 同步请求访问其内容范围。 返回的 ArrayBuffer 只能通过 unmap() detached(直接,或通过 GPUBuffer.destroy()GPUDevice.destroy()),并且不能是transferredTypeError 被试图这样做的任何其他操作抛出。

typedef [EnforceRange] unsigned long GPUMapModeFlags;
[Exposed=(Window, DedicatedWorker), SecureContext]
namespace GPUMapMode {
    const GPUFlagsConstant READ  = 0x0001;
    const GPUFlagsConstant WRITE = 0x0002;
};

GPUMapMode 标志确定调用 mapAsync() 时如何映射 GPUBuffer

READ

仅对使用 MAP_READ 用法创建的缓冲区有效。

映射缓冲区后,调用 getMappedRange() 将返回包含缓冲区当前值的 ArrayBuffer。 在调用 unmap() 后,将丢弃对返回的 ArrayBuffer 的更改。

WRITE

仅对使用 MAP_WRITE 用法创建的缓冲区有效。

映射缓冲区后,调用 getMappedRange() 将返回包含缓冲区当前值的 ArrayBuffer。 在调用 unmap() 后,对返回的 ArrayBuffer 的更改将存储在 GPUBuffer 中。

注:由于 MAP_WRITE 缓冲区使用只能与 COPY_SRC 缓冲区使用相结合,写入映射永远不会返回 GPU 生成的值,返回的 ArrayBuffer 将 仅包含默认初始化数据(零)或网页在先前映射期间写入的数据。

mapAsync(mode, offset, size)

映射 GPUBuffer 的给定范围,并在 GPUBuffer 的内容准备好使用 getMappedRange() 访问时解析返回的 Promise

返回的 Promise 的决议表示缓冲区已被映射。 它不保证 content timeline 可见的任何其他操作的完成,尤其不暗示从 onSubmittedWorkDone() 其他 {{GPUBuffer 上的 mapAsync()}} 已解决。

onSubmittedWorkDone() 返回的 Promise 的决议确实意味着在 GPUBuffer 上完成该调用之前进行的 mapAsync() 用于该队列的最后专门调用。

Called on: GPUBuffer this.

Arguments:

Arguments for the GPUBuffer.mapAsync(mode, offset, size) method.
Parameter Type Nullable Optional Description
mode GPUMapModeFlags 缓冲区是否应该映射为读取或写入。
offset GPUSize64 以字节为单位的偏移量到缓冲区到要映射的范围的开始。
size GPUSize64 要映射的范围的大小(以字节为单位)。

Returns: Promise<undefined>

内容时间线 步骤:

  1. contentTimeline 成为当前 内容时间线

  2. 如果 this.[[pending_map]] 不是 null

    1. 返回 a promise rejected with OperationError

  3. p 成为新的Promise

  4. this.[[pending_map]] 设置为 p

  5. 发布验证步骤thisDevice timeline 上。[[device]]

  6. 返回 p

设备时间线 validation steps:
  1. 如果 sizeundefined:

    1. 使 rangeSize 为 max(0, this.size - offset).

    否则:

    1. 使 rangeSizesize.

  2. 如果以下任何条件未满足:

    Then:

    1. contentTimeline 上发起 map failure steps

    2. Generate a validation error.

    3. 返回。

  3. 设置 this.[[internals]].state 为 "unavailable".

    注:由于缓冲区已映射,因此其内容不能在此完成和 unmap() 之间更改。

  4. 如果 this.[[device]] 丢失,或者当它 becomes lost 时:

    1. 内容时间线上运行地图失败步骤

    否则,在未指定的点:

    • 在完成当前使用this的排队操作后,

    • 并且不晚于 device timeline 收到 all currently-enqueued operations 完成通知后的下一个 device timeline 操作(无论它们是否 使用 这个),

    运行以下步骤:

    1. internalStateAtCompletionthis.[[internals]].state

      注:当且仅当此时缓冲区由于 unmap() 调用再次变为“available”,则 [[pending_map ]] != p 下面,所以映射将不会在下面的步骤中成功。

    2. dataForMappedRegionthis的内容 从偏移量 offset 开始,对于 rangeSize 字节。

    3. contentTimeline 上运行map success steps

内容时间线 map success steps:
  1. 如果 this.[[pending_map]] != p:

    注:映射已被 unmap() 取消。

    1. 断言 p 被拒绝。

    2. 返回。

  2. 断言 p 被挂起。

  3. 断言 internalStateAtCompletion 为 "unavailable".

  4. 使 mappinginitialize an active buffer mapping,模式为 mode 且范围为 [offset, offset + rangeSize].

    如果分配失败:

    1. 设置 this.[[pending_map]]null, 且以一个 RangeError reject p

    2. 返回。

  5. 设置 mapping.data 的内容为 dataForMappedRegion.

  6. 设置 this.[[mapping]]mapping.

  7. 设置 this.[[pending_map]]null,且 resolve p.

Content timeline map failure steps:
  1. 如果 this.[[pending_map]] != p:

    注:映射已被 unmap() 取消。

    1. 断言 p 已被拒绝。

    2. 返回。

  2. 断言 p 仍被挂起。

  3. 设置 this.[[pending_map]]null,并且以一个 OperationError 拒绝 p

getMappedRange(offset, size)

返回一个 ArrayBuffer,其中包含给定映射范围内 GPUBuffer 的内容。

Called on: GPUBuffer this.

Arguments:

Arguments for the GPUBuffer.getMappedRange(offset, size) method.
Parameter Type Nullable Optional Description
offset GPUSize64 缓冲区中以字节为单位的偏移量,以从中返回缓冲区内容。
size GPUSize64 要返回的 ArrayBuffer 的字节大小。

Returns: ArrayBuffer

内容时间线 步骤:

1.如果size 缺失:

  1. rangeSize 为最大值(0,this.size - offset)。

否则,让 rangeSizesize

  1. 如果不满足以下任何条件,则抛出 OperationError 并停止。

    注:获取 GPUBuffer 的映射范围始终有效,即 mappedAtCreation,即使它是 无效的,因为 内容时间线 可能不知道它是无效的。

  2. 使 datathis.[[mapping]].data.

  3. 使 view! create an ArrayBuffer,大小为 rangeSize, 但是它的指针在偏移处(offset - [[mapping]].range[0])可变地引用了 data 的内容。

    注:这里可能不会抛出 RangeError,因为 data 已经在 mapAsync()createBuffer() 期间分配。

  4. 设置 view.[[ArrayBufferDetachKey]] 为 "WebGPUBufferMapping".

    注:如果尝试 DetachArrayBuffer,这会导致抛出 TypeErrorunmap() 除外。

  5. Append viewthis.[[mapping]].views.

  6. 返回 view.

注:如果 getMappedRange() 在没有检查地图状态的情况下成功,用户代理应考虑发出开发人员可见的警告,方法是等待 mapAsync() 成功,查询 "mapped"mapState,或等待稍后的 onSubmittedWorkDone() 调用成功。

unmap()

取消 GPUBuffer 的映射范围并使其内容再次可供 GPU 使用。

Called on: GPUBuffer this.

Returns: undefined

内容时间线 步骤:

  1. 如果 this.[[pending_map]] 不为 null:

    1. 以一个 AbortError 拒绝 this.[[pending_map]]

    2. 设置 this.[[pending_map]]null.

  2. 如果 this.[[mapping]]null:

    1. 返回。

  3. 对每个 this.[[mapping]].views 中的 ArrayBuffer ab

    1. 执行 DetachArrayBuffer(ab, "WebGPUBufferMapping").

  4. 使 bufferUpdatenull.

  5. 如果 this.[[mapping]].mode 包含 WRITE:

    1. 设置 bufferUpdate 为 { data: this.[[mapping]].data, offset: this.[[mapping]].range[0] }.

    注:当缓冲区在没有 WRITE 模式的情况下映射,然后取消映射时,应用程序对映射范围 ArrayBuffer 所做的任何本地修改都将被丢弃,并且不会影响以后映射的内容。

  6. 设置 this.[[mapping]]null.

  7. this.[[device]]Device timeline 上运行后续步骤。

设备时间线 步骤:
  1. 如果this.[[device]]无效的,返回。

  2. 如果 bufferUpdate 不为 null

    1. this.[[device]].queue队列时间线 上执行以下步骤:

      队列时间线 步骤:

      1.更新this的内容 在偏移 bufferUpdate.offset 与数据 bufferUpdate.data 处。

  3. this.[[internals]].state 设置为 "available"。

6. 纹理与纹理视图

6.1. GPUTexture

问题:删除此定义:texture

一张纹理由一个或多个纹理子资源组成,每个纹理子资源由 mipmap level 唯一标识,并且仅对于 2d 纹理,array layer 和 [ =方面=]。

一个 texture subresource 是一个 subresource:每个都可以在一个 usage scope 中用于不同的 internal usages

mipmap level 中的每个子资源在每个空间维度上大约是较低级别中相应资源大小的一半(参见 logical miplevel-specific texture extent)。 级别 0 中的子资源具有纹理本身的尺寸。 这些通常用于表示纹理的细节级别。 GPUSampler 和 WGSL 提供了在细节层次之间选择和插值的工具,明确地或自动地。

"2d" 纹理可以是array layer的数组。 层中的每个子资源与其他层中的相应资源大小相同。 对于非二维纹理,所有子资源的数组层索引均为 0。

每个子资源都有一个方面。 颜色纹理只有一个方面:color深度或模板格式 纹理可能有多个方面: depth 方面,stencil 方面,或两者兼而有之,并且可以以特殊方式使用,例如 {{GPURenderPassDescriptor/depthStencilAttachment} } 和 "depth" 绑定。

"3d" 纹理可能有多个 切片,每个都是纹理中特定 z 值处的二维图像。 切片不是单独的子资源。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUTexture {
    GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});

    undefined destroy();

    readonly attribute GPUIntegerCoordinate width;
    readonly attribute GPUIntegerCoordinate height;
    readonly attribute GPUIntegerCoordinate depthOrArrayLayers;
    readonly attribute GPUIntegerCoordinate mipLevelCount;
    readonly attribute GPUSize32 sampleCount;
    readonly attribute GPUTextureDimension dimension;
    readonly attribute GPUTextureFormat format;
    readonly attribute GPUTextureUsageFlags usage;
};
GPUTexture includes GPUObjectBase;

GPUTexture 具有以下属性

width, of type GPUIntegerCoordinate, readonly

GPUTexture 的宽度。

height, of type GPUIntegerCoordinate, readonly

GPUTexture 的高度。

depthOrArrayLayers, of type GPUIntegerCoordinate, readonly

GPUTexture 的深度或层数。

mipLevelCount, of type GPUIntegerCoordinate, readonly

GPUTexture 的 mip 级别数。

sampleCount, of type GPUSize32, readonly

GPUTexture 的采样数。

dimension, of type GPUTextureDimension, readonly

GPUTexture 的每个子资源的纹素集的维度。

format, of type GPUTextureFormat, readonly

GPUTexture 的格式。

usage, of type GPUTextureUsageFlags, readonly

GPUTexture 的允许用法。

GPUTexture 有以下内部插槽:

[[size]], 类型为 GPUExtent3D

纹理的大小(与 widthheightdepthOrArrayLayers 属性相同)。

[[viewFormats]], 类型为 sequence<GPUTextureFormat>

在这个 GPUTexture 上创建视图时可以使用 GPUTextureViewDescriptor.format 的一组 GPUTextureFormat

[[destroyed]], 类型为 boolean, 初始值为 false

如果纹理被销毁,它就不能再用于任何操作,并且它的底层内存可以被释放。

compute render extent(baseSize, mipLevel)

Arguments:

Returns: GPUExtent3DDict

  1. 使 extent 为一个新的 GPUExtent3DDict 对象。

  2. 设置 extent.width 为 max(1, baseSize.widthmipLevel).

  3. 设置 extent.height 为 max(1, baseSize.heightmipLevel).

  4. 设置 extent.depthOrArrayLayers 为 1.

  5. 返回 extent.

texturelogical miplevel-specific texture extenttexture 在特定 miplevel 的纹素大小。 它是通过以下过程计算的:

Logical miplevel-specific texture extent(descriptor, mipLevel)

Arguments:

Returns: GPUExtent3DDict

  1. 使 extent 为一个新的 GPUExtent3DDict 对象.

  2. 如果 descriptor.dimension 为:

    "1d"
    "2d"
    "3d"
  3. 返回 extent.

特定miplevel物理纹理尺寸是指在特定mip级别的纹理大小(以像素为单位),包括可能的额外填充以形成纹理中的完整 [ =像素块= ]。 它通过以下步骤计算得出:

Physical miplevel-specific texture extent(descriptor, mipLevel)

Arguments:

Returns: GPUExtent3DDict

  1. 使 extent 为一个新的 GPUExtent3DDict 对象。

  2. 使 logicalExtentlogical miplevel-specific texture extent(descriptor, mipLevel).

  3. 如果 descriptor.dimension 为:

    "1d"
    "2d"
    "3d"
  4. 返回 extent.

6.1.1. GPUTextureDescriptor

dictionary GPUTextureDescriptor
         : GPUObjectDescriptorBase {
    required GPUExtent3D size;
    GPUIntegerCoordinate mipLevelCount = 1;
    GPUSize32 sampleCount = 1;
    GPUTextureDimension dimension = "2d";
    required GPUTextureFormat format;
    required GPUTextureUsageFlags usage;
    sequence<GPUTextureFormat> viewFormats = [];
};

GPUTextureDescriptor 具有以下成员:

size, of type GPUExtent3D

纹理的宽度、高度和深度或层数。

mipLevelCount, of type GPUIntegerCoordinate, defaulting to 1

纹理将包含的 mip 级别数。

sampleCount, of type GPUSize32, defaulting to 1

纹理的样本数量。sampleCount > 1 表示多重采样纹理。

dimension, of type GPUTextureDimension, defaulting to "2d"

纹理是一维的、二维层的数组还是三维的。

format, of type GPUTextureFormat

纹理的格式。

usage, of type GPUTextureUsageFlags

纹理的允许用途。

viewFormats, of type sequence<GPUTextureFormat>, defaulting to []

指定在调用 createView() 时允许的 format 值(除实际的 format 之外)。

注: 向该列表添加格式可能会对性能产生显著影响,因此最好避免不必要地添加格式。

实际性能影响高度依赖于目标系统;开发者必须测试各种系统,以了解其对特定应用的影响。 例如,在某些系统上,任何具有 formatviewFormats 条目(包括 "rgba8unorm-srgb")的纹理性能都会比一个没有该项的 "rgba8unorm" 纹理低一些。 在其他系统上,针对其他格式和格式组合也存在类似的注意事项。

此列表中的格式必须与纹理格式纹理视图格式兼容

如果两个GPUTextureFormats formatviewFormat纹理视图格式兼容的
  • format 等于 viewFormat, 或

  • formatviewFormat 区别仅在于它们是否为 srgb 格式(具有 -srgb 后缀)。

问题(gpuweb/gpuweb#168): 定义更大的兼容类。

enum GPUTextureDimension {
    "1d",
    "2d",
    "3d"
};
"1d"

指定具有一维宽度的纹理。

"2d"

指定具有宽度和高度并且可能具有层的纹理。 只有 "2d" 纹理可以有 mipmaps、多重采样、使用压缩或深度/模板格式,并用作渲染附件。

"3d"

指定具有宽度、高度和深度的纹理。

6.1.2. 纹理使用

typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
[Exposed=(Window, DedicatedWorker), SecureContext]
namespace GPUTextureUsage {
    const GPUFlagsConstant COPY_SRC          = 0x01;
    const GPUFlagsConstant COPY_DST          = 0x02;
    const GPUFlagsConstant TEXTURE_BINDING   = 0x04;
    const GPUFlagsConstant STORAGE_BINDING   = 0x08;
    const GPUFlagsConstant RENDER_ATTACHMENT = 0x10;
};

GPUTextureUsage 标志决定了 GPUTexture 在创建后如何使用:

COPY_SRC

纹理可以用作复制操作的来源。 (示例:作为 copyTextureToTexture()copyTextureToBuffer() 调用的 source 参数。)

COPY_DST

纹理可用作复制或写入操作的目标。 (示例:作为 copyTextureToTexture()copyBufferToTexture() 调用的“目标”参数,或作为 writeTexture() 调用的目标。)

TEXTURE_BINDING

纹理可以绑定用作着色器中的采样纹理(示例:作为绑定组 GPUTextureBindingLayout 的条目。)

STORAGE_BINDING

纹理可以绑定用作着色器中的存储纹理(示例:作为 GPUStorageTextureBindingLayout 的绑定组条目。)

RENDER_ATTACHMENT

纹理可以用作渲染过程中的颜色或深度/模板附件。 (示例:作为 GPURenderPassColorAttachment.viewGPURenderPassDepthStencilAttachment.view。)

maximum mipLevel count(dimension, size)

Arguments:

  1. 计算最大维度值 m

  2. 返回 floor(log2(m)) + 1.

6.1.3. 纹理创建

createTexture(descriptor)

创建一个 GPUTexture.

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createTexture(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUTextureDescriptor 要创建的 GPUTexture 的描述。

Returns: GPUTexture

内容时间线 步骤:

  1. ? validate GPUExtent3D shape(descriptor.size).

  2. ? 使用 this.[[device]] 验证descriptor.format 所需的纹理格式特性。

  3. 验证descriptor.viewFormats中每个元素所需的 ? 纹理格式所需特性this.[[device]]

  4. 使 t 为一个新的 GPUTexture 对象。

  5. 设置 t.widthdescriptor.size.width.

  6. 设置 t.heightdescriptor.size.height.

  7. 设置 t.depthOrArrayLayersdescriptor.size.depthOrArrayLayers.

  8. 设置 t.mipLevelCountdescriptor.mipLevelCount.

  9. 设置 t.sampleCountdescriptor.sampleCount.

  10. 设置 t.dimensiondescriptor.dimension.

  11. 设置 t.formatdescriptor.format.

  12. 设置 t.usagedescriptor.usage.

  13. this设备时间线 上发起 initialization steps

  14. 返回 t

设备时间线 initialization steps:
  1. 如果以下任何条件不满足,产生验证错误,使得 t无效,并停止。

    • 验证 GPUTextureDescriptor (this, descriptor) 返回 true.

  2. 设置 t.[[size]]descriptor.size.

  3. 设置 t.[[viewFormats]]descriptor.viewFormats.

验证 GPUTextureDescriptor(GPUDevice this, GPUTextureDescriptor descriptor):

如果满足以下所有要求,则返回“true”,否则返回“false”:

创建一个具有一个数组层和一个mip等级的16x16、RGBA格式的2D纹理:
const texture = gpuDevice.createTexture({
    size: { width: 16, height: 16 },
    format: 'rgba8unorm',
    usage: GPUTextureUsage.TEXTURE_BINDING,
});

6.1.4. 纹理销毁

一个不再需要GPUTexture的应用可以在垃圾收集之前选择通过调用destroy()来丢失对其的访问。

注:这允许用户代理在使用它完成所有先前提交的操作后回收与GPUTexture关联的GPU内存。

destroy()

销毁 GPUTexture.

Called on: GPUTexture this.

Returns: undefined

内容时间线 步骤:

  1. 设置 this.[[destroyed]] 为 true.

6.2. GPUTextureView

一个 GPUTextureView 是对某个 GPUTexture 定义的一部分 纹理子资源 的视图。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUTextureView {
};
GPUTextureView includes GPUObjectBase;

GPUTextureView 局有以下内部插槽:

[[texture]]

该视图所对应的GPUTexture

[[descriptor]]

GPUTextureViewDescriptor 描述这个纹理视图。

GPUTextureViewDescriptor 的所有可选字段均已定义。

[[renderExtent]]

对于可渲染视图,这是用于渲染的有效 GPUExtent3DDict

注:这个范围取决于 baseMipLevel

一组纹理视图 view子资源,具有 [[descriptor]] desc,是 view.[[texture]] 的子资源的子集,其中的每个子资源 s 都满足以下条件:

两个 GPUTextureView 对象之间的纹理视图别名仅当它们的子资源集合相交时才成立。

6.2.1. 纹理视图创建

dictionary GPUTextureViewDescriptor
         : GPUObjectDescriptorBase {
    GPUTextureFormat format;
    GPUTextureViewDimension dimension;
    GPUTextureAspect aspect = "all";
    GPUIntegerCoordinate baseMipLevel = 0;
    GPUIntegerCoordinate mipLevelCount;
    GPUIntegerCoordinate baseArrayLayer = 0;
    GPUIntegerCoordinate arrayLayerCount;
};

GPUTextureViewDescriptor 具有以下成员:

format, of type GPUTextureFormat

纹理视图的格式。必须是纹理的 format 或在其创建过程中指定的 viewFormats 之一。

dimension, of type GPUTextureViewDimension

纹理视图的维度。

aspect, of type GPUTextureAspect, defaulting to "all"

纹理视图可以访问纹理的哪些 aspect(s)

baseMipLevel, of type GPUIntegerCoordinate, defaulting to 0

纹理视图可访问的第一个(最详细的)mipmap 级别。

mipLevelCount, of type GPUIntegerCoordinate

纹理视图可以访问多少个 mipmap 级别,以 baseMipLevel 开头。

baseArrayLayer, of type GPUIntegerCoordinate, defaulting to 0

纹理视图可访问的第一个数组层的索引。

arrayLayerCount, of type GPUIntegerCoordinate

纹理视图可以访问多少个数组层,以 baseArrayLayer 开头。

enum GPUTextureViewDimension {
    "1d",
    "2d",
    "2d-array",
    "cube",
    "cube-array",
    "3d"
};
"1d"

纹理被视为一维图像。

对应的WGSL类型:

  • texture_1d

  • texture_storage_1d

"2d"

纹理被视为单个二维图像。

对应的WGSL类型:

  • texture_2d

  • texture_storage_2d

  • texture_multisampled_2d

  • texture_depth_2d

  • texture_depth_multisampled_2d

"2d-array"

纹理视图被视为二维图像的数组。

对应的WGSL类型:

  • texture_2d_array

  • texture_storage_2d_array

  • texture_depth_2d_array

"cube"

纹理被视为立方体贴图。 该视图有 6 个数组层,对应于立方体的 [+X, -X, +Y, -Y, +Z, -Z] 面。 采样是在立方体贴图的各个面上无缝完成的。

对应的WGSL类型:

  • texture_cube

  • texture_depth_cube

"cube-array"

纹理被视为 n 个立方体贴图的打包数组, 每个都有 6 个数组层,对应于立方体的 [+X, -X, +Y, -Y, +Z, -Z] 面。 采样是在立方体贴图的各个面上无缝完成的。

对应的WGSL类型:

  • texture_cube_array

  • texture_depth_cube_array

"3d"

纹理被视为 3 维图像。

对应的WGSL类型:

  • texture_3d

  • texture_storage_3d

每个 GPUTextureAspect 值对应一组 aspects方面集 是为下面的每个值定义的。

enum GPUTextureAspect {
    "all",
    "stencil-only",
    "depth-only"
};
"all"

所有纹理格式的可用方面都将对纹理视图可访问。对于颜色格式,颜色方面将是可访问的。对于 combined depth-stencil format,深度和模板方面都将是可访问的。只有一个方面的 Depth-or-stencil format 将只使该方面可访问。

set of aspects 是 [color, depth, stencil]。

"stencil-only"

仅深度或模板格式的模板方面可以访问纹理视图。

方面集是[stencil]。

"depth-only"

仅深度方面的 depth-or-stencil format 格式将可以在纹理视图中访问。

set of aspectsdepth

createView(descriptor)

创建一个 GPUTextureView.

注: 默认情况下,createView() 将创建一个可以表示整个纹理的维度视图。例如,在一个具有多个图层的 “2d” 纹理上调用 createView() 而不指定 dimension 会创建一个 “2d-array” GPUTextureView,即使指定了 arrayLayerCount 为 1。

对于在开发时图层计数未知的源创建的纹理,建议向 createView() 提供一个明确的 dimension,以确保着色器兼容性。

Called on: GPUTexture this.

Arguments:

Arguments for the GPUTexture.createView(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUTextureViewDescriptor Description of the GPUTextureView to create.

Returns: view, 类型为 GPUTextureView.

内容时间线 步骤:

  1. 验证 descriptor.formatthis.[[device]]所需的纹理格式功能。

  2. 使 view 为一个新的 GPUTextureView 对象。

  3. this设备时间线 上发起 initialization steps

  4. 返回 view.

设备时间线 initialization steps
  1. descriptor 设置为用 descriptorthis 进行解析 GPUTextureViewDescriptor 默认值的结果。

  2. 如果以下任何条件不满足,生成验证错误,使 view [无效],并停止。

  3. 使 view 为一个新的 GPUTextureView 对象。

  4. 设置 view.[[texture]]this

  5. 设置 view.[[descriptor]]descriptor

  6. 如果 this.usage 包含 RENDER_ATTACHMENT

    1. 使 renderExtentcompute render extent(this.[[size]], descriptor.baseMipLevel)。

    2. 设置 view.[[renderExtent]]renderExtent

当为GPUTextureView中的texture 解析 GPUTextureViewDescriptor 默认值时,执行以下步骤:
  1. 使 resolveddescriptor 的拷贝。

  2. 如果 resolved.format 不为 provided

    1. format 为返回的结果 解析GPUTextureAspect( format, descriptor.aspect).

    2. 如果 format 是 null:

      否则:

      • resolved.format 设置为 format

  3. 如果 resolved.mipLevelCount 没有 provided: 将 resolved.mipLevelCount 设置为 texture.mipLevelCountresolved.baseMipLevel

  4. 如果 resolved.dimension 没有 providedtexture.dimension 是:

    "1d"

    设置 resolved.dimension“1d”

    "2d"

    如果 texturearray layer count 为 1:

    否则:

    "3d"

    设置 resolved.dimension"3d"

  5. 如果 resolved.arrayLayerCount 没有 providedresolved.dimension 是:

    "1d", "2d", or "3d"

    设置 resolved.arrayLayerCount1

    "cube"

    设置 resolved.arrayLayerCount6

    "2d-array" or "cube-array"

    设置 resolved.arrayLayerCounttexturearray layer countresolved.baseArrayLayer

  6. Return resolved.

要确定GPUTexture texture数组层级计数,执行以下步骤:
  1. 如果 texture.dimension 为:

    "1d" or "3d"

    返回 1.

    "2d"

    返回 texture.depthOrArrayLayers.

6.3. 纹理格式

格式的名称指定组件的顺序、每个组件的位数和组件的数据类型。

如果格式具有-srgb后缀,则在着色器中读取和写入颜色值时将应用从伽玛到线性以及相反的sRGB转换。压缩纹理格式由features提供。它们的命名应遵循这里的约定,以纹理名作为前缀。例如etc2-rgba8unorm。

texel block是基于像素的GPUTextureFormat中纹理的单个可寻址元素,以及基于块的压缩GPUTextureFormat中的单个压缩块。

texel block widthtexel block height指定一个texel block的尺寸。

一个GPUTextureFormataspecttexel block copy footprint是一个纹理块在image copy过程中占用的字节数,如果适用的话。

注: GPUTextureFormattexel block memory cost是存储一个texel block所需的字节数。并非所有格式都完全定义了此值。 此值具有信息性质,非规范性质。

enum GPUTextureFormat {
    // 8-bit formats
    "r8unorm",
    "r8snorm",
    "r8uint",
    "r8sint",

    // 16-bit formats
    "r16uint",
    "r16sint",
    "r16float",
    "rg8unorm",
    "rg8snorm",
    "rg8uint",
    "rg8sint",

    // 32-bit formats
    "r32uint",
    "r32sint",
    "r32float",
    "rg16uint",
    "rg16sint",
    "rg16float",
    "rgba8unorm",
    "rgba8unorm-srgb",
    "rgba8snorm",
    "rgba8uint",
    "rgba8sint",
    "bgra8unorm",
    "bgra8unorm-srgb",
    // Packed 32-bit formats
    "rgb9e5ufloat",
    "rgb10a2unorm",
    "rg11b10ufloat",

    // 64-bit formats
    "rg32uint",
    "rg32sint",
    "rg32float",
    "rgba16uint",
    "rgba16sint",
    "rgba16float",

    // 128-bit formats
    "rgba32uint",
    "rgba32sint",
    "rgba32float",

    // Depth/stencil formats
    "stencil8",
    "depth16unorm",
    "depth24plus",
    "depth24plus-stencil8",
    "depth32float",

    // "depth32float-stencil8" feature
    "depth32float-stencil8",

    // BC compressed formats usable if "texture-compression-bc" is both
    // supported by the device/user agent and enabled in requestDevice.
    "bc1-rgba-unorm",
    "bc1-rgba-unorm-srgb",
    "bc2-rgba-unorm",
    "bc2-rgba-unorm-srgb",
    "bc3-rgba-unorm",
    "bc3-rgba-unorm-srgb",
    "bc4-r-unorm",
    "bc4-r-snorm",
    "bc5-rg-unorm",
    "bc5-rg-snorm",
    "bc6h-rgb-ufloat",
    "bc6h-rgb-float",
    "bc7-rgba-unorm",
    "bc7-rgba-unorm-srgb",

    // ETC2 compressed formats usable if "texture-compression-etc2" is both
    // supported by the device/user agent and enabled in requestDevice.
    "etc2-rgb8unorm",
    "etc2-rgb8unorm-srgb",
    "etc2-rgb8a1unorm",
    "etc2-rgb8a1unorm-srgb",
    "etc2-rgba8unorm",
    "etc2-rgba8unorm-srgb",
    "eac-r11unorm",
    "eac-r11snorm",
    "eac-rg11unorm",
    "eac-rg11snorm",

    // ASTC compressed formats usable if "texture-compression-astc" is both
    // supported by the device/user agent and enabled in requestDevice.
    "astc-4x4-unorm",
    "astc-4x4-unorm-srgb",
    "astc-5x4-unorm",
    "astc-5x4-unorm-srgb",
    "astc-5x5-unorm",
    "astc-5x5-unorm-srgb",
    "astc-6x5-unorm",
    "astc-6x5-unorm-srgb",
    "astc-6x6-unorm",
    "astc-6x6-unorm-srgb",
    "astc-8x5-unorm",
    "astc-8x5-unorm-srgb",
    "astc-8x6-unorm",
    "astc-8x6-unorm-srgb",
    "astc-8x8-unorm",
    "astc-8x8-unorm-srgb",
    "astc-10x5-unorm",
    "astc-10x5-unorm-srgb",
    "astc-10x6-unorm",
    "astc-10x6-unorm-srgb",
    "astc-10x8-unorm",
    "astc-10x8-unorm-srgb",
    "astc-10x10-unorm",
    "astc-10x10-unorm-srgb",
    "astc-12x10-unorm",
    "astc-12x10-unorm-srgb",
    "astc-12x12-unorm",
    "astc-12x12-unorm-srgb"
};

深度组件 “depth24plus”“depth24plus-stencil8” 格式可以实现为 24 位深度 值或 “depth32float” 值。

问题(gpuweb/gpuweb#1887):在GPUAdapter(?)上添加一些内容,估计每个像素的字节数为 “stencil8”“depth24plus-stencil8”,以及 “depth32float-stencil8”

stencil8 这种格式可以分为两种: 实际的"stencil8",或者"depth24stencil8",其中深度方面为隐藏且无法访问。

注: 虽然 depth32float 通道的精度严格高于 24-bit depth 通道的精度(在可表示范围(0.0到1.0)内的所有值),但请注意,可表示值集合并非严格的超集。

对于 24-bit depth,1 ULP 的值为常数 1 /(224 - 1)。 对于 depth32float,1 ULP 的值为不大于 1 /(224)的可变值。

格式是可渲染的,如果它是 color renderable format ,或depth-or-stencil format。 如果格式在§ 26.1.1 纯彩色格式中列出并具有RENDER_ATTACHMENT功能,则它是 颜色可渲染格式。任何其他格式都不是颜色可渲染格式。 所有depth-or-stencil formats都是可渲染的。

具有可渲染格式的格式也是可混合的 如果它可以与渲染管线混合一起使用。 请参阅§ 26.1 纹理格式功能

格式是可过滤的,如果它支持 GPUTextureSampleType “float” (而不仅仅是“unfilterable-float”); 也就是说,它可以与“filtering”GPUSampler一起使用。 请参阅§ 26.1 纹理格式功能

resolving GPUTextureAspect(format, aspect)

Arguments:

Returns: GPUTextureFormatnull

  1. 如果 aspect 为:

    "all"

    返回 format

    "depth-only"
    "stencil-only"

    如果 format 为 depth-stencil-format: 返回 formataspect-specific format 根据 § 26.1.2 深度模具格式null 如果方面不存在于 format 中。

  2. 返回 null

使用某些纹理格式需要在 GPUDevice 上启用功能。由于新格式可能会被添加到规范中,因此实现可能不知道这些枚举值。为了在实现之间规范化行为,如果未在设备上启用关联的功能,则尝试使用需要功能的格式将抛出异常。这使得行为与实现不知道格式时相同。

请参阅§ 26.1 纹理格式功能以获取有关哪些GPUTextureFormat需要功能的信息。

验证纹理格式所需的特性 GPUTextureFormat format 使用逻辑 device device 通过运行以下步骤:

1.如果格式 需要一个特性,而 device.[[features]] 不需要 contain 特点: 1.抛出一个TypeError

6.4. GPUExternalTexture

一个 GPUExternalTexture 是一个可采样的二维纹理,包装了一个外部视频对象。 GPUExternalTexture 对象的内容是一个快照,可能不会改变,无论是从 WebGPU 内部(它只是可采样的)还是从 WebGPU 外部(比如,由于视频帧的推进)。

它们通过使用 externalTexture 绑定组布局条目成员绑定到绑定组布局中。 外部纹理使用了多个绑定槽:请参阅 超过绑定槽限制

注: 外部纹理可以在不创建导入源副本的情况下实现, 但这取决于实现定义的因素。 底层表示的所有权可以是独占的,也可以与其他所有者(如视频解码器)共享,但应用程序无法看到这一点。

外部纹理的底层表示不可观察(除采样行为外),但通常可能包括

所使用的配置在时间、系统、用户代理、媒体源之间,或在单个视频源的帧内可能不稳定。 为了考虑到许多可能的表示,对每个外部纹理,该绑定保守地使用以下内容:

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUExternalTexture {
};
GPUExternalTexture includes GPUObjectBase;

GPUExternalTexture 局有以下内部插槽:

[[expired]], 类型为 boolean

指示对象是否已过期(不再可用)。 最初设置为 false

注: 与类似的 \[[destroyed]] 槽位不同,这个值可以从 true 更改回 false

[[descriptor]], 类型为 GPUExternalTextureDescriptor

创建纹理的描述符。

6.4.1. 引入外部纹理

从外部视频对象创建外部纹理,使用 importExternalTexture()

HTMLVideoElement 创建的外部纹理,在导入后的任务中会自动销毁,而不是像其他资源那样手动销毁或在垃圾回收时销毁。当外部纹理过期时,其 [[expired]] 插槽变为 true。

一旦 GPUExternalTexture 过期,必须再次调用 importExternalTexture()。然而,用户代理可能会取消过期,并再次返回同一个 GPUExternalTexture,而不是创建一个新的。除非应用程序的执行预定与视频的帧率相匹配(例如,使用 requestVideoFrameCallback()),否则这种情况通常会发生。如果再次返回相同的对象,它们将进行相等比较,而引用先前对象的 GPUBindGroupGPURenderBundle 等仍可使用。

dictionary GPUExternalTextureDescriptor
         : GPUObjectDescriptorBase {
    required HTMLVideoElement source;
    PredefinedColorSpace colorSpace = "srgb";
};
importExternalTexture(descriptor)

创建一个 GPUExternalTexture 包装提供的图像源。

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.importExternalTexture(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUExternalTextureDescriptor 提供外部图像源对象(以及任何创建选项)。

Returns: GPUExternalTexture

内容时间线 步骤:

  1. 使 sourcedescriptor.source.

  2. 如果 source 的当前图像内容与最近一次使用相同的 descriptor 调用 importExternalTexture() 相同(忽略 label),并且用户代理选择重用它:

    1. previousResult 为先前返回的 GPUExternalTexture

    2. previousResult.[[expired]] 设置为 false,更新对底层资源的所有权。

    3. resultpreviousResult

    注: 这允许应用程序检测到重复导入并避免重新创建依赖对象(如 GPUBindGroup )。实现仍然需要能够处理单个帧由多个 GPUExternalTexture 包装,因为即使对于相同的帧,导入元数据(如 colorSpace )也可能发生变化。

    否则:

    1. 如果 source is not origin-clean ,则抛出 SecurityError 并停止。

    2. usability? =检测图像参数可用性=。

    3. 如果 usability 不是 good:

      1. 生成验证错误

      2. 返回无效的 GPUExternalTexture

    4. data 是将 source 的当前图像内容转换为 descriptor.colorSpace 颜色空间并带有未预乘的透明度的结果。

      可能导致值超出范围 [0, 1]。如果需要进行截取,可以在采样后进行。

      注:这像是一个复制过程,但可以实现为对只读底层数据的引用,并在以后执行适当的元数据进行转换。

    5. result 是一个包装 data 的新 GPUExternalTexture 对象。

  3. 使用设备 this 和以下步骤 queue an automatic expiry task

    1. result.[[expired]] 设置为 true, 释放底层资源的所有权。

    注: 应该在同一个任务中导入外部视频纹理,该任务会对纹理进行采样 (通常应该使用 requestVideoFrameCallbackrequestAnimationFrame() 根据应用程序进行调度)。 否则,纹理可能在 应用程序完成使用之前被这些步骤销毁。

  4. result.label 设置为 descriptor.label

  5. 返回 result

以页面动画帧速率使用视频元素外部纹理进行渲染:
const videoElement = document.createElement('video');
// ... set up videoElement, wait for it to be ready...

let externalTexture;

function frame() {
    requestAnimationFrame(frame);

    // Re-import only if necessary
    if (!externalTexture || externalTexture.expired) {
        externalTexture = gpuDevice.importExternalTexture({
            source: videoElement
        });
    }

    // ... render using externalTexture...
}
requestAnimationFrame(frame);
以视频的帧速率使用视频元素外部纹理渲染,如果 requestVideoFrameCallback 可用:
const videoElement = document.createElement('video');
// ... set up videoElement...

function frame() {
    videoElement.requestVideoFrameCallback(frame);

    // Always re-import, because we know the video frame has advanced
    const externalTexture = gpuDevice.importExternalTexture({
        source: videoElement
    });

    // ... render using externalTexture...
}
videoElement.requestVideoFrameCallback(frame);

6.4.2. 采样外部纹理

外部纹理在WGSL中用 texture_external 表示,并且可以使用 textureLoadtextureSampleBaseClampToEdge 进行读取。

提供给textureSampleBaseClampToEdge的 sampler 用于采样底层纹理。 结果在由colorSpace设置的颜色空间中。 实现依赖的是,对于任何给定的外部纹理,采样器(和过滤器)是在转换为指定颜色空间的底层值之前还是之后应用的。

注: 如果内部表示是RGBA平面,那么采样行为就像在常规2D纹理上一样。 如果有多个底层平面(例如:Y+UV),采样器用于分别采样每个底层纹理,然后将YUV转换为指定颜色空间。

7. 采样器

7.1. GPUSampler

一个 GPUSampler 编码了变换和过滤信息,可以在着色器中使用这些信息来解释纹理资源数据。

GPUSampler 是通过 createSampler() 创建的。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSampler {
};
GPUSampler includes GPUObjectBase;

GPUSampler 具有以下内部插槽:

[[descriptor]], 类型为 GPUSamplerDescriptor, readonly

创建 GPUSamplerGPUSamplerDescriptor

[[isComparison]], 类型为 boolean

GPUSampler 是否用作比较采样器。

[[isFiltering]], 类型为 boolean

GPUSampler 是否对纹理的多个样本进行加权。

7.1.1. GPUSamplerDescriptor

GPUSamplerDescriptor 指定用于创建 GPUSampler 的选项。

dictionary GPUSamplerDescriptor
         : GPUObjectDescriptorBase {
    GPUAddressMode addressModeU = "clamp-to-edge";
    GPUAddressMode addressModeV = "clamp-to-edge";
    GPUAddressMode addressModeW = "clamp-to-edge";
    GPUFilterMode magFilter = "nearest";
    GPUFilterMode minFilter = "nearest";
    GPUMipmapFilterMode mipmapFilter = "nearest";
    float lodMinClamp = 0;
    float lodMaxClamp = 32;
    GPUCompareFunction compare;
    [Clamp] unsigned short maxAnisotropy = 1;
};
addressModeU, of type GPUAddressMode, defaulting to "clamp-to-edge"
addressModeV, of type GPUAddressMode, defaulting to "clamp-to-edge"
addressModeW, of type GPUAddressMode, defaulting to "clamp-to-edge"

分别为纹理宽度、高度和深度坐标指定 address modes

magFilter, of type GPUFilterMode, defaulting to "nearest"

指定样本足迹小于或等于一个纹素时的采样行为。

minFilter, of type GPUFilterMode, defaulting to "nearest"

指定样本足迹大于一个纹素时的采样行为。

mipmapFilter, of type GPUMipmapFilterMode, defaulting to "nearest"

指定在 mipmap 级别之间进行采样的行为。

lodMinClamp, of type float, defaulting to 0
lodMaxClamp, of type float, defaulting to 32

分别指定在对纹理进行采样时在内部使用的最小和最大细节级别。

compare, of type GPUCompareFunction

提供时,采样器将是具有指定 GPUCompareFunction 的比较采样器。

注:比较采样器可能会使用过滤,但采样结果将是 依赖于实现并且可能不同于正常的过滤规则。

maxAnisotropy, of type unsigned short, defaulting to 1

指定采样器使用的最大各向异性值夹具。

注:大多数实现支持范围在1到16之间(包括1和16)的maxAnisotropy值。所使用的maxAnisotropy值将被限制在平台支持的最大值内。

问题:解释如何计算LOD,以及在不同平台之间是否存在差异。

问题:解释各向异性采样是什么

GPUAddressMode描述了当采样留痕超出采样纹理的边界时,采样器的行为。

问题:更详细地描述“采样留痕”。

enum GPUAddressMode {
    "clamp-to-edge",
    "repeat",
    "mirror-repeat"
};
"clamp-to-edge"

纹理坐标被限制在 0.0 和 1.0 之间,包括 0.0 和 1.0 在内。

"repeat"

纹理坐标环绕到纹理的另一侧。

"mirror-repeat"

纹理坐标环绕到纹理的另一侧,但当坐标的整数部分为奇数时纹理会翻转。

GPUFilterModeGPUMipmapFilterMode 描述采样器在采样足迹与一个纹素不完全匹配时的行为。

enum GPUFilterMode {
    "nearest",
    "linear"
};

enum GPUMipmapFilterMode {
    "nearest",
    "linear"
};
"nearest"

返回最接近纹理坐标的纹理元素的值。

"linear"

在每个维度中选择两个纹素并返回它们值之间的线性插值。

GPUCompareFunction指定了比较采样器的行为。如果在着色器中使用了比较采样器,则输入值与采样纹理值进行比较,此比较测试的结果(0.0f表示通过,1.0f表示失败)会应用于过滤操作。

问题:描述过滤如何与比较采样相互作用。

enum GPUCompareFunction {
    "never",
    "less",
    "equal",
    "less-equal",
    "greater",
    "not-equal",
    "greater-equal",
    "always"
};
"never"

比较测试永远不会通过。

"less"

如果提供的值小于采样值,则通过比较测试。

"equal"

如果提供的值等于采样值,则通过比较测试。

"less-equal"

如果提供的值小于或等于采样值,则通过比较测试。

"greater"

如果提供的值大于采样值,则通过比较测试。

"not-equal"

如果提供的值不等于采样值,则通过比较测试。

"greater-equal"

如果提供的值大于或等于采样值,则通过比较测试。

"always"

比较测试总是通过。

7.1.2. 采样器创建

createSampler(descriptor)

创建一个 GPUSampler

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createSampler(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUSamplerDescriptor 要创建的 GPUSampler 的描述。

Returns: GPUSampler

内容时间线 步骤:

  1. s 成为一个新的 GPUSampler 对象。

  2. this设备时间线 上执行 initialization steps

  3. 返回 s

Device timeline initialization steps:
  1. 如果以下任何条件未满足,则生成一个验证错误,使 s 无效,并停止。

  2. 设置 s.[[descriptor]]descriptor.

  3. 设置 s.[[isComparison]]false 如果 a.[[descriptor]]compare 属性为“null”或未定义。 否则,将其设置为“true”。

  4. 设置 s.[[isFiltering]]false 如果 minFiltermagFiltermipmapFilter 都没有 "linear" 的值。 否则,将其设置为“true”。

创建一个进行三线性过滤和重复纹理坐标的 GPUSampler
const sampler = gpuDevice.createSampler({
    addressModeU: 'repeat',
    addressModeV: 'repeat',
    magFilter: 'linear',
    minFilter: 'linear',
    mipmapFilter: 'linear',
});

8. 资源绑定

8.1. GPUBindGroupLayout

一个 GPUBindGroupLayout 定义了在 GPUBindGroup 中绑定的一组资源与它们在着色器阶段中的可访问性之间的接口。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBindGroupLayout {
};
GPUBindGroupLayout includes GPUObjectBase;

GPUBindGroupLayout 具有以下内部插槽:

[[descriptor]], of type GPUBindGroupLayoutDescriptor

8.1.1. 绑定组布局创建

GPUBindGroupLayout 通过 GPUDevice.createBindGroupLayout() 被创建。

dictionary GPUBindGroupLayoutDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPUBindGroupLayoutEntry> entries;
};

GPUBindGroupLayoutEntry 描述了要包含在 GPUBindGroupLayout 中的单个着色器资源绑定。

dictionary GPUBindGroupLayoutEntry {
    required GPUIndex32 binding;
    required GPUShaderStageFlags visibility;

    GPUBufferBindingLayout buffer;
    GPUSamplerBindingLayout sampler;
    GPUTextureBindingLayout texture;
    GPUStorageTextureBindingLayout storageTexture;
    GPUExternalTextureBindingLayout externalTexture;
};

GPUBindGroupLayoutEntry 字典有以下成员:

binding, of type GPUIndex32

一个唯一的标识符,用于在 GPUBindGroupLayout 中的资源绑定,对应于 GPUBindGroupEntry.bindingGPUShaderModule 中的 @binding 属性。

visibility, of type GPUShaderStageFlags

GPUShaderStage成员的位集。 每个设置的位表示GPUBindGroupLayoutEntry的资源 将从关联的着色器阶段访问。

buffer, of type GPUBufferBindingLayout

provided 时,表示此 GPUBindGroupLayoutEntry绑定资源类型GPUBufferBinding

sampler, of type GPUSamplerBindingLayout

provided 时,表示此 GPUBindGroupLayoutEntry绑定资源类型GPUSampler

texture, of type GPUTextureBindingLayout

provided 时,表示此 GPUBindGroupLayoutEntrybinding resource typeGPUTextureView

storageTexture, of type GPUStorageTextureBindingLayout

provided 时,表示此 GPUBindGroupLayoutEntrybinding resource typeGPUTextureView

externalTexture, of type GPUExternalTextureBindingLayout

provided 时,表示此 GPUBindGroupLayoutEntry绑定资源类型GPUExternalTexture

typedef [EnforceRange] unsigned long GPUShaderStageFlags;
[Exposed=(Window, DedicatedWorker), SecureContext]
namespace GPUShaderStage {
    const GPUFlagsConstant VERTEX   = 0x1;
    const GPUFlagsConstant FRAGMENT = 0x2;
    const GPUFlagsConstant COMPUTE  = 0x4;
};

GPUShaderStage 包含以下标志,这些标志描述了此 GPUBindGroupLayoutEntry 的相应 GPUBindGroupEntry 的着色器阶段将对哪些可见:

VERTEX

顶点着色器可以访问绑定组条目。

FRAGMENT

片段着色器可以访问绑定组条目。

COMPUTE

计算着色器可以访问绑定组条目。

The binding member of a GPUBindGroupLayoutEntry is determined by which member of the GPUBindGroupLayoutEntry is defined: buffer, sampler, texture, storageTexture, or externalTexture. Only one may be defined for any given GPUBindGroupLayoutEntry. Each member has an associated GPUBindingResource type and each binding type has an associated internal usage, given by this table:

Binding member Resource type Binding type
Binding usage
buffer GPUBufferBinding "uniform" constant
"storage" storage
"read-only-storage" storage-read
sampler GPUSampler "filtering" constant
"non-filtering"
"comparison"
texture GPUTextureView "float" constant
"unfilterable-float"
"depth"
"sint"
"uint"
storageTexture GPUTextureView "write-only" storage
externalTexture GPUExternalTexture constant
如果朝着限制的插槽数超过了limits中支持的值,那么GPUBindGroupLayoutEntry值的list entries 超过了绑定插槽限制。每个条目可以向多个限制使用多个插槽。
  1. 对于每个 条目entries 中,如果:

    entry.buffer?.type"uniform"entry.buffer?.hasDynamicOffsettrue

    考虑使用 1 个 maxDynamicUniformBuffersPerPipelineLayout 插槽。

    entry.buffer?.type"storage"entry.buffer?.hasDynamicOffsettrue

    考虑使用 1 个 maxDynamicStorageBuffersPerPipelineLayout 插槽。

  2. 对每个在 « VERTEX, FRAGMENT, COMPUTE » 中的着色器阶段 stage:

    1. 对于每个 entriesentries entry.visibility 包含 stage,如果:

      entry.buffer?.type"uniform"

      考虑 1 maxUniformBuffersPerShaderStage 插槽将被使用。

      entry.buffer?.type"storage""read-only-storage"

      考虑 1 maxStorageBuffersPerShaderStage 插槽将被使用。

      entry.samplerprovided

      考虑 1 maxSamplersPerShaderStage 插槽将被使用。

      entry.textureprovided

      考虑 1 maxSampledTexturesPerShaderStage 插槽将被使用。

      entry.storageTextureprovided

      考虑 1 maxStorageTexturesPerShaderStage 插槽将被使用。

      entry.externalTextureprovided

      考虑 4 maxSampledTexturesPerShaderStage 插槽, 1 maxSamplersPerShaderStage 插槽, 及 1 maxUniformBuffersPerShaderStage 插槽 将被使用。

enum GPUBufferBindingType {
    "uniform",
    "storage",
    "read-only-storage"
};

dictionary GPUBufferBindingLayout {
    GPUBufferBindingType type = "uniform";
    boolean hasDynamicOffset = false;
    GPUSize64 minBindingSize = 0;
};

GPUBufferBindingLayout 字典具有以下成员:

type, of type GPUBufferBindingType, defaulting to "uniform"

指示绑定到此绑定的缓冲区所需的类型。

hasDynamicOffset, of type boolean, defaulting to false

指示此绑定是否需要动态偏移量。

minBindingSize, of type GPUSize64, defaulting to 0

指示与此绑定点一起使用的缓冲区绑定的最小size

createBindGroup()中,绑定始终根据此大小进行验证。

如果这个值不是 0,管线创建还将validates这个值≥变量的minimum buffer binding size

如果这个值是 0,它将被管线创建忽略,而是通过绘制/调度命令validate,每个GPUBindGroup中的绑定都满足变量的minimum buffer binding size

注: 理论上,对于早期验证指定的其他绑定相关字段(如sampleTypeformat),也可以进行类似的执行时验证,而目前这些字段只能在管线创建时进行验证。 然而,这种执行时验证可能会产生较高的成本或不必要的复杂性,因此只对minBindingSize 提供这种验证,这些字段预计将产生最大的人体工程学影响。

enum GPUSamplerBindingType {
    "filtering",
    "non-filtering",
    "comparison"
};

dictionary GPUSamplerBindingLayout {
    GPUSamplerBindingType type = "filtering";
};

GPUSamplerBindingLayout 字典具有以下成员:

type, of type GPUSamplerBindingType, defaulting to "filtering"

指示绑定到此绑定的采样器的必需类型。

enum GPUTextureSampleType {
    "float",
    "unfilterable-float",
    "depth",
    "sint",
    "uint"
};

dictionary GPUTextureBindingLayout {
    GPUTextureSampleType sampleType = "float";
    GPUTextureViewDimension viewDimension = "2d";
    boolean multisampled = false;
};

GPUTextureBindingLayout 字典具有以下成员:

sampleType, of type GPUTextureSampleType, defaulting to "float"

指示绑定到此绑定的纹理视图所需的类型。

viewDimension, of type GPUTextureViewDimension, defaulting to "2d"

指示绑定到此绑定的纹理视图所需的 dimension

multisampled, of type boolean, defaulting to false

指示绑定到此绑定的纹理视图是否必须进行多重采样。

enum GPUStorageTextureAccess {
    "write-only"
};

dictionary GPUStorageTextureBindingLayout {
    GPUStorageTextureAccess access = "write-only";
    required GPUTextureFormat format;
    GPUTextureViewDimension viewDimension = "2d";
};

GPUStorageTextureBindingLayout 字典具有以下成员:

access, of type GPUStorageTextureAccess, defaulting to "write-only"

此绑定的访问模式,指示可读性和可写性。

注: 目前只有一种访问模式"write-only",未来会扩展。

format, of type GPUTextureFormat

绑定到此绑定的纹理视图所需的 format

viewDimension, of type GPUTextureViewDimension, defaulting to "2d"

指示绑定到此绑定的纹理视图所需的 dimension

dictionary GPUExternalTextureBindingLayout {
};

A GPUBindGroupLayout 对象具有以下内部插槽:

[[entryMap]], 类型为 ordered map<GPUSize32, GPUBindGroupLayoutEntry>

指向这个 GPUBindGroupLayout 描述的 GPUBindGroupLayoutEntry 的绑定索引映射。

[[dynamicOffsetCount]], 类型为 GPUSize32

GPUBindGroupLayout 中具有动态偏移量的缓冲区绑定数。

[[exclusivePipeline]], 类型为 GPUPipelineBase?, 初始值为 null

创建此GPUBindGroupLayout的管线(如果它是作为 默认管线布局的一部分创建的)。如果不是 null ,则使用此GPUBindGroupLayout创建的GPUBindGroup只能与指定的GPUPipelineBase一起使用。

createBindGroupLayout(descriptor)

创建一个 GPUBindGroupLayout.

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createBindGroupLayout(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUBindGroupLayoutDescriptor 要创建的 GPUBindGroupLayout 的描述。

Returns: GPUBindGroupLayout

Content timeline steps:

  1. 对于每个 GPUBindGroupLayoutEntrydescriptor 中的 entry.entries:

    1. 如果 entry.storageTextureprovided:

      1. 验证所需特性的纹理格式对于 entry.storageTexture.formatthis.[[device]]

  2. layout 是一个新的 GPUBindGroupLayout 对象。

  3. this设备时间线 上发起 initialization steps

  4. 返回 layout

设备时间线 initialization steps:
  1. 如果以下任何条件未满足,生成验证错误,并使 layout 无效,然后停止。

  2. 设置 layout.[[descriptor]] to descriptor.

  3. 设置 layout.[[dynamicOffsetCount]] to the number of entries in descriptor where buffer is provided and buffer.hasDynamicOffset is true.

  4. 对每个 descriptor.entries 中的 GPUBindGroupLayoutEntry entry

    1. 使用 entry.binding 的键值,将 entry 插入 layout.[[entryMap]] 中。

8.1.2. 兼容性

如果满足以下所有条件,则两个GPUBindGroupLayout对象ab被认为是 组等价的:

如果绑定组布局是 group-equivalent 它们可以在所有内容中互换使用。

8.2. GPUBindGroup

GPUBindGroup 定义了一组要绑定在一起的资源,以及这些资源在着色器阶段的使用方式。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBindGroup {
};
GPUBindGroup includes GPUObjectBase;

GPUBindGroup 对象具有以下内部插槽:

[[layout]], of type GPUBindGroupLayout, readonly

与此 GPUBindGroup 关联的 GPUBindGroupLayout

[[entries]], of type sequence<GPUBindGroupEntry>, readonly

GPUBindGroup 描述的一组 GPUBindGroupEntry

[[usedResources]], of type ordered map<subresource, list<internal usage>>, readonly

此绑定组使用的缓冲区和纹理集 subresource,与 internal usage 标志列表相关联。

8.2.1. 绑定组创建

GPUBindGroup 通过 GPUDevice.createBindGroup() 创建。

dictionary GPUBindGroupDescriptor
         : GPUObjectDescriptorBase {
    required GPUBindGroupLayout layout;
    required sequence<GPUBindGroupEntry> entries;
};

GPUBindGroupDescriptor 字典有以下成员:

layout, of type GPUBindGroupLayout

GPUBindGroupLayout 该绑定组的条目将符合。

entries, of type sequence<GPUBindGroupEntry>

一个条目列表,描述为 layout 描述的每个绑定向着色器公开的资源。

typedef (GPUSampler or GPUTextureView or GPUBufferBinding or GPUExternalTexture) GPUBindingResource;

dictionary GPUBindGroupEntry {
    required GPUIndex32 binding;
    required GPUBindingResource resource;
};

GPUBindGroupEntry 描述了要绑定在 GPUBindGroup 中的单个资源,并且具有 以下成员:

binding, of type GPUIndex32

GPUBindGroup 中资源绑定的唯一标识符,对应于 GPUBindGroupLayoutEntry.bindingGPUShaderModule 中的 @binding 属性。

resource, of type GPUBindingResource

要绑定的资源,可以是 GPUSamplerGPUTextureViewGPUExternalTextureGPUBufferBinding

dictionary GPUBufferBinding {
    required GPUBuffer buffer;
    GPUSize64 offset = 0;
    GPUSize64 size;
};

GPUBufferBinding 描述一个缓冲区和可选范围以绑定为资源,并具有以下成员:

buffer, of type GPUBuffer

要绑定的 GPUBuffer

offset, of type GPUSize64, defaulting to 0

buffer 的开头到缓冲区绑定暴露给着色器的范围的开头的偏移量(以字节为单位)。

size, of type GPUSize64

缓冲区绑定的大小(以字节为单位)。 如果不是 provided,则指定从 offset 开始到 buffer 结束的范围。

createBindGroup(descriptor)

Creates a GPUBindGroup.

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createBindGroup(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUBindGroupDescriptor 要创建的 GPUBindGroup 的描述。

Returns: GPUBindGroup

内容时间线 步骤:

  1. 使 bindGroup 为一个新的 GPUBindGroup 对象。

  2. this设备时间线 上发起 initialization steps

  3. 返回 bindGroup

设备时间线 initialization steps:
  1. 使 limitsthis.[[device]].[[limits]].

  2. 如果以下任何条件未满足,生成验证错误,使 bindGroup 无效,并停止。

    • descriptor.layout 可以与 this 一起使用。

    • descriptor.layoutentries 长度 正好等于 descriptor.entries 的长度。

    对于 descriptor.entries 中的每个 GPUBindGroupEntry bindingDescriptor

  3. 使 bindGroup.[[layout]] = descriptor.layout.

  4. 使 bindGroup.[[entries]] = descriptor.entries.

  5. 使 bindGroup.[[usedResources]] = {}.

  6. 对每个 descriptor.entries 中的 GPUBindGroupEntry bindingDescriptor

    1. internalUsagelayoutBindingbinding usage

    2. resource看到的每个subresourceinternalUsage的形式添加到[[usedResources]]中。

effective buffer binding size(binding)
  1. 如果 binding.size 没有 提供

    1. 返回 max(0, binding.buffer.size - binding.offset);

  2. 返回 binding.size.

两个 GPUBufferBinding 对象 ab 当且仅当以下所有条件都为真时,被认为存在缓冲区绑定别名

问题:当 size 可以为未定义时,定义如何通过 offset/size 形成范围。

8.3. GPUPipelineLayout

一个GPUPipelineLayout定义了在setBindGroup()中设置的所有GPUBindGroup对象的资源与通过GPURenderCommandsMixin.setPipelineGPUComputePassEncoder.setPipeline设置的管线中的着色器之间的映射关系。

资源的完整绑定地址可以定义为一个三元组:

  1. 着色器阶段掩码,表示资源可见的阶段

  2. 绑定组索引

  3. 绑定编号

这个地址的组成部分也可以看作是管线的绑定空间。一个GPUBindGroup(带有相应的GPUBindGroupLayout)为固定的绑定组索引覆盖了该空间。其中包含的绑定需要是着色器在这个绑定组索引下使用的资源的超集。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUPipelineLayout {
};
GPUPipelineLayout includes GPUObjectBase;

GPUPipelineLayout 具有以下内部插槽:

[[bindGroupLayouts]], 类型为 list<GPUBindGroupLayout>

GPUPipelineLayoutDescriptor.bindGroupLayouts 中创建时提供的 GPUBindGroupLayout 对象。

注:对许多 GPURenderPipelineGPUComputePipeline 管线使用相同的 GPUPipelineLayout 可确保在这些管线之间切换时用户代理不需要在内部重新绑定任何资源。

GPUComputePipeline object X was created with GPUPipelineLayout.bindGroupLayouts A, B, C. GPUComputePipeline object Y was created with GPUPipelineLayout.bindGroupLayouts A, D, C. Supposing the command encoding sequence has two dispatches:
  1. setBindGroup(0, ...)

  2. setBindGroup(1, ...)

  3. setBindGroup(2, ...)

  4. setPipeline(X)

  5. dispatchWorkgroups()

  6. setBindGroup(1, ...)

  7. setPipeline(Y)

  8. dispatchWorkgroups()

在这种情况下,即使GPUPipelineLayout.bindGroupLayouts索引2处的GPUBindGroupLayout和插槽2处的GPUBindGroup均未发生更改,用户代理也需要重新绑定组插槽2以进行第二次分派。

注:GPUPipelineLayout 的预期用法是将最常见且最不经常更改的绑定组放在布局的 “底部”,即更低的绑定组槽数,如 0 或 1。绑定组需要在绘制调用之间更频繁地更改,其索引应该越高。这一总体原则允许用户代理在绘制调用之间最小化状态更改,从而降低 CPU 开销。

8.3.1. 管线布局创建

一个 GPUPipelineLayout 通过 GPUDevice.createPipelineLayout() 被创建。

dictionary GPUPipelineLayoutDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPUBindGroupLayout> bindGroupLayouts;
};

GPUPipelineLayoutDescriptor 字典定义了管线使用的所有 GPUBindGroupLayout,并具有以下成员:

bindGroupLayouts, of type sequence<GPUBindGroupLayout>

一个将被管线使用的GPUBindGroupLayout列表。每个元素对应于GPUShaderModule中的一个@group属性,其中第 N 个元素对应于 @group(N)

createPipelineLayout(descriptor)

创建一个 GPUPipelineLayout

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createPipelineLayout(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUPipelineLayoutDescriptor 要创建的 GPUPipelineLayout 的描述。

Returns: GPUPipelineLayout

内容时间线 步骤:

  1. 使 pl 为一个新的 GPUPipelineLayout 对象。

  2. this设备时间线 上发起 initialization steps

  3. 返回 pl

设备时间线 initialization steps:
  1. 使 limitsthis.[[device]].[[limits]].

  2. allEntries 成为连接 bgl.[[descriptor]].entries 中所有bgldescriptor.bindGroupLayouts的结果。

  3. 如果以下任何条件不满足[生成验证错误],使 pl 无效,并停止。

  4. 设置 pl.[[bindGroupLayouts]]descriptor.bindGroupLayouts.

注:如果两个GPUPipelineLayout对象的内部[[bindGroupLayouts]]序列包含group-equivalentGPUBindGroupLayout对象,那么它们在任何用途上都被认为是等效的。

8.4. 示例

创建一个描述具有统一缓冲区、纹理和采样器的绑定的 GPUBindGroupLayout。 然后使用 GPUBindGroupLayout 创建一个 GPUBindGroup 和一个 GPUPipelineLayout
const bindGroupLayout = gpuDevice.createBindGroupLayout({
    entries: [{
        binding: 0,
        visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
        buffer: {}
    }, {
        binding: 1,
        visibility: GPUShaderStage.FRAGMENT,
        texture: {}
    }, {
        binding: 2,
        visibility: GPUShaderStage.FRAGMENT,
        sampler: {}
    }]
});

const bindGroup = gpuDevice.createBindGroup({
    layout: bindGroupLayout,
    entries: [{
        binding: 0,
        resource: { buffer: buffer },
    }, {
        binding: 1,
        resource: texture
    }, {
        binding: 2,
        resource: sampler
    }]
});

const pipelineLayout = gpuDevice.createPipelineLayout({
    bindGroupLayouts: [bindGroupLayout]
});

9. 着色器模块

9.1. GPUShaderModule

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUShaderModule {
    Promise<GPUCompilationInfo> getCompilationInfo();
};
GPUShaderModule includes GPUObjectBase;

GPUShaderModule 是对内部着色器模块对象的引用。

9.1.1. 着色器模块创建

dictionary GPUShaderModuleDescriptor
         : GPUObjectDescriptorBase {
    required USVString code;
    object sourceMap;
    record<USVString, GPUShaderModuleCompilationHint> hints;
};
code, of type USVString

着色器模块的 WGSL 源代码。

sourceMap, of type object

如果定义了,可以解释为source-map-v3格式。

源映射是可选的,但可以作为一种支持开发工具集成的标准化方式,例如源语言调试[SourceMap]。 源映射中的WGSL名称(标识符)遵循WGSL标识符对比中定义的规则。

hints, of type record<USVString, GPUShaderModuleCompilationHint>

如果定义了一个从着色器映射的入口点名称到一个 GPUShaderModuleCompilationHint。这些 GPUShaderModuleCompilationHint 上不执行任何验证。实现应该使用 GPUShaderModuleCompilationHint 中的任何信息来执行尽可能多的 createShaderModule() 内的编译。入口点名称遵循 WGSL 标识符比较 中定义的规则。

注:在 hints 中提供信息没有任何可观察的效果,除了性能。因为一个单独的着色器模块可以容纳多个入口点,并且可以从一个着色器模块创建多个管线,因此在 createShaderModule() 中一次性进行尽可能多的编译会比在多个调用 createComputePipeline() / createRenderPipeline() 中多次编译性能更高。

createShaderModule(descriptor)

创建一个 GPUShaderModule

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createShaderModule(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUShaderModuleDescriptor 要创建的 GPUShaderModule 的描述。

Returns: GPUShaderModule

内容时间线 步骤:

  1. 使 sm 为一个新的 GPUShaderModule 对象。

  2. this设备时间线 上发起 initialization steps

  3. 返回 sm

设备时间线 initialization steps:
  1. result 成为使用WGSL源的shader module creation的结果 descriptor.code

  2. 如果以下任何要求未得到满足,产生一个验证错误,使 sm 无效,然后返回。

问题(gpuweb/gpuweb#2308): 是否应允许在此处出现内部错误(uncategorized errors),还是应该将其延迟至管线创建?

问题:描述剩余的 createShaderModule() 验证和算法步骤。

注: 用户代理不应该在此处引发的验证错误的 message 文本中包含详细的编译器错误消息或着色器文本: 这些细节可以通过 getCompilationInfo() 访问。 用户代理应该为开发者提供便于调试的易于阅读、格式化的错误详细信息(例如浏览器开发者控制台中的警告,可以展开以显示完整的着色器源代码)。

由于着色器编译错误在生产应用程序中应该很少出现,用户代理可以选择无论错误处理如何(GPU错误范围或者 uncapturederror 事件处理器),都向开发者显示它们,例如作为可展开的警告。 如果没有这样做,它们应该提供并记录另一种方法供开发者访问易于阅读的错误详细信息,例如添加一个复选框以无条件显示错误, 或者在将 GPUCompilationInfo 对象记录到控制台时显示人类可读的详细信息。

从 WGSL 代码创建一个 GPUShaderModule
// A simple vertex and fragment shader pair that will fill the viewport with red.
const shaderSource = `
    var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
        vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));

    @vertex
    fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> @builtin(position) vec4<f32> {
        return vec4(pos[vertexIndex], 1.0, 1.0);
    }

    @fragment
    fn fragmentMain() -> @location(0) vec4<f32> {
        return vec4(1.0, 0.0, 0.0, 1.0);
    }
`;

const shaderModule = gpuDevice.createShaderModule({
    code: shaderSource,
});
9.1.1.1. 着色器模块编译提示

着色器模块编译提示是可选的、额外的信息,用来表示给定的 GPUShaderModule 入口点将来打算如何使用。对于某些实现来说,这些信息可能有助于更早地编译着色器模块,从而可能提高性能。

dictionary GPUShaderModuleCompilationHint {
    (GPUPipelineLayout or GPUAutoLayoutMode) layout;
};
layout, of type (GPUPipelineLayout or GPUAutoLayoutMode)

一个 GPUPipelineLayout,将来可以在 createComputePipeline()createRenderPipeline() 调用中与 GPUShaderModule 一起使用。 如果设置为 “auto”,则将使用与此提示关联的入口点的默认管线布局

注: 如果可能,作者应该同时向 createShaderModule()createComputePipeline() / createRenderPipeline() 提供相同的信息。

如果作者在调用 createShaderModule() 时无法提供提示信息,他们通常不应该延迟调用 createShaderModule();而应该从 hintsGPUShaderModuleCompilationHint 中省略未知信息。省略此信息可能导致编译被推迟到 createComputePipeline() / createRenderPipeline()

如果作者对传递给 createShaderModule() 的提示信息与稍后传递给 createComputePipeline() / createRenderPipeline() 的相同模块的信息是否匹配没有把握,他们应该避免将该信息传递给 createShaderModule(),因为将不匹配的信息传递给 createShaderModule() 可能导致不必要的编译发生。

9.1.2. 着色器模块编译信息

enum GPUCompilationMessageType {
    "error",
    "warning",
    "info"
};

[Exposed=(Window, DedicatedWorker), Serializable, SecureContext]
interface GPUCompilationMessage {
    readonly attribute DOMString message;
    readonly attribute GPUCompilationMessageType type;
    readonly attribute unsigned long long lineNum;
    readonly attribute unsigned long long linePos;
    readonly attribute unsigned long long offset;
    readonly attribute unsigned long long length;
};

[Exposed=(Window, DedicatedWorker), Serializable, SecureContext]
interface GPUCompilationInfo {
    readonly attribute FrozenArray<GPUCompilationMessage> messages;
};

一个 GPUCompilationMessage 是由 GPUShaderModule 编译器生成的信息、警告或错误消息。这些消息旨在以人类可读的方式帮助开发人员诊断与他们的着色器 code 有关的问题。每个消息可能对应着色器代码中的某个单点,着色器代码的子串,或者可能根本不对应代码中的任何特定点。

GPUCompilationMessage 具有以下属性:

message, of type DOMString, readonly

这个编译信息的可读、可本地化文本

注:message应遵循最佳实践,关于语言和方向信息。这包括利用任何未来可能出现的有关字符串语言和方向元数据的标准。

编者按: 在撰写本文时,尚未提供任何可以与旧版API兼容且一致的语言/方向建议,但一旦出现,应正式采纳。

type, of type GPUCompilationMessageType, readonly

消息的严重级别。

如果type"error",它对应一个shader-creation error

lineNum, of type unsigned long long, readonly

在着色器 code 中与 message 对应的行号。值为基于一的,这样 lineNum 为 1 表示着色器 code 的第一行。行由 line breaks 分隔。

如果 message 对应于一个子字符串,则指向子字符串开始的行。如果 message 与着色器 code 中的任何特定点都不对应,则必须为 0

linePos, of type unsigned long long, readonly

偏移量,以UTF-16代码单元为单位,从着色器 code 的行起始位置 lineNummessage 对应的点或子串起始位置。值是从1开始的,这样,linePos 的值 1 表示行的第一个代码单元。

如果 message 对应一个子串,则该指针指向子串的第一个UTF-16代码单元。如果 message 不对应着色器 code 中的任何特定点,则必须为 0

offset, of type unsigned long long, readonly

从着色器code开始的偏移量,以UTF-16代码单元为单位,到message相对应的点或子字符串的开始位置。必须引用与lineNumlinePos相同的位置。如果message不对应着色器code中的任何特定点,则必须为 0

length, of type unsigned long long, readonly

在与 message 对应的子字符串中的 UTF-16 编码单元数。 如果消息不对应子字符串,则 length 必须为 0。

注:GPUCompilationMessage.lineNumGPUCompilationMessage.linePos 都是从1开始的,因为它们最常见的用途预计是打印出可直接与许多文本编辑器中显示的行号和列号相关联的易于阅读的消息。

注:GPUCompilationMessage.offsetGPUCompilationMessage.length 可以传递给 substr() 以获取着色器 code 子字符串中与 message 相对应的部分。

getCompilationInfo()

返回 GPUShaderModule 编译期间生成的任何消息。

消息的位置、顺序和内容是实现定义的。 特别是,消息可能不会按 lineNum 排序。

Called on: GPUShaderModule this

Returns: Promise<GPUCompilationInfo>

内容时间线 步骤:

  1. 使 contentTimeline 为当前 内容时间线

  2. 使 promisea new promise.

  3. this设备时间线 上发起 synchronization steps

  4. 返回 promise

设备时间线 synchronization steps:
  1. 设备时间线 通知 thisshader module creation 已完成时:

    1. messages 是在 thisshader module creation 期间生成的任何错误、警告或信息性消息的列表。

    2. contentTimeline 上发布后续步骤。

内容时间线 步骤:
  1. 使 info 为一个新的 GPUCompilationInfo

  2. 对每个 messages 中的 message

    1. 使 m 为一个新的 GPUCompilationMessage

    2. 设置 m.messagemessage 的文本。

    3. 如果 message 为一个 shader-creation error:

      设置 m.type"error"

      如果 message 为一个警告:

      设置 m.type"warning"

      否则

      设置 m.type"info"

    4. 如果 message 与着色器 code 中的特定子字符串或位置相关联:
      1. m.lineNum 设置为消息引用的第一行的基于一的编号。

      2. m.linePos 设置为 m.lineNum 上消息引用的第一个基于一的 UTF-16 代码单元编号,如果 message 引用整行,则为 1。

      3. m.offset 设置为从着色器开头到 message 引用的子字符串或位置开头的 UTF-16 代码单元数。

      4. m.length 设置为 message 引用的子字符串的 UTF-16 代码单元长度,如果 message 引用一个位置,则为 0。

      否则:
      1. 设置 m.lineNum0

      2. 设置 m.linePos0

      3. 设置 m.offset0

      4. 设置 m.length0

    5. 添加 minfo.messages

  3. info 解析 promise

10. 管线

一个管线,无论是GPUComputePipeline还是GPURenderPipeline,代表了完成输入数据处理的整个功能,这些数据以绑定和顶点缓冲区的形式存在,并产生一些输出,如输出渲染目标中的颜色。

在结构上,pipeline 由一系列可编程阶段(着色器)和固定功能状态(如混合模式)组成。

注:在内部,根据目标平台,驱动程序可能会将一些固定功能状态转换为着色器代码,并将其与用户提供的着色器链接在一起。这种链接是对象作为一个整体创建的原因之一。

这种组合状态是作为一个单独的对象创建的(GPUComputePipelineGPURenderPipeline),并使用一个命令进行切换(分别为:GPUComputePassEncoder.setPipeline()GPURenderCommandsMixin.setPipeline())。

创建管线有两种方法:

immediate pipeline creation

createComputePipeline() and createRenderPipeline() 返回一个可以立即在 pass 编码器中使用的管线对象。

当此操作失败时,管线对象将无效,调用将产生一个validation error或一个internal error

注: 返回一个句柄对象是立即进行的,但实际的管线创建并不是同步的。如果管线创建需要很长时间,这可能会在创建调用和首次使用它的submit()之间的某个时间点引入设备时间表中的停顿。这个时间点没有明确规定,但最可能是以下之一:在创建时,在 setPipeline() 中第一次使用管线时,在相应的GPUCommandEncoderGPURenderBundleEncoder的finish()时,或在GPUCommandBuffersubmit()时。

async pipeline creation

createComputePipelineAsync() and createRenderPipelineAsync() 返回一个 Promise,当管线创建完成时,它将解析为一个管线对象。

当这个操作失败时,Promise会被一个GPUPipelineError拒绝。

GPUPipelineError 描述管线创建失败。

[Exposed=(Window, DedicatedWorker), SecureContext, Serializable]
interface GPUPipelineError : DOMException {
    constructor((DOMString or undefined) message, GPUPipelineErrorInit options);
    readonly attribute GPUPipelineErrorReason reason;
};

dictionary GPUPipelineErrorInit {
    required GPUPipelineErrorReason reason;
};

enum GPUPipelineErrorReason {
    "validation",
    "internal"
};

GPUPipelineError constructor:

constructor()
Arguments for the GPUPipelineError.constructor() method.
Parameter Type Nullable Optional Description
message (DOMString or undefined) 基础DOMException的错误信息。
options GPUPipelineErrorInit 针对GPUPipelineError的特定选项。
  1. 设置 this.name"GPUPipelineError"

  2. 设置 this.messagemessage ?? ""

  3. 设置 this.reasonoptions.reason

GPUPipelineError 具有以下参数:

reason, of type GPUPipelineErrorReason, readonly

一个只读的slot-backed attribute,以GPUPipelineErrorReason的形式展示管线创建过程中遇到的错误类型:

GPUPipelineError 对象是 serializable objects.

它们的序列化步骤,给定 valueserialized,如下:
  1. 根据给定的序列化,运行DOMException序列解步骤

他们的 deserialization steps,给定 valueserialized,如下:
  1. 运行 DOMException deserialization steps 给定 valueserialized

10.1. 基础管线

enum GPUAutoLayoutMode {
    "auto"
};

dictionary GPUPipelineDescriptorBase
         : GPUObjectDescriptorBase {
    required (GPUPipelineLayout or GPUAutoLayoutMode) layout;
};

interface mixin GPUPipelineBase {
    [NewObject] GPUBindGroupLayout getBindGroupLayout(unsigned long index);
};

GPUPipelineBase 具有以下内部插槽:

[[layout]], 类型为 GPUPipelineLayout

此资源布局的定义可以与 this 一起使用。

GPUPipelineBase 具有以下方法:

getBindGroupLayout(index)

获取与GPUPipelineBaseGPUBindGroupLayout兼容的 index 处的GPUBindGroupLayout

Called on: GPUPipelineBase this

Arguments:

Arguments for the GPUPipelineBase.getBindGroupLayout(index) method.
Parameter Type Nullable Optional Description
index unsigned long 管线布局的 [[bindGroupLayouts]] 序列的索引。

Returns: GPUBindGroupLayout

内容时间线 steps:

  1. 使 layout 为一个新的 GPUBindGroupLayout 对象。

  2. this设备时间线 上发起 initialization steps

  3. 返回 layout

设备时间线 initialization steps:
  1. 如果以下任何条件不满足,生成验证错误,使得layout 无效,并停止。

  1. 初始化 layout,使其成为 this.[[layout]].[[bindGroupLayouts]][index] 的副本。

注意: GPUBindGroupLayout 仅按值使用,而不是按引用使用,因此这等同于在新的包装器中返回相同的内部对象。每次返回一个新的 GPUBindGroupLayout 包装器是为了避免在 内容时间线设备时间线 之间进行往返。

10.1.1. 默认管线布局

一个创建时将 layout 设置为 “auto”GPUPipelineBase 对象,会创建并使用一个默认布局。

注:默认布局是为简单 pipeline 提供的便利,但在大多数情况下推荐使用显式布局。从默认布局创建的绑定组不能与其他 pipeline 一起使用,当改变着色器时,默认布局的结构可能会改变,从而导致意外的绑定组创建错误。

要为GPUPipelineBase pipeline创建一个默认管线布局,请执行以下步骤:

  1. groupCount 为 0。

  2. groupDescs 为一个有 device.[[limits]].maxBindGroups 个新的 GPUBindGroupLayoutDescriptor 对象的序列。

  3. 对于 groupDescs 中的每个 groupDesc

    1. groupDesc.entries 设置为一个空的 序列

  4. 对于用于创建 pipeline 的描述符中的每个 GPUProgrammableStage stageDesc

    1. shaderStagestageDesc.entryPointstageDesc.module 中的 GPUShaderStageFlags

    2. 对于 stageDesc 静态使用 的每个资源 resource

      1. group 成为 resource 的 "group" 装饰。

      2. binding 成为 resource 的 "binding" 装饰。

      3. entry 成为一个新的 GPUBindGroupLayoutEntry

      4. entry.binding 设置为 binding

      5. entry.visibility 设置为 shaderStage

      6. 如果 resource 用于采样器绑定:

        1. samplerLayout 为一个新的 GPUSamplerBindingLayout

        2. 设置 entry.samplersamplerLayout

      7. 如果 resource 用于比较采样器绑定:

        1. 创建一个新的 GPUSamplerBindingLayout,将其命名为 samplerLayout

        2. samplerLayouttype 设置为 "comparison"

        3. entrysampler 设置为 samplerLayout

      8. 如果 resource 用于缓冲区绑定:

        1. 创建一个新的 GPUBufferBindingLayout,将其命名为 bufferLayout

        2. bufferLayoutminBindingSize 设置为 resource最小缓冲区绑定尺寸

        3. 如果 resource 用于只读存储缓冲区:

          1. bufferLayouttype 设置为 "read-only-storage"

        4. 如果 resource 用于存储缓冲区:

          1. bufferLayouttype 设置为 "storage"

        5. entrybuffer 设置为 bufferLayout

      9. 如果 resource 是采样纹理绑定:

        1. textureLayout 为一个新的 GPUTextureBindingLayout

        2. 如果 resource 是深度纹理绑定:

          否则,如果 resource 的采样类型是:

          f32 并且存在一个带有 textureSample* 内置的 resourcestatic use

          textureLayout.sampleType 设置为 "float"

          f32 其他情况

          textureLayout.sampleType 设置为 "unfilterable-float"

          i32

          textureLayout.sampleType 设置为 "sint"

          u32

          textureLayout.sampleType 设置为 "uint"

        3. textureLayout.viewDimension 设置为 resource 的维度。

        4. 如果 resource 是多重采样纹理:

          1. textureLayout.multisampled 设置为 true

        5. 设置 entry.texturetextureLayout

      10. 如果resource适用于存储纹理绑定:

        1. storageTextureLayout为一个新的GPUStorageTextureBindingLayout

        2. 设置storageTextureLayout.formatresource的格式。

        3. 设置storageTextureLayout.viewDimensionresource的维度。

        4. 如果resource是一个只写存储纹理:

          1. 设置storageTextureLayout.access"write-only"

        5. 设置entry.storageTexturestorageTextureLayout

      11. 设置groupCount为max(groupCountgroup+1)。

      12. 如果 groupDescs[group] 有一个条目 previousEntry,其 binding 等于 binding :

        1. 如果 entrypreviousEntry 具有不同的 visibility :

          1. entry.visibility 中设置的位添加到 previousEntry.visibility

        2. 如果 resource 是一个缓冲区绑定,并且 entrybuffer.minBindingSize 大于 previousEntry :

          1. 设置 previousEntry.buffer.minBindingSizeentry.buffer.minBindingSize

        3. 如果 resource 是一个采样纹理绑定,并且 entrypreviousEntrytexture.sampleType 不同, 且 entrypreviousEntry 都具有 texture.sampleType"float""unfilterable-float" 类型:

          1. 设置 previousEntry.texture.sampleType"float"

        4. 如果 entrypreviousEntry 之间的任何其他属性不相等:

          1. 返回 null(这将导致管线的创建失败)。

      13. 其他情况

        1. entry 附加到 groupDescs[group]。

  5. 对于从 0 到 groupCount - 1(含)的每个 i

    1. groupDescgroupDescs[i]。

    2. bindGroupLayout 为调用 device.createBindGroupLayout()(groupDesc)的结果。

    3. bindGroupLayout.[[exclusivePipeline]] 设置为 pipeline

    4. bindGroupLayout 添加到 groupLayouts

  6. desc 为一个新的 GPUPipelineLayoutDescriptor

  7. desc.bindGroupLayouts 设置为 groupLayouts

  8. 返回 device.createPipelineLayout()(desc)。

10.1.2. GPUProgrammableStage

一个 GPUProgrammableStage 描述了用户提供的 GPUShaderModule 中控制 pipeline 的可编程阶段之一的入口点。入口点名称遵循 WGSL 标识符比较 中定义的规则。

dictionary GPUProgrammableStage {
    required GPUShaderModule module;
    required USVString entryPoint;
    record<USVString, GPUPipelineConstantValue> constants;
};

typedef double GPUPipelineConstantValue; // May represent WGSL’s bool, f32, i32, u32, and f16 if enabled.

GPUProgrammableStage 具有以下成员:

module, of type GPUShaderModule

包含该可编程阶段将执行的代码的GPUShaderModule

entryPoint, of type USVString

函数名称位于 module 中,此阶段将使用该函数来执行其工作。

constants, of type record<USVString, GPUPipelineConstantValue>

指定着色器模块 modulepipeline-overridable 常量的值。

每个这样的 pipeline-overridable 常量都由单个 pipeline-overridable constant identifier string 唯一标识 (表示常量的数字ID,如果有,则表示常量的标识符名称)。源代码映射中的 WGSL 名称(标识符)遵循 WGSL identifier comparison 中定义的规则。

每个键值对的键必须等于这样一个常量的标识符字符串。当管线被执行时,该常量将具有指定的值。

值指定为 GPUPipelineConstantValue, 这是一个 double。它们被转换为管线可覆盖常量的 to WGSL 类型 (bool/i32/u32/f32/f16)。如果转换失败,则会生成一个验证错误。

Pipeline-overridable constants defined in WGSL:
@id(0)      override has_point_light: bool = true;  // Algorithmic control.
@id(1200)   override specular_param: f32 = 2.3;     // Numeric control.
@id(1300)   override gain: f32;                     // Must be overridden.
            override width: f32 = 0.0;              // Specifed at the API level
                                                    //   using the name "width".
            override depth: f32;                    // Specifed at the API level
                                                    //   using the name "depth".
                                                    //   Must be overridden.
            override height = 2 * depth;            // The default value
                                                    // (if not set at the API level),
                                                    // depends on another
                                                    // overridable constant.

对应的JavaScript代码,仅提供所需的重写(没有默认值):

{
    // ...
    constants: {
        1300: 2.0,  // "gain"
        depth: -1,  // "depth"
    }
}

相应的 JavaScript 代码,覆盖所有常量:

{
    // ...
    constants: {
        0: false,   // "has_point_light"
        1200: 3.0,  // "specular_param"
        1300: 2.0,  // "gain"
        width: 20,  // "width"
        depth: -1,  // "depth"
        height: 15, // "height"
    }
}
validating GPUProgrammableStage(stage, descriptor, layout)

Arguments:

返回 true,如果满足以下所有条件:

返回值 false 对应于 管线创建错误

validating shader binding(binding, layout)

Arguments:

bindGroup 成为绑定组索引,bindIndex 成为着色器绑定声明 variable 的绑定索引。

如果满足以下所有条件,则返回 true

缓冲区绑定变量 var最小缓冲区绑定大小的计算如下:
  1. T 成为 var 的[= store type =]。

  2. 如果 T 是一个运行时大小的数组或包含一个运行时大小的数组,将 array<E> 替换为 array<E, 1>

    注:这确保总是有足够的内存存放一个元素,允许数组索引被限制在数组的长度范围内,从而在内存里进行访问。

  3. 返回 SizeOf(T)。

注: 强制执行此下限可确保通过缓冲区变量进行的读写仅访问缓冲区绑定区域内的内存位置。

资源绑定、管线可覆盖 常量、着色器阶段输入或着色器阶段输出 被认为是由 GPUProgrammableStage 静态使用, 如果它存在于指定的 entryPoint着色器阶段接口 中, 在指定的着色器 module 中。

10.2. GPUComputePipeline

一个GPUComputePipeline是一种控制计算着色器阶段的管线,可以在GPUComputePassEncoder中使用。

计算输入和输出都包含在绑定中,根据给定的GPUPipelineLayout。 输出对应于类型为“storage”buffer绑定,以及类型为“write-only”storageTexture绑定。

计算 管线 的阶段:

  1. Compute shader

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePipeline {
};
GPUComputePipeline includes GPUObjectBase;
GPUComputePipeline includes GPUPipelineBase;

10.2.1. 计算管线创建

一个 GPUComputePipelineDescriptor 描述了一个计算 pipeline。请参阅 § 23.2 计算 以获取更多详细信息。

dictionary GPUComputePipelineDescriptor
         : GPUPipelineDescriptorBase {
    required GPUProgrammableStage compute;
};

GPUComputePipelineDescriptor 具有以下成员:

compute, of type GPUProgrammableStage

描述 管线 的计算着色器入口点。

createComputePipeline(descriptor)

Creates a GPUComputePipeline using immediate pipeline creation.

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createComputePipeline(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUComputePipelineDescriptor 要创建的 GPUComputePipeline 的描述。

Returns: GPUComputePipeline

内容时间线 步骤:

  1. pipeline 成为一个新的 GPUComputePipeline 对象。

  2. thisDevice timeline 上发布 initialization steps

  3. 返回 pipeline

设备时间线 initialization steps:
  1. 如果 descriptor.layout“auto”,则让 layout 成为 pipeline 的新默认管线布局;否则,使用 descriptor.layout

  2. 如果以下任何条件不满足 生成验证错误,则使 pipeline 变为 invalid,并停止。

  3. pipeline.[[layout]] 设置为 layout

createComputePipelineAsync(descriptor)

创建一个使用 异步管线创建GPUComputePipeline。 当创建的管线准备好可立即使用时,返回的 Promise 将会解析。

如果管线创建失败,返回的 Promise 会以 GPUPipelineError 拒绝。

注:尽可能使用此方法,因为它可以阻止 queue timeline 在管线编译上的工作。

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createComputePipelineAsync(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUComputePipelineDescriptor Description of the GPUComputePipeline to create.

Returns: Promise<GPUComputePipeline>

内容时间线 步骤:

  1. contentTimeline 为当前的 内容时间线

  2. promisea new promise

  3. this设备时间线上发布 initialization steps

  4. 返回 promise

设备时间线 initialization steps:
  1. pipeline 为一个新的 GPUComputePipeline,创建方式如同调用 this.createComputePipeline() 并使用 descriptor

  2. pipeline 准备好被使用或已变为 无效 的,在 contentTimeline 上发出后续步骤。

Content timeline steps:
  1. If pipeline...

    有效

    pipeline 解析 promise

    由于 内部错误 而变为 无效

    拒绝 promise,并附带一个 GPUPipelineError,其中 reason 的值为 “internal”

    由于 验证错误 而变为 无效

    拒绝 promise,并附带一个 GPUPipelineError,其中 reason 的值为 “validation”

Creating a simple GPUComputePipeline:
const computePipeline = gpuDevice.createComputePipeline({
    layout: pipelineLayout,
    compute: {
        module: computeShaderModule,
        entryPoint: 'computeMain',
    }
});

10.3. GPURenderPipeline

一个GPURenderPipeline是一种管线,用于控制顶点和片段着色器阶段,并可以在GPURenderPassEncoderGPURenderBundleEncoder中使用。

渲染 管线 输入包括:

渲染 管线 输出包括:

渲染 管线 包括以下渲染阶段

  1. 顶点获取,由GPUVertexState.buffers控制

  2. 顶点着色器,由GPUVertexState控制

  3. 图元装配,由GPUPrimitiveState控制

  4. 栅格化,由GPUPrimitiveStateGPUDepthStencilStateGPUMultisampleState控制

  5. 片段着色器,由GPUFragmentState控制

  6. 模板测试及操作,由GPUDepthStencilState控制

  7. 深度测试及写操作,由GPUDepthStencilState控制

  8. 输出合并,由GPUFragmentState.targets控制

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPipeline {
};
GPURenderPipeline includes GPUObjectBase;
GPURenderPipeline includes GPUPipelineBase;

GPURenderPipeline 具有以下内部插槽:

[[descriptor]], 类型为 GPURenderPipelineDescriptor

GPURenderPipelineDescriptor 描述这个管线。

GPURenderPipelineDescriptor 的所有可选字段都已定义。

[[writesDepth]], 类型为 boolean

如果管线写入深度/模板附件的深度组件,则为真

[[writesStencil]], 类型为 boolean

如果管线写入深度/模板附件的模板组件,则为真

10.3.1. 渲染管线创建

一个GPURenderPipelineDescriptor通过配置每个渲染阶段来描述一个渲染管线。 有关更多详细信息,请参阅§ 23.3 渲染

dictionary GPURenderPipelineDescriptor
         : GPUPipelineDescriptorBase {
    required GPUVertexState vertex;
    GPUPrimitiveState primitive = {};
    GPUDepthStencilState depthStencil;
    GPUMultisampleState multisample = {};
    GPUFragmentState fragment;
};

GPURenderPipelineDescriptor 具有以下成员:

vertex, of type GPUVertexState

描述了 管线 的顶点着色器入口点及其输入缓冲区布局。

primitive, of type GPUPrimitiveState, defaulting to {}

描述与 管线 相关的原始属性。

depthStencil, of type GPUDepthStencilState

描述了可选的深度模板属性,包括测试、操作和偏差。

multisample, of type GPUMultisampleState, defaulting to {}

描述 管线 的多重采样属性。

fragment, of type GPUFragmentState

描述了 管线 的片段着色器入口点及其输出颜色。如果没有 提供,则启用 § 23.3.8 无颜色输出 模式。

createRenderPipeline(descriptor)

创建一个使用即时管道创建GPURenderPipeline

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createRenderPipeline(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPURenderPipelineDescriptor Description of the GPURenderPipeline to create.

Returns: GPURenderPipeline

内容时间线 步骤:

  1. 如果 descriptor.fragment提供

    1. 对于 descriptor.fragment.targets 中每个非 null 的 colorStateFor each 操作:

      1. 使用 this.[[device]],对 colorState.format 进行 ? 验证纹理格式所需特性

  2. 如果 descriptor.depthStencil提供

    1. 使用 this.[[device]],对 descriptor.depthStencil.format 进行 ? 验证纹理格式所需特性

  3. pipeline 成为一个新的 GPURenderPipeline 对象。

  4. this设备时间线 上发出 初始化步骤

  5. 返回 pipeline

设备时间线 initialization steps:
  1. 如果 descriptor.layout“auto”,则让 layout 成为 pipeline 的一个新的默认管道布局,否则为 descriptor.layout

  2. 如果任何以下条件不满足: 产生一个验证错误,使 pipeline 无效,并停止。

  3. descriptor设置为pipeline.[[descriptor]]

  4. pipeline.[[writesDepth]]设置为false。

  5. pipeline.[[writesStencil]]设置为false。

  6. depthStencildescriptor.depthStencil

  7. 如果depthStencil不为null:

    1. pipeline.[[writesDepth]]设置为depthStencil.depthWriteEnabled

    2. 如果depthStencil.stencilWriteMask不为 0

      1. stencilFrontdepthStencil.stencilFront

      2. stencilBackdepthStencil.stencilBack

      3. cullModedescriptor.primitive.cullMode

      4. 如果 cullMode 不是 “front”,且 stencilFront.passOpstencilFront.depthFailOpstencilFront.failOp 中的任何一个不是 “keep”

        1. pipeline.[[writesStencil]]设置为true。

      5. 如果 cullMode 不是 “back”,且 stencilBack.passOpstencilBack.depthFailOpstencilBack.failOp 中的任何一个不是 “keep”

        1. pipeline.[[writesStencil]]设置为true。

  8. pipeline.[[layout]]设置为layout

问题:需要渲染状态的描述。

createRenderPipelineAsync(descriptor)

创建一个使用 异步管道创建GPURenderPipeline。 当创建的管道准备好使用且无需额外延迟时,返回的 Promise 将解析。

如果管道创建失败,返回的 Promise 将以 GPUPipelineError 拒绝。

注:只要可能,就优先使用此方法,因为它可以防止在管道编译上阻塞 队列时间线 工作。

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createRenderPipelineAsync(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPURenderPipelineDescriptor Description of the GPURenderPipeline to create.

Returns: Promise<GPURenderPipeline>

内容时间线 步骤:

  1. contentTimeline 是当前的Content timeline

  2. promise 是一个新的 promise

  3. this设备时间线 上发布 initialization steps

  4. 返回 promise

设备时间线 initialization steps:
  1. pipeline 成为一个新的GPURenderPipeline,就像调用 this.createRenderPipeline() 并使用 descriptor 一样;

  2. pipeline 准备好使用或者变为 无效 时,在 contentTimeline 上发出后续步骤。

内容时间线 步骤:
  1. 如果 pipeline 为...

    有效

    pipeline 作为结果解决 promise

    无效 due to an internal error

    拒绝 promise ,并使用GPUPipelineError,其中reason“internal”

    无效 due to an validation error

    拒绝 promise,并带有一个 GPUPipelineError,其 reason“validation”

validating GPURenderPipelineDescriptor(descriptor, layout, device)

Arguments:

如果满足以下所有条件,则返回 true:

validating inter-stage interfaces(device, descriptor)

Arguments:

Returns: boolean

  1. maxVertexShaderOutputComponentsdevice.limits.maxInterStageShaderComponents.

    1. 如果 descriptor.primitive.topology“point-list”

      1. maxVertexShaderOutputComponents 减 1。

  2. 如果不满足以下任一要求,则返回 false:

    • descriptor.vertex 的所有用户自定义输出的标量组件总数不能超过 maxVertexShaderOutputComponents。 (例如,f32 输出占用1个组件,vec3 输出占用3个组件。)

    • descriptor.vertex 的每个用户自定义输出的 location 必须 小于 device.limits.maxInterStageShaderVariables.

  3. 如果 descriptor.fragment 存在

    1. maxFragmentShaderInputComponentsdevice.limits.maxInterStageShaderComponents.

      1. 如果 front_facing builtindescriptor.fragment 的输入:

        1. maxFragmentShaderInputComponents 减 1。

      2. 如果 sample_index builtindescriptor.fragment 的输入:

        1. maxFragmentShaderInputComponents 减 1。

      3. 如果 sample_mask builtindescriptor.fragment 的输入:

        1. maxFragmentShaderInputComponents 减 1。

    2. 如果不满足以下任一要求,则返回 false:

      • descriptor.fragment 的所有用户自定义输入的标量组件总数不能超过 maxFragmentShaderInputComponents

      • 对于 descriptor.fragment 的每个用户自定义输入,descriptor.vertex 的用户自定义输出必须具有与输入相同的 location、类型和插值(interpolation)。

      注:仅顶点的管道可以在顶点阶段具有用户自定义输出; 它们的值将被丢弃。

    3. Assert descriptor.fragment 的每个用户自定义输入的 location 小于 device.limits.maxInterStageShaderVariables (由上述规则得到结果)

  4. 返回 true

Creating a simple GPURenderPipeline:
const renderPipeline = gpuDevice.createRenderPipeline({
    layout: pipelineLayout,
    vertex: {
        module: shaderModule,
        entryPoint: 'vertexMain'
    },
    fragment: {
        module: shaderModule,
        entryPoint: 'fragmentMain',
        targets: [{
            format: 'bgra8unorm',
        }],
    }
});

10.3.2. 原始状态

dictionary GPUPrimitiveState {
    GPUPrimitiveTopology topology = "triangle-list";
    GPUIndexFormat stripIndexFormat;
    GPUFrontFace frontFace = "ccw";
    GPUCullMode cullMode = "none";

    // Requires "depth-clip-control" feature.
    boolean unclippedDepth = false;
};

GPUPrimitiveState具有以下成员,它们描述了GPURenderPipeline如何从其顶点输入构造和光栅化图元:

topology, of type GPUPrimitiveTopology, defaulting to "triangle-list"

要从顶点输入构造的图元类型。

stripIndexFormat, of type GPUIndexFormat

对于具有条带拓扑的管道 (“line-strip”“triangle-strip”), 这决定了索引缓冲格式和原始重启值 (“uint16”/0xFFFF 或 “uint32”/0xFFFFFFFF)。 不允许在具有非条带拓扑的管道上使用。

注:某些实现需要知道原始重启值以编译管道状态对象。

要将带有条带拓扑的管道与索引绘制调用一起使用 (drawIndexed()drawIndexedIndirect()), 必须设置这个值,并且它必须与绘制调用使用的索引缓冲格式匹配 (设置在 setIndexBuffer() 中)。

有关更多细节,请参阅§ 23.3.3 基本体部件

frontFace, of type GPUFrontFace, defaulting to "ccw"

定义哪些多边形被视为 front-facing

cullMode, of type GPUCullMode, defaulting to "none"

定义将剔除哪个多边形方向(如果有)。

unclippedDepth, of type boolean, defaulting to false

如果为真,则表示 depth clipping 已禁用。

需要启用 "depth-clip-control" 功能。

validating GPUPrimitiveState(descriptor, device) Arguments:

如果满足以下所有条件返回 true

enum GPUPrimitiveTopology {
    "point-list",
    "line-list",
    "line-strip",
    "triangle-list",
    "triangle-strip"
};

GPUPrimitiveTopology 定义使用 GPURenderPipeline 进行的原始类型绘制调用。 有关更多详细信息,请参见 § 23.3.5 光栅化

"point-list"

每个顶点定义一个点基元。

"line-list"

每对连续的两个顶点定义一个线基元。

"line-strip"

每对连续的两个顶点定义一个线图元。第一个顶点之后的每个顶点定义它和前一个顶点之间的线图元。

"triangle-list"

三个顶点的每个连续三元组定义一个三角形基元。

"triangle-strip"

前两个顶点之后的每个顶点在它和前两个顶点之间定义一个三角形基元。

enum GPUFrontFace {
    "ccw",
    "cw"
};

GPUFrontFace 定义哪些多边形被 GPURenderPipeline 视为 front-facing。 有关更多详细信息,请参见 § 23.3.5.4 多边形光栅化

"ccw"

顶点的帧缓冲区坐标按逆时针顺序给出的多边形被认为是 front-facing

"cw"

顶点的帧缓冲区坐标按顺时针顺序给出的多边形被认为是 front-facing

enum GPUCullMode {
    "none",
    "front",
    "back"
};

GPUPrimitiveTopology 定义哪些多边形将被使用 GPURenderPipeline 进行的绘制调用剔除。 有关更多详细信息,请参见 § 23.3.5.4 多边形光栅化

"none"

没有多边形被丢弃。

"front"

Front-facing 多边形被丢弃。

"back"

Back-facing 多边形被丢弃。

注:GPUFrontFaceGPUCullMode"point-list""line-list""line-strip" 拓扑没有影响。

10.3.3. 多样本状态

dictionary GPUMultisampleState {
    GPUSize32 count = 1;
    GPUSampleMask mask = 0xFFFFFFFF;
    boolean alphaToCoverageEnabled = false;
};

GPUMultisampleState具有以下成员,这些成员描述了GPURenderPipeline如何与渲染传递的多重采样附件进行交互。

count, of type GPUSize32, defaulting to 1

每像素的样本数量。这个 GPURenderPipeline 只与具有匹配 sampleCount 的附件纹理(colorAttachmentsdepthStencilAttachment)兼容。

mask, of type GPUSampleMask, defaulting to 0xFFFFFFFF

掩码确定要写入的样本。

alphaToCoverageEnabled, of type boolean, defaulting to false

true 表示应使用片段的 alpha 通道生成样本覆盖遮罩。

validating GPUMultisampleState(descriptor) Arguments:

如果满足以下所有条件,则返回 true

10.3.4. 片元状态

dictionary GPUFragmentState
         : GPUProgrammableStage {
    required sequence<GPUColorTargetState?> targets;
};
validating GPUFragmentState(GPUDevice device, GPUFragmentState descriptor)

如果满足以下所有要求,则返回 true

Validating GPUFragmentState’s color attachment bytes per sample(GPUDevice device, sequence<GPUColorTargetState?> targets)
  1. formats 成为空的 list<GPUTextureFormat?>

  2. 对于 targets 中的每个 target:

    1. 如果 target 为 undefined,则继续。

    2. target.format 追加formats

  3. 计算每个采样的颜色附件字节数 必须 ≤ device.[[limits]].maxColorAttachmentBytesPerSample

Note: 注: 片元着色器输出的值可能比管线使用的值多。如果出现这种情况,多余的值将被忽略。

如果满足以下要求,component 就是一个 有效的 GPUBlendComponent

10.3.5. 颜色目标状态

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), SecureContext]
namespace GPUColorWrite {
    const GPUFlagsConstant RED   = 0x1;
    const GPUFlagsConstant GREEN = 0x2;
    const GPUFlagsConstant BLUE  = 0x4;
    const GPUFlagsConstant ALPHA = 0x8;
    const GPUFlagsConstant ALL   = 0xF;
};
10.3.5.1. 混合状态
dictionary GPUBlendComponent {
    GPUBlendOperation operation = "add";
    GPUBlendFactor srcFactor = "one";
    GPUBlendFactor dstFactor = "zero";
};

GPUBlendComponent 有以下成员,它们描述了片段的颜色或 alpha 分量是如何混合的:

operation, of type GPUBlendOperation, defaulting to "add"

定义用于计算写入目标附件组件的值的 GPUBlendOperation

srcFactor, of type GPUBlendFactor, defaulting to "one"

定义要对来自片段着色器的值执行的 GPUBlendFactor 操作。

dstFactor, of type GPUBlendFactor, defaulting to "zero"

定义要对目标附件中的值执行的 GPUBlendFactor 操作。

下表使用此表示法来描述给定片段位置的颜色分量:

RGBAsrc Color output by the fragment shader for the color attachment. 如果着色器不返回 alpha 通道,则不能使用 src-alpha 混合因子。
RGBAdst Color currently in the color attachment. 缺少绿色/蓝色/alpha 通道分别默认为“0、0、1”。
RGBAconst 当前 [[blendConstant]].
RGBAsrcFactor 源混合因子组件,由 srcFactor 定义。
RGBAdstFactor 目标混合因子组件,由 dstFactor 定义。
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"
};

GPUBlendFactor 定义了源或目标混合因子的计算方法:

GPUBlendFactor Blend factor RGBA components
"zero" (0, 0, 0, 0)
"one" (1, 1, 1, 1)
"src" (Rsrc, Gsrc, Bsrc, Asrc)
"one-minus-src" (1 - Rsrc, 1 - Gsrc, 1 - Bsrc, 1 - Asrc)
"src-alpha" (Asrc, Asrc, Asrc, Asrc)
"one-minus-src-alpha" (1 - Asrc, 1 - Asrc, 1 - Asrc, 1 - Asrc)
"dst" (Rdst, Gdst, Bdst, Adst)
"one-minus-dst" (1 - Rdst, 1 - Gdst, 1 - Bdst, 1 - Adst)
"dst-alpha" (Adst, Adst, Adst, Adst)
"one-minus-dst-alpha" (1 - Adst, 1 - Adst, 1 - Adst, 1 - Adst)
"src-alpha-saturated" (min(Asrc, 1 - Adst), min(Asrc, 1 - Adst), min(Asrc, 1 - Adst), 1)
"constant" (Rconst, Gconst, Bconst, Aconst)
"one-minus-constant" (1 - Rconst, 1 - Gconst, 1 - Bconst, 1 - Aconst)
enum GPUBlendOperation {
    "add",
    "subtract",
    "reverse-subtract",
    "min",
    "max"
};

GPUBlendOperation 定义用于组合源和目标混合因子的算法:

GPUBlendOperation RGBA Components
"add" RGBAsrc × RGBAsrcFactor + RGBAdst × RGBAdstFactor
"subtract" RGBAsrc × RGBAsrcFactor - RGBAdst × RGBAdstFactor
"reverse-subtract" RGBAdst × RGBAdstFactor - RGBAsrc × RGBAsrcFactor
"min" min(RGBAsrc, RGBAdst)
"max" max(RGBAsrc, RGBAdst)

10.3.6. 深度/模板状态

dictionary GPUDepthStencilState {
    required GPUTextureFormat format;

    required boolean depthWriteEnabled;
    required GPUCompareFunction depthCompare;

    GPUStencilFaceState stencilFront = {};
    GPUStencilFaceState stencilBack = {};

    GPUStencilValue stencilReadMask = 0xFFFFFFFF;
    GPUStencilValue stencilWriteMask = 0xFFFFFFFF;

    GPUDepthBias depthBias = 0;
    float depthBiasSlopeScale = 0;
    float depthBiasClamp = 0;
};

GPUDepthStencilState 具有以下成员,描述了 GPURenderPipeline 将如何影响渲染过程的 depthStencilAttachment

format, of type GPUTextureFormat

GPURenderPipeline 将与 depthStencilAttachmentformat 兼容。

depthWriteEnabled, of type boolean

指示这个 GPURenderPipeline 是否可以修改 depthStencilAttachment 深度值。

depthCompare, of type GPUCompareFunction

用于测试片元深度与 depthStencilAttachment 深度值的比较操作。

stencilFront, of type GPUStencilFaceState, defaulting to {}

定义了如何为朝前的图元执行模板比较和操作。

stencilBack, of type GPUStencilFaceState, defaulting to {}

定义了如何为朝后的图元执行模板比较和操作。

stencilReadMask, of type GPUStencilValue, defaulting to 0xFFFFFFFF

掩码控制在执行模板比较测试时读取哪些 depthStencilAttachment 模板值位。

stencilWriteMask, of type GPUStencilValue, defaulting to 0xFFFFFFFF

Bitmask controlling which depthStencilAttachment stencil value bits are written to when performing stencil operations.

depthBias, of type GPUDepthBias, defaulting to 0

添加到每个片元的恒定深度偏差。有关详细信息,请参阅 biased fragment depth

depthBiasSlopeScale, of type float, defaulting to 0

与片元的斜率成比例的深度偏差。有关详细信息,请参阅 biased fragment depth

depthBiasClamp, of type float, defaulting to 0

片元的最大深度偏差。有关详细信息,请参阅 biased fragment depth

对于正在写入至 depthStencilAttachment 附件 的片段,当使用 GPUDepthStencilState 状态 绘制时,biased fragment depth 的计算过程为:
  1. formatattachment.view.format

  2. r 为在转换为32位浮点值时 格式 中最小的正可表示值(大于 0)。

  3. maxDepthSlope 为片段深度值的水平和垂直斜率中的最大值。

  4. 如果 format 是 unorm 格式:

    1. bias(float)状态.depthBias * r + state.depthBiasSlopeScale * maxDepthSlope.

  5. 否则,如果 format 是一个 float 格式:

    1. bias(float)状态.depthBias * 2^(exp(原始最大深度) - r) + state.depthBiasSlopeScale * maxDepthSlope

  6. 如果 state.depthBiasClamp > 0:

    1. bias 设置为 min(状态.depthBiasClamp, bias)

  7. 否则,如果 state.depthBiasClamp < 0:

    1. bias 设置为 max(状态.depthBiasClamp, bias)

  8. 如果 state.depthBias ≠ 0 或者 state.depthBiasSlopeScale ≠ 0:

    1. 将片元深度值设置为 片段深度值 + bias

validating GPUDepthStencilState(descriptor)

Arguments:

只有当满足以下所有条件时,返回 true

问题:这个算法可以如何支持在扩展中添加的 depth/stencil 格式?

dictionary GPUStencilFaceState {
    GPUCompareFunction compare = "always";
    GPUStencilOperation failOp = "keep";
    GPUStencilOperation depthFailOp = "keep";
    GPUStencilOperation passOp = "keep";
};

GPUStencilFaceState具有以下成员,它们描述了模板比较和操作是如何执行的: