当前位置: 首页 > Web前端 > JavaScript

WebGL和WebGPU的比较[1]序曲

时间:2023-03-27 13:01:09 JavaScript

这是关于历史的,不适合直奔主题的朋友。1为什么是WebGPU而不是WebGL3.0如果深入到Web图形技术的底层,一定能追溯到1990年代提出的OpenGL技术,一定能看出WebGL是基于OpenGLES的.OpenGL在弱显卡时代发挥了应有的价值。显卡驱动我们都知道现在的显卡都必须安装显卡驱动。通过显卡驱动暴露的API,我们可以操作GPU来完成对图形处理器的操作。问题是显卡驱动和普通编程世界里的汇编一样。简单年表的图形APIOpenGL就是干这个的,负责封装上层的接口,处理下层的显卡驱动,但是众所周知,它的设计风格已经跟不上现代GPU的特点了。微软为此提供的最新图形API是Direct3D12,苹果为此提供的最新图形API是Metal,还有一个著名的组织创建了Vulkan,称为Khronos。D3D12现在在Windows和PlayStation上大放异彩,Metal在Mac和iPhone上,你可能会在Android手机评论中看到很多Vulkan。这三个图形API被称为三大现代图形API,与现代显卡(无论是PC还是移动设备)都息息相关。WebGL之所以能在各种浏览器中运行,忘了说OpenGL在2006年输给了Khronos,现在这个很老的显卡驱动基本上所有操作系统都安装不了了。那么问题来了,为什么基于OpenGLES的WebGL可以运行在各种操作系统的浏览器中呢?因为WebGL不能再是OpenGLES了。在Windows上,它现在通过D3D转换为显卡驱动程序,在macOS上它是Metal。不过,时间越是接近现在,这种无父无母的实施就越是困难。苹果的Safari浏览器近几年才支持WebGL2.0,放弃了OpenGLES中的GPGPU特性。也许你看不到Safari上实现的WebGL2.0的GPGPU。果子哥目前忙于Metal和更棒的M系列自研芯片呢。WebGPU名称的由来那么,综上所述,你明白为什么下一代Web图形界面不叫WebGL3.0了吗?它不再与GL一脉相承。为了防止现代巨头在名称上打架,采用了更接近硬件名称的WebGPU。WebGPU无论是编码风格还是性能,从根本上就和WebGL不在一个时代。说句题外话,OpenGL也不是没有学习价值,只是会存在一段时间,WebGL也一样。2编码风格与WebGL的比较WebGL其实可以说是OpenGL的影子,OpenGL的风格对WebGL的风格影响很大。研究过WebGL接口的朋友都知道一件事:gl变量,准确的说就是WebGLRenderingContext对象,而WebGL2.0就是WebGLRenderingContext2.OpenGL的编码风格,无论是操作shader,操作VBO,还是创建一些Buffer和Textureobjects,基本上要通过gl变量走完每个函数的过程,顺序很讲究,比如下面是创建两个shader并执行编译连接的代码:constvertexShaderCode=`attributevec4a_position;voidmain(){gl_Position=a_position;}`constfragmentShaderCode=`precisionmediumpfloat;voidmain(){gl_FragColor=vec4(1,0,0.5,1);}`constvertexShader=gl.createShader(gl.VERTEX_SHADER)gl.shaderSource(vertexShader,vertexShaderCode)gl.compileShader(vertexShader)constfragmentShader=gl.createShader(gl.FRAGMENT_SHADER)gl.shaderSource(fragmentShader,fragmentShaderCode)gl.compileShader(fragmentShader)constprogram=gl.createProgram()gl.attachShader(program,vertexShader)gl.attachShader(program,fragmentShader)gl.linkProgram(program)//还需要明确指定需要使用哪个程序gl.useProgram(program)//继续操作vertexdataandtriggerdrawing//...Createashader,分配shader代码并编译的三行jsWebGL调用可以说必须这样写。最多可以改变vs和fs的创建和编译顺序,但是必须在p有人说这些操作的CPU负载在rogram之前完成没有关系。可以封装成一个JavaScript函数来隐藏这些过程的细节,只需要传递参数即可。不错,这个封装的不错,很多js库都做了,很实用。然而,仍然存在一个无法逾越的鸿沟——那就是OpenGL本身的问题。每次调用gl.xxx,完成CPU到GPU的信号传输,改变GPU的状态,立即生效。熟悉计算机基础知识的朋友应该知道计算机内部的时间和硬件之间的距离有多么重要。全世界在信号传输上花费了数十年的时间。上面任意一个gl函数改变GPU状态的过程,大概需要走完CPU~bus~GPU这么长的距离。我们都知道做事最好一次性准备素材,不要来回跑那么多次,OpenGL就是这样。有人说这个为什么不改成发送一次?由于历史原因,在OpenGL盛行的时候,GPU的工作并没有那么复杂,也不需要这么高级的设计。综上所述,WebGL存在CPU负载隐患,这是由OpenGL的状态机制决定的。三大现代图形API并非如此。他们更倾向于先准备东西,最后向GPU提交一张完整的设计图和缓冲数据。GPU只需要拿着它就可以专注于它的工作。WebGPU的组装编码风格虽然WebGPU也有一个类似总管的对象——device,类型是GPUDevice,代表了一个可以操作GPU设备的高层抽象。它负责创建各种进行图形操作的对象,最后组装成一个“CommandBuffer(指令缓冲区,GPUCommandBuffer)”对象提交到队列中,这样就完成了CPU端的工作。因此,device.createXXX在进程中创建对象时,并不会像WebGL一样立即通知GPU完成状态改变,而是CPU端写的代码保证了后面传递给GPU的东西是逻辑上的和类型方面。准确,并让他们根据自己的坑站好,随时等待提交给GPU。在这里,指令缓冲区对象拥有完整的数据(几何、纹理、着色器、流水线调度逻辑等),GPU一拿到就知道要做什么。//异步函数中constdevice=awaitadapter.requestDevice()constbuffer=device.createBuffer({/*组装几何体,将数据传输到内存中,最终成为vertexAttribute和uniform等资源*/})consttexture=设备。createTexture({/*组装纹理和采样信息*/})constpipelineLayout=device.createPipelineLayout({/*创建管线布局,传递绑定组布局对象*/})/*创建着色器模块*/constvertexShaderModule=device.createShaderModule({/*...*/})constfragmentShaderModule=device.createShaderModule({/*...*/})/*计算着色器可能使用的着色器模块constcomputeShaderModule=device.createShaderModule({/*...*/})*/constbindGroupLayout=device.createBindGroupLayout({/*创建绑定组的布局对象*/})constpipelineLayout=device.createPipelineLayout({/*传递绑定组的布局对象*/})/*上面两个布局对象其实可以懒惰的,不创建。虽然绑定组需要绑定组布局来通知相应的流水线阶段绑定组的资源是什么样的,但是绑定组布局可以由流水线对象通过编程阶段的代码自行推断。这段绑定组布局对象的示例代码保存了完整的过程*/constpipeline=device.createRenderPipeline({/*创建管道并指定管道各阶段所需的材料。共有三个阶段可以按顺序传输着色实现可编程,即vertex、fragment、计算的各个阶段也可以指定自己需要的数据和信息,比如buffer等。另外,pipeline还需要一个pipelinelayout对象,其内置的bindinggrouplayout物体被shader知道后才能在channel中使用绑定组资源长什么样子*/})constbindGroup_0=deivce.createBindGroup({/*对资源进行分组,将buffer和texture分组到逻辑组中,方便每次进程调用,进程就是pipeline,这里必须传递绑定组布局对象,可以从管道中推断出来,也可以直接传递绑定组布局对象本身*/})constcommandEncoder=device.createCommandEncoder()//创建命令缓冲区编码器对象constrenderPassEncoder=commandEncoder.beginRenderPass()//启动一个渲染通道编码器//你也可以启动一个计算通道//constcomputePassEncoder=commandEncoder.beginComputePass({/*...*/})/*以渲染通道为例,使用renderPassEncoder完成这个通道中做什么的顺序设置,比如*///第一次绘制,设置管道0,绑定组0,绑定组1,vbo,触发绘制renderPassEncoder.setPipeline(renderPipeline_0)renderPassEncoder.setBindGroup(0,bindGroup_0)renderPassEncoder.setBindGroup(1,bindGroup_1)renderPassEncoder.setVertexBuffer(0,vbo,0,size)renderPassEncoder.draw(vertexCount)//第二次绘制,设置管道1,另一个绑定组并触发绘制renderPassEncoder.setPipeline(renderPipeline_1)renderPassEncoder.setBindGroup(1,another_bindGroup)renderPassEncoder.draw(vertexCount)//结束通道编码renderPassEncoder.endPass()//最后提交到队列,即commandEncoder调用finish完成编码,返回一个com命令缓存chongdevice.queue.submit([commandEncoder.finish()])以上过程是WebGPU的通用代码,很粗糙,没有细节,但基本上是channelencoder的部分代码有这样的逻辑,作者保持比较完整,以便读者更好地观察指令编码器如何对通道进行编码,最后结束编码以创建指令缓冲区并将其提交到队列。烹饪技巧被用来比喻烹饪。OpenGL编程就像做一道菜时需要什么调料,做完一道菜再继续做下一道菜;而现代的图形API是一锅多火,所有的材料都在正确的地方,包括加工过的配料和辅助材料。即使是一个厨师(CPU)也可以同时准备好几道菜,效率很高。3多线程和强大的通用计算(GPGPU)能力WebWorker多线程WebGL的主要管家对象是gl变量,它必须依赖于HTMLCanvas元素,也就是说必须由主线程获取,并且GPU状态只能在主线程中调度。WebWorker技术的多线程能力只能处理数据,没有多大用处。WebGPU改变了一般管家对象的获取方式。WebWorker中也可以访问adapter对象所依赖的navigator.gpu对象,因此也可以在Worker中创建一个device,也可以组装一个指令缓冲区,实现多线程指令缓冲区提交。实现CPU端多线程调度GPU的能力。GeneralComputing(GPGPU)如果WebWorker在CPU端是多线程的,那么GPU本身的多线程也必须使用。使这成为可能的是一种叫做“计算着色器”的东西,它是可编程管道中的一个可编程阶段,在OpenGL中出现较晚(因为早期的显卡没有利用其并行通用计算能力),更不用说WebGL直到2.0才支持它,苹果老兄甚至懒得为WebGL2.0实现这个功能。WebGPU出厂的时候就自带这个东西。它通过计算着色器,使用GPU中CU(ComputeUnit)旁边的共享内存,比普通显存快很多。关于计算着色器的资料不多,目前只能看例子,参考资料里还附了一篇博客。将GPGPU带到Web端后,脚本语言的runtime(deno、浏览器JavaScript,甚至未来的nodejs也可能支持WebGPU)可以接入GPU强大的并行计算能力。据说tensorflow.js使用了WebGPU作为后端技术后,性能有了很大的提升,对深度学习等领域有很大的帮助。即使用户的浏览器没有那么新潮,渲染编程也没有那么快取代WebGL,WebGPU的通用计算能力在其他领域也能大放异彩,更不用说计算着色器也可用于渲染。太诱人了!4浏览器实现截至本文发布,Edge和Chrome在金丝雀版本中可以通过flag开放试用。Edge和Chrome都使用Chromium核心。Chromium是一个通过Dawn模块实现的WebGPUAPI。根据相关资料,Dawn的DawnNative部分负责与三大图形API进行通信,并将信息传递给一个名为DawnWire的模块。DawnWire模块负责与JavaScriptAPI通信,也就是你编写的WebGPU代码。WGSL也实现了这部分。Dawn是用C++实现的,您可以在参考资料中找到链接。FireFox使用gfx-rs项目来实现WebGPU,显然是用Rust语言实现的,也有类似Dawn的模块设计。Safari更新了自己的WebKit来实现WebGPU。5未来展望,不谈宏图大计,但随着红绿蓝公司的GPU技术越来越成熟,各种移动端的GPU逐渐完善,现代三大图形API肯定还在发展,WebGPU肯定能够在Web上发布现代图形。图形处理单元(GPU)的强大能力,无论是图形游戏,还是通用并行计算带来的机器学习和AI能力。参考资料GoogleDawnPagegfx-rsGitHubHomePageWeb上的GPUCompute入门