Fix rendering multiple entities
this is done by using a large dynamic uniform buffer for storing all transforms of entities
This commit is contained in:
parent
3068710ba4
commit
ec960b8f94
|
@ -56,6 +56,12 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned-vec"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
||||
|
||||
[[package]]
|
||||
name = "android-activity"
|
||||
version = "0.4.2"
|
||||
|
@ -1075,6 +1081,7 @@ dependencies = [
|
|||
name = "lyra-engine"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"anyhow",
|
||||
"async-std",
|
||||
"async-trait",
|
||||
|
|
|
@ -29,3 +29,4 @@ syn = "2.0.26"
|
|||
quote = "1.0.29"
|
||||
edict = "0.5.0"
|
||||
atomicell = "0.1.9"
|
||||
aligned-vec = "0.5.0"
|
||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -79,11 +79,22 @@ async fn main() {
|
|||
indices: Some(crate::render::vertex::INDICES.to_vec())
|
||||
}, Material {
|
||||
shader_id: 0,
|
||||
texture: diffuse_texture
|
||||
texture: diffuse_texture.clone()
|
||||
}),
|
||||
TransformComponent::from(Transform::from_xyz(0.005, 0.0, 0.0)),
|
||||
));
|
||||
|
||||
world.spawn((MeshComponent::new(
|
||||
render::mesh::Mesh {
|
||||
vertices: crate::render::vertex::VERTICES.to_vec(),
|
||||
indices: Some(crate::render::vertex::INDICES.to_vec())
|
||||
}, Material {
|
||||
shader_id: 0,
|
||||
texture: diffuse_texture
|
||||
}),
|
||||
TransformComponent::from(Transform::from_xyz(0.005, 0.7, 0.0)),
|
||||
));
|
||||
|
||||
let mut camera = CameraComponent::new();
|
||||
camera.transform.translation += glam::Vec3::new(0.0, 0.0, 2.0);
|
||||
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
||||
|
@ -123,23 +134,14 @@ async fn main() {
|
|||
|
||||
for transform in world.query_mut::<(&mut TransformComponent,)>().iter_mut() {
|
||||
let t = &mut transform.transform;
|
||||
//debug!("Translation: {}", t.translation);
|
||||
debug!("Translation: {}", t.translation);
|
||||
|
||||
/* t.translation += glam::Vec3::new(0.0, 0.001, 0.0);
|
||||
t.translation.x *= -1.0; */
|
||||
t.translation.x += dir_x;
|
||||
t.translation.y += dir_y;
|
||||
}
|
||||
/* for (transform,) in world.query_mut::<(TransformComponent, )>().iter() {
|
||||
let t = &mut transform.transform;
|
||||
|
||||
/* debug!("Translation: {}", t.translation);
|
||||
|
||||
t.translation += glam::Vec3::new(0.0, 0.001, 0.0);
|
||||
t.translation.x *= -1.0 */
|
||||
t.translation.x += dir_x;
|
||||
t.translation.y += dir_y;
|
||||
} */
|
||||
debug!("end");
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::mem;
|
||||
use std::num::NonZeroU64;
|
||||
use std::sync::Arc;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use aligned_vec::AVec;
|
||||
use async_std::sync::Mutex;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use atomicell::{AtomicCell, RefMut};
|
||||
use edict::{EntityId, Entities};
|
||||
use glam::Mat4;
|
||||
use tracing::{debug, warn};
|
||||
use wgpu::{BindGroup, BindGroupLayout};
|
||||
use wgpu::{BindGroup, BindGroupLayout, Limits, BufferBinding};
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::window::Window;
|
||||
|
||||
|
@ -39,7 +43,75 @@ struct RenderBufferStorage {
|
|||
|
||||
render_texture: Option<RenderTexture>,
|
||||
texture_bindgroup: Option<BindGroup>,
|
||||
texture_layout: Option<BindGroupLayout>
|
||||
texture_layout: Option<BindGroupLayout>,
|
||||
|
||||
/// The index of the transform for this entity.
|
||||
/// The tuple is structured like this: (transform index, index of transform inside the buffer)
|
||||
transform_index: TransformBufferIndices,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct TransformBufferIndices {
|
||||
buffer_index: usize,
|
||||
transform_index: usize,
|
||||
}
|
||||
|
||||
struct TransformBuffers {
|
||||
/// A vector storing the EntityId and
|
||||
just_updated: HashMap<EntityId, TransformBufferIndices>,
|
||||
not_updated: HashMap<EntityId, TransformBufferIndices>,
|
||||
dead_indices: VecDeque<TransformBufferIndices>,
|
||||
next_indices: TransformBufferIndices,
|
||||
buffer_bindgroups: Vec<(wgpu::Buffer, wgpu::BindGroup)>,
|
||||
}
|
||||
|
||||
impl TransformBuffers {
|
||||
/// Update an entity's buffer with the new transform. Will panic if the entity isn't stored
|
||||
fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) {
|
||||
let indices = self.not_updated.remove(&entity)
|
||||
.expect("Use 'insert_entity' for new entities");
|
||||
self.just_updated.insert(entity, indices);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// Insert a new entity into the buffer, returns where it was stored.
|
||||
fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) -> TransformBufferIndices {
|
||||
// get a dead index, or create a new one
|
||||
let indices = if let Some(index) = self.dead_indices.pop_front() {
|
||||
index
|
||||
} else {
|
||||
// TODO: Create new buffer if this one is full
|
||||
let indices = &mut self.next_indices;
|
||||
let new = indices.clone();
|
||||
indices.transform_index += 1;
|
||||
|
||||
new
|
||||
};
|
||||
|
||||
let (buffer, _) = self.buffer_bindgroups.get(indices.buffer_index).unwrap();
|
||||
queue.write_buffer(buffer, Self::get_offset_for(limits, indices), bytemuck::bytes_of(&transform));
|
||||
|
||||
self.just_updated.insert(entity, indices);
|
||||
indices
|
||||
}
|
||||
|
||||
/// Collect the dead entities, mark entities and not updated for next updates.
|
||||
fn tick(&mut self) {
|
||||
// take the dead entities, these were ones that were not updated this tick
|
||||
let dead: VecDeque<TransformBufferIndices> = self.not_updated.values()
|
||||
.map(|t| t.clone()).collect();
|
||||
self.dead_indices = dead;
|
||||
|
||||
// swap just_updated into not_updated
|
||||
self.not_updated = self.just_updated.clone();
|
||||
self.just_updated.clear();
|
||||
}
|
||||
|
||||
fn get_offset_for(limits: &Limits, indices: TransformBufferIndices) -> u64 {
|
||||
indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BasicRenderer {
|
||||
|
@ -57,8 +129,11 @@ pub struct BasicRenderer {
|
|||
|
||||
buffer_storage: HashMap<EntityId, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
||||
|
||||
transform_buffer: wgpu::Buffer,
|
||||
transform_bind_group: wgpu::BindGroup,
|
||||
transform_buffers: TransformBuffers,
|
||||
transform_bind_group_layout: BindGroupLayout,
|
||||
//transform_bind_group: wgpu::BindGroup,
|
||||
|
||||
render_limits: Limits,
|
||||
|
||||
inuse_camera: RenderCamera,
|
||||
camera_buffer: wgpu::Buffer,
|
||||
|
@ -102,6 +177,7 @@ impl BasicRenderer {
|
|||
None,
|
||||
).await.unwrap();
|
||||
|
||||
let render_limits = device.limits();
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
|
||||
let present_mode = surface_caps.present_modes[0]; /* match surface_caps.present_modes.contains(&wgpu::PresentMode::Immediate) {
|
||||
|
@ -158,14 +234,6 @@ impl BasicRenderer {
|
|||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)),
|
||||
});
|
||||
|
||||
let transform_buffer = device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Transform Buffer"),
|
||||
contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
let transform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
|
@ -173,7 +241,7 @@ impl BasicRenderer {
|
|||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
|
@ -182,17 +250,43 @@ impl BasicRenderer {
|
|||
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: transform_buffer.as_entire_binding(),
|
||||
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 {
|
||||
buffer_bindgroups: vec![( 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(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Camera Buffer"),
|
||||
|
@ -250,8 +344,9 @@ impl BasicRenderer {
|
|||
render_jobs: VecDeque::new(),
|
||||
buffer_storage: HashMap::new(),
|
||||
|
||||
transform_buffer,
|
||||
transform_bind_group,
|
||||
render_limits,
|
||||
transform_buffers,
|
||||
transform_bind_group_layout,
|
||||
|
||||
inuse_camera: RenderCamera::new(size),
|
||||
camera_buffer,
|
||||
|
@ -259,7 +354,7 @@ impl BasicRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_model_buffers(&mut self, model_2d: &MeshComponent) -> RenderBufferStorage {
|
||||
fn create_model_buffers(&mut self, model_2d: &MeshComponent, transform_indices: TransformBufferIndices) -> RenderBufferStorage {
|
||||
let mesh = &model_2d.mesh;
|
||||
|
||||
let vertex_buffer = self.device.create_buffer_init(
|
||||
|
@ -340,24 +435,34 @@ impl BasicRenderer {
|
|||
render_texture: None,
|
||||
texture_layout: None,
|
||||
texture_bindgroup: Some(diffuse_bind_group),
|
||||
transform_index: transform_indices
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for BasicRenderer {
|
||||
fn prepare(&mut self, main_world: &mut edict::World) {
|
||||
|
||||
// render_limits.max_uniform_buffer_binding_size
|
||||
for (entity, model, transform) in main_world.query::<(Entities, &MeshComponent, &TransformComponent)>().iter() {
|
||||
// Create the render job and push it to the queue
|
||||
let job = RenderJob::new(model.mesh.clone(), model.material.clone(), entity, transform.transform, None);
|
||||
self.render_jobs.push_back(job);
|
||||
|
||||
if self.buffer_storage.get(&entity).is_none() {
|
||||
let buffers = self.create_model_buffers(model);
|
||||
let indices = self.transform_buffers.insert_entity(&self.queue, &self.render_limits,
|
||||
entity, transform.transform.calculate_mat4());
|
||||
|
||||
let buffers = self.create_model_buffers(model, indices);
|
||||
self.buffer_storage.insert(entity, buffers);
|
||||
} else {
|
||||
self.transform_buffers.update_entity(&self.queue, &self.render_limits,
|
||||
entity, transform.transform.calculate_mat4());
|
||||
}
|
||||
}
|
||||
|
||||
// collect dead entities
|
||||
self.transform_buffers.tick();
|
||||
|
||||
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
|
||||
let view_proj = self.inuse_camera.update_view_projection(camera);
|
||||
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[view_proj.clone()]));
|
||||
|
@ -393,22 +498,28 @@ impl Renderer for BasicRenderer {
|
|||
// Pop off jobs from the queue as they're being processed
|
||||
while let Some(job) = self.render_jobs.pop_front() {
|
||||
if let Some(pipeline) = self.render_pipelines.get(&job.material().shader_id) {
|
||||
// specify to use this pipeline
|
||||
render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
|
||||
|
||||
// get the mesh (containing vertices) and the buffers from storage
|
||||
let mesh = job.mesh();
|
||||
let buffers = self.buffer_storage.get(&job.entity()).unwrap();
|
||||
|
||||
// Bind the optional texture
|
||||
if let Some(tex) = buffers.texture_bindgroup.as_ref() {
|
||||
render_pass.set_bind_group(0, &tex, &[]);
|
||||
}
|
||||
|
||||
// Update transform buffer, and bind to the bind group
|
||||
self.queue.write_buffer(&self.transform_buffer, 0, bytemuck::cast_slice(&[job.transform().calculate_mat4()]));
|
||||
render_pass.set_bind_group(1, &self.transform_bind_group, &[]);
|
||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||
let transform_indices = buffers.transform_index;
|
||||
let (_, bindgroup) = self.transform_buffers.buffer_bindgroups.get(transform_indices.buffer_index).unwrap();
|
||||
let offset = TransformBuffers::get_offset_for(&self.render_limits, transform_indices);
|
||||
render_pass.set_bind_group(1, bindgroup, &[ offset as u32, ]);
|
||||
|
||||
// There will always be a camera (hopefully)
|
||||
// Bind camera
|
||||
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
||||
|
||||
// if this mesh uses indices, use them to draw the mesh
|
||||
if let Some(indices) = buffers.buffer_indices.as_ref() {
|
||||
let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug
|
||||
|
||||
|
|
Loading…
Reference in New Issue