- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
这部分不会详细展开,以后写教程时会深入。以下只是核心概念,是绝大多数 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 上下文的作用,但是是有本质区别的。
缓冲、纹理,即 GPUBuffer
、GPUTexture
均是 GPU 显存中的数据对象,能在客户端代码(如果没特别说明,就是指浏览器端的 JavaScript)组织、创建、上载数据、相互转化、反读数据。
WebGPU 进行渲染绘图时,Canvas 是一个特殊的 GPUTexture
。
采样器则是着色器程序对纹理采样时的参数封装。
看起来是 WebGL 类似对象 WebGLBuffer
、WebGLTexture
以及纹理采样函数的“升级”,实际上调用时提供了更细致的传参,在数据上载、纹理与缓冲相互转化、再从显存读取到内存的“映射机制”上却大有不同。
这三个对象被称作“资源”,均由 GPUDevice
创建。
绑定组,我更愿意称之为“资源绑定组”,即 GPUBindGroup
;资源即“缓冲、纹理、采样器”的任意组合。
使用绑定组,允许把一组你需要的资源“打组”,传进着色器代码中,它与下面的“管线”是紧密相关的。
为什么要打组呢?为什么我不能写个函数,按我需要把 GPUBuffer
、GPUTexture
、GPUSampler
挨个像 WebGL 一样绑定到某个绑定点呢?
有两个原因:
绑定组是由 GPUDevice
创建的,是由第 ⑤ 小节中的 可编程通道编码器 调用并与管线实际一起运作的。
着色器即 GPUShaderModule
,管线一般指 GPURenderPipeline
、GPUComputePipeline
两个。
着色器支持把任意着色器混在一段字符串中,顶点着色器、片元着色器、计算着色器可以共用一个 GPUShaderModule
对象,只需指定入口函数,这点与 WebGL 分开创建 VS、FS 是不一样的。
管线可不是 WebGLProgram
的升级,虽然 gl.useProgram
和 passEncoder.setPipeline
在行为上有类似的作用,即切换到指定的行为过程,但是,在 WebGPU 中这两个管线对象,除了附着对应的着色器对象外,还限定着管线不同阶段对应的状态参数。有三个状态参数对应着两大管线:
例如:
/*
---------
这里不详细展开,仅作为简略
---------
*/
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',
}
})
对应 GPUVertexState
、GPUFragmentState
、GPUComputeState
类型;上面说到绑定组是与管线紧密相关的,这几个状态参数对象,与绑定组中的各个资源对象有着对应关系。
着色器模块对象和管线对象也是由 GPUDevice
创建的,管线对象甚至提供了异步创建的方法。
WebGPU 使用“编码器”去“记录”一帧内要做什么事情,譬如切换管线、设定接下来要用什么缓冲、绑定组,进而要进行什么操作(绘图或触发并行计算)。
这有什么好处?
编码器“记录”这些行为,是在 CPU 侧,也就是 JavaScript 完成的,这就解决了 WebGL 全局状态对象的问题:改变一个状态,就要发起一条或多条 GL 函数的调用(尽管使用扩展或在 WebGL 2.0 用各种技术进行了弥补,但是也不能实际解决问题)。
编码记录完成后,会在 CPU 这边生成一个叫做“指令缓冲”对象,把当前帧的所有指令缓冲一次性提交给一个队列,那么当前帧就结束了战斗。
合情合理,大部分的逻辑组织交给更擅长处理这些事情的 CPU 完成,最后集中发射给 GPU,这就是 WebGPU 于 WebGL 的一大优点。
编码器有哪些?
上面一段文字比较粗略。
首先,为了区分绘图操作、GPU 通用计算操作,WebGPU 使用“渲染通道编码器”、“计算通道编码器”,也就是 GPURenderPassEncoder
、GPUComputePassEncoder
来实现各自的行为编码、记录;以渲染通道编码为例:
上图参考自博客 Raw WebGPU。
而能创建这两个特定 GPU 计算的“通道编码器”的,叫做“指令编码器”,也就是 GPUCommandEncoder
:
指令编码器除了承载上面两个通道编码器的编码结果外,还额外提供了资源的拷贝行为、查询行为的编码,例如纹理与缓冲对象之间的互相拷贝等:
在实际的代码中,是按 GPUCommandEncoder
调用某个方法的顺序进行记录的,例如 beginRenderPass()
、copyBufferToTexture()
等。
队列与指令缓冲
指令编码器的 finish
方法返回一个指令缓冲对象,即 GPUCommandBuffer
,这个可以提交给队列对象 GPUQueue
,队列对象是设备对象上的一个实例字段。
排列在队列上的除了指令缓冲,还有队列自己发出的“队列时间线”上的行为,例如写入缓冲数据、写入纹理数据等。图示如下:
缓冲映射,简单的说就是使得内存、显存中的缓冲数据可以交换着用的一种机制。详细的文章可以参考:
WebGPU 规范中不同的行为也许发生在的层面是不一样的,每个层面在运作的过程中都有它自己的时间线。规范给出了三条时间线:
GPUDevice
;设备时间线上的行为,大多数是指浏览器底层 WebGPU 实现中的变化,这类行为的层级低于 JavaScript 的执行,操作的是“内部对象”,却还没到 GPU 执行的部分,例如生成指令缓冲;GPUQueue
;队列时间线上发生的行为,通常就是指 GPU 中具体任务的执行,例如绘制、资源上载、资源复制、通用计算调度等。广播的原则 如果两个数组的后缘维度(从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为它们是广播兼容的。广播会在缺失维度和(或)轴长度为1的维度上进行。 在上面的对arr每一列减去列
之前在讲 MySQL 事务隔离性提到过,对于写操作给读操作的影响这种情形下发生的脏读、不可重复读、虚读问题。是通过MVCC 机制来进行解决的,那么MVCC到底是如何实现的,其内部原理是怎样的呢?我们要
我创建了一个 JavaScript 对象来保存用户在 ColorBox 中检查复选框时设置的值。 . 我对 jQuery 和“以正确的方式”编程 JavaScript 比较陌生,希望确保以下用于捕获用
我为了回答aquestion posted here on SO而玩示例,发现很难理解python的import *破坏作用域的机制。 首先是一点上下文:这个问题不涉及实际问题;我很清楚from fo
我想让我的类具有标识此类的参数 ID。例如我想要这样的东西: class Car { public static virtual string ID{get{return "car";}} }
更新:我使用的是 Java 1.6.34,没有机会升级到 Java 7。 我有一个场景,我每分钟只能调用一个方法 80 次。它实际上是由第 3 方编写的服务 API,如果您多次调用它,它会“关闭”(忽
希望这对于那些使用 Javascript 的人来说是一个简单的答案...... 我有一个日志文件,该文件正在被一个脚本监视,该脚本将注销中的新行提供给任何连接的浏览器。一些人评论说,他们希望看到的更多
我们正在开发针对 5.2 开发的 PHP 应用程序,但我们最近迁移到了 PHP 5.3。我们没有时间去解决所有迁移到 PHP 5.3 的问题。具体来说,我们有很多消息: Declaration of
简介 在实现定时调度功能的时候,我们往往会借助于第三方类库来完成,比如: quartz 、 spring schedule 等等。jdk从1.3版本开始,就提供了基于 timer 的定时调度功能。
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而
我将编写自己的自定义控件,它与 UIButton 有很大不同。由于差异太大,我决定从头开始编写。所以我所有的子类都是 UIControl。 当我的控件在内部被触摸时,我想以目标操作的方式触发一条消息。
在我的代码中,在创建 TIdIMAP4 连接之前,我设置了一大堆 SASL 机制,希望按照规定的“最好到最差”顺序,如下所示: IMAP.SASLMechanisms.Add.SASL := mIdS
在 Kubernetes 中,假设我们有 3 个 pod,它们物理上托管在节点 X、Y 和 Z 上。当我使用“kubectl expose”将它们公开为服务时,它们都是集群中的节点(除了 X、Y 和
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 9 年前。 Improve this ques
我知道进程间通信 (ipc) 有几种方法,例如: 文件 信号 socket 消息队列 管道 命名管道 信号量 共享内存 消息传递 内存映射文件 但是我无法找到将这些机制相互比较并指出它们在不同环境中的
当我尝试连接到 teradata 时,出现了TD2 机制不支持单点登录 错误。 在 C# 中,我遇到了类似的问题,我通过添加 connectionStringBuilder.Authetication
我有一个带有 JSON API 的简单 Javascript 应用程序。目前它在客户端运行,但我想将它从客户端移动到服务器。我习惯于学习新平台,但在这种情况下,我的时间非常有限 - 所以我需要找到绝对
我想了解事件绑定(bind)/解除绑定(bind)在浏览器中是如何工作的。具体来说,如果我删除一个已经绑定(bind)了事件的元素,例如使用 jQuery:$("#anElement").remove
我不是在寻找具体答案,只是一个想法或提示。我有以下问题: Android 应用程序是 Web 服务的客户端。它有一个线程,通过 http 协议(protocol)发送事件(带有请求 ID 的 XML
我正在研究 FreeBSD TCP/IP 栈。似乎有 2 种 syn flood 机制,syncookies 和 syncache。我的问题是关于 syncookies,它是从头开始还是在 SYN 队
我是一名优秀的程序员,十分优秀!