Add normal matrix for lighting, fix render multiple entities that use same model

This commit is contained in:
SeanOMik 2023-11-12 14:56:59 -05:00
parent a288c9b26c
commit 65467c5032
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
6 changed files with 229 additions and 157 deletions

View File

@ -77,10 +77,10 @@ async fn main() {
TransformComponent::from(Transform::from_xyz(3.0, 0.5, -2.2)), TransformComponent::from(Transform::from_xyz(3.0, 0.5, -2.2)),
)); */ )); */
world.spawn(( /* world.spawn((
ModelComponent(antique_camera_model), ModelComponent(antique_camera_model),
TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)), TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)),
)); )); */
/* let light = PointLight { /* let light = PointLight {
color: Vec3::new(1.0, 1.0, 1.0), color: Vec3::new(1.0, 1.0, 1.0),
@ -90,18 +90,22 @@ async fn main() {
quadratic: 0.032, quadratic: 0.032,
}; };
world.spawn((light,)); */ world.spawn((light,)); */
let mut cube_tran = Transform::from_xyz(-3.5, 0.0, -7.0);
cube_tran.rotate_y(math::Angle::Degrees(180.0));
world.spawn(( world.spawn((
PointLight { /* PointLight {
color: Vec3::new(1.0, 1.0, 1.0), color: Vec3::new(1.0, 1.0, 1.0),
intensity: 1.0, intensity: 1.0,
constant: 1.0, constant: 1.0,
linear: 0.045, linear: 0.045,
quadratic: 0.0075, quadratic: 0.0075,
}, }, */
TransformComponent::from(Transform::from_xyz(-2.5, 0.0, -10.0)), TransformComponent::from(cube_tran),
ModelComponent(cube_model.clone()), ModelComponent(cube_model.clone()),
)); ));
let mut light_tran = Transform::from_xyz(3.5, 0.0, -7.0);
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
world.spawn(( world.spawn((
PointLight { PointLight {
color: Vec3::new(0.361, 0.984, 0.0), color: Vec3::new(0.361, 0.984, 0.0),
@ -110,7 +114,7 @@ async fn main() {
linear: 0.045, linear: 0.045,
quadratic: 0.0075, quadratic: 0.0075,
}, },
TransformComponent::from(Transform::from_xyz(2.5, 0.0, -10.0)), TransformComponent::from(light_tran),
ModelComponent(cube_model), ModelComponent(cube_model),
)); ));
@ -154,7 +158,7 @@ async fn main() {
let mut sys = BatchedSystem::new(); let mut sys = BatchedSystem::new();
sys.with_criteria(FixedTimestep::new(45)); sys.with_criteria(FixedTimestep::new(45));
sys.with_system(spin_system); //sys.with_system(spin_system);
sys.with_system(fps_system); sys.with_system(fps_system);
game.with_system("fixed", sys, &[]); game.with_system("fixed", sys, &[]);

View File

@ -0,0 +1,28 @@
{
"rdocCaptureSettings": 1,
"settings": {
"autoStart": true,
"commandLine": "",
"environment": [
],
"executable": "/media/data_drive/Development/Rust/lyra-test/engine/target/debug/testbed",
"inject": false,
"numQueuedFrames": 1,
"options": {
"allowFullscreen": true,
"allowVSync": true,
"apiValidation": false,
"captureAllCmdLists": false,
"captureCallstacks": false,
"captureCallstacksOnlyDraws": false,
"debugOutputMute": true,
"delayForDebugger": 0,
"hookIntoChildren": false,
"refAllResources": false,
"softMemoryLimit": 0,
"verifyBufferAccess": false
},
"queuedFrameCap": 5,
"workingDir": "/media/data_drive/Development/Rust/lyra-test/engine/examples/testbed"
}
}

View File

@ -2,25 +2,21 @@ use edict::EntityId;
use crate::math::Transform; use crate::math::Transform;
use super::renderer::CachedTransform;
pub struct RenderJob { pub struct RenderJob {
pub entity: EntityId, pub entity: EntityId,
pub shader_id: u64, pub shader_id: u64,
pub mesh_buffer_id: uuid::Uuid, pub mesh_buffer_id: uuid::Uuid,
pub transform: Transform, pub transform: Transform,
pub last_transform: Option<CachedTransform>, // TODO: render interpolation
} }
impl RenderJob { impl RenderJob {
pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform, last_transform: Option<CachedTransform>) -> Self { pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform) -> Self {
Self { Self {
entity, entity,
shader_id, shader_id,
mesh_buffer_id, mesh_buffer_id,
transform, transform,
last_transform,
} }
} }
} }

View File

@ -47,9 +47,9 @@ struct MeshBufferStorage {
render_texture: Option<RenderTexture>, render_texture: Option<RenderTexture>,
texture_bindgroup: Option<BindGroup>, texture_bindgroup: Option<BindGroup>,
/// The index of the transform for this entity. // The index of the transform for this entity.
/// The tuple is structured like this: (transform index, index of transform inside the buffer) // The tuple is structured like this: (transform index, index of transform inside the buffer)
transform_index: TransformBufferIndices, //transform_index: TransformBufferIndices,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -78,8 +78,6 @@ pub struct BasicRenderer {
entity_last_transforms: HashMap<EntityId, CachedTransform>, entity_last_transforms: HashMap<EntityId, CachedTransform>,
transform_buffers: TransformBuffers, transform_buffers: TransformBuffers,
transform_bind_group_layout: BindGroupLayout,
//transform_bind_group: wgpu::BindGroup,
render_limits: Limits, render_limits: Limits,
@ -187,59 +185,7 @@ impl BasicRenderer {
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_src)), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_src)),
}); });
let transform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let transform_buffers = TransformBuffers::new(&device);
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: None,
},
count: None,
}
],
label: Some("transform_bind_group_layout"),
});
let transform_buffer = device.create_buffer(
&wgpu::BufferDescriptor {
label: Some("Transform Buffer 0"),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: render_limits.max_uniform_buffer_binding_size as u64,
mapped_at_creation: false,
}
);
let stride = render_limits.min_uniform_buffer_offset_alignment as usize + mem::size_of::<glam::Mat4>();
let transform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &transform_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: &transform_buffer,
offset: 0,
size: Some(NonZeroU64::new(stride as u64).unwrap())
}
)
}
],
label: Some("transform_bind_group"),
});
// create create the transform buffer storage
//let transforms = AVec::new(render_limits.min_uniform_buffer_offset_alignment as usize);
let transform_buffers = TransformBuffers {
max_transform_count: (render_limits.max_uniform_buffer_binding_size / mem::size_of::<glam::Mat4>() as u32) as usize,
buffer_bindgroups: vec![( 0, transform_buffer, transform_bind_group )],
just_updated: HashMap::new(),
not_updated: HashMap::new(),
dead_indices: VecDeque::new(),
next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 },
};
let camera_buffer = device.create_buffer_init( let camera_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor { &wgpu::util::BufferInitDescriptor {
@ -320,7 +266,6 @@ impl BasicRenderer {
render_limits, render_limits,
transform_buffers, transform_buffers,
transform_bind_group_layout,
inuse_camera: RenderCamera::new(size), inuse_camera: RenderCamera::new(size),
camera_buffer, camera_buffer,
@ -338,7 +283,7 @@ impl BasicRenderer {
let mut pipelines = HashMap::new(); let mut pipelines = HashMap::new();
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader, pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
vec![super::vertex::Vertex::desc(),], vec![super::vertex::Vertex::desc(),],
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout, vec![&s.texture_bind_group_layout, &s.transform_buffers.bindgroup_layout, &camera_bind_group_layout,
&s.light_buffers.bindgroup_layout]))); &s.light_buffers.bindgroup_layout])));
s.render_pipelines = pipelines; s.render_pipelines = pipelines;
@ -465,58 +410,20 @@ impl BasicRenderer {
buffer_indices, buffer_indices,
render_texture: None, render_texture: None,
texture_bindgroup: diffuse_bindgroup, texture_bindgroup: diffuse_bindgroup,
transform_index: transform_indices
} }
} }
fn expand_transform_buffers(&mut self) -> TransformBufferIndices {
let buffers = &mut self.transform_buffers;
let transform_buffer = self.device.create_buffer(
&wgpu::BufferDescriptor {
label: Some(&format!("Transform Buffer {}", buffers.buffer_bindgroups.len())),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: self.render_limits.max_uniform_buffer_binding_size as u64,
mapped_at_creation: false,
}
);
let stride = self.render_limits.min_uniform_buffer_offset_alignment as usize + mem::size_of::<glam::Mat4>();
let transform_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.transform_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: &transform_buffer,
offset: 0,
size: Some(NonZeroU64::new(stride as u64).unwrap())
}
)
}
],
label: Some("transform_bind_group"),
});
let indices = TransformBufferIndices { buffer_index: buffers.buffer_bindgroups.len(), transform_index: 0 };
buffers.buffer_bindgroups.push(( 0, transform_buffer, transform_bind_group ));
buffers.next_indices = indices;
indices
}
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed. /// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
fn process_mesh(&mut self, entity: EntityId, transform: Transform, mesh: &Mesh) -> bool { fn process_mesh(&mut self, entity: EntityId, transform: Transform, mesh: &Mesh) -> bool {
if self.transform_buffers.should_expand() {
self.transform_buffers.expand_buffers(&self.device);
}
let indices = self.transform_buffers.update_or_insert(&self.queue, &self.render_limits, let indices = self.transform_buffers.update_or_insert(&self.queue, &self.render_limits,
entity, || transform.calculate_mat4()); entity, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) ));
#[allow(clippy::map_entry)] #[allow(clippy::map_entry)]
if !self.mesh_buffers.contains_key(&mesh.uuid) { if !self.mesh_buffers.contains_key(&mesh.uuid) {
// check if the transform buffers need to be expanded
if self.transform_buffers.should_expand() {
self.expand_transform_buffers();
}
// create the mesh's buffers // create the mesh's buffers
let buffers = self.create_mesh_buffers(mesh, indices); let buffers = self.create_mesh_buffers(mesh, indices);
self.mesh_buffers.insert(mesh.uuid, buffers); self.mesh_buffers.insert(mesh.uuid, buffers);
@ -530,7 +437,6 @@ impl BasicRenderer {
impl Renderer for BasicRenderer { impl Renderer for BasicRenderer {
fn prepare(&mut self, main_world: &mut edict::World) { fn prepare(&mut self, main_world: &mut edict::World) {
let last_epoch = main_world.epoch(); let last_epoch = main_world.epoch();
debug!("Last epoch: {last_epoch:?}");
let mut alive_entities = HashSet::new(); let mut alive_entities = HashSet::new();
@ -560,6 +466,7 @@ impl Renderer for BasicRenderer {
cached cached
} }
}; };
//debug!("Transform: {:?}, comp: {:?}", cached.to_transform.translation, transform.transform.translation);
let fixed_time = match cached.last_updated_at { let fixed_time = match cached.last_updated_at {
Some(last_updated_at) => cached.cached_at - last_updated_at, Some(last_updated_at) => cached.cached_at - last_updated_at,
@ -577,7 +484,7 @@ impl Renderer for BasicRenderer {
} }
let shader = mesh.material().shader_uuid.unwrap_or(0); let shader = mesh.material().shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh.uuid, transform_val, None); let job = RenderJob::new(entity, shader, mesh.uuid, transform_val);
self.render_jobs.push_back(job); self.render_jobs.push_back(job);
} }
} }
@ -654,7 +561,7 @@ impl Renderer for BasicRenderer {
// get the mesh (containing vertices) and the buffers from storage // get the mesh (containing vertices) and the buffers from storage
let buffers = self.mesh_buffers.get(&job.mesh_buffer_id).unwrap(); let buffers = self.mesh_buffers.get(&job.mesh_buffer_id).unwrap();
// Bind the optional texture // Bind the optional texture
if let Some(tex) = buffers.texture_bindgroup.as_ref() { if let Some(tex) = buffers.texture_bindgroup.as_ref() {
render_pass.set_bind_group(0, tex, &[]); render_pass.set_bind_group(0, tex, &[]);
@ -663,10 +570,11 @@ impl Renderer for BasicRenderer {
} }
// Get the bindgroup for job's transform and bind to it using an offset. // Get the bindgroup for job's transform and bind to it using an offset.
let transform_indices = buffers.transform_index; let transform_indices = *self.transform_buffers.entity_indices(job.entity).unwrap();
let (_, _, bindgroup) = self.transform_buffers.buffer_bindgroups.get(transform_indices.buffer_index).unwrap(); let bindgroup = self.transform_buffers.bind_group(transform_indices).unwrap();
let offset = TransformBuffers::get_offset_for(&self.render_limits, transform_indices); //let bindgroup = self.transform_buffers.entity_bind_group(job.entity).unwrap();
render_pass.set_bind_group(1, bindgroup, &[ offset as u32, ]); let offset = TransformBuffers::index_offset(&self.render_limits, transform_indices) as u32;
render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
// Bind camera // Bind camera
render_pass.set_bind_group(2, &self.camera_bind_group, &[]); render_pass.set_bind_group(2, &self.camera_bind_group, &[]);

View File

@ -36,6 +36,8 @@ struct Lights {
@group(1) @binding(0) @group(1) @binding(0)
var<uniform> u_model_transform: mat4x4<f32>; var<uniform> u_model_transform: mat4x4<f32>;
@group(1) @binding(1)
var<uniform> u_model_normal_matrix: mat3x3<f32>;
@group(2) @binding(0) @group(2) @binding(0)
var<uniform> u_camera: CameraUniform; var<uniform> u_camera: CameraUniform;
@ -52,9 +54,8 @@ fn vs_main(
out.tex_coords = model.tex_coords; out.tex_coords = model.tex_coords;
out.clip_position = u_camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0); out.clip_position = u_camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz; out.world_normal = u_model_normal_matrix * model.normal;
//out.world_normal = model.normal;
var world_position: vec4<f32> = u_model_transform * vec4<f32>(model.position, 1.0); var world_position: vec4<f32> = u_model_transform * vec4<f32>(model.position, 1.0);
out.world_position = world_position.xyz; out.world_position = world_position.xyz;

View File

@ -1,75 +1,129 @@
use std::collections::{VecDeque, HashMap}; use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
use edict::EntityId; use edict::EntityId;
use wgpu::Limits; use wgpu::Limits;
#[derive(Copy, Clone, PartialEq, Eq, Debug)] use std::mem;
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) struct TransformBufferIndices { pub(crate) struct TransformBufferIndices {
pub buffer_index: usize, pub buffer_index: usize,
pub transform_index: usize, pub transform_index: usize,
} }
/// A struct representing a single transform buffer. There can be multiple of these
pub(crate) struct TransformBufferEntry {
pub len: usize,
pub transform_buf: wgpu::Buffer,
pub normal_mat_buf: wgpu::Buffer,
pub bindgroup: wgpu::BindGroup,
}
pub(crate) struct TransformBuffers { pub(crate) struct TransformBuffers {
//transform_layout: wgpu::BindGroupLayout, pub bindgroup_layout: wgpu::BindGroupLayout,
/// A vector storing the EntityId and /// A vector storing the EntityId and
pub just_updated: HashMap<EntityId, TransformBufferIndices>, pub just_updated: HashMap<EntityId, TransformBufferIndices>,
pub not_updated: HashMap<EntityId, TransformBufferIndices>, pub not_updated: HashMap<EntityId, TransformBufferIndices>,
pub dead_indices: VecDeque<TransformBufferIndices>, pub dead_indices: VecDeque<TransformBufferIndices>,
pub next_indices: TransformBufferIndices, pub next_indices: TransformBufferIndices,
/// (transform count, buffer, bindgroup) /// (transform count, buffer, bindgroup)
pub buffer_bindgroups: Vec<(usize, wgpu::Buffer, wgpu::BindGroup)>, pub buffer_bindgroups: Vec<TransformBufferEntry>,
/// The max amount of transforms in a buffer /// The max amount of transforms in a buffer
pub max_transform_count: usize, pub max_transform_count: usize,
} }
impl TransformBuffers { impl TransformBuffers {
pub fn new(device: &wgpu::Device) -> Self {
let limits = device.limits();
let bindgroup_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: true,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: None,
},
count: None,
}
],
label: Some("transform_bind_group_layout"),
});
let mut s = Self {
max_transform_count: limits.max_uniform_buffer_binding_size as usize / (mem::size_of::<glam::Mat4>() + mem::size_of::<glam::Mat3>()),
buffer_bindgroups: Vec::new(),
bindgroup_layout,
just_updated: HashMap::new(),
not_updated: HashMap::new(),
dead_indices: VecDeque::new(),
next_indices: TransformBufferIndices::default(),
};
// create the first uniform buffer
s.expand_buffers(device);
s
}
/// Update an entity's buffer with the new transform. Will panic if the entity isn't stored /// Update an entity's buffer with the new transform. Will panic if the entity isn't stored
pub fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) -> TransformBufferIndices { pub fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
let indices = self.not_updated.remove(&entity) let indices = self.not_updated.remove(&entity)
.or_else(|| self.just_updated.remove(&entity)) .or_else(|| self.just_updated.remove(&entity))
.expect("Use 'insert_entity' for new entities"); .expect("Use 'insert_entity' for new entities");
self.just_updated.insert(entity, indices); self.just_updated.insert(entity, indices);
let (_, buffer, _) = self.buffer_bindgroups.get(indices.buffer_index).unwrap(); let buffer = self.buffer_bindgroups.get(indices.buffer_index).unwrap();
queue.write_buffer(buffer, indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64, bytemuck::bytes_of(&transform)); let offset = Self::index_offset(limits, indices);
queue.write_buffer(&buffer.transform_buf, offset, bytemuck::bytes_of(&transform));
queue.write_buffer(&buffer.normal_mat_buf, offset, bytemuck::bytes_of(&normal_matrix));
indices indices
} }
/// Insert a new entity into the buffer, returns where it was stored. /// Insert a new entity into the buffer, returns where it was stored.
pub fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) -> TransformBufferIndices { pub fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
// get a dead index, or create a new one let indices = match self.dead_indices.pop_front() {
let (indices, buffer) = if let Some(index) = self.dead_indices.pop_front() { Some(indices) => indices,
let (_, buffer, _) = self.buffer_bindgroups.get(index.buffer_index).unwrap(); None => {
(index, buffer) let indices = &mut self.next_indices;
} else { let this_idx = *indices;
let indices = &mut self.next_indices; let entry = self.buffer_bindgroups.get_mut(indices.buffer_index).unwrap();
let this_idx = *indices;
let (count, buffer, _) = self.buffer_bindgroups.get_mut(indices.buffer_index).unwrap();
if *count >= self.max_transform_count { if entry.len >= self.max_transform_count {
panic!("Transform buffer is filled and 'next_indices' was not incremented! Was a new buffer created?"); panic!("Transform buffer is filled and 'next_indices' was not incremented! Was a new buffer created?");
}
entry.len += 1;
indices.transform_index += 1;
this_idx
} }
*count += 1;
indices.transform_index += 1;
(this_idx, &*buffer)
}; };
queue.write_buffer(buffer, Self::get_offset_for(limits, indices), bytemuck::bytes_of(&transform));
self.just_updated.insert(entity, indices); self.just_updated.insert(entity, indices);
indices self.update_entity(queue, limits, entity, transform, normal_matrix)
} }
/// Update or insert an entities transform /// Update or insert an entities transform
pub fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform_fn: TFn) -> TransformBufferIndices pub fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform_fn: TFn) -> TransformBufferIndices
where TFn: Fn() -> glam::Mat4 where TFn: Fn() -> (glam::Mat4, glam::Mat3)
{ {
let (tran, norm) = transform_fn();
if self.contains(entity) { if self.contains(entity) {
self.update_entity(queue, limits, entity, transform_fn()) self.update_entity(queue, limits, entity, tran, norm)
} else { } else {
self.insert_entity(queue, limits, entity, transform_fn()) self.insert_entity(queue, limits, entity, tran, norm)
} }
} }
@ -88,16 +142,97 @@ impl TransformBuffers {
self.just_updated.clear(); self.just_updated.clear();
} }
pub fn get_offset_for(limits: &Limits, indices: TransformBufferIndices) -> u64 { /// Returns the offset for the transform index in the buffer
pub fn index_offset(limits: &Limits, indices: TransformBufferIndices) -> u64 {
indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64 indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64
} }
/// Returns whether or not the transform buffers should be expanded /// Returns whether or not the transform buffers should be expanded
pub fn should_expand(&self) -> bool { pub fn should_expand(&self) -> bool {
if let Some(( count, _, _ )) = self.buffer_bindgroups.last() { if let Some(entry) = self.buffer_bindgroups.last() {
*count >= self.max_transform_count entry.len >= self.max_transform_count
} else { } else {
true true
} }
} }
/// Returns the bind group for the index
pub fn bind_group(&self, index: TransformBufferIndices) -> Option<&wgpu::BindGroup> {
self.buffer_bindgroups.get(index.buffer_index)
.map(|entry| &entry.bindgroup)
}
pub fn entity_bind_group(&self, entity: EntityId) -> Option<&wgpu::BindGroup> {
self.entity_indices(entity).and_then(|i| self.bind_group(*i))
}
/// Expand the Transform buffers by adding another uniform buffer binding
pub fn expand_buffers(&mut self, device: &wgpu::Device) {
let limits = device.limits();
let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
let transform_buffer = device.create_buffer(
&wgpu::BufferDescriptor {
label: Some(&format!("Transform Buffer {}", self.buffer_bindgroups.len())),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: max_buffer_sizes,
mapped_at_creation: false,
}
);
let normal_mat_buffer = device.create_buffer(
&wgpu::BufferDescriptor {
label: Some(&format!("Normal Matrix Buffer {}", self.buffer_bindgroups.len())),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: max_buffer_sizes,
mapped_at_creation: false,
}
);
/* let stride = limits.min_uniform_buffer_offset_alignment as u64
+ mem::size_of::<glam::Mat4>() as u64 + mem::size_of::<glam::Mat3>() as u64; */
let tran_stride = limits.min_uniform_buffer_offset_alignment as u64
+ mem::size_of::<glam::Mat4>() as u64;
let norm_stride = limits.min_uniform_buffer_offset_alignment as u64
+ mem::size_of::<glam::Mat3>() as u64;
let transform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.bindgroup_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: &transform_buffer,
offset: 0,
size: Some(NonZeroU64::new(tran_stride).unwrap())
}
)
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: &normal_mat_buffer,
offset: 0,
size: Some(NonZeroU64::new(norm_stride).unwrap())
}
)
}
],
label: Some("transform_bind_group"),
});
let entry = TransformBufferEntry {
bindgroup: transform_bind_group,
transform_buf: transform_buffer,
normal_mat_buf: normal_mat_buffer,
len: 0,
};
self.buffer_bindgroups.push(entry);
}
pub fn entity_indices(&self, entity: EntityId) -> Option<&TransformBufferIndices> {
self.just_updated.get(&entity).or_else(|| self.not_updated.get(&entity))
}
} }