Rust WebGPU实现库deno_webgpu的使用,为Deno和Rust提供跨平台图形与计算能力
deno_webgpu
这是一个为Deno实现WebGPU API的操作库,基于2024年3月31日的WebGPU规范草案实现。该实现主要依赖于GPU后端库wgpu的功能。
特性
- 实现WebGPU API规范
- 通过设置环境变量
DENO_WEBGPU_TRACE
可以输出wgpu跟踪日志到指定目录 - 使用WebGPU一致性测试套件进行测试验证
- 在GitHub CI中使用DX WARP和Vulkan lavapipe等软件进行测试(目前仅Windows支持)
安装
在项目目录中运行以下Cargo命令:
cargo add deno_webgpu
或者在Cargo.toml中添加:
deno_webgpu = "0.179.0"
完整示例
以下是使用deno_webgpu的基本示例代码:
use deno_webgpu::*;
async fn run() {
// 1. 获取适配器
let adapter = request_adapter(&RequestAdapterOptions {
power_preference: PowerPreference::Default,
force_fallback_adapter: false,
}).await.unwrap();
// 2. 获取设备
let (device, queue) = adapter.request_device(&DeviceDescriptor {
label: None,
required_features: &[],
required_limits: &Limits::downlevel_defaults(),
}).await.unwrap();
// 3. 创建着色器模块
let shader_module = device.create_shader_module(ShaderModuleDescriptor {
label: None,
source: ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
});
// 4. 创建渲染管线
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
layout: None,
vertex: VertexState {
module: &shader_module,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(FragmentState {
module: &shader_module,
entry_point: "fs_main",
targets: &[Some(ColorTargetState {
format: TextureFormat::Bgra8Unorm,
blend: Some(BlendState::REPLACE),
write_mask: ColorWrites::ALL,
})],
}),
// ...其他管线配置
});
// 5. 创建命令编码器
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor {
label: None,
});
// 6. 开始渲染通道
{
let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
label: None,
color_attachments: &[Some(RenderPassColorAttachment {
view: &texture_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
}),
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.draw(3, 1, 0, 0);
}
// 7. 提交命令
queue.submit(Some(encoder.finish()));
// 8. 设备轮询
device.poll(wgpu::Maintain::Wait);
}
配套着色器示例(shader.wgsl)
// Vertex shader
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(i32(in_vertex_index) - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return vec4<f32>(x, y, 0.0, 1.0);
}
// Fragment shander
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
这个示例展示了使用deno_webgpu创建简单的渲染管线和绘制三角形的基本流程。实际使用时需要根据具体需求调整配置和着色器代码。
完整示例demo
下面是一个更完整的可运行示例,展示了如何初始化WebGPU并渲染一个彩色三角形:
use deno_webgpu::*;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {
// 1. 获取适配器
let adapter = request_adapter(&RequestAdapterOptions {
power_preference: PowerPreference::Default,
force_fallback_adapter: false,
})
.await
.unwrap();
// 2. 获取设备
let (device, queue) = adapter
.request_device(&DeviceDescriptor {
label: None,
required_features: &[],
required_limits: &Limits::downlevel_defaults(),
})
.await
.unwrap();
// 3. 创建交换链
let size = window.inner_size();
let surface = unsafe { create_surface(&window) }.unwrap();
let swapchain_format = TextureFormat::Bgra8Unorm;
let mut swap_chain = device.create_swap_chain(
&surface,
&SwapChainDescriptor {
usage: TextureUsage::RENDER_ATTACHMENT,
format: swapchain_format,
width: size.width,
height: size.height,
present_mode: PresentMode::Fifo,
},
);
// 4. 创建着色器模块
let shader_module = device.create_shader_module(ShaderModuleDescriptor {
label: None,
source: ShaderSource::Wgsl(
r#"
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(i32(in_vertex_index) - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return vec4<f32>(x, y, 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
"#
.into(),
),
});
// 5. 创建渲染管线
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
layout: None,
vertex: VertexState {
module: &shader_module,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(FragmentState {
module: &shader_module,
entry_point: "fs_main",
targets: &[Some(ColorTargetState {
format: swapchain_format,
blend: Some(BlendState::REPLACE),
write_mask: ColorWrites::ALL,
})],
}),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: None,
polygon_mode: PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
});
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::RedrawRequested(_) => {
// 6. 获取当前帧
let frame = swap_chain.get_current_frame().unwrap().output;
// 7. 创建命令编码器
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor {
label: None,
});
// 8. 开始渲染通道
{
let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
label: None,
color_attachments: &[Some(RenderPassColorAttachment {
view: &frame.view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.draw(3, 1, 0, 0);
}
// 9. 提交命令
queue.submit(Some(encoder.finish()));
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => {}
}
});
}
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
futures::executor::block_on(run(event_loop, window));
}
这个完整示例包含了窗口创建、交换链初始化、渲染管线设置和事件循环处理。它会在窗口中渲染一个红色的三角形,背景为深蓝色。要运行此示例,你需要在Cargo.toml中添加以下依赖:
[dependencies]
deno_webgpu = "0.179.0"
winit = "0.28.6"
futures = "0.3.28"
1 回复
Rust WebGPU实现库deno_webgpu的使用指南
概述
deno_webgpu 是一个为 Deno 运行时提供的 WebGPU API 实现库,它基于 Rust 编写,为 Deno 和 Rust 开发者提供了跨平台的图形渲染和通用计算能力。WebGPU 是下一代图形 API,旨在提供更高效的底层硬件访问,同时保持跨平台兼容性。
主要特性
- 提供 WebGPU API 的完整实现
- 基于 Rust 的高性能实现
- 跨平台支持(Windows、macOS、Linux)
- 同时支持图形渲染和通用计算
- 与 Deno 生态无缝集成
安装与设置
在 Deno 中使用
- 确保已安装 Deno(1.30.0 或更高版本)
- 在代码中直接导入 WebGPU API:
// 导入WebGPU API
const { GPU } = await import("https://deno.land/x/webgpu@v0.1.0/mod.ts");
在 Rust 中使用
- 添加依赖到你的
Cargo.toml
:
[dependencies]
deno_webgpu = "0.1"
- 在 Rust 代码中使用:
use deno_webgpu::gpu::GPU;
完整示例代码
1. 图形渲染完整示例
// 导入WebGPU API
const { GPU } = await import("https://deno.land/x/webgpu@v0.1.0/mod.ts");
// 初始化WebGPU
const adapter = await GPU.requestAdapter();
const device = await adapter.requestDevice();
// 创建Canvas上下文
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const context = canvas.getContext("webgpu");
// 配置Canvas
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device: device,
format: canvasFormat,
});
// 创建渲染管线
const pipeline = device.createRenderPipeline({
vertex: {
module: device.createShaderModule({
code: `
@vertex
fn main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(
vec2(0.0, 0.5),
vec2(-0.5, -0.5),
vec2(0.5, -0.5)
);
return vec4(pos[vertex_index], 0.0, 1.0);
}
`,
}),
entryPoint: "main",
},
fragment: {
module: device.createShaderModule({
code: `
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4(1.0, 0.0, 0.0, 1.0);
}
`,
}),
entryPoint: "main",
targets: [{ format: canvasFormat }],
},
primitive: { topology: "triangle-list" },
});
// 渲染函数
function render() {
// 创建命令编码器
const commandEncoder = device.createCommandEncoder();
// 开始渲染通道
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
loadOp: "clear",
storeOp: "store",
}],
});
// 设置渲染管线和绘制
renderPass.setPipeline(pipeline);
renderPass.draw(3, 1, 0, 0);
renderPass.end();
// 提交命令
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(render);
}
// 开始渲染循环
render();
2. 计算着色器完整示例
// 导入WebGPU API
const { GPU } = await import("https://deno.land/x/webgpu@v0.1.0/mod.ts");
// 初始化WebGPU
const adapter = await GPU.requestAdapter();
const device = await adapter.requestDevice();
// 创建计算管线
const computePipeline = device.createComputePipeline({
compute: {
module: device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read_write> output: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
output[id.x] = f32(id.x) * 0.5;
}
`,
}),
entryPoint: "main",
},
});
// 创建输出缓冲区
const outputBuffer = device.createBuffer({
size: 256,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
// 创建用于读取结果的缓冲区
const resultBuffer = device.createBuffer({
size: 256,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
// 创建命令编码器并执行计算
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(computePipeline);
passEncoder.setBindGroup(0, device.createBindGroup({
layout: computePipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer: outputBuffer } }],
}));
passEncoder.dispatchWorkgroups(4);
passEncoder.end();
// 复制结果到可读缓冲区
commandEncoder.copyBufferToBuffer(
outputBuffer,
0,
resultBuffer,
0,
256
);
// 提交命令
device.queue.submit([commandEncoder.finish()]);
// 读取计算结果
await resultBuffer.mapAsync(GPUMapMode.READ);
const result = new Float32Array(resultBuffer.getMappedRange());
console.log("计算结果:", result);
resultBuffer.unmap();
注意事项
- 目前 deno_webgpu 仍在活跃开发中,API 可能会有变化
- 需要现代 GPU 和最新的驱动程序支持
- 在 Deno 中运行时需要
--unstable
标志 - 某些高级功能可能尚未完全实现