diff --git a/src/main.rs b/src/main.rs index 3847bb6..21fcfd8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use wgpu::util::DeviceExt; - use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, @@ -11,331 +9,16 @@ mod commands; mod handle; mod mesh; mod pool; +mod renderer; mod scene; use camera::*; -use commands::*; use handle::*; use mesh::*; use pool::*; +use renderer::*; use scene::*; -struct Renderer { - pub device: wgpu::Device, - pub mesh_pool: MeshPool, - pub texture_pool: TexturePool, - pub size: winit::dpi::PhysicalSize, - surface: wgpu::Surface, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - depth_texture: wgpu::Texture, - depth_texture_view: wgpu::TextureView, - camera_uniform: CameraUniform, - camera_buffer: wgpu::Buffer, - camera_bind_group: wgpu::BindGroup, - meshes_buffer: wgpu::Buffer, - meshes_bind_group: wgpu::BindGroup, - render_pipeline: wgpu::RenderPipeline, -} - -impl Renderer { - pub fn new( - size: winit::dpi::PhysicalSize, - surface: wgpu::Surface, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - ) -> Self { - let mesh_pool = MeshPool::default(); - let texture_pool = TexturePool::new(&device); - - let (depth_texture, depth_texture_view) = Self::make_depth_texture(&device, &config); - - let camera_uniform = CameraUniform::new(); - - let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[camera_uniform]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - - let camera_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - 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 = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - }], - label: Some("Camera Bind Group"), - }); - - let meshes_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Meshes Buffer"), - size: 65536, // TODO resizable meshes buffer/gpu vectors - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let meshes_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("Meshes Bind Group Layout"), - }); - - let meshes_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &meshes_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: meshes_buffer.as_entire_binding(), - }], - label: Some("Meshes Bind Group"), - }); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[ - &camera_bind_group_layout, - &meshes_bind_group_layout, - &texture_pool.bind_group_layout, - ], - push_constant_ranges: &[], - }); - - let shader = device.create_shader_module(&wgpu::include_wgsl!("shader.wgsl")); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc()], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - }], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: Self::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }); - - Self { - size, - surface, - device, - queue, - config, - mesh_pool, - texture_pool, - depth_texture, - depth_texture_view, - camera_uniform, - camera_buffer, - camera_bind_group, - meshes_buffer, - meshes_bind_group, - render_pipeline, - } - } - - pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - - fn make_depth_texture( - device: &wgpu::Device, - config: &wgpu::SurfaceConfiguration, - ) -> (wgpu::Texture, wgpu::TextureView) { - let size = wgpu::Extent3d { - width: config.width, - height: config.height, - depth_or_array_layers: 1, - }; - - let desc = wgpu::TextureDescriptor { - label: Some("Depth Texture"), - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, - }; - - let texture = device.create_texture(&desc); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - - (texture, view) - } - - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - let (depth_texture, depth_texture_view) = - Self::make_depth_texture(&self.device, &self.config); - self.depth_texture = depth_texture; - self.depth_texture_view = depth_texture_view; - } - } - - pub fn render( - &mut self, - camera: &impl Camera, - meshes: &Vec, - ) -> Result<(), wgpu::SurfaceError> { - self.camera_uniform.update(camera); - self.queue.write_buffer( - &self.camera_buffer, - 0, - bytemuck::cast_slice(&[self.camera_uniform]), - ); - - let mesh_commands = CommandSet::build(meshes); - - // TODO persistent staging buffer (write_buffer creates a new one per call) - self.queue - .write_buffer(&self.meshes_buffer, 0, mesh_commands.get_storage()); - - let output = self.surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: true, - }, - }], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.depth_texture_view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }); - - rp.set_pipeline(&self.render_pipeline); - rp.set_bind_group(0, &self.camera_bind_group, &[]); - rp.set_bind_group(1, &self.meshes_bind_group, &[]); - - let mut group: Option<&MeshGroup> = None; - for cmd in mesh_commands.iter() { - match cmd { - Command::BindMeshGroup { group_id } => { - group = self.mesh_pool.groups.get(group_id); - let group = group.unwrap(); - rp.set_vertex_buffer(0, group.vertices.slice(..)); - rp.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); - } - Command::BindTexture { texture_id } => { - let texture = self.texture_pool.textures.get(texture_id).unwrap(); - rp.set_bind_group(2, &texture.bind_group, &[]); - } - Command::Draw { - sub_id: _, - instance_range, - } => { - // TODO use sub_id in mesh draw - let indices = 0..(group.unwrap().index_capacity as u32); - rp.draw_indexed(indices, 0, instance_range); - } - } - } - } - - self.queue.submit(std::iter::once(encoder.finish())); - output.present(); - - Ok(()) - } -} - -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct CameraUniform { - vp: [[f32; 4]; 4], -} - -impl CameraUniform { - pub fn new() -> Self { - Self { - vp: glam::Mat4::IDENTITY.to_cols_array_2d(), - } - } - - pub fn update(&mut self, camera: &impl Camera) { - self.vp = camera.get_vp(); - } -} - fn load_model() -> (MeshData, TextureData) { use tobj::*; @@ -534,7 +217,7 @@ async fn make_window_renderer(window: &winit::window::Window) -> Renderer { }; surface.configure(&device, &config); - Renderer::new(size, instance, surface, device, queue, config) + Renderer::new(size, surface, device, queue, config) } fn main() { diff --git a/src/renderer.rs b/src/renderer.rs new file mode 100644 index 0000000..b331f56 --- /dev/null +++ b/src/renderer.rs @@ -0,0 +1,322 @@ +use super::camera::Camera; +use super::commands::{CommandSet, Command}; +use super::mesh::Vertex; +use super::pool::{MeshGroup, MeshPool, TexturePool}; +use super::scene::MeshInstance; +use wgpu::util::DeviceExt; + +pub struct Renderer { + pub device: wgpu::Device, + pub queue: wgpu::Queue, + pub mesh_pool: MeshPool, + pub texture_pool: TexturePool, + pub size: winit::dpi::PhysicalSize, + surface: wgpu::Surface, + config: wgpu::SurfaceConfiguration, + depth_texture: wgpu::Texture, + depth_texture_view: wgpu::TextureView, + camera_uniform: CameraUniform, + camera_buffer: wgpu::Buffer, + camera_bind_group: wgpu::BindGroup, + meshes_buffer: wgpu::Buffer, + meshes_bind_group: wgpu::BindGroup, + render_pipeline: wgpu::RenderPipeline, +} + +impl Renderer { + pub fn new( + size: winit::dpi::PhysicalSize, + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + ) -> Self { + let mesh_pool = MeshPool::default(); + let texture_pool = TexturePool::new(&device); + + let (depth_texture, depth_texture_view) = Self::make_depth_texture(&device, &config); + + let camera_uniform = CameraUniform::new(); + + let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let camera_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + 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 = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + label: Some("Camera Bind Group"), + }); + + let meshes_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Meshes Buffer"), + size: 65536, // TODO resizable meshes buffer/gpu vectors + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let meshes_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("Meshes Bind Group Layout"), + }); + + let meshes_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &meshes_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: meshes_buffer.as_entire_binding(), + }], + label: Some("Meshes Bind Group"), + }); + + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[ + &camera_bind_group_layout, + &meshes_bind_group_layout, + &texture_pool.bind_group_layout, + ], + push_constant_ranges: &[], + }); + + let shader = device.create_shader_module(&wgpu::include_wgsl!("shader.wgsl")); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + }], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: Self::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }); + + Self { + size, + surface, + device, + queue, + config, + mesh_pool, + texture_pool, + depth_texture, + depth_texture_view, + camera_uniform, + camera_buffer, + camera_bind_group, + meshes_buffer, + meshes_bind_group, + render_pipeline, + } + } + + pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + + fn make_depth_texture( + device: &wgpu::Device, + config: &wgpu::SurfaceConfiguration, + ) -> (wgpu::Texture, wgpu::TextureView) { + let size = wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }; + + let desc = wgpu::TextureDescriptor { + label: Some("Depth Texture"), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + }; + + let texture = device.create_texture(&desc); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + + (texture, view) + } + + pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + let (depth_texture, depth_texture_view) = + Self::make_depth_texture(&self.device, &self.config); + self.depth_texture = depth_texture; + self.depth_texture_view = depth_texture_view; + } + } + + pub fn render( + &mut self, + camera: &impl Camera, + meshes: &Vec, + ) -> Result<(), wgpu::SurfaceError> { + self.camera_uniform.update(camera); + self.queue.write_buffer( + &self.camera_buffer, + 0, + bytemuck::cast_slice(&[self.camera_uniform]), + ); + + let mesh_commands = CommandSet::build(meshes); + + // TODO persistent staging buffer (write_buffer creates a new one per call) + self.queue + .write_buffer(&self.meshes_buffer, 0, mesh_commands.get_storage()); + + let output = self.surface.get_current_texture()?; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: true, + }, + }], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_texture_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), + }); + + rp.set_pipeline(&self.render_pipeline); + rp.set_bind_group(0, &self.camera_bind_group, &[]); + rp.set_bind_group(1, &self.meshes_bind_group, &[]); + + let mut group: Option<&MeshGroup> = None; + for cmd in mesh_commands.iter() { + match cmd { + Command::BindMeshGroup { group_id } => { + group = self.mesh_pool.groups.get(group_id); + let group = group.unwrap(); + rp.set_vertex_buffer(0, group.vertices.slice(..)); + rp.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); + } + Command::BindTexture { texture_id } => { + let texture = self.texture_pool.textures.get(texture_id).unwrap(); + rp.set_bind_group(2, &texture.bind_group, &[]); + } + Command::Draw { + sub_id: _, + instance_range, + } => { + // TODO use sub_id in mesh draw + let indices = 0..(group.unwrap().index_capacity as u32); + rp.draw_indexed(indices, 0, instance_range); + } + } + } + } + + self.queue.submit(std::iter::once(encoder.finish())); + output.present(); + + Ok(()) + } +} + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct CameraUniform { + vp: [[f32; 4]; 4], +} + +impl CameraUniform { + pub fn new() -> Self { + Self { + vp: glam::Mat4::IDENTITY.to_cols_array_2d(), + } + } + + pub fn update(&mut self, camera: &impl Camera) { + self.vp = camera.get_vp(); + } +}