理解WebGPU 中的 GPUDevice :与 GPU 交互的核心接口
在 WebGPU 开发中, GPUDevice 是一个至关重要的对象,它是与 GPU 进行交互的核心接口。通过 GPUDevice ,开发者可以创建和管理 GPU 资源(如缓冲区、纹理、管线等),并提交命令缓冲区以执行渲染和计算任务。本文将详细介绍 GPUDevice 的核心属性和方法,并通过实际代码示例展示如何使用它来实现高性能的图形和计算任务。
什么是 GPUDevice ?
GPUDevice 是 WebGPU API 中的一个接口,表示逻辑 GPU 设备。它是从 GPUAdapter 请求而来的,用于创建和管理 GPU 资源,以及提交命令缓冲区以执行 GPU 操作。 GPUDevice 是开发者与 GPU 交互的主要接口,几乎所有与 GPU 相关的操作都需要通过它来完成。
GPUDevice 的核心属性
1. features
- 类型: GPUSupportedFeatures
- 描述:返回一个集合,表示该设备支持的特性(如纹理格式、管线特性等)。
- 示例:
const device = await adapter.requestDevice(); console.log("Supported Features:", device.features);
2. limits
- 类型: GPUSupportedLimits
- 描述:返回一个对象,表示该设备支持的资源限制(如最大纹理大小、最大缓冲区大小等)。
- 示例:
const device = await adapter.requestDevice(); console.log("Supported Limits:", device.limits);
3. queue
- 类型: GPUQueue
- 描述:返回一个 GPUQueue 对象,用于提交命令缓冲区以执行 GPU 操作。
- 示例:
const device = await adapter.requestDevice(); const queue = device.queue;
GPUDevice 的核心方法
1. createBuffer()
- 返回值: GPUBuffer
- 描述:创建一个 GPU 缓冲区,用于存储顶点数据、索引数据或通用计算数据。
- 参数:descriptor :一个对象,描述缓冲区的大小、用途等。
- 示例:
const buffer = device.createBuffer({ size: 1024, // 缓冲区大小(字节) usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, // 缓冲区用途 });
2. createTexture()
- 返回值: GPUTexture
- 描述:创建一个 GPU 纹理,用于存储图像数据或作为渲染目标。
- 参数:descriptor :一个对象,描述纹理的格式、大小和用途。
- 示例:
const texture = device.createTexture({ size: { width: 512, height: 512, depthOrArrayLayers: 1 }, format: 'rgba8unorm', usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT, });
3. createShaderModule()
- 返回值: GPUShaderModule
- 描述:创建一个着色器模块,用于定义顶点着色器和片元着色器的代码。
- 参数:descriptor :一个对象,包含着色器代码(WGSL)。
- 示例:
const shaderModule = device.createShaderModule({ code: ` @vertex fn vs_main() -> @builtin(position) vec4<f32> { return vec4<f32>(0.0, 0.0, 0.0, 1.0); } @fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); } ` });
4. createCommandEncoder()
- 返回值: GPUCommandEncoder
- 描述:创建一个命令编码器,用于记录 GPU 操作命令。
- 示例:
const commandEncoder = device.createCommandEncoder();
5. createRenderPipeline()
- 返回值: GPURenderPipeline
- 描述:创建一个渲染管线,用于定义渲染流程。
- 参数:descriptor :一个对象,描述管线的顶点着色器、片元着色器、目标格式等。
- 示例:
const pipeline = device.createRenderPipeline({ vertex: { module: shaderModule, entryPoint: 'vs_main', }, fragment: { module: shaderModule, entryPoint: 'fs_main', targets: [{ format: 'bgra8unorm' }], }, });
6. createComputePipeline()
- 返回值: GPUComputePipeline
- 描述:创建一个计算管线,用于定义通用计算任务。
- 参数:descriptor :一个对象,描述管线的计算着色器。
- 示例:
const computePipeline = device.createComputePipeline({ compute: { module: shaderModule, entryPoint: 'cs_main', }, });
示例代码:初始化 WebGPU 并创建资源
async function initWebGPU() {
// 检查浏览器是否支持 WebGPU
if (!navigator.gpu) {
throw new Error("WebGPU is not supported on this browser.");
}
// 请求 GPU 适配器
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
throw new Error("Couldn't request WebGPU adapter.");
}
// 请求 GPU 设备
const device = await adapter.requestDevice();
// 获取画布上下文
const canvas = document.querySelector("canvas");
const context = canvas.getContext("webgpu");
// 获取首选画布格式
const format = navigator.gpu.getPreferredCanvasFormat();
// 配置画布上下文
context.configure({
device,
format,
});
// 创建着色器模块
const shaderModule = device.createShaderModule({
code: `
@vertex fn vs_main() -> @builtin(position) vec4<f32> {
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
}
@fragment fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
`,
});
// 创建渲染管线
const pipeline = device.createRenderPipeline({
vertex: {
module: shaderModule,
entryPoint: 'vs_main',
},
fragment: {
module: shaderModule,
entryPoint: 'fs_main',
targets: [{ format }],
},
});
// 创建命令编码器
const commandEncoder = device.createCommandEncoder();
// 获取当前纹理视图
const textureView = context.getCurrentTexture().createView();
// 创建渲染通道描述符
const renderPassDescriptor = {
colorAttachments: [{
view: textureView,
loadOp: 'clear',
clearValue: { r: 0, g: 0, b: 0, a: 1 },
storeOp: 'store',
}],
};
// 开始渲染通道
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.draw(3, 1, 0, 0);
passEncoder.end();
// 提交命令缓冲区
device.queue.submit([commandEncoder.finish()]);
}
initWebGPU().catch((error) => {
console.error("Failed to initialize WebGPU:", error);
});