Use uniform buffer instead of push constants.

This will allow for compatibility with WebGPU.
wasm
James T. Martin 2022-07-08 14:16:34 -07:00
parent bc1d6a27f7
commit 7fb393ddda
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
2 changed files with 74 additions and 31 deletions

View File

@ -12,7 +12,7 @@ struct Vertex {
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct PushConstants {
struct Uniforms {
dimensions: [f32; 2],
field_of_view: f32,
}
@ -39,6 +39,9 @@ pub struct Graphics {
surface_stale: bool,
desired_size: winit::dpi::PhysicalSize<u32>,
dither_bind_group: BindGroup,
uniform_bind_group: BindGroup,
uniform_copy_buffer: Buffer,
uniform_buffer: Buffer,
}
impl Graphics {
@ -58,11 +61,8 @@ impl Graphics {
let format = surface.get_supported_formats(&adapter)[0];
let (device, queue) = adapter.request_device(&DeviceDescriptor {
label: None,
features: Features::PUSH_CONSTANTS,
limits: Limits {
max_push_constant_size: 128,
.. Limits::default()
}
features: Features::default(),
limits: Limits::downlevel_defaults()
}, None).await.expect("Failed to get wgpu device.");
let shader = device.create_shader_module(include_wgsl!("shader.wgsl"));
let dither_texture = device.create_texture_with_data(
@ -78,7 +78,7 @@ impl Graphics {
dimension: TextureDimension::D2,
format: TextureFormat::Rgba32Float,
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
label: Some("dither_texture")
label: Some("dither texture")
},
bytemuck::cast_slice(&*dither::bayer_texture())
);
@ -96,7 +96,7 @@ impl Graphics {
count: None,
}
],
label: Some("dither_bind_group_layout")
label: Some("dither bind group layout")
});
let dither_bind_group = device.create_bind_group(&BindGroupDescriptor {
layout: &dither_bind_group_layout,
@ -106,19 +106,51 @@ impl Graphics {
resource: BindingResource::TextureView(&dither_texture_view)
}
],
label: Some("dither_bind_group")
});
label: Some("dither bind group")
});
let uniform_buffer = device.create_buffer(&BufferDescriptor {
label: Some("uniform buffer"),
size: std::mem::size_of::<Uniforms>().next_power_of_two() as u64,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let uniform_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("uniform bind group layout"),
});
let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor {
layout: &uniform_bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}
],
label: Some("uniform bind group"),
});
let uniform_copy_buffer = device.create_buffer(&BufferDescriptor {
label: Some("uniform copy buffer"),
size: std::mem::size_of::<Uniforms>() as u64,
usage: BufferUsages::MAP_WRITE | BufferUsages::COPY_SRC,
mapped_at_creation: true,
});
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
layout: Some(&device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&dither_bind_group_layout],
push_constant_ranges: &[
PushConstantRange {
stages: ShaderStages::FRAGMENT,
range: 0..12,
}
]
bind_group_layouts: &[&dither_bind_group_layout, &uniform_bind_group_layout],
push_constant_ranges: &[]
})),
vertex: VertexState {
module: &shader,
@ -178,7 +210,10 @@ impl Graphics {
vertex_buffer,
surface_stale: true,
desired_size,
dither_bind_group
dither_bind_group,
uniform_bind_group,
uniform_copy_buffer,
uniform_buffer
}
}
@ -191,6 +226,16 @@ impl Graphics {
height: size.height,
present_mode: PresentMode::Mailbox
});
self.uniform_copy_buffer.slice(..).get_mapped_range_mut().copy_from_slice(bytemuck::cast_slice(&[Uniforms {
dimensions: [self.desired_size.width as f32, self.desired_size.height as f32],
field_of_view: std::f32::consts::PI,
}]));
self.uniform_copy_buffer.unmap();
// TODO: share encoder/submission
let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor::default());
encoder.copy_buffer_to_buffer(&self.uniform_copy_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::<Uniforms>() as u64);
self.queue.submit(std::iter::once(encoder.finish()));
self.uniform_copy_buffer.slice(..).map_async(MapMode::Write, |err| err.unwrap());
}
fn reconfigure_surface_if_stale(&mut self) {
@ -211,8 +256,7 @@ impl Graphics {
self.reconfigure_surface_if_stale();
let frame = self.surface.get_current_texture().expect("Failed to get surface texture");
let view = frame.texture.create_view(&TextureViewDescriptor::default());
let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor::default());
{
let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor::default()); {
let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
label: None,
color_attachments: &[
@ -233,11 +277,8 @@ impl Graphics {
depth_stencil_attachment: None
});
render_pass.set_pipeline(&self.pipeline);
render_pass.set_push_constants(ShaderStages::FRAGMENT, 0, bytemuck::bytes_of(&PushConstants {
dimensions: [self.desired_size.width as f32, self.desired_size.height as f32],
field_of_view: std::f32::consts::PI,
}));
render_pass.set_bind_group(0, &self.dither_bind_group, &[]);
render_pass.set_bind_group(1, &self.uniform_bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..VERTICES.len() as u32, 0..1);
}

View File

@ -15,12 +15,14 @@ fn vs_main(
return out;
}
struct PushConstants {
struct Uniforms {
dimensions: vec2<f32>,
field_of_view: f32,
}
var<push_constant> pc: PushConstants;
@group(1)
@binding(0)
var<uniform> uniforms: Uniforms;
let PI: f32 = 3.14159265358979323846264338327950288; // 3.14159274
@ -44,16 +46,16 @@ struct Ray {
/// window's dimensions and aspect ratio. Some of the image
/// will be cropped if the window's aspect ratio is not square.
fn pixel_to_square(pixel: vec2<f32>) -> vec2<f32> {
let square = ((pixel / pc.dimensions) - 0.5) * 2.0;
let square = ((pixel / uniforms.dimensions) - 0.5) * 2.0;
// Scale the window's smaller aspect ratio to make the coordinates square.
// For example, a 16:9 window will have an x coordinate from -1 to 1 and
// a y coordinate from -9/16ths to 9/16ths. The rest of the image lying outside
// of that range will be cropped out.
if (pc.dimensions.x > pc.dimensions.y) {
return vec2<f32>(square.x, square.y * pc.dimensions.y / pc.dimensions.x);
if (uniforms.dimensions.x > uniforms.dimensions.y) {
return vec2<f32>(square.x, square.y * uniforms.dimensions.y / uniforms.dimensions.x);
} else {
return vec2<f32>(square.x * pc.dimensions.x / pc.dimensions.y, square.y);
return vec2<f32>(square.x * uniforms.dimensions.x / uniforms.dimensions.y, square.y);
}
}
@ -129,7 +131,7 @@ fn camera_project(square: vec2<f32>) -> Ray {
// Our coordinates already range from -1 to 1, corresponding with the
// edges of the window, but we want the edges of the window to correspond
// with the angle of the FOV instead.
let circle = square * pc.field_of_view / PI;
let circle = square * uniforms.field_of_view / PI;
let sphere = project(circle);
return Ray(vec3<f32>(0.), sphere);
}