WebGPU学习之路 01

643 阅读2分钟

一、什么是WebGPU

WebGPU是一套基于浏览器的图形API,浏览器封装了现代图形API(Dx12、Vulkan、Metal),提供给Web 3D程序员,为 Web释放了更多的GPU 硬件的功能 (知乎上Orillusion的回答)

二、相比于WebGL,WebGPU会带来什么

  1. PromiseAPI / async + await 的异步语法加入:对网络数据加载、解码等同步耗时操作有了更好的支持,一定程度上避免了回调地狱问题
  2. 面向对象的 API 设计:相对于 WebGL API 的过程式调用,选项式图形 API的使用更科学
  3. 不再使用全局状态机制:使用指令缓冲节约了 CPU 到 GPU 之间的信息传递成本,要知道 WebGL 切换某个状态(例如纹理、着色器程序),就是在切换全局状态对象,这个在微观上是比较耗时的,而 60 帧速率的每一帧只有 16.67 毫秒左右,能在微观节约每一点每一滴的时间,累计起来就不少了
  4. 原生支持计算管线、计算着色器:WebGL 磨磨蹭蹭实现的 GPU 并行计算功能直接提供出来,并与渲染管线地位对等。 (知乎上为名的回答)

三、浅入WebGPU

  • WebGPU工作流

webGPU工作流.jpg

  • 相关执行代码
初始化WebGPU
async function initWebGPU(){
    if(!navigator.gpu) throw new Error('Not Support WebGPU') 
    const adapter = await navigator.gpu.requestAdapter({ 
         powerPreference: 'high-performance' 
         // powerPreference: 'low-power' 
     }) 
     if (!adapter) throw new Error('No Adapter Found') 
     const device = await adapter.requestDevice() 
     const context = canvas.getContext('webgpu') as GPUCanvasContext 
     const format = navigator.gpu.getPreferredCanvasFormat ? navigator.gpu.getPreferredCanvasFormat() : context.getPreferredFormat(adapter) 
     const devicePixelRatio = window.devicePixelRatio || 1 
     canvas.width = canvas.clientWidth * devicePixelRatio 
     canvas.height = canvas.clientHeight * devicePixelRatio 
     const size = {width: canvas.width, height: canvas.height} 
     context.configure({ 
 // json specific format when key and value are the same 
         device, format, 
 // prevent chrome warning 
         alphaMode: 'opaque' 
 }) 
 return {device, context, format, size} 
}

// 配置GPU管线
async function initPipeline(device: GPUDevice, format: GPUTextureFormat): Promise<GPURenderPipeline> { 
 const descriptor: GPURenderPipelineDescriptor = { 
     layout: 'auto', 
     vertex: { 
         module: device.createShaderModule({ 
         code: triangleVert 
         }), 
     entryPoint: 'main' 
     }, 
     primitive: { 
         topology: 'triangle-list' // try point-list, line-list, line-strip, triangle-strip? 
     }, 
     fragment: { 
         module: device.createShaderModule({ 
         code: redFrag 
         }), 
         entryPoint: 'main', 
         targets: [ 
         { 
             format: format 
         } 
         ] 
     } 
 } 
 return await device.createRenderPipelineAsync(descriptor) 
 } 
 
 录制command队列并提交
 function draw(device: GPUDevice, context: GPUCanvasContext, pipeline: GPURenderPipeline) { 
     const commandEncoder = device.createCommandEncoder() 
     const view = context.getCurrentTexture().createView() 
     const renderPassDescriptor: GPURenderPassDescriptor = { 
     colorAttachments: [ 
         { 
             view: view, 
             clearValue: { r: 0, g: 0, b: 0, a: 1.0 }, 
             loadOp: 'clear', // clear/load 
             storeOp: 'store' // store/discard 
         } 
     ] 
     } 
     const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor) 
     passEncoder.setPipeline(pipeline) 
     // 3 vertex form a triangle 
     passEncoder.draw(3) 
     passEncoder.end() 
     // webgpu run in a separate process, all the commands will be executed after submit 
     device.queue.submit([commandEncoder.finish()]) 
 }