Materials

This commit is contained in:
marceline-cramer 2022-02-02 16:08:53 -07:00
parent 306a983d14
commit f3fff03aff
7 changed files with 140 additions and 104 deletions

View File

@ -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<u32>,
}
@ -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<MeshInstance>>::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::<usize, Vec<MeshInstance>>::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,
});

View File

@ -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,
}

View File

@ -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<Planet>,
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,
})

View File

@ -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<MeshGroup>,
pub groups: Slab<MeshGroup>,
}
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<Texture>,
pub textures: Slab<Texture>,
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<Material>,
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 }
}
}

View File

@ -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<u32>,
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: _,

View File

@ -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,
}

View File

@ -42,8 +42,8 @@ var<storage,read> point_lights: PointLightData;
[[group(1), binding(0)]]
var<storage,read> meshes: MeshData;
[[group(2), binding(0)]] var t_albedo: texture_2d<f32>;
[[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<f32>;
[[stage(vertex)]]
fn vs_main(
@ -66,7 +66,7 @@ fn vs_main(
fn fs_main(
frag: VertexOutput,
) -> [[location(0)]] vec4<f32> {
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<f32>(0.0);