From f3fff03aff8f33c375deb5e68c95d84a1a0a6981 Mon Sep 17 00:00:00 2001 From: marceline-cramer Date: Wed, 2 Feb 2022 16:08:53 -0700 Subject: [PATCH] Materials --- src/commands.rs | 26 ++++----- src/handle.rs | 6 +++ src/main.rs | 51 +++++++++--------- src/pool.rs | 140 ++++++++++++++++++++++++++++-------------------- src/renderer.rs | 13 +++-- src/scene.rs | 2 +- src/shader.wgsl | 6 +-- 7 files changed, 140 insertions(+), 104 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index a388c04..affb60b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,7 +5,7 @@ use std::ops::Range; #[derive(Clone)] struct DrawState { group_id: usize, - texture_id: usize, + material_id: usize, sub_id: usize, instance_range: Range, } @@ -14,8 +14,8 @@ pub enum Command { BindMeshGroup { group_id: usize, }, - BindTexture { - texture_id: usize, + BindMaterial { + material_id: usize, }, Draw { sub_id: usize, @@ -27,7 +27,7 @@ pub struct CommandIterator<'a> { command_set: &'a CommandSet, command_idx: usize, // TODO use iterator instead lol last_group_id: usize, - last_texture_id: usize, + last_material_id: usize, } impl<'a> CommandIterator<'a> { @@ -36,7 +36,7 @@ impl<'a> CommandIterator<'a> { command_set, command_idx: 0, last_group_id: usize::MAX, - last_texture_id: usize::MAX, + last_material_id: usize::MAX, } } } @@ -48,16 +48,16 @@ impl<'a> Iterator for CommandIterator<'a> { let DrawState { group_id, sub_id, - texture_id, + material_id, instance_range, } = self.command_set.draws.get(self.command_idx)?.clone(); if group_id != self.last_group_id { self.last_group_id = group_id; Some(Command::BindMeshGroup { group_id }) - } else if texture_id != self.last_texture_id { - self.last_texture_id = texture_id; - Some(Command::BindTexture { texture_id }) + } else if material_id != self.last_material_id { + self.last_material_id = material_id; + Some(Command::BindMaterial { material_id }) } else { self.command_idx += 1; Some(Command::Draw { @@ -78,8 +78,8 @@ impl CommandSet { let mut sorted_meshes = HashMap::<(usize, usize), Vec>::new(); for instance in instances.iter() { let group_id = instance.mesh.group_id; - let texture_id = instance.albedo.id; - let key = (group_id, texture_id); + let material_id = instance.material.id; + let key = (group_id, material_id); if let Some(by_state) = sorted_meshes.get_mut(&key) { by_state.push(*instance); } else { @@ -90,7 +90,7 @@ impl CommandSet { let mut transforms = Vec::new(); let mut draws = Vec::new(); - for ((group_id, texture_id), mut meshes) in sorted_meshes.drain() { + for ((group_id, material_id), mut meshes) in sorted_meshes.drain() { let mut sorted_subs = HashMap::>::new(); for mesh in meshes.drain(..) { let sub_id = mesh.mesh.sub_id; @@ -109,7 +109,7 @@ impl CommandSet { let instance_range = start_idx..end_idx; draws.push(DrawState { group_id, - texture_id, + material_id, sub_id, instance_range, }); diff --git a/src/handle.rs b/src/handle.rs index 667c2e9..2960c8a 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -10,3 +10,9 @@ pub struct MeshHandle { pub struct TextureHandle { pub id: usize, } + +#[repr(C)] +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub struct MaterialHandle { + pub id: usize, +} diff --git a/src/main.rs b/src/main.rs index ebf2962..f2d087a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use pool::*; use renderer::*; use scene::*; -fn load_model() -> (MeshData, TextureData) { +fn load_model(ren: &mut Renderer) -> (MeshHandle, MaterialHandle) { use tobj::*; let model_data = include_bytes!("viking_room.obj").to_vec(); @@ -57,6 +57,9 @@ fn load_model() -> (MeshData, TextureData) { indices.extend(m.mesh.indices.iter().map(|i| i + index_base)); + let mesh_data = MeshData { vertices, indices }; + let mesh_handle = ren.mesh_pool.allocate(&ren.device, &mesh_data); + let albedo_data = include_bytes!("viking_room.png"); let albedo = image::load_from_memory(albedo_data).unwrap(); @@ -70,14 +73,22 @@ fn load_model() -> (MeshData, TextureData) { albedo_rgba.push(0xff); } - ( - MeshData { vertices, indices }, - TextureData { - width: dimensions.0, - height: dimensions.1, - data: albedo_rgba, - }, - ) + let albedo_data = TextureData { + width: dimensions.0, + height: dimensions.1, + data: albedo_rgba, + }; + + let albedo = ren + .texture_pool + .allocate(&ren.device, &ren.queue, &albedo_data); + + let material_data = MaterialData { albedo }; + let material_handle = + ren.material_pool + .allocate(&ren.device, &ren.texture_pool, &material_data); + + (mesh_handle, material_handle) } trait WorldState { @@ -91,11 +102,7 @@ struct Grid { impl Grid { fn new(ren: &mut Renderer) -> Self { - let (mesh_data, albedo_data) = load_model(); - let mesh = ren.mesh_pool.allocate(&ren.device, &mesh_data); - let albedo = ren - .texture_pool - .allocate(&ren.device, &ren.queue, &albedo_data); + let (mesh, material) = load_model(ren); let mut meshes = Vec::new(); for x in -5..5 { for y in -5..5 { @@ -103,7 +110,7 @@ impl Grid { let transform = glam::Mat4::from_translation(translation); meshes.push(MeshInstance { mesh, - albedo, + material, transform, }); } @@ -132,17 +139,13 @@ struct Planets { start: std::time::Instant, planets: Vec, mesh: MeshHandle, - albedo: TextureHandle, + material: MaterialHandle, } impl Planets { fn new(ren: &mut Renderer) -> Self { let start = std::time::Instant::now(); - let (mesh_data, albedo_data) = load_model(); - let mesh = ren.mesh_pool.allocate(&ren.device, &mesh_data); - let albedo = ren - .texture_pool - .allocate(&ren.device, &ren.queue, &albedo_data); + let (mesh, material) = load_model(ren); let mut planets = Vec::new(); for i in 0..10 { @@ -159,7 +162,7 @@ impl Planets { start, planets, mesh, - albedo, + material, } } } @@ -178,7 +181,7 @@ impl WorldState for Planets { let rotation = glam::Mat4::from_rotation_y(theta); meshes.push(MeshInstance { mesh: self.mesh, - albedo: self.albedo, + material: self.material, transform: rotation * translation, }); } @@ -194,7 +197,7 @@ async fn make_window_renderer(window: &winit::window::Window) -> Renderer { let surface = unsafe { instance.create_surface(window) }; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, + power_preference: wgpu::PowerPreference::LowPower, compatible_surface: Some(&surface), force_fallback_adapter: false, }) diff --git a/src/pool.rs b/src/pool.rs index 2179aec..4d99c61 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -1,5 +1,6 @@ -use super::handle::{MeshHandle, TextureHandle}; +use super::handle::{MaterialHandle, MeshHandle, TextureHandle}; use super::mesh::MeshData; +use slab::Slab; use wgpu::util::DeviceExt; pub struct MeshGroup { @@ -38,7 +39,7 @@ impl MeshGroup { #[derive(Default)] pub struct MeshPool { // TODO make this private - pub groups: slab::Slab, + pub groups: Slab, } impl MeshPool { @@ -61,19 +62,12 @@ pub struct TextureData { } pub struct Texture { - // TODO make this all private - pub texture: wgpu::Texture, - pub bind_group: wgpu::BindGroup, + texture: wgpu::Texture, + texture_view: wgpu::TextureView, } impl Texture { - pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, - sampler: &wgpu::Sampler, - bind_group_layout: &wgpu::BindGroupLayout, - data: &TextureData, - ) -> Self { + pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, data: &TextureData) -> Self { let size = wgpu::Extent3d { width: data.width, height: data.height, @@ -108,33 +102,17 @@ impl Texture { size, ); - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&texture_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sampler), - }, - ], - label: None, - }); - Texture { texture, - bind_group, + texture_view, } } } pub struct TexturePool { // TODO make this all private - pub textures: slab::Slab, + pub textures: Slab, pub sampler: wgpu::Sampler, - pub bind_group_layout: wgpu::BindGroupLayout, } impl TexturePool { @@ -150,33 +128,7 @@ impl TexturePool { ..Default::default() }); - let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("Texture Bind Group Layout"), - }); - - Self { - textures, - sampler, - bind_group_layout, - } + Self { textures, sampler } } pub fn allocate( @@ -185,8 +137,80 @@ impl TexturePool { queue: &wgpu::Queue, data: &TextureData, ) -> TextureHandle { - let texture = Texture::new(device, queue, &self.sampler, &self.bind_group_layout, data); + let texture = Texture::new(device, queue, data); let id = self.textures.insert(texture); TextureHandle { id } } } + +pub struct MaterialData { + pub albedo: TextureHandle, +} + +pub struct Material { + pub bind_group: wgpu::BindGroup, +} + +pub struct MaterialPool { + pub materials: Slab, + pub bind_group_layout: wgpu::BindGroupLayout, +} + +impl MaterialPool { + pub fn new(device: &wgpu::Device) -> Self { + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + ], + label: Some("Texture Bind Group Layout"), + }); + + Self { + materials: Default::default(), + bind_group_layout, + } + } + + pub fn allocate( + &mut self, + device: &wgpu::Device, + texture_pool: &TexturePool, + data: &MaterialData, + ) -> MaterialHandle { + let albedo_view = &texture_pool.textures[data.albedo.id].texture_view; + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &self.bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&texture_pool.sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(albedo_view), + }, + ], + label: None, + }); + + let material = Material { bind_group }; + let id = self.materials.insert(material); + MaterialHandle { id } + } +} diff --git a/src/renderer.rs b/src/renderer.rs index afcda33..d6bbab3 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,7 +1,7 @@ use super::camera::Camera; use super::commands::{Command, CommandSet}; use super::mesh::Vertex; -use super::pool::{MeshGroup, MeshPool, TexturePool}; +use super::pool::{MaterialPool, MeshGroup, MeshPool, TexturePool}; use super::scene::{MeshInstance, PointLight}; use wgpu::util::DeviceExt; @@ -10,6 +10,7 @@ pub struct Renderer { pub queue: wgpu::Queue, pub mesh_pool: MeshPool, pub texture_pool: TexturePool, + pub material_pool: MaterialPool, pub size: winit::dpi::PhysicalSize, surface: wgpu::Surface, config: wgpu::SurfaceConfiguration, @@ -34,6 +35,7 @@ impl Renderer { ) -> Self { let mesh_pool = MeshPool::default(); let texture_pool = TexturePool::new(&device); + let material_pool = MaterialPool::new(&device); let (depth_texture, depth_texture_view) = Self::make_depth_texture(&device, &config); @@ -131,7 +133,7 @@ impl Renderer { bind_group_layouts: &[ &camera_bind_group_layout, &meshes_bind_group_layout, - &texture_pool.bind_group_layout, + &material_pool.bind_group_layout, ], push_constant_ranges: &[], }); @@ -187,6 +189,7 @@ impl Renderer { config, mesh_pool, texture_pool, + material_pool, depth_texture, depth_texture_view, camera_uniform, @@ -323,9 +326,9 @@ impl Renderer { 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::BindMaterial { material_id } => { + let material = self.material_pool.materials.get(material_id).unwrap(); + rp.set_bind_group(2, &material.bind_group, &[]); } Command::Draw { sub_id: _, diff --git a/src/scene.rs b/src/scene.rs index e594c24..02256f6 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -3,7 +3,7 @@ use super::handle::*; #[derive(Copy, Clone, PartialEq)] pub struct MeshInstance { pub mesh: MeshHandle, - pub albedo: TextureHandle, + pub material: MaterialHandle, pub transform: glam::Mat4, } diff --git a/src/shader.wgsl b/src/shader.wgsl index b11e1a8..78fb980 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -42,8 +42,8 @@ var point_lights: PointLightData; [[group(1), binding(0)]] var meshes: MeshData; -[[group(2), binding(0)]] var t_albedo: texture_2d; -[[group(2), binding(1)]] var s_albedo: sampler; +[[group(2), binding(0)]] var m_sampler: sampler; +[[group(2), binding(1)]] var m_albedo: texture_2d; [[stage(vertex)]] fn vs_main( @@ -66,7 +66,7 @@ fn vs_main( fn fs_main( frag: VertexOutput, ) -> [[location(0)]] vec4 { - let albedo = textureSample(t_albedo, s_albedo, frag.tex_coords).rgb; + let albedo = textureSample(m_albedo, m_sampler, frag.tex_coords).rgb; let normal = normalize(frag.normal); var lum = vec3(0.0);