diff --git a/solar_engine/src/device_manager.rs b/solar_engine/src/device_manager.rs new file mode 100644 index 0000000..ad602ea --- /dev/null +++ b/solar_engine/src/device_manager.rs @@ -0,0 +1,10 @@ +use wgpu::{Device, Queue, Surface, SurfaceConfiguration}; +use crate::render::SampleCount; + +pub struct DeviceManager<'a> { + pub surface: Surface<'a>, + pub device: Device, + pub queue: Queue, + pub config: SurfaceConfiguration, + pub sample_count: SampleCount, +} \ No newline at end of file diff --git a/solar_engine/src/geometry_manager.rs b/solar_engine/src/geometry_manager.rs new file mode 100644 index 0000000..8f90347 --- /dev/null +++ b/solar_engine/src/geometry_manager.rs @@ -0,0 +1,60 @@ +use std::collections::HashMap; +use wgpu::{Device, Buffer}; +use crate::render::{create_circle_vertices, create_sphere_vertices, Geometry, Shape, Vertex}; +use wgpu::util::DeviceExt; + +pub struct GeometryManager { + pub geometries: HashMap, +} + +impl GeometryManager { + pub fn new(device: &Device) -> Self { + let mut geometries = HashMap::new(); + + // Circle + let (circle_vertices, circle_indices) = create_circle_vertices(512, 0.5, [0.5, 0.5, 0.5]); + geometries.insert( + Shape::Circle, + Self::create_geometry(device, &circle_vertices, &circle_indices), + ); + + // Sphere + let (sphere_vertices, sphere_indices) = create_sphere_vertices(32, 32, 0.5, [0.5, 0.5, 0.5]); + geometries.insert( + Shape::Sphere, + Self::create_geometry(device, &sphere_vertices, &sphere_indices), + ); + + // Füge hier beliebige weitere Shapes hinzu + + Self { geometries } + } + + pub fn get(&self, shape: &Shape) -> Option<&Geometry> { + self.geometries.get(shape) + } + + pub fn shapes(&self) -> impl Iterator + '_ { + self.geometries.keys().copied() + } + + fn create_geometry(device: &Device, vertices: &[Vertex], indices: &[u16]) -> Geometry { + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(indices), + usage: wgpu::BufferUsages::INDEX, + }); + + Geometry { + vertex_buffer, + index_buffer, + index_count: indices.len() as u32, + } + } +} diff --git a/solar_engine/src/globals.rs b/solar_engine/src/globals.rs new file mode 100644 index 0000000..7c4f90f --- /dev/null +++ b/solar_engine/src/globals.rs @@ -0,0 +1,95 @@ +use crate::camera::Camera; +use crate::render::Globals; +use wgpu::{BindGroup, BindGroupLayout, Buffer, Device, Queue}; +use bytemuck::{Pod, Zeroable}; +use wgpu::util::DeviceExt; + +#[repr(C)] +#[derive(Copy, Clone, Pod, Zeroable)] +pub struct GlobalsUniform { + pub view_proj: [[f32; 4]; 4], + pub resolution: [f32; 2], + _padding: [f32; 2], +} + +pub struct GlobalsManager { + buffer: Buffer, + pub(crate) bind_group: BindGroup, + layout: BindGroupLayout, + resolution: [f32; 2], +} + +impl GlobalsManager { + pub fn new(device: &Device, width: u32, height: u32, camera: &Camera) -> Self { + let resolution = [width as f32, height as f32]; + let view_proj = camera.build_view_projection_matrix(); + + let data = GlobalsUniform { + view_proj: view_proj.into(), + resolution, + _padding: [0.0; 2], + }; + + let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Globals Buffer"), + contents: bytemuck::cast_slice(&[data]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Globals Bind Group Layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("Globals Bind Group"), + layout: &layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + }], + }); + + Self { + buffer, + bind_group, + layout, + resolution, + } + } + + pub fn update(&mut self, queue: &Queue, camera: &Camera) { + let view_proj = camera.build_view_projection_matrix(); + let data = GlobalsUniform { + view_proj: view_proj.into(), + resolution: self.resolution, + _padding: [0.0; 2], + }; + queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[data])); + } + + pub fn resize(&mut self, width: u32, height: u32) { + self.resolution = [width as f32, height as f32]; + } + + pub fn layout(&self) -> &BindGroupLayout { + &self.layout + } + + pub fn bind_group(&self) -> &BindGroup { + &self.bind_group + } + + pub fn resolution(&self) -> [f32; 2] { + self.resolution + } +} diff --git a/solar_engine/src/instance_manager.rs b/solar_engine/src/instance_manager.rs new file mode 100644 index 0000000..92fa685 --- /dev/null +++ b/solar_engine/src/instance_manager.rs @@ -0,0 +1,65 @@ +use crate::render::{InstanceRaw, RenderInstance, Shape}; +use wgpu::{Buffer, Device, Queue}; +use wgpu::util::DeviceExt; +use std::mem::size_of; + +pub struct InstanceManager { + instances: Vec, + raw: Vec, + buffer: Buffer, +} + +impl InstanceManager { + pub fn new(device: &Device) -> Self { + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Instance Buffer (empty)"), + size: 1, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + Self { + instances: Vec::new(), + raw: Vec::new(), + buffer, + } + } + + pub fn set_instances(&mut self, device: &Device, queue: &Queue, instances: Vec) { + self.raw = instances.iter().map(RenderInstance::to_raw).collect(); + let byte_len = (self.raw.len() * size_of::()) as wgpu::BufferAddress; + + if byte_len > self.buffer.size() { + self.buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Instance Buffer (resized)"), + contents: bytemuck::cast_slice(&self.raw), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); + } else { + queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&self.raw)); + } + + self.instances = instances; + } + + pub fn raw_instances_for_shape(&self, shape: Shape) -> Vec<&InstanceRaw> { + self.instances + .iter() + .zip(self.raw.iter()) + .filter(|(inst, _)| inst.shape == shape) + .map(|(_, raw)| raw) + .collect() + } + + pub fn buffer(&self) -> &Buffer { + &self.buffer + } + + pub fn len(&self) -> usize { + self.instances.len() + } + + pub fn is_empty(&self) -> bool { + self.instances.is_empty() + } +} diff --git a/solar_engine/src/lib.rs b/solar_engine/src/lib.rs index 8f93fa4..e2909fa 100644 --- a/solar_engine/src/lib.rs +++ b/solar_engine/src/lib.rs @@ -6,6 +6,11 @@ mod application; mod input; mod camera; mod light; +mod renderer; +mod device_manager; +mod instance_manager; +mod globals; +mod geometry_manager; pub use body::Body; diff --git a/solar_engine/src/renderer.rs b/solar_engine/src/renderer.rs new file mode 100644 index 0000000..793a733 --- /dev/null +++ b/solar_engine/src/renderer.rs @@ -0,0 +1,231 @@ +use crate::camera::Camera; +use crate::geometry_manager::GeometryManager; +use crate::globals::GlobalsManager; +use crate::instance_manager::InstanceManager; +use crate::light::LightManager; +use crate::render::{Geometry, InstanceRaw, Shape}; +use std::collections::HashMap; +use wgpu::{Device, Queue, SurfaceTexture, TextureView}; + +pub struct Renderer { + pipeline: wgpu::RenderPipeline, + depth_texture: TextureView, + sample_count: u32, +} + +impl Renderer { + pub fn new( + device: &Device, + config: &wgpu::SurfaceConfiguration, + global_layout: &wgpu::BindGroupLayout, + light_manager: &mut LightManager, + camera: &Camera, + sample_count: u32, + ) -> Self { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + + let cluster_assignment = light_manager.compute_cluster_assignments( + camera.build_view_matrix(), + camera.build_view_projection_matrix(), + config.width as f32, + config.height as f32, + ); + + let cluster_buffers = light_manager.create_cluster_buffers(device, &cluster_assignment); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[ + global_layout, + &light_manager.layout, + &cluster_buffers.layout, + ], + push_constant_ranges: &[], + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + buffers: &[crate::render::Vertex::desc(), InstanceRaw::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: sample_count, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); + + let depth_texture = + Self::create_depth_texture(device, config.width, config.height, sample_count); + + Self { + pipeline, + depth_texture, + sample_count, + } + } + + pub fn resize(&mut self, device: &Device, width: u32, height: u32) { + self.depth_texture = Self::create_depth_texture(device, width, height, self.sample_count); + } + + fn create_depth_texture( + device: &Device, + width: u32, + height: u32, + sample_count: u32, + ) -> TextureView { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Depth Texture"), + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Depth32Float, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }); + texture.create_view(&Default::default()) + } + + pub fn render_frame( + &mut self, + device: &Device, + queue: &Queue, + output: SurfaceTexture, + view: &TextureView, + surface_format: wgpu::TextureFormat, + globals: &mut GlobalsManager, + camera: &Camera, + light_manager: &mut LightManager, + geometry: &GeometryManager, + instances: &InstanceManager, + ) -> Result<(), wgpu::SurfaceError> { + // Update uniform buffer + globals.update(queue, camera); + + // Update cluster buffers + let assignment = light_manager.compute_cluster_assignments( + camera.build_view_matrix(), + camera.build_view_projection_matrix(), + globals.resolution()[0], + globals.resolution()[1], + ); + light_manager.update_cluster_buffers(device, queue, &assignment); + light_manager.update_gpu(queue); + + let multisampled_texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Multisample Target"), + size: wgpu::Extent3d { + width: globals.resolution()[0] as u32, + height: globals.resolution()[1] as u32, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: self.sample_count, + dimension: wgpu::TextureDimension::D2, + format: surface_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }); + let multisampled_view = multisampled_texture.create_view(&Default::default()); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Main Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &multisampled_view, + resolve_target: Some(view), + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.1, + b: 0.2, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_texture, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: wgpu::StoreOp::Store, + }), + stencil_ops: None, + }), + ..Default::default() + }); + + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, &globals.bind_group, &[]); + pass.set_bind_group(1, &light_manager.bind_group, &[]); + + if let Some(clusters) = &light_manager.cluster_buffers { + pass.set_bind_group(2, &clusters.bind_group, &[]); + } + + for shape in geometry.shapes() { + if let Some(mesh) = geometry.get(&shape) { + let relevant = instances.raw_instances_for_shape(shape); + if relevant.is_empty() { + continue; + } + + pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + pass.set_vertex_buffer(1, instances.buffer().slice(..)); + pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16); + pass.draw_indexed(0..mesh.index_count, 0, 0..relevant.len() as u32); + } + } + } + + queue.submit(Some(encoder.finish())); + output.present(); + + Ok(()) + } +} diff --git a/solar_engine/src/state.rs b/solar_engine/src/state.rs index c30a257..ab79c74 100644 --- a/solar_engine/src/state.rs +++ b/solar_engine/src/state.rs @@ -5,12 +5,17 @@ use cgmath::{perspective, Deg, Matrix4, Point3, Vector3}; use log::info; use pollster::FutureExt; use wgpu::util::DeviceExt; -use wgpu::{Adapter, BindGroup, BindGroupLayout, Device, Instance, PresentMode, Queue, Surface, SurfaceCapabilities, SurfaceConfiguration}; +use wgpu::{Adapter, BindGroup, BindGroupLayout, Device, Instance, PresentMode, Queue, Surface, SurfaceCapabilities, SurfaceConfiguration, SurfaceError}; use winit::dpi::PhysicalSize; use winit::window::{Window}; use crate::camera::Camera; +use crate::device_manager::DeviceManager; +use crate::geometry_manager::GeometryManager; +use crate::globals::GlobalsManager; +use crate::instance_manager::InstanceManager; use crate::light::{ClusterBuffers, GpuLight, LightManager}; use crate::render::{create_circle_vertices, create_sphere_vertices, Geometry, Globals, InstanceRaw, RenderInstance, SampleCount, Shape, Vertex}; +use crate::renderer::Renderer; pub struct State<'a> { surface: Surface<'a>, @@ -18,67 +23,54 @@ pub struct State<'a> { queue: Queue, config: SurfaceConfiguration, sample_count: SampleCount, - size: PhysicalSize, + window: Arc, + pub camera: Camera, - render_pipeline: wgpu::RenderPipeline, - - instances: Vec, - instance_buffer: wgpu::Buffer, - - geometries: HashMap, - global_bind_group: BindGroup, - global_buffer: wgpu::Buffer, - - camera: Camera, - depth_texture: wgpu::TextureView, - + pub globals: GlobalsManager, + pub geometry_manager: GeometryManager, + pub instance_manager: InstanceManager, pub light_manager: LightManager, + pub renderer: Renderer, } impl<'a> State<'a> { pub(crate) fn new(window: Window) -> Self { - let window_arc = Arc::new(window); - let size = window_arc.inner_size(); + let window = Arc::new(window); + let size = window.inner_size(); + let instance = Self::create_gpu_instance(); - let surface = instance.create_surface(window_arc.clone()).unwrap(); + + let surface = instance.create_surface(window.clone()).unwrap(); + let adapter = Self::create_adapter(instance, &surface); + let (device, queue) = Self::create_device(&adapter); - let surface_caps = surface.get_capabilities(&adapter); - let config = Self::create_surface_config(size, surface_caps); + + let capabilities = surface.get_capabilities(&adapter); + let config = Self::create_surface_config(size, capabilities); surface.configure(&device, &config); let sample_count = SampleCount(Self::probe_msaa_support(&device, &config)); - info!("MSAA sample count: {}", sample_count.0); + info!("MSAA sample count: {}", sample_count.get()); - let aspect = config.width as f32 / config.height as f32; - let camera = Camera::new(aspect); - let view_proj = camera.build_view_projection_matrix(); + let camera = Camera::new(config.width as f32 / config.height as f32); - let globals = Globals { - view_proj: view_proj.into(), - resolution: [config.width as f32, config.height as f32], - _padding: [0.0, 0.0], - }; - let (global_buffer, global_bind_group_layout, global_bind_group) = Self::create_global_buffer(&device); - queue.write_buffer(&global_buffer, 0, bytemuck::cast_slice(&[globals])); + let globals = GlobalsManager::new(&device, config.width, config.height, &camera); + let geometry_manager = GeometryManager::new(&device); + let instance_manager = InstanceManager::new(&device); + let mut light_manager = LightManager::new(&device, 100); - let mut light_manager = LightManager::new(&device, 10); - - let render_pipeline = Self::create_render_pipeline(&queue, &device, &config, sample_count.0, &global_bind_group_layout, &mut light_manager, &camera); - let geometries = Self::create_geometries(&device); - - let instances = vec![]; - let instance_data: Vec = instances.iter().map(RenderInstance::to_raw).collect(); - let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Instance Buffer"), - contents: bytemuck::cast_slice(&instance_data), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); + let renderer = Renderer::new( + &device, + &config, + globals.layout(), + &mut light_manager, + &camera, + sample_count.get(), + ); - let depth_texture = Self::create_depth_texture(&device, config.width, config.height, sample_count.get()); - Self { surface, device, @@ -86,106 +78,13 @@ impl<'a> State<'a> { config, sample_count, size, - window: window_arc, - render_pipeline, - geometries, - global_bind_group, - global_buffer, - instances, - instance_buffer, + window, camera, - depth_texture, - light_manager - } - } - - fn create_global_buffer(device: &wgpu::Device) -> (wgpu::Buffer, BindGroupLayout, BindGroup) { - let global_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Global Buffer"), - size: size_of::() as u64, - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let global_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Global Bind Group Layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); - - let global_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &global_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: global_buffer.as_entire_binding(), - }], - label: Some("Global Bind Group"), - }); - - (global_buffer, global_bind_group_layout, global_bind_group) - } - - fn update_lights(&mut self) { - let light_data: Vec = self.light_manager.lights.iter().map(|l| l.to_gpu()).collect(); - self.queue.write_buffer(&self.light_manager.buffer, 0, bytemuck::cast_slice(&light_data)); - } - - fn create_depth_texture(device: &Device, width: u32, height: u32, sample_count: u32) -> wgpu::TextureView { - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("Depth Texture"), - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth32Float, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - view_formats: &[], - }); - texture.create_view(&Default::default()) - } - - fn create_geometries(device: &Device) -> HashMap { - let mut geometries = HashMap::new(); - - let (circle_vertices, circle_indices) = create_circle_vertices(512, 0.5, [0.5, 0.5, 0.5]); - let circle_geometry = Self::create_geometry(device, &circle_vertices, &circle_indices); - geometries.insert(Shape::Circle, circle_geometry); - - let (sphere_vertices, sphere_indices) = create_sphere_vertices(32, 32, 0.5, [0.5, 0.5, 0.5]); - let sphere_geometry = Self::create_geometry(device, &sphere_vertices, &sphere_indices); - geometries.insert(Shape::Sphere, sphere_geometry); - - geometries - } - - fn create_geometry(device: &Device, vertices: &[Vertex], indices: &[u16]) -> Geometry { - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(indices), - usage: wgpu::BufferUsages::INDEX, - }); - - Geometry { - vertex_buffer, - index_buffer, - index_count: indices.len() as u32, + globals, + geometry_manager, + instance_manager, + light_manager, + renderer, } } @@ -217,80 +116,6 @@ impl<'a> State<'a> { 1 // fallback }) } - - fn create_render_pipeline(queue: &Queue, device: &Device, config: &SurfaceConfiguration, sample_count: u32, global_bind_group_layout: &BindGroupLayout, light_manager: &mut LightManager, camera: &Camera) -> wgpu::RenderPipeline { - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), - }); - - let cluster_assignment = light_manager.compute_cluster_assignments( - camera.build_view_matrix(), - camera.build_view_projection_matrix(), - config.width as f32, - config.height as f32, - ); - - let cluster_buffers = light_manager.create_cluster_buffers(&device, &cluster_assignment); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[ - &global_bind_group_layout, - &light_manager.layout, - &cluster_buffers.layout, - ], - push_constant_ranges: &[], - }); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - buffers: &[Vertex::desc(), InstanceRaw::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: Some("fs_main"), - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: sample_count, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }) - } fn create_surface_config(size: PhysicalSize, capabilities: SurfaceCapabilities) -> wgpu::SurfaceConfiguration { let surface_format = capabilities.formats.iter() @@ -339,148 +164,39 @@ impl<'a> State<'a> { } pub(crate) fn resize(&mut self, new_size: PhysicalSize) { - self.size = new_size; + 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); - self.config.width = max(new_size.width, 1); - self.config.height = max(new_size.height, 1); - - self.surface.configure(&self.device, &self.config); - - self.camera.set_aspect(self.config.width as f32 / self.config.height as f32); - let view_proj = self.camera.build_view_projection_matrix(); - - let new_globals = Globals { - view_proj: view_proj.into(), - resolution: [self.config.width as f32, self.config.height as f32], - _padding: [0.0, 0.0], - }; - self.queue.write_buffer(&self.global_buffer, 0, bytemuck::cast_slice(&[new_globals])); - self.depth_texture = Self::create_depth_texture(&self.device, self.config.width, self.config.height, self.sample_count.get()); - - println!("Resized to {:?} from state!", new_size); + self.camera.set_aspect(new_size.width as f32 / new_size.height as f32); + self.globals.resize(new_size.width, new_size.height); + self.renderer.resize(&self.device, new_size.width, new_size.height); + } } - pub(crate) fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + pub fn render(&mut self) -> Result<(), SurfaceError> { let output = self.surface.get_current_texture()?; + let view = output.texture.create_view(&Default::default()); - let multisampled_texture = self.device.create_texture(&wgpu::TextureDescriptor { - label: Some("Multisampled Render Target"), - size: wgpu::Extent3d { - width: self.config.width, - height: self.config.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: self.sample_count.get(), - dimension: wgpu::TextureDimension::D2, - format: self.config.format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - view_formats: &[], - }); - let multisampled_view = multisampled_texture.create_view(&Default::default()); - - let view_proj = self.camera.build_view_projection_matrix(); - let globals = Globals { - view_proj: view_proj.into(), - resolution: [self.config.width as f32, self.config.height as f32], - _padding: [0.0, 0.0], - }; - self.queue.write_buffer(&self.global_buffer, 0, bytemuck::cast_slice(&[globals])); - - let assignment = self.light_manager.compute_cluster_assignments( - self.camera.build_view_matrix(), - self.camera.build_view_projection_matrix(), - self.config.width as f32, - self.config.height as f32, - ); - self.light_manager.update_cluster_buffers(&self.device, &self.queue, &assignment); - - let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &multisampled_view, - resolve_target: Some(&output.texture.create_view(&Default::default())), - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 1.0, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: wgpu::StoreOp::Store, - } - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.depth_texture, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, - }), - occlusion_query_set: None, - timestamp_writes: None, - }); - - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_bind_group(0, &self.global_bind_group, &[]); + self.renderer.render_frame( + &self.device, + &self.queue, + output, + &view, + self.config.format, + &mut self.globals, + &self.camera, + &mut self.light_manager, + &self.geometry_manager, + &self.instance_manager, - // Update the light manager buffer - self.light_manager.update_gpu(&self.queue); - render_pass.set_bind_group(1, &self.light_manager.bind_group, &[]); - - if let Some(clusters) = &self.light_manager.cluster_buffers { - render_pass.set_bind_group(2, &clusters.bind_group, &[]); - } - - for shape in self.geometries.keys().copied() { - let geometry = &self.geometries[&shape]; - - let relevant_instances: Vec<_> = self.instances - .iter() - .enumerate() - .filter(|(_, inst)| inst.shape == shape) - .map(|(i, _)| i as u32) - .collect(); - - if relevant_instances.is_empty() { - continue; - } - - render_pass.set_vertex_buffer(0, geometry.vertex_buffer.slice(..)); - render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); - render_pass.set_index_buffer(geometry.index_buffer.slice(..), wgpu::IndexFormat::Uint16); - render_pass.draw_indexed(0..geometry.index_count, 0, 0..relevant_instances.len() as u32); - } - } - - self.queue.submit(std::iter::once(encoder.finish())); - output.present(); - - Ok(()) + ) } - + pub fn set_instances(&mut self, instances: Vec) { - let raw_data: Vec = instances.iter().map(RenderInstance::to_raw).collect(); - let byte_len = (raw_data.len() * size_of::()) as wgpu::BufferAddress; - - // Resize the buffer if necessary - if byte_len > self.instance_buffer.size() { - self.instance_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Instance Buffer (resized)"), - contents: bytemuck::cast_slice(&raw_data), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); - } else { - self.queue.write_buffer(&self.instance_buffer, 0, bytemuck::cast_slice(&raw_data)); - } - - self.instances = instances; + self.instance_manager.set_instances(&self.device, &self.queue, instances); } pub fn window(&self) -> &Window {