一、什么是WebGPU
WebGPU是一套基于浏览器的图形API,浏览器封装了现代图形API(Dx12、Vulkan、Metal),提供给Web 3D程序员,为 Web释放了更多的GPU 硬件的功能 (知乎上Orillusion的回答)
二、相比于WebGL,WebGPU会带来什么
- PromiseAPI / async + await 的异步语法加入:对网络数据加载、解码等同步耗时操作有了更好的支持,一定程度上避免了回调地狱问题
- 面向对象的 API 设计:相对于 WebGL API 的过程式调用,选项式图形 API的使用更科学
- 不再使用全局状态机制:使用指令缓冲节约了 CPU 到 GPU 之间的信息传递成本,要知道 WebGL 切换某个状态(例如纹理、着色器程序),就是在切换全局状态对象,这个在微观上是比较耗时的,而 60 帧速率的每一帧只有 16.67 毫秒左右,能在微观节约每一点每一滴的时间,累计起来就不少了
- 原生支持计算管线、计算着色器:WebGL 磨磨蹭蹭实现的 GPU 并行计算功能直接提供出来,并与渲染管线地位对等。 (知乎上为名的回答)
三、浅入WebGPU
- WebGPU工作流
- 相关执行代码
初始化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()])
}