Skip to content

Commit

Permalink
Support node hierarchy in the scene
Browse files Browse the repository at this point in the history
  • Loading branch information
kvark committed Oct 30, 2023
1 parent a7efd1b commit b521df2
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 164 deletions.
21 changes: 21 additions & 0 deletions blade-graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@
pub use naga::{StorageAccess, VectorSize};
pub type Transform = mint::RowMatrix3x4<f32>;

pub const IDENTITY_TRANSFORM: Transform = mint::RowMatrix3x4 {
x: mint::Vector4 {
x: 1.0,
y: 0.0,
z: 0.0,
w: 0.0,
},
y: mint::Vector4 {
x: 0.0,
y: 1.0,
z: 0.0,
w: 0.0,
},
z: mint::Vector4 {
x: 0.0,
y: 0.0,
z: 1.0,
w: 0.0,
},
};

#[cfg_attr(
all(not(vulkan), not(gles), any(target_os = "ios", target_os = "macos")),
path = "metal/mod.rs"
Expand Down
23 changes: 11 additions & 12 deletions blade-render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,20 @@ pub struct Camera {
pub depth: f32,
}

pub struct Object {
pub model: blade_asset::Handle<Model>,
pub struct Node {
pub label: String,
pub transform: blade_graphics::Transform,
pub models: Vec<blade_asset::Handle<Model>>,
pub children: Vec<Node>,
}

impl From<blade_asset::Handle<Model>> for Object {
fn from(model: blade_asset::Handle<Model>) -> Self {
impl Default for Node {
fn default() -> Self {
Self {
model,
transform: [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
]
.into(),
label: String::new(),
transform: blade_graphics::IDENTITY_TRANSFORM,
models: Vec::new(),
children: Vec::new(),
}
}
}
Expand All @@ -77,7 +76,7 @@ impl Default for PostProcessing {

#[derive(Default)]
pub struct Scene {
pub objects: Vec<Object>,
pub roots: Vec<Node>,
pub environment_map: Option<blade_asset::Handle<Texture>>,
pub post_processing: PostProcessing,
}
248 changes: 153 additions & 95 deletions blade-render/src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod dummy;
mod env_map;
mod scene;

pub use dummy::DummyResources;
pub use env_map::EnvironmentMap;
Expand Down Expand Up @@ -872,7 +871,7 @@ impl Renderer {
gpu.destroy_buffer(self.debug.entry_buffer);
}

pub fn merge_scene(&mut self, scene: super::Scene) {
pub fn change_scene(&mut self, scene: super::Scene) {
self.scene = scene;
self.is_tlas_dirty = true;
}
Expand Down Expand Up @@ -1000,89 +999,60 @@ impl Renderer {
self.debug_view = debug_view;
}

/// Prepare to render a frame.
#[profiling::function]
#[allow(clippy::too_many_arguments)]
pub fn prepare(
pub fn rebuild_acceleration_structure(
&mut self,
command_encoder: &mut blade_graphics::CommandEncoder,
camera: &super::Camera,
asset_hub: &crate::AssetHub,
gpu: &blade_graphics::Context,
temp_buffers: &mut Vec<blade_graphics::Buffer>,
temp_acceleration_structures: &mut Vec<blade_graphics::AccelerationStructure>,
enable_debug_draw: bool,
accumulate_variance: bool,
reset_reservoirs: bool,
) {
if self.is_tlas_dirty {
self.is_tlas_dirty = false;
if self.acceleration_structure != blade_graphics::AccelerationStructure::default() {
temp_buffers.push(self.hit_buffer);
temp_acceleration_structures.push(self.acceleration_structure);
}

let (tlas, geometry_count) = self.scene.build_top_level_acceleration_structure(
command_encoder,
&asset_hub.models,
gpu,
temp_buffers,
);
self.acceleration_structure = tlas;
log::info!("Preparing ray tracing with {geometry_count} geometries in total");
let mut transfers = command_encoder.transfer();

{
// init the debug buffer
let data = [2, 0, 0, 0, self.debug.capacity, 0];
let size = 4 * data.len() as u64;
let staging = gpu.create_buffer(blade_graphics::BufferDesc {
name: "debug buf staging",
size,
memory: blade_graphics::Memory::Upload,
});
unsafe {
ptr::write(staging.data() as *mut _, data);
}
transfers.copy_buffer_to_buffer(staging.into(), self.debug.buffer.into(), size);
temp_buffers.push(staging);
}
fn extract_matrix3(transform: blade_graphics::Transform) -> glam::Mat3 {
let col_mx = mint::ColumnMatrix3x4::from(transform);
glam::Mat3::from_cols(col_mx.x.into(), col_mx.y.into(), col_mx.z.into())
}

self.vertex_buffers.clear();
self.index_buffers.clear();
self.textures.clear();
let dummy_white = self.textures.alloc(self.dummy.white_view);
let dummy_black = self.textures.alloc(self.dummy.black_view);

if geometry_count != 0 {
let hit_staging = {
// init the hit buffer
let hit_size = (geometry_count as usize * mem::size_of::<HitEntry>()) as u64;
self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc {
name: "hit entries",
size: hit_size,
memory: blade_graphics::Memory::Device,
});
let staging = gpu.create_buffer(blade_graphics::BufferDesc {
name: "hit staging",
size: hit_size,
memory: blade_graphics::Memory::Upload,
});
temp_buffers.push(staging);
transfers.copy_buffer_to_buffer(staging.at(0), self.hit_buffer.at(0), hit_size);
staging
self.vertex_buffers.clear();
self.index_buffers.clear();
self.textures.clear();
let dummy_white = self.textures.alloc(self.dummy.white_view);
let dummy_black = self.textures.alloc(self.dummy.black_view);

let mut instances = Vec::new();
let mut blases = Vec::new();
let mut hit_entries = Vec::new();
let mut texture_indices = HashMap::new();
let mut node_queue = vec![(glam::Mat4::IDENTITY, self.scene.roots.as_slice())];

while let Some((parent_transform, nodes)) = node_queue.pop() {
for node in nodes {
let transform = parent_transform
* glam::Mat4 {
x_axis: node.transform.x.into(),
y_axis: node.transform.y.into(),
z_axis: node.transform.z.into(),
w_axis: glam::Vec4::W,
};
let m3_object = glam::Mat3 {
x_axis: transform.x_axis.truncate(),
y_axis: transform.y_axis.truncate(),
z_axis: transform.z_axis.truncate(),
};

fn extract_matrix3(transform: blade_graphics::Transform) -> glam::Mat3 {
let col_mx = mint::ColumnMatrix3x4::from(transform);
glam::Mat3::from_cols(col_mx.x.into(), col_mx.y.into(), col_mx.z.into())
}
for &model_handle in node.models.iter() {
let model = &asset_hub.models[model_handle];
instances.push(blade_graphics::AccelerationStructureInstance {
acceleration_structure_index: blases.len() as u32,
transform: blade_graphics::Transform {
x: transform.x_axis.into(),
y: transform.y_axis.into(),
z: transform.z_axis.into(),
},
mask: 0xFF,
custom_index: hit_entries.len() as u32,
});
blases.push(model.acceleration_structure);

let mut texture_indices = HashMap::new();
let mut geometry_index = 0;
for object in self.scene.objects.iter() {
let m3_object = extract_matrix3(object.transform);
let model = &asset_hub.models[object.model];
for geometry in model.geometries.iter() {
let material = &model.materials[geometry.material_index];
let vertex_offset = geometry.vertex_range.start as u64
Expand Down Expand Up @@ -1154,29 +1124,100 @@ impl Renderer {
finish_pad: [0; 1],
};

log::debug!("Entry[{geometry_index}] = {hit_entry:?}");
unsafe {
ptr::write(
(hit_staging.data() as *mut HitEntry).add(geometry_index),
hit_entry,
);
}
geometry_index += 1;
log::debug!("Entry[{}] = {:?}", hit_entries.len(), hit_entry);
hit_entries.push(hit_entry);
}
}
assert_eq!(geometry_index, geometry_count as usize);

self.texture_resource_lookup.clear();
for (handle, res_id) in texture_indices {
self.texture_resource_lookup.insert(res_id, handle);
}
} else {
self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc {
name: "hit entries",
size: mem::size_of::<HitEntry>() as u64,
memory: blade_graphics::Memory::Device,
});
node_queue.push((transform, &node.children));
}
}

self.texture_resource_lookup.clear();
for (handle, res_id) in texture_indices {
self.texture_resource_lookup.insert(res_id, handle);
}

log::info!(
"Preparing ray tracing with {} geometries in total",
hit_entries.len()
);
let hit_size = (hit_entries.len().max(1) * mem::size_of::<HitEntry>()) as u64;
//TODO: reuse the hit buffer
if self.hit_buffer != blade_graphics::Buffer::default() {
temp_buffers.push(self.hit_buffer);
}
self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc {
name: "hit entries",
size: hit_size,
memory: blade_graphics::Memory::Device,
});
let staging = gpu.create_buffer(blade_graphics::BufferDesc {
name: "hit staging",
size: hit_size,
memory: blade_graphics::Memory::Upload,
});
temp_buffers.push(staging);
let mut transfers = command_encoder.transfer();
transfers.copy_buffer_to_buffer(staging.at(0), self.hit_buffer.at(0), hit_size);
unsafe {
ptr::copy_nonoverlapping(
hit_entries.as_ptr(),
staging.data() as *mut _,
hit_entries.len(),
);
}

// Needs to be a separate encoder in order to force synchronization
let sizes = gpu.get_top_level_acceleration_structure_sizes(instances.len() as u32);
self.acceleration_structure =
gpu.create_acceleration_structure(blade_graphics::AccelerationStructureDesc {
name: "TLAS",
ty: blade_graphics::AccelerationStructureType::TopLevel,
size: sizes.data,
});
let instance_buf = gpu.create_acceleration_structure_instance_buffer(&instances, &blases);
let scratch_buf = gpu.create_buffer(blade_graphics::BufferDesc {
name: "TLAS scratch",
size: sizes.scratch,
memory: blade_graphics::Memory::Device,
});

let mut tlas_encoder = command_encoder.acceleration_structure();

Check failure on line 1186 in blade-render/src/render/mod.rs

View workflow job for this annotation

GitHub Actions / build (MacOS, macos-latest, x86_64-apple-darwin)

cannot borrow `*command_encoder` as mutable more than once at a time
tlas_encoder.build_top_level(
self.acceleration_structure,
&blases,
instances.len() as u32,
instance_buf.at(0),
scratch_buf.at(0),
);

temp_buffers.push(instance_buf);
temp_buffers.push(scratch_buf);
}

/// Prepare to render a frame.
#[profiling::function]
#[allow(clippy::too_many_arguments)]
pub fn prepare(
&mut self,
command_encoder: &mut blade_graphics::CommandEncoder,
camera: &super::Camera,
asset_hub: &crate::AssetHub,
gpu: &blade_graphics::Context,
temp_buffers: &mut Vec<blade_graphics::Buffer>,
temp_acceleration_structures: &mut Vec<blade_graphics::AccelerationStructure>,
enable_debug_draw: bool,
accumulate_variance: bool,
reset_reservoirs: bool,
) {
let reset_debug_buffer = self.frame_index == 0;
if self.is_tlas_dirty {
self.is_tlas_dirty = false;
if self.acceleration_structure != blade_graphics::AccelerationStructure::default() {
temp_acceleration_structures.push(self.acceleration_structure);
}
self.rebuild_acceleration_structure(command_encoder, asset_hub, gpu, temp_buffers);
}

if let Some(handle) = self.scene.environment_map {
Expand All @@ -1186,13 +1227,29 @@ impl Renderer {
};

let mut transfer = command_encoder.transfer();
if enable_debug_draw {

if reset_debug_buffer {
// init the debug buffer
let data = [2, 0, 0, 0, self.debug.capacity, 0];
let size = 4 * data.len() as u64;
let staging = gpu.create_buffer(blade_graphics::BufferDesc {
name: "debug buf staging",
size,
memory: blade_graphics::Memory::Upload,
});
unsafe {
ptr::write(staging.data() as *mut _, data);
}
transfer.copy_buffer_to_buffer(staging.into(), self.debug.buffer.into(), size);
temp_buffers.push(staging);
} else if enable_debug_draw {
// reset the debug line count
transfer.fill_buffer(self.debug.buffer.at(4), 4, 0);
transfer.fill_buffer(self.debug.buffer.at(20), 4, 1);
} else {
transfer.fill_buffer(self.debug.buffer.at(20), 4, 0);
}

if reset_reservoirs || !accumulate_variance {
transfer.fill_buffer(
self.debug.buffer.at(32),
Expand All @@ -1214,8 +1271,9 @@ impl Renderer {
self.debug.entry_buffer.into(),
mem::size_of::<DebugEntry>() as u64,
);

if reset_reservoirs {
if !enable_debug_draw {
if !enable_debug_draw && !reset_debug_buffer {
transfer.fill_buffer(self.debug.buffer.at(4), 4, 0);
}
let total_reservoirs = self.screen_size.width as u64 * self.screen_size.height as u64;
Expand Down
Loading

0 comments on commit b521df2

Please sign in to comment.