diff --git a/src/app/dem_renderer.rs b/src/app/dem_renderer.rs index 47e46aa..4a2a487 100644 --- a/src/app/dem_renderer.rs +++ b/src/app/dem_renderer.rs @@ -1,7 +1,7 @@ use super::raster::Dem; use { bytemuck::{Pod, Zeroable}, - std::{borrow::Cow, rc::Rc}, + std::{borrow::Cow, num::NonZero, rc::Rc}, wgpu::util::DeviceExt, }; @@ -12,7 +12,8 @@ pub struct DemRenderer { vertex_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer, index_count: usize, - uniforms: Uniforms, + camera: Camera, + uniforms: UniformBufferManager, animation_start: std::time::Instant, } @@ -30,32 +31,27 @@ impl Vertex { } } -struct Uniforms { +struct Camera { projection_matrix: glam::Mat4, view_matrix: glam::Mat4, - buffer: wgpu::Buffer, } -impl Uniforms { - fn new(device: &wgpu::Device, aspect_ratio: f32) -> Self { - let projection_matrix = - glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, aspect_ratio, 1.0, 10000.0); +impl Camera { + fn new(device: &wgpu::Device, viewport_aspect_ratio: f32) -> Self { + let projection_matrix = glam::Mat4::perspective_rh( + std::f32::consts::FRAC_PI_4, + viewport_aspect_ratio, + 1.0, + 10000.0, + ); let view_matrix = glam::Mat4::look_at_rh(glam::Vec3::ZERO, glam::Vec3::ZERO, glam::Vec3::Z); - let camera_matrix = projection_matrix * view_matrix; - let camera_matrix_ref: &[f32; 16] = camera_matrix.as_ref(); - let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Uniform Buffer"), - contents: bytemuck::cast_slice(camera_matrix_ref), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); Self { projection_matrix, view_matrix, - buffer, } } - fn set_camera_position(&mut self, camera_position: glam::Vec3, camera_look_at: glam::Vec3) { + fn set_position(&mut self, camera_position: glam::Vec3, camera_look_at: glam::Vec3) { self.view_matrix = glam::Mat4::look_at_rh(camera_position, camera_look_at, glam::Vec3::Z); } @@ -64,14 +60,63 @@ impl Uniforms { glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, ratio, 1.0, 10.0); } - fn update_buffer(&self, queue: &wgpu::Queue) { - let camera_matrix = self.projection_matrix * self.view_matrix; - let camera_matrix_ref: &[f32; 16] = camera_matrix.as_ref(); - queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(camera_matrix_ref)) + fn get_matrix(&self) -> glam::Mat4 { + self.projection_matrix * self.view_matrix + } +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct UniformBufferContents { + camera_matrix: [f32; 16], + dem_texture_size: [u32; 2], +} +// This struct is 72 bytes but WGSL expects the buffer size to be be a multiple +// of the alignment of the member with the largest alignment. The mat4x4 +// type has a 16-byte alignment, so we round up to 16 * 5 = 80. +const UNIFORM_BUFFER_SIZE: u64 = 80; + +struct UniformBufferManager { + cpu_buffer: UniformBufferContents, + gpu_buffer: wgpu::Buffer, +} + +impl UniformBufferManager { + fn new(device: &wgpu::Device) -> Self { + let cpu_buffer = UniformBufferContents::zeroed(); + let gpu_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: &[0u8; UNIFORM_BUFFER_SIZE as usize], + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + Self { + cpu_buffer, + gpu_buffer, + } } - fn binding(&self) -> wgpu::BindingResource<'_> { - self.buffer.as_entire_binding() + fn set_camera_matrix(&mut self, camera_matrix: glam::Mat4) { + self.cpu_buffer + .camera_matrix + .clone_from_slice(&camera_matrix.to_cols_array()) + } + + fn set_dem_texture_size(&mut self, dem_texture_size: glam::UVec2) { + self.cpu_buffer + .dem_texture_size + .clone_from_slice(&dem_texture_size.to_array()); + } + + fn get_binding(&self) -> wgpu::BindingResource<'_> { + wgpu::BindingResource::Buffer(wgpu::BufferBinding { + buffer: &self.gpu_buffer, + offset: 0, + size: NonZero::new(UNIFORM_BUFFER_SIZE), + }) + } + + fn update_buffer(&self, queue: &wgpu::Queue) { + queue.write_buffer(&self.gpu_buffer, 0, bytemuck::bytes_of(&self.cpu_buffer)); } } @@ -80,6 +125,7 @@ impl DemRenderer { source: Rc, device: &wgpu::Device, surface_config: &wgpu::SurfaceConfiguration, + queue: &wgpu::Queue, ) -> Self { let (vertex_data, index_data) = create_vertices(&source); @@ -97,25 +143,67 @@ impl DemRenderer { let index_count = index_data.len(); - let uniforms = Uniforms::new( + let (dem_texture_view, dem_texture_size) = { + let texture_size = wgpu::Extent3d { + width: source.num_cells_x, + height: source.num_cells_y, + depth_or_array_layers: 1, + }; + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Dem Texture"), + size: texture_size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R16Uint, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + queue.write_texture( + wgpu::ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + bytemuck::cast_slice(&source.grid[..]), + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(std::mem::size_of::() as u32 * source.num_cells_x), + rows_per_image: Some(source.num_cells_y), + }, + texture_size, + ); + + ( + texture.create_view(&wgpu::TextureViewDescriptor::default()), + glam::UVec2::new(source.num_cells_x, source.num_cells_y), + ) + }; + + let camera = Camera::new( device, surface_config.width as f32 / surface_config.height as f32, ); + let mut uniforms = UniformBufferManager::new(device); + uniforms.set_dem_texture_size(dem_texture_size); + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, - visibility: wgpu::ShaderStages::VERTEX, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, - min_binding_size: wgpu::BufferSize::new(64), + min_binding_size: wgpu::BufferSize::new(UNIFORM_BUFFER_SIZE), }, count: None, }, - /*wgpu::BindGroupLayoutEntry { + wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { @@ -124,16 +212,22 @@ impl DemRenderer { view_dimension: wgpu::TextureViewDimension::D2, }, count: None, - },*/ + }, ], }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: uniforms.binding(), - }], + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniforms.get_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&dem_texture_view), + }, + ], label: Some("DemRendererBindGroup"), }); @@ -190,6 +284,7 @@ impl DemRenderer { index_buffer, index_count, animation_start: std::time::Instant::now(), + camera, uniforms, } } @@ -198,13 +293,14 @@ impl DemRenderer { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("DemRendererCommandEncoder"), }); - self.uniforms.set_camera_position( + self.camera.set_position( get_animated_camera_position( self.animation_start.elapsed(), self.get_max_dem_dimension(), ), self.get_dem_centre(), ); + self.uniforms.set_camera_matrix(self.camera.get_matrix()); self.uniforms.update_buffer(queue); { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { diff --git a/src/app/dem_renderer.wgsl b/src/app/dem_renderer.wgsl index 1c342e2..3849d8e 100644 --- a/src/app/dem_renderer.wgsl +++ b/src/app/dem_renderer.wgsl @@ -1,21 +1,34 @@ struct VertexOutput { + @location(0) test: vec2, @builtin(position) position: vec4, }; -@group(0) -@binding(0) -var transform: mat4x4; +struct Uniforms { + transform: mat4x4, + dem_texture_size: vec2 +} + +@group(0) @binding(0) +var uniforms: Uniforms; + +@group(0) @binding(1) +var dem_texture: texture_2d; + @vertex fn vs_main( @location(0) position: vec4, ) -> VertexOutput { var result: VertexOutput; - result.position = transform * position; + result.position = uniforms.transform * position; + result.test = position.xy; return result; } @fragment fn fs_solid(vertex: VertexOutput) -> @location(0) vec4 { - return vec4(1.0, 1.0, 1.0, 1.0); + let texture_index = vec2(fract(abs(vertex.test)/500.0)*vec2(uniforms.dem_texture_size)); + let v_i = textureLoad(dem_texture, texture_index, 0).r; + let v = f32(v_i) / 65535.0; + return vec4(v, v, 1.0, 1.0); } \ No newline at end of file diff --git a/src/app/mod.rs b/src/app/mod.rs index 3804172..baccbbc 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -153,6 +153,7 @@ impl MvuApp for App { dem.clone(), &context.device, &context.config, + &context.queue, )) } }