webgl与webgpu比较

news2025/4/5 17:21:50

绘制流程

webgl无论是操作着色器,还是操作 VBO,亦或者是创建一些 Buffer、Texture 对象,基本上都得通过 gl 变量一条一条函数地走过程,顺序是非常讲究的。每一次调用 gl.xxx 时,都会完成 CPU 到 GPU 的信号传递,改变 GPU 的状态,是立即生效的。任意一条 gl 函数改变 GPU 状态的过程,大致要走完 CPU ~ 总线 ~ GPU 这么长一段距离。

WebGPU 虽然也有一个总管家一样的对象 —— device,类型是 GPUDevice,表示可以操作 GPU 设备的一个高层级抽象,它负责创建操作图形运算的各个对象,最后装配成一个叫 “CommandBuffer(指令缓冲,GPUCommandBuffer)”的对象并提交给队列,这才完成 CPU 这边的劳动。device.createXXX 创建过程中的对象时,并不会像 WebGL 一样立即通知 GPU 完成状态的改变,而是在 CPU 端写的代码就从逻辑、类型上确保了待会传递给 GPU 的东西是准确的,并让他们按自己的坑站好位,随时等待提交给 GPU。

image.png

GPU计算

WebGL 没有用于(GPU)计算的特殊 API,但仍然存在一种较为“hack”的方法可以实现这一功能。即将数据转换为一张图像,图像作为一个纹理上传到 GPU,随着纹理着色器不断地进行计算,纹理同时会被渲染出来。最后,我们得到的计算结果是 元素中的一组像素,我们必须用 getPixelsData 同步地读取,然后将颜色代码转换回你的数据。这看起来效率很低。

WebGPU 为(计算着色器)提供的 API 是不同的,它很容易忽略改进的重要性,但同时,它为开发者提供了全新的功能。它的工作方式是这样的:

两种方式的差异:

  1. 数据将作为缓存(buffer)上传到 GPU,你无需再将它转换成一张图片,所以它的性能开销更小
  2. 计算是异步执行的,它不会阻塞 JS 主线程(这意味着以 60 帧进行实时后置处理与复杂的物理模拟器的时代已经到来!)
  3. 我们将再也不需要创建 canvas 元素了,因此我们可以避开它对于图像尺寸的限制
  4. 我们无需做昂贵的、同步的 getPixelsData 操作
  5. 我们无需花费时间在像素转换回值数据上

WebGPU 性能明显更高,比 WebGL 快 3.5 倍有余。

数据传入

在 WebGPU 中,一切都是按字节偏移量或索引(通常称为“location”)进行的。而webgl是通过名称连接的,所以参数的顺序并不重要。
image.png

画布

WebGL 为您管理画布。您可以在创建 WebGL 上下文时选择抗锯齿、preserveDrawingBuffer、模板、深度和 alpha,之后由 WebGL 管理画布本身。您所要做的就是设置 canvas.width和canvas.height。

WebGPU 你必须自己做很多事情。如果您想要深度缓冲区,您可以自己创建它(带或不带模板缓冲区)。如果您想要抗锯齿,您可以创建自己的多重采样纹理并将它们解析为画布纹理。但是,正因为如此,与 WebGL 不同,您可以使用一个 WebGPU device渲染到多个画布。

MipMap

在 WebGL 中,您可以创建纹理的 0 级 mip,然后调用 gl.generateMipmap,WebGL 将生成所有其他 mip 级别。WebGPU没有这样的功能。如果您想要纹理的 mip,您必须自己生成它们。

采样器

在 WebGL1 中,采样器不存在,或者换句话说,采样器由 WebGL 内部处理。在 WebGL2 中,使用采样器是可选的。在 WebGPU 中必须需要采样器。

缓冲区和纹理的大小

在 WebGL 中,我们可以创建缓冲区或纹理,然后随时更改其大小。例如,如果我们调用gl.bufferData,缓冲区将被重新分配。如果调用gl.texImage2D,纹理将被重新分配。纹理的常见模式是创建一个 1x1 像素占位符,然后可以立即开始渲染,然后异步加载图像。当图像加载完成后,我们可以就地更新纹理。

在 WebGPU 中,纹理和缓冲区大小、用法、格式是不可变的。我们可以更改它们的内容,但无法更改有关它们的任何其他内容。这意味着,要更改的 WebGL 中的模式(如上面提到的示例)需要重构以创建新资源。

// webgpu模式伪代码
let tex = createTexture(size: [1, 1]);
fillTextureWith1x1PixelPlaceholder(tex)
imageLoad(url).then(img => {
  tex.destroy();  // 删除旧纹理
  tex = createTexture(size: [img.width, img.height]);
  copyImageToTexture(tex, image));
});

在窗口画布大小改变时,webgl会自动采样处理,而webgpu需要我们手动销毁旧纹理(颜色和深度)并创建新纹理,属性sampleCount实际上是WebGL上下文创建属性的抗锯齿属性的模拟。在创建 WebGL 上下文时,sampleCount: 4 相当于 WebGL 的 antialias: true (默认值)或者说是MSAA,而sampleCount: 1 相当于 antialias: false

const newRenderTarget = device.createTexture({
        size: [canvas.width, canvas.height],
        format: presentationFormat,
        sampleCount,
        usage: GPUTextureUsage.RENDER_ATTACHMENT,
      });
      canvasInfo.renderTarget = newRenderTarget;

WebGL 会尽量不耗尽内存,这意味着如果请求 16000x16000 画布,WebGL 可能会返回 4096x4096 画布。WebGL 这样做的原因是 (1) 在多个显示器上拉伸画布可能会使尺寸大于 GPU 可以处理的大小 (2) 系统内存可能不足,WebGL 会返回较小的绘图缓冲区,而不仅仅是崩溃。
在 WebGPU 中,我们必须自己检查内存是否不足,并且与 WebGPU 中的其他所有内容一样,这样做是异步的。

device.pushErrorScope('out-of-memory');
context.configure({...});
if (sampleCount > 1) {
  const newRenderTarget = device.createTexture({...});
  ...
}
 
const newDepthTexture = device.createTexture({...});
...
device.popErrorScope().then(error => {
  if (error) {
    // 内存不足,尝试较小的大小
  }
});

Shader

代码风格

wgsl偏rust风格,glsl偏c风格。WGSL 的概念是,如果不指定变量的类型,它将根据右侧表达式的类型推导出来,而 GLSL 要求始终指定类型。

入口函数

WebGL 和 WebGPU 之间的另一个区别是,在 WebGPU 中,我们可以将多个着色器放在同一个代码块中。在 WebGL 中,始终会调用着色器的入口main函数,但在 WebGPU 中,当使用着色器时,可以指定要调用的函数。

编译

webgpu中可以一次性编译多个shader。

const shaderModule = device.createShaderModule({code: shaderSrc});

在 WebGL 中,如果着色器未编译,则需要通过gl.getShaderParameter检查_ COMPILE_STATUS_,如果编译失败,需要通过调用 gl.getShaderInfoLog 来提取错误消息。如果不这样做,则不会显示任何错误。

在 WebGPU 中,大多数实现都会将错误打印到 JavaScript 控制台。当然,仍然可以自己检查错误,但即使什么都不做,仍然可以获得一些有用的信息。

Program与Pipeline

WebGL 中发生的几件事将合并为 WebGPU 中的一件事情。例如,链接着色器、设置属性参数、选择绘制模式(点、线、三角形)、设置如何使用深度缓冲区。
createProgram => createRenderPipeline

Uniform

在 WebGL 情况下,我们计算一个值并将其传递到gl.uniform???适当的位置。

在 WebGPU 的情况下,我们将值写入类型化数组,然后将这些类型化数组的内容复制到相应的 GPU 缓冲区。

坐标

在 WebGL NDC空间中 Z 为 -1 到 +1;在 WebGPU 中,它是 0 到 1。
https://gpuweb.github.io/gpuweb/#coordinate-systems
纹理坐标(0, 0, 1, 1)在 WebGL 中引用左下角,但在 WebGPU 中引用左上角。

WGSL

gl_FragCoord是@builtin(position),中0,0 是左上角,而 WebGL 中 0,0 是左下角
gl_VertexID 是 @builtin(vertex_index) myVarOrField: u32
gl_InstanceID 是 @builtin(instance_index) myVarOrField: u32
gl_Position 是 @builtin(position) vec4f ,它可以是顶点着色器的返回值或顶点着色器返回的结构体中的字段
没有 gl_PointCoord 等价物,因为点在 WebGPU 中只有 1 个像素
没有#ifdef宏定义,需要用常量覆盖。

参考资料

  1. WebGPU from WebGL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/996449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode】210. 课程表 II——拓扑排序

题目链接:210. 课程表 II 题目描述: 现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。 例如…

CAPL自动化测试通信电压

通信电压 1、系统框架2、控制电压模块3、检查通信是否正常4、检查电压标准5、CAPL 控制VH1160的函数 分三个模块 控制电源模块,分析通信是否正常模块,判断电压是否符合标准。 1、系统框架 应该分为三大模块:控制电压模块,检查通…

国内 Docker 镜像加速器和国内公共镜像仓库那些事

前言 首先我们知道,全球最大的公共镜像仓库是 Docker 公司自己搭建的 Docker Hub,也是权威性最高的,里面包含了各种各样的官方镜像,Docker Hub 为每一个注册用户提供了个人镜像仓库服务,该个人镜像仓库是公共的。 以上…

如何用echarts画一个好看的饼图

前言 最近有个需求,需要绘制一个饼图,为此我根据这次需求来整理了一下关于 echarts 饼图绘制的一些知识点,在这次需求中我需要用到的属性我会详细讲解,其他的属性我会粗略地说一下(并加入其他博主的文章的跳转&#x…

【C++杂货铺】探索stack和queue的底层实现

文章目录 一、stack的介绍和使用1.1 stack的介绍1.2 stack的使用1.2.1 最小栈1.2.2 栈的压入、弹出序列1.2.3 逆波兰表达式求值1.2.4 用栈实现队列 二、queue的介绍和使用2.1 queue的介绍2.2 queue的使用2.2.1 二叉树的层序遍历 三、模拟实现3.1 stack模拟实现3.2 queue模拟实现…

数分面试题2-牛客

1、面对大方差如何解决 1,AB实验场景下,如果一个指标的方差较大表示它的波动较大,那么实验组和对照组的显著差异可能是因为方差较大即随机波动较大。解决方法有:PSM方法、CUPED(方差缩减) PSM代表"Propensity Score Matchin…

【C进阶】分析 C/C++程序的内存开辟与柔性数组(内有干货)

前言: 本文是对于动态内存管理知识后续的补充,以及加深对其的理解。对于动态内存管理涉及的大部分知识在这篇文章中 ---- 【C进阶】 动态内存管理_Dream_Chaser~的博客-CSDN博客 本文涉及的知识内容主要在两方面: 简单解析C/C程序…

CSS宽度问题

一、魔法 为 DOM 设置宽度有哪些方式呢?最常用的是配置width属性,width属性在配置时,也有多种方式: widthmin-widthmax-width 通常当配置了 width 时,不会再配置min-width max-width,如果将这三者混合使…

【数据结构】——排序的相关习题

目录 一、选择填空判断题题型一(插入排序——直接插入排序)题型二(插入排序——折半插入排序)题型三(插入排序——希尔排序)题型四(交换排序——冒泡排序)题型五(交换排序…

Java注解以及自定义注解

Java注解以及自定义注解 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为 我们提供的元注解和相关定义注解的语法。 1、注解 1.1 注解的官方定义 注解是一种元数据形式。…

OLED透明屏导航:驾驶安全的未来趋势

在不断发展的科技领域中,OLED透明屏技术的出现为导航系统带来了革命性的变革。 今天,尼伽将深入探讨OLED透明屏导航的技术原理和应用前景,展示其在驾驶安全方面的优势,并引用最新的数据、报告和行业动态,以增加可信度…

无涯教程-JavaScript - CUMIPMT函数

描述 CUMIPMT函数返回start_period和end_period之间的贷款累计利息。 语法 CUMIPMT (rate, nper, pv, start_period, end_period, type)争论 Argument描述Required/OptionalRateThe interest rate.RequiredNperThe total number of payment periods.RequiredPvThe present …

产品经理学习笔记

产品文档之BRD、MRD和PRD - 知乎BRD、MRD和PRD一起被认为是从市场到产品需要形成的标准规范文档: 1、BRD(Business Requirement Document),商业需求文档,是一份产品商业论证报告,基于商业目标或价值所描述的…

RocketMQMessageListener使用错误问题分析与排查

背景 RocketMQ与SpingBoot相结合可以大大降低我们开发的复杂度,但是最近在一个新项目中使用RocketMQMessageListener 监听消息,导致消费者启动失败,提示该消费组已经被创建了,请重新申请一个消费者组。 Caused by: org.apache.r…

java并发编程 ConcurrentLinkedQueue详解

文章目录 1 ConcurrentLinkedQueue是什么2 核心属性详解3 核心方法详解3.1 add(E e)3.2 offer(E e)3.3 poll()3.4 size()3.5 并发情况分析 4 总结 1 ConcurrentLinkedQueue是什么 ConcurrentLinkedQueue是一个无界的并发队列,和LinkedBlockingQueue相比&#xff0c…

【新版】系统架构设计师 - 软件架构设计<轻量级架构>

个人总结,仅供参考,欢迎加好友一起讨论 文章目录 架构 - 软件架构设计<轻量级架构>考点摘要轻量级架构表示层业务逻辑层持久层数据库 SSH与SSMHibernate与Mybatis 架构 - 软件架构设计<轻量级架构> 考点摘…

九)Stable Diffussion使用教程:ControlNet

在 ControlNet 出现之前,基于扩散模型的 AI 绘画是极难控制的,因为扩散的过程充满了随机性。 如果只是纯粹自娱自乐,这种随机性并不会带来多大困扰; 但在产业化上应用就难以普及了,因为随机性直接导致的就是缺乏稳定…

【C++漂流记】一文搞懂类与对象中的对象特征

在C中,类与对象是面向对象编程的基本概念。类是一种抽象的数据类型,用于描述对象的属性和行为。而对象则是类的实例,具体化了类的属性和行为。本文将介绍C中类与对象的对象特征,并重点讨论了对象的引用。 文章目录 一、构造函数和…

【云原生进阶之PaaS中间件】第二章Zookeeper-1-综述

1 Zookeeper基础 1.1 简介 ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等。 Zookeeper是hadoop的一个子项目,其发…

职场工作与生活

序言: 和很多在CSDN的博主一样,大家在工作之后就很少或者是不再回到CSDN,确实自己也一年多没上了。因为可能当初大家在这就是为了记录和分享当初自己学习技术的东西。而大家走出象牙塔开始工作后,发生了很大的转变。在国内…