关于图形学:WebGPU-导入2-核心概念与重要机制解读

48次阅读

共计 4281 个字符,预计需要花费 11 分钟才能阅读完成。

1. 外围概念

这部分不会具体开展,当前写教程时会深刻。以下只是外围概念,是绝大多数 WebGPU 原生程序要接触的,并不是全副。

① 适配器和设施

适配器,也就是 GPUAdapter,指代真正的物理显卡,WebGPU 给了个对象来代替它:

const adapter = await navigator.gpu.requestAdapter()

它提供了一个最重要行为,申请设施对象 GPUDevice

const device = await adapter.requestDevice()

那么什么是 Device?其实,显卡很忙。

WebGPU 程序只是三大图形 API 中某个的“下层封装”,除了 WebGPU,调用三大图形 API 的程序远不止,游戏、三维建模工具、视频编解码器,都有可能会调用,甚至会间接调取 GPU 厂商给的 SDK 或驱动程序。

显然,作为显卡“自身”,适配器为了极高效率地工作,喂给它的数据资源和指令最好就是翻译过的,尽可能专一地执行计算 —— 就像大老板不可能日理万机一样,最好给到老板的决策材料,就是通过整顿的,他要做的就是应用他多年的教训疾速决策、签字(效率高的老总 = RTX4090,超市小老板 = GT1030)。

那么,谁负责与各个部门(各个对显卡有须要的程序)负责人沟通具体业务呢?

我认为是老总的全权代理人,个别是秘书 + 副总经理。

不同封装有不同的概念,至多在 WebGPU 中,这个代理人叫做“设施”,GPUDevice,它简直就是显卡的分身,WebGPU 程序中所要调取的资源、创立的对象、要触发的行为,都交给设施对象实现。

每个 WebGPU 程序应该都有本人的 GPUDevice,不同的设施对象创立的 Buffer、Texture 等资源是不互通的,而适配器呢,个别状况下是同一个,除非你短时间内把电脑的显卡给更改过,前一会儿是独显,过一会儿可能是核显了(这段话还有待技术验证,仅为我不负责任的猜想)。

如果你写过原生的 WebGL,你可能会联想到 gl 上下文变量了,没错,设施对象大部分时候就是 gl 上下文的作用,然而是有本质区别的。

② 缓冲、纹理、采样器

缓冲、纹理,即 GPUBufferGPUTexture 均是 GPU 显存中的数据对象,能在客户端代码(如果没特地阐明,就是指浏览器端的 JavaScript)组织、创立、上载数据、互相转化、反读数据。

WebGPU 进行渲染绘图时,Canvas 是一个非凡的 GPUTexture

采样器则是着色器程序对纹理采样时的参数封装。

看起来是 WebGL 相似对象 WebGLBufferWebGLTexture 以及纹理采样函数的“降级”,实际上调用时提供了更粗疏的传参,在数据上载、纹理与缓冲互相转化、再从显存读取到内存的“映射机制”上却大有不同。

这三个对象被称作“资源”,均由 GPUDevice 创立。

③ 绑定组

绑定组,我更违心称之为“资源绑定组”,即 GPUBindGroup;资源即“缓冲、纹理、采样器”的任意组合。

应用绑定组,容许把一组你须要的资源“打组”,传进着色器代码中,它与上面的“管线”是严密相干的。

为什么要打组呢?为什么我不能写个函数,按我须要把 GPUBufferGPUTextureGPUSampler 挨个像 WebGL 一样绑定到某个绑定点呢?

有两个起因:

  • 性能角度:打组自身就是缩小 CPU 到 GPU 信号通信的一种形式,想想你的硬盘,是间断大文件传得快,还是细碎的小文件快?
  • 复用角度:不同的着色行为可能会用一样的资源汇合,此时同一个绑定组就能够复用;想一想,肉馅儿塞进包子里叫肉包,包进饺子皮里就是肉饺子了。

绑定组是由 GPUDevice 创立的,是由第 ⑤ 大节中的 可编程通道编码器 调用并与管线理论一起运作的。

④ 着色器与管线

着色器即 GPUShaderModule,管线个别指 GPURenderPipelineGPUComputePipeline 两个。

着色器反对把任意着色器混在一段字符串中,顶点着色器、片元着色器、计算着色器能够共用一个 GPUShaderModule 对象,只需指定入口函数,这点与 WebGL 离开创立 VS、FS 是不一样的。

管线可不是 WebGLProgram 的降级,尽管 gl.useProgrampassEncoder.setPipeline 在行为上有相似的作用,即切换到指定的行为过程,然而,在 WebGPU 中这两个管线对象,除了附着对应的着色器对象外,还限定着管线不同阶段对应的状态参数。有三个状态参数对应着两大管线:

  • vertex、fragment
  • compute

例如:

/*
  ---------
  这里不具体开展,仅作为简略
  ---------
*/

const positionAttribDesc: GPUVertexAttribute = {shaderLocation: 0, // wgsl - @location(0)
  offset: 0,
  format: 'float32x3'
}
const colorAttribDesc: GPUVertexAttribute = {shaderLocation: 1, // wgsl - @location(1)
  offset: 0,
  format: 'float32x3'
}
const positionBufferDesc: GPUVertexBufferLayout = {attributes: [positionAttribDesc],
  arrayStride: 4 * 3, // sizeof(float) * 3
}
const colorBufferDesc: GPUVertexBufferLayout = {attributes: [colorAttribDesc],
  arrayStride: 4 * 3, // sizeof(float) * 3
}
// --- 创立 state 参数对象
const vertexState: GPUVertexState = {
  module: shaderModule,
  entryPoint: 'vs_main',
  buffers: [positionBufferDesc, colorBufferDesc]
}
const fragmentState: GPUFragmentState = {
  module: shaderModule,
  entryPoint: 'fs_main',
  targets: [{format: navigator.gpu.getPreferredCanvasFormat()
  }],
}
const primitiveState: GPUPrimitiveState = {topology: 'triangle-list'}

// --- 渲染管线 --- 
const renderPipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: vertexState,
  fragment: fragmentState,
  primitive: primitiveState
})

// --- 计算管线 ---
const computePipeline = device.createComputePipeline({
  layout: 'auto',
  compute: {
    module: shaderModule,
    entryPoint: 'cs_main',
  }
})

对应 GPUVertexStateGPUFragmentStateGPUComputeState 类型;下面说到绑定组是与管线严密相干的,这几个状态参数对象,与绑定组中的各个资源对象有着对应关系。

着色器模块对象和管线对象也是由 GPUDevice 创立的,管线对象甚至提供了异步创立的办法。

⑤ 编码器与队列

WebGPU 应用“编码器”去“记录”一帧内要做什么事件,譬如切换管线、设定接下来要用什么缓冲、绑定组,进而要进行什么操作(绘图或触发并行计算)。

这有什么益处?

编码器“记录”这些行为,是在 CPU 侧,也就是 JavaScript 实现的,这就解决了 WebGL 全局状态对象的问题:扭转一个状态,就要发动一条或多条 GL 函数的调用(只管应用扩大或在 WebGL 2.0 用各种技术进行了补救,然而也不能理论解决问题)。

编码记录实现后,会在 CPU 这边生成一个叫做“指令缓冲”对象,把以后帧的所有指令缓冲一次性提交给一个队列,那么以后帧就完结了战斗。

荒诞不经,大部分的逻辑组织交给更善于解决这些事件的 CPU 实现,最初集中发射给 GPU,这就是 WebGPU 于 WebGL 的一大长处。

编码器有哪些?

下面一段文字比拟粗略。

首先,为了辨别绘图操作、GPU 通用计算操作,WebGPU 应用“渲染通道编码器”、“计算通道编码器”,也就是 GPURenderPassEncoderGPUComputePassEncoder 来实现各自的行为编码、记录;以渲染通道编码为例:

上图参考自博客 Raw WebGPU。

而能创立这两个特定 GPU 计算的“通道编码器”的,叫做“指令编码器”,也就是 GPUCommandEncoder

指令编码器除了承载下面两个通道编码器的编码后果外,还额定提供了资源的拷贝行为、查问行为的编码,例如纹理与缓冲对象之间的相互拷贝等:

在理论的代码中,是按 GPUCommandEncoder 调用某个办法的程序进行记录的,例如 beginRenderPass()copyBufferToTexture() 等。

队列与指令缓冲

指令编码器的 finish 办法返回一个指令缓冲对象,即 GPUCommandBuffer,这个能够提交给队列对象 GPUQueue,队列对象是设施对象上的一个实例字段。

排列在队列上的除了指令缓冲,还有队列本人收回的“队列工夫线”上的行为,例如写入缓冲数据、写入纹理数据等。图示如下:

2. 重要机制

① 缓冲映射机制

缓冲映射,简略的说就是使得内存、显存中的缓冲数据能够替换着用的一种机制。具体的文章能够参考:

# WebGPU 中的缓冲映射机制

② 工夫线

WebGPU 标准中不同的行为兴许产生在的层面是不一样的,每个层面在运作的过程中都有它本人的工夫线。标准给出了三条工夫线:

  • 内容工夫线:内容工夫线上的行为,大多数是 JavaScript 对象的创立、JavaScript 办法的调用,这是最下面的一层;
  • 设施工夫线:此“设施”非 GPUDevice;设施工夫线上的行为,大多数是指浏览器底层 WebGPU 实现中的变动,这类行为的层级低于 JavaScript 的执行,操作的是“外部对象”,却还没到 GPU 执行的局部,例如生成指令缓冲;
  • 队列工夫线:此“队列”非 GPUQueue;队列工夫线上产生的行为,通常就是指 GPU 中具体任务的执行,例如绘制、资源上载、资源复制、通用计算调度等。

正文完
 0