diff --git a/src/app.rs b/src/app.rs index 9398cef..d4534d2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,13 +5,12 @@ use winit::{ event_loop::{ControlFlow, EventLoop}, }; -use crate::renderer; +use crate::{camera, render, renderer}; pub(crate) struct App { window: winit::window::Window, event_loop: EventLoop<()>, - render_ctx: renderer::RenderContext, - renderer: renderer::Renderer, + render_ctx: render::Context, } impl App { @@ -25,18 +24,58 @@ impl App { .build(&event_loop) .unwrap(); - let render_ctx = renderer::RenderContext::new(&window).await; - let renderer = renderer::Renderer::new(&render_ctx); + let render_ctx = render::Context::new(&window).await; Self { window, event_loop, render_ctx, - renderer, } } - pub fn run(mut self) { + pub fn run(self) { + let mut camera_controller = camera::CameraController::new( + &self.render_ctx, + camera::Camera::new( + glam::Vec3 { + x: 4.01, + y: 4.01, + z: 20.0, + }, + -90.0_f32.to_radians(), + 0.0_f32.to_radians(), + ), + camera::Projection::new( + self.render_ctx.size.width, + self.render_ctx.size.height, + 90.0_f32.to_radians(), + 0.01, + 100.0, + ), + 10.0, + 0.25, + ); + + let camera_bind_group_layout = render::BindGroupLayoutBuilder::new() + .with_label("camera_bind_group_layout") + .with_entry( + wgpu::ShaderStages::COMPUTE, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + None, + ) + .build(&self.render_ctx); + let camera_bind_group = render::BindGroupBuilder::new() + .with_label("camera_bind_group") + .with_layout(&camera_bind_group_layout) + .with_entry(camera_controller.get_buffer().as_entire_binding()) + .build(&self.render_ctx); + + let renderer = renderer::Renderer::new(&self.render_ctx, &camera_bind_group_layout); + let mut last_render_time = Instant::now(); self.event_loop .run(move |event, _, control_flow| match event { @@ -46,7 +85,7 @@ impl App { } if window_id == self.window.id() => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, _ => { - self.renderer.input(&event); + camera_controller.process_events(&event); } }, Event::MainEventsCleared => { @@ -56,8 +95,9 @@ impl App { let now = Instant::now(); let dt = now - last_render_time; last_render_time = now; - self.renderer.update(dt, &self.render_ctx); - self.renderer.render(&self.render_ctx); + camera_controller.update(dt); + camera_controller.update_buffer(&self.render_ctx); + renderer.render(&self.render_ctx, &camera_bind_group); } _ => {} }); diff --git a/src/camera.rs b/src/camera.rs index e1298c6..538a2cc 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -2,7 +2,7 @@ use std::time::Duration; use wgpu::util::DeviceExt; use winit::event::{ElementState, KeyboardInput, VirtualKeyCode, WindowEvent}; -use crate::renderer::RenderContext; +use crate::render::Context; #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -103,7 +103,7 @@ pub(crate) struct CameraController { impl CameraController { pub fn new( - context: &RenderContext, + context: &Context, camera: Camera, projection: Projection, move_speed: f32, @@ -236,7 +236,7 @@ impl CameraController { // log::info!("Camera Pitch: {:?}", self.camera.pitch); } - pub fn update_buffer(&mut self, context: &RenderContext) { + pub fn update_buffer(&mut self, context: &Context) { self.uniform.update( self.camera.get_view_matrix(), self.projection.get_matrix(), diff --git a/src/main.rs b/src/main.rs index e1aedfd..ef18492 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ mod app; mod camera; +mod render; mod renderer; -mod texture; fn main() { env_logger::init(); diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..c2152dc --- /dev/null +++ b/src/render.rs @@ -0,0 +1,9 @@ +mod bind_group; +mod context; +mod texture; + +pub use self::{ + bind_group::{BindGroupBuilder, BindGroupLayoutBuilder}, + context::Context, + texture::{Texture, TextureBuilder}, +}; diff --git a/src/render/bind_group.rs b/src/render/bind_group.rs new file mode 100644 index 0000000..5258419 --- /dev/null +++ b/src/render/bind_group.rs @@ -0,0 +1,96 @@ +use std::num::NonZeroU32; + +use super::Context; + +#[derive(Debug, Default)] +pub struct BindGroupLayoutBuilder<'a> { + next_binding: u32, + entries: Vec, + label: Option<&'a str>, +} + +impl<'a> BindGroupLayoutBuilder<'a> { + pub fn new() -> Self { + Self::default() + } + + #[inline] + pub fn with_label(mut self, label: &'a str) -> Self { + self.label = Some(label); + self + } + + #[inline] + pub fn with_entry( + mut self, + visibility: wgpu::ShaderStages, + ty: wgpu::BindingType, + count: Option, + ) -> Self { + self.entries.push(wgpu::BindGroupLayoutEntry { + binding: self.next_binding, + visibility, + ty, + count, + }); + self.next_binding += 1; + self + } + + #[inline] + pub fn build(self, context: &Context) -> wgpu::BindGroupLayout { + context + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: self.label, + entries: &self.entries, + }) + } +} + +#[derive(Debug, Default)] +pub struct BindGroupBuilder<'a> { + next_binding: u32, + label: Option<&'a str>, + entries: Vec>, + layout: Option<&'a wgpu::BindGroupLayout>, +} + +impl<'a> BindGroupBuilder<'a> { + pub fn new() -> Self { + Self::default() + } + + #[inline] + pub fn with_label(mut self, label: &'a str) -> Self { + self.label = Some(label); + self + } + + #[inline] + pub fn with_entry(mut self, resource: wgpu::BindingResource<'a>) -> Self { + self.entries.push(wgpu::BindGroupEntry { + binding: self.next_binding, + resource, + }); + self.next_binding += 1; + self + } + + #[inline] + pub fn with_layout(mut self, layout: &'a wgpu::BindGroupLayout) -> Self { + self.layout = Some(layout); + self + } + + #[inline] + pub fn build(self, context: &Context) -> wgpu::BindGroup { + context + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: self.label, + layout: &self.layout.unwrap(), + entries: self.entries.as_slice(), + }) + } +} diff --git a/src/render/context.rs b/src/render/context.rs new file mode 100644 index 0000000..905c5a3 --- /dev/null +++ b/src/render/context.rs @@ -0,0 +1,69 @@ +use winit::{dpi::PhysicalSize, window::Window}; + +pub struct Context { + pub instance: wgpu::Instance, + pub size: PhysicalSize, + pub surface: wgpu::Surface, + pub surface_config: wgpu::SurfaceConfiguration, + pub adapter: wgpu::Adapter, + pub device: wgpu::Device, + pub queue: wgpu::Queue, +} + +impl Context { + pub async fn new(window: &Window) -> Self { + log::info!("Initialising WGPU context..."); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::VULKAN, + dx12_shader_compiler: Default::default(), + }); + + // To be able to start drawing we need a few things: + // - A surface + // - A GPU device to draw to the surface + // - A draw command queue + log::info!("Initialising window surface..."); + let surface = unsafe { instance.create_surface(&window) }.unwrap(); + + log::info!("Requesting GPU adapter..."); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: Some(&surface), + }) + .await + .unwrap(); + + log::info!("Checking GPU adapter meets requirements"); + log::info!("Requesting GPU device..."); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, + ) + .await + .unwrap(); + + log::info!("Configuring window surface..."); + let size = window.inner_size(); + let surface_config = surface + .get_default_config(&adapter, size.width, size.height) + .unwrap(); + surface.configure(&device, &surface_config); + + Self { + instance, + size, + surface, + surface_config, + adapter, + device, + queue, + } + } +} diff --git a/src/texture.rs b/src/render/texture.rs similarity index 71% rename from src/texture.rs rename to src/render/texture.rs index 7aa1c0d..a5416b1 100644 --- a/src/texture.rs +++ b/src/render/texture.rs @@ -1,7 +1,7 @@ -use crate::renderer::RenderContext; - // TODO: Support mip-mapping and multi-sampling +use super::{BindGroupBuilder, BindGroupLayoutBuilder, Context}; + #[derive(Debug, Clone)] pub struct TextureAttributes { pub size: wgpu::Extent3d, @@ -35,7 +35,7 @@ impl Default for TextureAttributes { } } -pub(crate) struct TextureBuilder { +pub struct TextureBuilder { pub attributes: TextureAttributes, } @@ -97,12 +97,12 @@ impl TextureBuilder { } #[inline] - pub fn build(self, context: &RenderContext) -> Texture { + pub fn build(self, context: &Context) -> Texture { Texture::new(context, self.attributes) } } -pub(crate) struct Texture { +pub struct Texture { pub attributes: TextureAttributes, pub texture: wgpu::Texture, pub view: wgpu::TextureView, @@ -112,7 +112,7 @@ pub(crate) struct Texture { } impl Texture { - pub fn new(context: &RenderContext, attributes: TextureAttributes) -> Self { + pub fn new(context: &Context, attributes: TextureAttributes) -> Self { let texture = context.device.create_texture(&wgpu::TextureDescriptor { label: None, size: attributes.size, @@ -141,46 +141,27 @@ impl Texture { wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3, }; - let bind_group_layout = - context - .device - .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: attributes.shader_visibility, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: view_dimension, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: attributes.shader_visibility, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let bind_group = context - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&sampler), - }, - ], - }); + let bind_group_layout = BindGroupLayoutBuilder::new() + .with_entry( + attributes.shader_visibility, + wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension, + multisampled: false, + }, + None, + ) + .with_entry( + attributes.shader_visibility, + wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + None, + ) + .build(context); + let bind_group = BindGroupBuilder::new() + .with_layout(&bind_group_layout) + .with_entry(wgpu::BindingResource::TextureView(&view)) + .with_entry(wgpu::BindingResource::Sampler(&sampler)) + .build(context); Self { attributes, @@ -192,7 +173,7 @@ impl Texture { } } - pub fn update(&self, context: &RenderContext, data: &[u8]) { + pub fn update(&self, context: &Context, data: &[u8]) { log::info!("Updating texture contents..."); let copy_texture = wgpu::ImageCopyTexture { texture: &self.texture, diff --git a/src/renderer.rs b/src/renderer.rs index 9a476db..dcbb94a 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,79 +1,4 @@ -use std::time::Duration; - -use wgpu::util::DeviceExt; -use winit::event::WindowEvent; -use winit::{dpi::PhysicalSize, window::Window}; - -use crate::camera; -use crate::texture::{Texture, TextureBuilder}; - -pub(crate) struct RenderContext { - pub instance: wgpu::Instance, - pub size: PhysicalSize, - pub surface: wgpu::Surface, - pub surface_config: wgpu::SurfaceConfiguration, - pub adapter: wgpu::Adapter, - pub device: wgpu::Device, - pub queue: wgpu::Queue, -} - -impl RenderContext { - pub async fn new(window: &Window) -> Self { - log::info!("Initialising WGPU context..."); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::VULKAN, - dx12_shader_compiler: Default::default(), - }); - - // To be able to start drawing we need a few things: - // - A surface - // - A GPU device to draw to the surface - // - A draw command queue - log::info!("Initialising window surface..."); - let surface = unsafe { instance.create_surface(&window) }.unwrap(); - - log::info!("Requesting GPU adapter..."); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - force_fallback_adapter: false, - compatible_surface: Some(&surface), - }) - .await - .unwrap(); - - log::info!("Checking GPU adapter meets requirements"); - log::info!("Requesting GPU device..."); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, - ) - .await - .unwrap(); - - log::info!("Configuring window surface..."); - let size = window.inner_size(); - let surface_config = surface - .get_default_config(&adapter, size.width, size.height) - .unwrap(); - surface.configure(&device, &surface_config); - - Self { - instance, - size, - surface, - surface_config, - adapter, - device, - queue, - } - } -} +use crate::render::{BindGroupBuilder, BindGroupLayoutBuilder, Context, Texture, TextureBuilder}; pub(crate) struct Renderer { clear_color: wgpu::Color, @@ -82,12 +7,10 @@ pub(crate) struct Renderer { render_pipeline: wgpu::RenderPipeline, render_texture: Texture, voxel_texture: Texture, - camera_controller: camera::CameraController, - camera_bind_group: wgpu::BindGroup, } impl Renderer { - pub fn new(context: &RenderContext) -> Self { + pub fn new(context: &Context, camera_bind_group_layout: &wgpu::BindGroupLayout) -> Self { log::info!("Creating render shader..."); let shader_descriptor = wgpu::include_wgsl!("../assets/shaders/shader.wgsl"); let shader = context.device.create_shader_module(shader_descriptor); @@ -140,54 +63,6 @@ impl Renderer { voxel_texture.update(&context, &data); log::info!("Creating camera..."); - let camera_controller = camera::CameraController::new( - context, - camera::Camera::new( - glam::Vec3 { - x: 4.01, - y: 4.01, - z: 20.0, - }, - -90.0_f32.to_radians(), - 0.0_f32.to_radians(), - ), - camera::Projection::new( - context.size.width, - context.size.height, - 90.0_f32.to_radians(), - 0.01, - 100.0, - ), - 10.0, - 0.25, - ); - - let camera_bind_group_layout = - context - .device - .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::COMPUTE, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("camera_bind_group_layout"), - }); - let camera_bind_group = context - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_controller.get_buffer().as_entire_binding(), - }], - label: Some("camera_bind_group"), - }); log::info!("Creating render pipeline..."); let render_pipeline = @@ -221,32 +96,21 @@ impl Renderer { log::info!("Creating compute pipeline..."); let cs_descriptor = wgpu::include_wgsl!("../assets/shaders/voxel_volume.wgsl"); let cs = context.device.create_shader_module(cs_descriptor); - let compute_layout = - context - .device - .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::COMPUTE, - ty: wgpu::BindingType::StorageTexture { - access: wgpu::StorageTextureAccess::WriteOnly, - format: render_texture.attributes.format, - view_dimension: wgpu::TextureViewDimension::D2, - }, - count: None, - }], - }); - let compute_bind_group = context - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &compute_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&render_texture.view), - }], - }); + let compute_layout = BindGroupLayoutBuilder::new() + .with_entry( + wgpu::ShaderStages::COMPUTE, + wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: render_texture.attributes.format, + view_dimension: wgpu::TextureViewDimension::D2, + }, + None, + ) + .build(context); + let compute_bind_group = BindGroupBuilder::new() + .with_layout(&compute_layout) + .with_entry(wgpu::BindingResource::TextureView(&render_texture.view)) + .build(context); let compute_pipeline = context .device @@ -281,12 +145,10 @@ impl Renderer { render_pipeline, render_texture, voxel_texture, - camera_controller, - camera_bind_group, } } - pub fn render(&mut self, context: &RenderContext) { + pub fn render(&self, context: &Context, camera_bind_group: &wgpu::BindGroup) { let frame = context.surface.get_current_texture().unwrap(); let view = frame .texture @@ -301,7 +163,7 @@ impl Renderer { compute_pass.set_pipeline(&self.compute_pipeline); compute_pass.set_bind_group(0, &self.compute_bind_group, &[]); compute_pass.set_bind_group(1, &self.voxel_texture.bind_group, &[]); - compute_pass.set_bind_group(2, &self.camera_bind_group, &[]); + compute_pass.set_bind_group(2, camera_bind_group, &[]); compute_pass.dispatch_workgroups(size.width / 8, size.height / 8, 1); drop(compute_pass); @@ -325,14 +187,13 @@ impl Renderer { context.queue.submit(Some(encoder.finish())); frame.present(); - } - pub fn input(&mut self, event: &WindowEvent) -> bool { - self.camera_controller.process_events(event) - } + // let mut command_buffers = Vec::with_capacity(encoders.len()); + // for encoder in encoders { + // command_buffers.push(encoder.finish()); + // } - pub fn update(&mut self, dt: Duration, render_ctx: &RenderContext) { - self.camera_controller.update(dt); - self.camera_controller.update_buffer(render_ctx) + // context.queue.submit(command_buffers); + // frame.present(); } }