1 unstable release
new 0.1.0-alpha.1 | Mar 21, 2025 |
---|
#1061 in Graphics APIs
Used in awsm-renderer
72KB
1.5K
SLoC
Renderer core
This is a core crate for the awsm renderer.
At this level, it's just a thin wrapper around the WebGPU API. It is intended to be used as a low-level primitive, without the headache of dealing with the raw web-sys
bindings directly.
The overall approach is to have Rust-friendly data types that can optionally be used to create all the descriptors, pipelines, etc. and turn these into the web-sys types (well-types, not just JsValue everywhere). However, methods take the raw web-sys types. This allows for a friendlier API, while still being able to use the raw web-sys types when needed, and so also doesn't restrict what's possible as more features are added.
In some cases like the command encoder, the abstracted type holds a reference to the raw web-sys type, and impls Deref to it, so you get a mixture of the original methods and nicer new ones as they are added.
Example usage:
use awsm_renderer_core::{command::{render_pass::{ColorAttachment, RenderPassDescriptor}, LoadOp, StoreOp}, error::Result, pipeline::{fragment::{ColorTargetState, FragmentState}, vertex::VertexState, PipelineDescriptor}, renderer::{AwsmRenderer, AwsmRendererBuilder}, shaders::ShaderCode};
use web_sys::HtmlCanvasElement;
pub async fn example(canvas: HtmlCanvasElement) -> Result<()> {
let renderer = AwsmRendererBuilder::new(web_sys::window().unwrap().navigator().gpu())
.init_adapter().await?
.init_device().await?
.init_context(canvas)?
.build()?;
static INIT_SHADER_CODE:&'static str = r#"
@vertex fn vs(@builtin(vertex_index) vertexIndex : u32) -> @builtin(position) vec4f {
let pos = array(
vec2f( 0.0, 0.5), // top center
vec2f(-0.5, -0.5), // bottom left
vec2f( 0.5, -0.5) // bottom right
);
return vec4f(pos[vertexIndex], 0.0, 1.0);
}
@fragment fn fs() -> @location(0) vec4f {
return vec4f(1.0, 0.0, 0.0, 1.0);
}
"#;
let shader = renderer.compile_shader(&ShaderCode::new(INIT_SHADER_CODE, None).into());
let vertex = VertexState::new(&shader, None);
let fragment = FragmentState::new(&shader, None, vec![ColorTargetState::new(renderer.current_context_format())]);
let pipeline_descriptor = PipelineDescriptor::new(vertex, None)
.with_fragment(fragment);
let pipeline = renderer.create_pipeline(&pipeline_descriptor.into()).await?;
let command_encoder = renderer.create_command_encoder(None);
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
color_attachments: vec![
ColorAttachment::new(&renderer.current_context_texture_view()?, LoadOp::Clear, StoreOp::Store)
],
..Default::default()
}.into())?;
render_pass.set_pipeline(&pipeline);
render_pass.draw(3);
render_pass.end();
renderer.submit_commands(&command_encoder.finish());
Ok(())
}
note: for things to look good, you'll want to make sure you (re)set the canvas size
One way to do this is to use the ResizeObserver from awsm-web:
let resize_observer = ResizeObserver::new(
clone!(canvas => move |entries| {
if let Some(entry) = entries.get(0) {
let width = entry.content_box_sizes[0].inline_size;
let height = entry.content_box_sizes[0].block_size;
canvas.set_width(width);
canvas.set_height(height);
}
}),
None
);
resize_observer.observe(&canvas);
Dependencies
~12–24MB
~349K SLoC