Skip to content

Commit

Permalink
Simplify cpu-gpu buffers interface via bytemuck
Browse files Browse the repository at this point in the history
  • Loading branch information
Azkellas committed Sep 29, 2023
1 parent db261b5 commit 208b744
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 76 deletions.
3 changes: 2 additions & 1 deletion lib/src/camera_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use crate::mouse_input::MouseState;
// This version removes the use of quaternion to avoid adding a dependency.
// To avoid having to do linear algebra ourselves, most computations are done in the shader.
// This is sub-optimal. Improving this is left as an exercise to the reader.
#[derive(Debug)]
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct CameraLookAt {
// Object the camera is looking at.
pub center: [f32; 3],
Expand Down
36 changes: 12 additions & 24 deletions lib/src/demo_boids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// See the README for more details.

use nanorand::{Rng, WyRand};
use std::mem;
use wgpu::util::DeviceExt;

use crate::frame_rate::FrameRate;
Expand All @@ -28,6 +27,9 @@ struct RenderPass {
particle_buffers: Vec<wgpu::Buffer>,
vertices_buffer: wgpu::Buffer,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct DemoBoidsSettings {
delta_t: f32, // cohesion
rule1_distance: f32, // separation
Expand All @@ -43,7 +45,7 @@ impl DemoBoidsSettings {
pub fn new() -> Self {
Self {
delta_t: 0.04f32,
rule1_distance: 0.1,
rule1_distance: 0.08,
rule2_distance: 0.025,
rule3_distance: 0.025,
rule1_scale: 0.02,
Expand All @@ -53,21 +55,8 @@ impl DemoBoidsSettings {
}
}

pub fn get_as_array(&self) -> [f32; 8] {
[
self.delta_t,
self.rule1_distance,
self.rule2_distance,
self.rule3_distance,
self.rule1_scale,
self.rule2_scale,
self.rule3_scale,
self.speed,
]
}

pub fn get_size() -> usize {
mem::size_of::<DemoBoidsSettings>()
pub fn get_size() -> u64 {
std::mem::size_of::<Self>() as _
}
}

Expand Down Expand Up @@ -112,7 +101,7 @@ impl Program for DemoBoidsProgram {
settings,
compute_pass,
render_pass,
frame_rate: FrameRate::new(500),
frame_rate: FrameRate::new(100),
last_update: instant::Instant::now(),
})
}
Expand Down Expand Up @@ -153,7 +142,7 @@ impl Program for DemoBoidsProgram {
queue.write_buffer(
&self.compute_pass.parameters,
0,
bytemuck::cast_slice(&self.settings.get_as_array()),
bytemuck::cast_slice(&[self.settings]),
);
}

Expand Down Expand Up @@ -326,10 +315,11 @@ impl DemoBoidsProgram {
device: &wgpu::Device,
adapter: &wgpu::Adapter,
) -> Result<(ComputePass, RenderPass), ProgramError> {
let sim_param_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let sim_param_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Simulation Parameter Buffer"),
contents: bytemuck::cast_slice(&DemoBoidsSettings::new().get_as_array()), // default content
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: DemoBoidsSettings::get_size(),
mapped_at_creation: false,
});

let vertex_buffer_data = [-0.01f32, -0.02, 0.01, -0.02, 0.00, 0.02];
Expand Down Expand Up @@ -363,9 +353,7 @@ impl DemoBoidsProgram {
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(
DemoBoidsSettings::get_size() as _
),
min_binding_size: wgpu::BufferSize::new(DemoBoidsSettings::get_size()),
},
count: None,
},
Expand Down
45 changes: 25 additions & 20 deletions lib/src/demo_polygon.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use wgpu::util::DeviceExt;

use crate::frame_rate::FrameRate;
use crate::program::{Program, ProgramError};
use crate::shader_builder::ShaderBuilder;
Expand All @@ -18,8 +16,11 @@ pub struct Pass {
/// Settings for the `DemoProgram`
/// `polygon_edge_count` is not exposed in ui on purpose for demo purposes
/// change it in the code with hot-reload enable to see it working.
#[derive(Clone, Copy, Debug)]
#[repr(C)]
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct DemoPolygonSettings {
// elapsed take the speed into consideration
elapsed: f32,
/// polygon radius in window, between 0 and 1
polygon_size: f32, // exposed in ui
/// regular polygon edge count, expected to be 3 or more
Expand All @@ -28,6 +29,20 @@ pub struct DemoPolygonSettings {
speed: f32, // exposed in ui
}

impl DemoPolygonSettings {
pub fn new() -> Self {
Self {
elapsed: 0.0,
polygon_size: 0.5,
polygon_edge_count: 3,
speed: 1.0,
}
}

pub fn get_size() -> u64 {
std::mem::size_of::<Self>() as _
}
}
/// Demo Program rotation a regular polygon showcasing the three type of live updates
/// shader: `draw.wgsl`
/// rust: `polygon_edge_count` in `DemoProgram::update`
Expand All @@ -38,7 +53,6 @@ pub struct DemoPolygonProgram {
_start_time: instant::Instant, // std::time::Instant is not compatible with wasm
last_update: instant::Instant,
settings: DemoPolygonSettings,
elapsed: f32, // elapsed take the speed into consideration
frame_rate: FrameRate,
}

Expand All @@ -56,12 +70,7 @@ impl Program for DemoPolygonProgram {
render_pass,
_start_time: instant::Instant::now(),
last_update: instant::Instant::now(),
settings: DemoPolygonSettings {
polygon_size: 0.5,
polygon_edge_count: 3,
speed: 1.0,
},
elapsed: 0.0,
settings: DemoPolygonSettings::new(),
frame_rate: FrameRate::default(),
})
}
Expand Down Expand Up @@ -99,18 +108,13 @@ impl Program for DemoPolygonProgram {

// update elapsed time, taking speed into consideration.
let last_frame_duration = self.last_update.elapsed().as_secs_f32();
self.elapsed += last_frame_duration * self.settings.speed;
self.settings.elapsed += last_frame_duration * self.settings.speed;
self.frame_rate.update(last_frame_duration);
self.last_update = instant::Instant::now();
queue.write_buffer(
&self.render_pass.uniform_buf,
0,
bytemuck::cast_slice(&[
self.elapsed,
self.settings.polygon_size,
self.settings.polygon_edge_count as f32,
0.0,
]),
bytemuck::cast_slice(&[self.settings]),
);
}

Expand Down Expand Up @@ -214,10 +218,11 @@ impl DemoPolygonProgram {
adapter: &wgpu::Adapter,
) -> Result<Pass, ProgramError> {
// create uniform buffer.
let uniforms = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[0.0, 0.0, 0.0, 0.0]),
let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Uniforms Buffer"),
size: DemoPolygonSettings::get_size(),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});

let uniforms_bind_group_layout =
Expand Down
64 changes: 40 additions & 24 deletions lib/src/demo_raymarching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ impl Vertex {
}
}

#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct DemoRaymarchingSettings {
pub camera: CameraLookAt,
pub size: [f32; 2],
pub elapsed: f32, // elapsed take the speed into consideration
_padding: [f32; 3], // padding for alignment
}

/// Demo raymarching program.
/// Everything is done in the shader.
/// Provides both 2d and 3d raymarching.
Expand All @@ -51,10 +60,28 @@ pub struct DemoRaymarchingProgram {
render_pass: Pass,
_start_time: instant::Instant, // std::time::Instant is not compatible with wasm
last_update: instant::Instant,
elapsed: f32, // elapsed take the speed into consideration
frame_rate: FrameRate,
size: [f32; 2],
camera: CameraLookAt,
settings: DemoRaymarchingSettings,
}

impl DemoRaymarchingSettings {
pub fn new() -> Self {
Self {
camera: CameraLookAt::default(),
size: [0.0, 0.0],
elapsed: 0.0,
_padding: [0.0; 3],
}
}

pub fn get_size() -> u64 {
dbg!(
"DemoRaymarchingSettings::get_size",
std::mem::size_of::<Self>(),
(bytemuck::cast_slice(&[Self::new()]) as &[u8]).len()
);
std::mem::size_of::<Self>() as _
}
}

impl Program for DemoRaymarchingProgram {
Expand All @@ -71,10 +98,8 @@ impl Program for DemoRaymarchingProgram {
render_pass,
_start_time: instant::Instant::now(),
last_update: instant::Instant::now(),
elapsed: 0.0,
frame_rate: FrameRate::new(200),
size: [0.0, 0.0],
camera: CameraLookAt::default(),
frame_rate: FrameRate::new(100),
settings: DemoRaymarchingSettings::new(),
})
}

Expand All @@ -101,8 +126,8 @@ impl Program for DemoRaymarchingProgram {
_device: &wgpu::Device,
_queue: &wgpu::Queue,
) {
self.size[0] = surface_configuration.width as f32;
self.size[1] = surface_configuration.height as f32;
self.settings.size[0] = surface_configuration.width as f32;
self.settings.size[1] = surface_configuration.height as f32;
}

/// Update program before rendering.
Expand All @@ -112,23 +137,13 @@ impl Program for DemoRaymarchingProgram {

// update elapsed time, taking speed into consideration.
let last_frame_duration = self.last_update.elapsed().as_secs_f32();
self.elapsed += last_frame_duration;
self.settings.elapsed += last_frame_duration;
self.frame_rate.update(last_frame_duration);
self.last_update = instant::Instant::now();
queue.write_buffer(
&self.render_pass.uniform_buf,
0,
bytemuck::cast_slice(&[
self.elapsed,
self.size[0],
self.size[1],
self.camera.angle,
self.camera.center[0],
self.camera.center[1],
self.camera.center[2],
self.camera.height,
self.camera.distance,
]),
bytemuck::cast_slice(&[self.settings]),
);
}

Expand Down Expand Up @@ -173,7 +188,7 @@ impl Program for DemoRaymarchingProgram {
}

fn get_camera(&mut self) -> Option<&mut crate::camera_control::CameraLookAt> {
Some(&mut self.camera)
Some(&mut self.settings.camera)
}
}

Expand Down Expand Up @@ -229,10 +244,11 @@ impl DemoRaymarchingProgram {
adapter: &wgpu::Adapter,
) -> Result<Pass, ProgramError> {
// create uniform buffer.
let uniforms = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: DemoRaymarchingSettings::get_size(),
mapped_at_creation: false,
});

let uniforms_bind_group_layout =
Expand Down
1 change: 0 additions & 1 deletion lib/src/mouse_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ impl MouseState {
self.position = [position.x as f32, position.y as f32];
}
WindowEvent::MouseWheel { delta, .. } => {
log::info!("delta: {:?}", delta);
match delta {
// native mode: line delta should be 1 or -1
MouseScrollDelta::LineDelta(_, y) => self.scroll_delta = y,
Expand Down
4 changes: 2 additions & 2 deletions shaders/demo_polygon/draw.wgsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
struct Uniforms {
elapsed: f32,
size: f32,
edge_count: f32,
edge_count: u32,
_padding: f32, // padding to 16 bytes, required for WebGL.
};

Expand Down Expand Up @@ -35,7 +35,7 @@ fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput {
// 012 123 234 345 ... (triangle_id + vertex_offset)
let vertex_id = triangle_id + vertex_offset;
// finally we will just have to consider the first element of the triangle as the center later.
let vertex_angle = 2.0 * pi * f32(vertex_id) / uniforms.edge_count;
let vertex_angle = 2.0 * pi * f32(vertex_id) / f32(uniforms.edge_count);

if vertex_offset > 0u {
// polygon edge.
Expand Down
8 changes: 4 additions & 4 deletions shaders/demo_raymarching/common.wgsl
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Vertex shader
struct Uniforms {
elapsed: f32,
width: f32,
height: f32,
camera_angle: f32,
camera_center: vec3<f32>,
camera_angle: f32,
camera_height: f32,
camera_distance: f32,
width: f32,
height: f32,
elapsed: f32,
};

struct VertexInput {
Expand Down

0 comments on commit 208b744

Please sign in to comment.