Ticket 007b — build any pending BLASes and refresh the TLAS + per-instance GI data. Called once per frame, before the SSGI probe passes that sample the TLAS. No-op when HW RT is off or when nothing has changed since the last rebuild. Encodes `build_acceleration_structures` into the caller's `encoder` so the builds are ordered ahead of the trace pass without a separate queue submit. Ticket 014 V4
(
&mut self,
scene: &crate::scene::SceneGraph,
instance_handles: &[f64],
)
| 7463 | /// / `instance_custom_data` conventions stay consistent across |
| 7464 | /// HW and SW trace paths. |
| 7465 | fn rebuild_instance_data( |
| 7466 | &mut self, |
| 7467 | scene: &crate::scene::SceneGraph, |
| 7468 | instance_handles: &[f64], |
| 7469 | ) -> (u32, bool) { |
| 7470 | let instance_count = instance_handles.len() as u32; |
| 7471 | let mut resized = false; |
| 7472 | let needed_cap = instance_count.max(self.tlas_max_instances).max(64); |
| 7473 | if self.tlas_instance_data_buffer.is_none() |
| 7474 | || needed_cap > self.tlas_max_instances |
| 7475 | { |
| 7476 | let new_cap = needed_cap.next_power_of_two(); |
| 7477 | self.tlas_instance_data_buffer = Some(self.device.create_buffer(&wgpu::BufferDescriptor { |
| 7478 | label: Some("tlas_instance_data"), |
| 7479 | size: (new_cap as u64) * std::mem::size_of::<InstanceGiDataCpu>() as u64, |
| 7480 | usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, |
| 7481 | mapped_at_creation: false, |
| 7482 | })); |
| 7483 | self.tlas_max_instances = new_cap; |
| 7484 | self.probe_trace_hw_bg_cache = [None, None]; |
| 7485 | // V4 — SDF bind group also references instance_data, so |
| 7486 | // invalidate when the buffer is re-allocated. |
| 7487 | self.probe_trace_sdf_bg_cache = [None, None]; |
| 7488 | // V14 — WSRC HW bake bg also references the TLAS + |
| 7489 | // instance_data buffer; invalidate on resize. |
| 7490 | self.wsrc_bake_hw_bg_cache = None; |
| 7491 | resized = true; |
| 7492 | } |
| 7493 | |
| 7494 | let mut instance_data: Vec<InstanceGiDataCpu> = |
| 7495 | Vec::with_capacity(instance_count as usize); |
| 7496 | for &h in instance_handles { |
| 7497 | let n = scene.nodes.get(h).unwrap(); |
| 7498 | let e = n.material.emissive; |
| 7499 | let (first_slot, has_card) = match n.card_first_slot { |
| 7500 | Some(s) => (s as f32, 1.0_f32), |
| 7501 | None => (0.0, 0.0), |
| 7502 | }; |
| 7503 | instance_data.push(InstanceGiDataCpu { |
| 7504 | albedo: n.flat_albedo, |
| 7505 | emissive_luma: (e[0] + e[1] + e[2]) * (1.0 / 3.0), |
| 7506 | normal_ws: n.flat_normal_ws, |
| 7507 | _pad0: 0.0, |
| 7508 | card_slot: [first_slot, 0.0, 0.0, has_card], |
| 7509 | card_aabb_min: [n.bounds_min[0], n.bounds_min[1], n.bounds_min[2], 0.0], |
| 7510 | card_aabb_max: [n.bounds_max[0], n.bounds_max[1], n.bounds_max[2], 0.0], |
| 7511 | }); |
| 7512 | } |
| 7513 | if !instance_data.is_empty() { |
| 7514 | self.queue.write_buffer( |
| 7515 | self.tlas_instance_data_buffer.as_ref().unwrap(), |
| 7516 | 0, |
| 7517 | bytemuck::cast_slice(&instance_data), |
| 7518 | ); |
| 7519 | } |
| 7520 | (instance_count, resized) |
| 7521 | } |
| 7522 |
no test coverage detected