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具有以下成员,它们描述了模板比较和操作是如何执行的:

compare, of type GPUCompareFunction, defaulting to "always"

在测试片元与 depthStencilAttachment 模板值时使用的 GPUCompareFunction

failOp, of type GPUStencilOperation, defaulting to "keep"

如果片元模板比较测试(由 compare 描述)失败,则执行的 GPUStencilOperation

depthFailOp, of type GPUStencilOperation, defaulting to "keep"

如果由 depthCompare 描述的片元深度比较失败,则执行的 GPUStencilOperation

passOp, of type GPUStencilOperation, defaulting to "keep"

如果片元模板比较测试通过,则执行由compare描述的GPUStencilOperation

enum GPUStencilOperation {
    "keep",
    "zero",
    "replace",
    "invert",
    "increment-clamp",
    "decrement-clamp",
    "increment-wrap",
    "decrement-wrap"
};

GPUStencilOperation 定义了以下操作:

"keep"

保持当前模板值。

"zero"

将模板值设置为“0”。

"replace"

将模板值设置为 [[stencilReference]]

"invert"

按位反转当前模板值。

"increment-clamp"

增加当前模板值,限制为 depthStencilAttachment 模板方面的最大可表示值。

"decrement-clamp"

减少当前模板值,钳位到 0

"increment-wrap"

将当前模板值递增,如果该值超过了 depthStencilAttachment 的模板方面可以表示的最大值,则将其回绕至零。

"decrement-wrap"

将当前模板值递减,如果该值低于 0 则回绕到depthStencilAttachment的模板方面的最大可表示值。

10.3.7. 顶点状态

enum GPUIndexFormat {
    "uint16",
    "uint32"
};

索引格式决定了缓冲区中索引值的数据类型,以及在带strip原语拓扑结构 (“line-strip”“triangle-strip”) 中使用时指定原语重启值。原语重启值指示哪个索引值表示应开始新的原语,而不是继续使用之前索引的顶点构建三角形条带。

指定strip原语拓扑结构的GPUPrimitiveState必须在用于索引绘制时指定stripIndexFormat,以便在管道创建时知道将用于重启的primitive restart value。指定列表原语拓扑结构的GPUPrimitiveState将在进行索引渲染时使用传递给 setIndexBuffer() 的索引格式。

Index format Byte size Primitive restart value
"uint16" 2 0xFFFF
"uint32" 4 0xFFFFFFFF
10.3.7.1. 顶点格式

顶点属性的GPUVertexFormat指示如何从顶点缓冲区解释数据并将其暴露给着色器。格式名称指定组件的顺序,每个组件的位数以及组件的顶点数据类型

每个顶点数据类型都可以映射到具有相同基本类型的任何WGSL标量类型,而无论组件的位数如何:

Vertex format prefix Vertex data type Compatible WGSL types
uint unsigned int u32
sint signed int i32
unorm unsigned normalized f16, f32
snorm signed normalized
float floating point

多组件格式在“x”后指定组件的数量。顶点格式和着色器类型之间的组件数量不匹配是允许的,通过丢弃组件或填充默认值来进行补偿。

顶点属性具有“unorm8x2”格式和字节值 [0x7F, 0xFF],可以通过以下类型在着色器中访问:
Shader type Shader value
f16 0.5h
f32 0.5f
vec2<f16> vec2(0.5h, 1.0h)
vec2<f32> vec2(0.5f, 1.0f)
vec3<f16> vec2(0.5h, 1.0h, 0.0h)
vec3<f32> vec2(0.5f, 1.0f, 0.0f)
vec4<f16> vec2(0.5h, 1.0h, 0.0h, 1.0h)
vec4<f32> vec2(0.5f, 1.0f, 0.0f, 1.0f)

请参阅§ 23.3.2 顶点处理以获取关于如何在着色器中显示顶点格式的更多信息。

enum GPUVertexFormat {
    "uint8x2",
    "uint8x4",
    "sint8x2",
    "sint8x4",
    "unorm8x2",
    "unorm8x4",
    "snorm8x2",
    "snorm8x4",
    "uint16x2",
    "uint16x4",
    "sint16x2",
    "sint16x4",
    "unorm16x2",
    "unorm16x4",
    "snorm16x2",
    "snorm16x4",
    "float16x2",
    "float16x4",
    "float32",
    "float32x2",
    "float32x3",
    "float32x4",
    "uint32",
    "uint32x2",
    "uint32x3",
    "uint32x4",
    "sint32",
    "sint32x2",
    "sint32x3",
    "sint32x4"
};
Vertex format Data type Components Byte size Example WGSL type
"uint8x2" unsigned int 2 2 vec2<u32>
"uint8x4" unsigned int 4 4 vec4<u32>
"sint8x2" signed int 2 2 vec2<i32>
"sint8x4" signed int 4 4 vec4<i32>
"unorm8x2" unsigned normalized 2 2 vec2<f32>
"unorm8x4" unsigned normalized 4 4 vec4<f32>
"snorm8x2" signed normalized 2 2 vec2<f32>
"snorm8x4" signed normalized 4 4 vec4<f32>
"uint16x2" unsigned int 2 4 vec2<u32>
"uint16x4" unsigned int 4 8 vec4<u32>
"sint16x2" signed int 2 4 vec2<i32>
"sint16x4" signed int 4 8 vec4<i32>
"unorm16x2" unsigned normalized 2 4 vec2<f32>
"unorm16x4" unsigned normalized 4 8 vec4<f32>
"snorm16x2" signed normalized 2 4 vec2<f32>
"snorm16x4" signed normalized 4 8 vec4<f32>
"float16x2" float 2 4 vec2<f16>
"float16x4" float 4 8 vec4<f16>
"float32" float 1 4 f32
"float32x2" float 2 8 vec2<f32>
"float32x3" float 3 12 vec3<f32>
"float32x4" float 4 16 vec4<f32>
"uint32" unsigned int 1 4 u32
"uint32x2" unsigned int 2 8 vec2<u32>
"uint32x3" unsigned int 3 12 vec3<u32>
"uint32x4" unsigned int 4 16 vec4<u32>
"sint32" signed int 1 4 i32
"sint32x2" signed int 2 8 vec2<i32>
"sint32x3" signed int 3 12 vec3<i32>
"sint32x4" signed int 4 16 vec4<i32>
enum GPUVertexStepMode {
    "vertex",
    "instance"
};

步进模式配置如何基于当前顶点或实例索引计算顶点缓冲数据的地址:

"vertex"

地址根据每个顶点的arrayStride进行递增,并在实例之间重置。

"instance"

地址对于每个实例会根据arrayStride进行增加。

dictionary GPUVertexState
         : GPUProgrammableStage {
    sequence<GPUVertexBufferLayout?> buffers = [];
};

一个顶点缓冲区在概念上是一个缓冲区内存的视图,作为一个结构数组。 arrayStride 是该数组元素之间的字节跨度。 顶点缓冲区的每个元素都类似于一个具有由其 attributes 定义的内存布局的结构,该布局描述结构的元素。

每个 GPUVertexAttribute 都描述了其 format 以及其在结构中的 offset,按字节为单位。

每个属性在顶点着色器中都作为单独的输入,每个输入都由数字 location 绑定,location 由 shaderLocation 指定。 GPUVertexState 中的每个 location 都必须是唯一的。

dictionary GPUVertexBufferLayout {
    required GPUSize64 arrayStride;
    GPUVertexStepMode stepMode = "vertex";
    required sequence<GPUVertexAttribute> attributes;
};
arrayStride, of type GPUSize64

此数组元素之间的跨度(以字节为单位)。

stepMode, of type GPUVertexStepMode, defaulting to "vertex"

该数组的每个元素表示每个顶点数据还是每个实例数据

attributes, of type sequence<GPUVertexAttribute>

定义每个元素内顶点属性布局的数组。

dictionary GPUVertexAttribute {
    required GPUVertexFormat format;
    required GPUSize64 offset;

    required GPUIndex32 shaderLocation;
};
format, of type GPUVertexFormat

属性的 GPUVertexFormat

offset, of type GPUSize64

从元素开头到属性数据的偏移量(以字节为单位)。

shaderLocation, of type GPUIndex32

与此属性关联的数字位置,将对应于在 vertex.module 中声明的 "@location" 属性

validating GPUVertexBufferLayout(device, descriptor, vertexStage)

Arguments:

仅当满足以下所有条件时返回 true

validating GPUVertexState(device, descriptor)

Arguments:

返回 true,当且仅当满足以下所有条件:

11. 副本

11.1. 缓冲副本

缓冲区复制操作操作原始字节。

WebGPU 提供 "缓冲" 的 GPUCommandEncoder 命令:

还有 “即时” GPUQueue 操作:

11.2. 图片副本

图像复制操作对纹理/“图像”数据进行操作,而不是字节。

WebGPU 为 GPUCommandEncoder 命令提供了“缓冲”:

以及“即时”GPUQueue 操作:

有些像素值具有多种可能的表示形式,例如,作为 r8snorm,-1.0 可以表示为 -127 或 -128。复制命令不能保证保留源的位表示。

以下定义由这些方法使用。

11.2.1. GPUImageDataLayout

dictionary GPUImageDataLayout {
    GPUSize64 offset = 0;
    GPUSize32 bytesPerRow;
    GPUSize32 rowsPerImage;
};

GPUImageDataLayout 是线性内存中 图像 的布局。 在将数据从 纹理 复制到 GPUBuffer 之间,或是安排从 GPUQueue 中写入 texture 时使用。

问题:更精确地定义图像。特别是将它们定义为由 纹素块 组成。

在字节数组和纹理之间复制的操作始终处理 纹素块 的行,我们将其称为 块行。不可能仅更新 纹素块 的一部分。

在图像复制的线性内存布局中,每个 块行 内的 纹素块 紧密排列,每个随后的 texel block 紧跟在前一个 texel block 后面,没有填充。 这包括将 depth-or-stencil format 纹理的特定方面进行复制: 模具值紧密包装在一组字节中; 深度值紧密包装在适当类型的数组中(“depth16unorm”或“depth32float”)。

问题:通过引用复制方法共享的常见算法来定义确切的复制语义。

offset, of type GPUSize64, defaulting to 0

偏移量(以字节为单位),从图像数据源(如 GPUImageCopyBuffer.buffer)的开头到该源内图像数据开始的偏移量。

bytesPerRow, of type GPUSize32

在字节中,从每个块行的开始到随后的块行之间的步幅。

如果有多个块行(即,复制高度或深度大于一个块),则需要。

rowsPerImage, of type GPUSize32

图像数量块行每个纹理的单个图像rowsPerImage × bytesPerRow 是数据的每个 图像 之间和后续图像之间的字节间距。

如果有多个 图像(即复制深度大于1),则需要。

11.2.2. GPUImageCopyBuffer

image copy操作中,GPUImageCopyBuffer定义了一个GPUBuffer,并结合 copySize,说明了图像数据如何在缓冲区的内存中布局(参见GPUImageDataLayout)。

dictionary GPUImageCopyBuffer
         : GPUImageDataLayout {
    required GPUBuffer buffer;
};
buffer, of type GPUBuffer

一个缓冲区,它可以包含要复制的图像数据,也可以存储正在复制的图像数据,具体取决于传递给它的方法。

validating GPUImageCopyBuffer

Arguments:

Returns: boolean

当且仅当满足以下所有条件时返回 true

11.2.3. GPUImageCopyTexture

image copy操作中,GPUImageCopyTexture 定义了一个 GPUTexture,并与 copySize 一起定义了纹理的子区域(跨一个或多个连续的 纹理子资源,在相同的 mip-map 级别上)。

dictionary GPUImageCopyTexture {
    required GPUTexture texture;
    GPUIntegerCoordinate mipLevel = 0;
    GPUOrigin3D origin = {};
    GPUTextureAspect aspect = "all";
};
texture, of type GPUTexture

要复制到/从中复制的纹理。

mipLevel, of type GPUIntegerCoordinate, defaulting to 0

要复制到/从中复制的 texture 的 Mip-map 级别。

origin, of type GPUOrigin3D, defaulting to {}

定义复制的起点 - 将要复制到/从的纹理子区域的最小角。与 copySize 一起, 定义完整的复制子区域。

aspect, of type GPUTextureAspect, defaulting to "all"

定义从 texture 复制到/从哪些方面的内容。

validating GPUImageCopyTexture

Arguments:

Returns: boolean

  1. blockWidthimageCopyTexture .texture.formattexel block width

  2. blockHeightimageCopyTexture .texture.formattexel block height

  3. 当且仅当满足以下所有条件时返回 true:

问题(gpuweb/gpuweb#69):定义具有1d3d纹理的副本。

11.2.4. GPUImageCopyTextureTagged

WebGPU 纹理包含原始数值数据,不带有描述颜色的语义元数据。然而,copyExternalImageToTexture() 从描述颜色的资源中复制。

GPUImageCopyTextureTagged 是一个带有颜色空间/编码和 alpha 预乘元数据的 GPUImageCopyTexture,这样语义颜色数据在复制过程中可以保留。这个元数据仅影响 copyExternalImageToTexture() 操作的语义,而不影响目标纹理的语义。

dictionary GPUImageCopyTextureTagged
         : GPUImageCopyTexture {
    PredefinedColorSpace colorSpace = "srgb";
    boolean premultipliedAlpha = false;
};
colorSpace, of type PredefinedColorSpace, defaulting to "srgb"

描述用于将数据编码到目标纹理中的颜色空间和编码方式。

可能导致 在范围 [0, 1] 之外的值 被写入目标纹理, 如果其格式可以表示它们。 否则,结果将被截断到目标纹理格式的范围。

注意: 如果 colorSpace 与源图像相匹配, 则可能不需要转换。请参见§ 3.10.2 色彩空间转换省略

premultipliedAlpha, of type boolean, defaulting to false

描述数据写入纹理时,是否应该将其 RGB 通道预先乘以 alpha 通道。

如果此选项设置为 true,并且 source 也预先乘以 alpha,则即使源 RGB 值超过其相应的 alpha 值,也必须保留这些值。

注意: 如果 premultipliedAlpha 与源图像匹配,则可能不需要进行转换。请参阅§ 3.10.2 色彩空间转换省略

11.2.5. GPUImageCopyExternalImage

dictionary GPUImageCopyExternalImage {
    required (ImageBitmap or HTMLVideoElement or HTMLCanvasElement or OffscreenCanvas) source;
    GPUOrigin2D origin = {};
    boolean flipY = false;
};

GPUImageCopyExternalImage has the following members:

source, of type (ImageBitmap or HTMLVideoElement or HTMLCanvasElement or OffscreenCanvas)

image copy的来源。在发出 copyExternalImageToTexture() 时捕获复制源数据。来源大小由来源类型定义,如下表所示:

Source type Width Height
ImageBitmap ImageBitmap.width ImageBitmap.height
HTMLVideoElement intrinsic width of the frame intrinsic height of the frame
HTMLCanvasElement HTMLCanvasElement.width HTMLCanvasElement.height
OffscreenCanvas OffscreenCanvas.width OffscreenCanvas.height
origin, of type GPUOrigin2D, defaulting to {}

定义副本的来源 - 要从中复制的源子区域的最小(左上)角。 结合 copySize,定义完整的复制子区域。

flipY, of type boolean, defaulting to false

描述源图像是否垂直翻转。

如果此选项设置为 true,副本将垂直翻转:源区域的底部行被复制到目标区域的第一行,依此类推。 origin选项仍然相对于源图像的左上角,向下增加。

11.2.6. Subroutines

imageCopyTexture subresource size

Arguments:

Returns: GPUExtent3D

imageCopyTextureimageCopyTexture 子资源大小 计算如下:

widthheightdepthOrArrayLayers 分别是 imageCopyTexture 的宽度、高度和深度,对应于 imageCopyTexture.texture 子资源mipmap 级别 imageCopyTexture.mipLevel物理 miplevel 特定纹理范围

validating linear texture data(layout, byteSize, format, copyExtent)

Arguments:

GPUImageDataLayout layout

线性纹理数据的布局。

GPUSize64 byteSize

线性数据的总大小,以字节为单位。

GPUTextureFormat format

纹理的格式。

GPUExtent3D copyExtent

要复制的纹理范围。

  1. 让:

  2. 如果以下输入验证要求不满足,则失败:

  3. 让:

    注意:这些默认值没有效果,因为它们总是乘以0。

  4. requiredBytesInCopy 等于 0。

  5. 如果 copyExtent.depthOrArrayLayers > 0:

    1. 使 requiredBytesInCopy 增加 bytesPerRow 乘以 rowsPerImage 乘以 (copyExtent.depthOrArrayLayers - 1)。

    2. 如果 heightInBlocks > 0:

      1. 使 requiredBytesInCopy 增加 bytesPerRow 乘以 (heightInBlocks - 1) + bytesInLastRow

  6. 如果以下条件不满足,则失败:

    • 布局适应线性数据:layout.offset + requiredBytesInCopybyteSize

validating texture copy range

Arguments:

GPUImageCopyTexture imageCopyTexture

复制到的纹理子资源和复制起点。

GPUExtent3D copySize

纹理的大小。

  1. blockWidthimageCopyTexturetexel block widthtexture.format.

  2. blockHeightimageCopyTexturetexel block heighttexture.format.

  3. subresourceSizeimageCopyTextureimageCopyTexture subresource size

  4. 如果满足以下所有条件,则返回:

两个 GPUTextureFormat format1format2兼容复制 的如果:
纹理拷贝的子资源集合imageCopyTexturecopySize)是满足以下条件的texture = imageCopyTexture.texture的子资源的子集:

12. 命令缓冲区

命令缓冲区是可以提交给GPUQueue执行的预先记录的GPU命令列表。 每个GPU命令代表在GPU上要执行的任务,例如设置状态,绘制,复制资源等。

一个GPUCommandBuffer只能提交一次,提交后它会变为无效。 要在多个提交中重复使用渲染命令,请使用GPURenderBundle

12.1. GPUCommandBuffer

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

GPUCommandBuffer 具有以下内部插槽:

[[command_list]], of type list<GPU command>

要在提交此命令缓冲区时在 Queue timeline 上执行的 GPU commands 列表。

[[renderState]], of type RenderState

当前由任何正在执行的渲染通道命令使用的状态,初始值为 null

12.1.1. 命令缓冲区创建

dictionary GPUCommandBufferDescriptor
         : GPUObjectDescriptorBase {
};

13. 命令编码

13.1. GPUCommandsMixin

GPUCommandsMixin 定义了所有编码命令接口的公共状态。它没有方法。

interface mixin GPUCommandsMixin {
};

GPUCommandsMixin将以下内部插槽添加到包含它的接口中:

[[state]], 类型为 encoder state

当前编码器的状态,初始设置为“open”。

[[commands]], of type list<GPU command>

要在包含这些命令的GPUCommandBuffer提交时在队列时间线上执行的GPU command列表。

编码器状态可能是以下几种之一:

"open"

编码器可以用来编码新的命令。

"locked"

编码器无法使用,因为它被子编码器锁定:它是一个 GPUCommandEncoder,并且 GPURenderPassEncoderGPUComputePassEncoder 是活动的。 当 pass 结束时,编码器再次变为"open"。

在此状态下发出的任何命令都会使编码器 无效

"ended"

编码器已经结束,无法再对新命令进行编码。

在此状态下发出的所有命令都将产生验证错误

验证GPUCommandsMixin encoder的编码器状态:

如果 encoder.[[state]] 为:

"open"

返回 true.

"locked"

使 encoder 无效, 且返回 false

"ended"

产生一个校验错误, 且返回 false

要在 GPUCommandsMixin encoder添加一个命令,该命令执行 GPU Command command 的步骤:
  1. command 添加到 encoder.[[commands]]

  2. command 作为 GPUCommandBuffer 的一部分执行时:

    1. 执行 command 的步骤。

13.2. GPUCommandEncoder

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCommandEncoder {
    GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
    GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});

    undefined copyBufferToBuffer(
        GPUBuffer source,
        GPUSize64 sourceOffset,
        GPUBuffer destination,
        GPUSize64 destinationOffset,
        GPUSize64 size);

    undefined copyBufferToTexture(
        GPUImageCopyBuffer source,
        GPUImageCopyTexture destination,
        GPUExtent3D copySize);

    undefined copyTextureToBuffer(
        GPUImageCopyTexture source,
        GPUImageCopyBuffer destination,
        GPUExtent3D copySize);

    undefined copyTextureToTexture(
        GPUImageCopyTexture source,
        GPUImageCopyTexture destination,
        GPUExtent3D copySize);

    undefined clearBuffer(
        GPUBuffer buffer,
        optional GPUSize64 offset = 0,
        optional GPUSize64 size);

    undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);

    undefined resolveQuerySet(
        GPUQuerySet querySet,
        GPUSize32 firstQuery,
        GPUSize32 queryCount,
        GPUBuffer destination,
        GPUSize64 destinationOffset);

    GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};
GPUCommandEncoder includes GPUObjectBase;
GPUCommandEncoder includes GPUCommandsMixin;
GPUCommandEncoder includes GPUDebugCommandsMixin;

13.2.1. 命令编码器创建

dictionary GPUCommandEncoderDescriptor
         : GPUObjectDescriptorBase {
};
createCommandEncoder(descriptor)

创建一个 GPUCommandEncoder

Called on: GPUDevice this.

Arguments:

Arguments for the GPUDevice.createCommandEncoder(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUCommandEncoderDescriptor 要创建的 GPUCommandEncoder 的描述。

Returns: GPUCommandEncoder

内容时间线 步骤:

  1. e 成为一个新的 GPUCommandEncoder 对象。

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

  3. 返回 e

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

问题:描述剩余的 createCommandEncoder() 验证和 算法步骤。

创建一个 GPUCommandEncoder,对一个缓冲区进行清除的命令进行编码,完成编码器以获得一个 GPUCommandBuffer,然后将其提交给 GPUQueue
const commandEncoder = gpuDevice.createCommandEncoder();
commandEncoder.clearBuffer(buffer);
const commandBuffer = commandEncoder.finish();
gpuDevice.queue.submit([commandBuffer]);

13.3. 通道编码

beginRenderPass(descriptor)

开始编码由 descriptor 描述的渲染过程。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.beginRenderPass(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPURenderPassDescriptor 要创建的 GPURenderPassEncoder 的描述。

Returns: GPURenderPassEncoder

内容时间线 步骤:

  1. 对于 descriptorcolorAttachments 中的每个非 nullcolorAttachment

    1. 如果 colorAttachmentclearValue 不是 null

      1. ? 验证 GPUColor 形状(colorAttachment.clearValue)。

  2. pass 为一个新的 GPURenderPassEncoder 对象。

  3. this设备时间线 上发布 初始化步骤

  4. 返回 pass

设备时间轴 初始化步骤
  1. 检查 this验证编码器状态。 如果返回为 false,将 pass 设为无效并返回。

  2. this.[[state]] 设置为 “锁定”。

  3. 如果以下任何要求未满足,则将 pass 设为 invalid 并返回。

  4. 对于 descriptor.colorAttachments 中的每个非“null” colorAttachment

    1. 在渲染操作期间,由 colorAttachment.view 观察到的 纹理子资源 被视为用作 附件

  5. depthStencilAttachment 成为 descriptor.depthStencilAttachment,如果没有 provided 则为“null”。

  6. 如果 depthStencilAttachment 不是 null

    1. depthStencilView 成为 depthStencilAttachment.view

    2. 如果 depthStencilAttachment.depthReadOnlystencilReadOnly 为 true:

      1. 在渲染操作期间,depthStencilView 的 [=GPUTextureView/subresources=} 被视为用作 attachment-read

    3. 否则,由 depthStencilView 观察到的 纹理子资源 被视为用作 附件

    4. 设置 pass.[[depthReadOnly]]depthStencilAttachment.depthReadOnly

    5. 设置 pass.[[stencilReadOnly]]depthStencilAttachment.stencilReadOnly

  7. pass.[[layout]] 设置为 从 pass 推导出 render targets layout(descriptor)。

  8. 对于 descriptor中的每个timestampWrites中的 timestampWrite,

    1. 如果 timestampWritelocation"beginning", 在 this[[commands]]append 一个 GPU command 将GPU的时间戳值写入 timestampWritequerySettimestampWritequeryIndex 索引处。

    2. 否则,如果 timestampWrite.location"end", 添加 timestampWritepass[[endTimestampWrites]]

  9. pass[[drawCount]]设置为0。

  10. pass[[maxDrawCount]]设置为 descriptormaxDrawCount

  11. this 上执行Enqueue a command,当执行时,在Queue timeline上执行后续步骤。

队列时间线 步骤:
  1. 让当前正在执行的GPUCommandBuffer[[renderState]]成为一个新的RenderState

  2. 问题:执行附件加载/清除。

问题:指定只读深度/模板的行为

beginComputePass(descriptor)

开始对 descriptor 描述的计算过程进行编码。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.beginComputePass(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUComputePassDescriptor

Returns: GPUComputePassEncoder

内容时间线 步骤:

1.让 pass 是一个新的 GPUComputePassEncoder 对象。 1.发出初始化步骤this设备时间线 上。

  1. 返回 pass

设备时间线 initialization steps:
  1. 验证 this 的编码器状态。 如果返回false,使 pass 无效并返回。

  2. 设定 this.[[state]] 为 “locked”。

  3. 如果以下任何要求没有得到满足,使 pass 无效并返回。

  4. 对于 descriptor.timestampWrites 中的每个 timestampWrite,遵循以下步骤:

    1. 如果 timestampWrite.location“beginning”, 将一个 GPU command 追加到 this.[[commands]] 中,该命令将 GPU 的时间戳值写入 timestampWrite.querySettimestampWrite.queryIndex 索引处。

    2. 否则,如果 timestampWrite.location“end”, 将 timestampWrite 追加到 pass.[[endTimestampWrites]] 中。

13.4. 缓冲区拷贝命令

copyBufferToBuffer(source, sourceOffset, destination, destinationOffset, size)

将命令编码到 GPUCommandEncoder 中,将数据从 GPUBuffer 的子区域复制到另一个 GPUBuffer 的子区域。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.copyBufferToBuffer(source, sourceOffset, destination, destinationOffset, size) method.
Parameter Type Nullable Optional Description
source GPUBuffer 要从中复制的 GPUBuffer
sourceOffset GPUSize64 以字节为单位的偏移量到source 从开始复制。
destination GPUBuffer 要复制到的 GPUBuffer
destinationOffset GPUSize64 destination 的偏移量(以字节为单位) 放置复制的数据。
size GPUSize64 要复制的字节数。

Returns: undefined

内容时间线 步骤:

  1. this.[[device]]设备时间线 上发布后续步骤。

设备时间线 步骤:
  1. this验证编码器状态。 如果它返回 false,则停止。

  2. 如果不满足以下任何条件,则制作this 无效 并停止。

  3. this执行一个命令,在执行时在队列时间线上发出后续步骤。

队列时间线 步骤:
  1. source 中从 sourceOffset 开始的 size 字节复制到 destination, 从 destinationOffset 开始。

clearBuffer(buffer, offset, size)

将一个命令编码到 GPUCommandEncoder 中,该命令将GPUBuffer 的子区域填充为零。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.clearBuffer(buffer, offset, size) method.
Parameter Type Nullable Optional Description
buffer GPUBuffer 要清除的GPUBuffer
offset GPUSize64 偏移字节到buffer 要清除的子区域开始的地方。
size GPUSize64 要清除的子区域的大小(以字节为单位)。默认为缓冲区大小减去 offset

Returns: undefined

内容时间线 步骤:

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

设备时间线 步骤:
  1. 验证this的编码器状态。如果返回 false, 停止。

  2. 如果 size 丢失, 将 size 设置为 max(0, |buffer|.{{GPUBuffer/size}} - |offset|)

  3. 如果以下任何条件不满足,则使 this 无效 并停止。

    • buffer 对于 this有效的

    • buffer.usage 包含 COPY_DST

    • size 是4的倍数。

    • offset 是4的倍数。

    • buffer.size ≥ (offset + size)。

  4. this 上执行入队命令,当执行时在Queue timeline 上发布后续步骤。

队列时间轴步骤:
  1. offset 开始将 buffersize 字节设置为 0

13.5. 图像复制命令

copyBufferToTexture(source, destination, copySize)

将一个命令编码到GPUCommandEncoder中,将数据从GPUBuffer的子区域复制到一个或多个连续的纹理子资源的子区域。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.copyBufferToTexture(source, destination, copySize) method.
Parameter Type Nullable Optional Description
source GPUImageCopyBuffer 结合copySize,定义源缓冲区的区域。
destination GPUImageCopyTexture 结合copySize,定义目标区域texture subresource
copySize GPUExtent3D

Returns: undefined

内容时间线 步骤:

  1. 验证 GPUOrigin3D 形状(destination.origin)。

  2. 验证 GPUExtent3D 形状(copySize)。

  3. this.[[device]]设备时间线 上发布后续步骤:

设备时间线 步骤:
  1. this验证编码器状态。 如果它返回 false,则停止。

  2. 如果不满足以下任何条件,则制作this 无效 并停止。

  3. 将一个命令加入队列,当执行时,在Queue timeline上执行后续步骤。

队列时间线 步骤:

问题:定义副本,包括对snorm的规定。

copyTextureToBuffer(source, destination, copySize)

将一个命令编码到 GPUCommandEncoder 中,该命令从一个或多个连续的 texture subresources 子区域中复制数据到 GPUBuffer 的子区域。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.copyTextureToBuffer(source, destination, copySize) method.
Parameter Type Nullable Optional Description
source GPUImageCopyTexture 结合 copySize,定义源texture subresources的区域。
destination GPUImageCopyBuffer 结合 copySize,定义目标缓冲区的区域。
copySize GPUExtent3D 定义所需复制区域的大小。

Returns: undefined

内容时间线 步骤:

  1. ? 验证 GPUOrigin3D 形状(source.origin)。

  2. ? 验证 GPUExtent3D 形状(copySize)。

  3. this.[[device]]设备时间线 上发布后续步骤:

设备时间线 步骤:
  1. 验证this的[=编码器状态$]。如果返回 false,则停止。

  2. 如果以下任何条件不满足,使this变为无效并停止。

  3. thisEnqueue a command,执行时会在 队列时间线 上发出后续步骤。

队列时间线 步骤:

问题:定义副本,包括 snorm 的规定。

copyTextureToTexture(source, destination, copySize)

将一个命令编码到GPUCommandEncoder中,该命令用于将数据从一个或多个连续的texture subresources的子区域复制到另一个或多个连续的texture subresources的子区域。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.copyTextureToTexture(source, destination, copySize) method.
Parameter Type Nullable Optional Description
source GPUImageCopyTexture 结合copySize,定义源纹理子资源的区域。
destination GPUImageCopyTexture 结合copySize,定义目标纹理子资源的区域。
copySize GPUExtent3D 复制尺寸。

Returns: undefined

内容时间线 步骤:

  1. ? 验证 GPUOrigin3D 形状(source.origin).

  2. ? 验证 GPUOrigin3D 形状(destination.origin).

  3. ? 验证 GPUExtent3D 形状(copySize).

  4. this.[[device]]设备时间线 上执行后续步骤:

设备时间线 步骤:

1.验证this编码器状态。如果返回结果为false,则停止。 1.如果以下任何条件不满足,则使this变为无效并停止。

  1. Enqueue a command on this which issues the subsequent steps on the Queue timeline when executed.

队列时间线 步骤:

问题:定义副本,包括 snorm 的规定。

13.6. 请求

writeTimestamp(querySet, queryIndex)

当所有先前的命令都已完成执行时,将时间戳值写入 querySet。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.writeTimestamp(querySet, queryIndex) method.
Parameter Type Nullable Optional Description
querySet GPUQuerySet 将存储时间戳值的查询集。
queryIndex GPUSize32 查询在查询集中的索引。

Returns: undefined

内容时间线 步骤:

  1. 如果 "timestamp-query" 不是 enabled for this

  2. 抛出一个TypeError

  3. thisDevice timeline 上发布后续步骤。[[device]]

设备时间线 步骤:
  1. this验证编码器状态。如果它返回 false,则停止。

  2. 如果不满足以下任何条件,则制作this 无效 并停止。

  3. 将一个命令插入this,当该命令执行时,在队列时间线上进行后续步骤。

队列时间线 步骤:
  1. 将当前的队列时间线时间戳(单位:纳秒)写入索引为 queryIndexquerySet 中。

resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset)

GPUQuerySet中的查询结果解析到GPUBuffer的一定范围内。

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) method.
Parameter Type Nullable Optional Description
querySet GPUQuerySet
firstQuery GPUSize32
queryCount GPUSize32
destination GPUBuffer
destinationOffset GPUSize64

Returns: undefined

内容时间线 步骤:

  1. this.[[device]]设备时间线 上发布后续步骤。

设备时间线 步骤:

1.验证this编码器状态。如果返回false,则停止。 1.如果任何以下条件不满足,将this设为无效并停止。

  • querySet 可以与 this 一起使用

  • destination 可以与 this 一起使用

  • destination.usage 包含 QUERY_RESOLVE

  • firstQuery <在 querySet 中的查询数量。

  • (firstQuery + queryCount) ≤ 在 querySet 中的查询数量。

  • destinationOffset 是256的倍数。

  • destinationOffset + 8 × queryCountdestination.size

1.在this入队一个命令,执行后在队列时间线上执行后续步骤。

队列时间线 步骤:
  1. 使 queryIndexfirstQuery.

  2. 使 offsetdestinationOffset.

  3. queryIndex < firstQuery + queryCount:

    1. 设置 8 个字节的 destination,从 offset 开始,作为在 queryIndex querySet 的值。

    2. 设置 queryIndexqueryIndex + 1.

    3. 设置 offsetoffset + 8.

13.7. 最终

一个包含GPUCommandEncoder记录的命令的GPUCommandBuffer可以通过调用finish()来创建。一旦调用了finish(),该命令编码器将不再可用。

finish(descriptor)

完成命令序列的记录并返回相应的 GPUCommandBuffer

Called on: GPUCommandEncoder this.

Arguments:

Arguments for the GPUCommandEncoder.finish(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUCommandBufferDescriptor

Returns: GPUCommandBuffer

Content timeline steps:

  1. commandBuffer 是一个新的 GPUCommandBuffer

  2. this.[[设备]]设备时间轴 上发出 finish steps

  3. 返回 commandBuffer

设备时间线 finish steps:
  1. 如果满足以下所有要求,那么将 validationSucceeded 设置为“true”,否则设置为“false”。

  2. this.[[state]] 设置为 "结束"。

  3. 如果 validationSucceededfalse,那么:

    1. 生成验证错误

    2. 返回一个新的 无效的 GPUCommandBuffer

  4. commandBuffer.[[command_list]] 设置为 this.[[commands]]

14. 可编程通道

interface mixin GPUBindingCommandsMixin {
    undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
        optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);

    undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
        Uint32Array dynamicOffsetsData,
        GPUSize64 dynamicOffsetsDataStart,
        GPUSize32 dynamicOffsetsDataLength);
};

GPUBindingCommandsMixin假定在同一个对象上存在GPUObjectBaseGPUCommandsMixin成员。它只能被同时包含这些mixin的接口所包含。

GPUBindingCommandsMixin具有以下内部插槽数:

[[bind_groups]], 类型为 ordered map<GPUIndex32, GPUBindGroup>

当前的GPUBindGroup用于每个索引,初始为空。

[[dynamic_offsets]], 类型为 ordered map<GPUIndex32, sequence>GPUBufferDynamicOffset>>

当前每个 [[bind_groups]] 条目的动态偏移量,初始为空。

14.1. 绑定组

setBindGroup() has two overloads:

setBindGroup(index, bindGroup, dynamicOffsets)

为给定的索引设置当前的 GPUBindGroup

Called on: GPUBindingCommandsMixin this.

Arguments:

index, 类型为 GPUIndex32, non-nullable, required

设置绑定组的索引。

bindGroup, 类型为 GPUBindGroup, nullable, required

绑定组以用于后续渲染或计算命令。

dynamicOffsets, of type sequence<GPUBufferDynamicOffset>, non-nullable, defaulting to []

包含每个标记为 buffer.hasDynamicOffsetbindGroup 项的字节缓冲区偏移量的数组。

Returns: undefined

注: dynamicOffsets[i] 用于绑定组中第 i 个动态缓冲区绑定, 当按照 GPUBindGroupLayoutEntry.binding 的顺序排序绑定时。 换句话说,dynamicOffsets 的顺序与动态缓冲区绑定的 GPUBindGroupLayoutEntry.binding 相同。

内容时间线 步骤:

  1. this.[[device]]设备时间线 上执行后续步骤。

设备时间线 步骤:
  1. 验证 this 的编码器状态Validate the encoder state。如果返回 false,则停止。

  2. 如果 bindGroupnull,则让 dynamicOffsetCount 为 0,否则为 bindGroup.[[layout]].[[dynamicOffsetCount]]

  3. 如果以下任何要求没有得到满足,使 this 变为invalid并停止。

  4. 如果 bindGroupnull

    1. this.[[bind_groups]][index] 中 Remove

    2. this.[[dynamic_offsets]][index] 中 Remove

    否则:

    1. 如果以下任何要求未得到满足,请使this invalid 并停止。

    2. this.[[bind_groups]][index] 设置为 bindGroup

    3. this.[[dynamic_offsets]][index] 设置为 dynamicOffsets 的副本。

setBindGroup(index, bindGroup, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength)

为给定索引设置当前 GPUBindGroup,将动态偏移量指定为 Uint32Array 的子集。

Called on: GPUBindingCommandsMixin this.

Arguments:

Arguments for the GPUBindingCommandsMixin.setBindGroup(index, bindGroup, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength) method.
Parameter Type Nullable Optional Description
index GPUIndex32 设置绑定组的索引。
bindGroup GPUBindGroup? 绑定组以用于后续渲染或计算命令。
dynamicOffsetsData Uint32Array 包含 bindGroup 中每个条目的缓冲区偏移量的数组(以字节为单位) 标记为 buffer.hasDynamicOffset
dynamicOffsetsDataStart GPUSize64 元素中的偏移到dynamicOffsetsData 缓冲区偏移数据开始的地方。
dynamicOffsetsDataLength GPUSize32 要从 dynamicOffsetsData 中读取的缓冲区偏移量的数量。

Returns: undefined

内容时间线 步骤:

  1. 如果以下任何要求未满足,请抛出一个 RangeError 并停止。

    • dynamicOffsetsDataStart 必须为 ≥ 0。

    • dynamicOffsetsDataStart + dynamicOffsetsDataLength 必须为 ≤ dynamicOffsetsData.length

  2. dynamicOffsets 成为一个 list,其中包含从索引 dynamicOffsetsDataStart 开始,dynamicOffsetsDataLength 个元素的范围 副本 dynamicOffsetsData

  3. 调用 this.setBindGroup(index, bindGroup, dynamicOffsets)。

要在给定的GPUBindGroup bindGroup遍历每个动态绑定偏移, 并为每个动态偏移执行一系列steps:
  1. dynamicOffsetIndex 为 0。

  2. layoutbindGroup.[[layout]]

  3. 对于按 entry.binding 的递增值排序的 bindGroup.[[entries]] 中的每个 GPUBindGroupEntry entry:

    1. bindingDescriptorlayout.[[entryMap]][entry.binding] 的 GPUBindGroupLayoutEntry

    2. 如果 bindingDescriptor.buffer? .hasDynamicOffset 为 true:

      1. bufferBindingentry.resource

      2. bufferLayoutbindingDescriptor.buffer

      3. 使用 bufferBindingbufferLayoutdynamicOffsetIndex 调用 steps

      4. dynamicOffsetIndexdynamicOffsetIndex + 1

Validate encoder bind groups(encoder, pipeline)

Arguments:

GPUBindingCommandsMixin encoder

正在验证其绑定组的编码器。

GPUPipelineBase pipeline

验证 encoders 绑定组兼容的管道。

  1. 如果不满足以下任何条件,则返回 false

否则返回 true.

编码器绑定组别名可写资源encoderpipeline) 如果任何可写缓冲区绑定范围与相同缓冲区的其他绑定范围重叠, 或者任何可写纹理绑定在 纹理子资源 中与任何其他纹理绑定重叠 (可以使用相同或不同的 GPUTextureView 对象)。

Arguments:

GPUBindingCommandsMixin encoder

正在验证其绑定组的编码器。

GPUPipelineBase pipeline

验证 encoders 绑定组兼容的管道。

  1. 对于[VERTEX, FRAGMENT, COMPUTE]中的每个stage:

    1. bufferBindings为(GPUBufferBinding, boolean)对的列表,其中后者表示资源是否被用作可写。

    2. textureViews为(GPUTextureViewboolean)对的列表,其中后者表示资源是否被用作可写。

    3. 对于pipeline.[[layout]].[[bindGroupLayouts]]中的每对(GPUIndex32 index, GPUBindGroupLayout bindGroupLayout):

      1. bindGroupEntriesencoder.[[bind_groups]][index].entries

      2. bindGroupLayoutEntriesbindGroupLayout.[[descriptor]].entries

      3. 对于 bindGroupLayoutEntries 中的每个 GPUBindGroupEntry bindGroupLayoutEntry 其中 bindGroupLayoutEntry.visibility 包含 stage:

        1. GPUBufferBinding resourcebindGroupEntry.resource. 1. 令 resourceWritable 为 (bindGroupLayoutEntry.buffer.type == “storage”). 1. 对于 bufferBindings 中的每一对 (GPUBufferBinding pastResource, boolean pastResourceWritable): 1. 如果 (resourceWritablepastResourceWritable) 为真,并且 pastResourceresourcebuffer-binding-aliasing,返回 true。 1. 将 ([resource], resourceWritable) 附加到 bufferBindings

        否则,如果 bindGroupEntry.resourceGPUTextureView

        1. GPUTextureView resourcebindGroupEntry.resource.

        2. resourceWritable 为 (bindGroupLayoutEntry.storageTexture.access == "write-only").

        3. 如果 bindGroupLayoutEntry.storageTexture 没有 provided,则 继续.

        4. 对于 textureViews 中的每一对 (GPUTextureView pastResource, boolean pastResourceWritable),

          1. 如果 (resourceWritablepastResourceWritable) 为真,并且 pastResourceresourcetexture-view-aliasing,返回 true

        5. 将 ([resource], resourceWritable) 附加到 textureViews

        否则,继续。

  2. 返回 false

注: 强烈建议实现优化此算法。

15. 调试标记

GPUDebugCommandsMixin 提供了将调试标签应用于命令组或将单个标签插入到命令序列的方法。

调试组可以嵌套以创建标记命令的层次结构,且必须具有良好的平衡。

object labels 一样,这些标签没有必需的行为,但可以显示在错误消息和浏览器开发者工具中,并且可以传递给本地API后端。

interface mixin GPUDebugCommandsMixin {
    undefined pushDebugGroup(USVString groupLabel);
    undefined popDebugGroup();
    undefined insertDebugMarker(USVString markerLabel);
};

GPUDebugCommandsMixin假定同一对象上具有GPUObjectBaseGPUCommandsMixin成员。它只能被那些也包含这些混入的接口引入。

GPUDebugCommandsMixin将以下内部插槽添加到包含它的接口中:

[[debug_group_stack]], 类型为 stack<USVString>

一个包含激活调试组标签的堆。

GPUDebugCommandsMixin 将以下方法添加到包含它的接口中:

pushDebugGroup(groupLabel)

开始包含后续命令的标记调试组。

Called on: GPUDebugCommandsMixin this.

Arguments:

Arguments for the GPUDebugCommandsMixin.pushDebugGroup(groupLabel) method.
Parameter Type Nullable Optional Description
groupLabel USVString 命令组的标签。

Returns: undefined

内容时间线 步骤:

  1. this.[[设备]]设备时间线 上发布后续步骤。

设备时间线 步骤:
  1. 验证 this编码器状态。如果返回 false,则停止。

  2. groupLabel 压入 this.[[debug_group_stack]]之中。

popDebugGroup()

Ends the labeled debug group most recently started by pushDebugGroup().

Called on: GPUDebugCommandsMixin this.

Returns: undefined

内容时间线 步骤:

  1. this.[[device]]设备时间线上发布后续步骤。

设备时间线 步骤:
  1. 验证 this 的编码器状态Validate the encoder state。如果返回 false,则停止。

  2. 如果以下任何要求未满足,将 this 设为 invalid,并停止。

  3. this.[[debug_group_stack]]Pop 弹出一个条目。

insertDebugMarker(markerLabel)

用标签标记命令流中的一个点。

Called on: GPUDebugCommandsMixin this.

Arguments:

Arguments for the GPUDebugCommandsMixin.insertDebugMarker(markerLabel) method.
Parameter Type Nullable Optional Description
markerLabel USVString 要插入的标签。

Returns: undefined

内容时间线 步骤:

  1. this.[[device]]设备时间线 上发布后续步骤。

设备时间线 步骤:
  1. this验证编码器状态。 如果它返回 false,则停止。

16. 计算通道

16.1. GPUComputePassEncoder

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePassEncoder {
    undefined setPipeline(GPUComputePipeline pipeline);
    undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1);
    undefined dispatchWorkgroupsIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);

    undefined end();
};
GPUComputePassEncoder includes GPUObjectBase;
GPUComputePassEncoder includes GPUCommandsMixin;
GPUComputePassEncoder includes GPUDebugCommandsMixin;
GPUComputePassEncoder includes GPUBindingCommandsMixin;

GPUComputePassEncoder 有以下内部槽位:

[[command_encoder]],类型为 GPUCommandEncoder, 只读

创建此计算传递编码器的 GPUCommandEncoder

[[pipeline]],类型为 GPUComputePipeline, 只读

当前的 GPUComputePipeline,最初为 null

[[endTimestampWrite]],类型为 GPU command?,只读,默认为 null

GPU command, 如果有, 在传递结束时写入一个时间戳。

16.1.1. 计算过程编码器创建

dictionary GPUComputePassTimestampWrites {
    required GPUQuerySet querySet;
    GPUSize32 beginningOfPassWriteIndex;
    GPUSize32 endOfPassWriteIndex;
};
querySet, of type GPUQuerySet

GPUQuerySet,类型为"timestamp",查询结果将写入于此。

beginningOfPassWriteIndex, of type GPUSize32

如果已定义,则指示querySet中的查询索引,计算过程开始时的时间戳将写入该索引。

endOfPassWriteIndex, of type GPUSize32

如果已定义,则指示querySet 中的查询索引,计算过程结束时的时间戳将写入该索引。

dictionary GPUComputePassDescriptor
         : GPUObjectDescriptorBase {
    GPUComputePassTimestampWrites timestampWrites;
};
timestampWrites, of type GPUComputePassTimestampWrites

定义将为此过程写入哪些时间戳值,以及将它们写入到何处。

16.1.2. 调度

setPipeline(管道)

设置当前GPUComputePipeline.

调用: GPUComputePassEncoder this.

参数:

Arguments for the GPUComputePassEncoder.setPipeline(pipeline) method.
Parameter Type Nullable Optional Description
pipeline GPUComputePipeline 用于后续调度命令的计算管道。

Returns: undefined

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 设置 this.[[pipeline]]管道

dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ)

要使用当前 GPUComputePipeline。 详情参见 § 23.2 计算

调用: GPUComputePassEncoder this.

参数:

Arguments for the GPUComputePassEncoder.dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ) method.
Parameter Type Nullable Optional Description
workgroupCountX GPUSize32 要调度的工作组网格的X维度。
workgroupCountY GPUSize32 要调度的工作组网格的Y维度。
workgroupCountZ GPUSize32 要调度的工作组网格的Z维度。
注意: 传递给 dispatchWorkgroups()dispatchWorkgroupsIndirect()xyz 值是要为每个维度调度的工作组数, 而不是 而不是要在每个维度上执行的着色器调用数。这与现代原生GPU API的行为相匹配,但与OpenCL的行为不同。 这意味着,如果一个 GPUShaderModule@workgroup_size(4, 4)定义了一个入口点,并且通过调用 computePass.dispatchWorkgroups(8, 8); 将工作分派给它; 入口点将被调用1024次:沿X轴和Y轴调度4x4工作组8次。

返回: 未定义

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态 。如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. passState 作为INDIRECT添加到使用范围中。

  4. 对此执行一个命令 ,该命令将在 队列时间线上发出后续步骤。

队列时间线 步骤:
  1. 使用 passState执行维度为 [workgroupCountX, workgroupCountY, workgroupCountZ] .[[pipeline]] using passState的工作组网格。[[bind_groups]].

dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset)

使用从 GPUBuffer读取的参数,使用当前 GPUComputePipeline 执行调度工作。 详情参见 § 23.2 计算

缓冲区中编码的间接调度参数必须是一个由 三个32位无符号整数值(总共12个字节), 组成的紧密压缩块,其顺序与 dispatchWorkgroups()的参数相同。 例如:

let dispatchIndirectParameters = new Uint32Array(3);
dispatchIndirectParameters[0] = workgroupCountX;
dispatchIndirectParameters[1] = workgroupCountY;
dispatchIndirectParameters[2] = workgroupCountZ;
调用: GPUComputePassEncoder this.

参数:

Arguments for the GPUComputePassEncoder.dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset) method.
Parameter Type Nullable Optional Description
indirectBuffer GPUBuffer 包含 间接调度参数的缓冲区。
indirectOffset GPUSize64 以字节为单位偏移到调度数据开始的 indirectBuffer

返回: undefined

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。 [[device]].

设备时间线 步骤:
  1. 验证此的编码器状态 。如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. indirectBuffer添加到 使用范围 中作为 INDIRECT.

  4. passState 成为当前状态的快照。

  5. 对此执行一个命令 ,该命令将在队列时间线上发出后续步骤。

队列时间线 步骤:
  1. workgroupCountX 是一个从 indirectBuffer 读取的32位无符号整数,位于 indirectOffset 字节。

  2. workgroupCountY 是一个无符号的32位整数,从 indirectBuffer 读取 (indirectOffset + 4) 字节。

  3. workgroupCountZ 是一个无符号的32位整数,从 indirectBuffer 读取 (indirectOffset + 8) 字节。

  4. 如果 workgroupCountX, workgroupCountY, or workgroupCountZ 大于 this.device.limits.maxComputeWorkgroupsPerDimension,请停止。

  5. 使用 passState执行维度为 [workgroupCountX, workgroupCountY, workgroupCountZ] 的工作组网格。[[pipeline]] 使用 passState.[[bind_groups]].

16.1.3. 最终确定

一旦用户完成了对过程的命令记录,就可以通过调用 end() 来 结束计算过程编码器。一旦调用了 end() ,就不能再使用计算过程编码器了。

end()

完成计算过程命令序列的记录。

调用: GPUComputePassEncoder this.

返回: undefined

内容时间线 步骤:

  1. 在设备时间轴上发布后续步骤。[[device]].

设备时间线 步骤:
  1. parentEncoderthis.[[command_encoder]].

  2. 如果以下任何要求未得到满足,则 生成一个验证错误 并停止。

  3. 设置 this.[[state]] 改为 "ended".

  4. 设置 parentEncoder.[[state]] 变为 "open".

  5. 如果未满足以下任何要求,请使 parentEncoder 无效 并停止。

  6. Extend parentEncoder.[[commands]] with this.[[commands]].

  7. 如果 this.[[endTimestampWrite]] 不为 null:

    1. Extend parentEncoder.[[commands]] with this.[[endTimestampWrite]].

17. 渲染过程

17.1. GPURenderPassEncoder

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPassEncoder {
    undefined setViewport(float x, float y,
        float width, float height,
        float minDepth, float maxDepth);

    undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y,
                        GPUIntegerCoordinate width, GPUIntegerCoordinate height);

    undefined setBlendConstant(GPUColor color);
    undefined setStencilReference(GPUStencilValue reference);

    undefined beginOcclusionQuery(GPUSize32 queryIndex);
    undefined endOcclusionQuery();

    undefined executeBundles(sequence<GPURenderBundle> bundles);
    undefined end();
};
GPURenderPassEncoder includes GPUObjectBase;
GPURenderPassEncoder includes GPUCommandsMixin;
GPURenderPassEncoder includes GPUDebugCommandsMixin;
GPURenderPassEncoder includes GPUBindingCommandsMixin;
GPURenderPassEncoder includes GPURenderCommandsMixin;

GPURenderPassEncoder 具有以下内部插槽,用于在编码时进行验证:

[[command_encoder]],类型为 GPUCommandEncoder,只读

创建此渲染过程编码器的GPUCommandEncoder

[[attachment_size]],只读

设置为以下范围:

  • width, height = 过程渲染附件的尺寸

[[occlusion_query_set]],类型为 GPUQuerySet,只读

用于存储过程的遮挡查询结果的 GPUQuerySet ,该过程在过程创建时使用 GPURenderPassDescriptor.occlusionQuerySet 初始化。

[[occlusion_query_active]],类型为 boolean

是否正在写入过程的 [[occlusion_query_set]]

[[endTimestampWrite]],类型为 GPU command?,只读,默认为 null

GPU command(如果有),在过程结束时写入时间戳。

[[maxDrawCount]] 类型为 GPUSize64,只读

此过程中允许的最大平局次数。

当作为 GPUCommandBuffer的一部分执行编码的渲染过程命令时,内部 RenderState 对象用于跟踪渲染所需的当前状态。

RenderState 包含以下用于执行渲染命令的内部插槽:

[[occlusionQueryIndex]],类型为 GPUSize32

存储遮挡查询结果的 [[occlusion_query_set]] 索引。

[[viewport]]

当前视口矩形和深度范围。最初设置为以下值:

  • x, y = 0.0, 0.0

  • width, height = 过程渲染目标的尺寸

  • minDepth, maxDepth = 0.0, 1.0

[[scissorRect]]

当前剪刀形矩形。最初设置为以下值:

  • x, y = 0, 0

  • width, height = 过程渲染目标的尺寸

[[blendConstant]],,类型为 GPUColor

当前混合常数值,最初为 [0, 0, 0, 0]

[[stencilReference]],类型为 GPUStencilValue

当前模具引用值,最初为 0

17.1.1. 渲染过程编码器创建

dictionary GPURenderPassTimestampWrites {
    required GPUQuerySet querySet;
    GPUSize32 beginningOfPassWriteIndex;
    GPUSize32 endOfPassWriteIndex;
};
querySet, of type GPUQuerySet

GPUQuerySet,类型为 "timestamp"查询结果将写入于此。

beginningOfPassWriteIndex, of type GPUSize32

如果已定义,则指示 querySet 中的查询索引,渲染过程开始时的时间戳将写入该索引。

endOfPassWriteIndex, of type GPUSize32

如果已定义,则指示 querySet 中的查询索引,渲染过程结束时的时间戳将写入该索引。

dictionary GPURenderPassDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPURenderPassColorAttachment?> colorAttachments;
    GPURenderPassDepthStencilAttachment depthStencilAttachment;
    GPUQuerySet occlusionQuerySet;
    GPURenderPassTimestampWrites timestampWrites;
    GPUSize64 maxDrawCount = 50000000;
};
colorAttachments, of type sequence<GPURenderPassColorAttachment?>

此序列中的一组 GPURenderPassColorAttachment 值定义了执行此渲染过程时将输出到哪些颜色附件。

由于 使用兼容性的原因,任何颜色附件都不能别名其他附件或渲染过程中使用的任何资源。

depthStencilAttachment, of type GPURenderPassDepthStencilAttachment

GPURenderPassDepthStencilAttachment 值,用于定义执行此渲染过程时将输出到并测试的深度/模具附件。

由于 使用兼容性,任何可写的深度/模具附件都不能别名为另一个附件或渲染过程中使用的任何资源。

occlusionQuerySet, of type GPUQuerySet

GPUQuerySet 值定义了此过程的遮挡查询结果的存储位置。

timestampWrites, of type GPURenderPassTimestampWrites

定义将为此过程写入哪些时间戳值,以及将它们写入到何处。

maxDrawCount, of type GPUSize64, defaulting to 50000000

将在渲染过程中执行的最大绘制调用数。某些实现用于调整渲染过程之前注入的工作的大小。保持默认值是一个很好的默认值,除非已知将执行更多的绘制调用。

有效用途

给定 GPUDevice deviceGPURenderPassDescriptor this,以下验证规则适用:

  1. this.colorAttachments.length 必须 ≤ device.[[limits]].maxColorAttachments.

  2. 对于每个非空 colorAttachment colorAttachments:

    1. colorAttachment 必须符合 GPURenderPassColorAttachment Valid Usage 规则。

  3. 如果 this.depthStencilAttachmentprovided:

    1. this.depthStencilAttachment 必须符合 GPURenderPassDepthStencilAttachment Valid Usage 规则。

  4. 必须至少存在一个附件,或者:

  5. 验证 GPURenderPassDescriptor的每个样本的颜色附件字节数(device, this.colorAttachments) 成功。

  6. 所有 view的非null成员colorAttachments, 和 this.depthStencilAttachment.view 必须具有相等的 sampleCounts.

  7. 对于 view 的非null成员。colorAttachmentsthis.depthStencilAttachment.view, 如果存在,则 [[renderExtent]] 必须匹配。

  8. 如果 this.occlusionQuerySet 不为 null:

    1. this.occlusionQuerySet.type 必须是 occlusion.

  9. 如果 this.timestampWritesprovided:

验证 GPURenderPassDescriptor的每个样本的颜色附件字节数、(GPUDevice device, sequence<GPURenderPassColorAttachment?> colorAttachments)
  1. formats 为空 列表<GPUTextureFormat?>

  2. 对于每种 colorAttachmentcolorAttachments:

    1. 如果 colorAttachmentundefined,请继续。

    2. Append colorAttachment.view.[[descriptor]].formatformats.

  3. 计算每个样本(formats) 的颜色附件字节数必须 ≤ device.[[limits]].maxColorAttachmentBytesPerSample.

17.1.1.1. 颜色附件
dictionary GPURenderPassColorAttachment {
    required GPUTextureView view;
    GPUTextureView resolveTarget;

    GPUColor clearValue;
    required GPULoadOp loadOp;
    required GPUStoreOp storeOp;
};
view, of type GPUTextureView

描述将为此颜色附件输出到的纹理子资源GPUTextureView

resolveTarget, of type GPUTextureView

描述纹理子资源GPUTextureView ,如果view是多采样的,该纹理子资源将接收此颜色附件的解析输出。

clearValue, of type GPUColor

指示在执行渲染过程之前要清除view 的值。 如果未 provided,则默认为 {r: 0, g: 0, b: 0, a: 0}。如果 loadOp"clear",则忽略。

clearValue的组成部分都是双值。 它们将转换为。 与渲染附件匹配的纹理格式的texel值 , 如果转换失败,将生成验证错误。

loadOp, of type GPULoadOp

指示在执行渲染过程之前要对 view 执行的加载操作。

注意:建议首选清除;有关详细信息,请参见 "clear"

storeOp, of type GPUStoreOp

在执行渲染过程之后要对 view 执行的存储操作。

GPURenderPassColorAttachment 有效用法

给定 GPURenderPassColorAttachment this:

  1. renderViewDescriptorthis.view.[[descriptor]].

  2. resolveViewDescriptorthis.resolveTarget.[[descriptor]].

  3. renderTexturethis.view.[[texture]].

  4. resolveTexturethis.resolveTarget.[[texture]].

以下验证规则适用:

如果满足以下要求,则 GPUTextureView viewrenderable texture view

其中 descriptorview.[[descriptor]].

计算每个样本的颜色附件字节数(formats)

参数:

返回: GPUSize32

  1. total 为 0.

  2. 对于formats中的每个非空format

    1. Assert: format 是一种 颜色可渲染的格式

    2. renderTargetPixelByteCostformat渲染目标像素字节成本

    3. renderTargetComponentAlignmentformat渲染目标组件对齐方式

    4. total 四舍五入到 renderTargetComponentAlignment 的最小倍数,该倍数大于或等于 total

    5. renderTargetPixelByteCost 添加到 total

  3. 返回 total

17.1.1.2. 深度/模板附件
dictionary GPURenderPassDepthStencilAttachment {
    required GPUTextureView view;

    float depthClearValue;
    GPULoadOp depthLoadOp;
    GPUStoreOp depthStoreOp;
    boolean depthReadOnly = false;

    GPUStencilValue stencilClearValue = 0;
    GPULoadOp stencilLoadOp;
    GPUStoreOp stencilStoreOp;
    boolean stencilReadOnly = false;
};
view, of type GPUTextureView

GPUTextureView ,描述将为此深度/模具附件输出和读取的纹理子源

depthClearValue, of type float

指示在执行渲染过程之前清除 view的深度组件的值。 如果 depthLoadOp 不是 "clear",则忽略。必须介于0.0和1.0之间(包括0.0和1.0)。

depthLoadOp, of type GPULoadOp

指示在执行渲染过程之前要对 view的深度组件执行的加载操作。

注意:建议首选清除;有关详细信息,请参见 "clear" for details.

depthStoreOp, of type GPUStoreOp

在执行渲染过程之后对 view的深度组件执行的存储操作。

depthReadOnly, of type boolean, defaulting to false

指示 view 的深度组件是只读的。

stencilClearValue, of type GPUStencilValue, defaulting to 0

指示在执行渲染过程之前清除 view的模具组件的值。如果 stencilLoadOp"clear",则忽略该值。

该值将通过采用与一个纹素 view 的模版方面中的位数相同的LSB数量来转换为 view的模版方面的类型。

stencilLoadOp, of type GPULoadOp

指示在执行渲染过程之前要对 view的模具组件执行的加载操作。

注意:建议首选清除;有关详细信息,请参见 "clear" for details.

stencilStoreOp, of type GPUStoreOp

在执行渲染过程后对 view的模具组件执行的存储操作。

stencilReadOnly, of type boolean, defaulting to false

指示 view 的模具组件是只读的。

GPURenderPassDepthStencilAttachment 有效用法

给定 GPURenderPassDepthStencilAttachment this,以下验证规则适用:

17.1.1.3. 装载和储存操作
enum GPULoadOp {
    "load",
    "clear",
};
"load"

将此附件的现有值加载到渲染过程中。

"clear"

将此附件的清除值加载到渲染过程中。

注意: 在一些GPU硬件(主要是移动设备)上, "clear" 要便宜得多, 因为它可以避免将数据从主内存加载到瓦片本地内存。 在其他GPU硬件上,没有显著差异。 因此,在初始值无关紧要的情况下 (例如,渲染目标将使用skybox清除),建议使用 "clear" 而不是 "load"

enum GPUStoreOp {
    "store",
    "discard",
};
"store"

存储此附件的渲染过程的结果值。

"discard"

丢弃此附件的渲染过程的结果值。

17.1.1.4. 渲染通道布局

GPURenderPassLayout 声明 GPURenderBundle. 的渲染目标的布局。它也在内部用于描述 GPURenderPassEncoder 布局和 GPURenderPipeline 布局。它确定渲染过程、渲染束和渲染管道之间的兼容性。

dictionary GPURenderPassLayout
         : GPUObjectDescriptorBase {
    required sequence<GPUTextureFormat?> colorFormats;
    GPUTextureFormat depthStencilFormat;
    GPUSize32 sampleCount = 1;
};
colorFormats, of type sequence<GPUTextureFormat?>

此过程或捆绑包的颜色附件的 GPUTextureFormat列表。

depthStencilFormat, of type GPUTextureFormat

此通道或束的深度/模板附件的GPUTextureFormat

sampleCount, of type GPUSize32, defaulting to 1

此过程或束的附件中每个像素的样本数。

以下情况下,两个 GPURenderPassLayoutequal
从过程派生渲染目标布局

参数:

返回: GPURenderPassLayout

  1. layout 成为一个新的 GPURenderPassLayout 对象。

  2. 对于 descriptor.colorAttachments 中的每种colorAttachment

    1. 如果 colorAttachment 不为 null:

      1. colorAttachment.view.[[texture]].sampleCount设置为 layout.sampleCount

      2. 附加 colorAttachment.view.[[descriptor]].formatlayout.colorFormats.

    2. 否则:

      1. 附加 nulllayout.colorFormats.

  3. depthStencilAttachmentdescriptor.depthStencilAttachment, 或者为 null 如果未 provided

  4. 如果 depthStencilAttachment 不为 null:

    1. viewdepthStencilAttachment.view.

    2. 设置 layout.sampleCountview.[[texture]].sampleCount.

    3. 设置 layout.depthStencilFormatview.[[descriptor]].format.

  5. 返回 layout.

从管道派生渲染目标布局

参数

返回: GPURenderPassLayout

  1. layout 成为一个新的 GPURenderPassLayout 对象。

  2. 设置 layout.sampleCountdescriptor.multisample.count.

  3. 如果 descriptor.depthStencilprovided:

    1. 设置 layout.depthStencilFormat 设置 descriptor.depthStencil.format.

  4. 如果 descriptor.fragmentprovided:

    1. 对于 descriptor.fragment.targets中的每种 colorTarget:

      1. 附加 colorTarget.formatlayout.colorFormats 如果 colorTarget 不为 null,否则附加 null

  5. 返回 layout.

17.1.2. 最终确定

一旦用户完成对过程的命令录制,就可以通过调用 end() 来结束渲染过程编码器。一旦调用了 end() ,就不能再使用渲染过程编码器了。

end()

完成渲染过程命令序列的记录。

调用: GPURenderPassEncoder this.

返回: undefined

内容时间线 步骤:

  1. 设备时间轴上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. parentEncoderthis.[[command_encoder]].

  2. 如果以下任何要求未得到满足,则 generate a validation error 并停止。

  3. 设置 this.[[state]] 为 "ended".

  4. 设置 parentEncoder.[[state]] 为 "open".

  5. 如果未满足以下任何要求,请使 parentEncoder 无效 并停止。

  6. Extend parentEncoder.[[commands]] with this.[[commands]].

  7. 如果 this.[[endTimestampWrite]] 不为 null:

    1. Extend parentEncoder.[[commands]] with this.[[endTimestampWrite]].

  8. thisEnqueue a render command ,该命令在执行时在 队列时间轴 上发出带有renderState的后续步骤。

队列时间线 步骤:
  1. 问题:执行附件存储/丢弃。

  2. renderStatenull.

17.2. GPURenderCommandsMixin

GPURenderCommandsMixin 定义了 GPURenderPassEncoderGPURenderBundleEncoder通用的渲染命令。

interface mixin GPURenderCommandsMixin {
    undefined setPipeline(GPURenderPipeline pipeline);

    undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size);
    undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer? buffer, optional GPUSize64 offset = 0, optional GPUSize64 size);

    undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1,
        optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0);
    undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1,
        optional GPUSize32 firstIndex = 0,
        optional GPUSignedOffset32 baseVertex = 0,
        optional GPUSize32 firstInstance = 0);

    undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
    undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
};

GPURenderCommandsMixin 假定在同一对象上存在 GPUObjectBase, GPUCommandsMixin, 和 GPUBindingCommandsMixin 成员。它必须仅由同样包含这些mixin的接口包含。

GPURenderCommandsMixin 具有以下内部插槽:

[[layout]],类型为 GPURenderPassLayout,只读

渲染过程的布局。

[[depthReadOnly]],布尔型,只读

如果为 true,则表示深度分量未被修改。

[[stencilReadOnly]],布尔型,只读

如果为 true,则表示未修改模具组件。

[[pipeline]],类型为 GPURenderPipeline

当前的 GPURenderPipeline,最初为 null.

[[index_buffer]],类型为 GPUBuffer

从中读取索引数据的当前缓冲区,最初为 null.

[[index_format]],类型为 GPUIndexFormat

[[index_buffer]]中索引数据的格式。

[[index_buffer_offset]],类型为 GPUSize64

当前设置的[[index_buffer]] 部分的偏移量(以字节为单位)。

[[index_buffer_size]],类型为 GPUSize64

当前设置的 [[index_buffer]] 的部分的字节大小, 最初为 0.

[[vertex_buffers]],类型为 ordered map<slot, GPUBuffer>

要从中读取每个插槽的顶点数据的当前 GPUBuffer,最初为空。

[[vertex_buffer_sizes]],类型为 ordered map<slot, GPUSize64>

当前为每个插槽设置的 GPUBuffer 部分的大小(以字节为单位),最初为空。

[[drawCount]],类型为 GPUSize64

此编码器中记录的绘制命令数。

要在GPURenderCommandsMixin编码器上 排队渲染命令 ,该 encoder 使用 RenderState renderState发出 GPU 命令 command 的步骤:
  1. Append commandencoder.[[commands]].

  2. command 作为GPUCommandBuffer commandBuffer命令缓冲区的一部分执行时:

    1. 使用 commandBuffer.[[renderState]] 作为 renderState发出command的步骤。

17.2.1. 绘图

setPipeline(pipeline)

Sets the current GPURenderPipeline.

调用: GPURenderCommandsMixin this.

参数:

Arguments for the GPURenderCommandsMixin.setPipeline(pipeline) method.
Parameter Type Nullable Optional Description
pipeline GPURenderPipeline 用于后续绘图命令的渲染管道。

返回: undefined

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态 ,如果返回 false,请停止。

  2. pipelineTargetsLayoutderive render targets layout from pipeline(pipeline.[[descriptor]]).

  3. 如果不满足以下任何条件,则使 this 无效 并停止。

  4. 设置 this.[[pipeline]]pipeline.

setIndexBuffer(buffer, indexFormat, offset, size)

设置当前索引缓冲区。

调用: GPURenderCommandsMixin this.

参数:

Arguments for the GPURenderCommandsMixin.setIndexBuffer(buffer, indexFormat, offset, size) method.
Parameter Type Nullable Optional Description
buffer GPUBuffer 包含用于后续绘图命令的索引数据的缓冲区。
indexFormat GPUIndexFormat buffer中包含的索引数据的格式。
offset GPUSize64 索引数据开始的 buffer 中的偏移量(以字节为单位)。默认值为 0
size GPUSize64 buffer中索引数据的大小(以字节为单位)。 默认为缓冲区的大小减去偏移量。

返回: undefined

设备时间线 上发布以下步骤。 this.[[device]]:

  1. 验证此的编码器状态 如果返回false,请停止。

  2. 如果 size 缺失, 设置 size 为 max(0, buffer.size - offset).

  3. 如果不满足以下任何条件,则使 this 无效 并停止。

  4. 添加 buffer使用范围 作为 input.

  5. 设置 this.[[index_buffer]]buffer.

  6. 设置 this.[[index_format]]indexFormat.

  7. 设置 this.[[index_buffer_offset]]offset.

  8. 设置 this.[[index_buffer_size]]size.

setVertexBuffer(slot, buffer, offset, size)

设置给定插槽的当前顶点缓冲区。

调用: GPURenderCommandsMixin this.

参数::

Arguments for the GPURenderCommandsMixin.setVertexBuffer(slot, buffer, offset, size) method.
Parameter Type Nullable Optional Description
slot GPUIndex32 要为其设置顶点缓冲区的顶点缓冲区插槽。
buffer GPUBuffer? 包含顶点数据的缓冲区,用于后续绘图命令。
offset GPUSize64 以字节为单位偏移到顶点数据开始的 buffer 中。默认值为 0
size GPUSize64 buffer中顶点数据的大小(以字节为单位)。 默认为缓冲区的大小减去偏移量。

返回: undefined

设备时间表 上发布以下步骤 this.[[device]]:

  1. 验证此的编码器状态 。如果返回false,请停止。

  2. bufferSize 为 0 如果 buffernull, 否则为 buffer.size

  3. 如果 size 缺失,则设置 size 为 max(0, bufferSize - offset).

  4. 如果以下任何要求未得到满足,则使 this 无效 并停止。

  5. 如果 buffernull:

    1. Remove this.[[vertex_buffers]][slot].

    2. Remove this.[[vertex_buffer_sizes]][slot].

    否则:

    1. 如果以下任何要求未得到满足,则使 this 无效 并停止。

    2. 添加 buffer使用范围 作为 input.

    3. 设置 this.[[vertex_buffers]][slot] 为 buffer.

    4. 设置 this.[[vertex_buffer_sizes]][slot] 为 size.

draw(vertexCount, instanceCount, firstVertex, firstInstance)

绘制基本体。 详情参见 § 23.3 渲染

调用: GPURenderCommandsMixin this.

参数:

Arguments for the GPURenderCommandsMixin.draw(vertexCount, instanceCount, firstVertex, firstInstance) method.
Parameter Type Nullable Optional Description
vertexCount GPUSize32 要绘制的顶点数。
instanceCount GPUSize32 要绘制的实例数。
firstVertex GPUSize32 偏移到顶点缓冲区中,以顶点为单位,开始绘制。
firstInstance GPUSize32 第一个要绘制的实例。

返回: undefined

在上 设备时间表 发布以下步骤。 this.[[device]]:

  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 增加 this.[[drawCount]] by 1.

  4. passState 成为 this状态的快照。

  5. 在此上排队一个渲染命令 ,该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 绘制 instanceCount 实例,从实例 firstInstance开始,由 vertexCount 垂直组成的基本体,从顶点 firstVertex, 开始,状态来自 passStaterenderState.

drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance)

绘制索引基元。 详情参见 § 23.3 渲染

调用: GPURenderCommandsMixin this.

参数:

Arguments for the GPURenderCommandsMixin.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance) method.
Parameter Type Nullable Optional Description
indexCount GPUSize32 要绘制的索引数。
instanceCount GPUSize32 要绘制的实例数。
firstIndex GPUSize32 偏移到索引缓冲区,在索引中,从开始绘制。
baseVertex GPUSignedOffset32 在索引到顶点缓冲区之前添加到每个索引值。
firstInstance GPUSize32 第一个要绘制的实例。

返回: undefined

设备时间表 上发布以下步骤。 this.[[device]]:

  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 增加 this.[[drawCount]] 1.

  4. passState 成为 this状态的快照。

  5. this排队一个渲染命令 ,该命令在执行时在队列时间轴上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 绘制 instanceCount 实例,从实例 firstInstance开始,由 indexCount 索引的垂直图元组成,从baseVertex的顶点的索引 firstIndex 开始,状态从 passStaterenderState开始。

注意:一个有效的程序也不应该将顶点索引与超出界限 GPUVertexStepMode."vertex" 一起使用。 WebGPU 实现有不同的处理方式,因此允许一系列行为。要么放弃整个绘制调用,要 么通过WGSL的无效内存引用来描述对越界属性的访问。

drawIndirect(indirectBuffer, indirectOffset)

使用从 GPUBuffer读取的参数绘制基本体。 详情参见 § 23.3 渲染

缓冲区中编码的 indirect draw parameters 必须是一个由 四个32位无符号整数值(总共16字节)组成的紧密压缩块,其顺序与 draw()的参数相同。例如:

let drawIndirectParameters = new Uint32Array(4);
drawIndirectParameters[0] = vertexCount;
drawIndirectParameters[1] = instanceCount;
drawIndirectParameters[2] = firstVertex;
drawIndirectParameters[3] = firstInstance;

除非启用了"indirect-first-instance"feature功能,否则与 firstInstance 对应的值必须为0。 如果未启用 "indirect-first-instance" featurefirstInstance 不为零,则 drawIndirect() 调用将被视为 no-op。

调用: GPURenderCommandsMixin this.

参数:

Arguments for the GPURenderCommandsMixin.drawIndirect(indirectBuffer, indirectOffset) method.
Parameter Type Nullable Optional Description
indirectBuffer GPUBuffer 包含 间接绘制参数的缓冲区。
indirectOffset GPUSize64 以字节为单位偏移到图形数据开始的 indirectBuffer 中。

返回: undefined

设备时间表 上发布以下步骤。 this.[[device]]:

  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 添加 indirectBufferusage scope 作为 input.

  4. 增加 this.[[drawCount]] by 1.

  5. passState 成为 this状态的快照。

  6. this排队一个渲染命令 ,该命令在执行时在队列时间轴上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. vertexCount 是一个从 indirectBuffer 取的无符号32位整数,位于 indirectOffset 字节。

  2. instanceCount 是一个无符号的32位整数,从 indirectBuffer 读取 (indirectOffset + 4) 字节。

  3. firstVertex 是一个无符号的32位整数,从 indirectBuffer 读取 (indirectOffset + 8) 字节。

  4. firstInstance 是一个无符号的32位整数,从 indirectBuffer 读取 (indirectOffset + 12) 字节。

  5. 绘制 instanceCount 实例,从实例 firstInstance开始,由 vertexCount 垂直组成的基本体,从顶点 firstVertex开始,状态来自 passStaterenderState

drawIndexedIndirect(indirectBuffer, indirectOffset)

使用从GPUBuffer读取的参数绘制索引基元。 详情参见 § 23.3 渲染

缓冲区中编码的indirect drawIndexed parameters 必须是一个由 五个32位无符号整数值(总共20字节)组成的紧密压缩块,其顺序与 drawIndexed()的参数相同。例如:

let drawIndexedIndirectParameters = new Uint32Array(5);
drawIndexedIndirectParameters[0] = indexCount;
drawIndexedIndirectParameters[1] = instanceCount;
drawIndexedIndirectParameters[2] = firstIndex;
drawIndexedIndirectParameters[3] = baseVertex;
drawIndexedIndirectParameters[4] = firstInstance;

除非启用了 "indirect-first-instance" feature 功能,否则与 firstInstance 对应的值必须为0。如果 "indirect-first-instance" feature 未启用且 firstInstance 不为零,则 drawIndexedIndirect() 调用将被视为 no-op。

调用: GPURenderCommandsMixin this.

参数:

Arguments for the GPURenderCommandsMixin.drawIndexedIndirect(indirectBuffer, indirectOffset) method.
Parameter Type Nullable Optional Description
indirectBuffer GPUBuffer 包含 间接drawIndexed参数的缓冲区。
indirectOffset GPUSize64 以字节为单位偏移到图形数据开始的 indirectBuffer 中。

返回: undefined

设备时间表上发布以下步骤 this.[[device]]:

  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 添加 indirectBuffer使用范围 作为 input.

  4. 增加 this.[[drawCount]] by 1.

  5. passState 成为 this状态的快照。

  6. this排队一个渲染命令 ,该命令在执行时在队列时间轴上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. indexCount 是一个无符号的32位整数,从 indirectBuffer 读取,位置为 indirectOffset 字节。

  2. instanceCount 是一个无符号的32位整数,从 indirectBuffer 读取,位置为 (indirectOffset + 4) 字节。

  3. firstIndex 是一个无符号的32位整数,从 indirectBuffer 读取,位置为 (indirectOffset + 8) b字节。

  4. baseVertex 是一个无符号的32位整数,从 indirectBuffer 读取,位置为 (indirectOffset + 12) 字节。

  5. firstInstance 是一个无符号的32位整数,从 indirectBuffer 读取,位置为 (indirectOffset + 16) 字节。

  6. 绘制 instanceCount 实例,从实例 firstInstance开始,由 indexCount 索引的垂直图元组成,从baseVertex的顶点的索引 firstIndex 开始,状态从 passStaterenderState开始。

要确定使用 GPURenderCommandsMixin encoder编码器是否 valid to draw 请运行以下步骤:
  1. 如果不满足以下任何条件,则返回 false:

  2. 否则返回 true.

要确定使用 GPURenderCommandsMixin encoder valid to draw indexed 请运行以下步骤:
  1. 如果不满足以下任何条件,则返回 false:

  2. 否则返回 true.

17.2.2. 光栅化状态

GPURenderPassEncoder 有几种方法会影响绘制命令如何光栅化到此编码器使用的附件。

setViewport(x, y, width, height, minDepth, maxDepth)

将光栅化阶段使用的视口设置为从标准化设备坐标线性映射到视口坐标。

调用: GPURenderPassEncoder this.

参数:

Arguments for the GPURenderPassEncoder.setViewport(x, y, width, height, minDepth, maxDepth) method.
Parameter Type Nullable Optional Description
x float 视口的最小X值(以像素为单位)。
y float 视口的最小Y值(以像素为单位)。
width float 口的宽度(以像素为单位)。
height float 视口的高度(以像素为单位)。
minDepth float 视口的最小深度值。
maxDepth float 视口的最大深度值。

返回: undefined

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效并停止。

    • x0

    • y0

    • width0

    • height0

    • x + widththis.[[attachment_size]].width

    • y + heightthis.[[attachment_size]].height

    • 0.0 ≤ minDepth ≤ 1.0

    • 0.0 ≤ maxDepth ≤ 1.0

    • minDepth < maxDepth

  3. this排队一个渲染命令 ,该命令在执行时在队列时间轴上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. x, y, width, 和 height 舍入到某种统一的精度,精度不亚于整数舍入。

  2. 设置 renderState.[[viewport]] 到范围 x, y, width, height, minDepth, 和 maxDepth.

setScissorRect(x, y, width, height)

设置光栅化阶段中使用的剪刀形矩形。转换到视口坐标后,落在剪刀形矩形之外的任何片段都将被丢弃。

调用: GPURenderPassEncoder this.

参数:

Arguments for the GPURenderPassEncoder.setScissorRect(x, y, width, height) method.
Parameter Type Nullable Optional Description
x GPUIntegerCoordinate 剪刀形矩形的最小X值(以像素为单位)。
y GPUIntegerCoordinate 剪刀形矩形的最小Y值(以像素为单位)。
width GPUIntegerCoordinate 剪刀形矩形的宽度(以像素为单位)。
height GPUIntegerCoordinate 剪刀形矩形的高度(以像素为单位)。

返回: undefined

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 在此上排队一个渲染命令 ,该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 设置 renderState.[[scissorRect]] 到范围 x, y, width, 和 height.

setBlendConstant(color)

设置与 "constant""one-minus-constant" GPUBlendFactor一起使用的常量混合颜色和alpha值。

调用: GPURenderPassEncoder this.

参数:

Arguments for the GPURenderPassEncoder.setBlendConstant(color) method.
Parameter Type Nullable Optional Description
color GPUColor 混合时要使用的颜色。

返回: undefined

内容时间线 步骤:

  1. ? 验证GPU颜色形状(color).

  2. 设备时间轴 上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态。 如果返回false,请停止。

  2. this排队一个渲染命令 该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 设置 renderState.[[blendConstant]]color.

setStencilReference(reference)

使用 "replace" GPUStencilOperation设置模具测试期间使用的 [[stencilReference]] 值。

调用: GPURenderPassEncoder this.

参数:

Arguments for the GPURenderPassEncoder.setStencilReference(reference) method.
Parameter Type Nullable Optional Description
reference GPUStencilValue 新的模具引用值。

返回: undefined

内容时间线 步骤:

  1. 设备时间轴上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态。 如果返回false,请停止。

  2. this排队一个渲染命令 ,该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 设置 renderState.[[stencilReference]]reference.

17.2.3. 查询

beginOcclusionQuery(queryIndex)
调用: GPURenderPassEncoder this.

参数:

Arguments for the GPURenderPassEncoder.beginOcclusionQuery(queryIndex) method.
Parameter Type Nullable Optional Description
queryIndex GPUSize32 查询集中查询的索引。

返回: undefined

Content timeline steps:

  1. Issue the subsequent steps on the Device timeline of 在 设备时间轴 上发布后续步骤。

设备时间线 步骤:
  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

    this.[[occlusion_query_set]] 不是 null.
  3. 设置 this.[[occlusion_query_active]]true.

  4. this排队一个渲染命令 ,该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 设置 renderState.[[occlusionQueryIndex]]queryIndex.

endOcclusionQuery()
调用: GPURenderPassEncoder this.

返回: undefined

Content timeline steps:

  1. 设备时间轴 上发布后续步骤。

  1. 设备时间线 步骤:

  2. 验证此的编码器状态。 如果返回false,请停止。

  3. 如果不满足以下任何条件,则使 this 无效 并停止。

  4. 设置 this.[[occlusion_query_active]]false.

  5. this排队一个渲染命令 ,该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

队列时间线 步骤:
  1. 如果自相应的 beginOcclusionQuery() 命令执行以来,任何片段样本都通过了所有片段测试,则 passingFragments 为非零,否则为零。

    注意:如果没有发生绘制调用,则 passingFragments 为零。

  2. passingFragments 写入 this.[[occlusion_query_set]] 的索引 renderState.[[occlusionQueryIndex]].

17.2.4.

executeBundles(bundles)

执行先前记录到给定的 GPURenderBundle 中的命令,作为此渲染通道的一部分。

执行 GPURenderBundle 时,它不会继承渲染通道的管道、绑定组或顶点和索引缓冲区。 执行 GPURenderBundle 后,渲染通道的管道、绑定组和顶点/索引缓冲区状态被清除(到初始的空值)。

注意:状态被清除,而不是恢复到先前的状态。 即使执行了零个 GPURenderBundles,也会发生这种情况。

调用 GPURenderPassEncoder this.

参数:

Arguments for the GPURenderPassEncoder.executeBundles(bundles) method.
Parameter Type Nullable Optional Description
bundles sequence<GPURenderBundle> 要执行的渲染包的列表。

返回: undefined

内容时间线 步骤:

  1. 设备时间轴 上发布后续步骤。 this.[[device]].

设备时间线 步骤:
  1. 验证此的编码器状态。 如果返回false,请停止。

  2. 如果不满足以下任何条件,则使 this 无效 并停止。

  3. 对于 bundles 中的每个 bundle

    1. this.[[drawCount]] 增加 bundle.[[drawCount]]

  4. Clear this.[[bind_groups]].

  5. 设置 this.[[pipeline]]null.

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

  7. Clear this.[[vertex_buffers]].

  8. passState 成为 this状态的快照。

  9. this排队一个渲染命令 ,该命令在执行时在 队列时间轴 上发出带有 renderState 的后续步骤。

  1. 队列时间线 步骤:

  2. 对于 bundles 中的每个 bundle

    1. 使用 passStaterenderState 执行 bundle.[[command_list]] 中的每个命令。

      注意:执行渲染包不能更改 renderState。 还要注意,渲染包看不到可变的 passState 状态。

18.

包是一种部分的、有限的通道,它只编码一次,然后可以作为未来通道编码器的一部分多次执行,而不像典型的命令缓冲区那样在使用后过期。 这可以减少重复发出而不改变的命令的编码和提交的开销。

18.1. GPURenderBundle

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderBundle {
};
GPURenderBundle includes GPUObjectBase;
[[command_list]], of type list<GPU command>

要在执行 GPURenderBundle 时提交给 GPURenderPassEncoderGPU commandslist

[[layout]], of type GPURenderPassLayout

渲染包的布局。

[[depthReadOnly]], of type boolean

如果为 true,则表示执行此渲染包不会修改深度分量。

[[stencilReadOnly]], of type boolean

如果为 true,则表示执行此渲染包不会修改模板分量。

[[drawCount]], of type GPUSize64

GPURenderBundle 中的绘制命令数。

18.1.1. 渲染包创建

dictionary GPURenderBundleDescriptor
         : GPUObjectDescriptorBase {
};
[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderBundleEncoder {
    GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
};
GPURenderBundleEncoder includes GPUObjectBase;
GPURenderBundleEncoder includes GPUCommandsMixin;
GPURenderBundleEncoder includes GPUDebugCommandsMixin;
GPURenderBundleEncoder includes GPUBindingCommandsMixin;
GPURenderBundleEncoder includes GPURenderCommandsMixin;
createRenderBundleEncoder(descriptor)

创建一个 GPURenderBundleEncoder

调用: GPUDevice this.

参数:

Arguments for the GPUDevice.createRenderBundleEncoder(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPURenderBundleEncoderDescriptor 要创建的 GPURenderBundleEncoder 的描述。

返回: GPURenderBundleEncoder

内容时间线 步骤:

  1. ? Validate texture format required features of each non-null element of descriptor.colorFormats with this.[[device]].

  2. ? Validate texture format required features of descriptor.depthStencilFormat with this.[[device]].

  3. e 成为一个新的 GPURenderBundleEncoder 对象。

  4. 设备时间轴 上发布 initialization steps

  5. 返回 e.

设备时间线 initialization steps:
  1. 如果以下任何条件不满足 generate a validation error,则使 e 无效,并停止。

  2. e.[[layout]] 设置为 descriptor 的包含的 GPURenderPassLayout 接口的副本。

  3. e.[[depthReadOnly]] 设置为 descriptor.depthReadOnly

  4. e.[[stencilReadOnly]] 设置为 descriptor.stencilReadOnly

  5. e.[[state]] 设置为 "open"。

  6. e.[[drawCount]] 设置为 0。

问题:描述 createRenderBundleEncoder() 的步骤的重置。

18.1.2. 编码

dictionary GPURenderBundleEncoderDescriptor
         : GPURenderPassLayout {
    boolean depthReadOnly = false;
    boolean stencilReadOnly = false;
};
depthReadOnly, of type boolean, defaulting to false

如果为 true,则表示渲染包不会修改任何渲染通道中执行渲染包的 GPURenderPassDepthStencilAttachment 的深度分量。

stencilReadOnly, of type boolean, defaulting to false

如果为 true,则表示渲染包不会修改任何渲染通道中执行渲染包的 GPURenderPassDepthStencilAttachment 的模板分量。

18.1.3. 最终化

finish(descriptor)

完成渲染包命令序列的记录。

调用: GPURenderBundleEncoder this.

参数:

Arguments for the GPURenderBundleEncoder.finish(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPURenderBundleDescriptor

返回: GPURenderBundle

内容时间线 步骤:

  1. renderBundle 成为一个新的 GPURenderBundle

  2. 设备时间轴 上发布后续步骤。 this.[[device]].

  3. 返回 renderBundle.

  1. 设备时间线 finish steps

  2. 如果满足以下所有要求,则让 validationSucceededtrue,否则为 false

  3. this.[[state]] 设置为 "ended"。

  4. 如果 validationSucceededfalse,则:

    1. 生成验证错误

    2. 返回一个新的 invalid GPURenderBundle

  5. renderBundle.[[command_list]] 设置为 this.[[commands]].

  6. renderBundle.[[drawCount]] 设置为 this.[[drawCount]].

19. 查询

19.1. GPUQueueDescriptor

GPUQueueDescriptor 描述了一个队列请求。

dictionary GPUQueueDescriptor
         : GPUObjectDescriptorBase {
};

19.2. GPUQueue

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUQueue {
    undefined submit(sequence<GPUCommandBuffer> commandBuffers);

    Promise<undefined> onSubmittedWorkDone();

    undefined writeBuffer(
        GPUBuffer buffer,
        GPUSize64 bufferOffset,
        [AllowShared] BufferSource data,
        optional GPUSize64 dataOffset = 0,
        optional GPUSize64 size);

    undefined writeTexture(
        GPUImageCopyTexture destination,
        [AllowShared] BufferSource data,
        GPUImageDataLayout dataLayout,
        GPUExtent3D size);

    undefined copyExternalImageToTexture(
        GPUImageCopyExternalImage source,
        GPUImageCopyTextureTagged destination,
        GPUExtent3D copySize);
};
GPUQueue includes GPUObjectBase;

GPUQueue 有以下方法:

writeBuffer(buffer, bufferOffset, data, dataOffset, size)

将提供的数据写入 GPUBuffer 中。

调用: GPUQueue this.

参数:

Arguments for the GPUQueue.writeBuffer(buffer, bufferOffset, data, dataOffset, size) method.
Parameter Type Nullable Optional Description
buffer GPUBuffer 要写入的缓冲区。
bufferOffset GPUSize64 要开始写入的字节偏移量。
data BufferSource 要写入 buffer 的数据。
dataOffset GPUSize64 要从中开始写入的 data 中的偏移量。如果 dataTypedArray,则以元素为单位,否则以字节为单位。
size GPUSize64 要从 data 写入 buffer 的内容的大小。如果 dataTypedArray,则以元素为单位,否则以字节为单位。

返回: undefined

内容时间线 步骤:

  1. 如果 dataArrayBufferDataView,则将元素类型设置为 "byte"。否则,data 是 TypedArray;将元素类型设置为 TypedArray 的类型。

  2. dataSize 成为 data 的大小,以元素为单位。

  3. 如果 size 丢失,则让 contentsSize 成为 dataSizedataOffset。否则,让 contentsSize 成为 size

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

    • contentsSize ≥ 0.

    • dataOffset + contentsSizedataSize.

    • contentsSize,转换为字节,是 4 字节的倍数。

  5. dataContents 成为 a copy of the bytes held by the buffer source

  6. contents 成为从 dataOffset 元素开始的 dataContentscontentsSize 元素。

  7. thisDevice timeline 上发布后续步骤。

设备时间线 步骤:
  1. If any of the following conditions are unsatisfied, 如果不满足以下任何条件,则 generate a validation error 并停止。

  2. bufferOffset 开始将 contents 写入 buffer

writeTexture(destination, data, dataLayout, size)

将提供的数据写入 GPUTexture 中。

调用: GPUQueue this.

参数:

Arguments for the GPUQueue.writeTexture(destination, data, dataLayout, size) method.
Parameter Type Nullable Optional Description
destination GPUImageCopyTexture 要写入的 texture subresource 和原点。
data BufferSource 要写入 destination 的数据。
dataLayout GPUImageDataLayout data 中内容的布局。
size GPUExtent3D 要从 data 写入 destination 的内容的范围。

返回: undefined

内容时间线 步骤:

  1. ? validate GPUOrigin3D shape(destination.origin).

  2. ? validate GPUExtent3D shape(size).

  3. dataBytes 成为 a copy of the bytes held by the buffer source data

  4. thisDevice timeline 上发布后续步骤。

设备时间线 步骤:
  1. texture 成为 destination.texture

  2. If any of the following conditions are unsatisfied, 如果不满足以下任何条件,则 generate a validation error 并停止。

    注意:与 GPUCommandEncoder.copyBufferToTexture() 不同,dataLayout.bytesPerRowdataLayout.offset 上没有对齐要求。

  3. contents 成为通过使用 dataLayoutsize 查看 dataBytesimages 的内容。

    问题:更正式地指定。

    注意:这被描述为将 data 的所有内容复制到设备时间线,但实际上 data 可能比必要的大得多。实现应通过仅复制必要的字节来优化。

  4. thisQueue timeline 上发布后续步骤。

Queue timeline 步骤:
  1. contents 写入 destination

    问题:定义复制,包括 snorm 的规定。

copyExternalImageToTexture(source, destination, copySize)

将平台图像/画布的内容复制到目标纹理中。

此操作根据 GPUImageCopyTextureTagged 的参数执行 color encoding 到目标编码。

复制到 -srgb 纹理会导致相同的纹理字节,而不是相同的解码值,就像复制到相应的非 -srgb 格式一样。因此,在复制操作之后,根据其格式是否为 -srgb,对目标纹理进行采样会产生不同的结果,其他都不变。

调用: GPUQueue this.

参数:

Arguments for the GPUQueue.copyExternalImageToTexture(source, destination, copySize) method.
Parameter Type Nullable Optional Description
source GPUImageCopyExternalImage 要复制到 destination 的源图像和原点。
destination GPUImageCopyTextureTagged 要写入的 texture subresource 和原点,以及其编码元数据。
copySize GPUExtent3D 要从 source 写入 destination 的内容的范围。

返回: undefined

内容时间线 步骤:

  1. ? validate GPUOrigin2D shape(source.origin).

  2. ? validate GPUOrigin3D shape(destination.origin).

  3. ? validate GPUExtent3D shape(copySize).

  4. sourceImagesource.source

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

  6. 如果不满足以下任何要求,则抛出 OperationError 并停止。

    • source.origin.x + copySize.width 必须 ≤ the width of sourceImage.

    • source.origin.y + copySize.height 必须 ≤ the height of sourceImage.

    • source.origin.z + copySize.depthOrArrayLayers 必须 ≤ 1.

  7. usability? check the usability of the image argument(source).

  8. this设备时间线 上发布后续步骤。

设备时间线 步骤:
  1. texture 成为 destination.texture

  2. 如果不满足以下任何要求,则 generate a validation error 并停止。

  3. 问题:执行实际的复制。

submit(commandBuffers)

GPU 在此队列上安排执行命令缓冲区。

提交的命令缓冲区不能再次使用。

调用: GPUQueue this.

参数:

Arguments for the GPUQueue.submit(commandBuffers) method.
Parameter Type Nullable Optional Description
commandBuffers sequence<GPUCommandBuffer>

返回: undefined

内容时间线 步骤:

  1. this设备时间线 上发布后续步骤。

设备时间线 步骤:
  1. 如果不满足以下任何要求,则 generate a validation error 并停止。

  2. 对于 commandBuffers 中的每个 commandBuffer

    1. 使 commandBuffer invalid

  3. thisQueue timeline 上发布后续步骤。

队列时间线 步骤:
  1. 对于 commandBuffers 中的每个 commandBuffer

    1. 执行 commandBuffer.[[command_list]] 中的每个命令。

onSubmittedWorkDone()

返回一个 Promise,一旦此队列完成处理到目前为止提交的所有工作,就会解析。

Promise 的解析意味着在该调用之前对 GPUBuffers 进行的 mapAsync() 调用的完成,该调用仅在该队列上独占使用。

调用: GPUQueue this.

返回: Promise<undefined>

内容时间线 步骤:

  1. contentTimeline 成为当前的 Content timeline

  2. promise 成为 a new promise

  3. thisDevice timeline 上发布 synchronization steps

  4. 返回 promise

Device timeline synchronization steps: // Device timeline synchronization steps
  1. device timeline 被告知 this 上的所有 currently-enqueued operations 的完成,或者如果 this 丢失,或者当 this becomes lost 时:

    1. contentTimeline 上发布后续步骤。

Content timeline 步骤:
  1. Resolve promise.

20. 查询

20.1. GPUQuerySet

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUQuerySet {
    undefined destroy();

    readonly attribute GPUQueryType type;
    readonly attribute GPUSize32 count;
};
GPUQuerySet includes GPUObjectBase;

GPUQuerySet 有以下属性:

type, of type GPUQueryType, readonly

GPUQuerySet 管理的查询的类型。

count, of type GPUSize32, readonly

GPUQuerySet 管理的查询的数量。

GPUQuerySet 有以下内部槽:

[[state]], of type query set state

GPUQuerySet 的当前状态。

每个 GPUQuerySetDevice timeline 上都有一个当前的 query set state,它是以下之一:

"available"

GPUQuerySet 可用于其内容的 GPU 操作。

"destroyed"

GPUQuerySet 不再可用于除 destroy 之外的任何操作。

20.1.1. 查询集创建

GPUQuerySetDescriptor 指定在创建 GPUQuerySet 时要使用的选项。

dictionary GPUQuerySetDescriptor
         : GPUObjectDescriptorBase {
    required GPUQueryType type;
    required GPUSize32 count;
};
type, of type GPUQueryType

GPUQuerySet 管理的查询的类型。

count, of type GPUSize32

GPUQuerySet 管理的查询的数量。

createQuerySet(descriptor)

Creates a GPUQuerySet.

调用: GPUDevice this.

参数:

Arguments for the GPUDevice.createQuerySet(descriptor) method.
Parameter Type Nullable Optional Description
descriptor GPUQuerySetDescriptor 要创建的 GPUQuerySet 的描述。

返回: GPUQuerySet

内容时间线 步骤:

  1. 如果 descriptor.type"timestamp", 但 "timestamp-query"enabled for this

    1. 抛出 TypeError

  2. q 成为一个新的 GPUQuerySet 对象。

  3. q.type 设置为 descriptor.type

  4. q.count 设置为 descriptor.count

  5. thisDevice timeline 上发布 initialization steps

  6. 返回 q

Device timeline initialization steps:
  1. 如果不满足以下任何要求,则 generate a validation error,使 q invalid,并停止。

    • thisvalid

    • descriptor.count 必须 ≤ 4096。

  2. q.[[state]] 设置为 available

创建一个保存 32 个遮挡查询结果的 GPUQuerySet
const querySet = gpuDevice.createQuerySet({
    type: 'occlusion',
    count: 32
});

20.1.2. 查询集销毁

不再需要 GPUQuerySet 的应用程序可以选择在垃圾回收之前通过调用 destroy() 来丢失对它的访问。

destroy()

销毁 GPUQuerySet

调用: GPUQuerySet this.

返回: undefined

内容时间线 步骤:

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

20.2. 查询类型

enum GPUQueryType {
    "occlusion",
    "timestamp",
};

20.3. 遮挡查询

遮挡查询仅在渲染通道上可用,用于查询一组绘图命令的所有片段测试通过的片段样本数,包括剪刀、样本掩码、alpha 到覆盖、模板和深度测试。查询的任何非零结果值都表示至少有一个样本通过了测试并到达了渲染管道的输出合并阶段,0 表示没有样本通过了测试。

在开始渲染通道时,GPURenderPassDescriptor.occlusionQuerySet 必须设置为能够在通道期间使用遮挡查询。通过调用 beginOcclusionQuery()endOcclusionQuery() 来开始和结束遮挡查询,这些调用不能嵌套。

20.4. 时间戳查询

时间戳查询允许应用程序将时间戳写入 GPUQuerySet,使用:

然后使用 GPUCommandEncoder.resolveQuerySet() 将时间戳值(以 64 位无符号整数的纳秒为单位)解析到 GPUBuffer 中。

时间戳查询需要 "timestamp-query" 在设备上 enabled for

注意:如果物理设备重置时间戳计数器,则时间戳值可能为零,请忽略它和以下值。

Issue(gpuweb/gpuweb#4069):编写关于时间戳值重置的规范文本。

There is a tracking vector here. 时间戳查询可以提供高分辨率的 GPU 计时。 有关安全考虑,请参见 § 2.1.7 定时攻击

Validate timestampWrites(GPUDevice device, (GPUComputePassTimestampWrites or GPURenderPassTimestampWrites) timestampWrites)

如果满足以下要求,则返回 true,否则返回 false

21. Canvas 渲染

21.1. HTMLCanvasElement.getContext()

GPUCanvasContext 对象由 HTMLCanvasElement 中的 getContext() 方法创建。 通过传递字符串字面量 'webgpu' 实例化其 contextType 参数。

从离屏的 HTMLCanvasElement 获取一个 GPUCanvasContext
const canvas = document.createElement('canvas');
const context = canvas.getContext('webgpu');

与WebGL或2D上下文创建不同的是, HTMLCanvasElement.getContext()OffscreenCanvas.getContext() 的第二个参数,即上下文创建属性字典 options 会被忽略。而是使用 GPUCanvasContext.configure() 来改变画布的配置,而无需替换画布本身。

To create a 'webgpu' context on a canvasHTMLCanvasElementOffscreenCanvascanvas:
  1. 使 context 成为一个新的 GPUCanvasContext.

  2. 设置 context.canvascanvas.

  3. Replace the drawing buffer of context.

  4. 返回 context.

注意:当调用 getContext() 获取 WebGPU 画布上下文时,用户代理应考虑在提供了被忽略的 options 参数时发出对开发者可见的警告。

21.2. GPUCanvasContext

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCanvasContext {
    readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;

    undefined configure(GPUCanvasConfiguration configuration);
    undefined unconfigure();

    GPUTexture getCurrentTexture();
};

GPUCanvasContext 有下列属性:

canvas, of type (HTMLCanvasElement or OffscreenCanvas), readonly

context 创建位置的画布。

GPUCanvasContext 具有以下内部槽位(internal slots):

[[configuration]], GPUCanvasConfiguration? 类型, 初始为 null

当前为该上下文配置的选项。

如果上下文尚未配置或已被取消配置,则为 null

[[textureDescriptor]], GPUTextureDescriptor? 类型, 初始为 null

当前配置的纹理描述符,根据[[configuration]]和画布派生而来。

如果上下文尚未配置或已被取消配置,则为 null

[[drawingBuffer]], 图像, 初始为 一个与画布大小相同的透明黑色图像。

绘图缓冲区是画布的工作副本图像数据。它通过[[currentTexture]](由getCurrentTexture()返回)可写访问。

绘图缓冲区用于get a copy of the image contents of a context,这在画布被显示或以其他方式读取时发生。即使[[configuration]].alphaMode"opaque",它也可能是透明的。alphaMode仅影响 get a copy of the image contents of a context 算法的结果。

绘图缓冲区的生命周期超过[[currentTexture]],并且即使画布已经呈现,它仍然包含先前渲染的内容。它只在“Replace the drawing buffer”时被清除。

每当读取绘图缓冲区时,实现必须确保所有先前提交的工作(例如队列提交)通过[[currentTexture]]已完成对其的写入。

[[currentTexture]], GPUTexture? 类型, 初始为 null

GPUTexture 用于当前帧绘制的纹理。 它提供了对底层 [[drawingBuffer]] 的可写视图。 如果 null ,则 getCurrentTexture() 会填充该槽,然后返回它。

在可见画布的稳态下,通过 currentTexture 对绘图缓冲区进行的任何更改在 updating the rendering of a WebGPU canvas 时呈现出来。 在此点或之前,纹理也会被销毁,[[currentTexture]] 被设置为 null,表示下一个调用 getCurrentTexture() 将创建一个新的纹理。

销毁 currentTexture 对绘图缓冲区内容没有影响,它只会提前终止对绘图缓冲区的写访问。 在同一帧中,getCurrentTexture() 仍然返回相同的已销毁纹理。

Expire the current texture 将 currentTexture 设置为 null 。 它由 configure()、画布调整大小、呈现、transferToImageBitmap() 和其他操作调用。

GPUCanvasContext 有下列方法:

configure(configuration)

配置此画布的上下文。 这将清除绘图缓冲区,使其变为透明黑色(在 Replace the drawing buffer 中)。

Called on: GPUCanvasContext this.

参数:

Arguments for the GPUCanvasContext.configure(configuration) method.
Parameter Type Nullable Optional Description
configuration GPUCanvasConfiguration 期望的上下文配置。

返回: undefined

Content timeline 步骤:

  1. deviceconfigurationdevice

  2. ? 使用 device.[[device]] 验证 configuration.formatValidate texture format required features

  3. ? 对于 configuration.viewFormats 中的每个元素,使用 device.[[device]] Validate texture format required features

  4. descriptorGPUTextureDescriptor for the canvas and configuration(this.canvas, configuration)。

  5. this.[[configuration]] 设置为 configuration

  6. this.[[textureDescriptor]] 设置为 descriptor

  7. Replace the drawing buffer,重置 this.[[drawingBuffer]] 为具有新格式和标签的位图。

  8. deviceDevice timeline 上执行后续步骤。

Device timeline 步骤:
  1. 如果以下任何要求未满足,则 generate a validation error 并停止。

    注意:这种早期验证在下一次 configure() 调用之前仍然有效,但是在画布调整大小时,size 的验证会发生变化。

unconfigure()

移除上下文配置。销毁在配置期间创建的任何纹理。

Called on: GPUCanvasContext this.

返回: undefined

Content timeline 步骤:

  1. this.[[configuration]] 设置为 null

  2. this.[[textureDescriptor]] 设置为 null

  3. Replace the drawing buffer,重置 this 的绘图缓冲区。

getCurrentTexture()

获取下一个将由 GPUCanvasContext 合成到文档中的 GPUTexture

注意: 应用程序应在渲染到画布纹理的同一任务中调用 getCurrentTexture()。 否则,在应用程序完成对其进行渲染之前,纹理可能会被这些步骤销毁。

过期任务(如下所定义)是可选实现的。 即使实现了过期任务,任务源的优先级没有明确定义,因此可能会在下一个任务中或在所有其他任务源为空之后发生(请参阅 automatic expiry task source)。 只有在显示可见画布(updating the rendering of a WebGPU canvas)以及其他调用者 Replace the drawing buffer 时,过期才得到保证。

Called on: GPUCanvasContext this.

返回: GPUTexture

Content timeline 步骤:

  1. 如果 this.[[configuration]]null

    1. 抛出一个 InvalidStateError 并停止。

  2. Assert this.[[textureDescriptor]] 不是 null

  3. devicethis.[[configuration]].device

  4. 如果 this.[[currentTexture]]null

    1. Replace the drawing buffer,重置 this.[[drawingBuffer]]

    2. this.[[currentTexture]] 设置为调用 device.createTexture() 的结果, 参数为 this.[[textureDescriptor]],但使用 GPUTexture 的底层存储指向 this.[[drawingBuffer]]

      注意: 如果无法创建纹理(例如由于验证失败或内存不足),则会生成错误并返回一个无效的 GPUTexture。 这里的一些验证与 configure() 中的验证重复。实现 不得 跳过这些重复的验证。

  5. 可选地queue an automatic expiry task,使用设备 device 和以下步骤:

    1. Expire the current texture,对 this 执行该操作。

      注意:如果在 updating the rendering of a WebGPU canvas 时已经发生过此操作,则其不会产生任何效果。

  6. 返回 this.[[currentTexture]].

注意:在 "Expire the current texture" 运行之前,每次调用 getCurrentTexture() 都将返回相同的 GPUTexture 对象,即使该 GPUTexture 被销毁、验证失败或分配失败。

To get a copy of the image contents of a context:

参数:

返回: image contents

  1. 确保所有提交的工作项(例如队列提交)已完成对图像(通过 context.[[currentTexture]])的写入。

  2. snapshotcontext.[[drawingBuffer]] 的副本。

  3. alphaModecontext.[[configuration]].alphaMode

  4. 如果 alphaMode"opaque":
    1. snapshot 的 alpha 通道清除为 1.0。

    2. snapshot 标记为不透明。

    注意: 如果 [[currentTexture]](如果有的话)已被销毁(例如在 Replace the drawing buffer 中),则无法观察到 alpha 通道,实现可能会原地清除 alpha 通道。

    否则:

    使用 alphaModesnapshot 进行标记。

  5. 返回 snapshot

GPUCanvasContext context 执行 Replace the drawing buffer
  1. Expire the current texture,对 context 执行该操作。

  2. configurationcontext.[[configuration]]

  3. context.[[drawingBuffer]] 设置为与 context.canvas 相同尺寸的透明黑色图像。

    • 如果 configuration 为 null,则绘图缓冲区标记为颜色空间 "srgb"。 在这种情况下,绘图缓冲区将保持空白,直到配置上下文。

    • 如果不是,则绘图缓冲区具有指定的 configuration.format, 并带有指定的 configuration.colorSpace 标记。

注意:在 "get a copy of the image contents of a context" 之前,将忽略 configuration.alphaMode

注意:如果绘图缓冲区已经被清除并具有正确的配置,这通常不会产生任何操作。

Expire the current texture 作用于一个 GPUCanvasContext context:
  1. 如果 context.[[currentTexture]] 不为 null:

    1. 调用 context.[[currentTexture]].destroy()(不销毁 context.[[drawingBuffer]])终止图像的写权限。

    2. context.[[currentTexture]] 设定为 null.

21.3. HTML 规范 Hooks

以下算法 "hook" 到 HTML 规范中的算法中,并必须在指定的时机运行。

当从具有 GPUCanvasContext contextHTMLCanvasElementOffscreenCanvas 读取 "位图(bitmap)" 时:
  1. 返回 context图像副本

    注意: 这种情况发生在许多地方,包括:

    如果 alphaMode"opaque", 这将导致 alpha 通道被清除。当实现能够以忽略 alpha 通道的方式读取或显示图像时,实现可能会跳过此步骤。

    如果应用程序仅需要用于交互操作(而非呈现),则在不需要时避免使用 "opaque"

updating the rendering of a WebGPU canvas (一个 HTMLCanvasElement 或一个具有 placeholder canvas elementOffscreenCanvas) 在具有 GPUCanvasContext contextHTMLCanvasElement 或具有 placeholder canvas elementOffscreenCanvas 中, 这会在 event loop processing model 的以下子步骤中发生:

执行以下步骤:

  1. context 运行 Expire the current texture

注意:如果这已经在由 getCurrentTexture() 排队的任务中发生过, 则没有任何效果。

注意: 这不适用于独立的 OffscreenCanvas(通过 new OffscreenCanvas() 创建的)。

当在具有 GPUCanvasContext context 的画布上调用 transferToImageBitmap() 时,在从画布的位图创建 ImageBitmap 后:
  1. context 运行 Replace the drawing buffer

注意:这相当于将(可能已清除 alpha 通道)的图像内容 "移动" 到 ImageBitmap 中,而不进行复制。

21.4. GPU画布配置(CanvasConfiguration)

supported context formats 是一个 setGPUTextureFormat,当作为 GPUCanvasConfiguration.format 指定时, 无论给定的 GPUCanvasConfiguration.device 如何,都必须支持这些格式。 初始设置为:«;"bgra8unorm", "rgba8unorm", "rgba16float"»。

注意:画布配置不能使用 srgb 格式,如 "bgra8unorm-srgb"。而应使用非 srgb 等效格式("bgra8unorm"),在 viewFormats 中指定 srgb 格式,并使用 createView() 创建具有 srgb 格式的视图。

enum GPUCanvasAlphaMode {
    "opaque",
    "premultiplied",
};

dictionary GPUCanvasConfiguration {
    required GPUDevice device;
    required GPUTextureFormat format;
    GPUTextureUsageFlags usage = 0x10;  // GPUTextureUsage.RENDER_ATTACHMENT
    sequence<GPUTextureFormat> viewFormats = [];
    PredefinedColorSpace colorSpace = "srgb";
    GPUCanvasAlphaMode alphaMode = "opaque";
};

GPUCanvasConfiguration 有下列成员:

device, of type GPUDevice

getCurrentTexture() 返回的纹理与之兼容的 GPUDevice

format, of type GPUTextureFormat

getCurrentTexture() 返回的纹理格式。 必须是 Supported context formats 中的一个。

usage, of type GPUTextureUsageFlags, defaulting to 0x10

getCurrentTexture() 返回的纹理使用方式。 RENDER_ATTACHMENT 是默认值,但如果显式设置了使用方式,则不会自动包括它。 如果希望将getCurrentTexture()返回的纹理用作渲染通道的颜色目标,请确保在设置自定义使用方式时包括RENDER_ATTACHMENT

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

getCurrentTexture()返回的纹理创建的视图可以使用的格式。

colorSpace, of type PredefinedColorSpace, defaulting to "srgb"

getCurrentTexture() 返回的纹理中写入的值应该使用的颜色空间进行显示。

alphaMode, of type GPUCanvasAlphaMode, defaulting to "opaque"

确定 alpha 值对从 getCurrentTexture() 返回的纹理内容在读取、显示或用作图像源时的影响。

使用特定的 GPUDevice 配置 GPUCanvasContext,使用此context的首选格式:
const canvas = document.createElement('canvas');
const context = canvas.getContext('webgpu');

context.configure({
    device: gpuDevice,
    format: navigator.gpu.getPreferredCanvasFormat(),
});
The GPUTextureDescriptor for the canvas and configuration( (HTMLCanvasElementOffscreenCanvas) canvas, GPUCanvasConfiguration configuration) 是有以下成员的 GPUTextureDescriptor

并将其他成员设置为它们的默认值。

canvas.width 表示 HTMLCanvasElement.widthOffscreenCanvas.width. canvas.height 表示 HTMLCanvasElement.heightOffscreenCanvas.height.

21.4.1. 画布颜色空间

在演示过程中,色值在 [0, 1] 范围之外的色度不应被截断至该范围;当format和用户的显示能力允许时,可以使用扩展值来显示画布 “颜色空间” 原色定义之外的颜色。 这与亮度相反,亮度应被截断至最大标准动态范围亮度。

21.4.2. 画布上下文的尺寸调整(Context sizing)

所有画布配置都在configure()中进行设置,除了画布的分辨率,分辨率是通过画布的 widthheight 来设置的。

注意: 与WebGL和2D画布一样,调整WebGPU画布的大小会导致绘图缓冲区的当前内容丢失。 在WebGPU中,它通过replacing the drawing buffer来实现这一点。

当具有GPUCanvasContext contextHTMLCanvasElementOffscreenCanvas canvaswidthheight 属性被修改时, update the canvas size:
  1. Replace the drawing buffercontext

  2. configuration 设为 context.[[configuration]]

  3. 如果 configuration 不为 null

    1. context.[[textureDescriptor]] 设置为 GPUTextureDescriptor for the canvas and configurationcanvas, configuration)。

注意:这可能导致GPUTextureDescriptor超过设备的maxTextureDimension2D。 在这种情况下,验证将在getCurrentTexture()中失败。

响应画布大小调整,使用ResizeObserver监测画布大小变化,并重新配置GPUCanvasContext以获取画布的准确像素尺寸:
const canvas = document.createElement('canvas');
const context = canvas.getContext('webgpu');

const resizeObserver = new ResizeObserver(entries => {
    for (const entry of entries) {
        if (entry.target != canvas) { continue; }
        canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
        canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
    }
});
resizeObserver.observe(canvas);

21.5. GPUCanvasAlphaMode

此枚举用于选择当读取画布内容时,画布的内容在显示到屏幕或作为图像源(在drawImage、toDataURL等中使用)时将如何被解释。

以下,src 是画布纹理中的值,dst 是画布被合成到的图像(例如HTML页面渲染或2D画布)。

"opaque"

将 RGB 作为不透明色,并忽略 alpha 值。 如果内容尚未不透明,则在 "get a copy of the image contents of a context" 中将 alpha 通道清除为 1.0。

"premultiplied"

以预乘方式读取 RGBA:颜色值通过其 alpha 值进行预乘。 50% alpha 的 100% 红色为 [0.5, 0, 0, 0.5]

如果输出到画布的out-of-gamut premultiplied RGBA values,且画布是:

作为图像源

值将按照 颜色空间转换 中描述的方式进行保留。

显示到屏幕

合成结果是未定义的。即使在合成之前颜色空间转换会产生在色域内的值也是如此,因为合成的中间格式没有指定。

22. 错误 & 调试

在一般情况下,WebGPU的正常操作过程中,通过dispatch error来引发错误。

在设备 丢失(描述如下) 后,不再暴露错误。 此时,实现不需要运行验证或错误跟踪: popErrorScope()uncapturederror 停止报告错误, 并且设备上对象的有效性变得不可观察。

此外,设备丢失本身不会产生错误。 相反的,GPUDevice.lost promise 解析来指示设备已丢失。

22.1. 致命错误

enum GPUDeviceLostReason {
    "unknown",
    "destroyed",
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUDeviceLostInfo {
    readonly attribute GPUDeviceLostReason reason;
    readonly attribute DOMString message;
};

partial interface GPUDevice {
    readonly attribute Promise<GPUDeviceLostInfo> lost;
};

GPUDevice 有下列额外的属性:

lost, of type Promise<GPUDeviceLostInfo>, readonly

一个 slot-backed attribute 持有一个 promise,该 promise 在设备创建时创建,设备的生命周期内保持 pending,然后在设备丢失时解析。 在初始化时,它被设置为 a new promise

22.2. GPUError

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUError {
    readonly attribute DOMString message;
};

GPUError 是从 popErrorScope()uncapturederror 事件中暴露的所有错误的基本接口。

错误只能在其各自的算法中明确说明可能生成的条件和生成的错误子类型的操作中生成。

在设备丢失后不会生成错误。参见§ 22 错误 & 调试

注意:GPUError 可能在本规范的将来版本中获得新的子类型。应用程序应该处理这种可能性,尽可能使用错误的 message,并使用 instanceof 进行特化。当需要序列化错误(例如,进入 JSON,用于调试报告)时,请使用 error.constructor.name

GPUError 具有以下属性:

message, of type DOMString, readonly

一个人类可读的,提供有关发生的错误的信息的 localizable text 消息。

注意:此消息通常用于应用程序开发人员调试其应用程序并捕获调试报告的信息,而不是向最终用户显示。

注意:用户代理不应在此消息中包含可能的机器可解析细节,例如 "out-of-memory" 上的空闲系统内存或内存耗尽的其他条件的其他细节。

注意:message 应遵循 best practices for language and direction information。这包括利用可能出现的任何关于报告字符串语言和方向元数据的未来标准。

Editorial: 在撰写本文时,尚无可用的语言/方向建议,可与遗留 API 兼容并保持一致,但是当有时,请正式采用它。

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUValidationError
        : GPUError {
    constructor(DOMString message);
};

GPUValidationErrorGPUError 的子类型,它指示操作未满足所有验证要求。验证错误始终表示应用程序错误,并且预计在使用相同的 [[features]][[limits]] 的所有设备上以相同的方式失败。

GPUDevice device generate avalidation error,运行以下步骤:
Content timeline 步骤:
  1. error 成为一个带有适当错误消息的新 GPUValidationError

  2. Dispatch error errordevice

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUOutOfMemoryError
        : GPUError {
    constructor(DOMString message);
};

GPUOutOfMemoryErrorGPUError 的子类型,它指示没有足够的空闲内存来完成所请求的操作。如果使用较低的内存要求(例如使用较小的纹理尺寸)再次尝试操作,或者首先释放其他资源使用的内存,则操作可能会成功。

GPUDevice device generate an out-of-memory error,运行下列步骤
Content timeline 步骤:
  1. error 成为一个带有适当错误消息的新 GPUOutOfMemoryError

  2. Dispatch error errordevice

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUInternalError
        : GPUError {
    constructor(DOMString message);
};

GPUInternalErrorGPUError 的子类型,它指示即使满足了所有验证要求,操作也因系统或实现特定原因而失败。 例如,操作可能以一种不容易被supported limits捕捉到的方式超出了实现的能力。相同的操作可能在其他设备上或在不同的情况下成功。

对于GPUDevice device generate an internal error,运行以下步骤:
Content timeline 步骤:
  1. error 成为一个带有适当错误消息的新 GPUInternalError

  2. Dispatch error errordevice

22.3. 错误范围

一个 GPU error scope 捕获在当前GPU error scope中生成的 GPUError。错误范围用于隔离在一组 WebGPU 调用中发生的错误,通常用于调试目的或使操作更具容错性。

GPU error scope 有以下内部槽(slot):

[[errors]], 类型为 list<GPUError>,初始值 []

当前GPU error scope中观察到的 GPUError(如果有)。

[[filter]],类型为 GPUErrorFilter

确定此 GPU error scope 观察的 GPUError 的类型。

enum GPUErrorFilter {
    "validation",
    "out-of-memory",
    "internal",
};

partial interface GPUDevice {
    undefined pushErrorScope(GPUErrorFilter filter);
    Promise<GPUError?> popErrorScope();
};

GPUErrorFilter 定义了在调用 pushErrorScope() 时应该捕获的错误类型:

pushErrorScope():

"validation"

表示错误范围将捕获 GPUValidationError

"out-of-memory"

表示错误范围将捕获 GPUOutOfMemoryError

"internal"

表示错误范围将捕获 GPUInternalError

GPUDevice 有以下内部槽(slot):

[[errorScopeStack]],类型为 stack<GPU error scope>

已推送到 GPUDeviceGPU error scopesstack

GPUError errorGPUDevice devicecurrent error scope 由向 deviceDevice timeline 发出以下步骤来确定:
Device timeline 步骤:
  1. 如果 error 是以下实例之一:

    GPUValidationError

    type 为 "validation"。

    GPUOutOfMemoryError

    type 为 "out-of-memory"。

    GPUInternalError

    type 为 "internal"。

  2. scopedevice.[[errorScopeStack]] 的最后一个 item

  3. scope 不是 undefined 时:

    1. 如果 scope.[[filter]]type,则返回 scope

    2. scope 设置为 device.[[errorScopeStack]] 的前一个 item

  4. 返回 undefined

要在 GPUDevice devicedispatch an error GPUError error,请在 deviceDevice timeline 上运行以下步骤:
Device timeline 步骤:
  1. 如果 devicelost,则返回。

    注意:设备丢失后不会生成任何错误。请参阅 § 22 错误 & 调试

  2. scopeerrordevicecurrent error scope

  3. 如果 scope 不是 undefined

    1. error Appendscope.[[errors]]

    2. 返回。

  4. 否则向 Content timeline 发出以下步骤:

Content timeline 步骤:
  1. 如果用户代理选择,queue a global task for GPUDevice device,并执行以下步骤:

    1. device 上触发一个名为 "uncapturederror" 的 GPUUncapturedErrorEvent,其 errorerror

注意:如果(且仅当)没有注册 uncapturederror 处理程序,则用户代理应该将未捕获的错误显示给开发人员,例如作为浏览器开发人员控制台中的警告。

注意:用户代理可以选择限制 GPUDevice 可以引发的 GPUUncapturedErrorEvent 的数量,以防止过多的错误处理或日志记录影响性能。

pushErrorScope(filter)

将一个新的 GPU error scope 推入 this[[errorScopeStack]]

Called on: GPUDevice this.

参数:

Arguments for the GPUDevice.pushErrorScope(filter) method.
Parameter Type Nullable Optional Description
filter GPUErrorFilter 此错误范围观察的错误类。

返回: undefined

Content timeline 步骤:

  1. thisDevice timeline 上发出后续步骤。

Device timeline 步骤:
  1. scope 为一个新的 GPU error scope

  2. scope.[[filter]] 设置为 filter

  3. scope Pushthis.[[errorScopeStack]]

popErrorScope()

this[[errorScopeStack]] 弹出一个 GPU error scope,并解析为错误范围观察到的任何 GPUError,如果没有则为 null

不能保证承诺解析的顺序。

Called on: GPUDevice this.

返回: Promise<GPUError?>

Content timeline 步骤:

  1. contentTimeline为当前Content timeline

  2. promisea new promise

  3. thisDevice timeline上发出check steps

  4. 返回promise

Device timeline check steps
  1. 如果 thislost,则在 contentTimeline 上发出以下步骤并返回:

    Content timeline 步骤:
    1. null Resolve promise

    注意:设备丢失后不会生成错误。请参阅 § 22 错误 & 调试

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

    然后在 contentTimeline 上发出以下步骤并返回:

    Content timeline 步骤:
    1. OperationError Reject promise

  3. scope 为从 this.[[errorScopeStack]] popping一个 item 的结果。

  4. errorscope.[[errors]] 中的任何一个,如果没有则为 null

    对于列表中的任何两个错误 E1 和 E2,如果 E2 是由 E1 导致的,则不应该选择 E2。

    注意: 例如,如果 E1 来自 t = createTexture(),并且 E2 来自 t.createView(),因为 tinvalid,则应该优先选择 E1,因为开发人员更容易理解出了什么问题。 由于这两个都是 GPUValidationError,唯一的区别在于 message 字段,而这个字段本来就是为人类阅读而设计的。

  5. 现在或将来的某个未指定的时间点,在 contentTimeline 上发出后续步骤。

    注意: 通过允许 popErrorScope() 调用以任何顺序解析,以及允许使用范围内观察到的任何错误,该规范允许验证以任意顺序完成,只要任何状态观察都是在遵守该规范的适当时间点进行的。例如,这允许实现在后台线程上执行仅取决于非状态输入的着色器编译,以与其他设备时间线工作并行完成,并稍后报告任何导致的错误。

Content timeline 步骤:
  1. error Resolve promise

使用错误范围来捕获可能失败的 GPUDevice 操作的验证错误:
gpuDevice.pushErrorScope('validation');

let sampler = gpuDevice.createSampler({
    maxAnisotropy: 0, // Invalid, maxAnisotropy must be at least 1.
});

gpuDevice.popErrorScope().then((error) => {
    if (error) {
        // There was an error creating the sampler, so discard it.
        sampler = null;
        console.error(`An error occured while creating sampler: ${error.message}`);
    }
});
注意:错误范围可以涵盖尽可能多的命令。错误范围覆盖的命令数量通常与应用程序对错误的响应行为相关。

例如:一个只包含创建单个资源(如纹理或缓冲区)的错误范围可以用于检测失败情况,比如内存不足的情况,此时应用程序可以尝试释放一些资源并再次进行分配。

然而,错误范围并不标识哪个命令失败。因此,例如将加载模型期间执行的所有命令包装在一个单独的错误范围中将无法提供足够的细粒度来确定问题是否是由于内存限制引起的。因此,释放资源通常不是对该范围的失败做出的有效响应。更合适的响应应该是允许应用程序回退到不同的模型或产生一个无法加载模型的警告。如果希望对内存限制作出响应,分配内存的操作总是可以包装在一个较小的嵌套错误范围中。

22.4. 遥测

当一个 GPUError 被生成,但没有被任何 GPU error scope 观察到时,用户代理可能会使用 GPUUncapturedErrorEventGPUDevicefire an event,并命名为 uncapturederror

注意:uncapturederror 事件旨在用于遥测和报告意外错误。它们可能不会被分派给所有未捕获的错误(例如,可能存在一个限制,用于显示的错误数量),并且不应该用于处理在应用程序正常运行期间可能发生的已知错误情况。在这些情况下,应该优先使用 pushErrorScope()popErrorScope()

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUUncapturedErrorEvent : Event {
    constructor(
        DOMString type,
        GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict
    );
    [SameObject] readonly attribute GPUError error;
};

dictionary GPUUncapturedErrorEventInit : EventInit {
    required GPUError error;
};

GPUUncapturedErrorEvent 有以下属性:

error, of type GPUError, readonly

一个 slot-backed attribute,保存一个表示未捕获的错误的对象。它的类型与 popErrorScope() 返回的错误相同。

partial interface GPUDevice {
    [Exposed=(Window, DedicatedWorker)]
    attribute EventHandler onuncapturederror;
};

GPUDevice 有以下属性:

onuncapturederror, of type EventHandler

uncapturederror 事件类型的一个 event handler IDL attribute

监听 GPUDevice 的未捕获错误:
gpuDevice.addEventListener('uncapturederror', (event) => {
    // Re-surface the error, because adding an event listener may silence console logs.
    console.error('A WebGPU error was not captured:', event.error);

    myEngineDebugReport.uncapturedErrors.push({
        type: event.error.constructor.name,
        message: event.error.message,
    });
});

23. 详细操作

这一部分描述了各种 GPU 操作的细节。

问题:这一部分是不完整的。

23.1. Transfer

Editorial: describe the transfers at the high level

23.2. 计算

计算操作提供了对 GPU 可编程硬件的直接访问。计算着色器没有着色器阶段的输入或输出,它们的结果是将数据写入绑定为 GPUBufferBindingType."storage"GPUStorageTextureBindingLayout 的存储绑定中的副作用。这些操作在 GPUComputePassEncoder 中被编码为:

Editorial: describe the computing algorithm

如果着色器没有在合理的时间内 执行完毕,由用户代理决定,那么 device 可能会 丢失设备

23.3. 渲染

渲染是通过一组 GPU 操作来完成的,这些操作在 GPURenderPassEncoder 中执行,并导致纹理数据的修改,这些纹理数据由渲染通道附件查看。这些操作是用以下方式编码的:

注意:渲染是 GPU 的传统用法,并且由硬件中的多个固定功能块支持。

主要的渲染算法:

render(descriptor, drawCall, state)

参数:

  1. 解析索引。参见 § 23.3.1 解析索引

    vertexList 成为 resolve indices(drawCall, state) 的结果。

  2. 处理顶点。参见 § 23.3.2 顶点处理

    执行 process vertices(vertexList, drawCall, descriptor.vertex, state)。

  3. 组装原语。参见 § 23.3.3 基本体部件

    执行 assemble primitives(vertexList, drawCall, descriptor.primitive)。

  4. 裁剪原语。参见 § 23.3.4 基本体剪裁

    primitiveList 成为这个阶段的结果。

  5. 光栅化。参见 § 23.3.5 光栅化

    rasterizationList 成为 rasterize(primitiveList, state) 的结果。

  6. 处理片段。参见 § 23.3.6 片段处理

    收集一个 fragments 列表,该列表是由执行 process fragment(rasterPoint, descriptor.fragment, state) 产生的,其中 rasterPointrasterizationList 中。

  7. 处理深度/模板

    Editorial: fill out the section, using fragments

  8. 写入像素

    Editorial: fill out the section

23.3.1. 解析索引

在渲染的第一个阶段,管线为每个实例构建了一个要处理的顶点列表。

resolve indices(drawCall, state)

参数:

返回:整数索引列表。

  1. vertexIndexList 成为一个空的索引列表。

  2. 如果 drawCall 是一个索引绘制调用:

    1. drawCall.indexCount 整数初始化 vertexIndexList

    2. 对于范围 0 .. drawCall.indexCount(非包含)中的 i

      1. relativeVertexIndex 成为 fetch index(i + drawCall.firstIndex, state.[[index_buffer]])。

      2. 如果 relativeVertexIndex 有特殊值 "out of bounds",停止并返回空列表。

        注意:实现可以选择在发生这种情况时显示警告,特别是在很容易检测到时(例如在非间接索引绘制调用中)。

      3. drawCall.baseVertex + relativeVertexIndex 添加到 vertexIndexList

  3. 否则:

    1. drawCall.vertexCount 整数初始化 vertexIndexList

    2. 将每个 vertexIndexListi 设置为值 drawCall.firstVertex + i

  4. 返回 vertexIndexList

注意:在间接绘制调用的情况下,将从间接缓冲区而不是绘制命令本身读取 drawCallindexCountvertexCount 和其他属性。

Editorial: specify indirect commands better.

fetch index(i, buffer, offset, format)

参数:

返回:无符号整数或 "out of bounds"

  1. indexSizestate.[[index_format]] 定义:

    "uint16"

    2

    "uint32"

    4

  2. 如果 state.[[index_buffer_offset]] + |i + 1| × indexSize > state.[[index_buffer_size]],返回特殊值 "out of bounds"

  3. 解释 state.[[index_buffer]] 中的数据,从偏移量 state.[[index_buffer_offset]] + i × indexSize 开始,大小为 indexSize 字节,作为无符号整数并返回它。

23.3.2. 顶点处理

顶点处理阶段是渲染 pipeline 的可编程阶段,它处理顶点属性数据,并为 § 23.3.4 基本体剪裁 生成裁剪空间位置,以及其他 § 23.3.6 片段处理 的数据。

process vertices(vertexIndexList, drawCall, desc, state)

参数:

vertexIndexList 中的每个顶点 vertexIndex,在每个索引 rawInstanceIndex 的实例中,都是独立处理的。rawInstanceIndex 的范围是从 0 到 drawCall.instanceCount - 1,包括两端。这个处理是并行的,任何副作用,比如写入 GPUBufferBindingType."storage" 绑定,都可能以任何顺序发生。

  1. instanceIndexrawInstanceIndex + drawCall.firstInstance。

  2. 对于 desc.buffers 列表中的每个非 null vertexBufferLayout

    1. i 为此列表中缓冲区布局的索引。

    2. vertexBuffervertexBufferOffsetvertexBufferBindingSizestate.[[vertex_buffers]] 的槽 i 处的缓冲区、偏移量和大小。

    3. vertexElementIndex 依赖于 vertexBufferLayout.stepMode

      "vertex"

      vertexIndex

      "instance"

      instanceIndex

    4. 对于 vertexBufferLayout.attributes 中的每个 attributeDesc

      1. attributeOffsetvertexBufferOffset + vertexElementIndex * vertexBufferLayout.arrayStride + attributeDesc.offset

      2. vertexBuffer 的偏移量 attributeOffset 开始,以格式 attributeDesc.format 加载属性 data。组件从缓冲区内存中的 xyzw 的顺序加载。

        如果这导致越界访问,则根据 WGSL 的 invalid memory reference 行为确定结果值。

      3. 可选(实现定义): 如果 attributeOffset + sizeof(attributeDesc.format) > vertexBufferOffset + vertexBufferBindingSize,则 empty vertexIndexList 并停止,取消绘制调用。

        注意:这允许实现在发出绘制调用之前检测索引缓冲区中的越界值,而不是使用 invalid memory reference 行为。

      4. 根据 channel formats 规则,将 data 转换为着色器可见格式。

        一个类型为 "snorm8x2" 和字节值为 [0x70, 0xD0] 的属性将在 WGSL 中转换为 vec2<f32>(0.88, -0.38)
      5. 调整 data 的大小以适应着色器类型:

        • 如果两者都是标量,或者都是相同维度的向量,则不需要调整。

        • 如果 data 是向量,但着色器类型是标量,则只提取第一个分量。

        • 如果两者都是向量,并且 data 具有更高的维度,则会丢弃额外的分量。

          如果期望的是 2 维向量,一个类型为 "float32x3" 和值为 vec3<f32>(1.0, 2.0, 3.0) 的属性将在着色器中显示为 vec2<f32>(1.0, 2.0)
        • 如果着色器类型是更高维度的向量,或者 data 是标量,则缺少的分量将从 vec4<*>(0, 0, 0, 1) 值中填充。

          如果期望的是 4 维向量,一个类型为 "sint32" 和值为 5 的属性将在着色器中显示为 vec4<i32>(5, 0, 0, 1)
      6. data 绑定到顶点着色器输入位置 attributeDesc.shaderLocation

  3. 对于 state.[[bind_groups]]index 处的每个 GPUBindGroup 组:

    1. 对于绑定组中的每个资源 GPUBindingResource

      1. entry 成为此资源的相应 GPUBindGroupLayoutEntry

      2. 如果 entry.visibility 包括 VERTEX

  4. 设置着色器 builtins

    • 如果有的话,将 vertex_index builtin 设置为 vertexIndex

    • 如果有的话,将 instance_index builtin 设置为 instanceIndex.

  5. 调用 desc 描述的顶点着色器入口点。

    注意:目标平台缓存顶点着色器调用的结果。不能保证任何重复超过一次的 vertexIndex 将导致多次调用。同样,不能保证单个 vertexIndex 只会被处理一次。

    如果着色器没有在合理的时间内 执行完毕,由用户代理决定,那么 device 可能会 丢失设备

23.3.3. 基本体部件

基本体由 GPU 的固定功能状态构成。

assemble primitives(vertexIndexList, drawCall, desc)

参数:

对于每个实例,基于 vertexIndexList,从已由着色器处理的顶点中组装基本体。

  1. 首先,如果基本体拓扑是条带(这意味着 desc.stripIndexFormat 不是 undefined),并且 drawCall 是索引的,则使用 desc.stripIndexFormat 的最大值作为分隔符将 vertexIndexList 分成子列表。

    例如:类型为 "uint16" 的值为 [1, 2, 65535, 4, 5, 6]vertexIndexList 将被分割为子列表 [1, 2][4, 5, 6]

  2. 对于子列表 vl,基本体生成根据 desc.topology 进行:

    "line-list"

    线基本体由 (vl.0, vl.1),然后 (vl.2, vl.3),然后 (vl.4 到 vl.5),等等组成。每个后续基本体占用 2 个顶点。

    "line-strip"

    线基本体由 (vl.0, vl.1),然后 (vl.1, vl.2),然后 (vl.2, vl.3),等等组成。每个后续基本体占用 1 个顶点。

    "triangle-list"

    三角形基本体由 (vl.0, vl.1, vl.2),然后 (vl.3, vl.4, vl.5),然后 (vl.6, vl.7, vl.8),等等组成。每个后续基本体占用 3 个顶点。

    "triangle-strip"

    三角形基本体由 (vl.0, vl.1, vl.2),然后 (vl.2, vl.1, vl.3),然后 (vl.2, vl.3, vl.4),然后 (vl.4, vl.3, vl.5),等等组成。每个后续基本体占用 1 个顶点。

    Editorial: should this be defined more formally?

    任何不完整的基本体都将被丢弃。

23.3.4. 基本体剪裁

顶点着色器必须生成内置的 "position"(类型为 vec4<f32>),它表示顶点的 clip position

Editorial: link to WGSL built-ins

基本体被剪裁到 clip volume,对于任何基本体内部的 clip position p,由以下不等式定义:

如果 descriptor.primitive.unclippedDepthtrue,则不应用 depth clippingclip volume 在 z 维度上没有边界。

如果基本体的每条边都完全位于 clip volume 内,则基本体不会改变。 如果基本体的边与 clip volume 的边界相交,则相交的边通过沿 clip volume 的边界的新边重新连接。 对于三角形基本体(descriptor.primitive.topology"triangle-list""triangle-strip"),这种重新连接可能会导致将新顶点引入到多边形中。

如果基本体与 clip volume 的边界的边相交,则剪裁的多边形必须包含此边界边上的一个点。

如果顶点着色器输出其他浮点值(标量和向量),并带有 "perspective" 插值限定符,则它们也会被剪裁。 基本体内的顶点关联的输出值不受剪裁的影响。 但是,如果基本体被剪裁,则剪裁产生的顶点分配的输出值将被剪裁。

考虑一个在顶点 ab 之间的边被剪裁,导致顶点 c,让我们定义 t 为边顶点之间的比率: c.p = t × a.p + (1 − t) × b.p, 其中 x.p 是顶点 x 的输出 clip position

对于每个具有相应片段输入的顶点输出值 "v",a.v 和 b.v 分别是顶点 ab 的输出。 剪裁的着色器输出 c.v 是基于插值限定符生成的:

"flat"

平面插值不受影响,基于 provoking vertex,它是基本体中的第一个顶点。输出值对整个基本体都是相同的,并且与 provoking vertex 的顶点输出相匹配:c.v = provoking vertex.v

"linear"

插值比率根据 clip position 的透视坐标进行调整,以便插值的结果在屏幕空间中是线性的。

Editorial: provide more specifics here, if possible

"perspective"

在剪裁空间中线性插值该值,从而产生透视正确的值:

c.v = t × a.v + (1 − t) × b.v

Editorial: link to interpolation qualifiers in WGSL

基本体剪裁的结果是一组新的基本体,这些基本体包含在 clip volume 内。

23.3.5. 光栅化

光栅化是将生成的基本体映射到 framebuffer 的 2 维渲染区域的硬件处理阶段 - 当前 GPURenderPassEncoder 中的渲染附件集。 此渲染区域被分割成一个均匀的像素网格。

framebuffer 坐标从渲染目标的左上角开始。每个单位都对应一个像素。有关更多信息,请参见 § 3.3 坐标系统

光栅化确定基本体影响的像素集。在多采样的情况下,每个像素进一步分成 descriptor.multisample.count 个样本。 standard sample patterns 如下,其中的位置以像素的左上角为原点,以 framebuffer 坐标表示,使得像素范围从 (0, 0) 到 (1, 1):

multisample.count Sample positions
1 Sample 0: (0.5, 0.5)
4 Sample 0: (0.375, 0.125)
Sample 1: (0.875, 0.375)
Sample 2: (0.125, 0.625)
Sample 3: (0.625, 0.875)

让我们定义一个 FragmentDestination 来包含:

position

framebuffer 空间中的 2D 像素位置

sampleIndex

如果 § 23.3.10 采样频率着色 处于活动状态,则为整数,否则为 null

我们还将使用 NDC 的概念 - 规范化设备坐标。 在此坐标系中,视口边界在 X 和 Y 中的范围为 -1 到 1,在 Z 中的范围为 0 到 1。

光栅化产生一个 RasterizationPoint 列表,每个列表包含以下数据:

destination

指的是 FragmentDestination

coverageMask

指的是多采样覆盖掩码(参见 § 23.3.11 采样遮掩

frontFacing

如果是基本体正面的点,则为 true

perspectiveDivisor

指的是基本体上插值的 1.0 ÷ W

depth

指的是视口坐标中的深度, 即 [[viewport]] minDepthmaxDepth 之间。

primitiveVertices

指的是形成基本体的顶点输出列表

barycentricCoordinates

指的是 § 23.3.5.3 重心坐标

Editorial: define the depth computation algorithm

rasterize(primitiveList, state)

参数:

返回: RasterizationPoint 列表。

每个基本体在 primitiveList 中独立处理。

  1. 首先,剪切的顶点被转换为 NDC - 规范化设备坐标。对于输出位置 p,计算 NDC 坐标如下:

    divisor(p) = 1.0 ÷ p.w

    ndc(p) = vector(p.x ÷ p.w, p.y ÷ p.w, p.z ÷ p.w)

  2. vpstate.[[viewport]]。将 NDC 坐标 n 映射到视口空间:

    • 从渲染目标偏移和大小计算 framebuffer 坐标:

      framebufferCoords(n) = vector(vp.x + 0.5 × (n.x + 1) × vp.width, vp.y + .5 × (n.y + 1) × vp.height)

    • 通过线性映射 [0,1] 到视口深度范围来计算深度:

      depth(n) = vp.minDepth + n.z × ( vp.maxDepth - vp.minDepth )

  3. rasterizationPoints 为空列表。

    Editorial: specify that each rasterization point gets assigned an interpolated divisor(p), framebufferCoords(n), depth(n), as well as the other attributes.

  4. 根据 primitive.topology,使用特定的光栅化算法继续:

    "point-list"

    如果没有被 § 23.3.4 基本体剪裁 过滤,则该点进入 § 23.3.5.1 点光栅化

    "line-list" or "line-strip"

    § 23.3.4 基本体剪裁 切割的线进入 § 23.3.5.2 线光栅化

    "triangle-list" or "triangle-strip"

    § 23.3.4 基本体剪裁 中产生的多边形进入 § 23.3.5.4 多边形光栅化

  5. rasterizationPoints 中删除所有点 rp,这些点具有 rp.destination.positionstate.[[scissorRect]] 之外。

  6. 返回 rasterizationPoints

23.3.5.1. 点光栅化

在包含点的 framebuffer 坐标的像素中选择一个 FragmentDestination

覆盖掩码取决于多采样模式:

采样频率

coverageMask = 1 ≪ sampleIndex

像素频率多采样

coverageMask = 1 ≪ descriptor.multisample.count − 1

无多采样

coverageMask = 1

23.3.5.2. 线光栅化

Editorial: fill out this section

23.3.5.3. 重心坐标

重心坐标是一个 n 个数字 bi 的列表,定义为在 framebuffer 空间中具有 n 个顶点 vi 的凸多边形内的点 p。 每个 bi 在 0 到 1 的范围内,包括 0 和 1,表示到顶点 vi 的接近程度。 它们的总和始终是恒定的:

∑ (bi) = 1

这些坐标唯一地指定了多边形内(或在其边界上)的任何点 p

p = ∑ (bi × pi)

对于具有 3 个顶点的多边形 - 三角形,任何点 p 的重心坐标可以计算如下:

Apolygon = A(v1, v2, v3) b1 = A(p, b2, b3) ÷ Apolygon b2 = A(b1, p, b3) ÷ Apolygon b3 = A(b1, b2, p) ÷ Apolygon

其中 A(点列表) 是具有给定顶点集的多边形的面积。

对于具有多于 3 个顶点的多边形,确切的算法取决于实现。可能的实现之一是将多边形三角化,并根据其所在的三角形计算点的重心坐标。

23.3.5.4. 多边形光栅化

如果多边形朝向投影,则多边形是 front-facing 的。 否则,多边形是 back-facing 的。

rasterize polygon()

参数:

返回: RasterizationPoint 列表。

  1. rasterizationPoints 成为空列表。

  2. v(i) 成为 n 个顶点的光栅化多边形中被裁剪的顶点编号 i(从 1 开始)的 framebuffer 坐标。

    注意:本节使用术语“多边形”而不是“三角形”,因为 § 23.3.4 基本体剪裁 阶段可能引入了额外的顶点。 这对应用程序是不可观察的。

  3. 确定多边形是否是 front-facing 的,这取决于多边形在 framebuffer 坐标中占用的 area 的符号:

    area = 0.5 × ((v1.x × vn.y − vn.x × v1.y) + ∑ (vi+1.x × vi.y − vi.x × vi+1.y))

    area 的符号根据 primitive.frontFace 进行解释:

    "ccw"

    area > 0 被认为是 front-facing,否则是 back-facing

    "cw"

    area < 0 被认为是 front-facing,否则是 back-facing

  4. 根据 primitive.cullMode 进行剔除:

    "none"

    所有多边形都通过此测试。

    "front"

    front-facing 多边形被丢弃,并且不在渲染管线的后续阶段中处理。

    "back"

    back-facing 多边形被丢弃。

  5. 确定 framebuffer 空间中多边形内的 fragments 集合 - 这些是计划进行的每个片段操作的位置。 此操作称为“点采样”。 逻辑基于 descriptor.multisample

    disabled

    Fragments 与像素中心相关联。 也就是说,所有坐标为 C 的点,其中 fract(C) = vector2(0.5, 0.5) 在 framebuffer 空间中,被包含在多边形中,都被包含在内。 如果像素中心在多边形的边缘上,是否包含在内是未定义的。

    注意:这成为光栅化器的精度主题。

    enabled

    每个像素与 descriptor.multisample.count 相关联,这是实现定义的。 这些位置是有序的,并且对于 framebuffer 的每个像素,列表都是相同的。 每个位置对应于多采样 framebuffer 中的一个片段。

    光栅化器在每个像素内构建被击中的位置的掩码,并将其作为“sample-mask”内置于片段着色器中。

  6. 对于每个类型为 FragmentDestination 的产生的片段:

    1. rp 成为一个新的 RasterizationPoint 对象

    2. 计算列表 b 作为该片段的 § 23.3.5.3 重心坐标。 将 rp.barycentricCoordinates 设置为 b

    3. di 成为 vi 的深度值。

      Editorial: define how this value is constructed.

    4. rp.depth 设置为 ∑ (bi × di)

    5. rp 追加到 rasterizationPoints

  7. 返回 rasterizationPoints

23.3.6. 片段处理

片段处理阶段是渲染 pipeline 的可编程阶段,用于计算要写入渲染目标的片段数据(通常是颜色)。

此阶段为每个 RasterizationPoint 生成一个 Fragment

process fragment(rp, desc, state)

参数:

返回: Fragmentnull

  1. fragment 成为一个新的 Fragment 对象。

  2. fragment.destination 设置为 rp.destination

  3. fragment.coverageMask 设置为 rp.coverageMask

  4. fragment.depth 设置为 rp.depth

  5. 如果 desc 不是 null

    1. 设置着色器输入 builtins。 对于入口点的每个非复合参数,标记为 builtin,根据注释设置其值:

      position

      vec4<f32>(rp.destination.position, rp.depth, rp.perspectiveDivisor)

      front_facing

      rp.frontFacing

      sample_index

      rp.destination.sampleIndex

      sample_mask

      rp.coverageMask

    2. 对于片段阶段的每个用户指定的 shader stage input

      1. value 成为基于 rp.barycentricCoordinatesrp.primitiveVertices 和输入上的 interpolation 限定符的插值片段输入。

        Editorial: describe the exact equations.

      2. 将相应的片段着色器 location 输入设置为 value

    3. 调用由 desc 描述的片段着色器入口点。

      如果 shader execution does not end 在合理的时间内,由用户代理确定,device 可能会变成 lost

    4. 如果片段发出了 discard,则返回 null

    5. fragment.colors 设置为来自着色器的用户指定的 shader stage output 值。

    6. 取出着色器输出 builtins

      1. 如果 frag_depth builtin 由着色器产生为 value

        1. vp 成为 state.[[viewport]]

        2. fragment.depth 设置为 clamp(value, vp.minDepth, vp.maxDepth)。

    7. 如果 sample_mask builtin 由着色器产生为 value

      1. fragment.coverageMask 设置为 fragment.coverageMaskvalue.

    否则,我们处于 § 23.3.8 无颜色输出 模式,fragment.colors 为空。

  6. 返回 fragment

片段的处理是并行的,而任何副作用,例如写入 GPUBufferBindingType."storage" 绑定,可能以任何顺序发生。

23.3.7. 输出合并

Editorial: fill out this section

此阶段的深度输入(如果有)将被截断到当前 [[viewport]] 深度范围(无论片段着色器阶段是否写入 frag_depth builtin)。

23.3.8. 无颜色输出

在无颜色输出模式下,pipeline 不会产生任何颜色附件输出。

pipeline 仍然执行光栅化并基于顶点位置输出产生深度值。深度测试和模板操作仍然可以使用。

23.3.9. Alpha覆盖

在 alpha-to-coverage 模式下,基于片段着色器输出值在 @location(0) 处的 alpha 分量,生成 MSAA 样本的额外 alpha-to-coverage mask

生成额外 mask 的算法取决于平台,对于不同的像素可能会有所不同。 它保证:

23.3.10. 采样频率着色

Editorial: fill out the section

23.3.11. 采样遮掩

像素的 final sample mask 计算为 rasterization mask & mask & shader-output mask

只考虑 mask 的较低 count 位。

如果 final sample mask 的位置 N 的最低有效位的值为 "0",则片段着色器的所有附件的样本颜色输出(对应于样本 N)都将被丢弃。此外,深度测试或模板操作也不会在深度模板附件的相关样本上执行。

注意:样本 N 的颜色输出是由片段着色器执行产生的,对于当前像素,SV_SampleIndex == N。如果片段着色器不使用此语义,则每个像素只执行一次。

rasterization mask 是由光栅化阶段产生的,基于光栅化多边形的形状。包含在形状中的样本在 mask 中获得相关位 1。

shader-output mask 获取片段着色器中 "sample_mask" builtin 的输出值。如果 builtin 不是从片段着色器输出的,并且 alphaToCoverageEnabled 已启用,则 shader-output mask 变为 alpha-to-coverage mask。否则,默认为 0xFFFFFFFF。

24. 类型定义

typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
typedef [EnforceRange] unsigned long GPUStencilValue;
typedef [EnforceRange] unsigned long GPUSampleMask;
typedef [EnforceRange] long GPUDepthBias;

typedef [EnforceRange] unsigned long long GPUSize64;
typedef [EnforceRange] unsigned long GPUIntegerCoordinate;
typedef [EnforceRange] unsigned long GPUIndex32;
typedef [EnforceRange] unsigned long GPUSize32;
typedef [EnforceRange] long GPUSignedOffset32;

typedef unsigned long GPUFlagsConstant;

24.1. 颜色 & 向量

dictionary GPUColorDict {
    required double r;
    required double g;
    required double b;
    required double a;
};
typedef (sequence<double> or GPUColorDict) GPUColor;

注意:double 足够大,可以精确地保存 32 位有符号/无符号整数和单精度浮点数。

r, of type double

红色通道值。

g, of type double

绿色通道值。

b, of type double

蓝色通道值。

a, of type double

透明通道值。

对于给定的 GPUColorcolor,根据其类型,语法为:
validate GPUColor shape(color)

参数:

返回: undefined

  1. 如果 color 是一个序列且 color.length ≠ 4,则抛出 TypeError

dictionary GPUOrigin2DDict {
    GPUIntegerCoordinate x = 0;
    GPUIntegerCoordinate y = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict) GPUOrigin2D;
对于给定的 GPUOrigin2Dorigin,根据其类型,语法为:
validate GPUOrigin2D shape(origin)

参数:

返回: undefined

  1. 如果 origin 是一个序列且 origin.length > 2,则抛出 TypeError

dictionary GPUOrigin3DDict {
    GPUIntegerCoordinate x = 0;
    GPUIntegerCoordinate y = 0;
    GPUIntegerCoordinate z = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;
对于给定的 GPUOrigin3Dorigin,根据其类型,语法为:
validate GPUOrigin3D shape(origin)

参数:

返回: undefined

  1. 如果 origin 是一个序列且 origin.length > 3,则抛出 TypeError

dictionary GPUExtent3DDict {
    required GPUIntegerCoordinate width;
    GPUIntegerCoordinate height = 1;
    GPUIntegerCoordinate depthOrArrayLayers = 1;
};
typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;
width, of type GPUIntegerCoordinate

范围的宽度。

height, of type GPUIntegerCoordinate, defaulting to 1

范围的高度。

depthOrArrayLayers, of type GPUIntegerCoordinate, defaulting to 1

范围的深度或其包含的数组层数。 如果与 GPUTexture 一起使用,其 GPUTextureDimension"3d",定义了纹理的深度。如果与 GPUTexture 一起使用,其 GPUTextureDimension"2d",定义了纹理中的数组层数。

对于给定的 GPUExtent3Dextent,根据其类型,语法为:
validate GPUExtent3D shape(extent)

参数:

返回: undefined

  1. 抛出 TypeError,如果:

25. 功能索引

25.1. "depth-clip-control"

允许禁用 depth clipping

此功能添加了以下 optional API surfaces

25.2. "depth32float-stencil8"

允许显式创建格式为 "depth32float-stencil8" 的纹理。

此功能添加了以下 optional API surfaces

25.3. "texture-compression-bc"

允许显式创建 BC 压缩格式的纹理。

此功能添加了以下 optional API surfaces

25.4. "texture-compression-etc2"

允许显式创建 ETC2 压缩格式的纹理。

此功能添加了以下 optional API surfaces

25.5. "texture-compression-astc"

允许显式创建 ASTC 压缩格式的纹理。

此功能添加了以下 optional API surfaces

25.6. "timestamp-query"

添加了从 GPU 命令缓冲区查询时间戳的能力。参见 § 20.4 时间戳查询

此功能添加了以下 optional API surfaces

25.7. "indirect-first-instance"

允许在 indirect draw parametersindirect drawIndexed parameters 中使用非零的 firstInstance 值。

此功能不添加 optional API surfaces

25.8. "shader-f16"

允许在 WGSL 中使用半精度浮点类型 f16

此功能添加了以下 optional API surfaces

25.9. "rg11b10ufloat-renderable"

允许在格式为 "rg11b10ufloat" 的纹理上使用 RENDER_ATTACHMENT

此功能不添加 optional API surfaces

25.10. "bgra8unorm-storage"

允许在格式为 "bgra8unorm" 的纹理上使用 STORAGE_BINDING

此功能不添加 optional API surfaces

25.11. "float32-filterable"

使格式为 "r32float""rg32float""rgba32float" 的纹理 filterable

26. 附录

26.1. 纹理格式功能

26.1.1. 纯彩色格式

所有纯彩色格式都支持 COPY_SRCCOPY_DSTTEXTURE_BINDING 用法。

RENDER_ATTACHMENTSTORAGE_BINDING 列分别指定对 GPUTextureUsage.RENDER_ATTACHMENTGPUTextureUsage.STORAGE_BINDING 用法的支持。

render target pixel byte costrender target component alignment 用于验证 maxColorAttachmentBytesPerSample 限制。

注意: 这些格式的 texel block memory cost 与其 texel block copy footprint 相同。

Format GPUTextureSampleType RENDER_ATTACHMENT blendable multisampling resolve STORAGE_BINDING Texel block copy footprint (Bytes) Render target pixel byte cost (Bytes)
8 bits per component (1-byte render target component alignment)
r8unorm "float",
"unfilterable-float"
1
r8snorm "float",
"unfilterable-float"
1
r8uint "uint" 1
r8sint "sint" 1
rg8unorm "float",
"unfilterable-float"
2
rg8snorm "float",
"unfilterable-float"
2
rg8uint "uint" 2
rg8sint "sint" 2
rgba8unorm "float",
"unfilterable-float"
4 8
rgba8unorm-srgb "float",
"unfilterable-float"
4 8
rgba8snorm "float",
"unfilterable-float"
4
rgba8uint "uint" 4
rgba8sint "sint" 4
bgra8unorm "float",
"unfilterable-float"
If "bgra8unorm-storage" is enabled 4 8
bgra8unorm-srgb "float",
"unfilterable-float"
4 8
16 bits per component (2-byte render target component alignment)
r16uint "uint" 2
r16sint "sint" 2
r16float "float",
"unfilterable-float"
2
rg16uint "uint" 4
rg16sint "sint" 4
rg16float "float",
"unfilterable-float"
4
rgba16uint "uint" 8
rgba16sint "sint" 8
rgba16float "float",
"unfilterable-float"
8
32 bits per component (4-byte render target component alignment)
r32uint "uint" 4
r32sint "sint" 4
r32float "unfilterable-float" 4
rg32uint "uint" 8
rg32sint "sint" 8
rg32float "unfilterable-float" 8
rgba32uint "uint" 16
rgba32sint "sint" 16
rgba32float "unfilterable-float" 16
mixed component width, 32 bits per texel (4-byte render target component alignment)
rgb10a2unorm "float",
"unfilterable-float"
4 8
rg11b10ufloat "float",
"unfilterable-float"
If "rg11b10ufloat-renderable" is enabled 4 8

26.1.2. 深度模具格式

depth-or-stencil format 是任意带有深度和/或模具方面的格式。 combined depth-stencil format 是一个既有深度又有模具方面的 depth-or-stencil format

所有的 depth-or-stencil formats 都支持 COPY_SRCCOPY_DSTTEXTURE_BINDINGRENDER_ATTACHMENT 用法。 所有这些格式都支持多重采样。然而某些复制操作也限制了源和目标格式。

深度纹理不能使用 "filtering" 采样器,但总是可以使用 "comparison" 采样器(它们可以使用过滤)。

Format Texel block memory cost (Bytes) Aspect GPUTextureSampleType Valid image copy source Valid image copy destination Texel block copy footprint (Bytes) Aspect-specific format
stencil8 1 − 4 stencil "uint" 1 stencil8
depth16unorm 2 depth "depth", "unfilterable-float" 2 depth16unorm
depth24plus 4 depth "depth", "unfilterable-float" depth24plus
depth24plus-stencil8 4 − 8 depth "depth", "unfilterable-float" depth24plus
stencil "uint" 1 stencil8
depth32float 4 depth "depth", "unfilterable-float" 4 depth32float
depth32float-stencil8 5 − 8 depth "depth", "unfilterable-float" 4 depth32float
stencil "uint" 1 stencil8

24-bit depth 指的是一个 24 位无符号归一化深度格式,其范围从 0.0 到 1.0,如果暴露出来,它将被拼写为 "depth24unorm"。

26.1.2.1. 读取和采样深度/模具纹理

绑定深度方面 GPUTextureViewtexture_depth_* 绑定或其他非深度 2d/cube 纹理类型的绑定是 possible 的。

模具方面 GPUTextureView 必须绑定到普通纹理绑定类型。GPUBindGroupLayout 中的 sampleType 必须是 "uint"

读取或采样纹理的深度或模具方面的行为就像纹理包含值 (V, X, X, X),其中 V 是实际的深度或模具值,每个 X 是实现定义的未指定值。

对于深度方面绑定,未指定的值在具有 texture_depth_* 类型的绑定中不可见。

If a depth texture is bound to tex with type texture_2d<f32>:

注意: 除了添加一个新的更受限制的模具采样器类型(如深度)之外,实现无法有效地掩盖深度/模具读取的驱动程序差异。由于这不是 WebGL 的可移植性痛点,因此不希望在 WebGPU 中出现问题。实际上,根据硬件的不同,期望是 (V, V, V, V)(V, 0, 0, 1)(其中 V 是深度或模具值)。

26.1.2.2. 复制深度/模具纹理

深度方面的深度32float格式("depth32float""depth32float-stencil8")具有有限的范围。 因此,从相同格式的其他纹理复制到此类纹理是有效的。

深度方面的深度24plus格式("depth24plus""depth24plus-stencil8")具有不透明的表示(实现为 24-bit depth"depth32float")。 因此,不允许使用这些格式进行深度方面的 image copies

注意: 可以模仿这些不允许的复制:

26.1.3. 压缩格式

所有的压缩纹理格式都支持COPY_SRCCOPY_DSTTEXTURE_BINDING用法。 所有这些格式都是可过滤的。 这些格式都不是可渲染的,也不支持多重采样。

compressed format是任何块大小大于1×1的格式。

注意: 每个这些格式的texel block memory cost与其texel block copy footprint相同。

Format Texel block copy footprint (Bytes) GPUTextureSampleType Texel block width/height Feature
rgb9e5ufloat 4 "float",
"unfilterable-float"
1 × 1
bc1-rgba-unorm 8 "float",
"unfilterable-float"
4 × 4 texture-compression-bc
bc1-rgba-unorm-srgb
bc2-rgba-unorm 16
bc2-rgba-unorm-srgb
bc3-rgba-unorm 16
bc3-rgba-unorm-srgb
bc4-r-unorm 8
bc4-r-snorm
bc5-rg-unorm 16
bc5-rg-snorm
bc6h-rgb-ufloat 16
bc6h-rgb-float
bc7-rgba-unorm 16
bc7-rgba-unorm-srgb
etc2-rgb8unorm 8 "float",
"unfilterable-float"
4 × 4 texture-compression-etc2
etc2-rgb8unorm-srgb
etc2-rgb8a1unorm 8
etc2-rgb8a1unorm-srgb
etc2-rgba8unorm 16
etc2-rgba8unorm-srgb
eac-r11unorm 8
eac-r11snorm
eac-rg11unorm 16
eac-rg11snorm
astc-4x4-unorm 16 "float",
"unfilterable-float"
4 × 4 texture-compression-astc
astc-4x4-unorm-srgb
astc-5x4-unorm 16 5 × 4
astc-5x4-unorm-srgb
astc-5x5-unorm 16 5 × 5
astc-5x5-unorm-srgb
astc-6x5-unorm 16 6 × 5
astc-6x5-unorm-srgb
astc-6x6-unorm 16 6 × 6
astc-6x6-unorm-srgb
astc-8x5-unorm 16 8 × 5
astc-8x5-unorm-srgb
astc-8x6-unorm 16 8 × 6
astc-8x6-unorm-srgb
astc-8x8-unorm 16 8 × 8
astc-8x8-unorm-srgb
astc-10x5-unorm 16 10 × 5
astc-10x5-unorm-srgb
astc-10x6-unorm 16 10 × 6
astc-10x6-unorm-srgb
astc-10x8-unorm 16 10 × 8
astc-10x8-unorm-srgb
astc-10x10-unorm 16 10 × 10
astc-10x10-unorm-srgb
astc-12x10-unorm 16 12 × 10
astc-12x10-unorm-srgb
astc-12x12-unorm 16 12 × 12
astc-12x12-unorm-srgb

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[I18N-GLOSSARY]
Richard Ishida; Addison Phillips. Internationalization Glossary. URL: https://w3c.github.io/i18n-glossary/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBGL-1]
Dean Jackson; Jeff Gilbert. WebGL Specification, Version 1.0. 9 August 2017. URL: https://www.khronos.org/registry/webgl/specs/latest/1.0/
[WEBGPU]
Kai Ninomiya; Brandon Jones; Myles Maxfield. WebGPU. URL: https://gpuweb.github.io/gpuweb/
https://github.com/gpuweb/gpuweb/blob/a63de4dff4bf001e6a4adea8b5ccd1dc7d735025/spec/index.bs
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[WGSL]
Alan Baker; Mehmet Oguz Derin; David Neto. WebGPU Shading Language. URL: https://gpuweb.github.io/gpuweb/wgsl/

Informative References

[SourceMap]
John Lenz; Nick Fitzgerald. Source Map Revision 3 Proposal. URL: https://sourcemaps.info/spec.html

IDL Index

interface mixin GPUObjectBase {
    attribute USVString label;
};

dictionary GPUObjectDescriptorBase {
    USVString label;
};

[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;
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSupportedFeatures {
    readonly setlike<DOMString>;
};

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

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

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

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

dictionary GPURequestAdapterOptions {
    GPUPowerPreference powerPreference;
    boolean forceFallbackAdapter = false;
};

enum GPUPowerPreference {
    "low-power",
    "high-performance"
};

[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 = []);
};

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

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"
};

[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;

[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"
};

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

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;
};

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

[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;

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

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

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;
};

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

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

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

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

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"
};

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

dictionary GPUExternalTextureDescriptor
         : GPUObjectDescriptorBase {
    required HTMLVideoElement source;
    PredefinedColorSpace colorSpace = "srgb";
};

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

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;
};

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

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

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

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

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

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

dictionary GPUBindGroupLayoutEntry {
    required GPUIndex32 binding;
    required GPUShaderStageFlags visibility;

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

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

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

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

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

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

enum GPUTextureSampleType {
    "float",
    "unfilterable-float",
    "depth",
    "sint",
    "uint"
};

dictionary GPUTextureBindingLayout {
    GPUTextureSampleType sampleType = "float";
    GPUTextureViewDimension viewDimension = "2d";
    boolean multisampled = false;
};

enum GPUStorageTextureAccess {
    "write-only"
};

dictionary GPUStorageTextureBindingLayout {
    GPUStorageTextureAccess access = "write-only";
    required GPUTextureFormat format;
    GPUTextureViewDimension viewDimension = "2d";
};

dictionary GPUExternalTextureBindingLayout {
};

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

dictionary GPUBindGroupDescriptor
         : GPUObjectDescriptorBase {
    required GPUBindGroupLayout layout;
    required sequence<GPUBindGroupEntry> entries;
};

typedef (GPUSampler or GPUTextureView or GPUBufferBinding or GPUExternalTexture) GPUBindingResource;

dictionary GPUBindGroupEntry {
    required GPUIndex32 binding;
    required GPUBindingResource resource;
};

dictionary GPUBufferBinding {
    required GPUBuffer buffer;
    GPUSize64 offset = 0;
    GPUSize64 size;
};

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

dictionary GPUPipelineLayoutDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPUBindGroupLayout> bindGroupLayouts;
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUShaderModule {
    Promise<GPUCompilationInfo> getCompilationInfo();
};
GPUShaderModule includes GPUObjectBase;

dictionary GPUShaderModuleDescriptor
         : GPUObjectDescriptorBase {
    required USVString code;
    object sourceMap;
    record<USVString, GPUShaderModuleCompilationHint> hints;
};

dictionary GPUShaderModuleCompilationHint {
    (GPUPipelineLayout or GPUAutoLayoutMode) layout;
};

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;
};

[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"
};

enum GPUAutoLayoutMode {
    "auto"
};

dictionary GPUPipelineDescriptorBase
         : GPUObjectDescriptorBase {
    required (GPUPipelineLayout or GPUAutoLayoutMode) layout;
};

interface mixin GPUPipelineBase {
    [NewObject] GPUBindGroupLayout getBindGroupLayout(unsigned long index);
};

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.

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

dictionary GPUComputePipelineDescriptor
         : GPUPipelineDescriptorBase {
    required GPUProgrammableStage compute;
};

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

dictionary GPURenderPipelineDescriptor
         : GPUPipelineDescriptorBase {
    required GPUVertexState vertex;
    GPUPrimitiveState primitive = {};
    GPUDepthStencilState depthStencil;
    GPUMultisampleState multisample = {};
    GPUFragmentState fragment;
};

dictionary GPUPrimitiveState {
    GPUPrimitiveTopology topology = "triangle-list";
    GPUIndexFormat stripIndexFormat;
    GPUFrontFace frontFace = "ccw";
    GPUCullMode cullMode = "none";

    // Requires "depth-clip-control" feature.
    boolean unclippedDepth = false;
};

enum GPUPrimitiveTopology {
    "point-list",
    "line-list",
    "line-strip",
    "triangle-list",
    "triangle-strip"
};

enum GPUFrontFace {
    "ccw",
    "cw"
};

enum GPUCullMode {
    "none",
    "front",
    "back"
};

dictionary GPUMultisampleState {
    GPUSize32 count = 1;
    GPUSampleMask mask = 0xFFFFFFFF;
    boolean alphaToCoverageEnabled = false;
};

dictionary GPUFragmentState
         : GPUProgrammableStage {
    required sequence<GPUColorTargetState?> targets;
};

dictionary GPUColorTargetState {
    required GPUTextureFormat format;

    GPUBlendState blend;
    GPUColorWriteFlags writeMask = 0xF;  // GPUColorWrite.ALL
};

dictionary GPUBlendState {
    required GPUBlendComponent color;
    required GPUBlendComponent alpha;
};

typedef [EnforceRange] unsigned long GPUColorWriteFlags;
[Exposed=(Window, DedicatedWorker), SecureContext]
namespace GPUColorWrite {
    const GPUFlagsConstant RED   = 0x1;
    const GPUFlagsConstant GREEN = 0x2;
    const GPUFlagsConstant BLUE  = 0x4;
    const GPUFlagsConstant ALPHA = 0x8;
    const GPUFlagsConstant ALL   = 0xF;
};

dictionary GPUBlendComponent {
    GPUBlendOperation operation = "add";
    GPUBlendFactor srcFactor = "one";
    GPUBlendFactor dstFactor = "zero";
};

enum GPUBlendFactor {
    "zero",
    "one",
    "src",
    "one-minus-src",
    "src-alpha",
    "one-minus-src-alpha",
    "dst",
    "one-minus-dst",
    "dst-alpha",
    "one-minus-dst-alpha",
    "src-alpha-saturated",
    "constant",
    "one-minus-constant"
};

enum GPUBlendOperation {
    "add",
    "subtract",
    "reverse-subtract",
    "min",
    "max"
};

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;
};

dictionary GPUStencilFaceState {
    GPUCompareFunction compare = "always";
    GPUStencilOperation failOp = "keep";
    GPUStencilOperation depthFailOp = "keep";
    GPUStencilOperation passOp = "keep";
};

enum GPUStencilOperation {
    "keep",
    "zero",
    "replace",
    "invert",
    "increment-clamp",
    "decrement-clamp",
    "increment-wrap",
    "decrement-wrap"
};

enum GPUIndexFormat {
    "uint16",
    "uint32"
};

enum GPUVertexFormat {
    "uint8x2",
    "uint8x4",
    "sint8x2",
    "sint8x4",
    "unorm8x2",
    "unorm8x4",
    "snorm8x2",
    "snorm8x4",
    "uint16x2",
    "uint16x4",
    "sint16x2",
    "sint16x4",
    "unorm16x2",
    "unorm16x4",
    "snorm16x2",
    "snorm16x4",
    "float16x2",
    "float16x4",
    "float32",
    "float32x2",
    "float32x3",
    "float32x4",
    "uint32",
    "uint32x2",
    "uint32x3",
    "uint32x4",
    "sint32",
    "sint32x2",
    "sint32x3",
    "sint32x4"
};

enum GPUVertexStepMode {
    "vertex",
    "instance"
};

dictionary GPUVertexState
         : GPUProgrammableStage {
    sequence<GPUVertexBufferLayout?> buffers = [];
};

dictionary GPUVertexBufferLayout {
    required GPUSize64 arrayStride;
    GPUVertexStepMode stepMode = "vertex";
    required sequence<GPUVertexAttribute> attributes;
};

dictionary GPUVertexAttribute {
    required GPUVertexFormat format;
    required GPUSize64 offset;

    required GPUIndex32 shaderLocation;
};

dictionary GPUImageDataLayout {
    GPUSize64 offset = 0;
    GPUSize32 bytesPerRow;
    GPUSize32 rowsPerImage;
};

dictionary GPUImageCopyBuffer
         : GPUImageDataLayout {
    required GPUBuffer buffer;
};

dictionary GPUImageCopyTexture {
    required GPUTexture texture;
    GPUIntegerCoordinate mipLevel = 0;
    GPUOrigin3D origin = {};
    GPUTextureAspect aspect = "all";
};

dictionary GPUImageCopyTextureTagged
         : GPUImageCopyTexture {
    PredefinedColorSpace colorSpace = "srgb";
    boolean premultipliedAlpha = false;
};

dictionary GPUImageCopyExternalImage {
    required (ImageBitmap or HTMLVideoElement or HTMLCanvasElement or OffscreenCanvas) source;
    GPUOrigin2D origin = {};
    boolean flipY = false;
};

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

dictionary GPUCommandBufferDescriptor
         : GPUObjectDescriptorBase {
};

interface mixin GPUCommandsMixin {
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCommandEncoder {
    GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
    GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});

    undefined copyBufferToBuffer(
        GPUBuffer source,
        GPUSize64 sourceOffset,
        GPUBuffer destination,
        GPUSize64 destinationOffset,
        GPUSize64 size);

    undefined copyBufferToTexture(
        GPUImageCopyBuffer source,
        GPUImageCopyTexture destination,
        GPUExtent3D copySize);

    undefined copyTextureToBuffer(
        GPUImageCopyTexture source,
        GPUImageCopyBuffer destination,
        GPUExtent3D copySize);

    undefined copyTextureToTexture(
        GPUImageCopyTexture source,
        GPUImageCopyTexture destination,
        GPUExtent3D copySize);

    undefined clearBuffer(
        GPUBuffer buffer,
        optional GPUSize64 offset = 0,
        optional GPUSize64 size);

    undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);

    undefined resolveQuerySet(
        GPUQuerySet querySet,
        GPUSize32 firstQuery,
        GPUSize32 queryCount,
        GPUBuffer destination,
        GPUSize64 destinationOffset);

    GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};
GPUCommandEncoder includes GPUObjectBase;
GPUCommandEncoder includes GPUCommandsMixin;
GPUCommandEncoder includes GPUDebugCommandsMixin;

dictionary GPUCommandEncoderDescriptor
         : GPUObjectDescriptorBase {
};

interface mixin GPUBindingCommandsMixin {
    undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
        optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);

    undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
        Uint32Array dynamicOffsetsData,
        GPUSize64 dynamicOffsetsDataStart,
        GPUSize32 dynamicOffsetsDataLength);
};

interface mixin GPUDebugCommandsMixin {
    undefined pushDebugGroup(USVString groupLabel);
    undefined popDebugGroup();
    undefined insertDebugMarker(USVString markerLabel);
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePassEncoder {
    undefined setPipeline(GPUComputePipeline pipeline);
    undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1);
    undefined dispatchWorkgroupsIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);

    undefined end();
};
GPUComputePassEncoder includes GPUObjectBase;
GPUComputePassEncoder includes GPUCommandsMixin;
GPUComputePassEncoder includes GPUDebugCommandsMixin;
GPUComputePassEncoder includes GPUBindingCommandsMixin;

dictionary GPUComputePassTimestampWrites {
    required GPUQuerySet querySet;
    GPUSize32 beginningOfPassWriteIndex;
    GPUSize32 endOfPassWriteIndex;
};

dictionary GPUComputePassDescriptor
         : GPUObjectDescriptorBase {
    GPUComputePassTimestampWrites timestampWrites;
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPassEncoder {
    undefined setViewport(float x, float y,
        float width, float height,
        float minDepth, float maxDepth);

    undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y,
                        GPUIntegerCoordinate width, GPUIntegerCoordinate height);

    undefined setBlendConstant(GPUColor color);
    undefined setStencilReference(GPUStencilValue reference);

    undefined beginOcclusionQuery(GPUSize32 queryIndex);
    undefined endOcclusionQuery();

    undefined executeBundles(sequence<GPURenderBundle> bundles);
    undefined end();
};
GPURenderPassEncoder includes GPUObjectBase;
GPURenderPassEncoder includes GPUCommandsMixin;
GPURenderPassEncoder includes GPUDebugCommandsMixin;
GPURenderPassEncoder includes GPUBindingCommandsMixin;
GPURenderPassEncoder includes GPURenderCommandsMixin;

dictionary GPURenderPassTimestampWrites {
    required GPUQuerySet querySet;
    GPUSize32 beginningOfPassWriteIndex;
    GPUSize32 endOfPassWriteIndex;
};

dictionary GPURenderPassDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPURenderPassColorAttachment?> colorAttachments;
    GPURenderPassDepthStencilAttachment depthStencilAttachment;
    GPUQuerySet occlusionQuerySet;
    GPURenderPassTimestampWrites timestampWrites;
    GPUSize64 maxDrawCount = 50000000;
};

dictionary GPURenderPassColorAttachment {
    required GPUTextureView view;
    GPUTextureView resolveTarget;

    GPUColor clearValue;
    required GPULoadOp loadOp;
    required GPUStoreOp storeOp;
};

dictionary GPURenderPassDepthStencilAttachment {
    required GPUTextureView view;

    float depthClearValue;
    GPULoadOp depthLoadOp;
    GPUStoreOp depthStoreOp;
    boolean depthReadOnly = false;

    GPUStencilValue stencilClearValue = 0;
    GPULoadOp stencilLoadOp;
    GPUStoreOp stencilStoreOp;
    boolean stencilReadOnly = false;
};

enum GPULoadOp {
    "load",
    "clear",
};

enum GPUStoreOp {
    "store",
    "discard",
};

dictionary GPURenderPassLayout
         : GPUObjectDescriptorBase {
    required sequence<GPUTextureFormat?> colorFormats;
    GPUTextureFormat depthStencilFormat;
    GPUSize32 sampleCount = 1;
};

interface mixin GPURenderCommandsMixin {
    undefined setPipeline(GPURenderPipeline pipeline);

    undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size);
    undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer? buffer, optional GPUSize64 offset = 0, optional GPUSize64 size);

    undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1,
        optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0);
    undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1,
        optional GPUSize32 firstIndex = 0,
        optional GPUSignedOffset32 baseVertex = 0,
        optional GPUSize32 firstInstance = 0);

    undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
    undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
};

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

dictionary GPURenderBundleDescriptor
         : GPUObjectDescriptorBase {
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderBundleEncoder {
    GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
};
GPURenderBundleEncoder includes GPUObjectBase;
GPURenderBundleEncoder includes GPUCommandsMixin;
GPURenderBundleEncoder includes GPUDebugCommandsMixin;
GPURenderBundleEncoder includes GPUBindingCommandsMixin;
GPURenderBundleEncoder includes GPURenderCommandsMixin;

dictionary GPURenderBundleEncoderDescriptor
         : GPURenderPassLayout {
    boolean depthReadOnly = false;
    boolean stencilReadOnly = false;
};

dictionary GPUQueueDescriptor
         : GPUObjectDescriptorBase {
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUQueue {
    undefined submit(sequence<GPUCommandBuffer> commandBuffers);

    Promise<undefined> onSubmittedWorkDone();

    undefined writeBuffer(
        GPUBuffer buffer,
        GPUSize64 bufferOffset,
        [AllowShared] BufferSource data,
        optional GPUSize64 dataOffset = 0,
        optional GPUSize64 size);

    undefined writeTexture(
        GPUImageCopyTexture destination,
        [AllowShared] BufferSource data,
        GPUImageDataLayout dataLayout,
        GPUExtent3D size);

    undefined copyExternalImageToTexture(
        GPUImageCopyExternalImage source,
        GPUImageCopyTextureTagged destination,
        GPUExtent3D copySize);
};
GPUQueue includes GPUObjectBase;

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUQuerySet {
    undefined destroy();

    readonly attribute GPUQueryType type;
    readonly attribute GPUSize32 count;
};
GPUQuerySet includes GPUObjectBase;

dictionary GPUQuerySetDescriptor
         : GPUObjectDescriptorBase {
    required GPUQueryType type;
    required GPUSize32 count;
};

enum GPUQueryType {
    "occlusion",
    "timestamp",
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCanvasContext {
    readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;

    undefined configure(GPUCanvasConfiguration configuration);
    undefined unconfigure();

    GPUTexture getCurrentTexture();
};

enum GPUCanvasAlphaMode {
    "opaque",
    "premultiplied",
};

dictionary GPUCanvasConfiguration {
    required GPUDevice device;
    required GPUTextureFormat format;
    GPUTextureUsageFlags usage = 0x10;  // GPUTextureUsage.RENDER_ATTACHMENT
    sequence<GPUTextureFormat> viewFormats = [];
    PredefinedColorSpace colorSpace = "srgb";
    GPUCanvasAlphaMode alphaMode = "opaque";
};

enum GPUDeviceLostReason {
    "unknown",
    "destroyed",
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUDeviceLostInfo {
    readonly attribute GPUDeviceLostReason reason;
    readonly attribute DOMString message;
};

partial interface GPUDevice {
    readonly attribute Promise<GPUDeviceLostInfo> lost;
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUError {
    readonly attribute DOMString message;
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUValidationError
        : GPUError {
    constructor(DOMString message);
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUOutOfMemoryError
        : GPUError {
    constructor(DOMString message);
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUInternalError
        : GPUError {
    constructor(DOMString message);
};

enum GPUErrorFilter {
    "validation",
    "out-of-memory",
    "internal",
};

partial interface GPUDevice {
    undefined pushErrorScope(GPUErrorFilter filter);
    Promise<GPUError?> popErrorScope();
};

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUUncapturedErrorEvent : Event {
    constructor(
        DOMString type,
        GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict
    );
    [SameObject] readonly attribute GPUError error;
};

dictionary GPUUncapturedErrorEventInit : EventInit {
    required GPUError error;
};

partial interface GPUDevice {
    [Exposed=(Window, DedicatedWorker)]
    attribute EventHandler onuncapturederror;
};

typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
typedef [EnforceRange] unsigned long GPUStencilValue;
typedef [EnforceRange] unsigned long GPUSampleMask;
typedef [EnforceRange] long GPUDepthBias;

typedef [EnforceRange] unsigned long long GPUSize64;
typedef [EnforceRange] unsigned long GPUIntegerCoordinate;
typedef [EnforceRange] unsigned long GPUIndex32;
typedef [EnforceRange] unsigned long GPUSize32;
typedef [EnforceRange] long GPUSignedOffset32;

typedef unsigned long GPUFlagsConstant;

dictionary GPUColorDict {
    required double r;
    required double g;
    required double b;
    required double a;
};
typedef (sequence<double> or GPUColorDict) GPUColor;

dictionary GPUOrigin2DDict {
    GPUIntegerCoordinate x = 0;
    GPUIntegerCoordinate y = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict) GPUOrigin2D;

dictionary GPUOrigin3DDict {
    GPUIntegerCoordinate x = 0;
    GPUIntegerCoordinate y = 0;
    GPUIntegerCoordinate z = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;

dictionary GPUExtent3DDict {
    required GPUIntegerCoordinate width;
    GPUIntegerCoordinate height = 1;
    GPUIntegerCoordinate depthOrArrayLayers = 1;
};
typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;