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 中使用

  1. 确保已安装 Deno(1.30.0 或更高版本)
  2. 在代码中直接导入 WebGPU API:
// 导入WebGPU API
const { GPU } = await import("https://deno.land/x/webgpu@v0.1.0/mod.ts");

在 Rust 中使用

  1. 添加依赖到你的 Cargo.toml
[dependencies]
deno_webgpu = "0.1"
  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();

注意事项

  1. 目前 deno_webgpu 仍在活跃开发中,API 可能会有变化
  2. 需要现代 GPU 和最新的驱动程序支持
  3. 在 Deno 中运行时需要 --unstable 标志
  4. 某些高级功能可能尚未完全实现
回到顶部