From dd4128123a7ade553e21b181772e8b93905d633d Mon Sep 17 00:00:00 2001 From: davids91 Date: Fri, 13 Sep 2024 20:11:03 +0200 Subject: [PATCH 01/19] Initial experiments and doc changes to prepare for cache update --- Cargo.toml | 5 +- assets/shaders/viewport_render.wgsl | 65 ++-- examples/minecraft.rs | 18 +- src/octree/mod.rs | 4 +- src/octree/raytracing/bevy/data.rs | 34 +- src/octree/raytracing/bevy/mod.rs | 417 ++++++++++++++++++--- src/octree/raytracing/bevy/types.rs | 77 +++- src/octree/raytracing/mod.rs | 2 +- src/octree/raytracing/raytracing_on_cpu.rs | 4 +- src/octree/update.rs | 4 +- 10 files changed, 522 insertions(+), 108 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7143be7..6e599af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,11 @@ authors = ["Dávid Tóth "] license = "MIT OR Apache-2.0" [features] -default = ["dot_vox_support"] +default = ["bevy_wgpu","dot_vox_support"] raytracing = ["dep:image", "dep:show-image"] serialization = ["dep:serde"] dot_vox_support = ["dep:dot_vox", "dep:nalgebra"] -bevy_wgpu = ["raytracing", "dep:bevy", "dep:iyes_perf_ui"] +bevy_wgpu = ["raytracing", "dep:bevy", "dep:iyes_perf_ui", "dep:crossbeam"] [dependencies] num-traits = "0.2.19" @@ -18,6 +18,7 @@ serde = { version = "1.0.183", features = ["derive"], optional = true } bendy = { git = "https://github.com/davids91/bendy.git" , features = ["std", "serde"]} dot_vox = { version = "5.1.1", optional = true } nalgebra = { version = "0.33.0", optional = true } +crossbeam = { version = "0.8.4", optional = true } # for example cpu_render image = { version = "0.25.1", optional = true } diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 0a618b1..12dfe78 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -389,21 +389,21 @@ fn traverse_brick( - brick_bounds.min_position ); current_index = vec3i( - clamp(i32(current_index.x), 0, i32(octreeMetaData.voxel_brick_dim - 1)), - clamp(i32(current_index.y), 0, i32(octreeMetaData.voxel_brick_dim - 1)), - clamp(i32(current_index.z), 0, i32(octreeMetaData.voxel_brick_dim - 1)) + clamp(i32(current_index.x), 0, i32(octree_meta_data.voxel_brick_dim - 1)), + clamp(i32(current_index.y), 0, i32(octree_meta_data.voxel_brick_dim - 1)), + clamp(i32(current_index.z), 0, i32(octree_meta_data.voxel_brick_dim - 1)) ); var current_bounds = Cube( ( brick_bounds.min_position - + vec3f(current_index) * (brick_bounds.size / f32(octreeMetaData.voxel_brick_dim)) + + vec3f(current_index) * (brick_bounds.size / f32(octree_meta_data.voxel_brick_dim)) ), - (brick_bounds.size / f32(octreeMetaData.voxel_brick_dim)) + (brick_bounds.size / f32(octree_meta_data.voxel_brick_dim)) ); var mapped_index = position_in_bitmap_64bits( - vec3u(current_index), octreeMetaData.voxel_brick_dim + vec3u(current_index), octree_meta_data.voxel_brick_dim ); if ( 0 == ( @@ -419,21 +419,21 @@ fn traverse_brick( } var prev_bitmap_position_full_resolution = vec3u( - vec3f(current_index) * (4. / f32(octreeMetaData.voxel_brick_dim)) + vec3f(current_index) * (4. / f32(octree_meta_data.voxel_brick_dim)) ); loop{ if current_index.x < 0 - || current_index.x >= i32(octreeMetaData.voxel_brick_dim) + || current_index.x >= i32(octree_meta_data.voxel_brick_dim) || current_index.y < 0 - || current_index.y >= i32(octreeMetaData.voxel_brick_dim) + || current_index.y >= i32(octree_meta_data.voxel_brick_dim) || current_index.z < 0 - || current_index.z >= i32(octreeMetaData.voxel_brick_dim) + || current_index.z >= i32(octree_meta_data.voxel_brick_dim) { return BrickHit(false, vec3u()); } let bitmap_position_full_resolution = vec3u( - vec3f(current_index) * (4. / f32(octreeMetaData.voxel_brick_dim)) + vec3f(current_index) * (4. / f32(octree_meta_data.voxel_brick_dim)) ); if( bitmap_position_full_resolution.x != prev_bitmap_position_full_resolution.x @@ -458,7 +458,7 @@ fn traverse_brick( mapped_index = u32(flat_projection( vec3u(current_index), - vec2u(octreeMetaData.voxel_brick_dim, octreeMetaData.voxel_brick_dim) + vec2u(octree_meta_data.voxel_brick_dim, octree_meta_data.voxel_brick_dim) )); if (brick_index_start + mapped_index) >= arrayLength(&voxels) { @@ -477,7 +477,7 @@ fn traverse_brick( ); current_bounds.min_position = ( current_bounds.min_position - + vec3f(step) * (brick_bounds.size / f32(octreeMetaData.voxel_brick_dim)) + + vec3f(step) * (brick_bounds.size / f32(octree_meta_data.voxel_brick_dim)) ); current_index = current_index + vec3i(round(step)); } @@ -500,7 +500,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ var node_stack: array; var node_stack_meta: u32 = 0; - var current_bounds = Cube(vec3(0.), f32(octreeMetaData.octree_size)); + var current_bounds = Cube(vec3(0.), f32(octree_meta_data.octree_size)); var ray_current_distance: f32 = 0.0; var target_octant = OOB_OCTANT; var current_node_key = EMPTY_MARKER; @@ -516,15 +516,15 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ while target_octant != OOB_OCTANT { current_node_key = OCTREE_ROOT_NODE_KEY; - current_bounds = Cube(vec3(0.), f32(octreeMetaData.octree_size)); + current_bounds = Cube(vec3(0.), f32(octree_meta_data.octree_size)); node_stack_push(&node_stack, &node_stack_meta, OCTREE_ROOT_NODE_KEY); while(!node_stack_is_empty(node_stack_meta)) { var leaf_miss = false; if is_leaf(nodes[current_node_key].sized_node_meta) { let leaf_brick_hit = traverse_brick( ray, &ray_current_distance, nodes[current_node_key].voxels_start_at, - children_buffer[nodes[current_node_key].children_starts_at], - children_buffer[nodes[current_node_key].children_starts_at + 1], + node_children[nodes[current_node_key].children_starts_at], + node_children[nodes[current_node_key].children_starts_at + 1], current_bounds, ray_scale_factors, direction_lut_index ); if leaf_brick_hit.hit == true { @@ -532,10 +532,10 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ nodes[current_node_key].voxels_start_at + u32(flat_projection( leaf_brick_hit.index, - vec2u(octreeMetaData.voxel_brick_dim, octreeMetaData.voxel_brick_dim) + vec2u(octree_meta_data.voxel_brick_dim, octree_meta_data.voxel_brick_dim) )) ); - current_bounds.size = round(current_bounds.size / f32(octreeMetaData.voxel_brick_dim)); + current_bounds.size = round(current_bounds.size / f32(octree_meta_data.voxel_brick_dim)); current_bounds.min_position = current_bounds.min_position + vec3f(leaf_brick_hit.index) * current_bounds.size; return OctreeRayIntersection( @@ -587,7 +587,9 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ } var target_bounds = child_bounds_for(current_bounds, target_octant); - var target_child_key = children_buffer[nodes[current_node_key].children_starts_at + target_octant]; + var target_child_key = node_children[ + nodes[current_node_key].children_starts_at + target_octant + ]; if ( target_child_key < arrayLength(&nodes) //!crate::object_pool::key_is_valid && 0 != ( @@ -617,7 +619,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ target_octant = step_octant(target_octant, step_vec); if OOB_OCTANT != target_octant { target_bounds = child_bounds_for(current_bounds, target_octant); - target_child_key = children_buffer[ + target_child_key = node_children[ nodes[current_node_key].children_starts_at + target_octant ]; } @@ -651,15 +653,15 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ + step_vec * current_bounds.size ); if( - current_octant_center.x < f32(octreeMetaData.octree_size) - && current_octant_center.y < f32(octreeMetaData.octree_size) - && current_octant_center.z < f32(octreeMetaData.octree_size) + current_octant_center.x < f32(octree_meta_data.octree_size) + && current_octant_center.y < f32(octree_meta_data.octree_size) + && current_octant_center.z < f32(octree_meta_data.octree_size) && current_octant_center.x > 0. && current_octant_center.y > 0. && current_octant_center.z > 0. ) { target_octant = hash_region( - current_octant_center, f32(octreeMetaData.octree_size) / 2. + current_octant_center, f32(octree_meta_data.octree_size) / 2. ); } else { target_octant = OOB_OCTANT; @@ -710,13 +712,13 @@ var output_texture: texture_storage_2d; var viewport: Viewport; @group(1) @binding(0) -var octreeMetaData: OctreeMetaData; +var octree_meta_data: OctreeMetaData; @group(1) @binding(1) var nodes: array; @group(1) @binding(2) -var children_buffer: array; +var node_children: array; @group(1) @binding(3) var voxels: array; @@ -724,6 +726,9 @@ var voxels: array; @group(1) @binding(4) var color_palette: array; +@group(1) @binding(5) +var data_meta_bytes: array; + @compute @workgroup_size(8, 8, 1) fn update( @builtin(global_invocation_id) invocation_id: vec3, @@ -762,12 +767,12 @@ fn update( /*// +++ DEBUG +++ // Display the xyz axes let root_hit = cube_intersect_ray( - Cube(vec3(0.,0.,0.), f32(octreeMetaData.octree_size)), ray + Cube(vec3(0.,0.,0.), f32(octree_meta_data.octree_size)), ray ); if root_hit.hit == true { if root_hit. impact_hit == true { - let axes_length = f32(octreeMetaData.octree_size) / 2.; - let axes_width = f32(octreeMetaData.octree_size) / 50.; + let axes_length = f32(octree_meta_data.octree_size) / 2.; + let axes_width = f32(octree_meta_data.octree_size) / 50.; let entry_point = point_in_ray_at_distance(ray, root_hit.impact_distance); if entry_point.x < axes_length && entry_point.y < axes_width && entry_point.z < axes_width { rgb_result.r = 1.; diff --git a/examples/minecraft.rs b/examples/minecraft.rs index 3b2bed4..fe0c181 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -7,7 +7,8 @@ use bevy::{prelude::*, window::WindowPlugin}; #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ raytracing::{ - bevy::create_viewing_glass, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, + bevy::create_viewing_glass, ShocoVoxRenderData, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, + Viewport, }, Albedo, V3c, }; @@ -64,11 +65,6 @@ fn setup(mut commands: Commands, images: ResMut>) { tree.save(&tree_path).ok().unwrap(); } - let origin = V3c::new( - tree.get_size() as f32 * 2., - tree.get_size() as f32 / 2., - tree.get_size() as f32 * -2., - ); commands.spawn(DomePosition { yaw: 0., roll: 0., @@ -152,9 +148,14 @@ fn rotate_camera( #[cfg(feature = "bevy_wgpu")] fn handle_zoom( keys: Res>, - mut viewing_glass: ResMut, mut angles_query: Query<&mut DomePosition>, + svx_data: Option>, ) { + if let Some(ref svx_data) = svx_data { + if svx_data.is_changed() { + println!("changed!! --> {:?}", svx_data.node_children[0..2].to_vec()); + } + } const ADDITION: f32 = 0.05; let angle_update_fn = |angle, delta| -> f32 { let new_angle = angle + delta; @@ -184,6 +185,9 @@ fn handle_zoom( if keys.pressed(KeyCode::PageDown) { angles_query.single_mut().radius *= 1.1; } + if keys.pressed(KeyCode::Tab) { + svx_data.unwrap().do_the_thing = true; + } } #[cfg(not(feature = "bevy_wgpu"))] diff --git a/src/octree/mod.rs b/src/octree/mod.rs index 9293b4e..f476b5d 100644 --- a/src/octree/mod.rs +++ b/src/octree/mod.rs @@ -81,7 +81,7 @@ where /// Provides immutable reference to the data, if there is any at the given position pub fn get(&self, position: &V3c) -> Option<&T> { let mut current_bounds = Cube::root_bounds(self.octree_size as f32); - let mut current_node_key = Octree::::ROOT_NODE_KEY as usize; + let mut current_node_key = Self::ROOT_NODE_KEY as usize; let position = V3c::from(*position); if !bound_contains(¤t_bounds, &position) { return None; @@ -118,7 +118,7 @@ where /// Provides mutable reference to the data, if there is any at the given position pub fn get_mut(&mut self, position: &V3c) -> Option<&mut T> { let mut current_bounds = Cube::root_bounds(self.octree_size as f32); - let mut current_node_key = Octree::::ROOT_NODE_KEY as usize; + let mut current_node_key = Self::ROOT_NODE_KEY as usize; let position = V3c::from(*position); if !bound_contains(¤t_bounds, &position) { return None; diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 1763db5..1b51c2a 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -44,7 +44,7 @@ where } pub fn create_bevy_view(&self) -> ShocoVoxRenderData { - let meta = OctreeMetaData { + let octree_meta = OctreeMetaData { octree_size: self.octree_size, voxel_brick_dim: DIM as u32, ambient_light_color: V3c::new(1., 1., 1.), @@ -56,9 +56,10 @@ where }; let mut nodes = Vec::new(); - let mut children_buffer = Vec::new(); + let mut node_children = Vec::new(); let mut voxels = Vec::new(); let mut color_palette = Vec::new(); + let mut data_meta_bytes = Vec::new(); // Build up Nodes let mut map_to_node_index_in_nodes_buffer = HashMap::new(); @@ -66,7 +67,7 @@ where if self.nodes.key_is_valid(i) { map_to_node_index_in_nodes_buffer.insert(i as usize, nodes.len()); nodes.push(SizedNode { - sized_node_meta: self.create_meta(i), + // sized_node_meta: self.create_meta(i), children_start_at: empty_marker(), voxels_start_at: empty_marker(), }); @@ -80,7 +81,7 @@ where continue; } nodes[map_to_node_index_in_nodes_buffer[&i]].children_start_at = - children_buffer.len() as u32; + node_children.len() as u32; if let NodeContent::Leaf(data) = self.nodes.get(i) { debug_assert!(matches!( self.node_children[i].content, @@ -90,7 +91,7 @@ where NodeChildrenArray::OccupancyBitmap(bitmap) => bitmap, _ => panic!("Found Leaf Node without occupancy bitmap!"), }; - children_buffer.extend_from_slice(&[ + node_children.extend_from_slice(&[ (occupied_bits & 0x00000000FFFFFFFF) as u32, ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32, ]); @@ -124,22 +125,37 @@ where if *child_index != self.node_children[i].empty_marker { debug_assert!(map_to_node_index_in_nodes_buffer .contains_key(&(*child_index as usize))); - children_buffer.push( + node_children.push( map_to_node_index_in_nodes_buffer[&(*child_index as usize)] as u32, ); } else { - children_buffer.push(*child_index); + node_children.push(*child_index); } } } } ShocoVoxRenderData { - meta, + do_the_thing: false, + data_meta_bytes, + // root_node: SizedNode { + // sized_node_meta: self.create_meta(Self::ROOT_NODE_KEY as usize), + // children_start_at: empty_marker(), + // voxels_start_at: empty_marker(), + // }, + octree_meta, nodes, - children_buffer, + node_children, voxels, color_palette, } } + + pub(crate) fn insert_elements_into_cache( + &self, + render_data: &mut ShocoVoxRenderData, + requested_nodes: Vec, + ) { + //TODO: find the first unused element, and overwrite it with the item + } } diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index cbdb072..0cc3291 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -12,22 +12,25 @@ use crate::octree::raytracing::bevy::types::{ use bevy::{ app::{App, Plugin}, asset::{AssetServer, Assets, Handle}, - ecs::system::{Res, ResMut}, - ecs::world::{FromWorld, World}, - prelude::IntoSystemConfigs, + ecs::{ + system::{Res, ResMut}, + world::{FromWorld, World}, + }, + prelude::{ExtractSchedule, IntoSystemConfigs}, render::{ extract_resource::ExtractResourcePlugin, prelude::Image, render_asset::{RenderAssetUsages, RenderAssets}, - render_graph, - render_graph::RenderGraph, + render_graph::{self, RenderGraph}, render_resource::{ - AsBindGroup, CachedPipelineState, ComputePassDescriptor, ComputePipelineDescriptor, - Extent3d, PipelineCache, TextureDimension, TextureFormat, TextureUsages, + encase::{StorageBuffer, UniformBuffer}, + AsBindGroup, BindingResource, BufferInitDescriptor, BufferUsages, CachedPipelineState, + ComputePassDescriptor, ComputePipelineDescriptor, Extent3d, PipelineCache, + TextureDimension, TextureFormat, TextureUsages, }, - renderer::{RenderContext, RenderDevice}, + renderer::{RenderContext, RenderDevice, RenderQueue}, texture::{FallbackImage, GpuImage}, - Render, RenderApp, RenderSet, + MainWorld, Render, RenderApp, RenderSet, }, }; @@ -67,6 +70,16 @@ impl FromWorld for ShocoVoxRenderPipeline { }); ShocoVoxRenderPipeline { + victim_pointer: 0, + render_queue: world.resource::().clone(), + viewport_buffer: None, + octree_meta_buffer: None, + nodes_buffer: None, + node_children_buffer: None, + voxels_buffer: None, + color_palette_buffer: None, + data_meta_bytes_buffer: None, + readable_data_meta_bytes_buffer: None, update_tree: true, viewing_glass_bind_group_layout, render_data_bind_group_layout, @@ -97,20 +110,306 @@ fn prepare_bind_groups( pipeline.viewing_glass_bind_group = Some(bind_group.bind_group); if pipeline.update_tree { - let tree_bind_group = render_data - .as_bind_group( - &pipeline.render_data_bind_group_layout, - &render_device, - &gpu_images, - &fallback_image, + //================================================================= + // Implementation with WGPU + //================================================================= + // Upload data to buffers + let mut buffer = UniformBuffer::new(Vec::::new()); + buffer.write(&render_data.octree_meta).unwrap(); + if let Some(metadata_buffer) = &pipeline.octree_meta_buffer { + pipeline + .render_queue + .write_buffer(metadata_buffer, 0, &buffer.into_inner()) + } else { + let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Metadata Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + pipeline.octree_meta_buffer = Some(metadata_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.nodes).unwrap(); + if let Some(nodes_buffer) = &pipeline.nodes_buffer { + pipeline + .render_queue + .write_buffer(nodes_buffer, 0, &buffer.into_inner()) + } else { + let nodes_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Nodes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.nodes_buffer = Some(nodes_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.node_children).unwrap(); + if let Some(children_buffer) = &pipeline.node_children_buffer { + pipeline + .render_queue + .write_buffer(children_buffer, 0, &buffer.into_inner()) + } else { + let children_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Children Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.node_children_buffer = Some(children_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.voxels).unwrap(); + if let Some(voxels_buffer) = &pipeline.voxels_buffer { + pipeline + .render_queue + .write_buffer(voxels_buffer, 0, &buffer.into_inner()) + } else { + let voxels_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Voxels Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.voxels_buffer = Some(voxels_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.data_meta_bytes).unwrap(); + if let Some(data_meta_bytes_buffer) = &pipeline.data_meta_bytes_buffer { + pipeline + .render_queue + .write_buffer(data_meta_bytes_buffer, 0, &buffer.into_inner()) + } else { + let data_meta_bytes_buffer = + render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Meta Bytes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, + }); + pipeline.data_meta_bytes_buffer = Some(data_meta_bytes_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.color_palette).unwrap(); + if let Some(color_palette_buffer) = &pipeline.color_palette_buffer { + pipeline + .render_queue + .write_buffer(color_palette_buffer, 0, &buffer.into_inner()) + } else { + let color_palette_buffer = + render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Color Palette Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.color_palette_buffer = Some(color_palette_buffer); + } + + // Create bind group + let tree_bind_group = render_device.create_bind_group( + ShocoVoxRenderData::label(), + &pipeline.render_data_bind_group_layout, + &[ + bevy::render::render_resource::BindGroupEntry { + binding: 0, + resource: pipeline + .octree_meta_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 1, + resource: pipeline.nodes_buffer.as_ref().unwrap().as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 2, + resource: pipeline + .node_children_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 3, + resource: pipeline.voxels_buffer.as_ref().unwrap().as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 4, + resource: pipeline + .color_palette_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 5, + resource: pipeline + .data_meta_bytes_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + ], + ); + pipeline.tree_bind_group = Some(tree_bind_group); + + //================================================================= + // Implementation with AsBindGroup + //================================================================= + // let tree_bind_group = render_data + // .as_bind_group( + // &pipeline.render_data_bind_group_layout, + // &render_device, + // &gpu_images, + // &fallback_image, + // ) + // .ok() + // .unwrap(); + + // // println!("bindings: {:?}", tree_bind_group.bindings); + // // let bindings = ; + // //TODO: set buffers by binding index(bindings[x].0), instead of array index + // // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = + // // &tree_bind_group.bindings[2].1 + // // { + // // debug_assert_eq!(tree_bind_group.bindings[2].0, 2); + // // pipeline.nodes_children_buffer = Some(buf.clone()); + // // } + // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = + // &tree_bind_group.bindings[0].1 + // { + // // compare binding to ShocoVoxRenderData field + // debug_assert_eq!(tree_bind_group.bindings[0].0, 5); + // pipeline.cache_bytes_buffer = Some(buf.clone()); + // } + + // pipeline.tree_bind_group = Some(tree_bind_group.bind_group); + + // Create the staging buffer helping in reading data from the GPU + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.data_meta_bytes).unwrap(); + if let Some(readable_data_meta_bytes_buffer) = &pipeline.readable_data_meta_bytes_buffer { + pipeline.render_queue.write_buffer( + readable_data_meta_bytes_buffer, + 0, + &buffer.into_inner(), ) - .ok() - .unwrap(); - pipeline.tree_bind_group = Some(tree_bind_group.bind_group); + } else { + let readable_data_meta_bytes_buffer = + render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Cache Bytes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + }); + pipeline.readable_data_meta_bytes_buffer = Some(readable_data_meta_bytes_buffer); + } + pipeline.update_tree = false; } } +pub(crate) fn handle_gpu_readback( + render_device: Res, + svx_data: Option>, + svx_pipeline: Option>, + mut world: ResMut, +) { + // // Data updates triggered by debug interface + // if let Some(svx_data) = svx_data { + // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); + // let svx_pipeline = svx_pipeline.unwrap(); + // if svx_data.do_the_thing { + // // GPU buffer read + // // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html + // let buffer_slice = svx_pipeline + // .readable_cache_bytes_buffer + // .as_ref() + // .unwrap() + // .slice(..); + // let (s, r) = crossbeam::channel::unbounded::<()>(); + // buffer_slice.map_async( + // bevy::render::render_resource::MapMode::Read, + // move |d| match d { + // Ok(_) => s.send(()).expect("Failed to send map update"), + // Err(err) => println!("Something's wrong: {err}"), + // }, + // ); + + // render_device + // .poll(bevy::render::render_resource::Maintain::wait()) + // .panic_on_timeout(); + + // r.recv().expect("Failed to receive the map_async message"); + // { + // let buffer_view = buffer_slice.get_mapped_range(); + + // let data = buffer_view + // .chunks(std::mem::size_of::()) + // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + // .collect::>(); + // println!("data: {}", data[0]); + // } + + // svx_pipeline + // .readable_cache_bytes_buffer + // .as_ref() + // .unwrap() + // .unmap(); + + // render_data_mainworld.do_the_thing = false; + // } + // } +} + +pub(crate) fn handle_cache( + svx_data: Option>, + svx_pipeline: Option>, + mut world: ResMut, +) { + //TODO: Document that all components are lost during extract transition + // Data updates triggered by debug interface + // if let Some(svx_data) = svx_data { + // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); + // let svx_pipeline = svx_pipeline.unwrap(); + // let render_queue = &svx_pipeline.render_queue.0; + // if svx_data.do_the_thing { + // let mut data: [u32; 2] = [0; 2]; + // data[0] = svx_data.children_buffer[1]; + // data[1] = svx_data.children_buffer[0]; + + // // GPU buffer Write + // // render_data_mainworld.children_buffer[0] = data[0]; + // // render_data_mainworld.children_buffer[1] = data[1]; + // // use bevy::render::render_resource::encase::StorageBuffer; + // // let mut data_buffer = StorageBuffer::new(Vec::::new()); + // // data_buffer.write(&data).unwrap(); + // // render_queue.write_buffer( + // // svx_pipeline.nodes_children_buffer.as_ref().unwrap(), + // // 0, + // // &data_buffer.into_inner(), + // // ); + + // // GPU buffer read + // let buffer_slice = svx_pipeline.cache_bytes_buffer.as_ref().unwrap().slice(..); + + // // .map_async( + // // bevy::render::render_resource::MapMode::Read, + // // move |d| match d { + // // Ok(_) => println!("data!"), + // // Err(err) => println!("Something's wrong kiddo"), + // // }, + // // ); + // let data = 0; + // render_data_mainworld.cache_bytes[0] = data; + // println!("data: {}", render_data_mainworld.cache_bytes[0]); + + // render_data_mainworld.do_the_thing = false; + // } + // } +} + pub(crate) fn create_ouput_texture( resolution: [u32; 2], mut images: ResMut>, @@ -133,14 +432,23 @@ pub(crate) fn create_ouput_texture( impl Plugin for ShocoVoxRenderPlugin { fn build(&self, app: &mut App) { - app.add_plugins(ExtractResourcePlugin::::default()); - app.add_plugins(ExtractResourcePlugin::::default()); + app.add_plugins(( + ExtractResourcePlugin::::default(), + ExtractResourcePlugin::::default(), + )); let render_app = app.sub_app_mut(RenderApp); + render_app.add_systems( + ExtractSchedule, + handle_cache.in_set(RenderSet::PrepareResources), + ); + render_app.add_systems( + ExtractSchedule, + handle_gpu_readback.in_set(RenderSet::Cleanup), + ); render_app.add_systems( Render, prepare_bind_groups.in_set(RenderSet::PrepareBindGroups), ); - let mut render_graph = render_app.world_mut().resource_mut::(); render_graph.add_node( ShocoVoxLabel, @@ -161,14 +469,16 @@ impl Plugin for ShocoVoxRenderPlugin { const WORKGROUP_SIZE: u32 = 8; impl render_graph::Node for ShocoVoxRenderNode { fn update(&mut self, world: &mut World) { - let pipeline = world.resource::(); - let render_data = world.get_resource::(); - let pipeline_cache = world.resource::(); - if !self.ready { - if let CachedPipelineState::Ok(_) = - pipeline_cache.get_compute_pipeline_state(pipeline.update_pipeline) - { - self.ready = render_data.is_some(); + { + let render_data = world.get_resource::(); + let svx_pipeline = world.resource::(); + let pipeline_cache = world.resource::(); + if !self.ready { + if let CachedPipelineState::Ok(_) = + pipeline_cache.get_compute_pipeline_state(svx_pipeline.update_pipeline) + { + self.ready = render_data.is_some(); + } } } } @@ -180,24 +490,41 @@ impl render_graph::Node for ShocoVoxRenderNode { world: &World, ) -> Result<(), render_graph::NodeRunError> { let pipeline_cache = world.resource::(); - let pipeline = world.resource::(); + let svx_pipeline = world.resource::(); + let command_encoder = render_context.command_encoder(); if self.ready { - let mut pass = render_context - .command_encoder() - .begin_compute_pass(&ComputePassDescriptor::default()); - - pass.set_bind_group(0, pipeline.viewing_glass_bind_group.as_ref().unwrap(), &[]); - pass.set_bind_group(1, pipeline.tree_bind_group.as_ref().unwrap(), &[]); - let pipeline = pipeline_cache - .get_compute_pipeline(pipeline.update_pipeline) - .unwrap(); - pass.set_pipeline(pipeline); - pass.dispatch_workgroups( - self.resolution[0] / WORKGROUP_SIZE, - self.resolution[1] / WORKGROUP_SIZE, - 1, - ); + { + let mut pass = + command_encoder.begin_compute_pass(&ComputePassDescriptor::default()); + + pass.set_bind_group( + 0, + svx_pipeline.viewing_glass_bind_group.as_ref().unwrap(), + &[], + ); + pass.set_bind_group(1, svx_pipeline.tree_bind_group.as_ref().unwrap(), &[]); + let pipeline = pipeline_cache + .get_compute_pipeline(svx_pipeline.update_pipeline) + .unwrap(); + pass.set_pipeline(pipeline); + pass.dispatch_workgroups( + self.resolution[0] / WORKGROUP_SIZE, + self.resolution[1] / WORKGROUP_SIZE, + 1, + ); + } + + command_encoder.copy_buffer_to_buffer( + svx_pipeline.data_meta_bytes_buffer.as_ref().unwrap(), + 0, + svx_pipeline + .readable_data_meta_bytes_buffer + .as_ref() + .unwrap(), + 0, + std::mem::size_of::() as u64, + ) } Ok(()) } diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 604aba4..27bc15d 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -9,8 +9,9 @@ use bevy::{ prelude::Image, render_graph::RenderLabel, render_resource::{ - AsBindGroup, BindGroup, BindGroupLayout, CachedComputePipelineId, ShaderType, + AsBindGroup, BindGroup, BindGroupLayout, Buffer, CachedComputePipelineId, ShaderType, }, + renderer::RenderQueue, }, }; @@ -23,7 +24,9 @@ pub(crate) struct Voxelement { #[derive(Clone, ShaderType)] pub(crate) struct SizedNode { /// Composite field: - /// - Byte 1: Boolean value, true in case node is a leaf + /// - Byte 1: composite byte with node entry descriptor + /// - bit 1: Boolean value, true in case node is a leaf + /// - bit 2: Boolean value, used bit is being set when the node is being queried /// - In case of internal nodes: /// - Byte 2: TBD /// - Byte 3: TBD @@ -34,9 +37,9 @@ pub(crate) struct SizedNode { /// - Byte 4: TBD pub(crate) sized_node_meta: u32, - /// index of where the data about this node is found in children_buffer + /// Cache index of where the data about this node is found in children_buffer /// - In case of internal nodes: - /// - 8 Index value of node children + /// - 8 Index value of node children ( in cache ) /// - In case of leaf nodes: /// - Byte 1-4: Occupancy bitmap MSB /// - Byte 5-8: Occupancy bitmap LSB @@ -48,7 +51,7 @@ pub(crate) struct SizedNode { /// - Byte 29-32: TBD pub(crate) children_start_at: u32, - /// index of where the voxel values contained in the node start inside the voxels buffer, + /// Cache index of where the voxel values contained in the node start inside the voxels buffer, /// or a "none_value". Should the field contain an index, the next voxel_brick_dim^3 elements /// inside the @voxels array count as part of the voxels associated with the node pub(crate) voxels_start_at: u32, @@ -86,14 +89,57 @@ pub struct ShocoVoxViewingGlass { #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] #[type_path = "shocovox::gpu::ShocoVoxRenderData"] pub struct ShocoVoxRenderData { + pub do_the_thing: bool, //STRICTLY FOR DEBUG REASONS + + // new layout + // #[uniform(0, visibility(compute))] + // pub(crate) meta: OctreeMetaData, + + // #[storage(1, visibility(compute))] + // pub(crate) root_node: SizedNode, + + // #[storage(2, visibility(compute))] + // pub(crate) nodes: Vec, + + // #[storage(3, visibility(compute))] + // pub(crate) children_buffer: Vec, + + // #[storage(4, visibilty(compute))] + // pub(crate) voxels: Vec, + + // #[storage(5, visibility(compute))] + // pub(crate) color_palette: Vec, + /// Bits storing information for multiple fields + /// Each array is the same size, but might be different in terms of bytes per index. + /// e.g. Nodes have 1 SizedNode under each index, children_buffer have 8, Voxelements have DIM*DIM*DIM + /// For each index in the array, 2 Bytes of metadata is accounted for. + /// Structure is the following: + /// _---------------------------------------------------------------------_ + /// | Byte 0 | metadata | + /// |---------------------------------------------------------------------| + /// | bit 0 | 1 in case node is used by the raytracing algorithm* | + /// | bit 1 | 1 in case voxel brick is used by the raytracing algorithm | + /// | bit 2 | 1 in case node is a leaf | + /// | ... | unused, potentially: 1 if node has voxels | + /// | ... | unused, potentially: children_buffer size: 2 or 8 | + /// | ... | unused, potentially: voxel brick size: 1, full or sparse | + /// |---------------------------------------------------------------------| + /// | Byte 1 | nodes lvl2 occupancy bitmap | + /// `---------------------------------------------------------------------` + /// + /// * the same bit is used for children_buffer + #[storage(5, visibility(compute))] + pub(crate) data_meta_bytes: Vec, + + // old layout.. TO BE REMOVED #[uniform(0, visibility(compute))] - pub(crate) meta: OctreeMetaData, + pub(crate) octree_meta: OctreeMetaData, #[storage(1, visibility(compute))] pub(crate) nodes: Vec, #[storage(2, visibility(compute))] - pub(crate) children_buffer: Vec, + pub node_children: Vec, #[storage(3, visibility(compute))] pub(crate) voxels: Vec, @@ -105,9 +151,24 @@ pub struct ShocoVoxRenderData { #[derive(Resource)] pub(crate) struct ShocoVoxRenderPipeline { pub update_tree: bool, + // The candidates for deletion inside nodes array on page deletion + pub(crate) victim_pointer: u32, + + pub(crate) render_queue: RenderQueue, + pub(crate) update_pipeline: CachedComputePipelineId, + + // Render data buffers + pub(crate) viewport_buffer: Option, + pub(crate) octree_meta_buffer: Option, + pub(crate) nodes_buffer: Option, + pub(crate) node_children_buffer: Option, + pub(crate) voxels_buffer: Option, + pub(crate) color_palette_buffer: Option, + pub(crate) data_meta_bytes_buffer: Option, + pub(crate) readable_data_meta_bytes_buffer: Option, + pub(crate) viewing_glass_bind_group_layout: BindGroupLayout, pub(crate) render_data_bind_group_layout: BindGroupLayout, - pub(crate) update_pipeline: CachedComputePipelineId, pub(crate) viewing_glass_bind_group: Option, pub(crate) tree_bind_group: Option, } diff --git a/src/octree/raytracing/mod.rs b/src/octree/raytracing/mod.rs index f54d278..7881c67 100644 --- a/src/octree/raytracing/mod.rs +++ b/src/octree/raytracing/mod.rs @@ -7,4 +7,4 @@ pub mod bevy; pub use crate::spatial::raytracing::Ray; #[cfg(feature = "bevy_wgpu")] -pub use bevy::types::{ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport}; +pub use bevy::types::{ShocoVoxRenderData, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport}; diff --git a/src/octree/raytracing/raytracing_on_cpu.rs b/src/octree/raytracing/raytracing_on_cpu.rs index 845881c..df026c3 100644 --- a/src/octree/raytracing/raytracing_on_cpu.rs +++ b/src/octree/raytracing/raytracing_on_cpu.rs @@ -284,9 +284,9 @@ where let mut step_vec = V3c::unit(0.); while target_octant != OOB_OCTANT { - current_node_key = Octree::::ROOT_NODE_KEY as usize; + current_node_key = Self::ROOT_NODE_KEY as usize; current_bounds = Cube::root_bounds(self.octree_size as f32); - node_stack.push(Octree::::ROOT_NODE_KEY); + node_stack.push(Self::ROOT_NODE_KEY); while !node_stack.is_empty() { let current_node_occupied_bits = self.occupied_8bit(*node_stack.last().unwrap()); debug_assert!(self diff --git a/src/octree/update.rs b/src/octree/update.rs index ce0d70d..541879c 100644 --- a/src/octree/update.rs +++ b/src/octree/update.rs @@ -42,7 +42,7 @@ where } // A vector does not consume significant resources in this case, e.g. a 4096*4096*4096 chunk has depth of 12 - let mut node_stack = vec![(Octree::::ROOT_NODE_KEY, root_bounds)]; + let mut node_stack = vec![(Self::ROOT_NODE_KEY, root_bounds)]; loop { let (current_node_key, current_bounds) = *node_stack.last().unwrap(); let current_node_key = current_node_key as usize; @@ -234,7 +234,7 @@ where } // A vector does not consume significant resources in this case, e.g. a 4096*4096*4096 chunk has depth of 12 - let mut node_stack = vec![(Octree::::ROOT_NODE_KEY, root_bounds)]; + let mut node_stack = vec![(Self::ROOT_NODE_KEY, root_bounds)]; let mut parent_target = child_octant_for(&root_bounds, &position); //This init value is never used loop { From 8b50a4d0cb54442b75c9a83c46b9f72917f9000c Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 14 Sep 2024 01:12:03 +0200 Subject: [PATCH 02/19] Moved node meta to common cache byte stream --- assets/shaders/viewport_render.wgsl | 37 +++++++------ src/octree/raytracing/bevy/data.rs | 82 ++++++++++++++++++++--------- src/octree/raytracing/bevy/types.rs | 15 +----- 3 files changed, 79 insertions(+), 55 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 12dfe78..863d47e 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -316,15 +316,13 @@ fn dda_step_to_next_sibling( } // Unique to this implementation, not adapted from rust code, corresponds to: -//crate::octree::raytracing::classic_raytracing_on_bevy_wgpu::meta_set_is_leaf -fn is_leaf(sized_node_meta: u32) -> bool { - return 0 < (0x01000000 & sized_node_meta); -} - -// Unique to this implementation, not adapted from rust code, corresponds to: -//crate::octree::raytracing::classic_raytracing_on_bevy_wgpu::meta_set_node_occupancy_bitmap -fn get_node_occupancy_bitmap(sized_node_meta: u32) -> u32 { - return (0x000000FF & sized_node_meta); +//crate::octree::raytracing::classic_raytracing_on_bevy_wgpu::set_node_meta_inner +fn get_node_occupancy_bitmap(node_key: u32, data_meta_bytes: u32) -> u32{ + if 0 == (node_key % 2) { + return ((data_meta_bytes & 0x0000FF00u) >> 8u ); + } else { + return ((data_meta_bytes & 0xFF000000u) >> 24u ); + } } //crate::spatial::math::step_octant @@ -520,7 +518,15 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ node_stack_push(&node_stack, &node_stack_meta, OCTREE_ROOT_NODE_KEY); while(!node_stack_is_empty(node_stack_meta)) { var leaf_miss = false; - if is_leaf(nodes[current_node_key].sized_node_meta) { + if ( // is_leaf + ( + (0 == (current_node_key % 2)) + && (0 != (data_meta_bytes[current_node_key / 2] & 0x00000004u)) + )||( + (1 == (current_node_key % 2)) + && (0 != (data_meta_bytes[current_node_key / 2] & 0x00040000u)) + ) + ){ let leaf_brick_hit = traverse_brick( ray, &ray_current_distance, nodes[current_node_key].voxels_start_at, node_children[nodes[current_node_key].children_starts_at], @@ -552,9 +558,9 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ if( leaf_miss || target_octant == OOB_OCTANT || EMPTY_MARKER == current_node_key // Should never happen - || 0 == get_node_occupancy_bitmap(nodes[current_node_key].sized_node_meta) + || 0 == get_node_occupancy_bitmap(current_node_key, data_meta_bytes[current_node_key / 2]) || ( 0 == ( - get_node_occupancy_bitmap(nodes[current_node_key].sized_node_meta) + get_node_occupancy_bitmap(current_node_key, data_meta_bytes[current_node_key / 2]) & RAY_TO_NODE_OCCUPANCY_BITMASK_LUT[target_octant][direction_lut_index] )) ){ @@ -593,7 +599,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ if ( target_child_key < arrayLength(&nodes) //!crate::object_pool::key_is_valid && 0 != ( - get_node_occupancy_bitmap(nodes[current_node_key].sized_node_meta) + get_node_occupancy_bitmap(current_node_key, data_meta_bytes[current_node_key / 2]) & ( // crate::spatial::math::octant_bitmask 0x00000001u << (target_octant & 0x000000FF) ) @@ -629,11 +635,11 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ || ( target_child_key < arrayLength(&nodes) //crate::object_pool::key_is_valid && 0 != ( - get_node_occupancy_bitmap(nodes[current_node_key].sized_node_meta) + get_node_occupancy_bitmap(current_node_key, data_meta_bytes[current_node_key / 2]) & (0x00000001u << target_octant) // crate::spatial::math::octant_bitmask ) && 0 != ( - get_node_occupancy_bitmap(nodes[target_child_key].sized_node_meta) + get_node_occupancy_bitmap(target_child_key, data_meta_bytes[target_child_key / 2]) & RAY_TO_NODE_OCCUPANCY_BITMASK_LUT[hash_region( point_in_ray_at_distance(ray, ray_current_distance) - target_bounds.min_position, round(target_bounds.size / 2.) @@ -686,7 +692,6 @@ fn is_empty(e: Voxelement) -> bool { } struct SizedNode { - sized_node_meta: u32, children_starts_at: u32, voxels_start_at: u32, } diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 1b51c2a..cd2ff88 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -9,38 +9,69 @@ use std::collections::HashMap; impl Octree where - T: Default + Clone + VoxelData, + T: VoxelData + PartialEq + Default + Clone + Copy, { - fn meta_set_is_leaf(sized_node_meta: &mut u32, is_leaf: bool) { - *sized_node_meta = - (*sized_node_meta & 0x00FFFFFF) | if is_leaf { 0x01000000 } else { 0x00000000 }; - } + fn set_node_meta_inner( + &self, + data_meta_bytes: &mut Vec, + data_index: usize, + is_leaf: bool, + occupancy_bitmap: u8, + ) { + // Erase the initial data + data_meta_bytes[data_index / 2] &= if 0 == data_index % 2 { + 0xFFFF00FAu32 + } else { + 0x00FAFFFFu32 + }; + + // Add occupancy bitmap to meta + data_meta_bytes[data_index / 2] |= if 0 == data_index % 2 { + (occupancy_bitmap as u32) << 8u32 + } else { + (occupancy_bitmap as u32) << 24u32 + }; + + // Add node is_leaf bit to meta + if is_leaf { + data_meta_bytes[data_index / 2] |= if 0 == data_index % 2 { + 0x000000004u32 + } else { + 0x00040000u32 + }; + } - fn meta_set_node_occupancy_bitmap(sized_node_meta: &mut u32, bitmap: u8) { - *sized_node_meta = (*sized_node_meta & 0xFFFFFF00) | bitmap as u32; + // Add node is_used bit to meta + if is_leaf { + data_meta_bytes[data_index / 2] |= if 0 == data_index % 2 { + 0x000000001u32 + } else { + 0x00010000u32 + }; + } } - fn create_meta(&self, node_key: usize) -> u32 { + fn set_meta_bytes_for_node( + &self, + data_meta_bytes: &mut Vec, + node_key: usize, + data_index: usize, + ) { let node = self.nodes.get(node_key); - let mut meta = 0; match node { NodeContent::Leaf(_) => { - Self::meta_set_is_leaf(&mut meta, true); - Self::meta_set_node_occupancy_bitmap( - &mut meta, + self.set_node_meta_inner( + data_meta_bytes, + data_index, + true, self.occupied_8bit(node_key as u32), ); } NodeContent::Internal(occupied_bits) => { - Self::meta_set_is_leaf(&mut meta, false); - Self::meta_set_node_occupancy_bitmap(&mut meta, *occupied_bits); + self.set_node_meta_inner(data_meta_bytes, data_index, false, *occupied_bits); } - _ => { - Self::meta_set_is_leaf(&mut meta, false); - Self::meta_set_node_occupancy_bitmap(&mut meta, 0x00); - } - }; - meta + _ => {} + } } pub fn create_bevy_view(&self) -> ShocoVoxRenderData { @@ -59,15 +90,16 @@ where let mut node_children = Vec::new(); let mut voxels = Vec::new(); let mut color_palette = Vec::new(); - let mut data_meta_bytes = Vec::new(); + // Size of meta for one element is 2 Bytes, + let mut data_meta_bytes = vec![0u32; self.nodes.len() / 2]; // Build up Nodes let mut map_to_node_index_in_nodes_buffer = HashMap::new(); - for i in 0..self.nodes.len() { - if self.nodes.key_is_valid(i) { - map_to_node_index_in_nodes_buffer.insert(i as usize, nodes.len()); + for node_key in 0..self.nodes.len() { + if self.nodes.key_is_valid(node_key) { + map_to_node_index_in_nodes_buffer.insert(node_key as usize, nodes.len()); + self.set_meta_bytes_for_node(&mut data_meta_bytes, node_key, nodes.len()); nodes.push(SizedNode { - // sized_node_meta: self.create_meta(i), children_start_at: empty_marker(), voxels_start_at: empty_marker(), }); diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 27bc15d..cb4e7dc 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -23,20 +23,6 @@ pub(crate) struct Voxelement { #[derive(Clone, ShaderType)] pub(crate) struct SizedNode { - /// Composite field: - /// - Byte 1: composite byte with node entry descriptor - /// - bit 1: Boolean value, true in case node is a leaf - /// - bit 2: Boolean value, used bit is being set when the node is being queried - /// - In case of internal nodes: - /// - Byte 2: TBD - /// - Byte 3: TBD - /// - Byte 4: Lvl2 Occupancy bitmap - /// - In case of leaf nodes: - /// - Byte 2: TBD - /// - Byte 3: TBD - /// - Byte 4: TBD - pub(crate) sized_node_meta: u32, - /// Cache index of where the data about this node is found in children_buffer /// - In case of internal nodes: /// - 8 Index value of node children ( in cache ) @@ -151,6 +137,7 @@ pub struct ShocoVoxRenderData { #[derive(Resource)] pub(crate) struct ShocoVoxRenderPipeline { pub update_tree: bool, + // The candidates for deletion inside nodes array on page deletion pub(crate) victim_pointer: u32, From 6f8b3c9f04addeb86cb5fc341d5e83bf8a1b8f36 Mon Sep 17 00:00:00 2001 From: davids91 Date: Wed, 6 Nov 2024 07:00:12 +0100 Subject: [PATCH 03/19] Restructured bevy module --- src/octree/raytracing/bevy/data.rs | 114 +++++- src/octree/raytracing/bevy/mod.rs | 479 +------------------------ src/octree/raytracing/bevy/pipeline.rs | 349 ++++++++++++++++++ src/octree/raytracing/bevy/types.rs | 1 - 4 files changed, 480 insertions(+), 463 deletions(-) create mode 100644 src/octree/raytracing/bevy/pipeline.rs diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index cd2ff88..3fbb8ab 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -1,10 +1,16 @@ use crate::object_pool::empty_marker; use crate::octree::{ - raytracing::bevy::types::{OctreeMetaData, ShocoVoxRenderData, SizedNode, Voxelement}, + raytracing::bevy::types::{ + OctreeMetaData, ShocoVoxRenderData, ShocoVoxRenderPipeline, SizedNode, Voxelement, + }, types::{NodeChildrenArray, NodeContent}, Octree, V3c, VoxelData, }; -use bevy::math::Vec4; +use bevy::{ + ecs::system::{Res, ResMut}, + math::Vec4, + render::{renderer::RenderDevice, MainWorld}, +}; use std::collections::HashMap; impl Octree @@ -191,3 +197,107 @@ where //TODO: find the first unused element, and overwrite it with the item } } + +pub(crate) fn handle_gpu_readback( + render_device: Res, + svx_data: Option>, + svx_pipeline: Option>, +) { + // // Data updates triggered by debug interface + // if let Some(svx_data) = svx_data { + // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); + // let svx_pipeline = svx_pipeline.unwrap(); + // if svx_data.do_the_thing { + // // GPU buffer read + // // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html + // let buffer_slice = svx_pipeline + // .readable_cache_bytes_buffer + // .as_ref() + // .unwrap() + // .slice(..); + // let (s, r) = crossbeam::channel::unbounded::<()>(); + // buffer_slice.map_async( + // bevy::render::render_resource::MapMode::Read, + // move |d| match d { + // Ok(_) => s.send(()).expect("Failed to send map update"), + // Err(err) => println!("Something's wrong: {err}"), + // }, + // ); + + // render_device + // .poll(bevy::render::render_resource::Maintain::wait()) + // .panic_on_timeout(); + + // r.recv().expect("Failed to receive the map_async message"); + // { + // let buffer_view = buffer_slice.get_mapped_range(); + + // let data = buffer_view + // .chunks(std::mem::size_of::()) + // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + // .collect::>(); + // println!("data: {}", data[0]); + // } + + // svx_pipeline + // .readable_cache_bytes_buffer + // .as_ref() + // .unwrap() + // .unmap(); + + // render_data_mainworld.do_the_thing = false; + // } + // } +} + +pub(crate) fn sync_with_main_world(// svx_data: Option>, + // svx_pipeline: Option>, + // mut world: ResMut, +) { +} + +pub(crate) fn handle_cache( + svx_data: Option>, + svx_pipeline: Option>, +) { + //TODO: Document that all components are lost during extract transition + // Data updates triggered by debug interface + // if let Some(svx_data) = svx_data { + // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); + // let svx_pipeline = svx_pipeline.unwrap(); + // let render_queue = &svx_pipeline.render_queue.0; + // if svx_data.do_the_thing { + // let mut data: [u32; 2] = [0; 2]; + // data[0] = svx_data.children_buffer[1]; + // data[1] = svx_data.children_buffer[0]; + + // // GPU buffer Write + // // render_data_mainworld.children_buffer[0] = data[0]; + // // render_data_mainworld.children_buffer[1] = data[1]; + // // use bevy::render::render_resource::encase::StorageBuffer; + // // let mut data_buffer = StorageBuffer::new(Vec::::new()); + // // data_buffer.write(&data).unwrap(); + // // render_queue.write_buffer( + // // svx_pipeline.nodes_children_buffer.as_ref().unwrap(), + // // 0, + // // &data_buffer.into_inner(), + // // ); + + // // GPU buffer read + // let buffer_slice = svx_pipeline.cache_bytes_buffer.as_ref().unwrap().slice(..); + + // // .map_async( + // // bevy::render::render_resource::MapMode::Read, + // // move |d| match d { + // // Ok(_) => println!("data!"), + // // Err(err) => println!("Something's wrong kiddo"), + // // }, + // // ); + // let data = 0; + // render_data_mainworld.cache_bytes[0] = data; + // println!("data: {}", render_data_mainworld.cache_bytes[0]); + + // render_data_mainworld.do_the_thing = false; + // } + // } +} diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index 0cc3291..d0ef1c6 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -1,41 +1,32 @@ mod data; +mod pipeline; pub mod types; pub use crate::octree::raytracing::bevy::types::{ ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, }; -use crate::octree::raytracing::bevy::types::{ - ShocoVoxLabel, ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline, +use crate::octree::raytracing::bevy::{ + data::{handle_cache, handle_gpu_readback, sync_with_main_world}, + pipeline::prepare_bind_groups, + types::{ShocoVoxLabel, ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline}, }; use bevy::{ app::{App, Plugin}, - asset::{AssetServer, Assets, Handle}, - ecs::{ - system::{Res, ResMut}, - world::{FromWorld, World}, - }, + asset::{Assets, Handle}, + ecs::system::ResMut, prelude::{ExtractSchedule, IntoSystemConfigs}, render::{ extract_resource::ExtractResourcePlugin, prelude::Image, - render_asset::{RenderAssetUsages, RenderAssets}, - render_graph::{self, RenderGraph}, - render_resource::{ - encase::{StorageBuffer, UniformBuffer}, - AsBindGroup, BindingResource, BufferInitDescriptor, BufferUsages, CachedPipelineState, - ComputePassDescriptor, ComputePipelineDescriptor, Extent3d, PipelineCache, - TextureDimension, TextureFormat, TextureUsages, - }, - renderer::{RenderContext, RenderDevice, RenderQueue}, - texture::{FallbackImage, GpuImage}, - MainWorld, Render, RenderApp, RenderSet, + render_asset::RenderAssetUsages, + render_graph::RenderGraph, + render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, + Render, RenderApp, RenderSet, }, }; -use std::borrow::Cow; - pub fn create_viewing_glass( viewport: &Viewport, resolution: [u32; 2], @@ -47,369 +38,6 @@ pub fn create_viewing_glass( } } -impl FromWorld for ShocoVoxRenderPipeline { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); - let viewing_glass_bind_group_layout = - ShocoVoxViewingGlass::bind_group_layout(render_device); - let render_data_bind_group_layout = ShocoVoxRenderData::bind_group_layout(render_device); - let shader = world - .resource::() - .load("shaders/viewport_render.wgsl"); - let pipeline_cache = world.resource::(); - let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { - label: None, - layout: vec![ - viewing_glass_bind_group_layout.clone(), - render_data_bind_group_layout.clone(), - ], - push_constant_ranges: Vec::new(), - shader, - shader_defs: vec![], - entry_point: Cow::from("update"), - }); - - ShocoVoxRenderPipeline { - victim_pointer: 0, - render_queue: world.resource::().clone(), - viewport_buffer: None, - octree_meta_buffer: None, - nodes_buffer: None, - node_children_buffer: None, - voxels_buffer: None, - color_palette_buffer: None, - data_meta_bytes_buffer: None, - readable_data_meta_bytes_buffer: None, - update_tree: true, - viewing_glass_bind_group_layout, - render_data_bind_group_layout, - update_pipeline, - viewing_glass_bind_group: None, - tree_bind_group: None, - } - } -} - -fn prepare_bind_groups( - gpu_images: Res>, - fallback_image: Res, - render_device: Res, - mut pipeline: ResMut, - octree_viewing_glass: Res, - render_data: Res, -) { - let bind_group = octree_viewing_glass - .as_bind_group( - &pipeline.viewing_glass_bind_group_layout, - &render_device, - &gpu_images, - &fallback_image, - ) - .ok() - .unwrap(); - pipeline.viewing_glass_bind_group = Some(bind_group.bind_group); - - if pipeline.update_tree { - //================================================================= - // Implementation with WGPU - //================================================================= - // Upload data to buffers - let mut buffer = UniformBuffer::new(Vec::::new()); - buffer.write(&render_data.octree_meta).unwrap(); - if let Some(metadata_buffer) = &pipeline.octree_meta_buffer { - pipeline - .render_queue - .write_buffer(metadata_buffer, 0, &buffer.into_inner()) - } else { - let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Metadata Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - pipeline.octree_meta_buffer = Some(metadata_buffer); - } - - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.nodes).unwrap(); - if let Some(nodes_buffer) = &pipeline.nodes_buffer { - pipeline - .render_queue - .write_buffer(nodes_buffer, 0, &buffer.into_inner()) - } else { - let nodes_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Nodes Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.nodes_buffer = Some(nodes_buffer); - } - - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.node_children).unwrap(); - if let Some(children_buffer) = &pipeline.node_children_buffer { - pipeline - .render_queue - .write_buffer(children_buffer, 0, &buffer.into_inner()) - } else { - let children_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Children Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.node_children_buffer = Some(children_buffer); - } - - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.voxels).unwrap(); - if let Some(voxels_buffer) = &pipeline.voxels_buffer { - pipeline - .render_queue - .write_buffer(voxels_buffer, 0, &buffer.into_inner()) - } else { - let voxels_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Voxels Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.voxels_buffer = Some(voxels_buffer); - } - - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.data_meta_bytes).unwrap(); - if let Some(data_meta_bytes_buffer) = &pipeline.data_meta_bytes_buffer { - pipeline - .render_queue - .write_buffer(data_meta_bytes_buffer, 0, &buffer.into_inner()) - } else { - let data_meta_bytes_buffer = - render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Meta Bytes Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, - }); - pipeline.data_meta_bytes_buffer = Some(data_meta_bytes_buffer); - } - - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.color_palette).unwrap(); - if let Some(color_palette_buffer) = &pipeline.color_palette_buffer { - pipeline - .render_queue - .write_buffer(color_palette_buffer, 0, &buffer.into_inner()) - } else { - let color_palette_buffer = - render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Color Palette Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.color_palette_buffer = Some(color_palette_buffer); - } - - // Create bind group - let tree_bind_group = render_device.create_bind_group( - ShocoVoxRenderData::label(), - &pipeline.render_data_bind_group_layout, - &[ - bevy::render::render_resource::BindGroupEntry { - binding: 0, - resource: pipeline - .octree_meta_buffer - .as_ref() - .unwrap() - .as_entire_binding(), - }, - bevy::render::render_resource::BindGroupEntry { - binding: 1, - resource: pipeline.nodes_buffer.as_ref().unwrap().as_entire_binding(), - }, - bevy::render::render_resource::BindGroupEntry { - binding: 2, - resource: pipeline - .node_children_buffer - .as_ref() - .unwrap() - .as_entire_binding(), - }, - bevy::render::render_resource::BindGroupEntry { - binding: 3, - resource: pipeline.voxels_buffer.as_ref().unwrap().as_entire_binding(), - }, - bevy::render::render_resource::BindGroupEntry { - binding: 4, - resource: pipeline - .color_palette_buffer - .as_ref() - .unwrap() - .as_entire_binding(), - }, - bevy::render::render_resource::BindGroupEntry { - binding: 5, - resource: pipeline - .data_meta_bytes_buffer - .as_ref() - .unwrap() - .as_entire_binding(), - }, - ], - ); - pipeline.tree_bind_group = Some(tree_bind_group); - - //================================================================= - // Implementation with AsBindGroup - //================================================================= - // let tree_bind_group = render_data - // .as_bind_group( - // &pipeline.render_data_bind_group_layout, - // &render_device, - // &gpu_images, - // &fallback_image, - // ) - // .ok() - // .unwrap(); - - // // println!("bindings: {:?}", tree_bind_group.bindings); - // // let bindings = ; - // //TODO: set buffers by binding index(bindings[x].0), instead of array index - // // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = - // // &tree_bind_group.bindings[2].1 - // // { - // // debug_assert_eq!(tree_bind_group.bindings[2].0, 2); - // // pipeline.nodes_children_buffer = Some(buf.clone()); - // // } - // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = - // &tree_bind_group.bindings[0].1 - // { - // // compare binding to ShocoVoxRenderData field - // debug_assert_eq!(tree_bind_group.bindings[0].0, 5); - // pipeline.cache_bytes_buffer = Some(buf.clone()); - // } - - // pipeline.tree_bind_group = Some(tree_bind_group.bind_group); - - // Create the staging buffer helping in reading data from the GPU - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.data_meta_bytes).unwrap(); - if let Some(readable_data_meta_bytes_buffer) = &pipeline.readable_data_meta_bytes_buffer { - pipeline.render_queue.write_buffer( - readable_data_meta_bytes_buffer, - 0, - &buffer.into_inner(), - ) - } else { - let readable_data_meta_bytes_buffer = - render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Cache Bytes Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, - }); - pipeline.readable_data_meta_bytes_buffer = Some(readable_data_meta_bytes_buffer); - } - - pipeline.update_tree = false; - } -} - -pub(crate) fn handle_gpu_readback( - render_device: Res, - svx_data: Option>, - svx_pipeline: Option>, - mut world: ResMut, -) { - // // Data updates triggered by debug interface - // if let Some(svx_data) = svx_data { - // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); - // let svx_pipeline = svx_pipeline.unwrap(); - // if svx_data.do_the_thing { - // // GPU buffer read - // // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html - // let buffer_slice = svx_pipeline - // .readable_cache_bytes_buffer - // .as_ref() - // .unwrap() - // .slice(..); - // let (s, r) = crossbeam::channel::unbounded::<()>(); - // buffer_slice.map_async( - // bevy::render::render_resource::MapMode::Read, - // move |d| match d { - // Ok(_) => s.send(()).expect("Failed to send map update"), - // Err(err) => println!("Something's wrong: {err}"), - // }, - // ); - - // render_device - // .poll(bevy::render::render_resource::Maintain::wait()) - // .panic_on_timeout(); - - // r.recv().expect("Failed to receive the map_async message"); - // { - // let buffer_view = buffer_slice.get_mapped_range(); - - // let data = buffer_view - // .chunks(std::mem::size_of::()) - // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) - // .collect::>(); - // println!("data: {}", data[0]); - // } - - // svx_pipeline - // .readable_cache_bytes_buffer - // .as_ref() - // .unwrap() - // .unmap(); - - // render_data_mainworld.do_the_thing = false; - // } - // } -} - -pub(crate) fn handle_cache( - svx_data: Option>, - svx_pipeline: Option>, - mut world: ResMut, -) { - //TODO: Document that all components are lost during extract transition - // Data updates triggered by debug interface - // if let Some(svx_data) = svx_data { - // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); - // let svx_pipeline = svx_pipeline.unwrap(); - // let render_queue = &svx_pipeline.render_queue.0; - // if svx_data.do_the_thing { - // let mut data: [u32; 2] = [0; 2]; - // data[0] = svx_data.children_buffer[1]; - // data[1] = svx_data.children_buffer[0]; - - // // GPU buffer Write - // // render_data_mainworld.children_buffer[0] = data[0]; - // // render_data_mainworld.children_buffer[1] = data[1]; - // // use bevy::render::render_resource::encase::StorageBuffer; - // // let mut data_buffer = StorageBuffer::new(Vec::::new()); - // // data_buffer.write(&data).unwrap(); - // // render_queue.write_buffer( - // // svx_pipeline.nodes_children_buffer.as_ref().unwrap(), - // // 0, - // // &data_buffer.into_inner(), - // // ); - - // // GPU buffer read - // let buffer_slice = svx_pipeline.cache_bytes_buffer.as_ref().unwrap().slice(..); - - // // .map_async( - // // bevy::render::render_resource::MapMode::Read, - // // move |d| match d { - // // Ok(_) => println!("data!"), - // // Err(err) => println!("Something's wrong kiddo"), - // // }, - // // ); - // let data = 0; - // render_data_mainworld.cache_bytes[0] = data; - // println!("data: {}", render_data_mainworld.cache_bytes[0]); - - // render_data_mainworld.do_the_thing = false; - // } - // } -} - pub(crate) fn create_ouput_texture( resolution: [u32; 2], mut images: ResMut>, @@ -432,22 +60,17 @@ pub(crate) fn create_ouput_texture( impl Plugin for ShocoVoxRenderPlugin { fn build(&self, app: &mut App) { - app.add_plugins(( - ExtractResourcePlugin::::default(), - ExtractResourcePlugin::::default(), - )); + app.add_plugins(ExtractResourcePlugin::::default()); + app.add_plugins(ExtractResourcePlugin::::default()); let render_app = app.sub_app_mut(RenderApp); - render_app.add_systems( - ExtractSchedule, - handle_cache.in_set(RenderSet::PrepareResources), - ); - render_app.add_systems( - ExtractSchedule, - handle_gpu_readback.in_set(RenderSet::Cleanup), - ); + render_app.add_systems(ExtractSchedule, sync_with_main_world); render_app.add_systems( Render, - prepare_bind_groups.in_set(RenderSet::PrepareBindGroups), + ( + handle_cache.in_set(RenderSet::PrepareResources), + prepare_bind_groups.in_set(RenderSet::PrepareBindGroups), + handle_gpu_readback.in_set(RenderSet::Cleanup), + ), ); let mut render_graph = render_app.world_mut().resource_mut::(); render_graph.add_node( @@ -465,67 +88,3 @@ impl Plugin for ShocoVoxRenderPlugin { render_app.init_resource::(); } } - -const WORKGROUP_SIZE: u32 = 8; -impl render_graph::Node for ShocoVoxRenderNode { - fn update(&mut self, world: &mut World) { - { - let render_data = world.get_resource::(); - let svx_pipeline = world.resource::(); - let pipeline_cache = world.resource::(); - if !self.ready { - if let CachedPipelineState::Ok(_) = - pipeline_cache.get_compute_pipeline_state(svx_pipeline.update_pipeline) - { - self.ready = render_data.is_some(); - } - } - } - } - - fn run( - &self, - _graph: &mut render_graph::RenderGraphContext, - render_context: &mut RenderContext, - world: &World, - ) -> Result<(), render_graph::NodeRunError> { - let pipeline_cache = world.resource::(); - let svx_pipeline = world.resource::(); - - let command_encoder = render_context.command_encoder(); - if self.ready { - { - let mut pass = - command_encoder.begin_compute_pass(&ComputePassDescriptor::default()); - - pass.set_bind_group( - 0, - svx_pipeline.viewing_glass_bind_group.as_ref().unwrap(), - &[], - ); - pass.set_bind_group(1, svx_pipeline.tree_bind_group.as_ref().unwrap(), &[]); - let pipeline = pipeline_cache - .get_compute_pipeline(svx_pipeline.update_pipeline) - .unwrap(); - pass.set_pipeline(pipeline); - pass.dispatch_workgroups( - self.resolution[0] / WORKGROUP_SIZE, - self.resolution[1] / WORKGROUP_SIZE, - 1, - ); - } - - command_encoder.copy_buffer_to_buffer( - svx_pipeline.data_meta_bytes_buffer.as_ref().unwrap(), - 0, - svx_pipeline - .readable_data_meta_bytes_buffer - .as_ref() - .unwrap(), - 0, - std::mem::size_of::() as u64, - ) - } - Ok(()) - } -} diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs new file mode 100644 index 0000000..56774e0 --- /dev/null +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -0,0 +1,349 @@ +use crate::octree::raytracing::bevy::types::{ + ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline, ShocoVoxViewingGlass, +}; + +use bevy::{ + asset::AssetServer, + ecs::{ + system::{Res, ResMut}, + world::{FromWorld, World}, + }, + render::{ + render_asset::RenderAssets, + render_graph::{self}, + render_resource::{ + encase::{StorageBuffer, UniformBuffer}, + AsBindGroup, BufferInitDescriptor, BufferUsages, CachedPipelineState, + ComputePassDescriptor, ComputePipelineDescriptor, PipelineCache, + }, + renderer::{RenderContext, RenderDevice, RenderQueue}, + texture::{FallbackImage, GpuImage}, + }, +}; +use std::borrow::Cow; + +impl FromWorld for ShocoVoxRenderPipeline { + fn from_world(world: &mut World) -> Self { + let render_device = world.resource::(); + let viewing_glass_bind_group_layout = + ShocoVoxViewingGlass::bind_group_layout(render_device); + let render_data_bind_group_layout = ShocoVoxRenderData::bind_group_layout(render_device); + let shader = world + .resource::() + .load("shaders/viewport_render.wgsl"); + let pipeline_cache = world.resource::(); + let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { + label: None, + layout: vec![ + viewing_glass_bind_group_layout.clone(), + render_data_bind_group_layout.clone(), + ], + push_constant_ranges: Vec::new(), + shader, + shader_defs: vec![], + entry_point: Cow::from("update"), + }); + + ShocoVoxRenderPipeline { + victim_pointer: 0, + render_queue: world.resource::().clone(), + octree_meta_buffer: None, + nodes_buffer: None, + node_children_buffer: None, + voxels_buffer: None, + color_palette_buffer: None, + data_meta_bytes_buffer: None, + readable_data_meta_bytes_buffer: None, + update_tree: true, + viewing_glass_bind_group_layout, + render_data_bind_group_layout, + update_pipeline, + viewing_glass_bind_group: None, + tree_bind_group: None, + } + } +} + +const WORKGROUP_SIZE: u32 = 8; +impl render_graph::Node for ShocoVoxRenderNode { + fn update(&mut self, world: &mut World) { + { + let render_data = world.get_resource::(); + let svx_pipeline = world.resource::(); + let pipeline_cache = world.resource::(); + if !self.ready { + if let CachedPipelineState::Ok(_) = + pipeline_cache.get_compute_pipeline_state(svx_pipeline.update_pipeline) + { + self.ready = render_data.is_some(); + } + } + } + } + + fn run( + &self, + _graph: &mut render_graph::RenderGraphContext, + render_context: &mut RenderContext, + world: &World, + ) -> Result<(), render_graph::NodeRunError> { + let pipeline_cache = world.resource::(); + let svx_pipeline = world.resource::(); + + let command_encoder = render_context.command_encoder(); + if self.ready { + { + let mut pass = + command_encoder.begin_compute_pass(&ComputePassDescriptor::default()); + + pass.set_bind_group( + 0, + svx_pipeline.viewing_glass_bind_group.as_ref().unwrap(), + &[], + ); + pass.set_bind_group(1, svx_pipeline.tree_bind_group.as_ref().unwrap(), &[]); + let pipeline = pipeline_cache + .get_compute_pipeline(svx_pipeline.update_pipeline) + .unwrap(); + pass.set_pipeline(pipeline); + pass.dispatch_workgroups( + self.resolution[0] / WORKGROUP_SIZE, + self.resolution[1] / WORKGROUP_SIZE, + 1, + ); + } + + command_encoder.copy_buffer_to_buffer( + svx_pipeline.data_meta_bytes_buffer.as_ref().unwrap(), + 0, + svx_pipeline + .readable_data_meta_bytes_buffer + .as_ref() + .unwrap(), + 0, + std::mem::size_of::() as u64, + ) + } + Ok(()) + } +} + +pub(crate) fn prepare_bind_groups( + gpu_images: Res>, + fallback_image: Res, + render_device: Res, + mut pipeline: ResMut, + octree_viewing_glass: Res, + render_data: Res, +) { + let bind_group = octree_viewing_glass + .as_bind_group( + &pipeline.viewing_glass_bind_group_layout, + &render_device, + &gpu_images, + &fallback_image, + ) + .ok() + .unwrap(); + pipeline.viewing_glass_bind_group = Some(bind_group.bind_group); + + if pipeline.update_tree { + //================================================================= + // Implementation with WGPU + //================================================================= + // Upload data to buffers + let mut buffer = UniformBuffer::new(Vec::::new()); + buffer.write(&render_data.octree_meta).unwrap(); + if let Some(metadata_buffer) = &pipeline.octree_meta_buffer { + pipeline + .render_queue + .write_buffer(metadata_buffer, 0, &buffer.into_inner()) + } else { + let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Metadata Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + pipeline.octree_meta_buffer = Some(metadata_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.nodes).unwrap(); + if let Some(nodes_buffer) = &pipeline.nodes_buffer { + pipeline + .render_queue + .write_buffer(nodes_buffer, 0, &buffer.into_inner()) + } else { + let nodes_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Nodes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.nodes_buffer = Some(nodes_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.node_children).unwrap(); + if let Some(children_buffer) = &pipeline.node_children_buffer { + pipeline + .render_queue + .write_buffer(children_buffer, 0, &buffer.into_inner()) + } else { + let children_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Children Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.node_children_buffer = Some(children_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.voxels).unwrap(); + if let Some(voxels_buffer) = &pipeline.voxels_buffer { + pipeline + .render_queue + .write_buffer(voxels_buffer, 0, &buffer.into_inner()) + } else { + let voxels_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Voxels Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.voxels_buffer = Some(voxels_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.data_meta_bytes).unwrap(); + if let Some(data_meta_bytes_buffer) = &pipeline.data_meta_bytes_buffer { + pipeline + .render_queue + .write_buffer(data_meta_bytes_buffer, 0, &buffer.into_inner()) + } else { + let data_meta_bytes_buffer = + render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Meta Bytes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, + }); + pipeline.data_meta_bytes_buffer = Some(data_meta_bytes_buffer); + } + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.color_palette).unwrap(); + if let Some(color_palette_buffer) = &pipeline.color_palette_buffer { + pipeline + .render_queue + .write_buffer(color_palette_buffer, 0, &buffer.into_inner()) + } else { + let color_palette_buffer = + render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Color Palette Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); + pipeline.color_palette_buffer = Some(color_palette_buffer); + } + + // Create bind group + let tree_bind_group = render_device.create_bind_group( + ShocoVoxRenderData::label(), + &pipeline.render_data_bind_group_layout, + &[ + bevy::render::render_resource::BindGroupEntry { + binding: 0, + resource: pipeline + .octree_meta_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 1, + resource: pipeline.nodes_buffer.as_ref().unwrap().as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 2, + resource: pipeline + .node_children_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 3, + resource: pipeline.voxels_buffer.as_ref().unwrap().as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 4, + resource: pipeline + .color_palette_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + bevy::render::render_resource::BindGroupEntry { + binding: 5, + resource: pipeline + .data_meta_bytes_buffer + .as_ref() + .unwrap() + .as_entire_binding(), + }, + ], + ); + pipeline.tree_bind_group = Some(tree_bind_group); + + //================================================================= + // Implementation with AsBindGroup + //================================================================= + // let tree_bind_group = render_data + // .as_bind_group( + // &pipeline.render_data_bind_group_layout, + // &render_device, + // &gpu_images, + // &fallback_image, + // ) + // .ok() + // .unwrap(); + + // // println!("bindings: {:?}", tree_bind_group.bindings); + // // let bindings = ; + // //TODO: set buffers by binding index(bindings[x].0), instead of array index + // // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = + // // &tree_bind_group.bindings[2].1 + // // { + // // debug_assert_eq!(tree_bind_group.bindings[2].0, 2); + // // pipeline.nodes_children_buffer = Some(buf.clone()); + // // } + // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = + // &tree_bind_group.bindings[0].1 + // { + // // compare binding to ShocoVoxRenderData field + // debug_assert_eq!(tree_bind_group.bindings[0].0, 5); + // pipeline.cache_bytes_buffer = Some(buf.clone()); + // } + + // pipeline.tree_bind_group = Some(tree_bind_group.bind_group); + + // Create the staging buffer helping in reading data from the GPU + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&render_data.data_meta_bytes).unwrap(); + if let Some(readable_data_meta_bytes_buffer) = &pipeline.readable_data_meta_bytes_buffer { + pipeline.render_queue.write_buffer( + readable_data_meta_bytes_buffer, + 0, + &buffer.into_inner(), + ) + } else { + let readable_data_meta_bytes_buffer = + render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Cache Bytes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + }); + pipeline.readable_data_meta_bytes_buffer = Some(readable_data_meta_bytes_buffer); + } + + pipeline.update_tree = false; + } +} diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index cb4e7dc..b3c6717 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -145,7 +145,6 @@ pub(crate) struct ShocoVoxRenderPipeline { pub(crate) update_pipeline: CachedComputePipelineId, // Render data buffers - pub(crate) viewport_buffer: Option, pub(crate) octree_meta_buffer: Option, pub(crate) nodes_buffer: Option, pub(crate) node_children_buffer: Option, From a31d427c685e65cfd49067354637cfbc3ad1dc59 Mon Sep 17 00:00:00 2001 From: davids91 Date: Wed, 6 Nov 2024 07:00:50 +0100 Subject: [PATCH 04/19] Started setting "used" bricks" --- assets/shaders/viewport_render.wgsl | 115 +++++++++++++++++++++++----- src/octree/raytracing/bevy/types.rs | 7 +- 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 863d47e..0de94c7 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -317,11 +317,101 @@ fn dda_step_to_next_sibling( // Unique to this implementation, not adapted from rust code, corresponds to: //crate::octree::raytracing::classic_raytracing_on_bevy_wgpu::set_node_meta_inner -fn get_node_occupancy_bitmap(node_key: u32, data_meta_bytes: u32) -> u32{ - if 0 == (node_key % 2) { - return ((data_meta_bytes & 0x0000FF00u) >> 8u ); - } else { - return ((data_meta_bytes & 0xFF000000u) >> 24u ); +fn get_node_occupancy_bitmap(node_key: u32, data_meta: u32) -> u32 { + // u32(i32(node_key % 2u) - 1) + // node key is even: (node_key % 2u) == 0 + // --> u32(i32(0) - 1) == 0xFFFFFFFF + // node key is odd: (node_key % 2u) == 1 + // --> u32(i32(1) - 1) == 0 + return ( + ( + ((data_meta & 0x0000FF00u) >> 8u ) + & u32(i32(node_key % 2u) - 1) + )|( + ((data_meta & 0xFF000000u) >> 24u ) + & ~u32(i32(node_key % 2u) - 1) + ) + ); +} + +// Unique to this implementation, not adapted from rust code +fn set_node_used(node_key: u32) { + var current_value; + var target_value; + + if 0 < ( // no need to set if already true + data_meta_bytes[node_key / 2] + & ( + 0x00000001u & u32(i32(node_key % 2u) - 1) + | 0x00010000u & ~u32(i32(node_key % 2u) - 1) + ) + ) { + return; + } + + loop{ + current_value = data_meta_bytes[node_key / 2]; + target_value = current_value | ( + 0x00000001u & u32(i32(node_key % 2u) - 1) + | 0x00010000u & ~u32(i32(node_key % 2u) - 1) + ); + let exchange_result = atomicCompareExchangeWeak( + &data_meta_bytes[node_key / 2], current_value, target_value + ); + if( + exchange_result.exchanged + || ( + 0 < ( + exchange_result.old_value + & ( + 0x00000001u & u32(i32(node_key % 2u) - 1) + | 0x00010000u & ~u32(i32(node_key % 2u) - 1) + ) + ) + ) + ){ + break; + } + } +} + +fn set_brick_used(node_key: u32) { + var current_value; + var target_value; + + if 0 < ( // no need to set if already true + data_meta_bytes[node_key / 2] + & ( + 0x00000002u & u32(i32(node_key % 2u) - 1) + | 0x00020000u & ~u32(i32(node_key % 2u) - 1) + ) + ) { + return; + } + + loop{ + current_value = data_meta_bytes[node_key / 2]; + target_value = current_value | ( + 0x00000002u & u32(i32(node_key % 2u) - 1) + | 0x00020000u & ~u32(i32(node_key % 2u) - 1) + ); + let exchange_result = atomicCompareExchangeWeak( + &data_meta_bytes[node_key / 2], current_value, target_value + ); + if( + exchange_result.exchanged + || ( + 0 < ( + exchange_result.old_value + & ( + 0x00000002u & u32(i32(node_key % 2u) - 1) + | 0x00020000u & ~u32(i32(node_key % 2u) - 1) + ) + ) + ) + ){ + break; + } } } @@ -354,19 +444,6 @@ fn position_in_bitmap_64bits(i: vec3u, dimension: u32) -> u32{ ); } -// Unique to this implementation, not adapted from rust code -fn get_occupancy_in_bitmap_64bits( - bit_position: u32, - bitmap_lsb: u32, - bitmap_msb: u32 -) -> bool { - // not possible to create a position mask directly, because of missing u64 type - if bit_position < 32 { - return 0 < (bitmap_lsb & u32(0x01u << bit_position)); - } - return 0 < (bitmap_msb & u32(0x01u << (bit_position - 32))); -} - struct BrickHit{ hit: bool, index: vec3u @@ -517,6 +594,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ current_bounds = Cube(vec3(0.), f32(octree_meta_data.octree_size)); node_stack_push(&node_stack, &node_stack_meta, OCTREE_ROOT_NODE_KEY); while(!node_stack_is_empty(node_stack_meta)) { + set_node_used(current_node_key); var leaf_miss = false; if ( // is_leaf ( @@ -792,7 +870,6 @@ fn update( } */// --- DEBUG --- - textureStore(output_texture, vec2u(invocation_id.xy), vec4f(rgb_result, 1.)); } diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index b3c6717..8193570 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -25,10 +25,10 @@ pub(crate) struct Voxelement { pub(crate) struct SizedNode { /// Cache index of where the data about this node is found in children_buffer /// - In case of internal nodes: - /// - 8 Index value of node children ( in cache ) + /// - 8 Index value of node children /// - In case of leaf nodes: - /// - Byte 1-4: Occupancy bitmap MSB - /// - Byte 5-8: Occupancy bitmap LSB + /// - Byte 1-4: Occupancy bitmap LSB + /// - Byte 5-8: Occupancy bitmap MSB /// - Byte 9-12: TBD /// - Byte 13-16: TBD /// - Byte 17-20: TBD @@ -106,6 +106,7 @@ pub struct ShocoVoxRenderData { /// | bit 0 | 1 in case node is used by the raytracing algorithm* | /// | bit 1 | 1 in case voxel brick is used by the raytracing algorithm | /// | bit 2 | 1 in case node is a leaf | + /// | ... | unused, potentially: 1 if node has user data | /// | ... | unused, potentially: 1 if node has voxels | /// | ... | unused, potentially: children_buffer size: 2 or 8 | /// | ... | unused, potentially: voxel brick size: 1, full or sparse | From 1793c9370c5deeef9f2c07aa6d2dba481ba19e63 Mon Sep 17 00:00:00 2001 From: davids91 Date: Wed, 6 Nov 2024 20:46:39 +0100 Subject: [PATCH 05/19] POC: Displaying "visible" bricks, stopping "used bit" update by pressing tab --- assets/shaders/viewport_render.wgsl | 63 +++++++++++++++++++++-------- src/octree/raytracing/bevy/data.rs | 48 ++++++++-------------- 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 0de94c7..15f74a1 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -336,8 +336,8 @@ fn get_node_occupancy_bitmap(node_key: u32, data_meta: u32) -> u32 { // Unique to this implementation, not adapted from rust code fn set_node_used(node_key: u32) { - var current_value; - var target_value; + var current_value: u32; + var target_value: u32; if 0 < ( // no need to set if already true data_meta_bytes[node_key / 2] @@ -375,28 +375,28 @@ fn set_node_used(node_key: u32) { } } -fn set_brick_used(node_key: u32) { - var current_value; - var target_value; +fn set_brick_used(key: u32) { + var current_value: u32; + var target_value: u32; if 0 < ( // no need to set if already true - data_meta_bytes[node_key / 2] + data_meta_bytes[key / 2] & ( - 0x00000002u & u32(i32(node_key % 2u) - 1) - | 0x00020000u & ~u32(i32(node_key % 2u) - 1) + 0x00000002u & u32(i32(key % 2u) - 1) + | 0x00020000u & ~u32(i32(key % 2u) - 1) ) ) { return; } loop{ - current_value = data_meta_bytes[node_key / 2]; + current_value = data_meta_bytes[key / 2]; target_value = current_value | ( - 0x00000002u & u32(i32(node_key % 2u) - 1) - | 0x00020000u & ~u32(i32(node_key % 2u) - 1) + 0x00000002u & u32(i32(key % 2u) - 1) + | 0x00020000u & ~u32(i32(key % 2u) - 1) ); let exchange_result = atomicCompareExchangeWeak( - &data_meta_bytes[node_key / 2], current_value, target_value + &data_meta_bytes[key / 2], current_value, target_value ); if( exchange_result.exchanged @@ -404,8 +404,8 @@ fn set_brick_used(node_key: u32) { 0 < ( exchange_result.old_value & ( - 0x00000002u & u32(i32(node_key % 2u) - 1) - | 0x00020000u & ~u32(i32(node_key % 2u) - 1) + 0x00000002u & u32(i32(key % 2u) - 1) + | 0x00020000u & ~u32(i32(key % 2u) - 1) ) ) ) @@ -594,7 +594,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ current_bounds = Cube(vec3(0.), f32(octree_meta_data.octree_size)); node_stack_push(&node_stack, &node_stack_meta, OCTREE_ROOT_NODE_KEY); while(!node_stack_is_empty(node_stack_meta)) { - set_node_used(current_node_key); + //set_node_used(current_node_key); var leaf_miss = false; if ( // is_leaf ( @@ -605,6 +605,16 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ && (0 != (data_meta_bytes[current_node_key / 2] & 0x00040000u)) ) ){ + let current_brick_key = ( + nodes[current_node_key].voxels_start_at + / (octree_meta_data.voxel_brick_dim * octree_meta_data.voxel_brick_dim * octree_meta_data.voxel_brick_dim) + ); + //set_brick_used(current_brick_key); + // +++ DEBUG +++ + if(0 == data_meta_bytes[arrayLength(&data_meta_bytes) - 1]){ + set_brick_used(current_brick_key); + } + // --- DEBUG --- let leaf_brick_hit = traverse_brick( ray, &ray_current_distance, nodes[current_node_key].voxels_start_at, node_children[nodes[current_node_key].children_starts_at], @@ -622,6 +632,26 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ current_bounds.size = round(current_bounds.size / f32(octree_meta_data.voxel_brick_dim)); current_bounds.min_position = current_bounds.min_position + vec3f(leaf_brick_hit.index) * current_bounds.size; + + /*/// +++ DEBUG +++ + var result_rgb = color_palette[voxels[hit_in_voxels].albedo_index]; + if 0 < ( // If brick marked "used" + data_meta_bytes[current_brick_key / 2] + & ( + 0x00000002u & u32(i32(current_brick_key % 2u) - 1) + | 0x00020000u & ~u32(i32(current_brick_key % 2u) - 1) + ) + ) { + result_rgb.r += 0.2; + } + return OctreeRayIntersection( + true, + result_rgb, + voxels[hit_in_voxels].content, + point_in_ray_at_distance(ray, ray_current_distance), + cube_impact_normal(current_bounds, point_in_ray_at_distance(ray, ray_current_distance)) + ); + *//// --- DEBUG --- return OctreeRayIntersection( true, color_palette[voxels[hit_in_voxels].albedo_index], @@ -629,6 +659,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ point_in_ray_at_distance(ray, ray_current_distance), cube_impact_normal(current_bounds, point_in_ray_at_distance(ray, ray_current_distance)) ); + } leaf_miss = true; } @@ -810,7 +841,7 @@ var voxels: array; var color_palette: array; @group(1) @binding(5) -var data_meta_bytes: array; +var data_meta_bytes: array>; @compute @workgroup_size(8, 8, 1) fn update( diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 3fbb8ab..133dfb9 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -97,7 +97,9 @@ where let mut voxels = Vec::new(); let mut color_palette = Vec::new(); // Size of meta for one element is 2 Bytes, - let mut data_meta_bytes = vec![0u32; self.nodes.len() / 2]; + // let mut data_meta_bytes = vec![0u32; self.nodes.len() / 2]; + //DEBUG interface +1 element + let mut data_meta_bytes = vec![0u32; (self.nodes.len() / 2) + 1]; // Build up Nodes let mut map_to_node_index_in_nodes_buffer = HashMap::new(); @@ -134,6 +136,7 @@ where ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32, ]); nodes[map_to_node_index_in_nodes_buffer[&i]].voxels_start_at = voxels.len() as u32; + debug_assert_eq!(0, voxels.len() % (DIM * DIM * DIM)); for z in 0..DIM { for y in 0..DIM { for x in 0..DIM { @@ -254,6 +257,7 @@ pub(crate) fn sync_with_main_world(// svx_data: Option>, // mut world: ResMut, ) { + // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); } pub(crate) fn handle_cache( @@ -262,42 +266,22 @@ pub(crate) fn handle_cache( ) { //TODO: Document that all components are lost during extract transition // Data updates triggered by debug interface - // if let Some(svx_data) = svx_data { - // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); + // if let Some(mut svx_data) = svx_data { // let svx_pipeline = svx_pipeline.unwrap(); // let render_queue = &svx_pipeline.render_queue.0; // if svx_data.do_the_thing { - // let mut data: [u32; 2] = [0; 2]; - // data[0] = svx_data.children_buffer[1]; - // data[1] = svx_data.children_buffer[0]; - // // GPU buffer Write - // // render_data_mainworld.children_buffer[0] = data[0]; - // // render_data_mainworld.children_buffer[1] = data[1]; - // // use bevy::render::render_resource::encase::StorageBuffer; - // // let mut data_buffer = StorageBuffer::new(Vec::::new()); - // // data_buffer.write(&data).unwrap(); - // // render_queue.write_buffer( - // // svx_pipeline.nodes_children_buffer.as_ref().unwrap(), - // // 0, - // // &data_buffer.into_inner(), - // // ); - - // // GPU buffer read - // let buffer_slice = svx_pipeline.cache_bytes_buffer.as_ref().unwrap().slice(..); - - // // .map_async( - // // bevy::render::render_resource::MapMode::Read, - // // move |d| match d { - // // Ok(_) => println!("data!"), - // // Err(err) => println!("Something's wrong kiddo"), - // // }, - // // ); - // let data = 0; - // render_data_mainworld.cache_bytes[0] = data; - // println!("data: {}", render_data_mainworld.cache_bytes[0]); + // let data: u32 = 1; + // use bevy::render::render_resource::encase::StorageBuffer; + // let mut data_buffer = StorageBuffer::new(Vec::::new()); + // data_buffer.write(&data).unwrap(); + // render_queue.write_buffer( + // svx_pipeline.data_meta_bytes_buffer.as_ref().unwrap(), + // ((svx_data.data_meta_bytes.len() - 1) * std::mem::size_of::()) as u64, + // &data_buffer.into_inner(), + // ); - // render_data_mainworld.do_the_thing = false; + // svx_data.do_the_thing = false; // } // } } From 972420cec74e458995c2cbe33a1d7a51b6f638ed Mon Sep 17 00:00:00 2001 From: davids91 Date: Wed, 6 Nov 2024 22:19:43 +0100 Subject: [PATCH 06/19] Made Nodechildren uniform --- assets/shaders/viewport_render.wgsl | 13 ++++--------- src/octree/raytracing/bevy/data.rs | 24 ++++++++++++++---------- src/octree/raytracing/bevy/types.rs | 28 +++++++++++++--------------- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 15f74a1..b0ef05a 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -617,8 +617,8 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ // --- DEBUG --- let leaf_brick_hit = traverse_brick( ray, &ray_current_distance, nodes[current_node_key].voxels_start_at, - node_children[nodes[current_node_key].children_starts_at], - node_children[nodes[current_node_key].children_starts_at + 1], + node_children[current_node_key * 8u], + node_children[current_node_key * 8u + 1u], current_bounds, ray_scale_factors, direction_lut_index ); if leaf_brick_hit.hit == true { @@ -702,9 +702,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ } var target_bounds = child_bounds_for(current_bounds, target_octant); - var target_child_key = node_children[ - nodes[current_node_key].children_starts_at + target_octant - ]; + var target_child_key = node_children[current_node_key * 8u + target_octant]; if ( target_child_key < arrayLength(&nodes) //!crate::object_pool::key_is_valid && 0 != ( @@ -734,9 +732,7 @@ fn get_by_ray(ray: Line) -> OctreeRayIntersection{ target_octant = step_octant(target_octant, step_vec); if OOB_OCTANT != target_octant { target_bounds = child_bounds_for(current_bounds, target_octant); - target_child_key = node_children[ - nodes[current_node_key].children_starts_at + target_octant - ]; + target_child_key = node_children[current_node_key * 8u + target_octant]; } if ( @@ -801,7 +797,6 @@ fn is_empty(e: Voxelement) -> bool { } struct SizedNode { - children_starts_at: u32, voxels_start_at: u32, } diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 133dfb9..e82253e 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -92,23 +92,23 @@ where ), }; - let mut nodes = Vec::new(); - let mut node_children = Vec::new(); - let mut voxels = Vec::new(); + let mut nodes = Vec::with_capacity(self.nodes.len()); + let mut voxels = Vec::with_capacity(self.nodes.len() * DIM * DIM * DIM); let mut color_palette = Vec::new(); - // Size of meta for one element is 2 Bytes, - // let mut data_meta_bytes = vec![0u32; self.nodes.len() / 2]; - //DEBUG interface +1 element - let mut data_meta_bytes = vec![0u32; (self.nodes.len() / 2) + 1]; + // Size of meta for one element is 2 Bytes, so array should be half + 1 for odd numbers + let mut data_meta_bytes = Vec::with_capacity((self.nodes.len() / 2) + 1); + let mut node_children = Vec::with_capacity(self.nodes.len() * 8); // Build up Nodes let mut map_to_node_index_in_nodes_buffer = HashMap::new(); for node_key in 0..self.nodes.len() { if self.nodes.key_is_valid(node_key) { map_to_node_index_in_nodes_buffer.insert(node_key as usize, nodes.len()); + if 0 == (nodes.len() % 2) { + data_meta_bytes.push(0); + } self.set_meta_bytes_for_node(&mut data_meta_bytes, node_key, nodes.len()); nodes.push(SizedNode { - children_start_at: empty_marker(), voxels_start_at: empty_marker(), }); } @@ -120,8 +120,6 @@ where if !self.nodes.key_is_valid(i) { continue; } - nodes[map_to_node_index_in_nodes_buffer[&i]].children_start_at = - node_children.len() as u32; if let NodeContent::Leaf(data) = self.nodes.get(i) { debug_assert!(matches!( self.node_children[i].content, @@ -134,6 +132,12 @@ where node_children.extend_from_slice(&[ (occupied_bits & 0x00000000FFFFFFFF) as u32, ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32, + 0, + 0, + 0, + 0, + 0, + 0, ]); nodes[map_to_node_index_in_nodes_buffer[&i]].voxels_start_at = voxels.len() as u32; debug_assert_eq!(0, voxels.len() % (DIM * DIM * DIM)); diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 8193570..ee15633 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -23,20 +23,6 @@ pub(crate) struct Voxelement { #[derive(Clone, ShaderType)] pub(crate) struct SizedNode { - /// Cache index of where the data about this node is found in children_buffer - /// - In case of internal nodes: - /// - 8 Index value of node children - /// - In case of leaf nodes: - /// - Byte 1-4: Occupancy bitmap LSB - /// - Byte 5-8: Occupancy bitmap MSB - /// - Byte 9-12: TBD - /// - Byte 13-16: TBD - /// - Byte 17-20: TBD - /// - Byte 21-24: TBD - /// - Byte 25-28: TBD - /// - Byte 29-32: TBD - pub(crate) children_start_at: u32, - /// Cache index of where the voxel values contained in the node start inside the voxels buffer, /// or a "none_value". Should the field contain an index, the next voxel_brick_dim^3 elements /// inside the @voxels array count as part of the voxels associated with the node @@ -106,9 +92,9 @@ pub struct ShocoVoxRenderData { /// | bit 0 | 1 in case node is used by the raytracing algorithm* | /// | bit 1 | 1 in case voxel brick is used by the raytracing algorithm | /// | bit 2 | 1 in case node is a leaf | + /// | ... | unused, potentially: 1 if node has children | /// | ... | unused, potentially: 1 if node has user data | /// | ... | unused, potentially: 1 if node has voxels | - /// | ... | unused, potentially: children_buffer size: 2 or 8 | /// | ... | unused, potentially: voxel brick size: 1, full or sparse | /// |---------------------------------------------------------------------| /// | Byte 1 | nodes lvl2 occupancy bitmap | @@ -125,6 +111,18 @@ pub struct ShocoVoxRenderData { #[storage(1, visibility(compute))] pub(crate) nodes: Vec, + /// [u32; 8] for each node, with the structure: + /// - In case of internal nodes: + /// - 8 Index value of node children + /// - In case of leaf nodes: + /// - Byte 1-4: Occupancy bitmap LSB + /// - Byte 5-8: Occupancy bitmap MSB + /// - Byte 9-12: TBD + /// - Byte 13-16: TBD + /// - Byte 17-20: TBD + /// - Byte 21-24: TBD + /// - Byte 25-28: TBD + /// - Byte 29-32: TBD #[storage(2, visibility(compute))] pub node_children: Vec, From 80c44fc7e8e1bda003e6f18ecb350174bf907616 Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 16 Nov 2024 07:00:15 +0100 Subject: [PATCH 07/19] metadata buffer to store 8 brick used bits for one index + minor renames --- Cargo.toml | 2 +- assets/shaders/viewport_render.wgsl | 99 ++++++++++++++++------ src/octree/raytracing/bevy/data.rs | 111 +++++++++++++------------ src/octree/raytracing/bevy/pipeline.rs | 26 +++--- src/octree/raytracing/bevy/types.rs | 25 +++--- 5 files changed, 160 insertions(+), 103 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d081b8b..a460b71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shocovox-rs" -version = "0.4.1" +version = "0.4.2" edition = "2021" authors = ["Dávid Tóth "] license = "MIT OR Apache-2.0" diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index aec1928..6bbc932 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -235,25 +235,49 @@ fn dda_step_to_next_sibling( } // Unique to this implementation, not adapted from rust code -/// Sets the bit true for the current not under the corresponding bitmask -/// bitmask for node: 0x00000001u -/// bitmask for brick: 0x00000002u -fn set_element_used(node_key: u32, mask: u32) { +/// Sets the used bit true for the given node +fn set_node_used(node_key: u32) { var current_value: u32; var target_value: u32; - if 0 < (nodes[node_key] & mask) // no need to set if already true + if 0 != (metadata[node_key] & 0x01u) // no need to set if already true { return; } loop{ - current_value = nodes[node_key]; - target_value = nodes[node_key] | mask; + current_value = metadata[node_key]; + target_value = metadata[node_key] | 0x01u; let exchange_result = atomicCompareExchangeWeak( - &nodes[node_key], current_value, target_value + &metadata[node_key], current_value, target_value ); - if(exchange_result.exchanged || 0 < (exchange_result.old_value & mask)){ + if(exchange_result.exchanged || 0 < (exchange_result.old_value & 0x01u)){ + break; + } + } +} + +// Unique to this implementation, not adapted from rust code +/// Sets the used bit true for the given brick +fn set_brick_used(brick_index: u32) { + var current_value: u32; + var target_value: u32; + + if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) // no need to set if already true + { + return; + } + + loop{ + current_value = metadata[brick_index / 8]; + target_value = metadata[brick_index / 8] | (0x01u << (24u + (brick_index % 8u))); + let exchange_result = atomicCompareExchangeWeak( + &metadata[brick_index / 8], current_value, target_value + ); + if( + exchange_result.exchanged + || 0 != ( exchange_result.old_value & (0x01u << (24u + (brick_index % 8u))) ) + ){ break; } } @@ -377,18 +401,18 @@ fn probe_brick( ray_scale_factors: ptr, direction_lut_index: u32, ) -> OctreeRayIntersection { - let brick_start_index = node_children[((leaf_node_key * 8) + brick_octant)]; - //set_element_used(brick_start_index, 0x00000002u); + let brick_index = node_children[((leaf_node_key * 8) + brick_octant)]; + //set_brick_used(brick_index); // +++ DEBUG +++ - if(0 == nodes[arrayLength(&nodes) - 1]){ - set_element_used(brick_start_index, 0x00000002u); + if(0 == metadata[arrayLength(&metadata) - 1]){ + set_brick_used(brick_index); } - if(0 != ((0x01u << (8 + brick_octant)) & nodes[leaf_node_key])) { // brick is not empty - if(0 == ((0x01u << (16 + brick_octant)) & nodes[leaf_node_key])) { // brick is solid + if(0 != ((0x01u << (8 + brick_octant)) & metadata[leaf_node_key])) { // brick is not empty + if(0 == ((0x01u << (16 + brick_octant)) & metadata[leaf_node_key])) { // brick is solid // Whole brick is solid, ray hits it at first connection return OctreeRayIntersection( true, - color_palette[brick_start_index], // Albedo is in color_palette + color_palette[brick_index], // Albedo is in color_palette, data is not a brick index in this case 0, // user data lost for now as color palette doesn't have it.. sorry point_in_ray_at_distance(ray, *ray_current_distance), cube_impact_normal(*brick_bounds, point_in_ray_at_distance(ray, *ray_current_distance)) @@ -396,14 +420,15 @@ fn probe_brick( } else { // brick is parted let leaf_brick_hit = traverse_brick( ray, ray_current_distance, - brick_start_index, + brick_index, brick_bounds, ray_scale_factors, direction_lut_index ); if leaf_brick_hit.hit == true { /// +++ DEBUG +++ + /*// Color in bricks displayed var result_rgb = color_palette[voxels[leaf_brick_hit.flat_index].albedo_index]; - if 0 < (nodes[brick_start_index] & 0x00000002u) { + if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) { result_rgb.r += 0.2; } return OctreeRayIntersection( @@ -421,7 +446,28 @@ fn probe_brick( ), point_in_ray_at_distance(ray, *ray_current_distance) ) - ); + );*/ + + // Display marked bricks only + if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) { + return OctreeRayIntersection( + true, + color_palette[voxels[leaf_brick_hit.flat_index].albedo_index], + voxels[leaf_brick_hit.flat_index].content, + point_in_ray_at_distance(ray, *ray_current_distance), + cube_impact_normal( + Cube( + (*brick_bounds).min_position + ( + vec3f(leaf_brick_hit.index) + * round((*brick_bounds).size / f32(octree_meta_data.voxel_brick_dim)) + ), + round((*brick_bounds).size / f32(octree_meta_data.voxel_brick_dim)), + ), + point_in_ray_at_distance(ray, *ray_current_distance) + ) + ); + } + /*return OctreeRayIntersection( true, result_rgb, //color_palette[voxels[leaf_brick_hit.flat_index].albedo_index], @@ -472,6 +518,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ /*// +++ DEBUG +++ var outer_safety = 0; */// --- DEBUG --- + set_node_used(OCTREE_ROOT_NODE_KEY); while target_octant != OOB_OCTANT { /*// +++ DEBUG +++ outer_safety += 1; @@ -482,14 +529,13 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ } */ // --- DEBUG --- current_node_key = OCTREE_ROOT_NODE_KEY; - current_node_meta = nodes[OCTREE_ROOT_NODE_KEY]; + current_node_meta = metadata[OCTREE_ROOT_NODE_KEY]; current_bounds = Cube(vec3(0.), f32(octree_meta_data.octree_size)); node_stack_push(&node_stack, &node_stack_meta, OCTREE_ROOT_NODE_KEY); /*// +++ DEBUG +++ var safety = 0; */// --- DEBUG --- while(!node_stack_is_empty(node_stack_meta)) { - //set_element_used(current_node_key, 0x00000001u); /*// +++ DEBUG +++ safety += 1; if(f32(safety) > f32(octree_meta_data.octree_size) * sqrt(30.)) { @@ -567,7 +613,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ ); if(EMPTY_MARKER != node_stack_last(node_stack_meta)){ current_node_key = node_stack[node_stack_last(node_stack_meta)]; - current_node_meta = nodes[current_node_key]; + current_node_meta = metadata[current_node_key]; target_octant = step_octant( hash_region( // parent current target octant // current bound center @@ -591,7 +637,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ if ( ( 0 == (0x00000004 & current_node_meta) // node is not a leaf - && target_child_key < arrayLength(&nodes) //!crate::object_pool::key_is_valid + && target_child_key < arrayLength(&metadata) //!crate::object_pool::key_is_valid ) && ( // There is overlap in node occupancy and potential ray hit area 0 != ( @@ -613,8 +659,9 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ ) ) { // PUSH + set_node_used(target_child_key); current_node_key = target_child_key; - current_node_meta = nodes[current_node_key]; + current_node_meta = metadata[current_node_key]; current_bounds = target_bounds; target_octant = hash_region( // child_target_octant (point_in_ray_at_distance(ray, ray_current_distance) - target_bounds.min_position), @@ -649,7 +696,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ if ( target_octant == OOB_OCTANT || ( // In case the current internal node has a valid target child - target_child_key < arrayLength(&nodes) //crate::object_pool::key_is_valid + target_child_key < arrayLength(&metadata) //crate::object_pool::key_is_valid && 0 == (0x00000004 & current_node_meta) // node is not a leaf && ( // target child is in the area the ray can potentially hit 0 != ( @@ -743,7 +790,7 @@ var viewport: Viewport; var octree_meta_data: OctreeMetaData; @group(1) @binding(1) -var nodes: array>; +var metadata: array>; @group(1) @binding(2) var node_children: array; diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 85f9f13..c4fba04 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -12,7 +12,7 @@ use crate::{ use bevy::{ ecs::system::{Res, ResMut}, math::Vec4, - render::{renderer::RenderDevice, MainWorld}, + render::renderer::RenderDevice, }; use std::collections::HashMap; @@ -20,8 +20,9 @@ impl Octree where T: Default + Clone + Copy + PartialEq + VoxelData, { + /// Updates metadata to set every element it might contain unused fn meta_set_element_unused(sized_node_meta: &mut u32) { - *sized_node_meta = *sized_node_meta & 0xFFFFFFFC; + *sized_node_meta = *sized_node_meta & 0x00FFFFFC; } /// Updates the meta element value to store that the corresponding node is a leaf node @@ -333,7 +334,7 @@ where self.octree_size as f32, ), }, - nodes, + metadata: nodes, node_children, voxels, node_occupied_bits, @@ -355,46 +356,46 @@ pub(crate) fn handle_gpu_readback( svx_data: Option>, svx_pipeline: Option>, ) { - // Data updates triggered by debug interface - if let Some(mut svx_data) = svx_data { - let svx_pipeline = svx_pipeline.unwrap(); - if svx_data.do_the_thing { - // GPU buffer read - // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html - let buffer_slice = svx_pipeline - .readable_nodes_buffer - .as_ref() - .unwrap() - .slice(..); - let (s, r) = crossbeam::channel::unbounded::<()>(); - buffer_slice.map_async( - bevy::render::render_resource::MapMode::Read, - move |d| match d { - Ok(_) => s.send(()).expect("Failed to send map update"), - Err(err) => println!("Something's wrong: {err}"), - }, - ); + // // Data updates triggered by debug interface + // if let Some(mut svx_data) = svx_data { + // let svx_pipeline = svx_pipeline.unwrap(); + // if svx_data.do_the_thing { + // // GPU buffer read + // // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html + // let buffer_slice = svx_pipeline + // .readable_nodes_buffer + // .as_ref() + // .unwrap() + // .slice(..); + // let (s, r) = crossbeam::channel::unbounded::<()>(); + // buffer_slice.map_async( + // bevy::render::render_resource::MapMode::Read, + // move |d| match d { + // Ok(_) => s.send(()).expect("Failed to send map update"), + // Err(err) => println!("Something's wrong: {err}"), + // }, + // ); - render_device - .poll(bevy::render::render_resource::Maintain::wait()) - .panic_on_timeout(); + // render_device + // .poll(bevy::render::render_resource::Maintain::wait()) + // .panic_on_timeout(); - r.recv().expect("Failed to receive the map_async message"); - { - let buffer_view = buffer_slice.get_mapped_range(); + // r.recv().expect("Failed to receive the map_async message"); + // { + // let buffer_view = buffer_slice.get_mapped_range(); - let data = buffer_view - .chunks(std::mem::size_of::()) - .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) - .collect::>(); - println!("data: {}", data[0]); - } + // let data = buffer_view + // .chunks(std::mem::size_of::()) + // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + // .collect::>(); + // println!("data: {}", data[0]); + // } - svx_pipeline.readable_nodes_buffer.as_ref().unwrap().unmap(); + // svx_pipeline.readable_nodes_buffer.as_ref().unwrap().unmap(); - svx_data.do_the_thing = false; - } - } + // svx_data.do_the_thing = false; + // } + // } } pub(crate) fn sync_with_main_world(// svx_data: Option>, @@ -410,22 +411,22 @@ pub(crate) fn handle_cache( ) { //TODO: Document that all components are lost during extract transition // Data updates triggered by debug interface - // if let Some(mut svx_data) = svx_data { - // let svx_pipeline = svx_pipeline.unwrap(); - // let render_queue = &svx_pipeline.render_queue.0; - // if svx_data.do_the_thing { - // // GPU buffer Write - // let data: u32 = 1; - // use bevy::render::render_resource::encase::StorageBuffer; - // let mut data_buffer = StorageBuffer::new(Vec::::new()); - // data_buffer.write(&data).unwrap(); - // render_queue.write_buffer( - // svx_pipeline.data_meta_bytes_buffer.as_ref().unwrap(), - // ((svx_data.data_meta_bytes.len() - 1) * std::mem::size_of::()) as u64, - // &data_buffer.into_inner(), - // ); + if let Some(mut svx_data) = svx_data { + let svx_pipeline = svx_pipeline.unwrap(); + let render_queue = &svx_pipeline.render_queue.0; + if svx_data.do_the_thing { + // GPU buffer Write + let data: u32 = 1; + use bevy::render::render_resource::encase::StorageBuffer; + let mut data_buffer = StorageBuffer::new(Vec::::new()); + data_buffer.write(&data).unwrap(); + render_queue.write_buffer( + svx_pipeline.metadata_buffer.as_ref().unwrap(), + ((svx_data.metadata.len() - 1) * std::mem::size_of::()) as u64, + &data_buffer.into_inner(), + ); - // svx_data.do_the_thing = false; - // } - // } + svx_data.do_the_thing = false; + } + } } diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index 81ca301..9dde0e7 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -48,12 +48,12 @@ impl FromWorld for ShocoVoxRenderPipeline { victim_pointer: 0, render_queue: world.resource::().clone(), octree_meta_buffer: None, - nodes_buffer: None, + metadata_buffer: None, node_children_buffer: None, node_ocbits_buffer: None, voxels_buffer: None, color_palette_buffer: None, - readable_nodes_buffer: None, + readable_metadata_buffer: None, update_tree: true, viewing_glass_bind_group_layout, render_data_bind_group_layout, @@ -114,9 +114,9 @@ impl render_graph::Node for ShocoVoxRenderNode { } command_encoder.copy_buffer_to_buffer( - svx_pipeline.nodes_buffer.as_ref().unwrap(), + svx_pipeline.metadata_buffer.as_ref().unwrap(), 0, - svx_pipeline.readable_nodes_buffer.as_ref().unwrap(), + svx_pipeline.readable_metadata_buffer.as_ref().unwrap(), 0, std::mem::size_of::() as u64, ) @@ -165,8 +165,8 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.nodes).unwrap(); - if let Some(nodes_buffer) = &pipeline.nodes_buffer { + buffer.write(&render_data.metadata).unwrap(); + if let Some(nodes_buffer) = &pipeline.metadata_buffer { pipeline .render_queue .write_buffer(nodes_buffer, 0, &buffer.into_inner()) @@ -176,7 +176,7 @@ pub(crate) fn prepare_bind_groups( contents: &buffer.into_inner(), usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, }); - pipeline.nodes_buffer = Some(nodes_buffer); + pipeline.metadata_buffer = Some(nodes_buffer); } let mut buffer = StorageBuffer::new(Vec::::new()); @@ -255,7 +255,11 @@ pub(crate) fn prepare_bind_groups( }, bevy::render::render_resource::BindGroupEntry { binding: 1, - resource: pipeline.nodes_buffer.as_ref().unwrap().as_entire_binding(), + resource: pipeline + .metadata_buffer + .as_ref() + .unwrap() + .as_entire_binding(), }, bevy::render::render_resource::BindGroupEntry { binding: 2, @@ -323,8 +327,8 @@ pub(crate) fn prepare_bind_groups( // Create the staging buffer helping in reading data from the GPU let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.nodes).unwrap(); - if let Some(readable_nodes_buffer) = &pipeline.readable_nodes_buffer { + buffer.write(&render_data.metadata).unwrap(); + if let Some(readable_nodes_buffer) = &pipeline.readable_metadata_buffer { pipeline .render_queue .write_buffer(readable_nodes_buffer, 0, &buffer.into_inner()) @@ -335,7 +339,7 @@ pub(crate) fn prepare_bind_groups( contents: &buffer.into_inner(), usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, }); - pipeline.readable_nodes_buffer = Some(readable_nodes_buffer); + pipeline.readable_metadata_buffer = Some(readable_nodes_buffer); } pipeline.update_tree = false; diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 5c48a66..007d5d7 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -55,16 +55,16 @@ pub struct ShocoVoxViewingGlass { pub struct ShocoVoxRenderData { pub do_the_thing: bool, //STRICTLY FOR DEBUG REASONS + /// Contains the properties of the Octree #[uniform(0, visibility(compute))] pub(crate) octree_meta: OctreeMetaData, - /// Composite field containing the properties of Nodes - /// Structure is the following: + /// Contains the properties of Nodes and Voxel Bricks /// _===================================================================_ /// | Byte 0 | Node properties | /// |---------------------------------------------------------------------| /// | bit 0 | 1 in case node is used by the raytracing algorithm *(2) | - /// | bit 1 | 1 in case voxel brick is used by the raytracing algorithm | + /// | bit 1 | unused | /// | bit 2 | 1 in case node is a leaf | /// | bit 3 | 1 in case node is uniform | /// | bit 4 | unused - potentially: 1 if node has voxels | @@ -72,22 +72,27 @@ pub struct ShocoVoxRenderData { /// | bit 6 | unused - potentially: voxel brick size: 1, full or sparse | /// | bit 7 | unused | /// |=====================================================================| - /// | Byte 1 | Child occupied | + /// | Byte 1 | Node Child occupied | /// |---------------------------------------------------------------------| /// | If Leaf | each bit is 0 if child brick is empty at octant *(1) | /// | If Node | unused | /// |=====================================================================| - /// | Byte 2 | Child structure | + /// | Byte 2 | Node Child structure | /// |---------------------------------------------------------------------| /// | If Leaf | each bit is 0 if child brick is solid, 1 if parted *(1) | /// | If Node | unused | /// |=====================================================================| - /// | Byte 3 | unused | + /// | Byte 3 | Voxel Bricks used *(3) | + /// |---------------------------------------------------------------------| + /// | each bit is 1 if voxel brick is used by the raytracing algorithm | /// `=====================================================================` /// *(1) Only first bit is used in case leaf is uniform - /// *(2) the same bit is used for node_children and node_occupied_bits + /// *(2) The same bit is used for node_children and node_occupied_bits + /// *(3) One index in the array covers 8 bricks, which is the theoretical maximum + /// number of bricks for one node. In practice however the number of bricks + /// are only 4-5 times more, than the number of nodes, because of the internal nodes. #[storage(1, visibility(compute))] - pub(crate) nodes: Vec, + pub(crate) metadata: Vec, /// Index values for Nodes, 8 value per @SizedNode entry. Each value points to: /// In case of Internal Nodes @@ -128,8 +133,8 @@ pub(crate) struct ShocoVoxRenderPipeline { // Render data buffers pub(crate) octree_meta_buffer: Option, - pub(crate) nodes_buffer: Option, - pub(crate) readable_nodes_buffer: Option, + pub(crate) metadata_buffer: Option, + pub(crate) readable_metadata_buffer: Option, pub(crate) node_children_buffer: Option, pub(crate) node_ocbits_buffer: Option, pub(crate) voxels_buffer: Option, From b056da4dbee0c96c15a78e0fb97cbac1ea6502ed Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 16 Nov 2024 10:21:12 +0100 Subject: [PATCH 08/19] Implemented OctreeGPUView, refactored bevy module --- Cargo.toml | 2 +- assets/shaders/viewport_render.wgsl | 12 +- examples/dot_cube.rs | 124 ++++- examples/minecraft.rs | 78 ++- src/octree/convert/bytecode.rs | 1 + src/octree/mod.rs | 2 + src/octree/node.rs | 4 +- src/octree/raytracing/bevy/data.rs | 658 +++++++++++++++---------- src/octree/raytracing/bevy/mod.rs | 24 +- src/octree/raytracing/bevy/pipeline.rs | 245 +++++++-- src/octree/raytracing/bevy/types.rs | 50 +- src/octree/raytracing/mod.rs | 4 +- src/octree/types.rs | 6 + 13 files changed, 809 insertions(+), 401 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a460b71..12fa46f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shocovox-rs" -version = "0.4.2" +version = "0.5.0" edition = "2021" authors = ["Dávid Tóth "] license = "MIT OR Apache-2.0" diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 6bbc932..55d4f09 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -283,6 +283,10 @@ fn set_brick_used(brick_index: u32) { } } +fn request_node(node: u32, child_octant: u32){ + +} + //crate::spatial::math::step_octant fn step_octant(octant: u32, step: ptr) -> u32 { return ( @@ -404,7 +408,7 @@ fn probe_brick( let brick_index = node_children[((leaf_node_key * 8) + brick_octant)]; //set_brick_used(brick_index); // +++ DEBUG +++ - if(0 == metadata[arrayLength(&metadata) - 1]){ + if(0 == debug_interface){ set_brick_used(brick_index); } if(0 != ((0x01u << (8 + brick_octant)) & metadata[leaf_node_key])) { // brick is not empty @@ -804,6 +808,12 @@ var voxels: array; @group(1) @binding(5) var color_palette: array; +// +++ DEBUG +++ +@group(1) @binding(6) +var debug_interface: u32; +// --- DEBUG --- + + @compute @workgroup_size(8, 8, 1) fn update( @builtin(global_invocation_id) invocation_id: vec3, diff --git a/examples/dot_cube.rs b/examples/dot_cube.rs index b4d02ad..12ce509 100644 --- a/examples/dot_cube.rs +++ b/examples/dot_cube.rs @@ -9,10 +9,8 @@ use iyes_perf_ui::{ #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{ - bevy::create_viewing_glass, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, - }, - Albedo, V3c, + raytracing::{OctreeGPUView, Ray, ShocoVoxRenderPlugin, Viewport}, + Albedo, Octree, V3c, VoxelData, }; #[cfg(feature = "bevy_wgpu")] @@ -24,6 +22,15 @@ const BRICK_DIMENSION: usize = 2; #[cfg(feature = "bevy_wgpu")] const TREE_SIZE: u32 = 16; +#[cfg(feature = "bevy_wgpu")] +#[derive(Resource)] +struct TreeResource +where + T: Default + Clone + PartialEq + VoxelData, +{ + tree: Octree, +} + #[cfg(feature = "bevy_wgpu")] fn main() { App::new() @@ -109,14 +116,14 @@ fn setup(mut commands: Commands, images: ResMut>) { } } - let render_data = tree.create_bevy_view(); - let viewing_glass = create_viewing_glass( - &Viewport { + let output_texture = tree.create_new_gpu_view( + Viewport { origin, direction: (V3c::new(0., 0., 0.) - origin).normalized(), w_h_fov: V3c::new(10., 10., 3.), }, DISPLAY_RESOLUTION, + &mut commands, images, ); @@ -130,13 +137,11 @@ fn setup(mut commands: Commands, images: ResMut>) { custom_size: Some(Vec2::new(1024., 768.)), ..default() }, - texture: viewing_glass.output_texture.clone(), + texture: output_texture, ..default() }); commands.spawn(Camera2dBundle::default()); - commands.insert_resource(render_data); - commands.insert_resource(viewing_glass); - + commands.insert_resource(TreeResource { tree }); commands.spawn(( PerfUiRoot::default(), PerfUiEntryFPS { @@ -163,31 +168,34 @@ struct DomePosition { yaw: f32, roll: f32, } - #[cfg(feature = "bevy_wgpu")] -fn rotate_camera( - angles_query: Query<&mut DomePosition>, - mut viewing_glass: ResMut, -) { +fn rotate_camera(angles_query: Query<&mut DomePosition>, mut tree_view: ResMut) { let (yaw, roll) = (angles_query.single().yaw, angles_query.single().roll); - let radius = angles_query.single().radius; - viewing_glass.viewport.origin = V3c::new( + + tree_view.viewing_glass.viewport.origin = V3c::new( radius / 2. + yaw.sin() * radius, radius + roll.sin() * radius * 2., radius / 2. + yaw.cos() * radius, ); - viewing_glass.viewport.direction = - (V3c::unit(radius / 2.) - viewing_glass.viewport.origin).normalized(); + tree_view.viewing_glass.viewport.direction = + (V3c::unit(radius / 2.) - tree_view.viewing_glass.viewport.origin).normalized(); } #[cfg(feature = "bevy_wgpu")] fn handle_zoom( keys: Res>, - mut viewing_glass: ResMut, + mut tree_view: ResMut, mut angles_query: Query<&mut DomePosition>, + tree: Res>, ) { - const ADDITION: f32 = 0.02; + if keys.pressed(KeyCode::Delete) { + tree_view.do_the_thing = true; + } + // if tree_view.is_changed() { + // println!("changed!! --> {:?}", tree_view.read_back); + // } + const ADDITION: f32 = 0.05; let angle_update_fn = |angle, delta| -> f32 { let new_angle = angle + delta; if new_angle < 360. { @@ -196,11 +204,75 @@ fn handle_zoom( 0. } }; + if keys.pressed(KeyCode::Tab) { + // Render the current view with CPU + let viewport_up_direction = V3c::new(0., 1., 0.); + let viewport_right_direction = viewport_up_direction + .cross(tree_view.viewing_glass.viewport.direction) + .normalized(); + let pixel_width = + tree_view.viewing_glass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; + let pixel_height = + tree_view.viewing_glass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; + let viewport_bottom_left = tree_view.viewing_glass.viewport.origin + + (tree_view.viewing_glass.viewport.direction + * tree_view.viewing_glass.viewport.w_h_fov.z) + - (viewport_up_direction * (tree_view.viewing_glass.viewport.w_h_fov.y / 2.)) + - (viewport_right_direction * (tree_view.viewing_glass.viewport.w_h_fov.x / 2.)); + + // define light + let diffuse_light_normal = V3c::new(0., -1., 1.).normalized(); + + use image::ImageBuffer; + use image::Rgb; + let mut img = ImageBuffer::new(DISPLAY_RESOLUTION[0], DISPLAY_RESOLUTION[1]); + + // cast each ray for a hit + for x in 0..DISPLAY_RESOLUTION[0] { + for y in 0..DISPLAY_RESOLUTION[1] { + let actual_y_in_image = DISPLAY_RESOLUTION[1] - y - 1; + //from the origin of the camera to the current point of the viewport + let glass_point = viewport_bottom_left + + viewport_right_direction * x as f32 * pixel_width + + viewport_up_direction * y as f32 * pixel_height; + let ray = Ray { + origin: tree_view.viewing_glass.viewport.origin, + direction: (glass_point - tree_view.viewing_glass.viewport.origin).normalized(), + }; + + use std::io::Write; + std::io::stdout().flush().ok().unwrap(); + + if let Some(hit) = tree.tree.get_by_ray(&ray) { + let (data, _, normal) = hit; + //Because both vector should be normalized, the dot product should be 1*1*cos(angle) + //That means it is in range -1, +1, which should be accounted for + let diffuse_light_strength = + 1. - (normal.dot(&diffuse_light_normal) / 2. + 0.5); + img.put_pixel( + x, + actual_y_in_image, + Rgb([ + (data.r as f32 * diffuse_light_strength) as u8, + (data.g as f32 * diffuse_light_strength) as u8, + (data.b as f32 * diffuse_light_strength) as u8, + ]), + ); + } else { + img.put_pixel(x, actual_y_in_image, Rgb([128, 128, 128])); + } + } + } + + img.save("example_junk_cpu_render.png").ok().unwrap(); + } + let multiplier = if keys.pressed(KeyCode::ShiftLeft) { 10.0 // Doesn't have any effect?! } else { 1.0 }; + if keys.pressed(KeyCode::ArrowUp) { angles_query.single_mut().roll = angle_update_fn(angles_query.single().roll, ADDITION); } @@ -222,12 +294,12 @@ fn handle_zoom( angles_query.single_mut().radius *= 1. + 0.02 * multiplier; } if keys.pressed(KeyCode::Home) { - viewing_glass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; - viewing_glass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; } if keys.pressed(KeyCode::End) { - viewing_glass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; - viewing_glass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; } } diff --git a/examples/minecraft.rs b/examples/minecraft.rs index c33f176..d040679 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -1,16 +1,10 @@ -#[cfg(feature = "bevy_wgpu")] -use shocovox_rs::octree::Octree; - #[cfg(feature = "bevy_wgpu")] use bevy::{prelude::*, window::WindowPlugin}; #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{ - bevy::create_viewing_glass, Ray, ShocoVoxRenderData, ShocoVoxRenderPlugin, - ShocoVoxViewingGlass, Viewport, - }, - Albedo, V3c, VoxelData, + raytracing::{OctreeGPUView, Ray, ShocoVoxRenderPlugin, Viewport}, + Albedo, Octree, V3c, VoxelData, }; #[cfg(feature = "bevy_wgpu")] @@ -85,9 +79,8 @@ fn setup(mut commands: Commands, images: ResMut>) { radius: tree.get_size() as f32 * 2.2, }); - let render_data = tree.create_bevy_view(); - let viewing_glass = create_viewing_glass( - &Viewport { + let output_texture = tree.create_new_gpu_view( + Viewport { origin: V3c { x: 0., y: 0., @@ -101,20 +94,20 @@ fn setup(mut commands: Commands, images: ResMut>) { w_h_fov: V3c::new(10., 10., 3.), }, DISPLAY_RESOLUTION, + &mut commands, images, ); + commands.spawn(SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(1024., 768.)), ..default() }, - texture: viewing_glass.output_texture.clone(), + texture: output_texture, ..default() }); commands.spawn(Camera2dBundle::default()); commands.insert_resource(TreeResource { tree }); - commands.insert_resource(render_data); - commands.insert_resource(viewing_glass); commands.spawn(( PerfUiRoot::default(), @@ -144,35 +137,32 @@ struct DomePosition { } #[cfg(feature = "bevy_wgpu")] -fn rotate_camera( - angles_query: Query<&mut DomePosition>, - mut viewing_glass: ResMut, -) { +fn rotate_camera(angles_query: Query<&mut DomePosition>, mut tree_view: ResMut) { let (yaw, roll) = (angles_query.single().yaw, angles_query.single().roll); - let radius = angles_query.single().radius; - viewing_glass.viewport.origin = V3c::new( + + tree_view.viewing_glass.viewport.origin = V3c::new( radius / 2. + yaw.sin() * radius, radius + roll.sin() * radius * 2., radius / 2. + yaw.cos() * radius, ); - viewing_glass.viewport.direction = - (V3c::unit(radius / 2.) - viewing_glass.viewport.origin).normalized(); + tree_view.viewing_glass.viewport.direction = + (V3c::unit(radius / 2.) - tree_view.viewing_glass.viewport.origin).normalized(); } #[cfg(feature = "bevy_wgpu")] fn handle_zoom( keys: Res>, - mut viewing_glass: ResMut, - svx_data: Option>, + mut tree_view: ResMut, mut angles_query: Query<&mut DomePosition>, tree: Res>, ) { - if let Some(ref svx_data) = svx_data { - if svx_data.is_changed() { - println!("changed!! --> {:?}", svx_data.node_children[0..2].to_vec()); - } + if keys.pressed(KeyCode::Delete) { + tree_view.do_the_thing = true; } + // if tree_view.is_changed() { + // println!("changed!! --> {:?}", tree_view.read_back); + // } const ADDITION: f32 = 0.05; let angle_update_fn = |angle, delta| -> f32 { let new_angle = angle + delta; @@ -186,14 +176,17 @@ fn handle_zoom( // Render the current view with CPU let viewport_up_direction = V3c::new(0., 1., 0.); let viewport_right_direction = viewport_up_direction - .cross(viewing_glass.viewport.direction) + .cross(tree_view.viewing_glass.viewport.direction) .normalized(); - let pixel_width = viewing_glass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; - let pixel_height = viewing_glass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; - let viewport_bottom_left = viewing_glass.viewport.origin - + (viewing_glass.viewport.direction * viewing_glass.viewport.w_h_fov.z) - - (viewport_up_direction * (viewing_glass.viewport.w_h_fov.y / 2.)) - - (viewport_right_direction * (viewing_glass.viewport.w_h_fov.x / 2.)); + let pixel_width = + tree_view.viewing_glass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; + let pixel_height = + tree_view.viewing_glass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; + let viewport_bottom_left = tree_view.viewing_glass.viewport.origin + + (tree_view.viewing_glass.viewport.direction + * tree_view.viewing_glass.viewport.w_h_fov.z) + - (viewport_up_direction * (tree_view.viewing_glass.viewport.w_h_fov.y / 2.)) + - (viewport_right_direction * (tree_view.viewing_glass.viewport.w_h_fov.x / 2.)); // define light let diffuse_light_normal = V3c::new(0., -1., 1.).normalized(); @@ -211,8 +204,8 @@ fn handle_zoom( + viewport_right_direction * x as f32 * pixel_width + viewport_up_direction * y as f32 * pixel_height; let ray = Ray { - origin: viewing_glass.viewport.origin, - direction: (glass_point - viewing_glass.viewport.origin).normalized(), + origin: tree_view.viewing_glass.viewport.origin, + direction: (glass_point - tree_view.viewing_glass.viewport.origin).normalized(), }; use std::io::Write; @@ -269,15 +262,12 @@ fn handle_zoom( angles_query.single_mut().radius *= 1. + 0.02 * multiplier; } if keys.pressed(KeyCode::Home) { - viewing_glass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; - viewing_glass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; } if keys.pressed(KeyCode::End) { - viewing_glass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; - viewing_glass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; - } - if keys.pressed(KeyCode::Delete) { - svx_data.unwrap().do_the_thing = true; + tree_view.viewing_glass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; + tree_view.viewing_glass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; } } diff --git a/src/octree/convert/bytecode.rs b/src/octree/convert/bytecode.rs index 7210069..846b77c 100644 --- a/src/octree/convert/bytecode.rs +++ b/src/octree/convert/bytecode.rs @@ -409,6 +409,7 @@ where )?; let node_children = Vec::decode_bencode_object(list.next_object()?.unwrap())?; Ok(Self { + views: Vec::new(), auto_simplify, octree_size: root_size, nodes, diff --git a/src/octree/mod.rs b/src/octree/mod.rs index 0efe2d3..37dee73 100644 --- a/src/octree/mod.rs +++ b/src/octree/mod.rs @@ -82,6 +82,8 @@ where let root_node_key = nodes.push(NodeContent::Nothing); // The first element is the root Node assert!(root_node_key == 0); Ok(Self { + #[cfg(feature = "bevy_wgpu")] + views: Vec::new(), auto_simplify: true, octree_size: size, nodes, diff --git a/src/octree/node.rs b/src/octree/node.rs index 059aed2..ee4d90d 100644 --- a/src/octree/node.rs +++ b/src/octree/node.rs @@ -90,7 +90,7 @@ where ///#################################################################################### impl BrickData where - T: VoxelData + PartialEq + Clone + Copy + Default, + T: VoxelData + PartialEq + Clone + Default, { /// Provides occupancy information for the part of the brick corresponmding /// to the given octant based on the contents of the brick @@ -231,7 +231,7 @@ where if homogeneous_type.is_empty() { *self = BrickData::Empty; } else { - *self = BrickData::Solid(*homogeneous_type); + *self = BrickData::Solid(homogeneous_type.clone()); } true } else { diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index c4fba04..77a92ed 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -1,25 +1,152 @@ use crate::object_pool::empty_marker; use crate::{ octree::{ - raytracing::bevy::types::{ - OctreeMetaData, ShocoVoxRenderData, ShocoVoxRenderPipeline, Voxelement, + raytracing::bevy::{ + create_output_texture, + types::{ + OctreeGPUView, OctreeMetaData, ShocoVoxRenderData, ShocoVoxRenderPipeline, + ShocoVoxViewingGlass, Viewport, Voxelement, + }, }, types::{NodeChildrenArray, NodeContent}, - Albedo, BrickData, Octree, V3c, VoxelData, + BrickData, Octree, V3c, VoxelData, }, spatial::lut::BITMAP_MASK_FOR_OCTANT_LUT, }; use bevy::{ ecs::system::{Res, ResMut}, math::Vec4, + prelude::{Assets, Commands, Handle, Image}, render::renderer::RenderDevice, }; -use std::collections::HashMap; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use super::types::OctreeGPUDataHandler; impl Octree where T: Default + Clone + Copy + PartialEq + VoxelData, { + //############################################################################## + // ███████ █████████ ███████████ ███████████ ██████████ ██████████ + // ███░░░░░███ ███░░░░░███░█░░░███░░░█░░███░░░░░███ ░░███░░░░░█░░███░░░░░█ + // ███ ░░███ ███ ░░░ ░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ + // ░███ ░███░███ ░███ ░██████████ ░██████ ░██████ + // ░███ ░███░███ ░███ ░███░░░░░███ ░███░░█ ░███░░█ + // ░░███ ███ ░░███ ███ ░███ ░███ ░███ ░███ ░ █ ░███ ░ █ + // ░░░███████░ ░░█████████ █████ █████ █████ ██████████ ██████████ + // ░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // █████████ ███████████ █████ █████ + // ███░░░░░███░░███░░░░░███░░███ ░░███ + // ███ ░░░ ░███ ░███ ░███ ░███ + // ░███ ░██████████ ░███ ░███ + // ░███ █████ ░███░░░░░░ ░███ ░███ + // ░░███ ░░███ ░███ ░███ ░███ + // ░░█████████ █████ ░░████████ + // ░░░░░░░░░ ░░░░░ ░░░░░░░░ + // █████ █████ █████ ██████████ █████ ███ █████ + // ░░███ ░░███ ░░███ ░░███░░░░░█░░███ ░███ ░░███ + // ░███ ░███ ░███ ░███ █ ░ ░███ ░███ ░███ + // ░███ ░███ ░███ ░██████ ░███ ░███ ░███ + // ░░███ ███ ░███ ░███░░█ ░░███ █████ ███ + // ░░░█████░ ░███ ░███ ░ █ ░░░█████░█████░ + // ░░███ █████ ██████████ ░░███ ░░███ + //############################################################################## + + /// Creates GPU compatible data renderable on the GPU from an octree + pub fn create_new_gpu_view( + &self, + viewport: Viewport, + resolution: [u32; 2], + commands: &mut Commands, + images: ResMut>, + ) -> Handle { + let mut gpu_data_handler = OctreeGPUDataHandler { + debug_gpu_interface: None, + readable_debug_gpu_interface: None, + render_data: ShocoVoxRenderData { + debug_gpu_interface: 0, + octree_meta: OctreeMetaData { + octree_size: self.octree_size, + voxel_brick_dim: DIM as u32, + ambient_light_color: V3c::new(1., 1., 1.), + ambient_light_position: V3c::new( + self.octree_size as f32, + self.octree_size as f32, + self.octree_size as f32, + ), + }, + metadata: Vec::with_capacity(self.nodes.len()), + node_children: Vec::with_capacity(self.nodes.len() * 8), + voxels: Vec::new(), + node_occupied_bits: Vec::with_capacity(self.nodes.len() * 2), + color_palette: Vec::new(), + }, + // victim_node_pointer: 0, + // victim_brick_pointer: 0, + map_to_color_index_in_palette: HashMap::new(), + map_to_node_index_in_nodes_buffer: HashMap::new(), + }; + + // Build up Nodes + for node_key in 0..self.nodes.len() { + if self.nodes.key_is_valid(node_key) { + gpu_data_handler.add_node_properties(&self, node_key); + } + } + + // Build up node content + for node_key in 0..self.nodes.len() { + if self.nodes.key_is_valid(node_key) { + gpu_data_handler.add_node_content(&self, node_key); + } + } + + let output_texture = create_output_texture(resolution, images); + commands.insert_resource(OctreeGPUView { + do_the_thing: false, + read_back: 0, + data_handler: Arc::new(Mutex::new(gpu_data_handler)), + viewing_glass: ShocoVoxViewingGlass { + output_texture: output_texture.clone(), + viewport: viewport, + }, + }); + output_texture + } +} + +//############################################################################## +// ███████ █████████ ███████████ ███████████ ██████████ ██████████ +// ███░░░░░███ ███░░░░░███░█░░░███░░░█░░███░░░░░███ ░░███░░░░░█░░███░░░░░█ +// ███ ░░███ ███ ░░░ ░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ +// ░███ ░███░███ ░███ ░██████████ ░██████ ░██████ +// ░███ ░███░███ ░███ ░███░░░░░███ ░███░░█ ░███░░█ +// ░░███ ███ ░░███ ███ ░███ ░███ ░███ ░███ ░ █ ░███ ░ █ +// ░░░███████░ ░░█████████ █████ █████ █████ ██████████ ██████████ +// ░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ +// █████████ ███████████ █████ █████ +// ███░░░░░███░░███░░░░░███░░███ ░░███ +// ███ ░░░ ░███ ░███ ░███ ░███ +// ░███ ░██████████ ░███ ░███ +// ░███ █████ ░███░░░░░░ ░███ ░███ +// ░░███ ░░███ ░███ ░███ ░███ +// ░░█████████ █████ ░░████████ +// ░░░░░░░░░ ░░░░░ ░░░░░░░░ +// ██████████ █████████ ███████████ █████████ +// ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ +// ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ +// ░███ ░███ ░███████████ ░███ ░███████████ +// ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ +// ░███ ███ ░███ ░███ ░███ ░███ ░███ +// ██████████ █████ █████ █████ █████ █████ +// ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ +//############################################################################## + +impl OctreeGPUDataHandler { /// Updates metadata to set every element it might contain unused fn meta_set_element_unused(sized_node_meta: &mut u32) { *sized_node_meta = *sized_node_meta & 0x00FFFFFC; @@ -44,11 +171,13 @@ where /// * `sized_node_meta` - the bytes to update /// * `brick` - the brick to describe into the bytes /// * `brick_octant` - the octant to update in the bytes - fn meta_add_leaf_brick_structure( + fn meta_add_leaf_brick_structure( sized_node_meta: &mut u32, brick: &BrickData, brick_octant: usize, - ) { + ) where + T: Default + Clone + PartialEq + VoxelData, + { match brick { BrickData::Empty => {} // Child structure properties already set to NIL BrickData::Solid(_voxel) => { @@ -67,7 +196,12 @@ where /// Updates the given meta element value to store the leaf structure of the given node /// the given NodeContent reference is expected to be a leaf node - fn meta_set_leaf_structure(sized_node_meta: &mut u32, leaf: &NodeContent) { + fn meta_set_leaf_structure( + sized_node_meta: &mut u32, + leaf: &NodeContent, + ) where + T: Default + Copy + Clone + PartialEq + VoxelData, + { match leaf { NodeContent::UniformLeaf(brick) => { Self::meta_set_is_leaf(sized_node_meta, true); @@ -88,7 +222,10 @@ where } /// Creates the descriptor bytes for the given node - fn create_node_properties(node: &NodeContent) -> u32 { + fn create_node_properties(node: &NodeContent) -> u32 + where + T: Default + Copy + Clone + PartialEq + VoxelData, + { let mut meta = 0; Self::meta_set_element_unused(&mut meta); match node { @@ -103,43 +240,177 @@ where meta } + fn add_node_properties(&mut self, tree: &Octree, node_key: usize) + where + T: Default + Copy + Clone + PartialEq + VoxelData, + { + if tree.nodes.key_is_valid(node_key) { + self.map_to_node_index_in_nodes_buffer + .insert(node_key, self.render_data.metadata.len()); + self.render_data + .metadata + .push(Self::create_node_properties(tree.nodes.get(node_key))); + } else { + panic!("Trying to query invalid node key to set node metadata!"); + } + } + + fn add_node_content(&mut self, tree: &Octree, node_key: usize) + where + T: Default + Copy + Clone + PartialEq + VoxelData, + { + let occupied_bits = tree.stored_occupied_bits(node_key); + self.render_data.node_occupied_bits.extend_from_slice(&[ + (occupied_bits & 0x00000000FFFFFFFF) as u32, + ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32, + ]); + match tree.nodes.get(node_key) { + NodeContent::UniformLeaf(brick) => { + debug_assert!( + matches!( + tree.node_children[node_key].content, + NodeChildrenArray::OccupancyBitmap(_) + ), + "Expected Uniform leaf to have OccupancyBitmap(_) instead of {:?}", + tree.node_children[node_key].content + ); + + let (brick_index, brick_added) = self.add_brick(brick); + + self.render_data.node_children.extend_from_slice(&[ + brick_index, + empty_marker(), + empty_marker(), + empty_marker(), + empty_marker(), + empty_marker(), + empty_marker(), + empty_marker(), + ]); + #[cfg(debug_assertions)] + { + if !brick_added { + // If no brick was added, the occupied bits should either be empty or full + if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = + tree.node_children[node_key].content + { + debug_assert!(occupied_bits == 0 || occupied_bits == u64::MAX); + } + } + } + } + NodeContent::Leaf(bricks) => { + debug_assert!( + matches!( + tree.node_children[node_key].content, + NodeChildrenArray::OccupancyBitmap(_) + ), + "Expected Leaf to have OccupancyBitmaps(_) instead of {:?}", + tree.node_children[node_key].content + ); + + let mut children = vec![empty_marker(); 8]; + for octant in 0..8 { + let (brick_index, brick_added) = self.add_brick(&bricks[octant]); + + children[octant] = brick_index; + #[cfg(debug_assertions)] + { + if !brick_added { + // If no brick was added, the relevant occupied bits should either be empty or full + if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = + tree.node_children[node_key].content + { + debug_assert!( + 0 == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] + || BITMAP_MASK_FOR_OCTANT_LUT[octant] + == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] + ); + } + } + } + } + self.render_data.node_children.extend_from_slice(&children); + } + NodeContent::Internal(_) => { + for c in 0..8 { + let child_index = &tree.node_children[node_key][c]; + if *child_index != empty_marker() { + debug_assert!(self + .map_to_node_index_in_nodes_buffer + .contains_key(&(*child_index as usize))); + self.render_data.node_children.push( + self.map_to_node_index_in_nodes_buffer[&(*child_index as usize)] as u32, + ); + } else { + self.render_data.node_children.push(*child_index); + } + } + } + NodeContent::Nothing => { + self.render_data + .node_children + .extend_from_slice(&[empty_marker(); 8]); + } + } + println!( + "bricks: {:?} <> nodes: {:?}", + self.render_data.voxels.len() / (DIM * DIM * DIM), + self.render_data.metadata.len() + ); + + // debug_assert_eq!( + // self.render_data.metadata.len() * 2, + // self.render_data.node_occupied_bits.len(), + // "Node occupancy bitmaps length({:?}) should match node count({:?})!", + // self.render_data.node_occupied_bits.len(), + // self.render_data.metadata.len(), + // ); + + // debug_assert_eq!( + // self.render_data.metadata.len() * 8, + // self.render_data.node_children.len(), + // "Node count({:?}) should match length of children buffer({:?})!", + // self.render_data.metadata.len(), + // self.render_data.node_children.len() + // ); + } + /// Loads a brick into the provided voxels vector and color palette /// * `brick` - The brick to upload /// * `voxels` - The destination buffer /// * `color_palette` - The used color palette /// * `map_to_color_index_in_palette` - Indexing helper for the color palette /// * `returns` - the identifier to set in @SizedNode and true if a new brick was aded to the voxels vector - fn add_brick_to_vec( - brick: &BrickData, - voxels: &mut Vec, - color_palette: &mut Vec, - map_to_color_index_in_palette: &mut HashMap, - ) -> (u32, bool) { + fn add_brick(&mut self, brick: &BrickData) -> (u32, bool) + where + T: Default + Clone + PartialEq + VoxelData, + { match brick { BrickData::Empty => (empty_marker(), false), BrickData::Solid(voxel) => { let albedo = voxel.albedo(); if let std::collections::hash_map::Entry::Vacant(e) = - map_to_color_index_in_palette.entry(albedo) + self.map_to_color_index_in_palette.entry(albedo) { - e.insert(color_palette.len()); - color_palette.push(Vec4::new( + e.insert(self.render_data.color_palette.len()); + self.render_data.color_palette.push(Vec4::new( albedo.r as f32 / 255., albedo.g as f32 / 255., albedo.b as f32 / 255., albedo.a as f32 / 255., )); } - (map_to_color_index_in_palette[&albedo] as u32, false) + (self.map_to_color_index_in_palette[&albedo] as u32, false) } BrickData::Parted(brick) => { - voxels.reserve(DIM * DIM * DIM); - let brick_index = voxels.len() / (DIM * DIM * DIM); + self.render_data.voxels.reserve(DIM * DIM * DIM); + let brick_index = self.render_data.voxels.len() / (DIM * DIM * DIM); debug_assert_eq!( - voxels.len() % (DIM * DIM * DIM), + self.render_data.voxels.len() % (DIM * DIM * DIM), 0, "Expected Voxel buffer length({:?}) to be divisble by {:?}", - voxels.len(), + self.render_data.voxels.len(), (DIM * DIM * DIM) ); for z in 0..DIM { @@ -147,19 +418,19 @@ where for x in 0..DIM { let albedo = brick[x][y][z].albedo(); if let std::collections::hash_map::Entry::Vacant(e) = - map_to_color_index_in_palette.entry(albedo) + self.map_to_color_index_in_palette.entry(albedo) { - e.insert(color_palette.len()); - color_palette.push(Vec4::new( + e.insert(self.render_data.color_palette.len()); + self.render_data.color_palette.push(Vec4::new( albedo.r as f32 / 255., albedo.g as f32 / 255., albedo.b as f32 / 255., albedo.a as f32 / 255., )); } - let albedo_index = map_to_color_index_in_palette[&albedo]; + let albedo_index = self.map_to_color_index_in_palette[&albedo]; - voxels.push(Voxelement { + self.render_data.voxels.push(Voxelement { albedo_index: albedo_index as u32, content: brick[x][y][z].user_data(), }); @@ -170,263 +441,130 @@ where } } } +} - /// Creates GPU compatible data renderable on the GPU from an octree - pub fn create_bevy_view(&self) -> ShocoVoxRenderData { - let mut nodes = Vec::with_capacity(self.nodes.len()); - let mut voxels = Vec::new(); - let mut node_occupied_bits = Vec::new(); - let mut color_palette = Vec::new(); - // Size of meta for one element is 2 Bytes, so array should be half + 1 for odd numbers - let mut node_children = Vec::with_capacity(self.nodes.len() * 8); - - // Build up Nodes - let mut map_to_node_index_in_nodes_buffer = HashMap::new(); - for i in 0..self.nodes.len() { - if self.nodes.key_is_valid(i) { - map_to_node_index_in_nodes_buffer.insert(i, nodes.len()); - nodes.push(Self::create_node_properties(self.nodes.get(i))); - } - } - - // Build up voxel content - let mut map_to_color_index_in_palette = HashMap::new(); - for i in 0..self.nodes.len() { - if !self.nodes.key_is_valid(i) { - continue; - } - let occupied_bits = self.stored_occupied_bits(i); - node_occupied_bits.extend_from_slice(&[ - (occupied_bits & 0x00000000FFFFFFFF) as u32, - ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32, - ]); - match self.nodes.get(i) { - NodeContent::UniformLeaf(brick) => { - debug_assert!( - matches!( - self.node_children[i].content, - NodeChildrenArray::OccupancyBitmap(_) - ), - "Expected Uniform leaf to have OccupancyBitmap(_) instead of {:?}", - self.node_children[i].content - ); - - let (brick_index, brick_added) = Self::add_brick_to_vec( - brick, - &mut voxels, - &mut color_palette, - &mut map_to_color_index_in_palette, - ); - - node_children.extend_from_slice(&[ - brick_index, - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - ]); - #[cfg(debug_assertions)] - { - if !brick_added { - // If no brick was added, the occupied bits should either be empty or full - if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = - self.node_children[i].content - { - debug_assert!(occupied_bits == 0 || occupied_bits == u64::MAX); - } - } - } - } - NodeContent::Leaf(bricks) => { - debug_assert!( - matches!( - self.node_children[i].content, - NodeChildrenArray::OccupancyBitmap(_) - ), - "Expected Leaf to have OccupancyBitmaps(_) instead of {:?}", - self.node_children[i].content - ); - - let mut children = vec![empty_marker(); 8]; - for octant in 0..8 { - let (brick_index, brick_added) = Self::add_brick_to_vec( - &bricks[octant], - &mut voxels, - &mut color_palette, - &mut map_to_color_index_in_palette, - ); - - children[octant] = brick_index; - #[cfg(debug_assertions)] - { - if !brick_added { - // If no brick was added, the relevant occupied bits should either be empty or full - if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = - self.node_children[i].content - { - debug_assert!( - 0 == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] - || BITMAP_MASK_FOR_OCTANT_LUT[octant] - == occupied_bits - & BITMAP_MASK_FOR_OCTANT_LUT[octant] - ); - } - } - } - } - node_children.extend_from_slice(&children); - } - NodeContent::Internal(_) => { - for c in 0..8 { - let child_index = &self.node_children[i][c]; - if *child_index != empty_marker() { - debug_assert!(map_to_node_index_in_nodes_buffer - .contains_key(&(*child_index as usize))); - node_children.push( - map_to_node_index_in_nodes_buffer[&(*child_index as usize)] as u32, - ); - } else { - node_children.push(*child_index); - } - } - } - NodeContent::Nothing => { - node_children.extend_from_slice(&[empty_marker(); 8]); - } - } - } - - // +++ DEBUG +++ - println!( - "bricks: {:?} <> nodes: {:?}", - voxels.len() / (DIM * DIM * DIM), - nodes.len() - ); - nodes.push(0); // Additional element in node array for debug purposes - /*debug_assert_eq!( - nodes.len() * 2, - node_occupied_bits.len(), - "Node occupancy bitmaps length({:?}) should match node count({:?})!", - node_occupied_bits.len(), - nodes.len(), - ); - - debug_assert_eq!( - nodes.len() * 8, - node_children.len(), - "Node count({:?}) should match length of children buffer({:?})!", - nodes.len(), - node_children.len() - );*/ - // --- DEBUG --- - ShocoVoxRenderData { - do_the_thing: false, - octree_meta: OctreeMetaData { - octree_size: self.octree_size, - voxel_brick_dim: DIM as u32, - ambient_light_color: V3c::new(1., 1., 1.), - ambient_light_position: V3c::new( - self.octree_size as f32, - self.octree_size as f32, - self.octree_size as f32, - ), - }, - metadata: nodes, - node_children, - voxels, - node_occupied_bits, - color_palette, - } - } - - pub(crate) fn insert_elements_into_cache( - &self, - render_data: &mut ShocoVoxRenderData, - requested_nodes: Vec, - ) { - //TODO: find the first unused element, and overwrite it with the item +pub(crate) fn sync_with_main_world( + tree_view: Option>, + mut world: ResMut, +) { + if let Some(tree_view) = tree_view { + let mut tree_view_mainworld = world.get_resource_mut::().unwrap(); + tree_view_mainworld.read_back = tree_view.read_back; } } +//############################################################################## +// █████████ ███████████ █████ █████ +// ███░░░░░███░░███░░░░░███░░███ ░░███ +// ███ ░░░ ░███ ░███ ░███ ░███ +// ░███ ░██████████ ░███ ░███ +// ░███ █████ ░███░░░░░░ ░███ ░███ +// ░░███ ░░███ ░███ ░███ ░███ +// ░░█████████ █████ ░░████████ +// ░░░░░░░░░ ░░░░░ ░░░░░░░░ +// ███████████ ██████████ █████████ ██████████ +// ░░███░░░░░███ ░░███░░░░░█ ███░░░░░███ ░░███░░░░███ +// ░███ ░███ ░███ █ ░ ░███ ░███ ░███ ░░███ +// ░██████████ ░██████ ░███████████ ░███ ░███ +// ░███░░░░░███ ░███░░█ ░███░░░░░███ ░███ ░███ +// ░███ ░███ ░███ ░ █ ░███ ░███ ░███ ███ +// █████ █████ ██████████ █████ █████ ██████████ +// ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ +//############################################################################## pub(crate) fn handle_gpu_readback( render_device: Res, - svx_data: Option>, - svx_pipeline: Option>, + tree_gpu_view: Option>, ) { - // // Data updates triggered by debug interface - // if let Some(mut svx_data) = svx_data { - // let svx_pipeline = svx_pipeline.unwrap(); - // if svx_data.do_the_thing { - // // GPU buffer read - // // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html - // let buffer_slice = svx_pipeline - // .readable_nodes_buffer - // .as_ref() - // .unwrap() - // .slice(..); - // let (s, r) = crossbeam::channel::unbounded::<()>(); - // buffer_slice.map_async( - // bevy::render::render_resource::MapMode::Read, - // move |d| match d { - // Ok(_) => s.send(()).expect("Failed to send map update"), - // Err(err) => println!("Something's wrong: {err}"), - // }, - // ); - - // render_device - // .poll(bevy::render::render_resource::Maintain::wait()) - // .panic_on_timeout(); - - // r.recv().expect("Failed to receive the map_async message"); - // { - // let buffer_view = buffer_slice.get_mapped_range(); + // Data updates triggered by debug interface + if let Some(mut tree_gpu_view) = tree_gpu_view { + if tree_gpu_view.do_the_thing { + let received_data; + { + let data_handler = tree_gpu_view.data_handler.lock().unwrap(); + // GPU buffer read + // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html + let buffer_slice = data_handler + .readable_debug_gpu_interface + .as_ref() + .unwrap() + .slice(..); + let (s, r) = crossbeam::channel::unbounded::<()>(); + buffer_slice.map_async(bevy::render::render_resource::MapMode::Read, move |d| { + match d { + Ok(_) => s.send(()).expect("Failed to send map update"), + Err(err) => println!("Something's wrong: {err}"), + } + }); - // let data = buffer_view - // .chunks(std::mem::size_of::()) - // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) - // .collect::>(); - // println!("data: {}", data[0]); - // } + render_device + .poll(bevy::render::render_resource::Maintain::wait()) + .panic_on_timeout(); - // svx_pipeline.readable_nodes_buffer.as_ref().unwrap().unmap(); + r.recv().expect("Failed to receive the map_async message"); + { + let buffer_view = buffer_slice.get_mapped_range(); - // svx_data.do_the_thing = false; - // } - // } + let data = buffer_view + .chunks(std::mem::size_of::()) + .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + .collect::>(); + received_data = data[0]; + } + data_handler + .readable_debug_gpu_interface + .as_ref() + .unwrap() + .unmap(); + } + tree_gpu_view.read_back = received_data; + tree_gpu_view.do_the_thing = false; + } + } } -pub(crate) fn sync_with_main_world(// svx_data: Option>, - // svx_pipeline: Option>, - // mut world: ResMut, -) { - // let mut render_data_mainworld = world.get_resource_mut::().unwrap(); -} +//############################################################################## +// █████████ ███████████ █████ █████ +// ███░░░░░███░░███░░░░░███░░███ ░░███ +// ███ ░░░ ░███ ░███ ░███ ░███ +// ░███ ░██████████ ░███ ░███ +// ░███ █████ ░███░░░░░░ ░███ ░███ +// ░░███ ░░███ ░███ ░███ ░███ +// ░░█████████ █████ ░░████████ +// ░░░░░░░░░ ░░░░░ ░░░░░░░░ -pub(crate) fn handle_cache( - svx_data: Option>, +// █████ ███ █████ ███████████ █████ ███████████ ██████████ +// ░░███ ░███ ░░███ ░░███░░░░░███ ░░███ ░█░░░███░░░█░░███░░░░░█ +// ░███ ░███ ░███ ░███ ░███ ░███ ░ ░███ ░ ░███ █ ░ +// ░███ ░███ ░███ ░██████████ ░███ ░███ ░██████ +// ░░███ █████ ███ ░███░░░░░███ ░███ ░███ ░███░░█ +// ░░░█████░█████░ ░███ ░███ ░███ ░███ ░███ ░ █ +// ░░███ ░░███ █████ █████ █████ █████ ██████████ +// ░░░ ░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ +//############################################################################## +pub(crate) fn write_to_gpu( + tree_gpu_view: Option>, svx_pipeline: Option>, ) { //TODO: Document that all components are lost during extract transition // Data updates triggered by debug interface - if let Some(mut svx_data) = svx_data { + if let Some(tree_gpu_view) = tree_gpu_view { let svx_pipeline = svx_pipeline.unwrap(); let render_queue = &svx_pipeline.render_queue.0; - if svx_data.do_the_thing { + if tree_gpu_view.do_the_thing { // GPU buffer Write - let data: u32 = 1; use bevy::render::render_resource::encase::StorageBuffer; - let mut data_buffer = StorageBuffer::new(Vec::::new()); - data_buffer.write(&data).unwrap(); + let data_buffer = StorageBuffer::new(vec![0, 0, 0, 1]); render_queue.write_buffer( - svx_pipeline.metadata_buffer.as_ref().unwrap(), - ((svx_data.metadata.len() - 1) * std::mem::size_of::()) as u64, + tree_gpu_view + .data_handler + .lock() + .unwrap() + .debug_gpu_interface + .as_ref() + .unwrap(), + 0, &data_buffer.into_inner(), ); - - svx_data.do_the_thing = false; } } } diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index d0ef1c6..18ba83c 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -3,13 +3,13 @@ mod pipeline; pub mod types; pub use crate::octree::raytracing::bevy::types::{ - ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, + OctreeGPUView, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, }; use crate::octree::raytracing::bevy::{ - data::{handle_cache, handle_gpu_readback, sync_with_main_world}, + data::{handle_gpu_readback, sync_with_main_world, write_to_gpu}, pipeline::prepare_bind_groups, - types::{ShocoVoxLabel, ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline}, + types::{ShocoVoxLabel, ShocoVoxRenderNode, ShocoVoxRenderPipeline}, }; use bevy::{ @@ -27,18 +27,7 @@ use bevy::{ }, }; -pub fn create_viewing_glass( - viewport: &Viewport, - resolution: [u32; 2], - images: ResMut>, -) -> ShocoVoxViewingGlass { - ShocoVoxViewingGlass { - output_texture: create_ouput_texture(resolution, images), - viewport: *viewport, - } -} - -pub(crate) fn create_ouput_texture( +pub(crate) fn create_output_texture( resolution: [u32; 2], mut images: ResMut>, ) -> Handle { @@ -60,14 +49,13 @@ pub(crate) fn create_ouput_texture( impl Plugin for ShocoVoxRenderPlugin { fn build(&self, app: &mut App) { - app.add_plugins(ExtractResourcePlugin::::default()); - app.add_plugins(ExtractResourcePlugin::::default()); + app.add_plugins(ExtractResourcePlugin::::default()); let render_app = app.sub_app_mut(RenderApp); render_app.add_systems(ExtractSchedule, sync_with_main_world); render_app.add_systems( Render, ( - handle_cache.in_set(RenderSet::PrepareResources), + write_to_gpu.in_set(RenderSet::PrepareResources), prepare_bind_groups.in_set(RenderSet::PrepareBindGroups), handle_gpu_readback.in_set(RenderSet::Cleanup), ), diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index 9dde0e7..98204f7 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -1,5 +1,6 @@ use crate::octree::raytracing::bevy::types::{ - ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline, ShocoVoxViewingGlass, + OctreeGPUView, ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline, + ShocoVoxViewingGlass, }; use bevy::{ @@ -13,7 +14,7 @@ use bevy::{ render_graph::{self}, render_resource::{ encase::{StorageBuffer, UniformBuffer}, - AsBindGroup, BufferInitDescriptor, BufferUsages, CachedPipelineState, + AsBindGroup, BufferDescriptor, BufferInitDescriptor, BufferUsages, CachedPipelineState, ComputePassDescriptor, ComputePipelineDescriptor, PipelineCache, }, renderer::{RenderContext, RenderDevice, RenderQueue}, @@ -22,6 +23,8 @@ use bevy::{ }; use std::borrow::Cow; +use super::types::OctreeGPUDataHandler; + impl FromWorld for ShocoVoxRenderPipeline { fn from_world(world: &mut World) -> Self { let render_device = world.resource::(); @@ -45,7 +48,7 @@ impl FromWorld for ShocoVoxRenderPipeline { }); ShocoVoxRenderPipeline { - victim_pointer: 0, + tree_data_handler: None, render_queue: world.resource::().clone(), octree_meta_buffer: None, metadata_buffer: None, @@ -64,18 +67,28 @@ impl FromWorld for ShocoVoxRenderPipeline { } } +//############################################################################## +// ███████████ █████ █████ ██████ █████ +// ░░███░░░░░███ ░░███ ░░███ ░░██████ ░░███ +// ░███ ░███ ░███ ░███ ░███░███ ░███ +// ░██████████ ░███ ░███ ░███░░███░███ +// ░███░░░░░███ ░███ ░███ ░███ ░░██████ +// ░███ ░███ ░███ ░███ ░███ ░░█████ +// █████ █████ ░░████████ █████ ░░█████ +// ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ +//############################################################################## const WORKGROUP_SIZE: u32 = 8; impl render_graph::Node for ShocoVoxRenderNode { fn update(&mut self, world: &mut World) { { - let render_data = world.get_resource::(); let svx_pipeline = world.resource::(); let pipeline_cache = world.resource::(); + let tree_gpu_view = world.get_resource::(); if !self.ready { if let CachedPipelineState::Ok(_) = pipeline_cache.get_compute_pipeline_state(svx_pipeline.update_pipeline) { - self.ready = render_data.is_some(); + self.ready = tree_gpu_view.is_some(); } } } @@ -119,38 +132,172 @@ impl render_graph::Node for ShocoVoxRenderNode { svx_pipeline.readable_metadata_buffer.as_ref().unwrap(), 0, std::mem::size_of::() as u64, + ); + // +++ DEBUG +++ + let tree_gpu_view = world.resource::(); + let data_handler = tree_gpu_view.data_handler.lock().unwrap(); + command_encoder.copy_buffer_to_buffer( + data_handler.debug_gpu_interface.as_ref().unwrap(), + 0, + data_handler.readable_debug_gpu_interface.as_ref().unwrap(), + 0, + std::mem::size_of::() as u64, ) + // --- DEBUG --- } Ok(()) } } +//############################################################################## +// █████████ ███████████ ██████████ █████████ ███████████ ██████████ +// ███░░░░░███░░███░░░░░███ ░░███░░░░░█ ███░░░░░███ ░█░░░███░░░█░░███░░░░░█ +// ███ ░░░ ░███ ░███ ░███ █ ░ ░███ ░███ ░ ░███ ░ ░███ █ ░ +// ░███ ░██████████ ░██████ ░███████████ ░███ ░██████ +// ░███ ░███░░░░░███ ░███░░█ ░███░░░░░███ ░███ ░███░░█ +// ░░███ ███ ░███ ░███ ░███ ░ █ ░███ ░███ ░███ ░███ ░ █ +// ░░█████████ █████ █████ ██████████ █████ █████ █████ ██████████ +// ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ +// ███████████ █████ ██████ █████ ██████████ +// ░░███░░░░░███░░███ ░░██████ ░░███ ░░███░░░░███ +// ░███ ░███ ░███ ░███░███ ░███ ░███ ░░███ +// ░██████████ ░███ ░███░░███░███ ░███ ░███ +// ░███░░░░░███ ░███ ░███ ░░██████ ░███ ░███ +// ░███ ░███ ░███ ░███ ░░█████ ░███ ███ +// ███████████ █████ █████ ░░█████ ██████████ +// ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ +// █████████ ███████████ ███████ █████ █████ ███████████ █████████ +// ███░░░░░███░░███░░░░░███ ███░░░░░███ ░░███ ░░███ ░░███░░░░░███ ███░░░░░███ +// ███ ░░░ ░███ ░███ ███ ░░███ ░███ ░███ ░███ ░███░███ ░░░ +// ░███ ░██████████ ░███ ░███ ░███ ░███ ░██████████ ░░█████████ +// ░███ █████ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███░░░░░░ ░░░░░░░░███ +// ░░███ ░░███ ░███ ░███ ░░███ ███ ░███ ░███ ░███ ███ ░███ +// ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ ░░█████████ +// ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░░ +//############################################################################## pub(crate) fn prepare_bind_groups( gpu_images: Res>, fallback_image: Res, render_device: Res, mut pipeline: ResMut, - octree_viewing_glass: Res, - render_data: Res, + tree_gpu_view: ResMut, ) { - let bind_group = octree_viewing_glass - .as_bind_group( - &pipeline.viewing_glass_bind_group_layout, - &render_device, - &gpu_images, - &fallback_image, - ) - .ok() - .unwrap(); - pipeline.viewing_glass_bind_group = Some(bind_group.bind_group); + pipeline.viewing_glass_bind_group = Some( + tree_gpu_view + .viewing_glass + .as_bind_group( + &pipeline.viewing_glass_bind_group_layout, + &render_device, + &gpu_images, + &fallback_image, + ) + .ok() + .unwrap() + .bind_group, + ); if pipeline.update_tree { + let mut data_handler = tree_gpu_view.data_handler.lock().unwrap(); + + // Create the staging buffer helping in reading data from the GPU + //############################################################################## + // █████████ ███████████ █████ █████ + // ███░░░░░███░░███░░░░░███░░███ ░░███ + // ███ ░░░ ░███ ░███ ░███ ░███ + // ░███ ░██████████ ░███ ░███ + // ░███ █████ ░███░░░░░░ ░███ ░███ + // ░░███ ░░███ ░███ ░███ ░███ + // ░░█████████ █████ ░░████████ + // ░░░░░░░░░ ░░░░░ ░░░░░░░░ + // ███████████ ██████████ █████████ ██████████ + // ░░███░░░░░███ ░░███░░░░░█ ███░░░░░███ ░░███░░░░███ + // ░███ ░███ ░███ █ ░ ░███ ░███ ░███ ░░███ + // ░██████████ ░██████ ░███████████ ░███ ░███ + // ░███░░░░░███ ░███░░█ ░███░░░░░███ ░███ ░███ + // ░███ ░███ ░███ ░ █ ░███ ░███ ░███ ███ + // █████ █████ ██████████ █████ █████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ + // ███████████ █████ █████ ███████████ ███████████ ██████████ ███████████ + // ░░███░░░░░███░░███ ░░███ ░░███░░░░░░█░░███░░░░░░█░░███░░░░░█░░███░░░░░███ + // ░███ ░███ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ █ ░ ░███ ░███ + // ░██████████ ░███ ░███ ░███████ ░███████ ░██████ ░██████████ + // ░███░░░░░███ ░███ ░███ ░███░░░█ ░███░░░█ ░███░░█ ░███░░░░░███ + // ░███ ░███ ░███ ░███ ░███ ░ ░███ ░ ░███ ░ █ ░███ ░███ + // ███████████ ░░████████ █████ █████ ██████████ █████ █████ + // ░░░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ + //############################################################################## + + if pipeline.readable_metadata_buffer.is_none() { + pipeline.readable_metadata_buffer = + Some(render_device.create_buffer(&BufferDescriptor { + mapped_at_creation: false, + size: (data_handler.render_data.metadata.len() * 4) as u64, + label: Some("Octree Node metadata Buffer"), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + })); + } + + // +++ DEBUG +++ + if data_handler.readable_debug_gpu_interface.is_none() { + data_handler.readable_debug_gpu_interface = + Some(render_device.create_buffer(&BufferDescriptor { + mapped_at_creation: false, + size: 4, + label: Some("Octree Debug interface Buffer"), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + })); + } + // --- DEBUG --- + + //############################################################################## + // ███████████ ██████████ ██████ █████ ██████████ ██████████ ███████████ + // ░░███░░░░░███ ░░███░░░░░█░░██████ ░░███ ░░███░░░░███ ░░███░░░░░█░░███░░░░░███ + // ░███ ░███ ░███ █ ░ ░███░███ ░███ ░███ ░░███ ░███ █ ░ ░███ ░███ + // ░██████████ ░██████ ░███░░███░███ ░███ ░███ ░██████ ░██████████ + // ░███░░░░░███ ░███░░█ ░███ ░░██████ ░███ ░███ ░███░░█ ░███░░░░░███ + // ░███ ░███ ░███ ░ █ ░███ ░░█████ ░███ ███ ░███ ░ █ ░███ ░███ + // █████ █████ ██████████ █████ ░░█████ ██████████ ██████████ █████ █████ + // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ + // ██████████ █████████ ███████████ █████████ + // ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ + // ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ + // ░███ ░███ ░███████████ ░███ ░███████████ + // ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ + // ░███ ███ ░███ ░███ ░███ ░███ ░███ + // ██████████ █████ █████ █████ █████ █████ + // ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ + // ███████████ █████ █████ ███████████ ███████████ ██████████ ███████████ + // ░░███░░░░░███░░███ ░░███ ░░███░░░░░░█░░███░░░░░░█░░███░░░░░█░░███░░░░░███ + // ░███ ░███ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ █ ░ ░███ ░███ + // ░██████████ ░███ ░███ ░███████ ░███████ ░██████ ░██████████ + // ░███░░░░░███ ░███ ░███ ░███░░░█ ░███░░░█ ░███░░█ ░███░░░░░███ + // ░███ ░███ ░███ ░███ ░███ ░ ░███ ░ ░███ ░ █ ░███ ░███ + // ███████████ ░░████████ █████ █████ ██████████ █████ █████ + // ░░░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ + //############################################################################## + //================================================================= // Implementation with WGPU //================================================================= // Upload data to buffers + // +++ DEBUG +++ + let buffer = UniformBuffer::new(vec![0, 0, 0, 0]); + if let Some(debug_buffer) = &data_handler.debug_gpu_interface { + pipeline + .render_queue + .write_buffer(debug_buffer, 0, &buffer.into_inner()) + } else { + let debug_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Debug Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, + }); + data_handler.debug_gpu_interface = Some(debug_buffer); + } + // --- DEBUG --- + let mut buffer = UniformBuffer::new(Vec::::new()); - buffer.write(&render_data.octree_meta).unwrap(); + buffer.write(&data_handler.render_data.octree_meta).unwrap(); if let Some(metadata_buffer) = &pipeline.octree_meta_buffer { pipeline .render_queue @@ -165,7 +312,7 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.metadata).unwrap(); + buffer.write(&data_handler.render_data.metadata).unwrap(); if let Some(nodes_buffer) = &pipeline.metadata_buffer { pipeline .render_queue @@ -180,7 +327,9 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.node_children).unwrap(); + buffer + .write(&data_handler.render_data.node_children) + .unwrap(); if let Some(children_buffer) = &pipeline.node_children_buffer { pipeline .render_queue @@ -195,7 +344,9 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.node_occupied_bits).unwrap(); + buffer + .write(&data_handler.render_data.node_occupied_bits) + .unwrap(); if let Some(ocbits_buffer) = &pipeline.node_ocbits_buffer { pipeline .render_queue @@ -210,7 +361,7 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.voxels).unwrap(); + buffer.write(&data_handler.render_data.voxels).unwrap(); if let Some(voxels_buffer) = &pipeline.voxels_buffer { pipeline .render_queue @@ -225,7 +376,9 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.color_palette).unwrap(); + buffer + .write(&data_handler.render_data.color_palette) + .unwrap(); if let Some(color_palette_buffer) = &pipeline.color_palette_buffer { pipeline .render_queue @@ -240,6 +393,24 @@ pub(crate) fn prepare_bind_groups( pipeline.color_palette_buffer = Some(color_palette_buffer); } + //############################################################################## + // ███████████ █████ ██████ █████ ██████████ + // ░░███░░░░░███░░███ ░░██████ ░░███ ░░███░░░░███ + // ░███ ░███ ░███ ░███░███ ░███ ░███ ░░███ + // ░██████████ ░███ ░███░░███░███ ░███ ░███ + // ░███░░░░░███ ░███ ░███ ░░██████ ░███ ░███ + // ░███ ░███ ░███ ░███ ░░█████ ░███ ███ + // ███████████ █████ █████ ░░█████ ██████████ + // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ + // █████████ ███████████ ███████ █████ █████ ███████████ + // ███░░░░░███░░███░░░░░███ ███░░░░░███ ░░███ ░░███ ░░███░░░░░███ + // ███ ░░░ ░███ ░███ ███ ░░███ ░███ ░███ ░███ ░███ + // ░███ ░██████████ ░███ ░███ ░███ ░███ ░██████████ + // ░███ █████ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███░░░░░░ + // ░░███ ░░███ ░███ ░███ ░░███ ███ ░███ ░███ ░███ + // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ + // ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░ ░░░░░ + //############################################################################## // Create bind group let tree_bind_group = render_device.create_bind_group( ShocoVoxRenderData::label(), @@ -289,6 +460,16 @@ pub(crate) fn prepare_bind_groups( .unwrap() .as_entire_binding(), }, + // +++ DEBUG +++ + bevy::render::render_resource::BindGroupEntry { + binding: 6, + resource: data_handler + .debug_gpu_interface + .as_ref() + .unwrap() + .as_entire_binding(), + }, + // --- DEBUG --- ], ); pipeline.tree_bind_group = Some(tree_bind_group); @@ -324,24 +505,6 @@ pub(crate) fn prepare_bind_groups( // } // pipeline.tree_bind_group = Some(tree_bind_group.bind_group); - - // Create the staging buffer helping in reading data from the GPU - let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&render_data.metadata).unwrap(); - if let Some(readable_nodes_buffer) = &pipeline.readable_metadata_buffer { - pipeline - .render_queue - .write_buffer(readable_nodes_buffer, 0, &buffer.into_inner()) - } else { - let readable_nodes_buffer = - render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Cache Bytes Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, - }); - pipeline.readable_metadata_buffer = Some(readable_nodes_buffer); - } - pipeline.update_tree = false; } } diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 007d5d7..5e0d057 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -1,4 +1,4 @@ -use crate::octree::V3cf32; +use crate::octree::{Albedo, V3cf32}; use bevy::{ asset::Handle, ecs::system::Resource, @@ -14,6 +14,10 @@ use bevy::{ renderer::RenderQueue, }, }; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; #[derive(Clone, ShaderType)] pub(crate) struct Voxelement { @@ -41,6 +45,38 @@ pub struct ShocoVoxRenderPlugin { } #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] +#[type_path = "shocovox::gpu::OctreeGPUView"] +pub struct OctreeGPUView { + // +++ DEBUG +++ + pub do_the_thing: bool, + pub read_back: u32, + // --- DEBUG --- + pub viewing_glass: ShocoVoxViewingGlass, + pub(crate) data_handler: Arc>, +} + +#[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] +#[type_path = "shocovox::gpu::OctreeGPUDataHandler"] +pub struct OctreeGPUDataHandler { + pub(crate) render_data: ShocoVoxRenderData, + // pub(crate) victim_node_pointer: u32, + // pub(crate) victim_brick_pointer: u32, + pub(crate) map_to_node_index_in_nodes_buffer: HashMap, + pub(crate) map_to_color_index_in_palette: HashMap, + pub(crate) debug_gpu_interface: Option, + pub(crate) readable_debug_gpu_interface: Option, + //TODO: Maybe this? + // Buffers for the RenderData + // pub(crate) octree_meta_buffer: Buffer, + // pub(crate) metadata_buffer: Buffer, + // pub(crate) node_children_buffer: Buffer, + // pub(crate) node_ocbits_buffer: Buffer, + // pub(crate) voxels_buffer: Buffer, + // pub(crate) color_palette_buffer: Buffer, + // pub(crate) readable_metadata_buffer: Buffer, +} + +#[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxViewingGlass"] pub struct ShocoVoxViewingGlass { #[storage_texture(0, image_format = Rgba8Unorm, access = ReadWrite)] @@ -50,11 +86,13 @@ pub struct ShocoVoxViewingGlass { pub viewport: Viewport, } -#[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] +#[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxRenderData"] pub struct ShocoVoxRenderData { - pub do_the_thing: bool, //STRICTLY FOR DEBUG REASONS - + // +++ DEBUG +++ + #[storage(6, visibility(compute))] + pub(crate) debug_gpu_interface: u32, + // --- DEBUG --- /// Contains the properties of the Octree #[uniform(0, visibility(compute))] pub(crate) octree_meta: OctreeMetaData, @@ -125,9 +163,6 @@ pub struct ShocoVoxRenderData { pub(crate) struct ShocoVoxRenderPipeline { pub update_tree: bool, - // The candidates for deletion inside nodes array on page deletion - pub(crate) victim_pointer: u32, - pub(crate) render_queue: RenderQueue, pub(crate) update_pipeline: CachedComputePipelineId, @@ -139,6 +174,7 @@ pub(crate) struct ShocoVoxRenderPipeline { pub(crate) node_ocbits_buffer: Option, pub(crate) voxels_buffer: Option, pub(crate) color_palette_buffer: Option, + pub(crate) tree_data_handler: Option, pub(crate) viewing_glass_bind_group_layout: BindGroupLayout, pub(crate) render_data_bind_group_layout: BindGroupLayout, diff --git a/src/octree/raytracing/mod.rs b/src/octree/raytracing/mod.rs index 7881c67..8615da6 100644 --- a/src/octree/raytracing/mod.rs +++ b/src/octree/raytracing/mod.rs @@ -7,4 +7,6 @@ pub mod bevy; pub use crate::spatial::raytracing::Ray; #[cfg(feature = "bevy_wgpu")] -pub use bevy::types::{ShocoVoxRenderData, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport}; +pub use bevy::types::{ + OctreeGPUView, ShocoVoxRenderData, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, +}; diff --git a/src/octree/types.rs b/src/octree/types.rs index 2b05283..6cbfbf8 100644 --- a/src/octree/types.rs +++ b/src/octree/types.rs @@ -4,6 +4,9 @@ use std::error::Error; #[cfg(feature = "serialization")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "bevy_wgpu")] +use {crate::octree::raytracing::bevy::types::OctreeGPUDataHandler, std::sync::Arc}; + #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub(crate) enum BrickData @@ -85,6 +88,9 @@ where pub(crate) octree_size: u32, pub(crate) nodes: ObjectPool>, pub(crate) node_children: Vec>, // Children index values of each Node + + #[cfg(feature = "bevy_wgpu")] + pub(crate) views: Vec>, } #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] From 3d5b70a0308c04cab1c335eadeeffa889a872750 Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 16 Nov 2024 20:26:48 +0100 Subject: [PATCH 09/19] limited renderdata length --- assets/shaders/viewport_render.wgsl | 7 +- examples/dot_cube.rs | 1 + examples/minecraft.rs | 1 + src/octree/convert/bytecode.rs | 1 + src/octree/raytracing/bevy/data.rs | 416 ++++++++++++++++------------ src/octree/raytracing/bevy/types.rs | 6 +- 6 files changed, 248 insertions(+), 184 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 55d4f09..b6808de 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -406,11 +406,12 @@ fn probe_brick( direction_lut_index: u32, ) -> OctreeRayIntersection { let brick_index = node_children[((leaf_node_key * 8) + brick_octant)]; - //set_brick_used(brick_index); // +++ DEBUG +++ + //set_brick_used(brick_index); if(0 == debug_interface){ set_brick_used(brick_index); } + // --- DEBUG --- if(0 != ((0x01u << (8 + brick_octant)) & metadata[leaf_node_key])) { // brick is not empty if(0 == ((0x01u << (16 + brick_octant)) & metadata[leaf_node_key])) { // brick is solid // Whole brick is solid, ray hits it at first connection @@ -474,7 +475,7 @@ fn probe_brick( /*return OctreeRayIntersection( true, - result_rgb, //color_palette[voxels[leaf_brick_hit.flat_index].albedo_index], + color_palette[voxels[leaf_brick_hit.flat_index].albedo_index], voxels[leaf_brick_hit.flat_index].content, point_in_ray_at_distance(ray, *ray_current_distance), cube_impact_normal( @@ -496,7 +497,7 @@ fn probe_brick( return OctreeRayIntersection(false, vec4f(0.), 0, vec3f(0.), vec3f(0., 0., 1.)); } -fn get_by_ray(ray: ptr) -> OctreeRayIntersection{ +fn get_by_ray(ray: ptr) -> OctreeRayIntersection { var ray_scale_factors = get_dda_scale_factors(ray); // Should be const, but then it can't be passed as ptr let direction_lut_index = hash_direction((*ray).direction); diff --git a/examples/dot_cube.rs b/examples/dot_cube.rs index 12ce509..34d4246 100644 --- a/examples/dot_cube.rs +++ b/examples/dot_cube.rs @@ -117,6 +117,7 @@ fn setup(mut commands: Commands, images: ResMut>) { } let output_texture = tree.create_new_gpu_view( + 42, //TODO: decide actual number Viewport { origin, direction: (V3c::new(0., 0., 0.) - origin).normalized(), diff --git a/examples/minecraft.rs b/examples/minecraft.rs index d040679..04ab737 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -80,6 +80,7 @@ fn setup(mut commands: Commands, images: ResMut>) { }); let output_texture = tree.create_new_gpu_view( + 42, Viewport { origin: V3c { x: 0., diff --git a/src/octree/convert/bytecode.rs b/src/octree/convert/bytecode.rs index 846b77c..e4c099a 100644 --- a/src/octree/convert/bytecode.rs +++ b/src/octree/convert/bytecode.rs @@ -409,6 +409,7 @@ where )?; let node_children = Vec::decode_bencode_object(list.next_object()?.unwrap())?; Ok(Self { + #[cfg(feature = "bevy_wgpu")] views: Vec::new(), auto_simplify, octree_size: root_size, diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 77a92ed..d49ac69 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -1,4 +1,5 @@ use crate::object_pool::empty_marker; +use crate::spatial::math::flat_projection; use crate::{ octree::{ raytracing::bevy::{ @@ -59,6 +60,7 @@ where /// Creates GPU compatible data renderable on the GPU from an octree pub fn create_new_gpu_view( &self, + size: usize, viewport: Viewport, resolution: [u32; 2], commands: &mut Commands, @@ -79,21 +81,28 @@ where self.octree_size as f32, ), }, - metadata: Vec::with_capacity(self.nodes.len()), - node_children: Vec::with_capacity(self.nodes.len() * 8), - voxels: Vec::new(), - node_occupied_bits: Vec::with_capacity(self.nodes.len() * 2), - color_palette: Vec::new(), + metadata: vec![0; size], + node_occupied_bits: vec![0; size * 2], + node_children: vec![0; size * 8], + color_palette: vec![Vec4::ZERO; size * 8 * (DIM * DIM * DIM)], + voxels: vec![ + Voxelement { + albedo_index: 0, + content: 0 + }; + size * 8 * (DIM * DIM * DIM) + ], }, - // victim_node_pointer: 0, - // victim_brick_pointer: 0, + victim_node_pointer: 0, + victim_brick_pointer: 0, map_to_color_index_in_palette: HashMap::new(), - map_to_node_index_in_nodes_buffer: HashMap::new(), + map_to_node_index_in_metadata: HashMap::new(), }; // Build up Nodes + gpu_data_handler.add_root_node_properties(&self); for node_key in 0..self.nodes.len() { - if self.nodes.key_is_valid(node_key) { + if self.nodes.key_is_valid(node_key) && node_key != Self::ROOT_NODE_KEY as usize { gpu_data_handler.add_node_properties(&self, node_key); } } @@ -105,6 +114,11 @@ where } } + println!( + "Color palette size: {:?}", + gpu_data_handler.map_to_color_index_in_palette.keys().len() + ); + let output_texture = create_output_texture(resolution, images); commands.insert_resource(OctreeGPUView { do_the_thing: false, @@ -119,51 +133,64 @@ where } } -//############################################################################## -// ███████ █████████ ███████████ ███████████ ██████████ ██████████ -// ███░░░░░███ ███░░░░░███░█░░░███░░░█░░███░░░░░███ ░░███░░░░░█░░███░░░░░█ -// ███ ░░███ ███ ░░░ ░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ -// ░███ ░███░███ ░███ ░██████████ ░██████ ░██████ -// ░███ ░███░███ ░███ ░███░░░░░███ ░███░░█ ░███░░█ -// ░░███ ███ ░░███ ███ ░███ ░███ ░███ ░███ ░ █ ░███ ░ █ -// ░░░███████░ ░░█████████ █████ █████ █████ ██████████ ██████████ -// ░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ -// █████████ ███████████ █████ █████ -// ███░░░░░███░░███░░░░░███░░███ ░░███ -// ███ ░░░ ░███ ░███ ░███ ░███ -// ░███ ░██████████ ░███ ░███ -// ░███ █████ ░███░░░░░░ ░███ ░███ -// ░░███ ░░███ ░███ ░███ ░███ -// ░░█████████ █████ ░░████████ -// ░░░░░░░░░ ░░░░░ ░░░░░░░░ -// ██████████ █████████ ███████████ █████████ -// ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ -// ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ -// ░███ ░███ ░███████████ ░███ ░███████████ -// ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ -// ░███ ███ ░███ ░███ ░███ ░███ ░███ -// ██████████ █████ █████ █████ █████ █████ -// ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ -//############################################################################## - impl OctreeGPUDataHandler { - /// Updates metadata to set every element it might contain unused - fn meta_set_element_unused(sized_node_meta: &mut u32) { - *sized_node_meta = *sized_node_meta & 0x00FFFFFC; + /// Provides with the first available index in the metadata buffer which can be overwritten + /// with node related information. It never returns with 0, because that is reserved for the root node, + /// which should not be overwritten. + fn first_available_node(&mut self) -> usize { + let data_length = self.render_data.metadata.len(); + for index in self.victim_node_pointer..data_length { + debug_assert_eq!(self.victim_node_pointer, index); + self.victim_node_pointer = (self.victim_node_pointer + 1) % data_length; + if 0 == (self.render_data.metadata[index] & 0x01) && 0 != self.victim_node_pointer { + // set the used bit of the element to 1, found a node to overwrite + self.render_data.metadata[index] |= 0x01; + return index; + } else { + // set the used bit of the element to zero, and continue the search + self.render_data.metadata[index] &= 0xFFFFFFFE; + } + } + self.first_available_node() } - /// Updates the meta element value to store that the corresponding node is a leaf node - fn meta_set_is_leaf(sized_node_meta: &mut u32, is_leaf: bool) { - *sized_node_meta = - (*sized_node_meta & 0xFFFFFFFB) | if is_leaf { 0x00000004 } else { 0x00000000 }; + fn first_available_brick(&mut self) -> usize { + let data_length = self.render_data.metadata.len() * 8; + for brick_index in self.victim_brick_pointer..data_length { + debug_assert_eq!(self.victim_brick_pointer, brick_index); + self.victim_brick_pointer = (self.victim_brick_pointer + 1) % data_length; + let brick_index_used_mask = 0x01 << (24 + (brick_index % 8)); + if 0 == (self.render_data.metadata[brick_index / 8] & brick_index_used_mask) { + // set the used bit of the element to 1, found a node to overwrite + self.render_data.metadata[brick_index / 8] |= brick_index_used_mask; + return brick_index; + } else { + // set the used bit of the element to zero, and continue the search + self.render_data.metadata[brick_index / 8] &= !brick_index_used_mask; + } + } + self.first_available_brick() } - /// Updates the meta element value to store that the corresponding node is a uniform leaf node - fn meta_set_is_uniform(sized_node_meta: &mut u32, is_uniform: bool) { - *sized_node_meta = - (*sized_node_meta & 0xFFFFFFF7) | if is_uniform { 0x00000008 } else { 0x00000000 }; - } + //############################################################################## + // ██████████ █████████ ███████████ █████████ + // ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ + // ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ + // ░███ ░███ ░███████████ ░███ ░███████████ + // ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ + // ░███ ███ ░███ ░███ ░███ ░███ ░███ + // ██████████ █████ █████ █████ █████ █████ + // ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ + // ██████████ ██████████ █████████ █████ █████████ ██████ █████ + // ░░███░░░░███ ░░███░░░░░█ ███░░░░░███░░███ ███░░░░░███░░██████ ░░███ + // ░███ ░░███ ░███ █ ░ ░███ ░░░ ░███ ███ ░░░ ░███░███ ░███ + // ░███ ░███ ░██████ ░░█████████ ░███ ░███ ░███░░███░███ + // ░███ ░███ ░███░░█ ░░░░░░░░███ ░███ ░███ █████ ░███ ░░██████ + // ░███ ███ ░███ ░ █ ███ ░███ ░███ ░░███ ░░███ ░███ ░░█████ + // ██████████ ██████████░░█████████ █████ ░░█████████ █████ ░░█████ + // ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ + //############################################################################## /// Updates the meta element value to store the brick structure of the given leaf node. /// Does not erase anything in @sized_node_meta, it's expected to be cleared before /// the first use of this function @@ -194,76 +221,103 @@ impl OctreeGPUDataHandler { }; } - /// Updates the given meta element value to store the leaf structure of the given node - /// the given NodeContent reference is expected to be a leaf node - fn meta_set_leaf_structure( - sized_node_meta: &mut u32, - leaf: &NodeContent, - ) where + /// Creates the descriptor bytes for the given node + fn create_node_properties(node: &NodeContent) -> u32 + where T: Default + Copy + Clone + PartialEq + VoxelData, { - match leaf { - NodeContent::UniformLeaf(brick) => { - Self::meta_set_is_leaf(sized_node_meta, true); - Self::meta_set_is_uniform(sized_node_meta, true); - Self::meta_add_leaf_brick_structure(sized_node_meta, brick, 0); + let mut meta = 0; + match node { + NodeContent::Internal(_) | NodeContent::Nothing => { + meta &= 0xFFFFFFFB; // element is not leaf + meta &= 0xFFFFFFF7; // element is not uniform } NodeContent::Leaf(bricks) => { - Self::meta_set_is_leaf(sized_node_meta, true); - Self::meta_set_is_uniform(sized_node_meta, false); + meta |= 0x00000004; // element is leaf + meta &= 0xFFFFFFF7; // element is not uniform for octant in 0..8 { - Self::meta_add_leaf_brick_structure(sized_node_meta, &bricks[octant], octant); + Self::meta_add_leaf_brick_structure(&mut meta, &bricks[octant], octant); } } - NodeContent::Internal(_) | NodeContent::Nothing => { - panic!("Expected node content to be of a leaf"); + NodeContent::UniformLeaf(brick) => { + meta |= 0x00000004; // element is leaf + meta |= 0x00000008; // element is uniform + Self::meta_add_leaf_brick_structure(&mut meta, brick, 0); } - } + }; + meta } - /// Creates the descriptor bytes for the given node - fn create_node_properties(node: &NodeContent) -> u32 + //############################################################################## + // █████████ ██████████ ██████████ + // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ + // ░███ ░███ ░███ ░░███ ░███ ░░███ + // ░███████████ ░███ ░███ ░███ ░███ + // ░███░░░░░███ ░███ ░███ ░███ ░███ + // ░███ ░███ ░███ ███ ░███ ███ + // █████ █████ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // ██████ █████ ███████ ██████████ ██████████ + // ░░██████ ░░███ ███░░░░░███ ░░███░░░░███ ░░███░░░░░█ + // ░███░███ ░███ ███ ░░███ ░███ ░░███ ░███ █ ░ + // ░███░░███░███ ░███ ░███ ░███ ░███ ░██████ + // ░███ ░░██████ ░███ ░███ ░███ ░███ ░███░░█ + // ░███ ░░█████ ░░███ ███ ░███ ███ ░███ ░ █ + // █████ ░░█████ ░░░███████░ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + //############################################################################## + + fn add_root_node_properties(&mut self, tree: &Octree) where T: Default + Copy + Clone + PartialEq + VoxelData, { - let mut meta = 0; - Self::meta_set_element_unused(&mut meta); - match node { - NodeContent::Leaf(_) | NodeContent::UniformLeaf(_) => { - Self::meta_set_is_leaf(&mut meta, true); - Self::meta_set_leaf_structure(&mut meta, node); - } - NodeContent::Internal(_) | NodeContent::Nothing => { - Self::meta_set_is_leaf(&mut meta, false); - } - }; - meta + let node_key = Octree::::ROOT_NODE_KEY as usize; + self.map_to_node_index_in_metadata.insert(node_key, 0); + self.render_data.metadata[0] = Self::create_node_properties(tree.nodes.get(node_key)); + + // Update occupied bits + let occupied_bits = tree.stored_occupied_bits(node_key); + self.render_data.node_occupied_bits[0] = (occupied_bits & 0x00000000FFFFFFFF) as u32; + self.render_data.node_occupied_bits[1] = + ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; } - fn add_node_properties(&mut self, tree: &Octree, node_key: usize) + fn add_node_properties( + &mut self, + tree: &Octree, + node_key: usize, + ) -> usize where T: Default + Copy + Clone + PartialEq + VoxelData, { - if tree.nodes.key_is_valid(node_key) { - self.map_to_node_index_in_nodes_buffer - .insert(node_key, self.render_data.metadata.len()); - self.render_data - .metadata - .push(Self::create_node_properties(tree.nodes.get(node_key))); - } else { - panic!("Trying to query invalid node key to set node metadata!"); - } + debug_assert!( + tree.nodes.key_is_valid(node_key), + "Expected key({node_key}) to be valid inside tree!" + ); + let element_index = self.first_available_node(); + self.map_to_node_index_in_metadata + .insert(node_key, element_index); + self.render_data.metadata[element_index] = + Self::create_node_properties(tree.nodes.get(node_key)); + + // Update occupied bits + let occupied_bits = tree.stored_occupied_bits(node_key); + self.render_data.node_occupied_bits[element_index * 2] = + (occupied_bits & 0x00000000FFFFFFFF) as u32; + self.render_data.node_occupied_bits[element_index * 2 + 1] = + ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; + element_index } fn add_node_content(&mut self, tree: &Octree, node_key: usize) where T: Default + Copy + Clone + PartialEq + VoxelData, { - let occupied_bits = tree.stored_occupied_bits(node_key); - self.render_data.node_occupied_bits.extend_from_slice(&[ - (occupied_bits & 0x00000000FFFFFFFF) as u32, - ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32, - ]); + debug_assert!( + self.map_to_node_index_in_metadata.contains_key(&node_key), + "Trying to add content of node not in context of data handler" + ); + let node_element_index = self.map_to_node_index_in_metadata[&node_key]; match tree.nodes.get(node_key) { NodeContent::UniformLeaf(brick) => { debug_assert!( @@ -276,17 +330,14 @@ impl OctreeGPUDataHandler { ); let (brick_index, brick_added) = self.add_brick(brick); - - self.render_data.node_children.extend_from_slice(&[ - brick_index, - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - empty_marker(), - ]); + self.render_data.node_children[node_element_index * 8 + 0] = brick_index; + self.render_data.node_children[node_element_index * 8 + 1] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 2] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 3] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 4] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 5] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 6] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 7] = empty_marker(); #[cfg(debug_assertions)] { if !brick_added { @@ -308,12 +359,9 @@ impl OctreeGPUDataHandler { "Expected Leaf to have OccupancyBitmaps(_) instead of {:?}", tree.node_children[node_key].content ); - - let mut children = vec![empty_marker(); 8]; for octant in 0..8 { let (brick_index, brick_added) = self.add_brick(&bricks[octant]); - - children[octant] = brick_index; + self.render_data.node_children[node_element_index * 8 + octant] = brick_index; #[cfg(debug_assertions)] { if !brick_added { @@ -330,110 +378,115 @@ impl OctreeGPUDataHandler { } } } - self.render_data.node_children.extend_from_slice(&children); } NodeContent::Internal(_) => { - for c in 0..8 { - let child_index = &tree.node_children[node_key][c]; + for octant in 0..8 { + let child_index = &tree.node_children[node_key][octant]; if *child_index != empty_marker() { debug_assert!(self - .map_to_node_index_in_nodes_buffer + .map_to_node_index_in_metadata .contains_key(&(*child_index as usize))); - self.render_data.node_children.push( - self.map_to_node_index_in_nodes_buffer[&(*child_index as usize)] as u32, - ); + let child_index_in_metadata = + self.map_to_node_index_in_metadata[&(*child_index as usize)]; + self.render_data.node_children[node_element_index * 8 + octant as usize] = + child_index_in_metadata as u32; } else { - self.render_data.node_children.push(*child_index); + self.render_data.node_children[node_element_index * 8 + octant as usize] = + empty_marker(); } } } NodeContent::Nothing => { - self.render_data - .node_children - .extend_from_slice(&[empty_marker(); 8]); + for octant in 0..8 { + self.render_data.node_children[node_element_index * 8 + octant as usize] = + empty_marker(); + } } } - println!( - "bricks: {:?} <> nodes: {:?}", - self.render_data.voxels.len() / (DIM * DIM * DIM), - self.render_data.metadata.len() - ); - - // debug_assert_eq!( - // self.render_data.metadata.len() * 2, - // self.render_data.node_occupied_bits.len(), - // "Node occupancy bitmaps length({:?}) should match node count({:?})!", - // self.render_data.node_occupied_bits.len(), - // self.render_data.metadata.len(), - // ); - - // debug_assert_eq!( - // self.render_data.metadata.len() * 8, - // self.render_data.node_children.len(), - // "Node count({:?}) should match length of children buffer({:?})!", - // self.render_data.metadata.len(), - // self.render_data.node_children.len() - // ); } + //############################################################################## + // █████████ ██████████ ██████████ + // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ + // ░███ ░███ ░███ ░░███ ░███ ░░███ + // ░███████████ ░███ ░███ ░███ ░███ + // ░███░░░░░███ ░███ ░███ ░███ ░███ + // ░███ ░███ ░███ ███ ░███ ███ + // █████ █████ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // ███████████ ███████████ █████ █████████ █████ ████ + // ░░███░░░░░███░░███░░░░░███ ░░███ ███░░░░░███░░███ ███░ + // ░███ ░███ ░███ ░███ ░███ ███ ░░░ ░███ ███ + // ░██████████ ░██████████ ░███ ░███ ░███████ + // ░███░░░░░███ ░███░░░░░███ ░███ ░███ ░███░░███ + // ░███ ░███ ░███ ░███ ░███ ░░███ ███ ░███ ░░███ + // ███████████ █████ █████ █████ ░░█████████ █████ ░░████ + // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░ + //############################################################################## /// Loads a brick into the provided voxels vector and color palette /// * `brick` - The brick to upload - /// * `voxels` - The destination buffer - /// * `color_palette` - The used color palette - /// * `map_to_color_index_in_palette` - Indexing helper for the color palette /// * `returns` - the identifier to set in @SizedNode and true if a new brick was aded to the voxels vector fn add_brick(&mut self, brick: &BrickData) -> (u32, bool) where T: Default + Clone + PartialEq + VoxelData, { + debug_assert_eq!( + self.render_data.voxels.len() % (DIM * DIM * DIM), + 0, + "Expected Voxel buffer length({:?}) to be divisble by {:?}", + self.render_data.voxels.len(), + (DIM * DIM * DIM) + ); + match brick { BrickData::Empty => (empty_marker(), false), BrickData::Solid(voxel) => { let albedo = voxel.albedo(); + // The number of colors inserted into the palette is the size of the color palette map + let color_palette_size = self.map_to_color_index_in_palette.keys().len(); if let std::collections::hash_map::Entry::Vacant(e) = self.map_to_color_index_in_palette.entry(albedo) { - e.insert(self.render_data.color_palette.len()); - self.render_data.color_palette.push(Vec4::new( + e.insert(color_palette_size); + self.render_data.color_palette[color_palette_size] = Vec4::new( albedo.r as f32 / 255., albedo.g as f32 / 255., albedo.b as f32 / 255., albedo.a as f32 / 255., - )); + ); } (self.map_to_color_index_in_palette[&albedo] as u32, false) } BrickData::Parted(brick) => { - self.render_data.voxels.reserve(DIM * DIM * DIM); - let brick_index = self.render_data.voxels.len() / (DIM * DIM * DIM); - debug_assert_eq!( - self.render_data.voxels.len() % (DIM * DIM * DIM), - 0, - "Expected Voxel buffer length({:?}) to be divisble by {:?}", - self.render_data.voxels.len(), - (DIM * DIM * DIM) - ); + let brick_index = self.first_available_brick(); + println!("first first_available_brick: {:?}", brick_index); for z in 0..DIM { for y in 0..DIM { for x in 0..DIM { + // The number of colors inserted into the palette is the size of the color palette map + let potential_new_albedo_index = + self.map_to_color_index_in_palette.keys().len(); let albedo = brick[x][y][z].albedo(); - if let std::collections::hash_map::Entry::Vacant(e) = + let albedo_index = if let std::collections::hash_map::Entry::Vacant(e) = self.map_to_color_index_in_palette.entry(albedo) { - e.insert(self.render_data.color_palette.len()); - self.render_data.color_palette.push(Vec4::new( - albedo.r as f32 / 255., - albedo.g as f32 / 255., - albedo.b as f32 / 255., - albedo.a as f32 / 255., - )); - } - let albedo_index = self.map_to_color_index_in_palette[&albedo]; - - self.render_data.voxels.push(Voxelement { + e.insert(potential_new_albedo_index); + self.render_data.color_palette[potential_new_albedo_index] = + Vec4::new( + albedo.r as f32 / 255., + albedo.g as f32 / 255., + albedo.b as f32 / 255., + albedo.a as f32 / 255., + ); + potential_new_albedo_index + } else { + self.map_to_color_index_in_palette[&albedo] + }; + self.render_data.voxels[(brick_index * (DIM * DIM * DIM)) + + flat_projection(x, y, z, DIM)] = Voxelement { albedo_index: albedo_index as u32, content: brick[x][y][z].user_data(), - }); + }; } } } @@ -443,14 +496,20 @@ impl OctreeGPUDataHandler { } } -pub(crate) fn sync_with_main_world( - tree_view: Option>, - mut world: ResMut, +pub(crate) fn sync_with_main_world(// tree_view: Option>, + // mut world: ResMut, ) { - if let Some(tree_view) = tree_view { - let mut tree_view_mainworld = world.get_resource_mut::().unwrap(); - tree_view_mainworld.read_back = tree_view.read_back; - } + // This function is unused because ExtractResource plugin is handling the sync + // However, it is only one way: MainWorld --> RenderWorld + // Any modification here is overwritten by the plugin if it is active, + // in order to enable data flow in the opposite direction, extractresource plugin + // needs to be disabled, and the sync logic (both ways) needs to be implemented here + // refer to: https://www.reddit.com/r/bevy/comments/1ay50ee/copy_from_render_world_to_main_world/ + // if let Some(tree_view) = tree_view { + // let mut tree_view_mainworld = world.get_resource_mut::().unwrap(); + // tree_view_mainworld.read_back = tree_view.read_back; + // println!("Read back in render world: {:?}", tree_view.read_back); + // } } //############################################################################## @@ -473,10 +532,10 @@ pub(crate) fn sync_with_main_world( //############################################################################## pub(crate) fn handle_gpu_readback( render_device: Res, - tree_gpu_view: Option>, + mut tree_gpu_view: Option>, ) { // Data updates triggered by debug interface - if let Some(mut tree_gpu_view) = tree_gpu_view { + if let Some(ref mut tree_gpu_view) = tree_gpu_view { if tree_gpu_view.do_the_thing { let received_data; { @@ -509,6 +568,7 @@ pub(crate) fn handle_gpu_readback( .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) .collect::>(); received_data = data[0]; + // println!("received_data: {:?}", data); } data_handler .readable_debug_gpu_interface diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 5e0d057..f0fce67 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -59,9 +59,9 @@ pub struct OctreeGPUView { #[type_path = "shocovox::gpu::OctreeGPUDataHandler"] pub struct OctreeGPUDataHandler { pub(crate) render_data: ShocoVoxRenderData, - // pub(crate) victim_node_pointer: u32, - // pub(crate) victim_brick_pointer: u32, - pub(crate) map_to_node_index_in_nodes_buffer: HashMap, + pub(crate) victim_node_pointer: usize, + pub(crate) victim_brick_pointer: usize, + pub(crate) map_to_node_index_in_metadata: HashMap, pub(crate) map_to_color_index_in_palette: HashMap, pub(crate) debug_gpu_interface: Option, pub(crate) readable_debug_gpu_interface: Option, From d167c78fedd46eae9db52481da21fb9dc7af8022 Mon Sep 17 00:00:00 2001 From: davids91 Date: Sun, 17 Nov 2024 08:00:06 +0100 Subject: [PATCH 10/19] Implemented Victim node logic --- assets/shaders/viewport_render.wgsl | 91 ++++--- examples/minecraft.rs | 2 +- src/octree/node.rs | 63 ++++- src/octree/raytracing/bevy/data.rs | 331 +++++++++++++++++-------- src/octree/raytracing/bevy/pipeline.rs | 4 +- src/octree/raytracing/bevy/types.rs | 16 +- 6 files changed, 361 insertions(+), 146 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index b6808de..5bef3b7 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -405,14 +405,20 @@ fn probe_brick( ray_scale_factors: ptr, direction_lut_index: u32, ) -> OctreeRayIntersection { - let brick_index = node_children[((leaf_node_key * 8) + brick_octant)]; - // +++ DEBUG +++ - //set_brick_used(brick_index); - if(0 == debug_interface){ - set_brick_used(brick_index); - } - // --- DEBUG --- if(0 != ((0x01u << (8 + brick_octant)) & metadata[leaf_node_key])) { // brick is not empty + let brick_index = node_children[((leaf_node_key * 8) + brick_octant)]; + + // Brick not available in memory + if EMPTY_MARKER == brick_index { + return OctreeRayIntersection(true, vec4f(0.5,0.6,0.2,1.), 0, vec3f(0.), vec3f(0., 0., 1.)); + } + + // +++ DEBUG +++ + //set_brick_used(brick_index); + if(0 == debug_interface){ + set_brick_used(brick_index); + } + // --- DEBUG --- if(0 == ((0x01u << (16 + brick_octant)) & metadata[leaf_node_key])) { // brick is solid // Whole brick is solid, ray hits it at first connection return OctreeRayIntersection( @@ -509,6 +515,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { var current_node_meta = 0u; var target_octant = OOB_OCTANT; var step_vec = vec3f(0.); + var missing_data_color = vec3f(0.); let root_intersect = cube_intersect_ray(current_bounds, ray); if(root_intersect.hit){ @@ -523,7 +530,6 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { /*// +++ DEBUG +++ var outer_safety = 0; */// --- DEBUG --- - set_node_used(OCTREE_ROOT_NODE_KEY); while target_octant != OOB_OCTANT { /*// +++ DEBUG +++ outer_safety += 1; @@ -549,8 +555,18 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { ); } */// --- DEBUG --- - var target_bounds: Cube; var do_backtrack_after_leaf_miss = false; + var target_child_key = node_children[(current_node_key * 8) + target_octant]; + var target_bounds = child_bounds_for(¤t_bounds, target_octant); + var bitmap_pos_in_node = clamp( + ( + point_in_ray_at_distance(ray, ray_current_distance) + - current_bounds.min_position + ) * 4. / current_bounds.size, + vec3f(FLOAT_ERROR_TOLERANCE), + vec3f(4. - FLOAT_ERROR_TOLERANCE) + ); + if (target_octant != OOB_OCTANT) { if(0 != (0x00000004 & current_node_meta)) { // node is leaf var hit: OctreeRayIntersection; @@ -560,9 +576,6 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { current_node_key, 0u, ¤t_bounds, &ray_scale_factors, direction_lut_index ); - if hit.hit == true { - return hit; - } do_backtrack_after_leaf_miss = true; } else { // node is a non-uniform leaf target_bounds = child_bounds_for(¤t_bounds, target_octant); @@ -572,21 +585,32 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { &target_bounds, &ray_scale_factors, direction_lut_index ); - if hit.hit == true { - return hit; - } + } + if hit.hit == true { + hit.albedo += vec4f(missing_data_color, 0.); + return hit; } } } - var bitmap_pos_in_node = clamp( - ( - point_in_ray_at_distance(ray, ray_current_distance) - - current_bounds.min_position - ) * 4. / current_bounds.size, - vec3f(FLOAT_ERROR_TOLERANCE), - vec3f(4. - FLOAT_ERROR_TOLERANCE) - ); + if( // In case node doesn't yet have the relevant child node uploaded to GPU + (0 == (0x00000004 & current_node_meta)) // node is not a leaf + && target_octant != OOB_OCTANT + && target_child_key >= arrayLength(&metadata) // target child key is invalid + && ( // node is occupied at target octant + 0 != ( + BITMAP_MASK_FOR_OCTANT_LUT[target_octant][0] + & node_occupied_bits[current_node_key * 2] + ) + || 0 != ( + BITMAP_MASK_FOR_OCTANT_LUT[target_octant][1] + & node_occupied_bits[current_node_key * 2 + 1] + ) + ) + ){ + missing_data_color += vec3f(0.3,0.2,0.0); + } + if( do_backtrack_after_leaf_miss || target_octant == OOB_OCTANT || EMPTY_MARKER == current_node_key // Guards statements in other conditions, but should never happen @@ -636,9 +660,6 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { } continue; } - - target_bounds = child_bounds_for(¤t_bounds, target_octant); - var target_child_key = node_children[(current_node_key * 8) + target_octant]; if ( ( 0 == (0x00000004 & current_node_meta) // node is not a leaf @@ -753,7 +774,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { target_octant = OOB_OCTANT; } } // while (ray inside root bounds) - return OctreeRayIntersection(false, vec4f(0.), 0, vec3f(0.), vec3f(0., 0., 1.)); + return OctreeRayIntersection(false, vec4f(missing_data_color, 1.), 0, vec3f(0.), vec3f(0., 0., 1.)); } struct Voxelement { @@ -849,6 +870,8 @@ fn update( dot(ray_result.impact_normal, vec3f(-0.5,0.5,-0.5)) / 2. + 0.5 ) ).rgb; + } else { + rgb_result = (rgb_result + ray_result.albedo.rgb) / 2.; } /*// +++ DEBUG +++ @@ -870,7 +893,7 @@ fn update( rgb_result.b = 1.; } } - //rgb_result.b += 0.1; // Also color in the area of the octree + rgb_result.b += 0.1; // Also color in the area of the octree } */// --- DEBUG --- textureStore(output_texture, vec2u(invocation_id.xy), vec4f(rgb_result, 1.)); @@ -928,6 +951,18 @@ var BITMAP_INDEX_LUT: array, 4>, 4> = array BITMAP_MASK_FOR_OCTANT_LUT: array, 8> = array, 8>( + array(0x00330033u,0x00000000u), + array(0x00CC00CCu,0x00000000u), + array(0x00000000u,0x00330033u), + array(0x00000000u,0x00CC00CCu), + array(0x33003300u,0x00000000u), + array(0xCC00CC00u,0x00000000u), + array(0x00000000u,0x33003300u), + array(0x00000000u,0xCC00CC00u), +); + // Note: should be const var RAY_TO_NODE_OCCUPANCY_BITMASK_LUT: array, 64> = array, 64>( array(1,0,15,0,65537,65537,983055,983055,4369,0,65535,0,286331153,286331153,4294967295,4294967295,), diff --git a/examples/minecraft.rs b/examples/minecraft.rs index 04ab737..f2bc5ea 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -80,7 +80,7 @@ fn setup(mut commands: Commands, images: ResMut>) { }); let output_texture = tree.create_new_gpu_view( - 42, + 20, Viewport { origin: V3c { x: 0., diff --git a/src/octree/node.rs b/src/octree/node.rs index ee4d90d..0153528 100644 --- a/src/octree/node.rs +++ b/src/octree/node.rs @@ -7,9 +7,24 @@ use crate::spatial::{ math::{set_occupancy_in_bitmap_64bits, BITMAP_DIMENSION}, }; -///#################################################################################### -/// NodeChildren -///#################################################################################### +//#################################################################################### +// ██████ █████ ███████ ██████████ ██████████ +// ░░██████ ░░███ ███░░░░░███ ░░███░░░░███ ░░███░░░░░█ +// ░███░███ ░███ ███ ░░███ ░███ ░░███ ░███ █ ░ +// ░███░░███░███ ░███ ░███ ░███ ░███ ░██████ +// ░███ ░░██████ ░███ ░███ ░███ ░███ ░███░░█ +// ░███ ░░█████ ░░███ ███ ░███ ███ ░███ ░ █ +// █████ ░░█████ ░░░███████░ ██████████ ██████████ +// ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ +// █████████ █████ █████ █████ █████ ██████████ ███████████ ██████████ ██████ █████ +// ███░░░░░███░░███ ░░███ ░░███ ░░███ ░░███░░░░███ ░░███░░░░░███ ░░███░░░░░█░░██████ ░░███ +// ███ ░░░ ░███ ░███ ░███ ░███ ░███ ░░███ ░███ ░███ ░███ █ ░ ░███░███ ░███ +// ░███ ░███████████ ░███ ░███ ░███ ░███ ░██████████ ░██████ ░███░░███░███ +// ░███ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███░░░░░███ ░███░░█ ░███ ░░██████ +// ░░███ ███ ░███ ░███ ░███ ░███ █ ░███ ███ ░███ ░███ ░███ ░ █ ░███ ░░█████ +// ░░█████████ █████ █████ █████ ███████████ ██████████ █████ █████ ██████████ █████ ░░█████ +// ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ +//#################################################################################### impl NodeChildren where T: Default + Clone + Eq, @@ -85,9 +100,24 @@ where } } -///#################################################################################### -/// BrickData -///#################################################################################### +//#################################################################################### +// ███████████ ███████████ █████ █████████ █████ ████ +// ░░███░░░░░███░░███░░░░░███ ░░███ ███░░░░░███░░███ ███░ +// ░███ ░███ ░███ ░███ ░███ ███ ░░░ ░███ ███ +// ░██████████ ░██████████ ░███ ░███ ░███████ +// ░███░░░░░███ ░███░░░░░███ ░███ ░███ ░███░░███ +// ░███ ░███ ░███ ░███ ░███ ░░███ ███ ░███ ░░███ +// ███████████ █████ █████ █████ ░░█████████ █████ ░░████ +// ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░ +// ██████████ █████████ ███████████ █████████ +// ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ +// ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ +// ░███ ░███ ░███████████ ░███ ░███████████ +// ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ +// ░███ ███ ░███ ░███ ░███ ░███ ░███ +// ██████████ █████ █████ █████ █████ █████ +// ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ +//#################################################################################### impl BrickData where T: VoxelData + PartialEq + Clone + Default, @@ -240,9 +270,24 @@ where } } -///#################################################################################### -/// NodeContent -///#################################################################################### +//#################################################################################### +// ██████ █████ ███████ ██████████ ██████████ +// ░░██████ ░░███ ███░░░░░███ ░░███░░░░███ ░░███░░░░░█ +// ░███░███ ░███ ███ ░░███ ░███ ░░███ ░███ █ ░ +// ░███░░███░███ ░███ ░███ ░███ ░███ ░██████ +// ░███ ░░██████ ░███ ░███ ░███ ░███ ░███░░█ +// ░███ ░░█████ ░░███ ███ ░███ ███ ░███ ░ █ +// █████ ░░█████ ░░░███████░ ██████████ ██████████ +// ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ +// █████████ ███████ ██████ █████ ███████████ ██████████ ██████ █████ ███████████ +// ███░░░░░███ ███░░░░░███ ░░██████ ░░███ ░█░░░███░░░█░░███░░░░░█░░██████ ░░███ ░█░░░███░░░█ +// ███ ░░░ ███ ░░███ ░███░███ ░███ ░ ░███ ░ ░███ █ ░ ░███░███ ░███ ░ ░███ ░ +// ░███ ░███ ░███ ░███░░███░███ ░███ ░██████ ░███░░███░███ ░███ +// ░███ ░███ ░███ ░███ ░░██████ ░███ ░███░░█ ░███ ░░██████ ░███ +// ░░███ ███░░███ ███ ░███ ░░█████ ░███ ░███ ░ █ ░███ ░░█████ ░███ +// ░░█████████ ░░░███████░ █████ ░░█████ █████ ██████████ █████ ░░█████ █████ +// ░░░░░░░░░ ░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ +//#################################################################################### impl NodeContent where diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index d49ac69..9705565 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -25,7 +25,7 @@ use std::{ sync::{Arc, Mutex}, }; -use super::types::OctreeGPUDataHandler; +use super::types::{OctreeGPUDataHandler, VictimPointer}; impl Octree where @@ -82,9 +82,9 @@ where ), }, metadata: vec![0; size], - node_occupied_bits: vec![0; size * 2], - node_children: vec![0; size * 8], - color_palette: vec![Vec4::ZERO; size * 8 * (DIM * DIM * DIM)], + node_ocbits: vec![0; size * 2], + node_children: vec![empty_marker(); size * 8], + color_palette: vec![Vec4::ZERO; u16::MAX as usize], voxels: vec![ Voxelement { albedo_index: 0, @@ -93,31 +93,33 @@ where size * 8 * (DIM * DIM * DIM) ], }, - victim_node_pointer: 0, - victim_brick_pointer: 0, + victim_node: VictimPointer::new(size), + victim_brick: VictimPointer::new(size), map_to_color_index_in_palette: HashMap::new(), map_to_node_index_in_metadata: HashMap::new(), }; - // Build up Nodes - gpu_data_handler.add_root_node_properties(&self); - for node_key in 0..self.nodes.len() { - if self.nodes.key_is_valid(node_key) && node_key != Self::ROOT_NODE_KEY as usize { - gpu_data_handler.add_node_properties(&self, node_key); - } - } + // Push root node and its contents + gpu_data_handler.add_node(&self, Self::ROOT_NODE_KEY as usize, true); + // +++ DEBUG +++ + + //Push additional nodes to try to overwrite existing ones + /*for node_key in 10..15 { + gpu_data_handler.add_node(&self, node_key, false); + }*/ - // Build up node content - for node_key in 0..self.nodes.len() { - if self.nodes.key_is_valid(node_key) { - gpu_data_handler.add_node_content(&self, node_key); + //delete some random bricks from leaf nodes + for i in 15..20 { + if 0 != (gpu_data_handler.render_data.metadata[i] & 0x00000004) { + gpu_data_handler.render_data.node_children[i * 8 + 3] = empty_marker(); } } - println!( - "Color palette size: {:?}", - gpu_data_handler.map_to_color_index_in_palette.keys().len() - ); + // reset used bits + for meta in gpu_data_handler.render_data.metadata.iter_mut() { + *meta &= 0x00FFFFFE; + } + // --- DEBUG --- let output_texture = create_output_texture(resolution, images); commands.insert_resource(OctreeGPUView { @@ -133,45 +135,188 @@ where } } -impl OctreeGPUDataHandler { - /// Provides with the first available index in the metadata buffer which can be overwritten +//############################################################################## +// █████ █████ █████ █████████ ███████████ █████ ██████ ██████ +// ░░███ ░░███ ░░███ ███░░░░░███░█░░░███░░░█░░███ ░░██████ ██████ +// ░███ ░███ ░███ ███ ░░░ ░ ░███ ░ ░███ ░███░█████░███ +// ░███ ░███ ░███ ░███ ░███ ░███ ░███░░███ ░███ +// ░░███ ███ ░███ ░███ ░███ ░███ ░███ ░░░ ░███ +// ░░░█████░ ░███ ░░███ ███ ░███ ░███ ░███ ░███ +// ░░███ █████ ░░█████████ █████ █████ █████ █████ +// ░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ +// ███████████ ███████████ ███████████ +// ░░███░░░░░███░█░░░███░░░█░░███░░░░░███ +// ░███ ░███░ ░███ ░ ░███ ░███ +// ░██████████ ░███ ░██████████ +// ░███░░░░░░ ░███ ░███░░░░░███ +// ░███ ░███ ░███ ░███ +// █████ █████ █████ █████ +// ░░░░░ ░░░░░ ░░░░░ ░░░░░ +//############################################################################## +impl VictimPointer { + pub(crate) fn is_full(&self) -> bool { + self.stored_items >= (self.max_meta_len - 1) + } + + pub(crate) fn new(max_meta_len: usize) -> Self { + Self { + max_meta_len, + stored_items: 0, + meta_index: 0, + child: 0, + } + } + + pub(crate) fn step(&mut self) { + if self.child >= 7 { + self.skip_node(); + } else { + self.child += 1; + } + } + + pub(crate) fn skip_node(&mut self) { + if self.meta_index == 0 { + self.meta_index = self.max_meta_len - 1; + } else { + self.meta_index -= 1; + } + self.child = 0; + } + + /// Provides the first available index in the metadata buffer which can be overwritten /// with node related information. It never returns with 0, because that is reserved for the root node, /// which should not be overwritten. - fn first_available_node(&mut self) -> usize { - let data_length = self.render_data.metadata.len(); - for index in self.victim_node_pointer..data_length { - debug_assert_eq!(self.victim_node_pointer, index); - self.victim_node_pointer = (self.victim_node_pointer + 1) % data_length; - if 0 == (self.render_data.metadata[index] & 0x01) && 0 != self.victim_node_pointer { - // set the used bit of the element to 1, found a node to overwrite - self.render_data.metadata[index] |= 0x01; - return index; - } else { - // set the used bit of the element to zero, and continue the search - self.render_data.metadata[index] &= 0xFFFFFFFE; + fn first_available_node(&mut self, render_data: &mut ShocoVoxRenderData) -> usize { + // If there is space left in the cache, use it all up + if !self.is_full() { + render_data.metadata[self.stored_items] |= 0x01; + self.stored_items += 1; + return self.stored_items - 1; + } + + //look for the next internal node ( with node children ) + loop { + let child_node_index = + render_data.node_children[self.meta_index * 8 + self.child] as usize; + + // child at target is not empty in a non-leaf node, which means + // the target child might point to an internal node if it's valid + if + // parent node has a child at target octant, which isn't invalidated + child_node_index != empty_marker() as usize + // parent node is not a leaf + && (0 == (render_data.metadata[self.meta_index] & 0x00000004)) + { + let child_meta_index = + render_data.node_children[self.meta_index * 8 + self.child] as usize; + if 0 == (render_data.metadata[child_meta_index] & 0x01) { + //mark child as used + render_data.metadata[child_meta_index] |= 0x01; + + //erase connection to parent node + render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); + + if + // erased node is a leaf and it has children + 0 != (render_data.metadata[child_meta_index] & 0x00000004) + && 0 != (render_data.metadata[child_meta_index] & 0x00FF0000) + { + // Since the node is deleted, if it has any stored bricks, those are freed up + for octant in 0..8 { + let brick_index = + render_data.node_children[child_meta_index * 8 + octant] as usize; + if + // child is valid + brick_index != empty_marker() as usize + // child is not empty and not solid + && 0 != (render_data.metadata[child_meta_index] + & (0x01 << (8 + octant))) + && 0 != (render_data.metadata[child_meta_index] + & (0x01 << (16 + octant))) + { + // mark brick unused + render_data.metadata[brick_index / 8] &= + !(0x01 << (24 + (brick_index % 8))); + } + } + } + + self.step(); + return child_meta_index; + } else { + // mark child as unused + render_data.metadata[child_meta_index] &= 0xFFFFFFFE; + } } + self.step(); } - self.first_available_node() } - fn first_available_brick(&mut self) -> usize { - let data_length = self.render_data.metadata.len() * 8; - for brick_index in self.victim_brick_pointer..data_length { - debug_assert_eq!(self.victim_brick_pointer, brick_index); - self.victim_brick_pointer = (self.victim_brick_pointer + 1) % data_length; - let brick_index_used_mask = 0x01 << (24 + (brick_index % 8)); - if 0 == (self.render_data.metadata[brick_index / 8] & brick_index_used_mask) { - // set the used bit of the element to 1, found a node to overwrite - self.render_data.metadata[brick_index / 8] |= brick_index_used_mask; - return brick_index; + /// Finds the first available brick, and marks it as used + fn first_available_brick(&mut self, render_data: &mut ShocoVoxRenderData) -> usize { + let max_brick_count = render_data.metadata.len() * 8; + + // If there is space left in the cache, use it all up + if self.stored_items < max_brick_count - 1 { + render_data.metadata[self.stored_items / 8] |= 0x01 << (24 + (self.stored_items % 8)); + self.stored_items += 1; + return self.stored_items - 1; + } + + // look for the next victim leaf node with bricks + loop { + let brick_index = render_data.node_children[self.meta_index * 8 + self.child] as usize; + if + // child at target is not empty + brick_index != empty_marker() as usize + //node is leaf + &&(0 != (render_data.metadata[self.meta_index] & 0x00000004)) + && ( + // In case of uniform leaf nodes, only the first child might have a brick + // So the node is either not uniform, or the target child points to 0 + (0 == render_data.metadata[self.meta_index] & 0x00000008) + || (0 == self.child) + ) + && ( + // node child is not empty, and parted at octant + 0 != (render_data.metadata[self.meta_index] + & (0x01 << (8 + self.child))) + && 0 != (render_data.metadata[self.meta_index] + & (0x01 << (16 + self.child))) + ) + { + let brick_used_mask = 0x01 << (24 + (brick_index % 8)); + // if used bit for the brick is 0, it can be safely overwritten + if 0 == (render_data.metadata[brick_index / 8] & brick_used_mask) { + // mark brick as used + render_data.metadata[brick_index / 8] |= brick_used_mask; + + // erase connection of child brick to parent node + render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); + + // step victim pointer forward + self.step(); + return brick_index; + } + + // set the bricks used bit to 0 for the currently used brick + render_data.metadata[brick_index / 8] &= !brick_used_mask; + } + if + // node is not leaf + (0 == (render_data.metadata[self.meta_index] & 0x00000004)) + // in case node is uniform, octant 0 should have been checked for vacancy already, so it is safe to skip to next leaf node + || (0 != (render_data.metadata[self.meta_index] & 0x00000008)) + { + self.skip_node(); } else { - // set the used bit of the element to zero, and continue the search - self.render_data.metadata[brick_index / 8] &= !brick_index_used_mask; + self.step(); } } - self.first_available_brick() } - +} +impl OctreeGPUDataHandler { //############################################################################## // ██████████ █████████ ███████████ █████████ // ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ @@ -266,58 +411,36 @@ impl OctreeGPUDataHandler { // █████ ░░█████ ░░░███████░ ██████████ ██████████ // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ //############################################################################## - - fn add_root_node_properties(&mut self, tree: &Octree) - where - T: Default + Copy + Clone + PartialEq + VoxelData, - { - let node_key = Octree::::ROOT_NODE_KEY as usize; - self.map_to_node_index_in_metadata.insert(node_key, 0); - self.render_data.metadata[0] = Self::create_node_properties(tree.nodes.get(node_key)); - - // Update occupied bits - let occupied_bits = tree.stored_occupied_bits(node_key); - self.render_data.node_occupied_bits[0] = (occupied_bits & 0x00000000FFFFFFFF) as u32; - self.render_data.node_occupied_bits[1] = - ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; - } - - fn add_node_properties( + fn add_node( &mut self, tree: &Octree, node_key: usize, - ) -> usize - where + try_add_children: bool, + ) where T: Default + Copy + Clone + PartialEq + VoxelData, { - debug_assert!( - tree.nodes.key_is_valid(node_key), - "Expected key({node_key}) to be valid inside tree!" - ); - let element_index = self.first_available_node(); + if try_add_children && self.victim_node.is_full() { + // Do not add additional nodes at initial upload if the cache is already full + return; + } + + // Determine the index in meta + let node_element_index = self.victim_node.first_available_node(&mut self.render_data); self.map_to_node_index_in_metadata - .insert(node_key, element_index); - self.render_data.metadata[element_index] = + .insert(node_key, node_element_index); + + // Add node properties to metadata + self.render_data.metadata[node_element_index] = Self::create_node_properties(tree.nodes.get(node_key)); - // Update occupied bits + // Update occupancy in ocbits let occupied_bits = tree.stored_occupied_bits(node_key); - self.render_data.node_occupied_bits[element_index * 2] = + self.render_data.node_ocbits[node_element_index * 2] = (occupied_bits & 0x00000000FFFFFFFF) as u32; - self.render_data.node_occupied_bits[element_index * 2 + 1] = + self.render_data.node_ocbits[node_element_index * 2 + 1] = ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; - element_index - } - fn add_node_content(&mut self, tree: &Octree, node_key: usize) - where - T: Default + Copy + Clone + PartialEq + VoxelData, - { - debug_assert!( - self.map_to_node_index_in_metadata.contains_key(&node_key), - "Trying to add content of node not in context of data handler" - ); - let node_element_index = self.map_to_node_index_in_metadata[&node_key]; + // Add node content match tree.nodes.get(node_key) { NodeContent::UniformLeaf(brick) => { debug_assert!( @@ -381,15 +504,20 @@ impl OctreeGPUDataHandler { } NodeContent::Internal(_) => { for octant in 0..8 { - let child_index = &tree.node_children[node_key][octant]; - if *child_index != empty_marker() { - debug_assert!(self - .map_to_node_index_in_metadata - .contains_key(&(*child_index as usize))); - let child_index_in_metadata = - self.map_to_node_index_in_metadata[&(*child_index as usize)]; + let child_key = tree.node_children[node_key][octant] as usize; + if child_key != empty_marker() as usize { + if try_add_children + && !self.map_to_node_index_in_metadata.contains_key(&child_key) + { + self.add_node(tree, child_key, try_add_children); + } + self.render_data.node_children[node_element_index * 8 + octant as usize] = - child_index_in_metadata as u32; + *self + .map_to_node_index_in_metadata + .get(&child_key) + .unwrap_or(&(empty_marker() as usize)) + as u32; } else { self.render_data.node_children[node_element_index * 8 + octant as usize] = empty_marker(); @@ -458,8 +586,9 @@ impl OctreeGPUDataHandler { (self.map_to_color_index_in_palette[&albedo] as u32, false) } BrickData::Parted(brick) => { - let brick_index = self.first_available_brick(); - println!("first first_available_brick: {:?}", brick_index); + let brick_index = self + .victim_brick + .first_available_brick(&mut self.render_data); for z in 0..DIM { for y in 0..DIM { for x in 0..DIM { @@ -551,7 +680,7 @@ pub(crate) fn handle_gpu_readback( buffer_slice.map_async(bevy::render::render_resource::MapMode::Read, move |d| { match d { Ok(_) => s.send(()).expect("Failed to send map update"), - Err(err) => println!("Something's wrong: {err}"), + Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), } }); diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index 98204f7..cedb61e 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -344,9 +344,7 @@ pub(crate) fn prepare_bind_groups( } let mut buffer = StorageBuffer::new(Vec::::new()); - buffer - .write(&data_handler.render_data.node_occupied_bits) - .unwrap(); + buffer.write(&data_handler.render_data.node_ocbits).unwrap(); if let Some(ocbits_buffer) = &pipeline.node_ocbits_buffer { pipeline .render_queue diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index f0fce67..6057793 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -55,12 +55,20 @@ pub struct OctreeGPUView { pub(crate) data_handler: Arc>, } +#[derive(Debug, Clone)] +pub(crate) struct VictimPointer { + pub(crate) max_meta_len: usize, //TODO: should be private to type + pub(crate) stored_items: usize, + pub(crate) meta_index: usize, + pub(crate) child: usize, +} + #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] #[type_path = "shocovox::gpu::OctreeGPUDataHandler"] pub struct OctreeGPUDataHandler { pub(crate) render_data: ShocoVoxRenderData, - pub(crate) victim_node_pointer: usize, - pub(crate) victim_brick_pointer: usize, + pub(crate) victim_node: VictimPointer, + pub(crate) victim_brick: VictimPointer, pub(crate) map_to_node_index_in_metadata: HashMap, pub(crate) map_to_color_index_in_palette: HashMap, pub(crate) debug_gpu_interface: Option, @@ -124,7 +132,7 @@ pub struct ShocoVoxRenderData { /// |---------------------------------------------------------------------| /// | each bit is 1 if voxel brick is used by the raytracing algorithm | /// `=====================================================================` - /// *(1) Only first bit is used in case leaf is uniform + /// *(1) Only first bit is used in case uniform leaf nodes /// *(2) The same bit is used for node_children and node_occupied_bits /// *(3) One index in the array covers 8 bricks, which is the theoretical maximum /// number of bricks for one node. In practice however the number of bricks @@ -146,7 +154,7 @@ pub struct ShocoVoxRenderData { /// Buffer of Node occupancy bitmaps. Each node has a 64 bit bitmap, /// which is stored in 2 * u32 values #[storage(3, visibility(compute))] - pub(crate) node_occupied_bits: Vec, + pub(crate) node_ocbits: Vec, /// Buffer of Voxel Bricks. Each brick contains voxel_brick_dim^3 elements. /// Each Brick has a corresponding 64 bit occupancy bitmap in the @voxel_maps buffer. From 67888f2d6bf2d2f70b836b3225d038ab7917a856 Mon Sep 17 00:00:00 2001 From: davids91 Date: Sun, 17 Nov 2024 09:01:11 +0100 Subject: [PATCH 11/19] Minor restructure and polishing --- assets/shaders/viewport_render.wgsl | 24 +- examples/dot_cube.rs | 4 +- examples/minecraft.rs | 4 +- src/octree/raytracing/bevy/cache.rs | 503 ++++++++++++++++++++++++ src/octree/raytracing/bevy/data.rs | 515 +------------------------ src/octree/raytracing/bevy/mod.rs | 15 +- src/octree/raytracing/bevy/pipeline.rs | 22 +- src/octree/raytracing/bevy/types.rs | 20 +- src/octree/raytracing/mod.rs | 4 +- 9 files changed, 555 insertions(+), 556 deletions(-) create mode 100644 src/octree/raytracing/bevy/cache.rs diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 5bef3b7..f2a41d1 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -237,19 +237,14 @@ fn dda_step_to_next_sibling( // Unique to this implementation, not adapted from rust code /// Sets the used bit true for the given node fn set_node_used(node_key: u32) { - var current_value: u32; - var target_value: u32; - - if 0 != (metadata[node_key] & 0x01u) // no need to set if already true - { + if 0 != (metadata[node_key] & 0x01u) { + // no need to set if already true return; } loop{ - current_value = metadata[node_key]; - target_value = metadata[node_key] | 0x01u; let exchange_result = atomicCompareExchangeWeak( - &metadata[node_key], current_value, target_value + &metadata[node_key], metadata[node_key], metadata[node_key] | 0x01u ); if(exchange_result.exchanged || 0 < (exchange_result.old_value & 0x01u)){ break; @@ -260,19 +255,16 @@ fn set_node_used(node_key: u32) { // Unique to this implementation, not adapted from rust code /// Sets the used bit true for the given brick fn set_brick_used(brick_index: u32) { - var current_value: u32; - var target_value: u32; - - if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) // no need to set if already true - { + if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) { + // no need to set if already true return; } loop{ - current_value = metadata[brick_index / 8]; - target_value = metadata[brick_index / 8] | (0x01u << (24u + (brick_index % 8u))); let exchange_result = atomicCompareExchangeWeak( - &metadata[brick_index / 8], current_value, target_value + &metadata[brick_index / 8], + metadata[brick_index / 8], + metadata[brick_index / 8] | (0x01u << (24u + (brick_index % 8u))) ); if( exchange_result.exchanged diff --git a/examples/dot_cube.rs b/examples/dot_cube.rs index 34d4246..02e5b89 100644 --- a/examples/dot_cube.rs +++ b/examples/dot_cube.rs @@ -9,7 +9,7 @@ use iyes_perf_ui::{ #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUView, Ray, ShocoVoxRenderPlugin, Viewport}, + raytracing::{OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, Albedo, Octree, V3c, VoxelData, }; @@ -37,7 +37,7 @@ fn main() { .insert_resource(ClearColor(Color::BLACK)) .add_plugins(( DefaultPlugins.set(WindowPlugin::default()), - ShocoVoxRenderPlugin { + SvxRenderPlugin { resolution: DISPLAY_RESOLUTION, }, bevy::diagnostic::FrameTimeDiagnosticsPlugin, diff --git a/examples/minecraft.rs b/examples/minecraft.rs index f2bc5ea..d5cc93b 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -3,7 +3,7 @@ use bevy::{prelude::*, window::WindowPlugin}; #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUView, Ray, ShocoVoxRenderPlugin, Viewport}, + raytracing::{OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, Albedo, Octree, V3c, VoxelData, }; @@ -42,7 +42,7 @@ fn main() { }), ..default() }), - ShocoVoxRenderPlugin { + SvxRenderPlugin { resolution: DISPLAY_RESOLUTION, }, bevy::diagnostic::FrameTimeDiagnosticsPlugin, diff --git a/src/octree/raytracing/bevy/cache.rs b/src/octree/raytracing/bevy/cache.rs new file mode 100644 index 0000000..6b522a9 --- /dev/null +++ b/src/octree/raytracing/bevy/cache.rs @@ -0,0 +1,503 @@ +use crate::object_pool::empty_marker; +use crate::spatial::math::flat_projection; +use crate::{ + octree::{ + raytracing::bevy::types::{SvxRenderData, Voxelement}, + types::{NodeChildrenArray, NodeContent}, + BrickData, Octree, VoxelData, + }, + spatial::lut::BITMAP_MASK_FOR_OCTANT_LUT, +}; +use bevy::math::Vec4; + +use super::types::{OctreeGPUDataHandler, VictimPointer}; + +//############################################################################## +// █████ █████ █████ █████████ ███████████ █████ ██████ ██████ +// ░░███ ░░███ ░░███ ███░░░░░███░█░░░███░░░█░░███ ░░██████ ██████ +// ░███ ░███ ░███ ███ ░░░ ░ ░███ ░ ░███ ░███░█████░███ +// ░███ ░███ ░███ ░███ ░███ ░███ ░███░░███ ░███ +// ░░███ ███ ░███ ░███ ░███ ░███ ░███ ░░░ ░███ +// ░░░█████░ ░███ ░░███ ███ ░███ ░███ ░███ ░███ +// ░░███ █████ ░░█████████ █████ █████ █████ █████ +// ░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ +// ███████████ ███████████ ███████████ +// ░░███░░░░░███░█░░░███░░░█░░███░░░░░███ +// ░███ ░███░ ░███ ░ ░███ ░███ +// ░██████████ ░███ ░██████████ +// ░███░░░░░░ ░███ ░███░░░░░███ +// ░███ ░███ ░███ ░███ +// █████ █████ █████ █████ +// ░░░░░ ░░░░░ ░░░░░ ░░░░░ +//############################################################################## +impl VictimPointer { + pub(crate) fn is_full(&self) -> bool { + self.stored_items >= (self.max_meta_len - 1) + } + + pub(crate) fn new(max_meta_len: usize) -> Self { + Self { + max_meta_len, + stored_items: 0, + meta_index: 0, + child: 0, + } + } + + pub(crate) fn step(&mut self) { + if self.child >= 7 { + self.skip_node(); + } else { + self.child += 1; + } + } + + pub(crate) fn skip_node(&mut self) { + if self.meta_index == 0 { + self.meta_index = self.max_meta_len - 1; + } else { + self.meta_index -= 1; + } + self.child = 0; + } + + /// Provides the first available index in the metadata buffer which can be overwritten + /// with node related information. It never returns with 0, because that is reserved for the root node, + /// which should not be overwritten. + fn first_available_node(&mut self, render_data: &mut SvxRenderData) -> usize { + // If there is space left in the cache, use it all up + if !self.is_full() { + render_data.metadata[self.stored_items] |= 0x01; + self.stored_items += 1; + return self.stored_items - 1; + } + + //look for the next internal node ( with node children ) + loop { + let child_node_index = + render_data.node_children[self.meta_index * 8 + self.child] as usize; + + // child at target is not empty in a non-leaf node, which means + // the target child might point to an internal node if it's valid + if + // parent node has a child at target octant, which isn't invalidated + child_node_index != empty_marker() as usize + // parent node is not a leaf + && (0 == (render_data.metadata[self.meta_index] & 0x00000004)) + { + let child_meta_index = + render_data.node_children[self.meta_index * 8 + self.child] as usize; + if 0 == (render_data.metadata[child_meta_index] & 0x01) { + //mark child as used + render_data.metadata[child_meta_index] |= 0x01; + + //erase connection to parent node + render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); + + if + // erased node is a leaf and it has children + 0 != (render_data.metadata[child_meta_index] & 0x00000004) + && 0 != (render_data.metadata[child_meta_index] & 0x00FF0000) + { + // Since the node is deleted, if it has any stored bricks, those are freed up + for octant in 0..8 { + let brick_index = + render_data.node_children[child_meta_index * 8 + octant] as usize; + if + // child is valid + brick_index != empty_marker() as usize + // child is not empty and not solid + && 0 != (render_data.metadata[child_meta_index] + & (0x01 << (8 + octant))) + && 0 != (render_data.metadata[child_meta_index] + & (0x01 << (16 + octant))) + { + // mark brick unused + render_data.metadata[brick_index / 8] &= + !(0x01 << (24 + (brick_index % 8))); + } + } + } + + self.step(); + return child_meta_index; + } else { + // mark child as unused + render_data.metadata[child_meta_index] &= 0xFFFFFFFE; + } + } + self.step(); + } + } + + /// Finds the first available brick, and marks it as used + fn first_available_brick(&mut self, render_data: &mut SvxRenderData) -> usize { + let max_brick_count = render_data.metadata.len() * 8; + + // If there is space left in the cache, use it all up + if self.stored_items < max_brick_count - 1 { + render_data.metadata[self.stored_items / 8] |= 0x01 << (24 + (self.stored_items % 8)); + self.stored_items += 1; + return self.stored_items - 1; + } + + // look for the next victim leaf node with bricks + loop { + let brick_index = render_data.node_children[self.meta_index * 8 + self.child] as usize; + if + // child at target is not empty + brick_index != empty_marker() as usize + //node is leaf + &&(0 != (render_data.metadata[self.meta_index] & 0x00000004)) + && ( + // In case of uniform leaf nodes, only the first child might have a brick + // So the node is either not uniform, or the target child points to 0 + (0 == render_data.metadata[self.meta_index] & 0x00000008) + || (0 == self.child) + ) + && ( + // node child is not empty, and parted at octant + 0 != (render_data.metadata[self.meta_index] + & (0x01 << (8 + self.child))) + && 0 != (render_data.metadata[self.meta_index] + & (0x01 << (16 + self.child))) + ) + { + let brick_used_mask = 0x01 << (24 + (brick_index % 8)); + // if used bit for the brick is 0, it can be safely overwritten + if 0 == (render_data.metadata[brick_index / 8] & brick_used_mask) { + // mark brick as used + render_data.metadata[brick_index / 8] |= brick_used_mask; + + // erase connection of child brick to parent node + render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); + + // step victim pointer forward + self.step(); + return brick_index; + } + + // set the bricks used bit to 0 for the currently used brick + render_data.metadata[brick_index / 8] &= !brick_used_mask; + } + if + // node is not leaf + (0 == (render_data.metadata[self.meta_index] & 0x00000004)) + // in case node is uniform, octant 0 should have been checked for vacancy already, so it is safe to skip to next leaf node + || (0 != (render_data.metadata[self.meta_index] & 0x00000008)) + { + self.skip_node(); + } else { + self.step(); + } + } + } +} +impl OctreeGPUDataHandler { + //############################################################################## + // ██████████ █████████ ███████████ █████████ + // ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ + // ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ + // ░███ ░███ ░███████████ ░███ ░███████████ + // ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ + // ░███ ███ ░███ ░███ ░███ ░███ ░███ + // ██████████ █████ █████ █████ █████ █████ + // ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ + + // ██████████ ██████████ █████████ █████ █████████ ██████ █████ + // ░░███░░░░███ ░░███░░░░░█ ███░░░░░███░░███ ███░░░░░███░░██████ ░░███ + // ░███ ░░███ ░███ █ ░ ░███ ░░░ ░███ ███ ░░░ ░███░███ ░███ + // ░███ ░███ ░██████ ░░█████████ ░███ ░███ ░███░░███░███ + // ░███ ░███ ░███░░█ ░░░░░░░░███ ░███ ░███ █████ ░███ ░░██████ + // ░███ ███ ░███ ░ █ ███ ░███ ░███ ░░███ ░░███ ░███ ░░█████ + // ██████████ ██████████░░█████████ █████ ░░█████████ █████ ░░█████ + // ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ + //############################################################################## + /// Updates the meta element value to store the brick structure of the given leaf node. + /// Does not erase anything in @sized_node_meta, it's expected to be cleared before + /// the first use of this function + /// for the given brick octant + /// * `sized_node_meta` - the bytes to update + /// * `brick` - the brick to describe into the bytes + /// * `brick_octant` - the octant to update in the bytes + fn meta_add_leaf_brick_structure( + sized_node_meta: &mut u32, + brick: &BrickData, + brick_octant: usize, + ) where + T: Default + Clone + PartialEq + VoxelData, + { + match brick { + BrickData::Empty => {} // Child structure properties already set to NIL + BrickData::Solid(_voxel) => { + // set child Occupied bits, child Structure bits already set to NIL + *sized_node_meta |= 0x01 << (8 + brick_octant); + } + BrickData::Parted(_brick) => { + // set child Occupied bits + *sized_node_meta |= 0x01 << (8 + brick_octant); + + // set child Structure bits + *sized_node_meta |= 0x01 << (16 + brick_octant); + } + }; + } + + /// Creates the descriptor bytes for the given node + fn create_node_properties(node: &NodeContent) -> u32 + where + T: Default + Copy + Clone + PartialEq + VoxelData, + { + let mut meta = 0; + match node { + NodeContent::Internal(_) | NodeContent::Nothing => { + meta &= 0xFFFFFFFB; // element is not leaf + meta &= 0xFFFFFFF7; // element is not uniform + } + NodeContent::Leaf(bricks) => { + meta |= 0x00000004; // element is leaf + meta &= 0xFFFFFFF7; // element is not uniform + for octant in 0..8 { + Self::meta_add_leaf_brick_structure(&mut meta, &bricks[octant], octant); + } + } + NodeContent::UniformLeaf(brick) => { + meta |= 0x00000004; // element is leaf + meta |= 0x00000008; // element is uniform + Self::meta_add_leaf_brick_structure(&mut meta, brick, 0); + } + }; + meta + } + + //############################################################################## + // █████████ ██████████ ██████████ + // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ + // ░███ ░███ ░███ ░░███ ░███ ░░███ + // ░███████████ ░███ ░███ ░███ ░███ + // ░███░░░░░███ ░███ ░███ ░███ ░███ + // ░███ ░███ ░███ ███ ░███ ███ + // █████ █████ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // ██████ █████ ███████ ██████████ ██████████ + // ░░██████ ░░███ ███░░░░░███ ░░███░░░░███ ░░███░░░░░█ + // ░███░███ ░███ ███ ░░███ ░███ ░░███ ░███ █ ░ + // ░███░░███░███ ░███ ░███ ░███ ░███ ░██████ + // ░███ ░░██████ ░███ ░███ ░███ ░███ ░███░░█ + // ░███ ░░█████ ░░███ ███ ░███ ███ ░███ ░ █ + // █████ ░░█████ ░░░███████░ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + //############################################################################## + pub(crate) fn add_node( + &mut self, + tree: &Octree, + node_key: usize, + try_add_children: bool, + ) where + T: Default + Copy + Clone + PartialEq + VoxelData, + { + if try_add_children && self.victim_node.is_full() { + // Do not add additional nodes at initial upload if the cache is already full + return; + } + + // Determine the index in meta + let node_element_index = self.victim_node.first_available_node(&mut self.render_data); + self.map_to_node_index_in_metadata + .insert(node_key, node_element_index); + + // Add node properties to metadata + self.render_data.metadata[node_element_index] = + Self::create_node_properties(tree.nodes.get(node_key)); + + // Update occupancy in ocbits + let occupied_bits = tree.stored_occupied_bits(node_key); + self.render_data.node_ocbits[node_element_index * 2] = + (occupied_bits & 0x00000000FFFFFFFF) as u32; + self.render_data.node_ocbits[node_element_index * 2 + 1] = + ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; + + // Add node content + match tree.nodes.get(node_key) { + NodeContent::UniformLeaf(brick) => { + debug_assert!( + matches!( + tree.node_children[node_key].content, + NodeChildrenArray::OccupancyBitmap(_) + ), + "Expected Uniform leaf to have OccupancyBitmap(_) instead of {:?}", + tree.node_children[node_key].content + ); + + let (brick_index, brick_added) = self.add_brick(brick); + self.render_data.node_children[node_element_index * 8 + 0] = brick_index; + self.render_data.node_children[node_element_index * 8 + 1] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 2] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 3] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 4] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 5] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 6] = empty_marker(); + self.render_data.node_children[node_element_index * 8 + 7] = empty_marker(); + #[cfg(debug_assertions)] + { + if !brick_added { + // If no brick was added, the occupied bits should either be empty or full + if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = + tree.node_children[node_key].content + { + debug_assert!(occupied_bits == 0 || occupied_bits == u64::MAX); + } + } + } + } + NodeContent::Leaf(bricks) => { + debug_assert!( + matches!( + tree.node_children[node_key].content, + NodeChildrenArray::OccupancyBitmap(_) + ), + "Expected Leaf to have OccupancyBitmaps(_) instead of {:?}", + tree.node_children[node_key].content + ); + for octant in 0..8 { + let (brick_index, brick_added) = self.add_brick(&bricks[octant]); + self.render_data.node_children[node_element_index * 8 + octant] = brick_index; + #[cfg(debug_assertions)] + { + if !brick_added { + // If no brick was added, the relevant occupied bits should either be empty or full + if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = + tree.node_children[node_key].content + { + debug_assert!( + 0 == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] + || BITMAP_MASK_FOR_OCTANT_LUT[octant] + == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] + ); + } + } + } + } + } + NodeContent::Internal(_) => { + for octant in 0..8 { + let child_key = tree.node_children[node_key][octant] as usize; + if child_key != empty_marker() as usize { + if try_add_children + && !self.map_to_node_index_in_metadata.contains_key(&child_key) + { + self.add_node(tree, child_key, try_add_children); + } + + self.render_data.node_children[node_element_index * 8 + octant as usize] = + *self + .map_to_node_index_in_metadata + .get(&child_key) + .unwrap_or(&(empty_marker() as usize)) + as u32; + } else { + self.render_data.node_children[node_element_index * 8 + octant as usize] = + empty_marker(); + } + } + } + NodeContent::Nothing => { + for octant in 0..8 { + self.render_data.node_children[node_element_index * 8 + octant as usize] = + empty_marker(); + } + } + } + } + + //############################################################################## + // █████████ ██████████ ██████████ + // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ + // ░███ ░███ ░███ ░░███ ░███ ░░███ + // ░███████████ ░███ ░███ ░███ ░███ + // ░███░░░░░███ ░███ ░███ ░███ ░███ + // ░███ ░███ ░███ ███ ░███ ███ + // █████ █████ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // ███████████ ███████████ █████ █████████ █████ ████ + // ░░███░░░░░███░░███░░░░░███ ░░███ ███░░░░░███░░███ ███░ + // ░███ ░███ ░███ ░███ ░███ ███ ░░░ ░███ ███ + // ░██████████ ░██████████ ░███ ░███ ░███████ + // ░███░░░░░███ ░███░░░░░███ ░███ ░███ ░███░░███ + // ░███ ░███ ░███ ░███ ░███ ░░███ ███ ░███ ░░███ + // ███████████ █████ █████ █████ ░░█████████ █████ ░░████ + // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░ + //############################################################################## + /// Loads a brick into the provided voxels vector and color palette + /// * `brick` - The brick to upload + /// * `returns` - the identifier to set in @SizedNode and true if a new brick was aded to the voxels vector + fn add_brick(&mut self, brick: &BrickData) -> (u32, bool) + where + T: Default + Clone + PartialEq + VoxelData, + { + debug_assert_eq!( + self.render_data.voxels.len() % (DIM * DIM * DIM), + 0, + "Expected Voxel buffer length({:?}) to be divisble by {:?}", + self.render_data.voxels.len(), + (DIM * DIM * DIM) + ); + + match brick { + BrickData::Empty => (empty_marker(), false), + BrickData::Solid(voxel) => { + let albedo = voxel.albedo(); + // The number of colors inserted into the palette is the size of the color palette map + let color_palette_size = self.map_to_color_index_in_palette.keys().len(); + if let std::collections::hash_map::Entry::Vacant(e) = + self.map_to_color_index_in_palette.entry(albedo) + { + e.insert(color_palette_size); + self.render_data.color_palette[color_palette_size] = Vec4::new( + albedo.r as f32 / 255., + albedo.g as f32 / 255., + albedo.b as f32 / 255., + albedo.a as f32 / 255., + ); + } + (self.map_to_color_index_in_palette[&albedo] as u32, false) + } + BrickData::Parted(brick) => { + let brick_index = self + .victim_brick + .first_available_brick(&mut self.render_data); + for z in 0..DIM { + for y in 0..DIM { + for x in 0..DIM { + // The number of colors inserted into the palette is the size of the color palette map + let potential_new_albedo_index = + self.map_to_color_index_in_palette.keys().len(); + let albedo = brick[x][y][z].albedo(); + let albedo_index = if let std::collections::hash_map::Entry::Vacant(e) = + self.map_to_color_index_in_palette.entry(albedo) + { + e.insert(potential_new_albedo_index); + self.render_data.color_palette[potential_new_albedo_index] = + Vec4::new( + albedo.r as f32 / 255., + albedo.g as f32 / 255., + albedo.b as f32 / 255., + albedo.a as f32 / 255., + ); + potential_new_albedo_index + } else { + self.map_to_color_index_in_palette[&albedo] + }; + self.render_data.voxels[(brick_index * (DIM * DIM * DIM)) + + flat_projection(x, y, z, DIM)] = Voxelement { + albedo_index: albedo_index as u32, + content: brick[x][y][z].user_data(), + }; + } + } + } + (brick_index as u32, true) + } + } + } +} diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 9705565..34db7fd 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -1,18 +1,13 @@ use crate::object_pool::empty_marker; -use crate::spatial::math::flat_projection; -use crate::{ - octree::{ - raytracing::bevy::{ - create_output_texture, - types::{ - OctreeGPUView, OctreeMetaData, ShocoVoxRenderData, ShocoVoxRenderPipeline, - ShocoVoxViewingGlass, Viewport, Voxelement, - }, +use crate::octree::{ + raytracing::bevy::{ + create_output_texture, + types::{ + OctreeGPUView, OctreeMetaData, SvxRenderData, SvxRenderPipeline, SvxViewingGlass, + Viewport, Voxelement, }, - types::{NodeChildrenArray, NodeContent}, - BrickData, Octree, V3c, VoxelData, }, - spatial::lut::BITMAP_MASK_FOR_OCTANT_LUT, + Octree, V3c, VoxelData, }; use bevy::{ ecs::system::{Res, ResMut}, @@ -69,7 +64,7 @@ where let mut gpu_data_handler = OctreeGPUDataHandler { debug_gpu_interface: None, readable_debug_gpu_interface: None, - render_data: ShocoVoxRenderData { + render_data: SvxRenderData { debug_gpu_interface: 0, octree_meta: OctreeMetaData { octree_size: self.octree_size, @@ -126,7 +121,7 @@ where do_the_thing: false, read_back: 0, data_handler: Arc::new(Mutex::new(gpu_data_handler)), - viewing_glass: ShocoVoxViewingGlass { + viewing_glass: SvxViewingGlass { output_texture: output_texture.clone(), viewport: viewport, }, @@ -135,496 +130,6 @@ where } } -//############################################################################## -// █████ █████ █████ █████████ ███████████ █████ ██████ ██████ -// ░░███ ░░███ ░░███ ███░░░░░███░█░░░███░░░█░░███ ░░██████ ██████ -// ░███ ░███ ░███ ███ ░░░ ░ ░███ ░ ░███ ░███░█████░███ -// ░███ ░███ ░███ ░███ ░███ ░███ ░███░░███ ░███ -// ░░███ ███ ░███ ░███ ░███ ░███ ░███ ░░░ ░███ -// ░░░█████░ ░███ ░░███ ███ ░███ ░███ ░███ ░███ -// ░░███ █████ ░░█████████ █████ █████ █████ █████ -// ░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ -// ███████████ ███████████ ███████████ -// ░░███░░░░░███░█░░░███░░░█░░███░░░░░███ -// ░███ ░███░ ░███ ░ ░███ ░███ -// ░██████████ ░███ ░██████████ -// ░███░░░░░░ ░███ ░███░░░░░███ -// ░███ ░███ ░███ ░███ -// █████ █████ █████ █████ -// ░░░░░ ░░░░░ ░░░░░ ░░░░░ -//############################################################################## -impl VictimPointer { - pub(crate) fn is_full(&self) -> bool { - self.stored_items >= (self.max_meta_len - 1) - } - - pub(crate) fn new(max_meta_len: usize) -> Self { - Self { - max_meta_len, - stored_items: 0, - meta_index: 0, - child: 0, - } - } - - pub(crate) fn step(&mut self) { - if self.child >= 7 { - self.skip_node(); - } else { - self.child += 1; - } - } - - pub(crate) fn skip_node(&mut self) { - if self.meta_index == 0 { - self.meta_index = self.max_meta_len - 1; - } else { - self.meta_index -= 1; - } - self.child = 0; - } - - /// Provides the first available index in the metadata buffer which can be overwritten - /// with node related information. It never returns with 0, because that is reserved for the root node, - /// which should not be overwritten. - fn first_available_node(&mut self, render_data: &mut ShocoVoxRenderData) -> usize { - // If there is space left in the cache, use it all up - if !self.is_full() { - render_data.metadata[self.stored_items] |= 0x01; - self.stored_items += 1; - return self.stored_items - 1; - } - - //look for the next internal node ( with node children ) - loop { - let child_node_index = - render_data.node_children[self.meta_index * 8 + self.child] as usize; - - // child at target is not empty in a non-leaf node, which means - // the target child might point to an internal node if it's valid - if - // parent node has a child at target octant, which isn't invalidated - child_node_index != empty_marker() as usize - // parent node is not a leaf - && (0 == (render_data.metadata[self.meta_index] & 0x00000004)) - { - let child_meta_index = - render_data.node_children[self.meta_index * 8 + self.child] as usize; - if 0 == (render_data.metadata[child_meta_index] & 0x01) { - //mark child as used - render_data.metadata[child_meta_index] |= 0x01; - - //erase connection to parent node - render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); - - if - // erased node is a leaf and it has children - 0 != (render_data.metadata[child_meta_index] & 0x00000004) - && 0 != (render_data.metadata[child_meta_index] & 0x00FF0000) - { - // Since the node is deleted, if it has any stored bricks, those are freed up - for octant in 0..8 { - let brick_index = - render_data.node_children[child_meta_index * 8 + octant] as usize; - if - // child is valid - brick_index != empty_marker() as usize - // child is not empty and not solid - && 0 != (render_data.metadata[child_meta_index] - & (0x01 << (8 + octant))) - && 0 != (render_data.metadata[child_meta_index] - & (0x01 << (16 + octant))) - { - // mark brick unused - render_data.metadata[brick_index / 8] &= - !(0x01 << (24 + (brick_index % 8))); - } - } - } - - self.step(); - return child_meta_index; - } else { - // mark child as unused - render_data.metadata[child_meta_index] &= 0xFFFFFFFE; - } - } - self.step(); - } - } - - /// Finds the first available brick, and marks it as used - fn first_available_brick(&mut self, render_data: &mut ShocoVoxRenderData) -> usize { - let max_brick_count = render_data.metadata.len() * 8; - - // If there is space left in the cache, use it all up - if self.stored_items < max_brick_count - 1 { - render_data.metadata[self.stored_items / 8] |= 0x01 << (24 + (self.stored_items % 8)); - self.stored_items += 1; - return self.stored_items - 1; - } - - // look for the next victim leaf node with bricks - loop { - let brick_index = render_data.node_children[self.meta_index * 8 + self.child] as usize; - if - // child at target is not empty - brick_index != empty_marker() as usize - //node is leaf - &&(0 != (render_data.metadata[self.meta_index] & 0x00000004)) - && ( - // In case of uniform leaf nodes, only the first child might have a brick - // So the node is either not uniform, or the target child points to 0 - (0 == render_data.metadata[self.meta_index] & 0x00000008) - || (0 == self.child) - ) - && ( - // node child is not empty, and parted at octant - 0 != (render_data.metadata[self.meta_index] - & (0x01 << (8 + self.child))) - && 0 != (render_data.metadata[self.meta_index] - & (0x01 << (16 + self.child))) - ) - { - let brick_used_mask = 0x01 << (24 + (brick_index % 8)); - // if used bit for the brick is 0, it can be safely overwritten - if 0 == (render_data.metadata[brick_index / 8] & brick_used_mask) { - // mark brick as used - render_data.metadata[brick_index / 8] |= brick_used_mask; - - // erase connection of child brick to parent node - render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); - - // step victim pointer forward - self.step(); - return brick_index; - } - - // set the bricks used bit to 0 for the currently used brick - render_data.metadata[brick_index / 8] &= !brick_used_mask; - } - if - // node is not leaf - (0 == (render_data.metadata[self.meta_index] & 0x00000004)) - // in case node is uniform, octant 0 should have been checked for vacancy already, so it is safe to skip to next leaf node - || (0 != (render_data.metadata[self.meta_index] & 0x00000008)) - { - self.skip_node(); - } else { - self.step(); - } - } - } -} -impl OctreeGPUDataHandler { - //############################################################################## - // ██████████ █████████ ███████████ █████████ - // ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ - // ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ - // ░███ ░███ ░███████████ ░███ ░███████████ - // ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ - // ░███ ███ ░███ ░███ ░███ ░███ ░███ - // ██████████ █████ █████ █████ █████ █████ - // ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ - - // ██████████ ██████████ █████████ █████ █████████ ██████ █████ - // ░░███░░░░███ ░░███░░░░░█ ███░░░░░███░░███ ███░░░░░███░░██████ ░░███ - // ░███ ░░███ ░███ █ ░ ░███ ░░░ ░███ ███ ░░░ ░███░███ ░███ - // ░███ ░███ ░██████ ░░█████████ ░███ ░███ ░███░░███░███ - // ░███ ░███ ░███░░█ ░░░░░░░░███ ░███ ░███ █████ ░███ ░░██████ - // ░███ ███ ░███ ░ █ ███ ░███ ░███ ░░███ ░░███ ░███ ░░█████ - // ██████████ ██████████░░█████████ █████ ░░█████████ █████ ░░█████ - // ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ - //############################################################################## - /// Updates the meta element value to store the brick structure of the given leaf node. - /// Does not erase anything in @sized_node_meta, it's expected to be cleared before - /// the first use of this function - /// for the given brick octant - /// * `sized_node_meta` - the bytes to update - /// * `brick` - the brick to describe into the bytes - /// * `brick_octant` - the octant to update in the bytes - fn meta_add_leaf_brick_structure( - sized_node_meta: &mut u32, - brick: &BrickData, - brick_octant: usize, - ) where - T: Default + Clone + PartialEq + VoxelData, - { - match brick { - BrickData::Empty => {} // Child structure properties already set to NIL - BrickData::Solid(_voxel) => { - // set child Occupied bits, child Structure bits already set to NIL - *sized_node_meta |= 0x01 << (8 + brick_octant); - } - BrickData::Parted(_brick) => { - // set child Occupied bits - *sized_node_meta |= 0x01 << (8 + brick_octant); - - // set child Structure bits - *sized_node_meta |= 0x01 << (16 + brick_octant); - } - }; - } - - /// Creates the descriptor bytes for the given node - fn create_node_properties(node: &NodeContent) -> u32 - where - T: Default + Copy + Clone + PartialEq + VoxelData, - { - let mut meta = 0; - match node { - NodeContent::Internal(_) | NodeContent::Nothing => { - meta &= 0xFFFFFFFB; // element is not leaf - meta &= 0xFFFFFFF7; // element is not uniform - } - NodeContent::Leaf(bricks) => { - meta |= 0x00000004; // element is leaf - meta &= 0xFFFFFFF7; // element is not uniform - for octant in 0..8 { - Self::meta_add_leaf_brick_structure(&mut meta, &bricks[octant], octant); - } - } - NodeContent::UniformLeaf(brick) => { - meta |= 0x00000004; // element is leaf - meta |= 0x00000008; // element is uniform - Self::meta_add_leaf_brick_structure(&mut meta, brick, 0); - } - }; - meta - } - - //############################################################################## - // █████████ ██████████ ██████████ - // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ - // ░███ ░███ ░███ ░░███ ░███ ░░███ - // ░███████████ ░███ ░███ ░███ ░███ - // ░███░░░░░███ ░███ ░███ ░███ ░███ - // ░███ ░███ ░███ ███ ░███ ███ - // █████ █████ ██████████ ██████████ - // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ - // ██████ █████ ███████ ██████████ ██████████ - // ░░██████ ░░███ ███░░░░░███ ░░███░░░░███ ░░███░░░░░█ - // ░███░███ ░███ ███ ░░███ ░███ ░░███ ░███ █ ░ - // ░███░░███░███ ░███ ░███ ░███ ░███ ░██████ - // ░███ ░░██████ ░███ ░███ ░███ ░███ ░███░░█ - // ░███ ░░█████ ░░███ ███ ░███ ███ ░███ ░ █ - // █████ ░░█████ ░░░███████░ ██████████ ██████████ - // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ - //############################################################################## - fn add_node( - &mut self, - tree: &Octree, - node_key: usize, - try_add_children: bool, - ) where - T: Default + Copy + Clone + PartialEq + VoxelData, - { - if try_add_children && self.victim_node.is_full() { - // Do not add additional nodes at initial upload if the cache is already full - return; - } - - // Determine the index in meta - let node_element_index = self.victim_node.first_available_node(&mut self.render_data); - self.map_to_node_index_in_metadata - .insert(node_key, node_element_index); - - // Add node properties to metadata - self.render_data.metadata[node_element_index] = - Self::create_node_properties(tree.nodes.get(node_key)); - - // Update occupancy in ocbits - let occupied_bits = tree.stored_occupied_bits(node_key); - self.render_data.node_ocbits[node_element_index * 2] = - (occupied_bits & 0x00000000FFFFFFFF) as u32; - self.render_data.node_ocbits[node_element_index * 2 + 1] = - ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; - - // Add node content - match tree.nodes.get(node_key) { - NodeContent::UniformLeaf(brick) => { - debug_assert!( - matches!( - tree.node_children[node_key].content, - NodeChildrenArray::OccupancyBitmap(_) - ), - "Expected Uniform leaf to have OccupancyBitmap(_) instead of {:?}", - tree.node_children[node_key].content - ); - - let (brick_index, brick_added) = self.add_brick(brick); - self.render_data.node_children[node_element_index * 8 + 0] = brick_index; - self.render_data.node_children[node_element_index * 8 + 1] = empty_marker(); - self.render_data.node_children[node_element_index * 8 + 2] = empty_marker(); - self.render_data.node_children[node_element_index * 8 + 3] = empty_marker(); - self.render_data.node_children[node_element_index * 8 + 4] = empty_marker(); - self.render_data.node_children[node_element_index * 8 + 5] = empty_marker(); - self.render_data.node_children[node_element_index * 8 + 6] = empty_marker(); - self.render_data.node_children[node_element_index * 8 + 7] = empty_marker(); - #[cfg(debug_assertions)] - { - if !brick_added { - // If no brick was added, the occupied bits should either be empty or full - if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = - tree.node_children[node_key].content - { - debug_assert!(occupied_bits == 0 || occupied_bits == u64::MAX); - } - } - } - } - NodeContent::Leaf(bricks) => { - debug_assert!( - matches!( - tree.node_children[node_key].content, - NodeChildrenArray::OccupancyBitmap(_) - ), - "Expected Leaf to have OccupancyBitmaps(_) instead of {:?}", - tree.node_children[node_key].content - ); - for octant in 0..8 { - let (brick_index, brick_added) = self.add_brick(&bricks[octant]); - self.render_data.node_children[node_element_index * 8 + octant] = brick_index; - #[cfg(debug_assertions)] - { - if !brick_added { - // If no brick was added, the relevant occupied bits should either be empty or full - if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = - tree.node_children[node_key].content - { - debug_assert!( - 0 == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] - || BITMAP_MASK_FOR_OCTANT_LUT[octant] - == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] - ); - } - } - } - } - } - NodeContent::Internal(_) => { - for octant in 0..8 { - let child_key = tree.node_children[node_key][octant] as usize; - if child_key != empty_marker() as usize { - if try_add_children - && !self.map_to_node_index_in_metadata.contains_key(&child_key) - { - self.add_node(tree, child_key, try_add_children); - } - - self.render_data.node_children[node_element_index * 8 + octant as usize] = - *self - .map_to_node_index_in_metadata - .get(&child_key) - .unwrap_or(&(empty_marker() as usize)) - as u32; - } else { - self.render_data.node_children[node_element_index * 8 + octant as usize] = - empty_marker(); - } - } - } - NodeContent::Nothing => { - for octant in 0..8 { - self.render_data.node_children[node_element_index * 8 + octant as usize] = - empty_marker(); - } - } - } - } - - //############################################################################## - // █████████ ██████████ ██████████ - // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ - // ░███ ░███ ░███ ░░███ ░███ ░░███ - // ░███████████ ░███ ░███ ░███ ░███ - // ░███░░░░░███ ░███ ░███ ░███ ░███ - // ░███ ░███ ░███ ███ ░███ ███ - // █████ █████ ██████████ ██████████ - // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ - // ███████████ ███████████ █████ █████████ █████ ████ - // ░░███░░░░░███░░███░░░░░███ ░░███ ███░░░░░███░░███ ███░ - // ░███ ░███ ░███ ░███ ░███ ███ ░░░ ░███ ███ - // ░██████████ ░██████████ ░███ ░███ ░███████ - // ░███░░░░░███ ░███░░░░░███ ░███ ░███ ░███░░███ - // ░███ ░███ ░███ ░███ ░███ ░░███ ███ ░███ ░░███ - // ███████████ █████ █████ █████ ░░█████████ █████ ░░████ - // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░ - //############################################################################## - /// Loads a brick into the provided voxels vector and color palette - /// * `brick` - The brick to upload - /// * `returns` - the identifier to set in @SizedNode and true if a new brick was aded to the voxels vector - fn add_brick(&mut self, brick: &BrickData) -> (u32, bool) - where - T: Default + Clone + PartialEq + VoxelData, - { - debug_assert_eq!( - self.render_data.voxels.len() % (DIM * DIM * DIM), - 0, - "Expected Voxel buffer length({:?}) to be divisble by {:?}", - self.render_data.voxels.len(), - (DIM * DIM * DIM) - ); - - match brick { - BrickData::Empty => (empty_marker(), false), - BrickData::Solid(voxel) => { - let albedo = voxel.albedo(); - // The number of colors inserted into the palette is the size of the color palette map - let color_palette_size = self.map_to_color_index_in_palette.keys().len(); - if let std::collections::hash_map::Entry::Vacant(e) = - self.map_to_color_index_in_palette.entry(albedo) - { - e.insert(color_palette_size); - self.render_data.color_palette[color_palette_size] = Vec4::new( - albedo.r as f32 / 255., - albedo.g as f32 / 255., - albedo.b as f32 / 255., - albedo.a as f32 / 255., - ); - } - (self.map_to_color_index_in_palette[&albedo] as u32, false) - } - BrickData::Parted(brick) => { - let brick_index = self - .victim_brick - .first_available_brick(&mut self.render_data); - for z in 0..DIM { - for y in 0..DIM { - for x in 0..DIM { - // The number of colors inserted into the palette is the size of the color palette map - let potential_new_albedo_index = - self.map_to_color_index_in_palette.keys().len(); - let albedo = brick[x][y][z].albedo(); - let albedo_index = if let std::collections::hash_map::Entry::Vacant(e) = - self.map_to_color_index_in_palette.entry(albedo) - { - e.insert(potential_new_albedo_index); - self.render_data.color_palette[potential_new_albedo_index] = - Vec4::new( - albedo.r as f32 / 255., - albedo.g as f32 / 255., - albedo.b as f32 / 255., - albedo.a as f32 / 255., - ); - potential_new_albedo_index - } else { - self.map_to_color_index_in_palette[&albedo] - }; - self.render_data.voxels[(brick_index * (DIM * DIM * DIM)) - + flat_projection(x, y, z, DIM)] = Voxelement { - albedo_index: albedo_index as u32, - content: brick[x][y][z].user_data(), - }; - } - } - } - (brick_index as u32, true) - } - } - } -} - pub(crate) fn sync_with_main_world(// tree_view: Option>, // mut world: ResMut, ) { @@ -732,7 +237,7 @@ pub(crate) fn handle_gpu_readback( //############################################################################## pub(crate) fn write_to_gpu( tree_gpu_view: Option>, - svx_pipeline: Option>, + svx_pipeline: Option>, ) { //TODO: Document that all components are lost during extract transition // Data updates triggered by debug interface diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index 18ba83c..6f5e451 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -1,15 +1,16 @@ +mod cache; mod data; mod pipeline; pub mod types; pub use crate::octree::raytracing::bevy::types::{ - OctreeGPUView, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, + OctreeGPUView, SvxRenderPlugin, SvxViewingGlass, Viewport, }; use crate::octree::raytracing::bevy::{ data::{handle_gpu_readback, sync_with_main_world, write_to_gpu}, pipeline::prepare_bind_groups, - types::{ShocoVoxLabel, ShocoVoxRenderNode, ShocoVoxRenderPipeline}, + types::{SvxLabel, SvxRenderNode, SvxRenderPipeline}, }; use bevy::{ @@ -47,7 +48,7 @@ pub(crate) fn create_output_texture( images.add(output_texture) } -impl Plugin for ShocoVoxRenderPlugin { +impl Plugin for SvxRenderPlugin { fn build(&self, app: &mut App) { app.add_plugins(ExtractResourcePlugin::::default()); let render_app = app.sub_app_mut(RenderApp); @@ -62,17 +63,17 @@ impl Plugin for ShocoVoxRenderPlugin { ); let mut render_graph = render_app.world_mut().resource_mut::(); render_graph.add_node( - ShocoVoxLabel, - ShocoVoxRenderNode { + SvxLabel, + SvxRenderNode { ready: false, resolution: self.resolution, }, ); - render_graph.add_node_edge(ShocoVoxLabel, bevy::render::graph::CameraDriverLabel); + render_graph.add_node_edge(SvxLabel, bevy::render::graph::CameraDriverLabel); } fn finish(&self, app: &mut App) { let render_app = app.sub_app_mut(RenderApp); - render_app.init_resource::(); + render_app.init_resource::(); } } diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index cedb61e..38f9c47 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -1,6 +1,5 @@ use crate::octree::raytracing::bevy::types::{ - OctreeGPUView, ShocoVoxRenderData, ShocoVoxRenderNode, ShocoVoxRenderPipeline, - ShocoVoxViewingGlass, + OctreeGPUView, SvxRenderData, SvxRenderNode, SvxRenderPipeline, SvxViewingGlass, }; use bevy::{ @@ -25,12 +24,11 @@ use std::borrow::Cow; use super::types::OctreeGPUDataHandler; -impl FromWorld for ShocoVoxRenderPipeline { +impl FromWorld for SvxRenderPipeline { fn from_world(world: &mut World) -> Self { let render_device = world.resource::(); - let viewing_glass_bind_group_layout = - ShocoVoxViewingGlass::bind_group_layout(render_device); - let render_data_bind_group_layout = ShocoVoxRenderData::bind_group_layout(render_device); + let viewing_glass_bind_group_layout = SvxViewingGlass::bind_group_layout(render_device); + let render_data_bind_group_layout = SvxRenderData::bind_group_layout(render_device); let shader = world .resource::() .load("shaders/viewport_render.wgsl"); @@ -47,7 +45,7 @@ impl FromWorld for ShocoVoxRenderPipeline { entry_point: Cow::from("update"), }); - ShocoVoxRenderPipeline { + SvxRenderPipeline { tree_data_handler: None, render_queue: world.resource::().clone(), octree_meta_buffer: None, @@ -78,10 +76,10 @@ impl FromWorld for ShocoVoxRenderPipeline { // ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ //############################################################################## const WORKGROUP_SIZE: u32 = 8; -impl render_graph::Node for ShocoVoxRenderNode { +impl render_graph::Node for SvxRenderNode { fn update(&mut self, world: &mut World) { { - let svx_pipeline = world.resource::(); + let svx_pipeline = world.resource::(); let pipeline_cache = world.resource::(); let tree_gpu_view = world.get_resource::(); if !self.ready { @@ -101,7 +99,7 @@ impl render_graph::Node for ShocoVoxRenderNode { world: &World, ) -> Result<(), render_graph::NodeRunError> { let pipeline_cache = world.resource::(); - let svx_pipeline = world.resource::(); + let svx_pipeline = world.resource::(); let command_encoder = render_context.command_encoder(); if self.ready { @@ -179,7 +177,7 @@ pub(crate) fn prepare_bind_groups( gpu_images: Res>, fallback_image: Res, render_device: Res, - mut pipeline: ResMut, + mut pipeline: ResMut, tree_gpu_view: ResMut, ) { pipeline.viewing_glass_bind_group = Some( @@ -411,7 +409,7 @@ pub(crate) fn prepare_bind_groups( //############################################################################## // Create bind group let tree_bind_group = render_device.create_bind_group( - ShocoVoxRenderData::label(), + SvxRenderData::label(), &pipeline.render_data_bind_group_layout, &[ bevy::render::render_resource::BindGroupEntry { diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 6057793..ffd1642 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -40,7 +40,7 @@ pub struct Viewport { pub w_h_fov: V3cf32, } -pub struct ShocoVoxRenderPlugin { +pub struct SvxRenderPlugin { pub resolution: [u32; 2], } @@ -51,7 +51,7 @@ pub struct OctreeGPUView { pub do_the_thing: bool, pub read_back: u32, // --- DEBUG --- - pub viewing_glass: ShocoVoxViewingGlass, + pub viewing_glass: SvxViewingGlass, pub(crate) data_handler: Arc>, } @@ -66,7 +66,7 @@ pub(crate) struct VictimPointer { #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] #[type_path = "shocovox::gpu::OctreeGPUDataHandler"] pub struct OctreeGPUDataHandler { - pub(crate) render_data: ShocoVoxRenderData, + pub(crate) render_data: SvxRenderData, pub(crate) victim_node: VictimPointer, pub(crate) victim_brick: VictimPointer, pub(crate) map_to_node_index_in_metadata: HashMap, @@ -86,7 +86,7 @@ pub struct OctreeGPUDataHandler { #[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxViewingGlass"] -pub struct ShocoVoxViewingGlass { +pub struct SvxViewingGlass { #[storage_texture(0, image_format = Rgba8Unorm, access = ReadWrite)] pub output_texture: Handle, @@ -96,7 +96,7 @@ pub struct ShocoVoxViewingGlass { #[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxRenderData"] -pub struct ShocoVoxRenderData { +pub struct SvxRenderData { // +++ DEBUG +++ #[storage(6, visibility(compute))] pub(crate) debug_gpu_interface: u32, @@ -109,7 +109,7 @@ pub struct ShocoVoxRenderData { /// _===================================================================_ /// | Byte 0 | Node properties | /// |---------------------------------------------------------------------| - /// | bit 0 | 1 in case node is used by the raytracing algorithm *(2) | + /// | bit 0 | 1 if node is used by the raytracing algorithm *(2) *(4) | /// | bit 1 | unused | /// | bit 2 | 1 in case node is a leaf | /// | bit 3 | 1 in case node is uniform | @@ -137,6 +137,8 @@ pub struct ShocoVoxRenderData { /// *(3) One index in the array covers 8 bricks, which is the theoretical maximum /// number of bricks for one node. In practice however the number of bricks /// are only 4-5 times more, than the number of nodes, because of the internal nodes. + /// *(4) Root node does not have this bit used, because it will never be overwritten + /// due to the victim pointer logic #[storage(1, visibility(compute))] pub(crate) metadata: Vec, @@ -168,7 +170,7 @@ pub struct ShocoVoxRenderData { } #[derive(Resource)] -pub(crate) struct ShocoVoxRenderPipeline { +pub(crate) struct SvxRenderPipeline { pub update_tree: bool, pub(crate) render_queue: RenderQueue, @@ -191,9 +193,9 @@ pub(crate) struct ShocoVoxRenderPipeline { } #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] -pub(crate) struct ShocoVoxLabel; +pub(crate) struct SvxLabel; -pub(crate) struct ShocoVoxRenderNode { +pub(crate) struct SvxRenderNode { pub(crate) ready: bool, pub(crate) resolution: [u32; 2], } diff --git a/src/octree/raytracing/mod.rs b/src/octree/raytracing/mod.rs index 8615da6..25d7501 100644 --- a/src/octree/raytracing/mod.rs +++ b/src/octree/raytracing/mod.rs @@ -7,6 +7,4 @@ pub mod bevy; pub use crate::spatial::raytracing::Ray; #[cfg(feature = "bevy_wgpu")] -pub use bevy::types::{ - OctreeGPUView, ShocoVoxRenderData, ShocoVoxRenderPlugin, ShocoVoxViewingGlass, Viewport, -}; +pub use bevy::types::{OctreeGPUView, SvxRenderData, SvxRenderPlugin, SvxViewingGlass, Viewport}; From a07b4792ec20e3b9bf0a648f52f4080d3a74b59e Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 23 Nov 2024 02:01:08 +0100 Subject: [PATCH 12/19] Implemented missing data display --- assets/shaders/viewport_render.wgsl | 131 +++++++++++++++++++++++----- src/octree/raytracing/bevy/data.rs | 6 +- src/octree/update.rs | 4 +- 3 files changed, 113 insertions(+), 28 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index f2a41d1..a25dbd1 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -495,6 +495,84 @@ fn probe_brick( return OctreeRayIntersection(false, vec4f(0.), 0, vec3f(0.), vec3f(0., 0., 1.)); } +fn traverse_node_for_ocbits( + ray: ptr, + ray_current_distance: ptr, + node_key: u32, + node_bounds: ptr, + ray_scale_factors: ptr, +) -> vec3f { + let original_distance = *ray_current_distance; + + var position = vec3f( + point_in_ray_at_distance(ray, *ray_current_distance) + - (*node_bounds).min_position + ); + + var current_index = vec3i(vec3f( + clamp( (position.x * 4. / (*node_bounds).size), 0.5, 3.5), + clamp( (position.y * 4. / (*node_bounds).size), 0.5, 3.5), + clamp( (position.z * 4. / (*node_bounds).size), 0.5, 3.5), + )); + + var current_bounds = Cube( + ( + (*node_bounds).min_position + + vec3f(current_index) * ((*node_bounds).size / 4.) + ), + round((*node_bounds).size / 4.) + ); + + var safety = 0u; + var rgb_result = vec3f(0.); + loop { + if safety > 10 || current_index.x < 0 || current_index.x >= 4 + || current_index.y < 0 || current_index.y >= 4 + || current_index.z < 0 || current_index.z >= 4 + { + break; + } + + let bitmap_index = BITMAP_INDEX_LUT[u32(current_index.x)] + [u32(current_index.y)] + [u32(current_index.z)]; + if ( + ( + (bitmap_index < 32) + && ( + 0u != (node_occupied_bits[node_key * 2] + & (0x01u << bitmap_index) )) + )||( + (bitmap_index >= 32) + && ( + 0u != (node_occupied_bits[node_key * 2 + 1] + & (0x01u << (bitmap_index - 32)) + )) + ) + ){ + rgb_result = vec3f( + 1. - (f32(safety) * 0.25), + 1. - (f32(safety) * 0.25), + 1. - (f32(safety) * 0.25) + ); + break; + } + + let step = round(dda_step_to_next_sibling( + ray, + ray_current_distance, + ¤t_bounds, + ray_scale_factors + )); + current_bounds.min_position += step * current_bounds.size; + current_index += vec3i(step); + safety += 1u; + } + + *ray_current_distance = original_distance; + return rgb_result; +} + fn get_by_ray(ray: ptr) -> OctreeRayIntersection { var ray_scale_factors = get_dda_scale_factors(ray); // Should be const, but then it can't be passed as ptr let direction_lut_index = hash_direction((*ray).direction); @@ -559,6 +637,34 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { vec3f(4. - FLOAT_ERROR_TOLERANCE) ); + if( + // In case node doesn't yet have the relevant child node uploaded to GPU + (0 == (0x00000004 & current_node_meta)) // node is not a leaf + && target_octant != OOB_OCTANT + && target_child_key == EMPTY_MARKER // target child key is invalid + && ( // node is occupied at target octant + 0 != ( + BITMAP_MASK_FOR_OCTANT_LUT[target_octant][0] + & node_occupied_bits[current_node_key * 2] + ) + || 0 != ( + BITMAP_MASK_FOR_OCTANT_LUT[target_octant][1] + & node_occupied_bits[current_node_key * 2 + 1] + ) + ) + ){ + missing_data_color += ( + vec3f(0.5,0.3,0.0) * + traverse_node_for_ocbits( + ray, + &ray_current_distance, + current_node_key, + ¤t_bounds, + &ray_scale_factors + ) + ); + } + if (target_octant != OOB_OCTANT) { if(0 != (0x00000004 & current_node_meta)) { // node is leaf var hit: OctreeRayIntersection; @@ -585,24 +691,6 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { } } - if( // In case node doesn't yet have the relevant child node uploaded to GPU - (0 == (0x00000004 & current_node_meta)) // node is not a leaf - && target_octant != OOB_OCTANT - && target_child_key >= arrayLength(&metadata) // target child key is invalid - && ( // node is occupied at target octant - 0 != ( - BITMAP_MASK_FOR_OCTANT_LUT[target_octant][0] - & node_occupied_bits[current_node_key * 2] - ) - || 0 != ( - BITMAP_MASK_FOR_OCTANT_LUT[target_octant][1] - & node_occupied_bits[current_node_key * 2 + 1] - ) - ) - ){ - missing_data_color += vec3f(0.3,0.2,0.0); - } - if( do_backtrack_after_leaf_miss || target_octant == OOB_OCTANT || EMPTY_MARKER == current_node_key // Guards statements in other conditions, but should never happen @@ -624,7 +712,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { & node_occupied_bits[current_node_key * 2 + 1] ) ) - ){ + ) { // POP node_stack_pop(&node_stack, &node_stack_meta); step_vec = dda_step_to_next_sibling( @@ -652,10 +740,11 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { } continue; } + if ( ( 0 == (0x00000004 & current_node_meta) // node is not a leaf - && target_child_key < arrayLength(&metadata) //!crate::object_pool::key_is_valid + && target_child_key != EMPTY_MARKER //crate::object_pool::key_is_valid ) && ( // There is overlap in node occupancy and potential ray hit area 0 != ( @@ -714,7 +803,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { if ( target_octant == OOB_OCTANT || ( // In case the current internal node has a valid target child - target_child_key < arrayLength(&metadata) //crate::object_pool::key_is_valid + target_child_key != EMPTY_MARKER //crate::object_pool::key_is_valid && 0 == (0x00000004 & current_node_meta) // node is not a leaf && ( // target child is in the area the ray can potentially hit 0 != ( diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 34db7fd..995fc3b 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -98,11 +98,6 @@ where gpu_data_handler.add_node(&self, Self::ROOT_NODE_KEY as usize, true); // +++ DEBUG +++ - //Push additional nodes to try to overwrite existing ones - /*for node_key in 10..15 { - gpu_data_handler.add_node(&self, node_key, false); - }*/ - //delete some random bricks from leaf nodes for i in 15..20 { if 0 != (gpu_data_handler.render_data.metadata[i] & 0x00000004) { @@ -114,6 +109,7 @@ where for meta in gpu_data_handler.render_data.metadata.iter_mut() { *meta &= 0x00FFFFFE; } + // --- DEBUG --- let output_texture = create_output_texture(resolution, images); diff --git a/src/octree/update.rs b/src/octree/update.rs index 8badc36..0e7ba37 100644 --- a/src/octree/update.rs +++ b/src/octree/update.rs @@ -472,8 +472,8 @@ where if let NodeContent::Internal(ref mut occupied_bits) = self.nodes.get_mut(node_key as usize) { - let corrected_update_size = ((node_bounds.size * actual_update_size as f32) - / BITMAP_DIMENSION as f32) + let corrected_update_size = ((actual_update_size as f32 * BITMAP_DIMENSION as f32) + / node_bounds.size) .ceil() as usize; set_occupancy_in_bitmap_64bits( &matrix_index_for(&node_bounds, &(position.into()), BITMAP_DIMENSION), From 4bd70ef688f4c49ab45528cdeff6ace7844fa2cc Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 23 Nov 2024 12:43:28 +0100 Subject: [PATCH 13/19] Refactor of bevy module, Clarified architecture and API --- examples/dot_cube.rs | 82 ++--- examples/minecraft.rs | 55 ++- src/octree/convert/bytecode.rs | 2 - src/octree/mod.rs | 2 - src/octree/raytracing/bevy/cache.rs | 6 +- src/octree/raytracing/bevy/data.rs | 165 +++++---- src/octree/raytracing/bevy/mod.rs | 51 ++- src/octree/raytracing/bevy/pipeline.rs | 451 +++++++++---------------- src/octree/raytracing/bevy/types.rs | 67 ++-- src/octree/raytracing/mod.rs | 4 +- src/octree/types.rs | 3 - 11 files changed, 370 insertions(+), 518 deletions(-) diff --git a/examples/dot_cube.rs b/examples/dot_cube.rs index 02e5b89..b869550 100644 --- a/examples/dot_cube.rs +++ b/examples/dot_cube.rs @@ -9,7 +9,7 @@ use iyes_perf_ui::{ #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, + raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, Albedo, Octree, V3c, VoxelData, }; @@ -22,15 +22,6 @@ const BRICK_DIMENSION: usize = 2; #[cfg(feature = "bevy_wgpu")] const TREE_SIZE: u32 = 16; -#[cfg(feature = "bevy_wgpu")] -#[derive(Resource)] -struct TreeResource -where - T: Default + Clone + PartialEq + VoxelData, -{ - tree: Octree, -} - #[cfg(feature = "bevy_wgpu")] fn main() { App::new() @@ -51,8 +42,6 @@ fn main() { #[cfg(feature = "bevy_wgpu")] fn setup(mut commands: Commands, images: ResMut>) { - // use shocovox_rs::octree::raytracing::bevy::create_viewing_glass; - let origin = V3c::new( TREE_SIZE as f32 * 2., TREE_SIZE as f32 / 2., @@ -64,19 +53,6 @@ fn setup(mut commands: Commands, images: ResMut>) { .ok() .unwrap(); - // +++ DEBUG +++ - // tree.insert(&V3c::new(0, 0, 0), Albedo::from(0x66FFFF)) - // .ok() - // .unwrap(); - // tree.insert(&V3c::new(3, 3, 3), Albedo::from(0x66FFFF)) - // .ok() - // .unwrap(); - // assert!(tree.get(&V3c::new(3, 3, 3)).is_some()); - // tree.insert_at_lod(&V3c::new(0, 0, 0), 128, Albedo::from(0x66FFFF)) - // .ok() - // .unwrap(); - - // ---DEBUG --- for x in 0..TREE_SIZE { for y in 0..TREE_SIZE { for z in 0..TREE_SIZE { @@ -116,8 +92,15 @@ fn setup(mut commands: Commands, images: ResMut>) { } } - let output_texture = tree.create_new_gpu_view( - 42, //TODO: decide actual number + commands.spawn(DomePosition { + yaw: 0., + roll: 0., + radius: tree.get_size() as f32 * 2.2, + }); + + let host = OctreeGPUHost::new(tree); + let output_texture = host.create_new_view( + 40, //TODO: decide actual number Viewport { origin, direction: (V3c::new(0., 0., 0.) - origin).normalized(), @@ -127,12 +110,7 @@ fn setup(mut commands: Commands, images: ResMut>) { &mut commands, images, ); - - commands.spawn(DomePosition { - yaw: 0., - roll: 0., - radius: tree.get_size() as f32 * 2.2, - }); + commands.insert_resource(host); commands.spawn(SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(1024., 768.)), @@ -142,7 +120,6 @@ fn setup(mut commands: Commands, images: ResMut>) { ..default() }); commands.spawn(Camera2dBundle::default()); - commands.insert_resource(TreeResource { tree }); commands.spawn(( PerfUiRoot::default(), PerfUiEntryFPS { @@ -174,13 +151,13 @@ fn rotate_camera(angles_query: Query<&mut DomePosition>, mut tree_view: ResMut>, mut tree_view: ResMut, mut angles_query: Query<&mut DomePosition>, - tree: Res>, + tree: Res>, ) { if keys.pressed(KeyCode::Delete) { tree_view.do_the_thing = true; @@ -209,17 +186,16 @@ fn handle_zoom( // Render the current view with CPU let viewport_up_direction = V3c::new(0., 1., 0.); let viewport_right_direction = viewport_up_direction - .cross(tree_view.viewing_glass.viewport.direction) + .cross(tree_view.spyglass.viewport.direction) .normalized(); let pixel_width = - tree_view.viewing_glass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; + tree_view.spyglass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; let pixel_height = - tree_view.viewing_glass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; - let viewport_bottom_left = tree_view.viewing_glass.viewport.origin - + (tree_view.viewing_glass.viewport.direction - * tree_view.viewing_glass.viewport.w_h_fov.z) - - (viewport_up_direction * (tree_view.viewing_glass.viewport.w_h_fov.y / 2.)) - - (viewport_right_direction * (tree_view.viewing_glass.viewport.w_h_fov.x / 2.)); + tree_view.spyglass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; + let viewport_bottom_left = tree_view.spyglass.viewport.origin + + (tree_view.spyglass.viewport.direction * tree_view.spyglass.viewport.w_h_fov.z) + - (viewport_up_direction * (tree_view.spyglass.viewport.w_h_fov.y / 2.)) + - (viewport_right_direction * (tree_view.spyglass.viewport.w_h_fov.x / 2.)); // define light let diffuse_light_normal = V3c::new(0., -1., 1.).normalized(); @@ -237,8 +213,8 @@ fn handle_zoom( + viewport_right_direction * x as f32 * pixel_width + viewport_up_direction * y as f32 * pixel_height; let ray = Ray { - origin: tree_view.viewing_glass.viewport.origin, - direction: (glass_point - tree_view.viewing_glass.viewport.origin).normalized(), + origin: tree_view.spyglass.viewport.origin, + direction: (glass_point - tree_view.spyglass.viewport.origin).normalized(), }; use std::io::Write; @@ -282,11 +258,9 @@ fn handle_zoom( } if keys.pressed(KeyCode::ArrowLeft) { angles_query.single_mut().yaw = angle_update_fn(angles_query.single().yaw, ADDITION); - // println!("viewport: {:?}", viewing_glass.viewport); } if keys.pressed(KeyCode::ArrowRight) { angles_query.single_mut().yaw = angle_update_fn(angles_query.single().yaw, -ADDITION); - // println!("viewport: {:?}", viewing_glass.viewport); } if keys.pressed(KeyCode::PageUp) { angles_query.single_mut().radius *= 1. - 0.02 * multiplier; @@ -295,12 +269,12 @@ fn handle_zoom( angles_query.single_mut().radius *= 1. + 0.02 * multiplier; } if keys.pressed(KeyCode::Home) { - tree_view.viewing_glass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; - tree_view.viewing_glass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; } if keys.pressed(KeyCode::End) { - tree_view.viewing_glass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; - tree_view.viewing_glass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; } } diff --git a/examples/minecraft.rs b/examples/minecraft.rs index d5cc93b..3685a4f 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -3,7 +3,7 @@ use bevy::{prelude::*, window::WindowPlugin}; #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, + raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, Albedo, Octree, V3c, VoxelData, }; @@ -20,15 +20,6 @@ const DISPLAY_RESOLUTION: [u32; 2] = [1024, 768]; #[cfg(feature = "bevy_wgpu")] const BRICK_DIMENSION: usize = 32; -#[cfg(feature = "bevy_wgpu")] -#[derive(Resource)] -struct TreeResource -where - T: Default + Clone + PartialEq + VoxelData, -{ - tree: Octree, -} - #[cfg(feature = "bevy_wgpu")] fn main() { App::new() @@ -79,7 +70,8 @@ fn setup(mut commands: Commands, images: ResMut>) { radius: tree.get_size() as f32 * 2.2, }); - let output_texture = tree.create_new_gpu_view( + let host = OctreeGPUHost::new(tree); + let output_texture = host.create_new_view( 20, Viewport { origin: V3c { @@ -98,7 +90,7 @@ fn setup(mut commands: Commands, images: ResMut>) { &mut commands, images, ); - + commands.insert_resource(host); commands.spawn(SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(1024., 768.)), @@ -108,8 +100,6 @@ fn setup(mut commands: Commands, images: ResMut>) { ..default() }); commands.spawn(Camera2dBundle::default()); - commands.insert_resource(TreeResource { tree }); - commands.spawn(( PerfUiRoot::default(), PerfUiEntryFPS { @@ -142,13 +132,13 @@ fn rotate_camera(angles_query: Query<&mut DomePosition>, mut tree_view: ResMut>, mut tree_view: ResMut, mut angles_query: Query<&mut DomePosition>, - tree: Res>, + tree: Res>, ) { if keys.pressed(KeyCode::Delete) { tree_view.do_the_thing = true; @@ -177,17 +167,16 @@ fn handle_zoom( // Render the current view with CPU let viewport_up_direction = V3c::new(0., 1., 0.); let viewport_right_direction = viewport_up_direction - .cross(tree_view.viewing_glass.viewport.direction) + .cross(tree_view.spyglass.viewport.direction) .normalized(); let pixel_width = - tree_view.viewing_glass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; + tree_view.spyglass.viewport.w_h_fov.x as f32 / DISPLAY_RESOLUTION[0] as f32; let pixel_height = - tree_view.viewing_glass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; - let viewport_bottom_left = tree_view.viewing_glass.viewport.origin - + (tree_view.viewing_glass.viewport.direction - * tree_view.viewing_glass.viewport.w_h_fov.z) - - (viewport_up_direction * (tree_view.viewing_glass.viewport.w_h_fov.y / 2.)) - - (viewport_right_direction * (tree_view.viewing_glass.viewport.w_h_fov.x / 2.)); + tree_view.spyglass.viewport.w_h_fov.y as f32 / DISPLAY_RESOLUTION[1] as f32; + let viewport_bottom_left = tree_view.spyglass.viewport.origin + + (tree_view.spyglass.viewport.direction * tree_view.spyglass.viewport.w_h_fov.z) + - (viewport_up_direction * (tree_view.spyglass.viewport.w_h_fov.y / 2.)) + - (viewport_right_direction * (tree_view.spyglass.viewport.w_h_fov.x / 2.)); // define light let diffuse_light_normal = V3c::new(0., -1., 1.).normalized(); @@ -205,8 +194,8 @@ fn handle_zoom( + viewport_right_direction * x as f32 * pixel_width + viewport_up_direction * y as f32 * pixel_height; let ray = Ray { - origin: tree_view.viewing_glass.viewport.origin, - direction: (glass_point - tree_view.viewing_glass.viewport.origin).normalized(), + origin: tree_view.spyglass.viewport.origin, + direction: (glass_point - tree_view.spyglass.viewport.origin).normalized(), }; use std::io::Write; @@ -250,11 +239,9 @@ fn handle_zoom( } if keys.pressed(KeyCode::ArrowLeft) { angles_query.single_mut().yaw = angle_update_fn(angles_query.single().yaw, ADDITION); - // println!("viewport: {:?}", viewing_glass.viewport); } if keys.pressed(KeyCode::ArrowRight) { angles_query.single_mut().yaw = angle_update_fn(angles_query.single().yaw, -ADDITION); - // println!("viewport: {:?}", viewing_glass.viewport); } if keys.pressed(KeyCode::PageUp) { angles_query.single_mut().radius *= 1. - 0.02 * multiplier; @@ -263,12 +250,12 @@ fn handle_zoom( angles_query.single_mut().radius *= 1. + 0.02 * multiplier; } if keys.pressed(KeyCode::Home) { - tree_view.viewing_glass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; - tree_view.viewing_glass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.x *= 1. + 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.y *= 1. + 0.09 * multiplier; } if keys.pressed(KeyCode::End) { - tree_view.viewing_glass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; - tree_view.viewing_glass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.x *= 1. - 0.09 * multiplier; + tree_view.spyglass.viewport.w_h_fov.y *= 1. - 0.09 * multiplier; } } diff --git a/src/octree/convert/bytecode.rs b/src/octree/convert/bytecode.rs index e4c099a..7210069 100644 --- a/src/octree/convert/bytecode.rs +++ b/src/octree/convert/bytecode.rs @@ -409,8 +409,6 @@ where )?; let node_children = Vec::decode_bencode_object(list.next_object()?.unwrap())?; Ok(Self { - #[cfg(feature = "bevy_wgpu")] - views: Vec::new(), auto_simplify, octree_size: root_size, nodes, diff --git a/src/octree/mod.rs b/src/octree/mod.rs index 37dee73..0efe2d3 100644 --- a/src/octree/mod.rs +++ b/src/octree/mod.rs @@ -82,8 +82,6 @@ where let root_node_key = nodes.push(NodeContent::Nothing); // The first element is the root Node assert!(root_node_key == 0); Ok(Self { - #[cfg(feature = "bevy_wgpu")] - views: Vec::new(), auto_simplify: true, octree_size: size, nodes, diff --git a/src/octree/raytracing/bevy/cache.rs b/src/octree/raytracing/bevy/cache.rs index 6b522a9..1a6f81f 100644 --- a/src/octree/raytracing/bevy/cache.rs +++ b/src/octree/raytracing/bevy/cache.rs @@ -2,7 +2,7 @@ use crate::object_pool::empty_marker; use crate::spatial::math::flat_projection; use crate::{ octree::{ - raytracing::bevy::types::{SvxRenderData, Voxelement}, + raytracing::bevy::types::{OctreeRenderData, Voxelement}, types::{NodeChildrenArray, NodeContent}, BrickData, Octree, VoxelData, }, @@ -64,7 +64,7 @@ impl VictimPointer { /// Provides the first available index in the metadata buffer which can be overwritten /// with node related information. It never returns with 0, because that is reserved for the root node, /// which should not be overwritten. - fn first_available_node(&mut self, render_data: &mut SvxRenderData) -> usize { + fn first_available_node(&mut self, render_data: &mut OctreeRenderData) -> usize { // If there is space left in the cache, use it all up if !self.is_full() { render_data.metadata[self.stored_items] |= 0x01; @@ -131,7 +131,7 @@ impl VictimPointer { } /// Finds the first available brick, and marks it as used - fn first_available_brick(&mut self, render_data: &mut SvxRenderData) -> usize { + fn first_available_brick(&mut self, render_data: &mut OctreeRenderData) -> usize { let max_brick_count = render_data.metadata.len() * 8; // If there is space left in the cache, use it all up diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 995fc3b..5d10ac9 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -1,11 +1,8 @@ use crate::object_pool::empty_marker; use crate::octree::{ - raytracing::bevy::{ - create_output_texture, - types::{ - OctreeGPUView, OctreeMetaData, SvxRenderData, SvxRenderPipeline, SvxViewingGlass, - Viewport, Voxelement, - }, + raytracing::bevy::types::{ + OctreeGPUDataHandler, OctreeGPUHost, OctreeGPUView, OctreeMetaData, OctreeRenderData, + OctreeSpyGlass, SvxRenderPipeline, VictimPointer, Viewport, Voxelement, }, Octree, V3c, VoxelData, }; @@ -13,16 +10,21 @@ use bevy::{ ecs::system::{Res, ResMut}, math::Vec4, prelude::{Assets, Commands, Handle, Image}, - render::renderer::RenderDevice, + render::{ + render_asset::RenderAssetUsages, + render_resource::{ + encase::{StorageBuffer, UniformBuffer}, + Extent3d, TextureDimension, TextureFormat, TextureUsages, + }, + renderer::RenderDevice, + }, }; use std::{ collections::HashMap, sync::{Arc, Mutex}, }; -use super::types::{OctreeGPUDataHandler, VictimPointer}; - -impl Octree +impl OctreeGPUHost where T: Default + Clone + Copy + PartialEq + VoxelData, { @@ -53,27 +55,26 @@ where //############################################################################## /// Creates GPU compatible data renderable on the GPU from an octree - pub fn create_new_gpu_view( + pub fn create_new_view( &self, size: usize, viewport: Viewport, resolution: [u32; 2], commands: &mut Commands, - images: ResMut>, + mut images: ResMut>, ) -> Handle { let mut gpu_data_handler = OctreeGPUDataHandler { - debug_gpu_interface: None, - readable_debug_gpu_interface: None, - render_data: SvxRenderData { + read_back: 0, + render_data: OctreeRenderData { debug_gpu_interface: 0, octree_meta: OctreeMetaData { - octree_size: self.octree_size, + octree_size: self.tree.octree_size, voxel_brick_dim: DIM as u32, ambient_light_color: V3c::new(1., 1., 1.), ambient_light_position: V3c::new( - self.octree_size as f32, - self.octree_size as f32, - self.octree_size as f32, + self.tree.octree_size as f32, + self.tree.octree_size as f32, + self.tree.octree_size as f32, ), }, metadata: vec![0; size], @@ -95,7 +96,7 @@ where }; // Push root node and its contents - gpu_data_handler.add_node(&self, Self::ROOT_NODE_KEY as usize, true); + gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, true); // +++ DEBUG +++ //delete some random bricks from leaf nodes @@ -112,12 +113,26 @@ where // --- DEBUG --- - let output_texture = create_output_texture(resolution, images); + let mut output_texture = Image::new_fill( + Extent3d { + width: resolution[0], + height: resolution[1], + depth_or_array_layers: 1, + }, + TextureDimension::D2, + &[0, 0, 0, 255], + TextureFormat::Rgba8Unorm, + RenderAssetUsages::RENDER_WORLD, + ); + output_texture.texture_descriptor.usage = TextureUsages::COPY_DST + | TextureUsages::STORAGE_BINDING + | TextureUsages::TEXTURE_BINDING; + let output_texture = images.add(output_texture); + commands.insert_resource(OctreeGPUView { do_the_thing: false, - read_back: 0, data_handler: Arc::new(Mutex::new(gpu_data_handler)), - viewing_glass: SvxViewingGlass { + spyglass: OctreeSpyGlass { output_texture: output_texture.clone(), viewport: viewport, }, @@ -162,51 +177,52 @@ pub(crate) fn sync_with_main_world(// tree_view: Option>, //############################################################################## pub(crate) fn handle_gpu_readback( render_device: Res, - mut tree_gpu_view: Option>, + tree_gpu_view: Option>, + svx_pipeline: Option>, ) { // Data updates triggered by debug interface - if let Some(ref mut tree_gpu_view) = tree_gpu_view { + if let (Some(ref mut tree_gpu_view), Some(ref mut pipeline)) = (tree_gpu_view, svx_pipeline) { if tree_gpu_view.do_the_thing { - let received_data; - { - let data_handler = tree_gpu_view.data_handler.lock().unwrap(); - // GPU buffer read - // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html - let buffer_slice = data_handler - .readable_debug_gpu_interface - .as_ref() - .unwrap() - .slice(..); - let (s, r) = crossbeam::channel::unbounded::<()>(); - buffer_slice.map_async(bevy::render::render_resource::MapMode::Read, move |d| { - match d { - Ok(_) => s.send(()).expect("Failed to send map update"), - Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), - } - }); + let mut data_handler = tree_gpu_view.data_handler.lock().unwrap(); + // GPU buffer read + // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html + let buffer_slice = pipeline + .resources + .as_ref() + .unwrap() + .readable_debug_gpu_interface + .slice(..); + let (s, r) = crossbeam::channel::unbounded::<()>(); + buffer_slice.map_async( + bevy::render::render_resource::MapMode::Read, + move |d| match d { + Ok(_) => s.send(()).expect("Failed to send map update"), + Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), + }, + ); - render_device - .poll(bevy::render::render_resource::Maintain::wait()) - .panic_on_timeout(); + render_device + .poll(bevy::render::render_resource::Maintain::wait()) + .panic_on_timeout(); - r.recv().expect("Failed to receive the map_async message"); - { - let buffer_view = buffer_slice.get_mapped_range(); + r.recv().expect("Failed to receive the map_async message"); + { + let buffer_view = buffer_slice.get_mapped_range(); - let data = buffer_view - .chunks(std::mem::size_of::()) - .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) - .collect::>(); - received_data = data[0]; - // println!("received_data: {:?}", data); - } - data_handler - .readable_debug_gpu_interface - .as_ref() - .unwrap() - .unmap(); + let data = buffer_view + .chunks(std::mem::size_of::()) + .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + .collect::>(); + data_handler.read_back = data[0]; + // println!("received_data: {:?}", data); } - tree_gpu_view.read_back = received_data; + pipeline + .resources + .as_ref() + .unwrap() + .readable_debug_gpu_interface + .unmap(); + std::mem::drop(data_handler); tree_gpu_view.do_the_thing = false; } } @@ -235,23 +251,24 @@ pub(crate) fn write_to_gpu( tree_gpu_view: Option>, svx_pipeline: Option>, ) { - //TODO: Document that all components are lost during extract transition - // Data updates triggered by debug interface - if let Some(tree_gpu_view) = tree_gpu_view { - let svx_pipeline = svx_pipeline.unwrap(); - let render_queue = &svx_pipeline.render_queue.0; + if let (Some(tree_gpu_view), Some(pipeline)) = (tree_gpu_view, svx_pipeline) { + let render_queue = &pipeline.render_queue.0; + + // Data updates for spyglass viewport + if let Some(resources) = &pipeline.resources { + let mut buffer = UniformBuffer::new(Vec::::new()); + buffer.write(&tree_gpu_view.spyglass.viewport).unwrap(); + pipeline + .render_queue + .write_buffer(&resources.viewport_buffer, 0, &buffer.into_inner()); + } + + // Data updates triggered by debug interface if tree_gpu_view.do_the_thing { // GPU buffer Write - use bevy::render::render_resource::encase::StorageBuffer; let data_buffer = StorageBuffer::new(vec![0, 0, 0, 1]); render_queue.write_buffer( - tree_gpu_view - .data_handler - .lock() - .unwrap() - .debug_gpu_interface - .as_ref() - .unwrap(), + &pipeline.resources.as_ref().unwrap().debug_gpu_interface, 0, &data_buffer.into_inner(), ); diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index 6f5e451..efe721a 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -4,48 +4,37 @@ mod pipeline; pub mod types; pub use crate::octree::raytracing::bevy::types::{ - OctreeGPUView, SvxRenderPlugin, SvxViewingGlass, Viewport, + OctreeGPUHost, OctreeGPUView, OctreeSpyGlass, SvxRenderPlugin, Viewport, }; -use crate::octree::raytracing::bevy::{ - data::{handle_gpu_readback, sync_with_main_world, write_to_gpu}, - pipeline::prepare_bind_groups, - types::{SvxLabel, SvxRenderNode, SvxRenderPipeline}, +use crate::octree::{ + raytracing::bevy::{ + data::{handle_gpu_readback, sync_with_main_world, write_to_gpu}, + pipeline::prepare_bind_groups, + types::{SvxLabel, SvxRenderNode, SvxRenderPipeline}, + }, + Octree, VoxelData, }; use bevy::{ app::{App, Plugin}, - asset::{Assets, Handle}, - ecs::system::ResMut, prelude::{ExtractSchedule, IntoSystemConfigs}, render::{ - extract_resource::ExtractResourcePlugin, - prelude::Image, - render_asset::RenderAssetUsages, - render_graph::RenderGraph, - render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, - Render, RenderApp, RenderSet, + extract_resource::ExtractResourcePlugin, render_graph::RenderGraph, Render, RenderApp, + RenderSet, }, }; -pub(crate) fn create_output_texture( - resolution: [u32; 2], - mut images: ResMut>, -) -> Handle { - let mut output_texture = Image::new_fill( - Extent3d { - width: resolution[0], - height: resolution[1], - depth_or_array_layers: 1, - }, - TextureDimension::D2, - &[0, 0, 0, 255], - TextureFormat::Rgba8Unorm, - RenderAssetUsages::RENDER_WORLD, - ); - output_texture.texture_descriptor.usage = - TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING; - images.add(output_texture) +impl OctreeGPUHost +where + T: Default + Clone + Copy + PartialEq + VoxelData, +{ + pub fn new(tree: Octree) -> Self { + OctreeGPUHost { + tree, + views: Vec::new(), + } + } } impl Plugin for SvxRenderPlugin { diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index 38f9c47..a00dfad 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -1,5 +1,5 @@ use crate::octree::raytracing::bevy::types::{ - OctreeGPUView, SvxRenderData, SvxRenderNode, SvxRenderPipeline, SvxViewingGlass, + OctreeGPUView, OctreeRenderData, OctreeSpyGlass, SvxRenderNode, SvxRenderPipeline, }; use bevy::{ @@ -14,7 +14,7 @@ use bevy::{ render_resource::{ encase::{StorageBuffer, UniformBuffer}, AsBindGroup, BufferDescriptor, BufferInitDescriptor, BufferUsages, CachedPipelineState, - ComputePassDescriptor, ComputePipelineDescriptor, PipelineCache, + ComputePassDescriptor, ComputePipelineDescriptor, OwnedBindingResource, PipelineCache, }, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::{FallbackImage, GpuImage}, @@ -22,13 +22,13 @@ use bevy::{ }; use std::borrow::Cow; -use super::types::OctreeGPUDataHandler; +use super::types::OctreeRenderDataResources; impl FromWorld for SvxRenderPipeline { fn from_world(world: &mut World) -> Self { let render_device = world.resource::(); - let viewing_glass_bind_group_layout = SvxViewingGlass::bind_group_layout(render_device); - let render_data_bind_group_layout = SvxRenderData::bind_group_layout(render_device); + let spyglass_bind_group_layout = OctreeSpyGlass::bind_group_layout(render_device); + let render_data_bind_group_layout = OctreeRenderData::bind_group_layout(render_device); let shader = world .resource::() .load("shaders/viewport_render.wgsl"); @@ -36,7 +36,7 @@ impl FromWorld for SvxRenderPipeline { let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { label: None, layout: vec![ - viewing_glass_bind_group_layout.clone(), + spyglass_bind_group_layout.clone(), render_data_bind_group_layout.clone(), ], push_constant_ranges: Vec::new(), @@ -46,21 +46,12 @@ impl FromWorld for SvxRenderPipeline { }); SvxRenderPipeline { - tree_data_handler: None, render_queue: world.resource::().clone(), - octree_meta_buffer: None, - metadata_buffer: None, - node_children_buffer: None, - node_ocbits_buffer: None, - voxels_buffer: None, - color_palette_buffer: None, - readable_metadata_buffer: None, update_tree: true, - viewing_glass_bind_group_layout, + spyglass_bind_group_layout, render_data_bind_group_layout, update_pipeline, - viewing_glass_bind_group: None, - tree_bind_group: None, + resources: None, } } } @@ -109,10 +100,14 @@ impl render_graph::Node for SvxRenderNode { pass.set_bind_group( 0, - svx_pipeline.viewing_glass_bind_group.as_ref().unwrap(), + &svx_pipeline.resources.as_ref().unwrap().spyglass_bind_group, + &[], + ); + pass.set_bind_group( + 1, + &svx_pipeline.resources.as_ref().unwrap().tree_bind_group, &[], ); - pass.set_bind_group(1, svx_pipeline.tree_bind_group.as_ref().unwrap(), &[]); let pipeline = pipeline_cache .get_compute_pipeline(svx_pipeline.update_pipeline) .unwrap(); @@ -125,19 +120,25 @@ impl render_graph::Node for SvxRenderNode { } command_encoder.copy_buffer_to_buffer( - svx_pipeline.metadata_buffer.as_ref().unwrap(), + &svx_pipeline.resources.as_ref().unwrap().metadata_buffer, 0, - svx_pipeline.readable_metadata_buffer.as_ref().unwrap(), + &svx_pipeline + .resources + .as_ref() + .unwrap() + .readable_metadata_buffer, 0, std::mem::size_of::() as u64, ); // +++ DEBUG +++ - let tree_gpu_view = world.resource::(); - let data_handler = tree_gpu_view.data_handler.lock().unwrap(); command_encoder.copy_buffer_to_buffer( - data_handler.debug_gpu_interface.as_ref().unwrap(), + &svx_pipeline.resources.as_ref().unwrap().debug_gpu_interface, 0, - data_handler.readable_debug_gpu_interface.as_ref().unwrap(), + &svx_pipeline + .resources + .as_ref() + .unwrap() + .readable_debug_gpu_interface, 0, std::mem::size_of::() as u64, ) @@ -180,327 +181,209 @@ pub(crate) fn prepare_bind_groups( mut pipeline: ResMut, tree_gpu_view: ResMut, ) { - pipeline.viewing_glass_bind_group = Some( - tree_gpu_view - .viewing_glass - .as_bind_group( - &pipeline.viewing_glass_bind_group_layout, - &render_device, - &gpu_images, - &fallback_image, - ) - .ok() - .unwrap() - .bind_group, - ); + if pipeline.resources.is_some() && !pipeline.update_tree { + return; + } - if pipeline.update_tree { - let mut data_handler = tree_gpu_view.data_handler.lock().unwrap(); + let data_handler = tree_gpu_view.data_handler.lock().unwrap(); + if let Some(resources) = &pipeline.resources { + let mut buffer = UniformBuffer::new(Vec::::new()); + buffer.write(&data_handler.render_data.octree_meta).unwrap(); + pipeline + .render_queue + .write_buffer(&resources.metadata_buffer, 0, &buffer.into_inner()); - // Create the staging buffer helping in reading data from the GPU - //############################################################################## - // █████████ ███████████ █████ █████ - // ███░░░░░███░░███░░░░░███░░███ ░░███ - // ███ ░░░ ░███ ░███ ░███ ░███ - // ░███ ░██████████ ░███ ░███ - // ░███ █████ ░███░░░░░░ ░███ ░███ - // ░░███ ░░███ ░███ ░███ ░███ - // ░░█████████ █████ ░░████████ - // ░░░░░░░░░ ░░░░░ ░░░░░░░░ - // ███████████ ██████████ █████████ ██████████ - // ░░███░░░░░███ ░░███░░░░░█ ███░░░░░███ ░░███░░░░███ - // ░███ ░███ ░███ █ ░ ░███ ░███ ░███ ░░███ - // ░██████████ ░██████ ░███████████ ░███ ░███ - // ░███░░░░░███ ░███░░█ ░███░░░░░███ ░███ ░███ - // ░███ ░███ ░███ ░ █ ░███ ░███ ░███ ███ - // █████ █████ ██████████ █████ █████ ██████████ - // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ - // ███████████ █████ █████ ███████████ ███████████ ██████████ ███████████ - // ░░███░░░░░███░░███ ░░███ ░░███░░░░░░█░░███░░░░░░█░░███░░░░░█░░███░░░░░███ - // ░███ ░███ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ █ ░ ░███ ░███ - // ░██████████ ░███ ░███ ░███████ ░███████ ░██████ ░██████████ - // ░███░░░░░███ ░███ ░███ ░███░░░█ ░███░░░█ ░███░░█ ░███░░░░░███ - // ░███ ░███ ░███ ░███ ░███ ░ ░███ ░ ░███ ░ █ ░███ ░███ - // ███████████ ░░████████ █████ █████ ██████████ █████ █████ - // ░░░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ - //############################################################################## + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&data_handler.render_data.metadata).unwrap(); + pipeline + .render_queue + .write_buffer(&resources.metadata_buffer, 0, &buffer.into_inner()); - if pipeline.readable_metadata_buffer.is_none() { - pipeline.readable_metadata_buffer = - Some(render_device.create_buffer(&BufferDescriptor { - mapped_at_creation: false, - size: (data_handler.render_data.metadata.len() * 4) as u64, - label: Some("Octree Node metadata Buffer"), - usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, - })); - } + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer + .write(&data_handler.render_data.node_children) + .unwrap(); + pipeline.render_queue.write_buffer( + &resources.node_children_buffer, + 0, + &buffer.into_inner(), + ); - // +++ DEBUG +++ - if data_handler.readable_debug_gpu_interface.is_none() { - data_handler.readable_debug_gpu_interface = - Some(render_device.create_buffer(&BufferDescriptor { - mapped_at_creation: false, - size: 4, - label: Some("Octree Debug interface Buffer"), - usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, - })); - } - // --- DEBUG --- + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&data_handler.render_data.node_ocbits).unwrap(); + pipeline + .render_queue + .write_buffer(&resources.node_ocbits_buffer, 0, &buffer.into_inner()); - //############################################################################## - // ███████████ ██████████ ██████ █████ ██████████ ██████████ ███████████ - // ░░███░░░░░███ ░░███░░░░░█░░██████ ░░███ ░░███░░░░███ ░░███░░░░░█░░███░░░░░███ - // ░███ ░███ ░███ █ ░ ░███░███ ░███ ░███ ░░███ ░███ █ ░ ░███ ░███ - // ░██████████ ░██████ ░███░░███░███ ░███ ░███ ░██████ ░██████████ - // ░███░░░░░███ ░███░░█ ░███ ░░██████ ░███ ░███ ░███░░█ ░███░░░░░███ - // ░███ ░███ ░███ ░ █ ░███ ░░█████ ░███ ███ ░███ ░ █ ░███ ░███ - // █████ █████ ██████████ █████ ░░█████ ██████████ ██████████ █████ █████ - // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ - // ██████████ █████████ ███████████ █████████ - // ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███ - // ░███ ░░███ ░███ ░███ ░ ░███ ░ ░███ ░███ - // ░███ ░███ ░███████████ ░███ ░███████████ - // ░███ ░███ ░███░░░░░███ ░███ ░███░░░░░███ - // ░███ ███ ░███ ░███ ░███ ░███ ░███ - // ██████████ █████ █████ █████ █████ █████ - // ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ - // ███████████ █████ █████ ███████████ ███████████ ██████████ ███████████ - // ░░███░░░░░███░░███ ░░███ ░░███░░░░░░█░░███░░░░░░█░░███░░░░░█░░███░░░░░███ - // ░███ ░███ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ █ ░ ░███ ░███ - // ░██████████ ░███ ░███ ░███████ ░███████ ░██████ ░██████████ - // ░███░░░░░███ ░███ ░███ ░███░░░█ ░███░░░█ ░███░░█ ░███░░░░░███ - // ░███ ░███ ░███ ░███ ░███ ░ ░███ ░ ░███ ░ █ ░███ ░███ - // ███████████ ░░████████ █████ █████ ██████████ █████ █████ - // ░░░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ - //############################################################################## + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&data_handler.render_data.voxels).unwrap(); + pipeline + .render_queue + .write_buffer(&resources.voxels_buffer, 0, &buffer.into_inner()); + + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer + .write(&data_handler.render_data.color_palette) + .unwrap(); + pipeline + .render_queue + .write_buffer(&resources.color_palette_buffer, 0, &buffer.into_inner()) + } else { + // Create the staging buffer helping in reading data from the GPU + let readable_metadata_buffer = render_device.create_buffer(&BufferDescriptor { + mapped_at_creation: false, + size: (data_handler.render_data.metadata.len() * 4) as u64, + label: Some("Octree Node metadata Buffer"), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + }); - //================================================================= - // Implementation with WGPU - //================================================================= - // Upload data to buffers // +++ DEBUG +++ let buffer = UniformBuffer::new(vec![0, 0, 0, 0]); - if let Some(debug_buffer) = &data_handler.debug_gpu_interface { - pipeline - .render_queue - .write_buffer(debug_buffer, 0, &buffer.into_inner()) - } else { - let debug_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Debug Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, - }); - data_handler.debug_gpu_interface = Some(debug_buffer); - } + let debug_gpu_interface = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Debug Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, + }); + let readable_debug_gpu_interface = render_device.create_buffer(&BufferDescriptor { + mapped_at_creation: false, + size: 4, + label: Some("Octree Debug interface Buffer"), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + }); // --- DEBUG --- - let mut buffer = UniformBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.octree_meta).unwrap(); - if let Some(metadata_buffer) = &pipeline.octree_meta_buffer { - pipeline - .render_queue - .write_buffer(metadata_buffer, 0, &buffer.into_inner()) - } else { - let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Metadata Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - pipeline.octree_meta_buffer = Some(metadata_buffer); - } + let octree_meta_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Metadata Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); let mut buffer = StorageBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.metadata).unwrap(); - if let Some(nodes_buffer) = &pipeline.metadata_buffer { - pipeline - .render_queue - .write_buffer(nodes_buffer, 0, &buffer.into_inner()) - } else { - let nodes_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Nodes Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, - }); - pipeline.metadata_buffer = Some(nodes_buffer); - } + let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Nodes Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, + }); let mut buffer = StorageBuffer::new(Vec::::new()); buffer .write(&data_handler.render_data.node_children) .unwrap(); - if let Some(children_buffer) = &pipeline.node_children_buffer { - pipeline - .render_queue - .write_buffer(children_buffer, 0, &buffer.into_inner()) - } else { - let children_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Node Children Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.node_children_buffer = Some(children_buffer); - } + let node_children_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Node Children Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); let mut buffer = StorageBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.node_ocbits).unwrap(); - if let Some(ocbits_buffer) = &pipeline.node_ocbits_buffer { - pipeline - .render_queue - .write_buffer(ocbits_buffer, 0, &buffer.into_inner()) - } else { - let ocbits_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Node Occupied bits Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.node_ocbits_buffer = Some(ocbits_buffer); - } + let node_ocbits_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Node Occupied bits Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); let mut buffer = StorageBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.voxels).unwrap(); - if let Some(voxels_buffer) = &pipeline.voxels_buffer { - pipeline - .render_queue - .write_buffer(voxels_buffer, 0, &buffer.into_inner()) - } else { - let voxels_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Voxels Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.voxels_buffer = Some(voxels_buffer); - } + let voxels_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Voxels Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); let mut buffer = StorageBuffer::new(Vec::::new()); buffer .write(&data_handler.render_data.color_palette) .unwrap(); - if let Some(color_palette_buffer) = &pipeline.color_palette_buffer { - pipeline - .render_queue - .write_buffer(color_palette_buffer, 0, &buffer.into_inner()) - } else { - let color_palette_buffer = - render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Color Palette Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, - }); - pipeline.color_palette_buffer = Some(color_palette_buffer); - } + let color_palette_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Color Palette Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + }); - //############################################################################## - // ███████████ █████ ██████ █████ ██████████ - // ░░███░░░░░███░░███ ░░██████ ░░███ ░░███░░░░███ - // ░███ ░███ ░███ ░███░███ ░███ ░███ ░░███ - // ░██████████ ░███ ░███░░███░███ ░███ ░███ - // ░███░░░░░███ ░███ ░███ ░░██████ ░███ ░███ - // ░███ ░███ ░███ ░███ ░░█████ ░███ ███ - // ███████████ █████ █████ ░░█████ ██████████ - // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ - // █████████ ███████████ ███████ █████ █████ ███████████ - // ███░░░░░███░░███░░░░░███ ███░░░░░███ ░░███ ░░███ ░░███░░░░░███ - // ███ ░░░ ░███ ░███ ███ ░░███ ░███ ░███ ░███ ░███ - // ░███ ░██████████ ░███ ░███ ░███ ░███ ░██████████ - // ░███ █████ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███░░░░░░ - // ░░███ ░░███ ░███ ░███ ░░███ ███ ░███ ░███ ░███ - // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ - // ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░ ░░░░░ - //############################################################################## // Create bind group let tree_bind_group = render_device.create_bind_group( - SvxRenderData::label(), + OctreeRenderData::label(), &pipeline.render_data_bind_group_layout, &[ bevy::render::render_resource::BindGroupEntry { binding: 0, - resource: pipeline - .octree_meta_buffer - .as_ref() - .unwrap() - .as_entire_binding(), + resource: octree_meta_buffer.as_entire_binding(), }, bevy::render::render_resource::BindGroupEntry { binding: 1, - resource: pipeline - .metadata_buffer - .as_ref() - .unwrap() - .as_entire_binding(), + resource: metadata_buffer.as_entire_binding(), }, bevy::render::render_resource::BindGroupEntry { binding: 2, - resource: pipeline - .node_children_buffer - .as_ref() - .unwrap() - .as_entire_binding(), + resource: node_children_buffer.as_entire_binding(), }, bevy::render::render_resource::BindGroupEntry { binding: 3, - resource: pipeline - .node_ocbits_buffer - .as_ref() - .unwrap() - .as_entire_binding(), + resource: node_ocbits_buffer.as_entire_binding(), }, bevy::render::render_resource::BindGroupEntry { binding: 4, - resource: pipeline.voxels_buffer.as_ref().unwrap().as_entire_binding(), + resource: voxels_buffer.as_entire_binding(), }, bevy::render::render_resource::BindGroupEntry { binding: 5, - resource: pipeline - .color_palette_buffer - .as_ref() - .unwrap() - .as_entire_binding(), + resource: color_palette_buffer.as_entire_binding(), }, // +++ DEBUG +++ bevy::render::render_resource::BindGroupEntry { binding: 6, - resource: data_handler - .debug_gpu_interface - .as_ref() - .unwrap() - .as_entire_binding(), + resource: debug_gpu_interface.as_entire_binding(), }, // --- DEBUG --- ], ); - pipeline.tree_bind_group = Some(tree_bind_group); - //================================================================= - // Implementation with AsBindGroup - //================================================================= - // let tree_bind_group = render_data - // .as_bind_group( - // &pipeline.render_data_bind_group_layout, - // &render_device, - // &gpu_images, - // &fallback_image, - // ) - // .ok() - // .unwrap(); + let spyglass_prepared_bind_group = tree_gpu_view + .spyglass + .as_bind_group( + &pipeline.spyglass_bind_group_layout, + &render_device, + &gpu_images, + &fallback_image, + ) + .ok() + .unwrap(); + let spyglass_bind_group = spyglass_prepared_bind_group.bind_group; - // // println!("bindings: {:?}", tree_bind_group.bindings); - // // let bindings = ; - // //TODO: set buffers by binding index(bindings[x].0), instead of array index - // // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = - // // &tree_bind_group.bindings[2].1 - // // { - // // debug_assert_eq!(tree_bind_group.bindings[2].0, 2); - // // pipeline.nodes_children_buffer = Some(buf.clone()); - // // } - // if let bevy::render::render_resource::OwnedBindingResource::Buffer(buf) = - // &tree_bind_group.bindings[0].1 - // { - // // compare binding to ShocoVoxRenderData field - // debug_assert_eq!(tree_bind_group.bindings[0].0, 5); - // pipeline.cache_bytes_buffer = Some(buf.clone()); - // } + debug_assert_eq!( + 1, spyglass_prepared_bind_group.bindings[1].0, + "Expected Spyglass binding to be 1, instead of {:?}", + spyglass_prepared_bind_group.bindings[1].0 + ); + let viewport_buffer = if let OwnedBindingResource::Buffer(buffer) = + &spyglass_prepared_bind_group.bindings[1].1 + { + buffer.clone() + } else { + panic!( + "Unexpected binding for spyglass bind group; Expected buffer instead of : {:?}", + spyglass_prepared_bind_group.bindings[1].1 + ); + }; - // pipeline.tree_bind_group = Some(tree_bind_group.bind_group); - pipeline.update_tree = false; + pipeline.resources = Some(OctreeRenderDataResources { + spyglass_bind_group, + tree_bind_group, + viewport_buffer, + octree_meta_buffer, + metadata_buffer, + readable_metadata_buffer, + node_children_buffer, + node_ocbits_buffer, + voxels_buffer, + color_palette_buffer, + debug_gpu_interface, + readable_debug_gpu_interface, + }); } + + pipeline.update_tree = false; } diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index ffd1642..8099467 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -1,4 +1,4 @@ -use crate::octree::{Albedo, V3cf32}; +use crate::octree::{Albedo, Octree, V3cf32, VoxelData}; use bevy::{ asset::Handle, ecs::system::Resource, @@ -44,14 +44,22 @@ pub struct SvxRenderPlugin { pub resolution: [u32; 2], } +#[derive(Resource)] +pub struct OctreeGPUHost +where + T: Default + Clone + PartialEq + VoxelData, +{ + pub tree: Octree, + pub views: Vec>, +} + #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] #[type_path = "shocovox::gpu::OctreeGPUView"] pub struct OctreeGPUView { // +++ DEBUG +++ pub do_the_thing: bool, - pub read_back: u32, // --- DEBUG --- - pub viewing_glass: SvxViewingGlass, + pub spyglass: OctreeSpyGlass, pub(crate) data_handler: Arc>, } @@ -66,27 +74,19 @@ pub(crate) struct VictimPointer { #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] #[type_path = "shocovox::gpu::OctreeGPUDataHandler"] pub struct OctreeGPUDataHandler { - pub(crate) render_data: SvxRenderData, + // +++ DEBUG +++ + pub read_back: u32, + // --- DEBUG --- + pub(crate) render_data: OctreeRenderData, pub(crate) victim_node: VictimPointer, pub(crate) victim_brick: VictimPointer, pub(crate) map_to_node_index_in_metadata: HashMap, pub(crate) map_to_color_index_in_palette: HashMap, - pub(crate) debug_gpu_interface: Option, - pub(crate) readable_debug_gpu_interface: Option, - //TODO: Maybe this? - // Buffers for the RenderData - // pub(crate) octree_meta_buffer: Buffer, - // pub(crate) metadata_buffer: Buffer, - // pub(crate) node_children_buffer: Buffer, - // pub(crate) node_ocbits_buffer: Buffer, - // pub(crate) voxels_buffer: Buffer, - // pub(crate) color_palette_buffer: Buffer, - // pub(crate) readable_metadata_buffer: Buffer, } #[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxViewingGlass"] -pub struct SvxViewingGlass { +pub struct OctreeSpyGlass { #[storage_texture(0, image_format = Rgba8Unorm, access = ReadWrite)] pub output_texture: Handle, @@ -94,9 +94,26 @@ pub struct SvxViewingGlass { pub viewport: Viewport, } +#[derive(Clone)] +pub(crate) struct OctreeRenderDataResources { + pub(crate) spyglass_bind_group: BindGroup, + pub(crate) tree_bind_group: BindGroup, + + pub(crate) viewport_buffer: Buffer, + pub(crate) octree_meta_buffer: Buffer, + pub(crate) metadata_buffer: Buffer, + pub(crate) readable_metadata_buffer: Buffer, + pub(crate) node_children_buffer: Buffer, + pub(crate) node_ocbits_buffer: Buffer, + pub(crate) voxels_buffer: Buffer, + pub(crate) color_palette_buffer: Buffer, + pub(crate) debug_gpu_interface: Buffer, + pub(crate) readable_debug_gpu_interface: Buffer, +} + #[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxRenderData"] -pub struct SvxRenderData { +pub struct OctreeRenderData { // +++ DEBUG +++ #[storage(6, visibility(compute))] pub(crate) debug_gpu_interface: u32, @@ -176,20 +193,10 @@ pub(crate) struct SvxRenderPipeline { pub(crate) render_queue: RenderQueue, pub(crate) update_pipeline: CachedComputePipelineId, - // Render data buffers - pub(crate) octree_meta_buffer: Option, - pub(crate) metadata_buffer: Option, - pub(crate) readable_metadata_buffer: Option, - pub(crate) node_children_buffer: Option, - pub(crate) node_ocbits_buffer: Option, - pub(crate) voxels_buffer: Option, - pub(crate) color_palette_buffer: Option, - pub(crate) tree_data_handler: Option, - - pub(crate) viewing_glass_bind_group_layout: BindGroupLayout, + // Data layout and data + pub(crate) spyglass_bind_group_layout: BindGroupLayout, pub(crate) render_data_bind_group_layout: BindGroupLayout, - pub(crate) viewing_glass_bind_group: Option, - pub(crate) tree_bind_group: Option, + pub(crate) resources: Option, } #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] diff --git a/src/octree/raytracing/mod.rs b/src/octree/raytracing/mod.rs index 25d7501..7ff9d95 100644 --- a/src/octree/raytracing/mod.rs +++ b/src/octree/raytracing/mod.rs @@ -7,4 +7,6 @@ pub mod bevy; pub use crate::spatial::raytracing::Ray; #[cfg(feature = "bevy_wgpu")] -pub use bevy::types::{OctreeGPUView, SvxRenderData, SvxRenderPlugin, SvxViewingGlass, Viewport}; +pub use bevy::types::{ + OctreeGPUHost, OctreeGPUView, OctreeRenderData, OctreeSpyGlass, SvxRenderPlugin, Viewport, +}; diff --git a/src/octree/types.rs b/src/octree/types.rs index 6cbfbf8..83b9f45 100644 --- a/src/octree/types.rs +++ b/src/octree/types.rs @@ -88,9 +88,6 @@ where pub(crate) octree_size: u32, pub(crate) nodes: ObjectPool>, pub(crate) node_children: Vec>, // Children index values of each Node - - #[cfg(feature = "bevy_wgpu")] - pub(crate) views: Vec>, } #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] From 99f8ed36d7d3535905df230053592855e583ac9e Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 23 Nov 2024 21:07:18 +0100 Subject: [PATCH 14/19] Implemented GPU Node requests --- assets/shaders/viewport_render.wgsl | 169 ++++++++++----------- src/octree/raytracing/bevy/data.rs | 53 +++++-- src/octree/raytracing/bevy/pipeline.rs | 194 +++++++++++++++++-------- src/octree/raytracing/bevy/types.rs | 35 +++-- 4 files changed, 273 insertions(+), 178 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index a25dbd1..2242381 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -275,8 +275,27 @@ fn set_brick_used(brick_index: u32) { } } -fn request_node(node: u32, child_octant: u32){ - +// Unique to this implementation, not adapted from rust code +/// Requests the child of the given node to be uploaded +fn request_node(node_meta_index: u32, child_octant: u32) -> bool { + let request_data = ( + (node_meta_index & 0x00FFFFFFu) + | ((child_octant & 0x000000FF) << 24) + ); + var request_index = 0u; + loop{ + let exchange_result = atomicCompareExchangeWeak( + &node_requests[request_index], 0u, request_data + ); + if(exchange_result.exchanged || exchange_result.old_value == request_data) { + break; + } + request_index += 1u; + if(request_index >= arrayLength(&node_requests)) { + return false; + } + } + return true; } //crate::spatial::math::step_octant @@ -399,18 +418,7 @@ fn probe_brick( ) -> OctreeRayIntersection { if(0 != ((0x01u << (8 + brick_octant)) & metadata[leaf_node_key])) { // brick is not empty let brick_index = node_children[((leaf_node_key * 8) + brick_octant)]; - - // Brick not available in memory - if EMPTY_MARKER == brick_index { - return OctreeRayIntersection(true, vec4f(0.5,0.6,0.2,1.), 0, vec3f(0.), vec3f(0., 0., 1.)); - } - - // +++ DEBUG +++ - //set_brick_used(brick_index); - if(0 == debug_interface){ - set_brick_used(brick_index); - } - // --- DEBUG --- + set_brick_used(brick_index); if(0 == ((0x01u << (16 + brick_octant)) & metadata[leaf_node_key])) { // brick is solid // Whole brick is solid, ray hits it at first connection return OctreeRayIntersection( @@ -427,51 +435,7 @@ fn probe_brick( brick_bounds, ray_scale_factors, direction_lut_index ); if leaf_brick_hit.hit == true { - - /// +++ DEBUG +++ - /*// Color in bricks displayed - var result_rgb = color_palette[voxels[leaf_brick_hit.flat_index].albedo_index]; - if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) { - result_rgb.r += 0.2; - } return OctreeRayIntersection( - true, - result_rgb, - voxels[leaf_brick_hit.flat_index].content, - point_in_ray_at_distance(ray, *ray_current_distance), - cube_impact_normal( - Cube( - (*brick_bounds).min_position + ( - vec3f(leaf_brick_hit.index) - * round((*brick_bounds).size / f32(octree_meta_data.voxel_brick_dim)) - ), - round((*brick_bounds).size / f32(octree_meta_data.voxel_brick_dim)), - ), - point_in_ray_at_distance(ray, *ray_current_distance) - ) - );*/ - - // Display marked bricks only - if 0 != ( metadata[brick_index / 8] & (0x01u << (24u + (brick_index % 8u))) ) { - return OctreeRayIntersection( - true, - color_palette[voxels[leaf_brick_hit.flat_index].albedo_index], - voxels[leaf_brick_hit.flat_index].content, - point_in_ray_at_distance(ray, *ray_current_distance), - cube_impact_normal( - Cube( - (*brick_bounds).min_position + ( - vec3f(leaf_brick_hit.index) - * round((*brick_bounds).size / f32(octree_meta_data.voxel_brick_dim)) - ), - round((*brick_bounds).size / f32(octree_meta_data.voxel_brick_dim)), - ), - point_in_ray_at_distance(ray, *ray_current_distance) - ) - ); - } - - /*return OctreeRayIntersection( true, color_palette[voxels[leaf_brick_hit.flat_index].albedo_index], voxels[leaf_brick_hit.flat_index].content, @@ -487,7 +451,6 @@ fn probe_brick( point_in_ray_at_distance(ray, *ray_current_distance) ) ); - *//// --- DEBUG --- } } } @@ -495,13 +458,16 @@ fn probe_brick( return OctreeRayIntersection(false, vec4f(0.), 0, vec3f(0.), vec3f(0., 0., 1.)); } +// Unique to this implementation, not adapted from rust code +/// Traverses the node to provide information about how the occupied bits of the node +/// and the given ray collides. The higher the number, the closer the hit is. fn traverse_node_for_ocbits( ray: ptr, ray_current_distance: ptr, node_key: u32, node_bounds: ptr, ray_scale_factors: ptr, -) -> vec3f { +) -> f32 { let original_distance = *ray_current_distance; var position = vec3f( @@ -524,7 +490,7 @@ fn traverse_node_for_ocbits( ); var safety = 0u; - var rgb_result = vec3f(0.); + var result = 0.; loop { if safety > 10 || current_index.x < 0 || current_index.x >= 4 || current_index.y < 0 || current_index.y >= 4 @@ -539,22 +505,15 @@ fn traverse_node_for_ocbits( if ( ( (bitmap_index < 32) - && ( - 0u != (node_occupied_bits[node_key * 2] + && (0u != (node_occupied_bits[node_key * 2] & (0x01u << bitmap_index) )) )||( (bitmap_index >= 32) - && ( - 0u != (node_occupied_bits[node_key * 2 + 1] - & (0x01u << (bitmap_index - 32)) - )) + && (0u != (node_occupied_bits[node_key * 2 + 1] + & (0x01u << (bitmap_index - 32)) )) ) ){ - rgb_result = vec3f( - 1. - (f32(safety) * 0.25), - 1. - (f32(safety) * 0.25), - 1. - (f32(safety) * 0.25) - ); + result = 1. - (f32(safety) * 0.25); break; } @@ -570,7 +529,7 @@ fn traverse_node_for_ocbits( } *ray_current_distance = original_distance; - return rgb_result; + return result; } fn get_by_ray(ray: ptr) -> OctreeRayIntersection { @@ -638,7 +597,7 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { ); if( - // In case node doesn't yet have the relevant child node uploaded to GPU + // In case node doesn't yet have the target child node uploaded to GPU (0 == (0x00000004 & current_node_meta)) // node is not a leaf && target_octant != OOB_OCTANT && target_child_key == EMPTY_MARKER // target child key is invalid @@ -653,16 +612,29 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { ) ) ){ - missing_data_color += ( - vec3f(0.5,0.3,0.0) * - traverse_node_for_ocbits( - ray, - &ray_current_distance, - current_node_key, - ¤t_bounds, - &ray_scale_factors - ) - ); + if request_node(current_node_key, target_octant) { + missing_data_color += ( + vec3f(0.5,0.3,0.0) * + vec3f(traverse_node_for_ocbits( + ray, + &ray_current_distance, + current_node_key, + ¤t_bounds, + &ray_scale_factors + )) + ); + } else { + missing_data_color += ( + vec3f(0.7,0.2,0.0) * + vec3f(traverse_node_for_ocbits( + ray, + &ray_current_distance, + current_node_key, + ¤t_bounds, + &ray_scale_factors + )) + ); + } } if (target_octant != OOB_OCTANT) { @@ -676,13 +648,25 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { ); do_backtrack_after_leaf_miss = true; } else { // node is a non-uniform leaf - target_bounds = child_bounds_for(¤t_bounds, target_octant); - hit = probe_brick( - ray, &ray_current_distance, - current_node_key, target_octant, - &target_bounds, - &ray_scale_factors, direction_lut_index - ); + if // node not empty at target octant, while the brick is marked unavailable + (0 != ((0x01u << (8 + target_octant)) & current_node_meta)) + && EMPTY_MARKER == node_children[(current_node_key * 8) + target_octant] + { + // child brick is not yet uploaded to GPU + if request_node(current_node_key, target_octant) { + missing_data_color += vec3f(0.3,0.1,0.0); + } else { + missing_data_color += vec3f(0.7,0.0,0.0); + } + } else { + target_bounds = child_bounds_for(¤t_bounds, target_octant); + hit = probe_brick( + ray, &ray_current_distance, + current_node_key, target_octant, + &target_bounds, + &ray_scale_factors, direction_lut_index + ); + } } if hit.hit == true { hit.albedo += vec4f(missing_data_color, 0.); @@ -893,6 +877,9 @@ var output_texture: texture_storage_2d; @group(0) @binding(1) var viewport: Viewport; +@group(0) @binding(2) +var node_requests: array>; + @group(1) @binding(0) var octree_meta_data: OctreeMetaData; diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 5d10ac9..1be257d 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -133,6 +133,7 @@ where do_the_thing: false, data_handler: Arc::new(Mutex::new(gpu_data_handler)), spyglass: OctreeSpyGlass { + node_requests: vec![0; 4], output_texture: output_texture.clone(), viewport: viewport, }, @@ -175,23 +176,49 @@ pub(crate) fn sync_with_main_world(// tree_view: Option>, // █████ █████ ██████████ █████ █████ ██████████ // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ //############################################################################## +/// Handles data reads from GPU every loop. +/// Based on https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html pub(crate) fn handle_gpu_readback( render_device: Res, tree_gpu_view: Option>, svx_pipeline: Option>, ) { - // Data updates triggered by debug interface if let (Some(ref mut tree_gpu_view), Some(ref mut pipeline)) = (tree_gpu_view, svx_pipeline) { + let resources = pipeline.resources.as_ref().unwrap(); + let mut data_handler = tree_gpu_view.data_handler.lock().unwrap(); + + // Read node requests from GPU + let buffer_slice = resources.readable_node_requests_buffer.slice(..); + let (s, r) = crossbeam::channel::unbounded::<()>(); + buffer_slice.map_async( + bevy::render::render_resource::MapMode::Read, + move |d| match d { + Ok(_) => s.send(()).expect("Failed to send map update"), + Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), + }, + ); + + render_device + .poll(bevy::render::render_resource::Maintain::wait()) + .panic_on_timeout(); + + r.recv().expect("Failed to receive the map_async message"); + { + let buffer_view = buffer_slice.get_mapped_range(); + + let data = buffer_view + .chunks(std::mem::size_of::()) + .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + .collect::>(); + // println!("node_requests: {:?}", data); + } + resources.readable_node_requests_buffer.unmap(); + + // +++ DEBUG +++ + // Data updates triggered by debug interface if tree_gpu_view.do_the_thing { - let mut data_handler = tree_gpu_view.data_handler.lock().unwrap(); - // GPU buffer read // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html - let buffer_slice = pipeline - .resources - .as_ref() - .unwrap() - .readable_debug_gpu_interface - .slice(..); + let buffer_slice = resources.readable_debug_gpu_interface.slice(..); let (s, r) = crossbeam::channel::unbounded::<()>(); buffer_slice.map_async( bevy::render::render_resource::MapMode::Read, @@ -216,15 +243,11 @@ pub(crate) fn handle_gpu_readback( data_handler.read_back = data[0]; // println!("received_data: {:?}", data); } - pipeline - .resources - .as_ref() - .unwrap() - .readable_debug_gpu_interface - .unmap(); + resources.readable_debug_gpu_interface.unmap(); std::mem::drop(data_handler); tree_gpu_view.do_the_thing = false; } + // --- DEBUG --- } } diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index a00dfad..c2f6076 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -1,5 +1,5 @@ use crate::octree::raytracing::bevy::types::{ - OctreeGPUView, OctreeRenderData, OctreeSpyGlass, SvxRenderNode, SvxRenderPipeline, + OctreeGPUView, OctreeRenderData, OctreeSpyGlass, SvxRenderNode, SvxRenderPipeline, Viewport, }; use bevy::{ @@ -13,11 +13,12 @@ use bevy::{ render_graph::{self}, render_resource::{ encase::{StorageBuffer, UniformBuffer}, - AsBindGroup, BufferDescriptor, BufferInitDescriptor, BufferUsages, CachedPipelineState, - ComputePassDescriptor, ComputePipelineDescriptor, OwnedBindingResource, PipelineCache, + AsBindGroup, BindGroupEntry, BindingResource, BufferDescriptor, BufferInitDescriptor, + BufferUsages, CachedPipelineState, ComputePassDescriptor, ComputePipelineDescriptor, + PipelineCache, ShaderSize, }, renderer::{RenderContext, RenderDevice, RenderQueue}, - texture::{FallbackImage, GpuImage}, + texture::GpuImage, }, }; use std::borrow::Cow; @@ -91,23 +92,22 @@ impl render_graph::Node for SvxRenderNode { ) -> Result<(), render_graph::NodeRunError> { let pipeline_cache = world.resource::(); let svx_pipeline = world.resource::(); + let tree_gpu_view = world.resource::(); let command_encoder = render_context.command_encoder(); if self.ready { + let data_handler = world + .resource::() + .data_handler + .lock() + .unwrap(); + let resources = svx_pipeline.resources.as_ref().unwrap(); { let mut pass = command_encoder.begin_compute_pass(&ComputePassDescriptor::default()); - pass.set_bind_group( - 0, - &svx_pipeline.resources.as_ref().unwrap().spyglass_bind_group, - &[], - ); - pass.set_bind_group( - 1, - &svx_pipeline.resources.as_ref().unwrap().tree_bind_group, - &[], - ); + pass.set_bind_group(0, &resources.spyglass_bind_group, &[]); + pass.set_bind_group(1, &resources.tree_bind_group, &[]); let pipeline = pipeline_cache .get_compute_pipeline(svx_pipeline.update_pipeline) .unwrap(); @@ -120,25 +120,32 @@ impl render_graph::Node for SvxRenderNode { } command_encoder.copy_buffer_to_buffer( - &svx_pipeline.resources.as_ref().unwrap().metadata_buffer, + &resources.metadata_buffer, 0, - &svx_pipeline - .resources - .as_ref() - .unwrap() - .readable_metadata_buffer, + &resources.readable_metadata_buffer, 0, - std::mem::size_of::() as u64, + (std::mem::size_of_val(&data_handler.render_data.metadata[0]) + * data_handler.render_data.metadata.len()) as u64, + ); + + debug_assert!( + !tree_gpu_view.spyglass.node_requests.is_empty(), + "Expected node requests array to not be empty" + ); + command_encoder.copy_buffer_to_buffer( + &resources.node_requests_buffer, + 0, + &resources.readable_node_requests_buffer, + 0, + (std::mem::size_of_val(&tree_gpu_view.spyglass.node_requests[0]) + * tree_gpu_view.spyglass.node_requests.len()) as u64, ); + // +++ DEBUG +++ command_encoder.copy_buffer_to_buffer( - &svx_pipeline.resources.as_ref().unwrap().debug_gpu_interface, + &resources.debug_gpu_interface, 0, - &svx_pipeline - .resources - .as_ref() - .unwrap() - .readable_debug_gpu_interface, + &resources.readable_debug_gpu_interface, 0, std::mem::size_of::() as u64, ) @@ -176,7 +183,6 @@ impl render_graph::Node for SvxRenderNode { //############################################################################## pub(crate) fn prepare_bind_groups( gpu_images: Res>, - fallback_image: Res, render_device: Res, mut pipeline: ResMut, tree_gpu_view: ResMut, @@ -229,16 +235,34 @@ pub(crate) fn prepare_bind_groups( .render_queue .write_buffer(&resources.color_palette_buffer, 0, &buffer.into_inner()) } else { + //############################################################################## + // ███████████ ███████████ ██████████ ██████████ + // ░█░░░███░░░█░░███░░░░░███ ░░███░░░░░█░░███░░░░░█ + // ░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ + // ░███ ░██████████ ░██████ ░██████ + // ░███ ░███░░░░░███ ░███░░█ ░███░░█ + // ░███ ░███ ░███ ░███ ░ █ ░███ ░ █ + // █████ █████ █████ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // █████████ ███████████ ███████ █████ █████ ███████████ + // ███░░░░░███░░███░░░░░███ ███░░░░░███ ░░███ ░░███ ░░███░░░░░███ + // ███ ░░░ ░███ ░███ ███ ░░███ ░███ ░███ ░███ ░███ + // ░███ ░██████████ ░███ ░███ ░███ ░███ ░██████████ + // ░███ █████ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███░░░░░░ + // ░░███ ░░███ ░███ ░███ ░░███ ███ ░███ ░███ ░███ + // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ + // ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░ ░░░░░ + //############################################################################## // Create the staging buffer helping in reading data from the GPU let readable_metadata_buffer = render_device.create_buffer(&BufferDescriptor { mapped_at_creation: false, size: (data_handler.render_data.metadata.len() * 4) as u64, - label: Some("Octree Node metadata Buffer"), + label: Some("Octree Node metadata staging Buffer"), usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, }); // +++ DEBUG +++ - let buffer = UniformBuffer::new(vec![0, 0, 0, 0]); + let buffer = UniformBuffer::new(vec![0u8; 4]); let debug_gpu_interface = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Debug Buffer"), contents: &buffer.into_inner(), @@ -254,7 +278,7 @@ pub(crate) fn prepare_bind_groups( let mut buffer = UniformBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.octree_meta).unwrap(); let octree_meta_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Metadata Buffer"), + label: Some("Octree Tree Metadata Buffer"), contents: &buffer.into_inner(), usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, }); @@ -262,7 +286,7 @@ pub(crate) fn prepare_bind_groups( let mut buffer = StorageBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.metadata).unwrap(); let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Nodes Buffer"), + label: Some("Octree Metadata Buffer"), contents: &buffer.into_inner(), usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, }); @@ -280,7 +304,7 @@ pub(crate) fn prepare_bind_groups( let mut buffer = StorageBuffer::new(Vec::::new()); buffer.write(&data_handler.render_data.node_ocbits).unwrap(); let node_ocbits_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Node Occupied bits Buffer"), + label: Some("Octree Node Occupied Bits Buffer"), contents: &buffer.into_inner(), usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, }); @@ -341,47 +365,99 @@ pub(crate) fn prepare_bind_groups( ], ); - let spyglass_prepared_bind_group = tree_gpu_view - .spyglass - .as_bind_group( - &pipeline.spyglass_bind_group_layout, - &render_device, - &gpu_images, - &fallback_image, - ) - .ok() - .unwrap(); - let spyglass_bind_group = spyglass_prepared_bind_group.bind_group; + //############################################################################## + // █████████ ███████████ █████ █████ + // ███░░░░░███░░███░░░░░███░░███ ░░███ + // ░███ ░░░ ░███ ░███ ░░███ ███ + // ░░█████████ ░██████████ ░░█████ + // ░░░░░░░░███ ░███░░░░░░ ░░███ + // ███ ░███ ░███ ░███ + // ░░█████████ █████ █████ + // ░░░░░░░░░ ░░░░░ ░░░░░ + // █████████ █████ █████████ █████████ █████████ + // ███░░░░░███░░███ ███░░░░░███ ███░░░░░███ ███░░░░░███ + // ███ ░░░ ░███ ░███ ░███ ░███ ░░░ ░███ ░░░ + // ░███ ░███ ░███████████ ░░█████████ ░░█████████ + // ░███ █████ ░███ ░███░░░░░███ ░░░░░░░░███ ░░░░░░░░███ + // ░░███ ░░███ ░███ █ ░███ ░███ ███ ░███ ███ ░███ + // ░░█████████ ███████████ █████ █████░░█████████ ░░█████████ + // ░░░░░░░░░ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░ + // █████████ ███████████ ███████ █████ █████ ███████████ + // ███░░░░░███░░███░░░░░███ ███░░░░░███ ░░███ ░░███ ░░███░░░░░███ + // ███ ░░░ ░███ ░███ ███ ░░███ ░███ ░███ ░███ ░███ + // ░███ ░██████████ ░███ ░███ ░███ ░███ ░██████████ + // ░███ █████ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███░░░░░░ + // ░░███ ░░███ ░███ ░███ ░░███ ███ ░███ ░███ ░███ + // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ + //############################################################################## + let mut buffer = UniformBuffer::new([0u8; Viewport::SHADER_SIZE.get() as usize]); + buffer.write(&tree_gpu_view.spyglass.viewport).unwrap(); + let viewport_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Viewport Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); - debug_assert_eq!( - 1, spyglass_prepared_bind_group.bindings[1].0, - "Expected Spyglass binding to be 1, instead of {:?}", - spyglass_prepared_bind_group.bindings[1].0 + debug_assert!( + !tree_gpu_view.spyglass.node_requests.is_empty(), + "Expected node requests array to not be empty" + ); + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&tree_gpu_view.spyglass.node_requests).unwrap(); + let node_requests_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Octree Node requests Buffer"), + contents: &buffer.into_inner(), + usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, + }); + + let readable_node_requests_buffer = render_device.create_buffer(&BufferDescriptor { + mapped_at_creation: false, + size: (tree_gpu_view.spyglass.node_requests.len() + * std::mem::size_of_val(&tree_gpu_view.spyglass.node_requests[0])) + as u64, + label: Some("Octree Node requests staging Buffer"), + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + }); + + let output_texture_view = gpu_images + .get(&tree_gpu_view.spyglass.output_texture) + .unwrap() + .texture_view + .clone(); + let spyglass_bind_group = render_device.create_bind_group( + OctreeSpyGlass::label(), + &pipeline.spyglass_bind_group_layout, + &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView(&output_texture_view.clone()), + }, + BindGroupEntry { + binding: 1, + resource: viewport_buffer.as_entire_binding(), + }, + BindGroupEntry { + binding: 2, + resource: node_requests_buffer.as_entire_binding(), + }, + ], ); - let viewport_buffer = if let OwnedBindingResource::Buffer(buffer) = - &spyglass_prepared_bind_group.bindings[1].1 - { - buffer.clone() - } else { - panic!( - "Unexpected binding for spyglass bind group; Expected buffer instead of : {:?}", - spyglass_prepared_bind_group.bindings[1].1 - ); - }; pipeline.resources = Some(OctreeRenderDataResources { + node_requests_buffer, spyglass_bind_group, tree_bind_group, viewport_buffer, octree_meta_buffer, metadata_buffer, - readable_metadata_buffer, node_children_buffer, node_ocbits_buffer, voxels_buffer, color_palette_buffer, debug_gpu_interface, + readable_node_requests_buffer, readable_debug_gpu_interface, + readable_metadata_buffer, }); } diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 8099467..eca7253 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -84,33 +84,42 @@ pub struct OctreeGPUDataHandler { pub(crate) map_to_color_index_in_palette: HashMap, } -#[derive(Clone, AsBindGroup, TypePath)] -#[type_path = "shocovox::gpu::ShocoVoxViewingGlass"] -pub struct OctreeSpyGlass { - #[storage_texture(0, image_format = Rgba8Unorm, access = ReadWrite)] - pub output_texture: Handle, - - #[uniform(1, visibility(compute))] - pub viewport: Viewport, -} - #[derive(Clone)] pub(crate) struct OctreeRenderDataResources { + // Spyglass group pub(crate) spyglass_bind_group: BindGroup, - pub(crate) tree_bind_group: BindGroup, - pub(crate) viewport_buffer: Buffer, + pub(crate) node_requests_buffer: Buffer, + + // Octree render data group + pub(crate) tree_bind_group: BindGroup, pub(crate) octree_meta_buffer: Buffer, pub(crate) metadata_buffer: Buffer, - pub(crate) readable_metadata_buffer: Buffer, pub(crate) node_children_buffer: Buffer, pub(crate) node_ocbits_buffer: Buffer, pub(crate) voxels_buffer: Buffer, pub(crate) color_palette_buffer: Buffer, pub(crate) debug_gpu_interface: Buffer, + + // Staging buffers for data reads + pub(crate) readable_node_requests_buffer: Buffer, + pub(crate) readable_metadata_buffer: Buffer, pub(crate) readable_debug_gpu_interface: Buffer, } +#[derive(Clone, AsBindGroup, TypePath)] +#[type_path = "shocovox::gpu::ShocoVoxViewingGlass"] +pub struct OctreeSpyGlass { + #[storage_texture(0, image_format = Rgba8Unorm, access = ReadWrite)] + pub output_texture: Handle, + + #[uniform(1, visibility(compute))] + pub viewport: Viewport, + + #[storage(2, visibility(compute))] + pub(crate) node_requests: Vec, +} + #[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxRenderData"] pub struct OctreeRenderData { From bef8335c52f21489c44ca75ad965eef802fa2802 Mon Sep 17 00:00:00 2001 From: davids91 Date: Sun, 24 Nov 2024 01:34:03 +0100 Subject: [PATCH 15/19] Refactor to establish view and octree connection --- examples/dot_cube.rs | 16 ++-- examples/minecraft.rs | 25 ++--- src/octree/raytracing/bevy/cache.rs | 8 +- src/octree/raytracing/bevy/data.rs | 126 ++++++++++++++----------- src/octree/raytracing/bevy/mod.rs | 32 ++++--- src/octree/raytracing/bevy/pipeline.rs | 91 +++++++++--------- src/octree/raytracing/bevy/types.rs | 23 +++-- src/octree/raytracing/mod.rs | 3 +- src/octree/types.rs | 1 + 9 files changed, 180 insertions(+), 145 deletions(-) diff --git a/examples/dot_cube.rs b/examples/dot_cube.rs index b869550..e389a1e 100644 --- a/examples/dot_cube.rs +++ b/examples/dot_cube.rs @@ -9,7 +9,7 @@ use iyes_perf_ui::{ #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, + raytracing::{OctreeGPUHost, OctreeGPUView, Ray, Viewport}, Albedo, Octree, V3c, VoxelData, }; @@ -28,9 +28,9 @@ fn main() { .insert_resource(ClearColor(Color::BLACK)) .add_plugins(( DefaultPlugins.set(WindowPlugin::default()), - SvxRenderPlugin { - resolution: DISPLAY_RESOLUTION, - }, + shocovox_rs::octree::raytracing::RenderBevyPlugin::::new( + DISPLAY_RESOLUTION, + ), bevy::diagnostic::FrameTimeDiagnosticsPlugin, PerfUiPlugin, )) @@ -98,19 +98,21 @@ fn setup(mut commands: Commands, images: ResMut>) { radius: tree.get_size() as f32 * 2.2, }); - let host = OctreeGPUHost::new(tree); + let mut host = OctreeGPUHost { tree }; + let mut views = SvxViewSet::default(); let output_texture = host.create_new_view( - 40, //TODO: decide actual number + &mut views, + 40, Viewport { origin, direction: (V3c::new(0., 0., 0.) - origin).normalized(), w_h_fov: V3c::new(10., 10., 3.), }, DISPLAY_RESOLUTION, - &mut commands, images, ); commands.insert_resource(host); + commands.insert_resource(views); commands.spawn(SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(1024., 768.)), diff --git a/examples/minecraft.rs b/examples/minecraft.rs index 3685a4f..ca3e57e 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -3,8 +3,8 @@ use bevy::{prelude::*, window::WindowPlugin}; #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxRenderPlugin, Viewport}, - Albedo, Octree, V3c, VoxelData, + raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxViewSet, Viewport}, + Albedo, Octree, V3c, }; #[cfg(feature = "bevy_wgpu")] @@ -33,9 +33,9 @@ fn main() { }), ..default() }), - SvxRenderPlugin { - resolution: DISPLAY_RESOLUTION, - }, + shocovox_rs::octree::raytracing::RenderBevyPlugin::::new( + DISPLAY_RESOLUTION, + ), bevy::diagnostic::FrameTimeDiagnosticsPlugin, PerfUiPlugin, )) @@ -70,8 +70,10 @@ fn setup(mut commands: Commands, images: ResMut>) { radius: tree.get_size() as f32 * 2.2, }); - let host = OctreeGPUHost::new(tree); + let mut host = OctreeGPUHost { tree }; + let mut views = SvxViewSet::default(); let output_texture = host.create_new_view( + &mut views, 20, Viewport { origin: V3c { @@ -87,10 +89,10 @@ fn setup(mut commands: Commands, images: ResMut>) { w_h_fov: V3c::new(10., 10., 3.), }, DISPLAY_RESOLUTION, - &mut commands, images, ); commands.insert_resource(host); + commands.insert_resource(views); commands.spawn(SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(1024., 768.)), @@ -128,10 +130,10 @@ struct DomePosition { } #[cfg(feature = "bevy_wgpu")] -fn rotate_camera(angles_query: Query<&mut DomePosition>, mut tree_view: ResMut) { +fn rotate_camera(angles_query: Query<&mut DomePosition>, view_set: ResMut) { let (yaw, roll) = (angles_query.single().yaw, angles_query.single().roll); let radius = angles_query.single().radius; - + let mut tree_view = view_set.views[0].lock().unwrap(); tree_view.spyglass.viewport.origin = V3c::new( radius / 2. + yaw.sin() * radius, radius + roll.sin() * radius * 2., @@ -144,10 +146,11 @@ fn rotate_camera(angles_query: Query<&mut DomePosition>, mut tree_view: ResMut>, - mut tree_view: ResMut, + tree: ResMut>, + view_set: ResMut, mut angles_query: Query<&mut DomePosition>, - tree: Res>, ) { + let mut tree_view = view_set.views[0].lock().unwrap(); if keys.pressed(KeyCode::Delete) { tree_view.do_the_thing = true; } diff --git a/src/octree/raytracing/bevy/cache.rs b/src/octree/raytracing/bevy/cache.rs index 1a6f81f..db47e51 100644 --- a/src/octree/raytracing/bevy/cache.rs +++ b/src/octree/raytracing/bevy/cache.rs @@ -288,17 +288,20 @@ impl OctreeGPUDataHandler { // █████ ░░█████ ░░░███████░ ██████████ ██████████ // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ //############################################################################## + /// Writes the data of the node to the first available index, and returns the index of the overwritten data + /// It may fail to add the node, when @try_add_children is set to true pub(crate) fn add_node( &mut self, tree: &Octree, node_key: usize, try_add_children: bool, - ) where + ) -> Option + where T: Default + Copy + Clone + PartialEq + VoxelData, { if try_add_children && self.victim_node.is_full() { // Do not add additional nodes at initial upload if the cache is already full - return; + return None; } // Determine the index in meta @@ -408,6 +411,7 @@ impl OctreeGPUDataHandler { } } } + Some(node_element_index) } //############################################################################## diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 1be257d..e08af22 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -2,7 +2,7 @@ use crate::object_pool::empty_marker; use crate::octree::{ raytracing::bevy::types::{ OctreeGPUDataHandler, OctreeGPUHost, OctreeGPUView, OctreeMetaData, OctreeRenderData, - OctreeSpyGlass, SvxRenderPipeline, VictimPointer, Viewport, Voxelement, + OctreeSpyGlass, SvxRenderPipeline, SvxViewSet, VictimPointer, Viewport, Voxelement, }, Octree, V3c, VoxelData, }; @@ -26,7 +26,7 @@ use std::{ impl OctreeGPUHost where - T: Default + Clone + Copy + PartialEq + VoxelData, + T: Default + Clone + Copy + PartialEq + VoxelData + Send + Sync + 'static, { //############################################################################## // ███████ █████████ ███████████ ███████████ ██████████ ██████████ @@ -56,15 +56,14 @@ where /// Creates GPU compatible data renderable on the GPU from an octree pub fn create_new_view( - &self, + &mut self, + svx_view_set: &mut SvxViewSet, size: usize, viewport: Viewport, resolution: [u32; 2], - commands: &mut Commands, mut images: ResMut>, ) -> Handle { let mut gpu_data_handler = OctreeGPUDataHandler { - read_back: 0, render_data: OctreeRenderData { debug_gpu_interface: 0, octree_meta: OctreeMetaData { @@ -129,15 +128,15 @@ where | TextureUsages::TEXTURE_BINDING; let output_texture = images.add(output_texture); - commands.insert_resource(OctreeGPUView { + svx_view_set.views.push(Arc::new(Mutex::new(OctreeGPUView { do_the_thing: false, - data_handler: Arc::new(Mutex::new(gpu_data_handler)), + data_handler: gpu_data_handler, spyglass: OctreeSpyGlass { node_requests: vec![0; 4], output_texture: output_texture.clone(), viewport: viewport, }, - }); + }))); output_texture } } @@ -178,14 +177,16 @@ pub(crate) fn sync_with_main_world(// tree_view: Option>, //############################################################################## /// Handles data reads from GPU every loop. /// Based on https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html -pub(crate) fn handle_gpu_readback( +pub(crate) fn handle_gpu_readback( render_device: Res, - tree_gpu_view: Option>, + tree_gpu_host: Option>>, + svx_view_set: ResMut, svx_pipeline: Option>, -) { - if let (Some(ref mut tree_gpu_view), Some(ref mut pipeline)) = (tree_gpu_view, svx_pipeline) { +) where + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, +{ + if let (Some(ref mut tree_gpu_host), Some(ref mut pipeline)) = (tree_gpu_host, svx_pipeline) { let resources = pipeline.resources.as_ref().unwrap(); - let mut data_handler = tree_gpu_view.data_handler.lock().unwrap(); // Read node requests from GPU let buffer_slice = resources.readable_node_requests_buffer.slice(..); @@ -206,47 +207,45 @@ pub(crate) fn handle_gpu_readback( { let buffer_view = buffer_slice.get_mapped_range(); - let data = buffer_view + svx_view_set.views[0].lock().unwrap().spyglass.node_requests = buffer_view .chunks(std::mem::size_of::()) .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) .collect::>(); - // println!("node_requests: {:?}", data); } resources.readable_node_requests_buffer.unmap(); // +++ DEBUG +++ // Data updates triggered by debug interface - if tree_gpu_view.do_the_thing { - // https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html - let buffer_slice = resources.readable_debug_gpu_interface.slice(..); - let (s, r) = crossbeam::channel::unbounded::<()>(); - buffer_slice.map_async( - bevy::render::render_resource::MapMode::Read, - move |d| match d { - Ok(_) => s.send(()).expect("Failed to send map update"), - Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), - }, - ); + // let data_handler = tree_gpu_view.data_handler.lock().unwrap(); + // if tree_gpu_view.do_the_thing { + // let buffer_slice = resources.readable_debug_gpu_interface.slice(..); + // let (s, r) = crossbeam::channel::unbounded::<()>(); + // buffer_slice.map_async( + // bevy::render::render_resource::MapMode::Read, + // move |d| match d { + // Ok(_) => s.send(()).expect("Failed to send map update"), + // Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), + // }, + // ); - render_device - .poll(bevy::render::render_resource::Maintain::wait()) - .panic_on_timeout(); + // render_device + // .poll(bevy::render::render_resource::Maintain::wait()) + // .panic_on_timeout(); - r.recv().expect("Failed to receive the map_async message"); - { - let buffer_view = buffer_slice.get_mapped_range(); + // r.recv().expect("Failed to receive the map_async message"); + // { + // let buffer_view = buffer_slice.get_mapped_range(); - let data = buffer_view - .chunks(std::mem::size_of::()) - .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) - .collect::>(); - data_handler.read_back = data[0]; - // println!("received_data: {:?}", data); - } - resources.readable_debug_gpu_interface.unmap(); - std::mem::drop(data_handler); - tree_gpu_view.do_the_thing = false; - } + // let data = buffer_view + // .chunks(std::mem::size_of::()) + // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + // .collect::>(); + // // println!("received_data: {:?}", data); + // } + // resources.readable_debug_gpu_interface.unmap(); + // std::mem::drop(data_handler); + // tree_gpu_view.do_the_thing = false; + // } // --- DEBUG --- } } @@ -270,24 +269,42 @@ pub(crate) fn handle_gpu_readback( // ░░███ ░░███ █████ █████ █████ █████ ██████████ // ░░░ ░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ //############################################################################## -pub(crate) fn write_to_gpu( - tree_gpu_view: Option>, +pub(crate) fn write_to_gpu( + tree_gpu_host: Option>>, svx_pipeline: Option>, -) { - if let (Some(tree_gpu_view), Some(pipeline)) = (tree_gpu_view, svx_pipeline) { - let render_queue = &pipeline.render_queue.0; - + svx_view_set: ResMut, +) where + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, +{ + if let Some(pipeline) = svx_pipeline { + let render_queue = &pipeline.render_queue; // Data updates for spyglass viewport if let Some(resources) = &pipeline.resources { let mut buffer = UniformBuffer::new(Vec::::new()); - buffer.write(&tree_gpu_view.spyglass.viewport).unwrap(); - pipeline - .render_queue - .write_buffer(&resources.viewport_buffer, 0, &buffer.into_inner()); + buffer + .write(&svx_view_set.views[0].lock().unwrap().spyglass.viewport) + .unwrap(); + render_queue.write_buffer(&resources.viewport_buffer, 0, &buffer.into_inner()); } + //TODO: Write node requests + // let meta_buffer_updated_index_min = 0; + // let meta_buffer_updated_index_max = 0; + // for node_request in &mut tree_gpu_view.spyglass.node_requests { + // let requested_parent_node_key = *node_request & 0x00FFFFFF; + // let requsted_node_child_octant = (*node_request & 0xFF000000) >> 24; + // let requested_node_key = + // let data_handler = tree_gpu_view.data_handler.lock().unwrap(); + // if let Some(updated_meta_index) = data_handler.add_node(data, node_key, false) {} + // } + //TODO: Write bricks + // + //TODO: write back updated node requests array + // + + /*// +++ DEBUG +++ // Data updates triggered by debug interface - if tree_gpu_view.do_the_thing { + if tree_gpu_host.views[0].do_the_thing { // GPU buffer Write let data_buffer = StorageBuffer::new(vec![0, 0, 0, 1]); render_queue.write_buffer( @@ -296,5 +313,6 @@ pub(crate) fn write_to_gpu( &data_buffer.into_inner(), ); } + */// --- DEBUG --- } } diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index efe721a..70170dd 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -4,7 +4,7 @@ mod pipeline; pub mod types; pub use crate::octree::raytracing::bevy::types::{ - OctreeGPUHost, OctreeGPUView, OctreeSpyGlass, SvxRenderPlugin, Viewport, + OctreeGPUHost, OctreeGPUView, OctreeSpyGlass, RenderBevyPlugin, SvxViewSet, Viewport, }; use crate::octree::{ @@ -13,7 +13,7 @@ use crate::octree::{ pipeline::prepare_bind_groups, types::{SvxLabel, SvxRenderNode, SvxRenderPipeline}, }, - Octree, VoxelData, + VoxelData, }; use bevy::{ @@ -25,29 +25,35 @@ use bevy::{ }, }; -impl OctreeGPUHost +impl RenderBevyPlugin where - T: Default + Clone + Copy + PartialEq + VoxelData, + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, { - pub fn new(tree: Octree) -> Self { - OctreeGPUHost { - tree, - views: Vec::new(), + pub fn new(resolution: [u32; 2]) -> Self { + RenderBevyPlugin { + dummy: std::marker::PhantomData, + resolution, } } } -impl Plugin for SvxRenderPlugin { +impl Plugin for RenderBevyPlugin +where + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, +{ fn build(&self, app: &mut App) { - app.add_plugins(ExtractResourcePlugin::::default()); + app.add_plugins(( + ExtractResourcePlugin::>::default(), + ExtractResourcePlugin::::default(), + )); let render_app = app.sub_app_mut(RenderApp); render_app.add_systems(ExtractSchedule, sync_with_main_world); render_app.add_systems( Render, ( - write_to_gpu.in_set(RenderSet::PrepareResources), - prepare_bind_groups.in_set(RenderSet::PrepareBindGroups), - handle_gpu_readback.in_set(RenderSet::Cleanup), + write_to_gpu::.in_set(RenderSet::PrepareResources), + prepare_bind_groups::.in_set(RenderSet::PrepareBindGroups), + handle_gpu_readback::.in_set(RenderSet::Cleanup), ), ); let mut render_graph = render_app.world_mut().resource_mut::(); diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index c2f6076..d718717 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -1,5 +1,8 @@ -use crate::octree::raytracing::bevy::types::{ - OctreeGPUView, OctreeRenderData, OctreeSpyGlass, SvxRenderNode, SvxRenderPipeline, Viewport, +use crate::octree::{ + raytracing::bevy::types::{ + OctreeRenderData, OctreeSpyGlass, SvxRenderNode, SvxRenderPipeline, Viewport, + }, + VoxelData, }; use bevy::{ @@ -23,7 +26,7 @@ use bevy::{ }; use std::borrow::Cow; -use super::types::OctreeRenderDataResources; +use super::types::{OctreeRenderDataResources, SvxViewSet}; impl FromWorld for SvxRenderPipeline { fn from_world(world: &mut World) -> Self { @@ -73,12 +76,11 @@ impl render_graph::Node for SvxRenderNode { { let svx_pipeline = world.resource::(); let pipeline_cache = world.resource::(); - let tree_gpu_view = world.get_resource::(); if !self.ready { if let CachedPipelineState::Ok(_) = pipeline_cache.get_compute_pipeline_state(svx_pipeline.update_pipeline) { - self.ready = tree_gpu_view.is_some(); + self.ready = !world.resource::().views.is_empty(); } } } @@ -90,17 +92,13 @@ impl render_graph::Node for SvxRenderNode { render_context: &mut RenderContext, world: &World, ) -> Result<(), render_graph::NodeRunError> { - let pipeline_cache = world.resource::(); - let svx_pipeline = world.resource::(); - let tree_gpu_view = world.resource::(); - - let command_encoder = render_context.command_encoder(); if self.ready { - let data_handler = world - .resource::() - .data_handler - .lock() - .unwrap(); + let pipeline_cache = world.resource::(); + let svx_pipeline = world.resource::(); + let svx_viewset = world.resource::(); + let current_view = svx_viewset.views[0].lock().unwrap(); + let command_encoder = render_context.command_encoder(); + let data_handler = ¤t_view.data_handler; let resources = svx_pipeline.resources.as_ref().unwrap(); { let mut pass = @@ -129,7 +127,7 @@ impl render_graph::Node for SvxRenderNode { ); debug_assert!( - !tree_gpu_view.spyglass.node_requests.is_empty(), + !current_view.spyglass.node_requests.is_empty(), "Expected node requests array to not be empty" ); command_encoder.copy_buffer_to_buffer( @@ -137,8 +135,8 @@ impl render_graph::Node for SvxRenderNode { 0, &resources.readable_node_requests_buffer, 0, - (std::mem::size_of_val(&tree_gpu_view.spyglass.node_requests[0]) - * tree_gpu_view.spyglass.node_requests.len()) as u64, + (std::mem::size_of_val(¤t_view.spyglass.node_requests[0]) + * current_view.spyglass.node_requests.len()) as u64, ); // +++ DEBUG +++ @@ -181,34 +179,35 @@ impl render_graph::Node for SvxRenderNode { // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ ░░█████████ // ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░░ //############################################################################## -pub(crate) fn prepare_bind_groups( +pub(crate) fn prepare_bind_groups( gpu_images: Res>, render_device: Res, mut pipeline: ResMut, - tree_gpu_view: ResMut, -) { + svx_viewset: ResMut, +) where + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, +{ if pipeline.resources.is_some() && !pipeline.update_tree { return; } - let data_handler = tree_gpu_view.data_handler.lock().unwrap(); + let tree_view = &svx_viewset.views[0].lock().unwrap(); + let render_data = &tree_view.data_handler.render_data; if let Some(resources) = &pipeline.resources { let mut buffer = UniformBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.octree_meta).unwrap(); + buffer.write(&render_data.octree_meta).unwrap(); pipeline .render_queue .write_buffer(&resources.metadata_buffer, 0, &buffer.into_inner()); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.metadata).unwrap(); + buffer.write(&render_data.metadata).unwrap(); pipeline .render_queue .write_buffer(&resources.metadata_buffer, 0, &buffer.into_inner()); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer - .write(&data_handler.render_data.node_children) - .unwrap(); + buffer.write(&render_data.node_children).unwrap(); pipeline.render_queue.write_buffer( &resources.node_children_buffer, 0, @@ -216,21 +215,19 @@ pub(crate) fn prepare_bind_groups( ); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.node_ocbits).unwrap(); + buffer.write(&render_data.node_ocbits).unwrap(); pipeline .render_queue .write_buffer(&resources.node_ocbits_buffer, 0, &buffer.into_inner()); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.voxels).unwrap(); + buffer.write(&render_data.voxels).unwrap(); pipeline .render_queue .write_buffer(&resources.voxels_buffer, 0, &buffer.into_inner()); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer - .write(&data_handler.render_data.color_palette) - .unwrap(); + buffer.write(&render_data.color_palette).unwrap(); pipeline .render_queue .write_buffer(&resources.color_palette_buffer, 0, &buffer.into_inner()) @@ -256,7 +253,7 @@ pub(crate) fn prepare_bind_groups( // Create the staging buffer helping in reading data from the GPU let readable_metadata_buffer = render_device.create_buffer(&BufferDescriptor { mapped_at_creation: false, - size: (data_handler.render_data.metadata.len() * 4) as u64, + size: (render_data.metadata.len() * 4) as u64, label: Some("Octree Node metadata staging Buffer"), usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, }); @@ -276,7 +273,7 @@ pub(crate) fn prepare_bind_groups( }); // --- DEBUG --- let mut buffer = UniformBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.octree_meta).unwrap(); + buffer.write(&render_data.octree_meta).unwrap(); let octree_meta_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Tree Metadata Buffer"), contents: &buffer.into_inner(), @@ -284,7 +281,7 @@ pub(crate) fn prepare_bind_groups( }); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.metadata).unwrap(); + buffer.write(&render_data.metadata).unwrap(); let metadata_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Metadata Buffer"), contents: &buffer.into_inner(), @@ -292,9 +289,7 @@ pub(crate) fn prepare_bind_groups( }); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer - .write(&data_handler.render_data.node_children) - .unwrap(); + buffer.write(&render_data.node_children).unwrap(); let node_children_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Node Children Buffer"), contents: &buffer.into_inner(), @@ -302,7 +297,7 @@ pub(crate) fn prepare_bind_groups( }); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.node_ocbits).unwrap(); + buffer.write(&render_data.node_ocbits).unwrap(); let node_ocbits_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Node Occupied Bits Buffer"), contents: &buffer.into_inner(), @@ -310,7 +305,7 @@ pub(crate) fn prepare_bind_groups( }); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&data_handler.render_data.voxels).unwrap(); + buffer.write(&render_data.voxels).unwrap(); let voxels_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Voxels Buffer"), contents: &buffer.into_inner(), @@ -318,9 +313,7 @@ pub(crate) fn prepare_bind_groups( }); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer - .write(&data_handler.render_data.color_palette) - .unwrap(); + buffer.write(&render_data.color_palette).unwrap(); let color_palette_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Color Palette Buffer"), contents: &buffer.into_inner(), @@ -391,7 +384,7 @@ pub(crate) fn prepare_bind_groups( // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ //############################################################################## let mut buffer = UniformBuffer::new([0u8; Viewport::SHADER_SIZE.get() as usize]); - buffer.write(&tree_gpu_view.spyglass.viewport).unwrap(); + buffer.write(&tree_view.spyglass.viewport).unwrap(); let viewport_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Viewport Buffer"), contents: &buffer.into_inner(), @@ -399,11 +392,11 @@ pub(crate) fn prepare_bind_groups( }); debug_assert!( - !tree_gpu_view.spyglass.node_requests.is_empty(), + !tree_view.spyglass.node_requests.is_empty(), "Expected node requests array to not be empty" ); let mut buffer = StorageBuffer::new(Vec::::new()); - buffer.write(&tree_gpu_view.spyglass.node_requests).unwrap(); + buffer.write(&tree_view.spyglass.node_requests).unwrap(); let node_requests_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { label: Some("Octree Node requests Buffer"), contents: &buffer.into_inner(), @@ -412,15 +405,15 @@ pub(crate) fn prepare_bind_groups( let readable_node_requests_buffer = render_device.create_buffer(&BufferDescriptor { mapped_at_creation: false, - size: (tree_gpu_view.spyglass.node_requests.len() - * std::mem::size_of_val(&tree_gpu_view.spyglass.node_requests[0])) + size: (tree_view.spyglass.node_requests.len() + * std::mem::size_of_val(&tree_view.spyglass.node_requests[0])) as u64, label: Some("Octree Node requests staging Buffer"), usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, }); let output_texture_view = gpu_images - .get(&tree_gpu_view.spyglass.output_texture) + .get(&tree_view.spyglass.output_texture) .unwrap() .texture_view .clone(); diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index eca7253..846c462 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -40,17 +40,27 @@ pub struct Viewport { pub w_h_fov: V3cf32, } -pub struct SvxRenderPlugin { +pub struct RenderBevyPlugin +where + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, +{ + pub(crate) dummy: std::marker::PhantomData, pub resolution: [u32; 2], } -#[derive(Resource)] +#[derive(Resource, Clone, TypePath, ExtractResource)] +#[type_path = "shocovox::gpu::OctreeGPUHost"] pub struct OctreeGPUHost where - T: Default + Clone + PartialEq + VoxelData, + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, { pub tree: Octree, - pub views: Vec>, +} + +#[derive(Default, Resource, Clone, TypePath, ExtractResource)] +#[type_path = "shocovox::gpu::SvxViewSet"] +pub struct SvxViewSet { + pub views: Vec>>, } #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] @@ -60,7 +70,7 @@ pub struct OctreeGPUView { pub do_the_thing: bool, // --- DEBUG --- pub spyglass: OctreeSpyGlass, - pub(crate) data_handler: Arc>, + pub(crate) data_handler: OctreeGPUDataHandler, } #[derive(Debug, Clone)] @@ -74,9 +84,6 @@ pub(crate) struct VictimPointer { #[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] #[type_path = "shocovox::gpu::OctreeGPUDataHandler"] pub struct OctreeGPUDataHandler { - // +++ DEBUG +++ - pub read_back: u32, - // --- DEBUG --- pub(crate) render_data: OctreeRenderData, pub(crate) victim_node: VictimPointer, pub(crate) victim_brick: VictimPointer, diff --git a/src/octree/raytracing/mod.rs b/src/octree/raytracing/mod.rs index 7ff9d95..66e9ec7 100644 --- a/src/octree/raytracing/mod.rs +++ b/src/octree/raytracing/mod.rs @@ -8,5 +8,6 @@ pub use crate::spatial::raytracing::Ray; #[cfg(feature = "bevy_wgpu")] pub use bevy::types::{ - OctreeGPUHost, OctreeGPUView, OctreeRenderData, OctreeSpyGlass, SvxRenderPlugin, Viewport, + OctreeGPUHost, OctreeGPUView, OctreeRenderData, OctreeSpyGlass, RenderBevyPlugin, SvxViewSet, + Viewport, }; diff --git a/src/octree/types.rs b/src/octree/types.rs index 83b9f45..e5611f7 100644 --- a/src/octree/types.rs +++ b/src/octree/types.rs @@ -80,6 +80,7 @@ pub trait VoxelData { /// A Brick can be indexed directly, as opposed to the octree which is essentially a /// tree-graph where each node has 8 children. #[cfg_attr(feature = "serialization", derive(Serialize))] +#[derive(Clone)] pub struct Octree where T: Default + Clone + PartialEq + VoxelData, From 55096b8d8cfe31329b62aeed6ae816b15f5d6fad Mon Sep 17 00:00:00 2001 From: davids91 Date: Sat, 30 Nov 2024 14:13:29 +0100 Subject: [PATCH 16/19] Initial implementation of GPU Data upload --- Cargo.toml | 3 +- assets/shaders/viewport_render.wgsl | 50 ++-- examples/minecraft.rs | 2 +- src/octree/raytracing/bevy/cache.rs | 144 +++++++--- src/octree/raytracing/bevy/data.rs | 406 +++++++++++++++++++++++++--- src/octree/raytracing/bevy/mod.rs | 2 +- src/octree/raytracing/bevy/types.rs | 4 +- src/spatial/lut.rs | 8 +- 8 files changed, 498 insertions(+), 121 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 12fa46f..a2d7df9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ default = ["bevy_wgpu","dot_vox_support"] raytracing = ["dep:image", "dep:show-image"] serialization = ["dep:serde"] dot_vox_support = ["dep:dot_vox", "dep:nalgebra"] -bevy_wgpu = ["raytracing", "dep:bevy", "dep:iyes_perf_ui", "dep:crossbeam"] +bevy_wgpu = ["raytracing", "dep:bevy", "dep:iyes_perf_ui", "dep:crossbeam", "dep:bimap"] [dependencies] num-traits = "0.2.19" @@ -19,6 +19,7 @@ bendy = { git = "https://github.com/davids91/bendy.git" , features = ["std", "se dot_vox = { version = "5.1.1", optional = true } nalgebra = { version = "0.33.0", optional = true } crossbeam = { version = "0.8.4", optional = true } +bimap = { version = "0.6.3", optional = true } # for example cpu_render image = { version = "0.25.1", optional = true } diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index 2242381..e6c22d2 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -285,7 +285,7 @@ fn request_node(node_meta_index: u32, child_octant: u32) -> bool { var request_index = 0u; loop{ let exchange_result = atomicCompareExchangeWeak( - &node_requests[request_index], 0u, request_data + &node_requests[request_index], EMPTY_MARKER, request_data ); if(exchange_result.exchanged || exchange_result.old_value == request_data) { break; @@ -611,6 +611,8 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { & node_occupied_bits[current_node_key * 2 + 1] ) ) + // Request node only once per ray iteration to prioritize nodes in sight for cache + && 0 == (missing_data_color.r + missing_data_color.g + missing_data_color.b) ){ if request_node(current_node_key, target_octant) { missing_data_color += ( @@ -640,25 +642,27 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { if (target_octant != OOB_OCTANT) { if(0 != (0x00000004 & current_node_meta)) { // node is leaf var hit: OctreeRayIntersection; - if(0 != (0x00000008 & current_node_meta)) { // node is a uniform leaf - hit = probe_brick( - ray, &ray_current_distance, - current_node_key, 0u, ¤t_bounds, - &ray_scale_factors, direction_lut_index - ); - do_backtrack_after_leaf_miss = true; - } else { // node is a non-uniform leaf - if // node not empty at target octant, while the brick is marked unavailable - (0 != ((0x01u << (8 + target_octant)) & current_node_meta)) - && EMPTY_MARKER == node_children[(current_node_key * 8) + target_octant] - { - // child brick is not yet uploaded to GPU - if request_node(current_node_key, target_octant) { - missing_data_color += vec3f(0.3,0.1,0.0); - } else { - missing_data_color += vec3f(0.7,0.0,0.0); - } + + if // node not empty at target octant, while the brick is marked unavailable + (0 != ((0x01u << (8 + target_octant)) & current_node_meta)) + && EMPTY_MARKER == node_children[(current_node_key * 8) + target_octant] + { + // child brick is not yet uploaded to GPU + if request_node(current_node_key, target_octant) { + missing_data_color += vec3f(0.3,0.1,0.0); } else { + missing_data_color += vec3f(0.7,0.0,0.0); + } + do_backtrack_after_leaf_miss = true; + } else { + if(0 != (0x00000008 & current_node_meta)) { // node is a uniform leaf + hit = probe_brick( + ray, &ray_current_distance, + current_node_key, 0u, ¤t_bounds, + &ray_scale_factors, direction_lut_index + ); + do_backtrack_after_leaf_miss = true; + } else { // node is a non-uniform leaf target_bounds = child_bounds_for(¤t_bounds, target_octant); hit = probe_brick( ray, &ray_current_distance, @@ -667,10 +671,10 @@ fn get_by_ray(ray: ptr) -> OctreeRayIntersection { &ray_scale_factors, direction_lut_index ); } - } - if hit.hit == true { - hit.albedo += vec4f(missing_data_color, 0.); - return hit; + if hit.hit == true { + hit.albedo += vec4f(missing_data_color, 0.); + return hit; + } } } } diff --git a/examples/minecraft.rs b/examples/minecraft.rs index ca3e57e..9e04703 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -74,7 +74,7 @@ fn setup(mut commands: Commands, images: ResMut>) { let mut views = SvxViewSet::default(); let output_texture = host.create_new_view( &mut views, - 20, + 39, Viewport { origin: V3c { x: 0., diff --git a/src/octree/raytracing/bevy/cache.rs b/src/octree/raytracing/bevy/cache.rs index db47e51..bac4d38 100644 --- a/src/octree/raytracing/bevy/cache.rs +++ b/src/octree/raytracing/bevy/cache.rs @@ -32,14 +32,14 @@ use super::types::{OctreeGPUDataHandler, VictimPointer}; //############################################################################## impl VictimPointer { pub(crate) fn is_full(&self) -> bool { - self.stored_items >= (self.max_meta_len - 1) + self.max_meta_len <= self.stored_items } pub(crate) fn new(max_meta_len: usize) -> Self { Self { max_meta_len, stored_items: 0, - meta_index: 0, + meta_index: max_meta_len - 1, child: 0, } } @@ -61,15 +61,22 @@ impl VictimPointer { self.child = 0; } + pub(crate) fn went_around(&self) -> bool { + self.meta_index == 0 && self.child == 0 + } + /// Provides the first available index in the metadata buffer which can be overwritten - /// with node related information. It never returns with 0, because that is reserved for the root node, - /// which should not be overwritten. - fn first_available_node(&mut self, render_data: &mut OctreeRenderData) -> usize { + /// with node related meta information and the index of the meta from where the child was taken. + /// The available index is never 0, because that is reserved for the root node, which should not be overwritten. + fn first_available_node( + &mut self, + render_data: &mut OctreeRenderData, + ) -> (usize, Option) { // If there is space left in the cache, use it all up if !self.is_full() { render_data.metadata[self.stored_items] |= 0x01; self.stored_items += 1; - return self.stored_items - 1; + return (self.stored_items - 1, None); } //look for the next internal node ( with node children ) @@ -88,11 +95,13 @@ impl VictimPointer { let child_meta_index = render_data.node_children[self.meta_index * 8 + self.child] as usize; if 0 == (render_data.metadata[child_meta_index] & 0x01) { + let severed_parent_index = self.meta_index; //mark child as used render_data.metadata[child_meta_index] |= 0x01; //erase connection to parent node - render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); + render_data.node_children[severed_parent_index * 8 + self.child] = + empty_marker(); if // erased node is a leaf and it has children @@ -119,8 +128,9 @@ impl VictimPointer { } } + // step victim pointer forward and return with the requested data self.step(); - return child_meta_index; + return (child_meta_index, Some(severed_parent_index)); } else { // mark child as unused render_data.metadata[child_meta_index] &= 0xFFFFFFFE; @@ -131,14 +141,19 @@ impl VictimPointer { } /// Finds the first available brick, and marks it as used - fn first_available_brick(&mut self, render_data: &mut OctreeRenderData) -> usize { + /// Returns with the resulting brick index, and optionally + /// the index in the meta, where the brick was taken from + fn first_available_brick( + &mut self, + render_data: &mut OctreeRenderData, + ) -> (usize, Option) { let max_brick_count = render_data.metadata.len() * 8; // If there is space left in the cache, use it all up if self.stored_items < max_brick_count - 1 { render_data.metadata[self.stored_items / 8] |= 0x01 << (24 + (self.stored_items % 8)); self.stored_items += 1; - return self.stored_items - 1; + return (self.stored_items - 1, None); } // look for the next victim leaf node with bricks @@ -170,11 +185,12 @@ impl VictimPointer { render_data.metadata[brick_index / 8] |= brick_used_mask; // erase connection of child brick to parent node + let severed_parent_node = self.meta_index; render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); - // step victim pointer forward + // step victim pointer forward and return with the requested data self.step(); - return brick_index; + return (brick_index, Some(severed_parent_node)); } // set the bricks used bit to 0 for the currently used brick @@ -288,25 +304,29 @@ impl OctreeGPUDataHandler { // █████ ░░█████ ░░░███████░ ██████████ ██████████ // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ //############################################################################## - /// Writes the data of the node to the first available index, and returns the index of the overwritten data + /// Writes the data of the node to the first available index + /// returns the index of the overwritten metadata entry and a vector + /// of index values where metadata entries were taken from, taking a child of another node. + /// May take children from 0-2 nodes! /// It may fail to add the node, when @try_add_children is set to true pub(crate) fn add_node( &mut self, tree: &Octree, node_key: usize, try_add_children: bool, - ) -> Option + ) -> (Option, Vec) where T: Default + Copy + Clone + PartialEq + VoxelData, { if try_add_children && self.victim_node.is_full() { // Do not add additional nodes at initial upload if the cache is already full - return None; + return (None, Vec::new()); } // Determine the index in meta - let node_element_index = self.victim_node.first_available_node(&mut self.render_data); - self.map_to_node_index_in_metadata + let (node_element_index, severed_parent_index) = + self.victim_node.first_available_node(&mut self.render_data); + self.node_key_vs_meta_index .insert(node_key, node_element_index); // Add node properties to metadata @@ -321,6 +341,11 @@ impl OctreeGPUDataHandler { ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; // Add node content + let mut severed_parents = if let Some(severed_parent_index) = severed_parent_index { + vec![severed_parent_index] + } else { + Vec::new() + }; match tree.nodes.get(node_key) { NodeContent::UniformLeaf(brick) => { debug_assert!( @@ -332,8 +357,16 @@ impl OctreeGPUDataHandler { tree.node_children[node_key].content ); - let (brick_index, brick_added) = self.add_brick(brick); - self.render_data.node_children[node_element_index * 8 + 0] = brick_index; + if try_add_children { + let (brick_index, severed_parent_data) = self.add_brick(brick); + self.render_data.node_children[node_element_index * 8 + 0] = brick_index; + if let Some(severed_parent_index) = severed_parent_data { + severed_parents.push(severed_parent_index); + } + } else { + self.render_data.node_children[node_element_index * 8 + 0] = empty_marker(); + } + self.render_data.node_children[node_element_index * 8 + 1] = empty_marker(); self.render_data.node_children[node_element_index * 8 + 2] = empty_marker(); self.render_data.node_children[node_element_index * 8 + 3] = empty_marker(); @@ -343,7 +376,7 @@ impl OctreeGPUDataHandler { self.render_data.node_children[node_element_index * 8 + 7] = empty_marker(); #[cfg(debug_assertions)] { - if !brick_added { + if let BrickData::Solid(_) | BrickData::Empty = brick { // If no brick was added, the occupied bits should either be empty or full if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = tree.node_children[node_key].content @@ -362,24 +395,36 @@ impl OctreeGPUDataHandler { "Expected Leaf to have OccupancyBitmaps(_) instead of {:?}", tree.node_children[node_key].content ); - for octant in 0..8 { - let (brick_index, brick_added) = self.add_brick(&bricks[octant]); - self.render_data.node_children[node_element_index * 8 + octant] = brick_index; - #[cfg(debug_assertions)] - { - if !brick_added { - // If no brick was added, the relevant occupied bits should either be empty or full - if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = - tree.node_children[node_key].content - { - debug_assert!( - 0 == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] - || BITMAP_MASK_FOR_OCTANT_LUT[octant] - == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] - ); + if try_add_children { + for octant in 0..8 { + let (brick_index, severed_parent_data) = self.add_brick(&bricks[octant]); + self.render_data.node_children[node_element_index * 8 + octant] = + brick_index; + if let Some(severed_parent_index) = severed_parent_data { + severed_parents.push(severed_parent_index); + } + #[cfg(debug_assertions)] + { + if let BrickData::Solid(_) | BrickData::Empty = bricks[octant] { + // If no brick was added, the relevant occupied bits should either be empty or full + if let NodeChildrenArray::OccupancyBitmap(occupied_bits) = + tree.node_children[node_key].content + { + debug_assert!( + 0 == occupied_bits & BITMAP_MASK_FOR_OCTANT_LUT[octant] + || BITMAP_MASK_FOR_OCTANT_LUT[octant] + == occupied_bits + & BITMAP_MASK_FOR_OCTANT_LUT[octant] + ); + } } } } + } else { + for octant in 0..8 { + self.render_data.node_children[node_element_index * 8 + octant] = + empty_marker(); + } } } NodeContent::Internal(_) => { @@ -387,15 +432,17 @@ impl OctreeGPUDataHandler { let child_key = tree.node_children[node_key][octant] as usize; if child_key != empty_marker() as usize { if try_add_children - && !self.map_to_node_index_in_metadata.contains_key(&child_key) + && !self.node_key_vs_meta_index.contains_left(&child_key) { + // In case @try_add_children is true, no new node is added in case the cache is full, + // so there will be no severed parents in this case self.add_node(tree, child_key, try_add_children); } self.render_data.node_children[node_element_index * 8 + octant as usize] = *self - .map_to_node_index_in_metadata - .get(&child_key) + .node_key_vs_meta_index + .get_by_left(&child_key) .unwrap_or(&(empty_marker() as usize)) as u32; } else { @@ -411,7 +458,7 @@ impl OctreeGPUDataHandler { } } } - Some(node_element_index) + (Some(node_element_index), severed_parents) } //############################################################################## @@ -434,8 +481,14 @@ impl OctreeGPUDataHandler { //############################################################################## /// Loads a brick into the provided voxels vector and color palette /// * `brick` - The brick to upload - /// * `returns` - the identifier to set in @SizedNode and true if a new brick was aded to the voxels vector - fn add_brick(&mut self, brick: &BrickData) -> (u32, bool) + /// * `returns` - (index in @render_data.metadata, None) if a brick was uploaded and no other node had its child taken away + /// - (index in @render_data.metadata, Some(severed_parent_index)) + /// --> if a new brick was added to the voxels vector, taking away a brick child from a leaf node + /// - (index in @render_data.color_palette, None) if brick is solid, and no node were stripped of its brick + pub(crate) fn add_brick( + &mut self, + brick: &BrickData, + ) -> (u32, Option) where T: Default + Clone + PartialEq + VoxelData, { @@ -448,7 +501,7 @@ impl OctreeGPUDataHandler { ); match brick { - BrickData::Empty => (empty_marker(), false), + BrickData::Empty => (empty_marker(), None), BrickData::Solid(voxel) => { let albedo = voxel.albedo(); // The number of colors inserted into the palette is the size of the color palette map @@ -464,12 +517,13 @@ impl OctreeGPUDataHandler { albedo.a as f32 / 255., ); } - (self.map_to_color_index_in_palette[&albedo] as u32, false) + (self.map_to_color_index_in_palette[&albedo] as u32, None) } BrickData::Parted(brick) => { - let brick_index = self + let (brick_index, severed_parent_data) = self .victim_brick .first_available_brick(&mut self.render_data); + let severed_parent_index = severed_parent_data; for z in 0..DIM { for y in 0..DIM { for x in 0..DIM { @@ -500,7 +554,7 @@ impl OctreeGPUDataHandler { } } } - (brick_index as u32, true) + (brick_index as u32, severed_parent_index) } } } diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index e08af22..0eb7ebb 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -4,23 +4,24 @@ use crate::octree::{ OctreeGPUDataHandler, OctreeGPUHost, OctreeGPUView, OctreeMetaData, OctreeRenderData, OctreeSpyGlass, SvxRenderPipeline, SvxViewSet, VictimPointer, Viewport, Voxelement, }, - Octree, V3c, VoxelData, + BrickData, NodeContent, Octree, V3c, VoxelData, }; use bevy::{ ecs::system::{Res, ResMut}, math::Vec4, - prelude::{Assets, Commands, Handle, Image}, + prelude::{Assets, Handle, Image}, render::{ render_asset::RenderAssetUsages, render_resource::{ - encase::{StorageBuffer, UniformBuffer}, - Extent3d, TextureDimension, TextureFormat, TextureUsages, + encase::{internal::WriteInto, StorageBuffer, UniformBuffer}, + Buffer, Extent3d, ShaderSize, TextureDimension, TextureFormat, TextureUsages, }, - renderer::RenderDevice, + renderer::{RenderDevice, RenderQueue}, }, }; +use bimap::BiHashMap; use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, sync::{Arc, Mutex}, }; @@ -91,11 +92,12 @@ where victim_node: VictimPointer::new(size), victim_brick: VictimPointer::new(size), map_to_color_index_in_palette: HashMap::new(), - map_to_node_index_in_metadata: HashMap::new(), + node_key_vs_meta_index: BiHashMap::new(), + uploaded_color_palette_size: 0, }; // Push root node and its contents - gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, true); + gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, false); // +++ DEBUG +++ //delete some random bricks from leaf nodes @@ -105,11 +107,6 @@ where } } - // reset used bits - for meta in gpu_data_handler.render_data.metadata.iter_mut() { - *meta &= 0x00FFFFFE; - } - // --- DEBUG --- let mut output_texture = Image::new_fill( @@ -132,7 +129,7 @@ where do_the_thing: false, data_handler: gpu_data_handler, spyglass: OctreeSpyGlass { - node_requests: vec![0; 4], + node_requests: vec![empty_marker(); 4], output_texture: output_texture.clone(), viewport: viewport, }, @@ -188,10 +185,9 @@ pub(crate) fn handle_gpu_readback( if let (Some(ref mut tree_gpu_host), Some(ref mut pipeline)) = (tree_gpu_host, svx_pipeline) { let resources = pipeline.resources.as_ref().unwrap(); - // Read node requests from GPU - let buffer_slice = resources.readable_node_requests_buffer.slice(..); - let (s, r) = crossbeam::channel::unbounded::<()>(); - buffer_slice.map_async( + let node_requests_buffer_slice = resources.readable_node_requests_buffer.slice(..); + let (s, node_requests_recv) = crossbeam::channel::unbounded::<()>(); + node_requests_buffer_slice.map_async( bevy::render::render_resource::MapMode::Read, move |d| match d { Ok(_) => s.send(()).expect("Failed to send map update"), @@ -203,17 +199,55 @@ pub(crate) fn handle_gpu_readback( .poll(bevy::render::render_resource::Maintain::wait()) .panic_on_timeout(); - r.recv().expect("Failed to receive the map_async message"); + let mut view = svx_view_set.views[0].lock().unwrap(); + node_requests_recv + .recv() + .expect("Failed to receive the map_async message"); { - let buffer_view = buffer_slice.get_mapped_range(); - - svx_view_set.views[0].lock().unwrap().spyglass.node_requests = buffer_view + let buffer_view = node_requests_buffer_slice.get_mapped_range(); + view.spyglass.node_requests = buffer_view .chunks(std::mem::size_of::()) .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) .collect::>(); } resources.readable_node_requests_buffer.unmap(); + if { + let mut is_metadata_required_this_loop = false; + for node_request in &view.spyglass.node_requests { + if *node_request != empty_marker() { + is_metadata_required_this_loop = true; + break; + } + } + is_metadata_required_this_loop + } { + let metadata_buffer_slice = resources.readable_metadata_buffer.slice(..); + let (s, metadata_recv) = crossbeam::channel::unbounded::<()>(); + metadata_buffer_slice.map_async( + bevy::render::render_resource::MapMode::Read, + move |d| match d { + Ok(_) => s.send(()).expect("Failed to send map update"), + Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), + }, + ); + + render_device + .poll(bevy::render::render_resource::Maintain::wait()) + .panic_on_timeout(); + metadata_recv + .recv() + .expect("Failed to receive the map_async message"); + { + let buffer_view = metadata_buffer_slice.get_mapped_range(); + view.data_handler.render_data.metadata = buffer_view + .chunks(std::mem::size_of::()) + .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) + .collect::>(); + } + resources.readable_metadata_buffer.unmap(); + } + // +++ DEBUG +++ // Data updates triggered by debug interface // let data_handler = tree_gpu_view.data_handler.lock().unwrap(); @@ -269,38 +303,320 @@ pub(crate) fn handle_gpu_readback( // ░░███ ░░███ █████ █████ █████ █████ ██████████ // ░░░ ░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ //############################################################################## + +fn write_range_to_buffer( + array: &Vec, + range: std::ops::Range, + buffer: &Buffer, + render_queue: &RenderQueue, +) where + U: Send + Sync + 'static + ShaderSize + WriteInto, +{ + if !range.is_empty() { + let element_size = std::mem::size_of_val(&array[0]); + let byte_offset = (range.start * element_size) as u64; + let slice = array.get(range.clone()).expect( + &format!( + "Expected range {:?} to be in bounds of {:?}", + range, + array.len(), + ) + .to_owned(), + ); + let mut buf = StorageBuffer::new(Vec::::new()); + buf.write(slice).unwrap(); + render_queue.write_buffer(buffer, byte_offset, &buf.into_inner()); + } +} + pub(crate) fn write_to_gpu( tree_gpu_host: Option>>, svx_pipeline: Option>, svx_view_set: ResMut, ) where - T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, + T: Default + Clone + Copy + PartialEq + VoxelData + Send + Sync + 'static, { - if let Some(pipeline) = svx_pipeline { + if let (Some(pipeline), Some(tree_host)) = (svx_pipeline, tree_gpu_host) { let render_queue = &pipeline.render_queue; + let resources = if let Some(resources) = &pipeline.resources { + resources + } else { + // No resources available yet, can't write to them + return; + }; + + let mut view = svx_view_set.views[0].lock().unwrap(); + // Data updates for spyglass viewport - if let Some(resources) = &pipeline.resources { - let mut buffer = UniformBuffer::new(Vec::::new()); - buffer - .write(&svx_view_set.views[0].lock().unwrap().spyglass.viewport) - .unwrap(); - render_queue.write_buffer(&resources.viewport_buffer, 0, &buffer.into_inner()); - } + let mut buffer = UniformBuffer::new(Vec::::new()); + buffer.write(&view.spyglass.viewport).unwrap(); + render_queue.write_buffer(&resources.viewport_buffer, 0, &buffer.into_inner()); - //TODO: Write node requests - // let meta_buffer_updated_index_min = 0; - // let meta_buffer_updated_index_max = 0; - // for node_request in &mut tree_gpu_view.spyglass.node_requests { - // let requested_parent_node_key = *node_request & 0x00FFFFFF; - // let requsted_node_child_octant = (*node_request & 0xFF000000) >> 24; - // let requested_node_key = - // let data_handler = tree_gpu_view.data_handler.lock().unwrap(); - // if let Some(updated_meta_index) = data_handler.add_node(data, node_key, false) {} - // } - //TODO: Write bricks - // - //TODO: write back updated node requests array - // + // Handle node requests, update cache + let tree = &tree_host.tree; + { + let mut meta_updated = std::ops::Range { + start: view.data_handler.render_data.metadata.len(), + end: 0, + }; + let mut ocbits_updated = std::ops::Range { + start: view.data_handler.render_data.node_ocbits.len(), + end: 0, + }; + let mut node_children_updated = std::ops::Range { + start: view.data_handler.render_data.node_children.len(), + end: 0, + }; + let mut voxels_updated = std::ops::Range { + start: view.data_handler.render_data.voxels.len(), + end: 0, + }; + let mut node_requests = view.spyglass.node_requests.clone(); + let mut updated_this_loop = HashSet::::new(); + for node_request in &mut node_requests { + if *node_request == empty_marker() { + continue; + } + let requested_parent_meta_index = (*node_request & 0x00FFFFFF) as usize; + let requested_child_octant = (*node_request & 0xFF000000) >> 24; + + if updated_this_loop.contains(&requested_parent_meta_index) { + // Do not accept a request if the requester meta is already overwritten + continue; + } + + debug_assert!(view + .data_handler + .node_key_vs_meta_index + .contains_right(&requested_parent_meta_index)); + let requested_parent_node_key = view + .data_handler + .node_key_vs_meta_index + .get_by_right(&requested_parent_meta_index) + .unwrap(); + + debug_assert!( + tree.nodes.key_is_valid(*requested_parent_node_key), + "Expected parent node({:?}) to be valid in GPU request.", + requested_parent_node_key + ); + + match tree.nodes.get(*requested_parent_node_key) { + NodeContent::Nothing => {} // parent is empty, nothing to do + NodeContent::Internal(_) => { + + let requested_child_node_key = tree_host.tree.node_children + [*requested_parent_node_key][requested_child_octant] + as usize; + debug_assert!( + tree.nodes.key_is_valid(requested_child_node_key), + "Expected key({:?}, child of node[{:?}][{:?}] in meta[{:?}]) to be valid in GPU request.", + requested_child_node_key, requested_parent_node_key, requested_child_octant, requested_parent_meta_index + ); + let (child_index, severed_parents) = if !view + .data_handler + .node_key_vs_meta_index + .contains_left(&requested_child_node_key) + { + let (child_index, severed_parents) = + view.data_handler + .add_node(&tree, requested_child_node_key, false); + // println!("overwriting meta[{:?}]", child_index); + (child_index.expect("Expected to succeed adding a node into the GPU cache through data_handler"), severed_parents) + } else { + ( + *view + .data_handler + .node_key_vs_meta_index + .get_by_left(&requested_child_node_key) + .unwrap(), + Vec::new(), + ) + }; + + // Update connection to parent + view.data_handler.render_data.node_children + [requested_parent_meta_index * 8 + requested_child_octant as usize] = + child_index as u32; + + debug_assert!( + view.data_handler + .node_key_vs_meta_index + .contains_right(&requested_parent_meta_index), + "Requester parent erased while adding its child node to meta" + ); + + // Set updated buffers range + meta_updated.start = meta_updated.start.min(child_index); + meta_updated.end = meta_updated.end.max(child_index + 1); + ocbits_updated.start = ocbits_updated.start.min(child_index * 2); + ocbits_updated.end = ocbits_updated.end.max(child_index * 2 + 2); + node_children_updated.start = node_children_updated + .start + .min(requested_parent_meta_index * 8); + node_children_updated.end = node_children_updated + .end + .max(requested_parent_meta_index * 8 + 8); + for severed_parent_index in severed_parents { + updated_this_loop.insert(severed_parent_index); + node_children_updated.start = + node_children_updated.start.min(severed_parent_index * 8); + node_children_updated.end = + node_children_updated.end.max(severed_parent_index * 8 + 8); + } + } + NodeContent::UniformLeaf(brick) => { + // Only upload brick if it's not already available + if matches!(brick, BrickData::Parted(_) | BrickData::Solid(_)) + && view.data_handler.render_data.node_children + [requested_parent_meta_index * 8] + == empty_marker() + { + let (brick_index, severed_parent_index) = + view.data_handler.add_brick(&brick); + view.data_handler.render_data.node_children + [requested_parent_meta_index * 8] = brick_index; + + // Set updated buffers range + meta_updated.start = + meta_updated.start.min(requested_parent_meta_index); + meta_updated.end = + meta_updated.end.max(requested_parent_meta_index + 1); + node_children_updated.start = node_children_updated + .start + .min((requested_parent_meta_index * 8) as usize); + node_children_updated.end = node_children_updated + .end + .max((requested_parent_meta_index * 8) as usize + 8); + + if let BrickData::Parted(_) = brick { + voxels_updated.start = voxels_updated + .start + .min(brick_index as usize * (DIM * DIM * DIM)); + voxels_updated.end = voxels_updated.end.max( + brick_index as usize * (DIM * DIM * DIM) + (DIM * DIM * DIM), + ); + } + + if let Some(severed_parent_index) = severed_parent_index { + updated_this_loop.insert(severed_parent_index); + node_children_updated.start = + node_children_updated.start.min(severed_parent_index * 8); + node_children_updated.end = + node_children_updated.end.max(severed_parent_index * 8 + 8); + } + } + } + NodeContent::Leaf(bricks) => { + // Only upload brick if it's not already available + if matches!( + bricks[requested_child_octant as usize], + BrickData::Parted(_) | BrickData::Solid(_) + ) && view.data_handler.render_data.node_children + [requested_parent_meta_index * 8 + requested_child_octant as usize] + == empty_marker() + { + let (brick_index, severed_parent_index) = view + .data_handler + .add_brick(&bricks[requested_child_octant as usize]); + view.data_handler.render_data.node_children[requested_parent_meta_index + * 8 + + requested_child_octant as usize] = brick_index; + + // Set updated buffers range + meta_updated.start = + meta_updated.start.min(requested_parent_meta_index); + meta_updated.end = + meta_updated.end.max(requested_parent_meta_index + 1); + node_children_updated.start = node_children_updated + .start + .min((requested_parent_meta_index * 8) as usize); + node_children_updated.end = node_children_updated + .end + .max((requested_parent_meta_index * 8) as usize + 8); + + if let BrickData::Parted(_) = bricks[requested_child_octant as usize] { + voxels_updated.start = voxels_updated + .start + .min(brick_index as usize * (DIM * DIM * DIM)); + voxels_updated.end = voxels_updated.end.max( + brick_index as usize * (DIM * DIM * DIM) + (DIM * DIM * DIM), + ); + } + if let Some(severed_parent_index) = severed_parent_index { + updated_this_loop.insert(severed_parent_index); + node_children_updated.start = + node_children_updated.start.min(severed_parent_index * 8); + node_children_updated.end = + node_children_updated.end.max(severed_parent_index * 8 + 8); + } + + } + } + } + + } + + for node_request in &mut node_requests { + *node_request = empty_marker(); + } + + // write back updated data + let host_color_count = view.data_handler.map_to_color_index_in_palette.keys().len(); + let color_palette_size_diff = + host_color_count - view.data_handler.uploaded_color_palette_size; + let resources = &pipeline.resources.as_ref().unwrap(); + + debug_assert!( + host_color_count >= view.data_handler.uploaded_color_palette_size, + "Expected host color palette({:?}), to be larger, than colors stored on the GPU({:?})", + host_color_count, view.data_handler.uploaded_color_palette_size + ); + view.data_handler.uploaded_color_palette_size = + view.data_handler.map_to_color_index_in_palette.keys().len(); + + // Node requests + let mut buffer = StorageBuffer::new(Vec::::new()); + buffer.write(&node_requests).unwrap(); + render_queue.write_buffer(&resources.node_requests_buffer, 0, &buffer.into_inner()); + + // Color palette + if 0 < color_palette_size_diff { + // Upload color palette delta to GPU + write_range_to_buffer( + &view.data_handler.render_data.color_palette, + (host_color_count - color_palette_size_diff)..(host_color_count), + &resources.color_palette_buffer, + &render_queue, + ); + } + + // Render data + write_range_to_buffer( + &view.data_handler.render_data.metadata, + meta_updated, + &resources.metadata_buffer, + &render_queue, + ); + write_range_to_buffer( + &view.data_handler.render_data.node_children, + node_children_updated, + &resources.node_children_buffer, + &render_queue, + ); + write_range_to_buffer( + &view.data_handler.render_data.node_ocbits, + ocbits_updated, + &resources.node_ocbits_buffer, + &render_queue, + ); + write_range_to_buffer( + &view.data_handler.render_data.voxels, + voxels_updated, + &resources.voxels_buffer, + &render_queue, + ); + } /*// +++ DEBUG +++ // Data updates triggered by debug interface diff --git a/src/octree/raytracing/bevy/mod.rs b/src/octree/raytracing/bevy/mod.rs index 70170dd..123e9ee 100644 --- a/src/octree/raytracing/bevy/mod.rs +++ b/src/octree/raytracing/bevy/mod.rs @@ -39,7 +39,7 @@ where impl Plugin for RenderBevyPlugin where - T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, + T: Default + Clone + Copy + PartialEq + VoxelData + Send + Sync + 'static, { fn build(&self, app: &mut App) { app.add_plugins(( diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 846c462..6c14013 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -14,6 +14,7 @@ use bevy::{ renderer::RenderQueue, }, }; +use bimap::BiHashMap; use std::{ collections::HashMap, sync::{Arc, Mutex}, @@ -87,8 +88,9 @@ pub struct OctreeGPUDataHandler { pub(crate) render_data: OctreeRenderData, pub(crate) victim_node: VictimPointer, pub(crate) victim_brick: VictimPointer, - pub(crate) map_to_node_index_in_metadata: HashMap, + pub(crate) node_key_vs_meta_index: BiHashMap, pub(crate) map_to_color_index_in_palette: HashMap, + pub(crate) uploaded_color_palette_size: usize, } #[derive(Clone)] diff --git a/src/spatial/lut.rs b/src/spatial/lut.rs index dca996a..da40265 100644 --- a/src/spatial/lut.rs +++ b/src/spatial/lut.rs @@ -198,13 +198,13 @@ pub(crate) const OCTANT_OFFSET_REGION_LUT: [V3cf32; 8] = [ pub(crate) const BITMAP_MASK_FOR_OCTANT_LUT: [u64; 8] = [ 0x0000000000330033, - 0x0000000000cc00cc, + 0x0000000000CC00CC, 0x0033003300000000, - 0x00cc00cc00000000, + 0x00CC00CC00000000, 0x0000000033003300, - 0x00000000cc00cc00, + 0x00000000CC00CC00, 0x3300330000000000, - 0xcc00cc0000000000, + 0xCC00CC0000000000, ]; pub(crate) const BITMAP_INDEX_LUT: [[[usize; 4]; 4]; 4] = [ From a0a945f73617f81b749cfac903bf8aa55c1bb54a Mon Sep 17 00:00:00 2001 From: davids91 Date: Sun, 8 Dec 2024 01:21:34 +0100 Subject: [PATCH 17/19] Refactor, Bugfixes, Conceptual improvements --- assets/shaders/viewport_render.wgsl | 15 +- src/octree/raytracing/bevy/cache.rs | 449 ++++++++++++++++++---------- src/octree/raytracing/bevy/data.rs | 174 ++++++----- src/octree/raytracing/bevy/types.rs | 22 +- 4 files changed, 394 insertions(+), 266 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index e6c22d2..c889904 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -278,16 +278,19 @@ fn set_brick_used(brick_index: u32) { // Unique to this implementation, not adapted from rust code /// Requests the child of the given node to be uploaded fn request_node(node_meta_index: u32, child_octant: u32) -> bool { - let request_data = ( - (node_meta_index & 0x00FFFFFFu) - | ((child_octant & 0x000000FF) << 24) - ); var request_index = 0u; loop{ let exchange_result = atomicCompareExchangeWeak( - &node_requests[request_index], EMPTY_MARKER, request_data + &node_requests[request_index], EMPTY_MARKER, + (node_meta_index & 0x00FFFFFFu)|((child_octant & 0x000000FF) << 24) ); - if(exchange_result.exchanged || exchange_result.old_value == request_data) { + if( + exchange_result.exchanged + ||( + exchange_result.old_value + == ((node_meta_index & 0x00FFFFFFu)|((child_octant & 0x000000FF) << 24)) + ) + ) { break; } request_index += 1u; diff --git a/src/octree/raytracing/bevy/cache.rs b/src/octree/raytracing/bevy/cache.rs index bac4d38..171aa13 100644 --- a/src/octree/raytracing/bevy/cache.rs +++ b/src/octree/raytracing/bevy/cache.rs @@ -1,4 +1,5 @@ use crate::object_pool::empty_marker; +use crate::octree::raytracing::bevy::types::BrickOwnedBy; use crate::spatial::math::flat_projection; use crate::{ octree::{ @@ -31,6 +32,19 @@ use super::types::{OctreeGPUDataHandler, VictimPointer}; // ░░░░░ ░░░░░ ░░░░░ ░░░░░ //############################################################################## impl VictimPointer { + /// returns with the meta index, and the target octant currently pointed at + pub(crate) fn current_target_meta_index(&self, render_data: &OctreeRenderData) -> u32 { + if 0 < self.get_loop_count() { + render_data.node_children[self.meta_index * 8 + self.child] + } else { + self.stored_items as u32 - 1 + } + } + + pub(crate) fn len(&self) -> usize { + self.max_meta_len + } + pub(crate) fn is_full(&self) -> bool { self.max_meta_len <= self.stored_items } @@ -38,6 +52,7 @@ impl VictimPointer { pub(crate) fn new(max_meta_len: usize) -> Self { Self { max_meta_len, + loop_count: 0, stored_items: 0, meta_index: max_meta_len - 1, child: 0, @@ -54,6 +69,7 @@ impl VictimPointer { pub(crate) fn skip_node(&mut self) { if self.meta_index == 0 { + self.loop_count += 1; self.meta_index = self.max_meta_len - 1; } else { self.meta_index -= 1; @@ -61,154 +77,48 @@ impl VictimPointer { self.child = 0; } - pub(crate) fn went_around(&self) -> bool { - self.meta_index == 0 && self.child == 0 + /// Tells the number of times the victim node pointer has started from the first element in the cache + pub(crate) fn get_loop_count(&self) -> usize { + self.loop_count } /// Provides the first available index in the metadata buffer which can be overwritten /// with node related meta information and the index of the meta from where the child was taken. /// The available index is never 0, because that is reserved for the root node, which should not be overwritten. - fn first_available_node( - &mut self, - render_data: &mut OctreeRenderData, - ) -> (usize, Option) { + fn go_to_first_available_node(&mut self, render_data: &mut OctreeRenderData) { // If there is space left in the cache, use it all up if !self.is_full() { - render_data.metadata[self.stored_items] |= 0x01; + render_data.metadata[self.stored_items] |= OctreeGPUDataHandler::NODE_USED_MASK; + self.meta_index = self.stored_items; self.stored_items += 1; - return (self.stored_items - 1, None); + return; } //look for the next internal node ( with node children ) loop { - let child_node_index = - render_data.node_children[self.meta_index * 8 + self.child] as usize; - // child at target is not empty in a non-leaf node, which means // the target child might point to an internal node if it's valid - if - // parent node has a child at target octant, which isn't invalidated - child_node_index != empty_marker() as usize - // parent node is not a leaf - && (0 == (render_data.metadata[self.meta_index] & 0x00000004)) + // parent node has a child at target octant, which isn't invalid + if 0 == (render_data.metadata[self.meta_index] & OctreeGPUDataHandler::NODE_LEAF_MASK) + && render_data.node_children[self.meta_index * 8 + self.child] != empty_marker() { let child_meta_index = render_data.node_children[self.meta_index * 8 + self.child] as usize; - if 0 == (render_data.metadata[child_meta_index] & 0x01) { - let severed_parent_index = self.meta_index; - //mark child as used - render_data.metadata[child_meta_index] |= 0x01; - - //erase connection to parent node - render_data.node_children[severed_parent_index * 8 + self.child] = - empty_marker(); - - if - // erased node is a leaf and it has children - 0 != (render_data.metadata[child_meta_index] & 0x00000004) - && 0 != (render_data.metadata[child_meta_index] & 0x00FF0000) - { - // Since the node is deleted, if it has any stored bricks, those are freed up - for octant in 0..8 { - let brick_index = - render_data.node_children[child_meta_index * 8 + octant] as usize; - if - // child is valid - brick_index != empty_marker() as usize - // child is not empty and not solid - && 0 != (render_data.metadata[child_meta_index] - & (0x01 << (8 + octant))) - && 0 != (render_data.metadata[child_meta_index] - & (0x01 << (16 + octant))) - { - // mark brick unused - render_data.metadata[brick_index / 8] &= - !(0x01 << (24 + (brick_index % 8))); - } - } - } - - // step victim pointer forward and return with the requested data - self.step(); - return (child_meta_index, Some(severed_parent_index)); + if 0 == (render_data.metadata[child_meta_index] + & OctreeGPUDataHandler::NODE_USED_MASK) + { + render_data.metadata[child_meta_index] |= OctreeGPUDataHandler::NODE_USED_MASK; + return; } else { // mark child as unused - render_data.metadata[child_meta_index] &= 0xFFFFFFFE; + render_data.metadata[child_meta_index] &= !OctreeGPUDataHandler::NODE_USED_MASK; } } self.step(); } } - - /// Finds the first available brick, and marks it as used - /// Returns with the resulting brick index, and optionally - /// the index in the meta, where the brick was taken from - fn first_available_brick( - &mut self, - render_data: &mut OctreeRenderData, - ) -> (usize, Option) { - let max_brick_count = render_data.metadata.len() * 8; - - // If there is space left in the cache, use it all up - if self.stored_items < max_brick_count - 1 { - render_data.metadata[self.stored_items / 8] |= 0x01 << (24 + (self.stored_items % 8)); - self.stored_items += 1; - return (self.stored_items - 1, None); - } - - // look for the next victim leaf node with bricks - loop { - let brick_index = render_data.node_children[self.meta_index * 8 + self.child] as usize; - if - // child at target is not empty - brick_index != empty_marker() as usize - //node is leaf - &&(0 != (render_data.metadata[self.meta_index] & 0x00000004)) - && ( - // In case of uniform leaf nodes, only the first child might have a brick - // So the node is either not uniform, or the target child points to 0 - (0 == render_data.metadata[self.meta_index] & 0x00000008) - || (0 == self.child) - ) - && ( - // node child is not empty, and parted at octant - 0 != (render_data.metadata[self.meta_index] - & (0x01 << (8 + self.child))) - && 0 != (render_data.metadata[self.meta_index] - & (0x01 << (16 + self.child))) - ) - { - let brick_used_mask = 0x01 << (24 + (brick_index % 8)); - // if used bit for the brick is 0, it can be safely overwritten - if 0 == (render_data.metadata[brick_index / 8] & brick_used_mask) { - // mark brick as used - render_data.metadata[brick_index / 8] |= brick_used_mask; - - // erase connection of child brick to parent node - let severed_parent_node = self.meta_index; - render_data.node_children[self.meta_index * 8 + self.child] = empty_marker(); - - // step victim pointer forward and return with the requested data - self.step(); - return (brick_index, Some(severed_parent_node)); - } - - // set the bricks used bit to 0 for the currently used brick - render_data.metadata[brick_index / 8] &= !brick_used_mask; - } - if - // node is not leaf - (0 == (render_data.metadata[self.meta_index] & 0x00000004)) - // in case node is uniform, octant 0 should have been checked for vacancy already, so it is safe to skip to next leaf node - || (0 != (render_data.metadata[self.meta_index] & 0x00000008)) - { - self.skip_node(); - } else { - self.step(); - } - } - } } + impl OctreeGPUDataHandler { //############################################################################## // ██████████ █████████ ███████████ █████████ @@ -229,6 +139,23 @@ impl OctreeGPUDataHandler { // ██████████ ██████████░░█████████ █████ ░░█████████ █████ ░░█████ // ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ //############################################################################## + + /// Bitmask in metadata where the non-zero bits represent if the given node is used + const NODE_USED_MASK: u32 = 0x00000001; + + /// Bitmask in metadata where the non-zero bits represent if the given node is a leaf + const NODE_LEAF_MASK: u32 = 0x00000004; + + /// Bitmask in metadata where the non-zero bits represent if the given leaf is uniform + /// Note: Non-leaf nodes can't be uniform + const NODE_UNIFORM_MASK: u32 = 0x00000008; + + /// Provides the mask used with one metadata element to signal that the contained brick is used. + /// Index of the metadata element should be brick index divided by 8, as one metadata element contains 8 bricks + fn brick_used_mask(brick_index: usize) -> u32 { + 0x01 << (24 + (brick_index % 8)) + } + /// Updates the meta element value to store the brick structure of the given leaf node. /// Does not erase anything in @sized_node_meta, it's expected to be cleared before /// the first use of this function @@ -267,25 +194,153 @@ impl OctreeGPUDataHandler { let mut meta = 0; match node { NodeContent::Internal(_) | NodeContent::Nothing => { - meta &= 0xFFFFFFFB; // element is not leaf - meta &= 0xFFFFFFF7; // element is not uniform + meta &= !Self::NODE_LEAF_MASK; // element is not leaf + meta &= !Self::NODE_UNIFORM_MASK; // element is not uniform } NodeContent::Leaf(bricks) => { - meta |= 0x00000004; // element is leaf - meta &= 0xFFFFFFF7; // element is not uniform + meta |= Self::NODE_LEAF_MASK; // element is leaf + meta &= !Self::NODE_UNIFORM_MASK; // element is not uniform for octant in 0..8 { Self::meta_add_leaf_brick_structure(&mut meta, &bricks[octant], octant); } } NodeContent::UniformLeaf(brick) => { - meta |= 0x00000004; // element is leaf - meta |= 0x00000008; // element is uniform + meta |= Self::NODE_LEAF_MASK; // element is leaf + meta |= Self::NODE_UNIFORM_MASK; // element is uniform Self::meta_add_leaf_brick_structure(&mut meta, brick, 0); } }; meta } + //############################################################################## + // ██████████ ███████████ █████████ █████████ ██████████ + // ░░███░░░░░█░░███░░░░░███ ███░░░░░███ ███░░░░░███░░███░░░░░█ + // ░███ █ ░ ░███ ░███ ░███ ░███ ░███ ░░░ ░███ █ ░ + // ░██████ ░█████████ ░███████████ ░░█████████ ░██████ + // ░███░░█ ░███░░░░░███ ░███░░░░░███ ░░░░░░░░███ ░███░░█ + // ░███ ░ █ ░███ ░███ ░███ ░███ ███ ░███ ░███ ░ █ + // ██████████ █████ █████ █████ █████░░█████████ ██████████ + // ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░ + // ██████ █████ ███████ ██████████ ██████████ + // ░░██████ ░░███ ███░░░░░███ ░░███░░░░███ ░░███░░░░░█ + // ░███░███ ░███ ███ ░░███ ░███ ░░███ ░███ █ ░ + // ░███░░███░███ ░███ ░███ ░███ ░███ ░██████ + // ░███ ░░██████ ░███ ░███ ░███ ░███ ░███░░█ + // ░███ ░░█████ ░░███ ███ ░███ ███ ░███ ░ █ + // █████ ░░█████ ░░░███████░ ██████████ ██████████ + // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ + // █████████ █████ █████ █████ █████ ██████████ + // ███░░░░░███░░███ ░░███ ░░███ ░░███ ░░███░░░░███ + // ███ ░░░ ░███ ░███ ░███ ░███ ░███ ░░███ + // ░███ ░███████████ ░███ ░███ ░███ ░███ + // ░███ ░███░░░░░███ ░███ ░███ ░███ ░███ + // ░░███ ███ ░███ ░███ ░███ ░███ █ ░███ ███ + // ░░█████████ █████ █████ █████ ███████████ ██████████ + // ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░░ ░░░░░░░░░░ + //############################################################################## + /// Erases the child node pointed by the given victim pointer + /// returns with the vector of node index values and brick index values modified + fn erase_node_child( + &mut self, + meta_index: usize, + child_octant: usize, + tree: &Octree, + ) -> (Vec, Vec) + where + T: Default + Clone + PartialEq + VoxelData, + { + let mut modified_bricks = Vec::new(); + let mut modified_nodes = vec![meta_index]; + debug_assert!( + self.node_key_vs_meta_index.contains_right(&meta_index), + "Expected parent node to be in metadata index hash! (meta: {meta_index})" + ); + let parent_key = self + .node_key_vs_meta_index + .get_by_right(&meta_index) + .unwrap(); + + debug_assert!( + tree.nodes.key_is_valid(*parent_key), + "Expected parent node({:?}) to be valid", + parent_key + ); + + let child_index = self.render_data.node_children[meta_index * 8 + child_octant] as usize; + debug_assert_ne!( + child_index, + empty_marker() as usize, + "Expected victim pointer to point to an erasable node/brick, instead of: {child_index}" + ); + + match tree.nodes.get(*parent_key) { + NodeContent::Nothing => { + panic!("HOW DO I ERASE NOTHING. AMERICA EXPLAIN") + } + NodeContent::Internal(_occupied_bits) => { + debug_assert!( + self.node_key_vs_meta_index.contains_right(&child_index), + "Expected parent node to be in metadata index hash!" + ); + let child_key = self + .node_key_vs_meta_index + .get_by_right(&child_index) + .unwrap(); + debug_assert!( + tree.nodes.key_is_valid(*child_key), + "Expected removed child node({:?}) to be valid", + child_key + ); + modified_nodes.push(child_index); + + if let NodeContent::Leaf(_) | NodeContent::UniformLeaf(_) = + tree.nodes.get(*child_key) + { + // make the children bricks of the removed leaf orphan + for octant in 0..8 { + let brick_index = + self.render_data.node_children[child_index * 8 + octant] as usize; + if brick_index != empty_marker() as usize { + self.brick_ownership[brick_index] = BrickOwnedBy::NotOwned; + modified_bricks.push(brick_index); + + // mark brick as unused + self.render_data.metadata[brick_index / 8] &= + !Self::brick_used_mask(brick_index); + + // Eliminate connection + self.render_data.node_children[child_index * 8 + octant] = + empty_marker(); + } + } + } + } + NodeContent::UniformLeaf(_) | NodeContent::Leaf(_) => { + debug_assert!( + (0 == child_octant) + || matches!(tree.nodes.get(*parent_key), NodeContent::Leaf(_)), + "Expected child octant in uniform leaf to be 0 in: {:?}", + (meta_index, child_octant) + ); + if child_index != empty_marker() as usize { + self.brick_ownership[child_index] = BrickOwnedBy::NotOwned; + modified_bricks.push(child_index); + + // mark brick as unused + self.render_data.metadata[child_index / 8] &= + !Self::brick_used_mask(child_index); + } + } + } + + //Erase connection to parent + self.render_data.node_children[meta_index * 8 + child_octant] = empty_marker(); + + //return with updated ranges in voxels and metadata + (modified_nodes, modified_bricks) + } + //############################################################################## // █████████ ██████████ ██████████ // ███░░░░░███ ░░███░░░░███ ░░███░░░░███ @@ -305,27 +360,37 @@ impl OctreeGPUDataHandler { // ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ //############################################################################## /// Writes the data of the node to the first available index - /// returns the index of the overwritten metadata entry and a vector - /// of index values where metadata entries were taken from, taking a child of another node. - /// May take children from 0-2 nodes! - /// It may fail to add the node, when @try_add_children is set to true + /// Upon success, returns the index in metadata where the node is added + /// and vectors of modified nodes, bricks: + /// (meta_index, modified_nodes, modified_bricks) pub(crate) fn add_node( &mut self, tree: &Octree, node_key: usize, try_add_children: bool, - ) -> (Option, Vec) + ) -> Option<(usize, Vec, Vec)> where T: Default + Copy + Clone + PartialEq + VoxelData, { if try_add_children && self.victim_node.is_full() { // Do not add additional nodes at initial upload if the cache is already full - return (None, Vec::new()); + return None; } // Determine the index in meta - let (node_element_index, severed_parent_index) = - self.victim_node.first_available_node(&mut self.render_data); + self.victim_node + .go_to_first_available_node(&mut self.render_data); + + let node_element_index = self + .victim_node + .current_target_meta_index(&self.render_data) as usize; + let (meta_index, child_octant) = (self.victim_node.meta_index, self.victim_node.child); + let (mut modified_nodes, mut modified_bricks) = + if !self.node_key_vs_meta_index.contains_right(&meta_index) { + (Vec::new(), Vec::new()) + } else { + self.erase_node_child(meta_index, child_octant, tree) + }; self.node_key_vs_meta_index .insert(node_key, node_element_index); @@ -341,11 +406,6 @@ impl OctreeGPUDataHandler { ((occupied_bits & 0xFFFFFFFF00000000) >> 32) as u32; // Add node content - let mut severed_parents = if let Some(severed_parent_index) = severed_parent_index { - vec![severed_parent_index] - } else { - Vec::new() - }; match tree.nodes.get(node_key) { NodeContent::UniformLeaf(brick) => { debug_assert!( @@ -358,11 +418,13 @@ impl OctreeGPUDataHandler { ); if try_add_children { - let (brick_index, severed_parent_data) = self.add_brick(brick); + let (brick_index, mut current_modified_nodes, mut current_modified_bricks) = + self.add_brick(tree, node_key, 0); + modified_bricks.push(brick_index as usize); + modified_nodes.append(&mut current_modified_nodes); + modified_bricks.append(&mut current_modified_bricks); + self.render_data.node_children[node_element_index * 8 + 0] = brick_index; - if let Some(severed_parent_index) = severed_parent_data { - severed_parents.push(severed_parent_index); - } } else { self.render_data.node_children[node_element_index * 8 + 0] = empty_marker(); } @@ -397,12 +459,14 @@ impl OctreeGPUDataHandler { ); if try_add_children { for octant in 0..8 { - let (brick_index, severed_parent_data) = self.add_brick(&bricks[octant]); + let (brick_index, mut current_modified_nodes, mut current_modified_bricks) = + self.add_brick(tree, node_key, octant); + modified_bricks.push(brick_index as usize); + modified_nodes.append(&mut current_modified_nodes); + modified_bricks.append(&mut current_modified_bricks); + self.render_data.node_children[node_element_index * 8 + octant] = brick_index; - if let Some(severed_parent_index) = severed_parent_data { - severed_parents.push(severed_parent_index); - } #[cfg(debug_assertions)] { if let BrickData::Solid(_) | BrickData::Empty = bricks[octant] { @@ -458,7 +522,8 @@ impl OctreeGPUDataHandler { } } } - (Some(node_element_index), severed_parents) + modified_nodes.push(node_element_index); + Some((node_element_index, modified_nodes, modified_bricks)) } //############################################################################## @@ -479,16 +544,40 @@ impl OctreeGPUDataHandler { // ███████████ █████ █████ █████ ░░█████████ █████ ░░████ // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░ //############################################################################## + fn go_to_first_available_brick(&mut self) -> usize { + let mut brick_index; + loop { + brick_index = self.victim_brick; + if + // current brick is not owned or used + BrickOwnedBy::NotOwned == self.brick_ownership[brick_index] + || (0 + == (self.render_data.metadata[brick_index / 8] + & Self::brick_used_mask(brick_index))) + { + // mark brick used + self.render_data.metadata[brick_index / 8] |= Self::brick_used_mask(brick_index); + break; + } + + // mark current brick unused and step the iterator forward + self.render_data.metadata[brick_index / 8] &= !Self::brick_used_mask(brick_index); + self.victim_brick = (brick_index + 1) % (self.render_data.metadata.len() * 8); + } + + brick_index + } + /// Loads a brick into the provided voxels vector and color palette /// * `brick` - The brick to upload - /// * `returns` - (index in @render_data.metadata, None) if a brick was uploaded and no other node had its child taken away - /// - (index in @render_data.metadata, Some(severed_parent_index)) - /// --> if a new brick was added to the voxels vector, taking away a brick child from a leaf node - /// - (index in @render_data.color_palette, None) if brick is solid, and no node were stripped of its brick + /// * `tree` - The octree where the brick is found + /// * `returns` - the index where the brick is found and potentially a list of nodes and bricks modified during insertion pub(crate) fn add_brick( &mut self, - brick: &BrickData, - ) -> (u32, Option) + tree: &Octree, + node_key: usize, + target_octant: usize, + ) -> (u32, Vec, Vec) where T: Default + Clone + PartialEq + VoxelData, { @@ -500,8 +589,16 @@ impl OctreeGPUDataHandler { (DIM * DIM * DIM) ); + let brick = match tree.nodes.get(node_key) { + NodeContent::UniformLeaf(brick) => brick, + NodeContent::Leaf(bricks) => &bricks[target_octant], + NodeContent::Nothing | NodeContent::Internal(_) => { + panic!("Expected to add brick of Internal or empty node!") + } + }; + match brick { - BrickData::Empty => (empty_marker(), None), + BrickData::Empty => (empty_marker(), Vec::new(), Vec::new()), BrickData::Solid(voxel) => { let albedo = voxel.albedo(); // The number of colors inserted into the palette is the size of the color palette map @@ -517,13 +614,36 @@ impl OctreeGPUDataHandler { albedo.a as f32 / 255., ); } - (self.map_to_color_index_in_palette[&albedo] as u32, None) + ( + self.map_to_color_index_in_palette[&albedo] as u32, + Vec::new(), + Vec::new(), + ) } BrickData::Parted(brick) => { - let (brick_index, severed_parent_data) = self - .victim_brick - .first_available_brick(&mut self.render_data); - let severed_parent_index = severed_parent_data; + let brick_index = self.go_to_first_available_brick(); + let (modified_nodes, modified_bricks) = + if let BrickOwnedBy::Node(key, octant) = self.brick_ownership[brick_index] { + debug_assert!( + self.node_key_vs_meta_index.contains_left(&(key as usize)), + "Expected brick to be owned by a node used in cache" + ); + + self.erase_node_child( + *self + .node_key_vs_meta_index + .get_by_left(&(key as usize)) + .unwrap(), + octant as usize, + tree, + ) + } else { + (Vec::new(), Vec::new()) + }; + + self.brick_ownership[brick_index as usize] = + BrickOwnedBy::Node(node_key as u32, target_octant as u8); + for z in 0..DIM { for y in 0..DIM { for x in 0..DIM { @@ -554,7 +674,8 @@ impl OctreeGPUDataHandler { } } } - (brick_index as u32, severed_parent_index) + + (brick_index as u32, modified_nodes, modified_bricks) } } } diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index 0eb7ebb..ac4e787 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -1,8 +1,9 @@ use crate::object_pool::empty_marker; use crate::octree::{ raytracing::bevy::types::{ - OctreeGPUDataHandler, OctreeGPUHost, OctreeGPUView, OctreeMetaData, OctreeRenderData, - OctreeSpyGlass, SvxRenderPipeline, SvxViewSet, VictimPointer, Viewport, Voxelement, + BrickOwnedBy, OctreeGPUDataHandler, OctreeGPUHost, OctreeGPUView, OctreeMetaData, + OctreeRenderData, OctreeSpyGlass, SvxRenderPipeline, SvxViewSet, VictimPointer, Viewport, + Voxelement, }, BrickData, NodeContent, Octree, V3c, VoxelData, }; @@ -90,22 +91,24 @@ where ], }, victim_node: VictimPointer::new(size), - victim_brick: VictimPointer::new(size), + victim_brick: 0, map_to_color_index_in_palette: HashMap::new(), node_key_vs_meta_index: BiHashMap::new(), + brick_ownership: vec![BrickOwnedBy::NotOwned; size * 8], uploaded_color_palette_size: 0, }; // Push root node and its contents - gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, false); + // gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, true); // +++ DEBUG +++ + gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, false); //delete some random bricks from leaf nodes - for i in 15..20 { - if 0 != (gpu_data_handler.render_data.metadata[i] & 0x00000004) { - gpu_data_handler.render_data.node_children[i * 8 + 3] = empty_marker(); - } - } + // for i in 15..20 { + // if 0 != (gpu_data_handler.render_data.metadata[i] & 0x00000004) { + // gpu_data_handler.render_data.node_children[i * 8 + 3] = empty_marker(); + // } + // } // --- DEBUG --- @@ -323,9 +326,9 @@ fn write_range_to_buffer( ) .to_owned(), ); - let mut buf = StorageBuffer::new(Vec::::new()); - buf.write(slice).unwrap(); - render_queue.write_buffer(buffer, byte_offset, &buf.into_inner()); + unsafe { + render_queue.write_buffer(buffer, byte_offset, &slice.align_to::().1); + } } } @@ -372,7 +375,9 @@ pub(crate) fn write_to_gpu( end: 0, }; let mut node_requests = view.spyglass.node_requests.clone(); - let mut updated_this_loop = HashSet::::new(); + let mut modified_nodes = HashSet::::new(); + let mut modified_bricks = HashSet::::new(); + let victim_node_loop_count = view.data_handler.victim_node.get_loop_count(); for node_request in &mut node_requests { if *node_request == empty_marker() { continue; @@ -380,7 +385,7 @@ pub(crate) fn write_to_gpu( let requested_parent_meta_index = (*node_request & 0x00FFFFFF) as usize; let requested_child_octant = (*node_request & 0xFF000000) >> 24; - if updated_this_loop.contains(&requested_parent_meta_index) { + if modified_nodes.contains(&requested_parent_meta_index) { // Do not accept a request if the requester meta is already overwritten continue; } @@ -393,45 +398,46 @@ pub(crate) fn write_to_gpu( .data_handler .node_key_vs_meta_index .get_by_right(&requested_parent_meta_index) - .unwrap(); + .unwrap() + .clone(); debug_assert!( - tree.nodes.key_is_valid(*requested_parent_node_key), + tree.nodes.key_is_valid(requested_parent_node_key), "Expected parent node({:?}) to be valid in GPU request.", requested_parent_node_key ); - match tree.nodes.get(*requested_parent_node_key) { + modified_nodes.insert(requested_parent_meta_index); + match tree.nodes.get(requested_parent_node_key) { NodeContent::Nothing => {} // parent is empty, nothing to do NodeContent::Internal(_) => { - let requested_child_node_key = tree_host.tree.node_children - [*requested_parent_node_key][requested_child_octant] + [requested_parent_node_key][requested_child_octant] as usize; debug_assert!( tree.nodes.key_is_valid(requested_child_node_key), "Expected key({:?}, child of node[{:?}][{:?}] in meta[{:?}]) to be valid in GPU request.", requested_child_node_key, requested_parent_node_key, requested_child_octant, requested_parent_meta_index ); - let (child_index, severed_parents) = if !view + let child_index = if !view .data_handler .node_key_vs_meta_index .contains_left(&requested_child_node_key) { - let (child_index, severed_parents) = + let (child_index, currently_modified_nodes, currently_modified_bricks) = view.data_handler - .add_node(&tree, requested_child_node_key, false); - // println!("overwriting meta[{:?}]", child_index); - (child_index.expect("Expected to succeed adding a node into the GPU cache through data_handler"), severed_parents) + .add_node(&tree, requested_child_node_key, false) + .expect("Expected to succeed adding a node into the GPU cache through data_handler"); + modified_nodes.extend(currently_modified_nodes); + modified_bricks.extend(currently_modified_bricks); + + child_index } else { - ( - *view - .data_handler - .node_key_vs_meta_index - .get_by_left(&requested_child_node_key) - .unwrap(), - Vec::new(), - ) + *view + .data_handler + .node_key_vs_meta_index + .get_by_left(&requested_child_node_key) + .unwrap() }; // Update connection to parent @@ -446,24 +452,8 @@ pub(crate) fn write_to_gpu( "Requester parent erased while adding its child node to meta" ); - // Set updated buffers range - meta_updated.start = meta_updated.start.min(child_index); - meta_updated.end = meta_updated.end.max(child_index + 1); ocbits_updated.start = ocbits_updated.start.min(child_index * 2); ocbits_updated.end = ocbits_updated.end.max(child_index * 2 + 2); - node_children_updated.start = node_children_updated - .start - .min(requested_parent_meta_index * 8); - node_children_updated.end = node_children_updated - .end - .max(requested_parent_meta_index * 8 + 8); - for severed_parent_index in severed_parents { - updated_this_loop.insert(severed_parent_index); - node_children_updated.start = - node_children_updated.start.min(severed_parent_index * 8); - node_children_updated.end = - node_children_updated.end.max(severed_parent_index * 8 + 8); - } } NodeContent::UniformLeaf(brick) => { // Only upload brick if it's not already available @@ -472,22 +462,14 @@ pub(crate) fn write_to_gpu( [requested_parent_meta_index * 8] == empty_marker() { - let (brick_index, severed_parent_index) = - view.data_handler.add_brick(&brick); + let (brick_index, currently_modified_nodes, currently_modified_bricks) = + view.data_handler + .add_brick(&tree, requested_parent_node_key, 0); view.data_handler.render_data.node_children [requested_parent_meta_index * 8] = brick_index; - // Set updated buffers range - meta_updated.start = - meta_updated.start.min(requested_parent_meta_index); - meta_updated.end = - meta_updated.end.max(requested_parent_meta_index + 1); - node_children_updated.start = node_children_updated - .start - .min((requested_parent_meta_index * 8) as usize); - node_children_updated.end = node_children_updated - .end - .max((requested_parent_meta_index * 8) as usize + 8); + modified_nodes.extend(currently_modified_nodes); + modified_bricks.extend(currently_modified_bricks); if let BrickData::Parted(_) = brick { voxels_updated.start = voxels_updated @@ -498,13 +480,6 @@ pub(crate) fn write_to_gpu( ); } - if let Some(severed_parent_index) = severed_parent_index { - updated_this_loop.insert(severed_parent_index); - node_children_updated.start = - node_children_updated.start.min(severed_parent_index * 8); - node_children_updated.end = - node_children_updated.end.max(severed_parent_index * 8 + 8); - } } } NodeContent::Leaf(bricks) => { @@ -516,24 +491,18 @@ pub(crate) fn write_to_gpu( [requested_parent_meta_index * 8 + requested_child_octant as usize] == empty_marker() { - let (brick_index, severed_parent_index) = view - .data_handler - .add_brick(&bricks[requested_child_octant as usize]); + let (brick_index, currently_modified_nodes, currently_modified_bricks) = + view.data_handler.add_brick( + &tree, + requested_parent_node_key, + requested_child_octant as usize, + ); view.data_handler.render_data.node_children[requested_parent_meta_index * 8 + requested_child_octant as usize] = brick_index; - // Set updated buffers range - meta_updated.start = - meta_updated.start.min(requested_parent_meta_index); - meta_updated.end = - meta_updated.end.max(requested_parent_meta_index + 1); - node_children_updated.start = node_children_updated - .start - .min((requested_parent_meta_index * 8) as usize); - node_children_updated.end = node_children_updated - .end - .max((requested_parent_meta_index * 8) as usize + 8); + modified_nodes.extend(currently_modified_nodes); + modified_bricks.extend(currently_modified_bricks); if let BrickData::Parted(_) = bricks[requested_child_octant as usize] { voxels_updated.start = voxels_updated @@ -543,24 +512,49 @@ pub(crate) fn write_to_gpu( brick_index as usize * (DIM * DIM * DIM) + (DIM * DIM * DIM), ); } - if let Some(severed_parent_index) = severed_parent_index { - updated_this_loop.insert(severed_parent_index); - node_children_updated.start = - node_children_updated.start.min(severed_parent_index * 8); - node_children_updated.end = - node_children_updated.end.max(severed_parent_index * 8 + 8); - } - } } } + if victim_node_loop_count != view.data_handler.victim_node.get_loop_count() { + break; + } } + debug_assert!( + // Either all node requests are empty + node_requests + .iter() + .filter(|&v| *v == empty_marker()) + .count() + == node_requests.len() + // Or some ndoes were updated this loop + || 0 < modified_nodes.len() + // Or the distance traveled by the victim pointer this loop is small enough + || (view.data_handler.victim_node.len() as f32 * 0.5) as usize + > (victim_node_loop_count - view.data_handler.victim_node.get_loop_count()), + "Couldn't process a single request because size of the buffer is too small." + ); + for node_request in &mut node_requests { *node_request = empty_marker(); } + // Set updated buffers range based on modified nodes and bricks + for modified_node_index in &modified_nodes { + meta_updated.start = meta_updated.start.min(*modified_node_index); + meta_updated.end = meta_updated.end.max(modified_node_index + 1); + node_children_updated.start = + node_children_updated.start.min(modified_node_index * 8); + node_children_updated.end = + node_children_updated.end.max(modified_node_index * 8 + 8); + } + + for modified_brick_index in &modified_bricks { + meta_updated.start = meta_updated.start.min(modified_brick_index / 8); + meta_updated.end = meta_updated.end.max(modified_brick_index / 8 + 1); + } + // write back updated data let host_color_count = view.data_handler.map_to_color_index_in_palette.keys().len(); let color_palette_size_diff = diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 6c14013..3f7ffdf 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -64,7 +64,7 @@ pub struct SvxViewSet { pub views: Vec>>, } -#[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] +#[derive(Resource, Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::OctreeGPUView"] pub struct OctreeGPUView { // +++ DEBUG +++ @@ -76,20 +76,29 @@ pub struct OctreeGPUView { #[derive(Debug, Clone)] pub(crate) struct VictimPointer { - pub(crate) max_meta_len: usize, //TODO: should be private to type + //TODO: most fields should be private to type + pub(crate) max_meta_len: usize, + pub(crate) loop_count: usize, pub(crate) stored_items: usize, pub(crate) meta_index: usize, pub(crate) child: usize, } -#[derive(Resource, Clone, AsBindGroup, TypePath, ExtractResource)] +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum BrickOwnedBy { + NotOwned, + Node(u32, u8), +} + +#[derive(Resource, Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::OctreeGPUDataHandler"] pub struct OctreeGPUDataHandler { pub(crate) render_data: OctreeRenderData, pub(crate) victim_node: VictimPointer, - pub(crate) victim_brick: VictimPointer, + pub(crate) victim_brick: usize, pub(crate) node_key_vs_meta_index: BiHashMap, pub(crate) map_to_color_index_in_palette: HashMap, + pub(crate) brick_ownership: Vec, pub(crate) uploaded_color_palette_size: usize, } @@ -165,13 +174,14 @@ pub struct OctreeRenderData { /// |=====================================================================| /// | Byte 3 | Voxel Bricks used *(3) | /// |---------------------------------------------------------------------| - /// | each bit is 1 if voxel brick is used by the raytracing algorithm | + /// | each bit is 1 if brick is used (do not delete please) | /// `=====================================================================` /// *(1) Only first bit is used in case uniform leaf nodes /// *(2) The same bit is used for node_children and node_occupied_bits /// *(3) One index in the array covers 8 bricks, which is the theoretical maximum /// number of bricks for one node. In practice however the number of bricks - /// are only 4-5 times more, than the number of nodes, because of the internal nodes. + /// are only 4-5 times more, than the number of nodes, because of the internal nodes; + /// And only a fraction of them are visible in a render. /// *(4) Root node does not have this bit used, because it will never be overwritten /// due to the victim pointer logic #[storage(1, visibility(compute))] From fafd9e7bb743002992ab583a577e1dad0cc8e4cc Mon Sep 17 00:00:00 2001 From: davids91 Date: Sun, 8 Dec 2024 13:14:45 +0100 Subject: [PATCH 18/19] Polishing, bugfixes, documentation --- assets/shaders/viewport_render.wgsl | 5 -- examples/dot_cube.rs | 10 +-- examples/minecraft.rs | 12 +-- src/octree/raytracing/bevy/cache.rs | 92 ++++++++++++---------- src/octree/raytracing/bevy/data.rs | 81 +++---------------- src/octree/raytracing/bevy/pipeline.rs | 34 +------- src/octree/raytracing/bevy/types.rs | 25 ++---- src/octree/raytracing/raytracing_on_cpu.rs | 2 +- src/octree/types.rs | 3 - 9 files changed, 72 insertions(+), 192 deletions(-) diff --git a/assets/shaders/viewport_render.wgsl b/assets/shaders/viewport_render.wgsl index c889904..96f714d 100644 --- a/assets/shaders/viewport_render.wgsl +++ b/assets/shaders/viewport_render.wgsl @@ -905,11 +905,6 @@ var voxels: array; @group(1) @binding(5) var color_palette: array; -// +++ DEBUG +++ -@group(1) @binding(6) -var debug_interface: u32; -// --- DEBUG --- - @compute @workgroup_size(8, 8, 1) fn update( diff --git a/examples/dot_cube.rs b/examples/dot_cube.rs index e389a1e..4516c7d 100644 --- a/examples/dot_cube.rs +++ b/examples/dot_cube.rs @@ -9,8 +9,8 @@ use iyes_perf_ui::{ #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUHost, OctreeGPUView, Ray, Viewport}, - Albedo, Octree, V3c, VoxelData, + raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxViewSet, Viewport}, + Albedo, V3c, }; #[cfg(feature = "bevy_wgpu")] @@ -169,12 +169,6 @@ fn handle_zoom( mut angles_query: Query<&mut DomePosition>, tree: Res>, ) { - if keys.pressed(KeyCode::Delete) { - tree_view.do_the_thing = true; - } - // if tree_view.is_changed() { - // println!("changed!! --> {:?}", tree_view.read_back); - // } const ADDITION: f32 = 0.05; let angle_update_fn = |angle, delta| -> f32 { let new_angle = angle + delta; diff --git a/examples/minecraft.rs b/examples/minecraft.rs index 9e04703..da49963 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -3,7 +3,7 @@ use bevy::{prelude::*, window::WindowPlugin}; #[cfg(feature = "bevy_wgpu")] use shocovox_rs::octree::{ - raytracing::{OctreeGPUHost, OctreeGPUView, Ray, SvxViewSet, Viewport}, + raytracing::{OctreeGPUHost, Ray, SvxViewSet, Viewport}, Albedo, Octree, V3c, }; @@ -67,14 +67,14 @@ fn setup(mut commands: Commands, images: ResMut>) { commands.spawn(DomePosition { yaw: 0., roll: 0., - radius: tree.get_size() as f32 * 2.2, + radius: tree.get_size() as f32 * 0.8, }); let mut host = OctreeGPUHost { tree }; let mut views = SvxViewSet::default(); let output_texture = host.create_new_view( &mut views, - 39, + 35, Viewport { origin: V3c { x: 0., @@ -151,12 +151,6 @@ fn handle_zoom( mut angles_query: Query<&mut DomePosition>, ) { let mut tree_view = view_set.views[0].lock().unwrap(); - if keys.pressed(KeyCode::Delete) { - tree_view.do_the_thing = true; - } - // if tree_view.is_changed() { - // println!("changed!! --> {:?}", tree_view.read_back); - // } const ADDITION: f32 = 0.05; let angle_update_fn = |angle, delta| -> f32 { let new_angle = angle + delta; diff --git a/src/octree/raytracing/bevy/cache.rs b/src/octree/raytracing/bevy/cache.rs index 171aa13..327126c 100644 --- a/src/octree/raytracing/bevy/cache.rs +++ b/src/octree/raytracing/bevy/cache.rs @@ -32,23 +32,17 @@ use super::types::{OctreeGPUDataHandler, VictimPointer}; // ░░░░░ ░░░░░ ░░░░░ ░░░░░ //############################################################################## impl VictimPointer { - /// returns with the meta index, and the target octant currently pointed at - pub(crate) fn current_target_meta_index(&self, render_data: &OctreeRenderData) -> u32 { - if 0 < self.get_loop_count() { - render_data.node_children[self.meta_index * 8 + self.child] - } else { - self.stored_items as u32 - 1 - } - } - + /// Returns the size of the buffer this pointer covers pub(crate) fn len(&self) -> usize { self.max_meta_len } + /// Returns true if no new nodes can be added without overwriting another pub(crate) fn is_full(&self) -> bool { self.max_meta_len <= self.stored_items } + /// Creates object, based on the given cache length it should cover pub(crate) fn new(max_meta_len: usize) -> Self { Self { max_meta_len, @@ -59,6 +53,8 @@ impl VictimPointer { } } + /// Steps the iterator forward to the next children, if available, or the next node. + /// Wraps around pub(crate) fn step(&mut self) { if self.child >= 7 { self.skip_node(); @@ -67,6 +63,7 @@ impl VictimPointer { } } + /// Steps the iterator forward one node pub(crate) fn skip_node(&mut self) { if self.meta_index == 0 { self.loop_count += 1; @@ -77,21 +74,23 @@ impl VictimPointer { self.child = 0; } - /// Tells the number of times the victim node pointer has started from the first element in the cache + /// Provides the number of times the victim node pointer has started from the first element in the cache pub(crate) fn get_loop_count(&self) -> usize { self.loop_count } /// Provides the first available index in the metadata buffer which can be overwritten - /// with node related meta information and the index of the meta from where the child was taken. - /// The available index is never 0, because that is reserved for the root node, which should not be overwritten. - fn go_to_first_available_node(&mut self, render_data: &mut OctreeRenderData) { + /// with node related meta information and optionally the source where the child was taken from. + fn first_available_node( + &mut self, + render_data: &mut OctreeRenderData, + ) -> (usize, Option<(usize, u8)>) { // If there is space left in the cache, use it all up if !self.is_full() { render_data.metadata[self.stored_items] |= OctreeGPUDataHandler::NODE_USED_MASK; self.meta_index = self.stored_items; self.stored_items += 1; - return; + return (self.meta_index, None); } //look for the next internal node ( with node children ) @@ -108,7 +107,7 @@ impl VictimPointer { & OctreeGPUDataHandler::NODE_USED_MASK) { render_data.metadata[child_meta_index] |= OctreeGPUDataHandler::NODE_USED_MASK; - return; + return (child_meta_index, Some((self.meta_index, self.child as u8))); } else { // mark child as unused render_data.metadata[child_meta_index] &= !OctreeGPUDataHandler::NODE_USED_MASK; @@ -267,7 +266,9 @@ impl OctreeGPUDataHandler { parent_key ); + // Erase connection to parent let child_index = self.render_data.node_children[meta_index * 8 + child_octant] as usize; + self.render_data.node_children[meta_index * 8 + child_octant] = empty_marker(); debug_assert_ne!( child_index, empty_marker() as usize, @@ -281,7 +282,7 @@ impl OctreeGPUDataHandler { NodeContent::Internal(_occupied_bits) => { debug_assert!( self.node_key_vs_meta_index.contains_right(&child_index), - "Expected parent node to be in metadata index hash!" + "Expected erased child node index[{child_index}] to be in metadata index hash!" ); let child_key = self .node_key_vs_meta_index @@ -289,10 +290,8 @@ impl OctreeGPUDataHandler { .unwrap(); debug_assert!( tree.nodes.key_is_valid(*child_key), - "Expected removed child node({:?}) to be valid", - child_key + "Expected erased child node({child_key}) to be valid" ); - modified_nodes.push(child_index); if let NodeContent::Leaf(_) | NodeContent::UniformLeaf(_) = tree.nodes.get(*child_key) @@ -303,7 +302,6 @@ impl OctreeGPUDataHandler { self.render_data.node_children[child_index * 8 + octant] as usize; if brick_index != empty_marker() as usize { self.brick_ownership[brick_index] = BrickOwnedBy::NotOwned; - modified_bricks.push(brick_index); // mark brick as unused self.render_data.metadata[brick_index / 8] &= @@ -312,9 +310,12 @@ impl OctreeGPUDataHandler { // Eliminate connection self.render_data.node_children[child_index * 8 + octant] = empty_marker(); + + modified_bricks.push(brick_index); } } } + modified_nodes.push(child_index); } NodeContent::UniformLeaf(_) | NodeContent::Leaf(_) => { debug_assert!( @@ -334,9 +335,6 @@ impl OctreeGPUDataHandler { } } - //Erase connection to parent - self.render_data.node_children[meta_index * 8 + child_octant] = empty_marker(); - //return with updated ranges in voxels and metadata (modified_nodes, modified_bricks) } @@ -370,27 +368,22 @@ impl OctreeGPUDataHandler { try_add_children: bool, ) -> Option<(usize, Vec, Vec)> where - T: Default + Copy + Clone + PartialEq + VoxelData, + T: Default + Copy + Clone + PartialEq + VoxelData + Send + Sync + 'static, { if try_add_children && self.victim_node.is_full() { // Do not add additional nodes at initial upload if the cache is already full return None; } - // Determine the index in meta - self.victim_node - .go_to_first_available_node(&mut self.render_data); - - let node_element_index = self - .victim_node - .current_target_meta_index(&self.render_data) as usize; - let (meta_index, child_octant) = (self.victim_node.meta_index, self.victim_node.child); - let (mut modified_nodes, mut modified_bricks) = - if !self.node_key_vs_meta_index.contains_right(&meta_index) { - (Vec::new(), Vec::new()) - } else { - self.erase_node_child(meta_index, child_octant, tree) - }; + // Determine the index in meta, overwrite a currently present node if needed + let (node_element_index, robbed_parent) = + self.victim_node.first_available_node(&mut self.render_data); + let (mut modified_nodes, mut modified_bricks) = if let Some(robbed_parent) = robbed_parent { + self.erase_node_child(robbed_parent.0, robbed_parent.1 as usize, tree) + } else { + (vec![node_element_index], Vec::new()) + }; + self.node_key_vs_meta_index .insert(node_key, node_element_index); @@ -522,7 +515,6 @@ impl OctreeGPUDataHandler { } } } - modified_nodes.push(node_element_index); Some((node_element_index, modified_nodes, modified_bricks)) } @@ -544,7 +536,8 @@ impl OctreeGPUDataHandler { // ███████████ █████ █████ █████ ░░█████████ █████ ░░████ // ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░ //############################################################################## - fn go_to_first_available_brick(&mut self) -> usize { + /// Provides the index of the first brick available to be overwritten, through the second chance algorithm + fn first_available_brick(&mut self) -> usize { let mut brick_index; loop { brick_index = self.victim_brick; @@ -579,7 +572,7 @@ impl OctreeGPUDataHandler { target_octant: usize, ) -> (u32, Vec, Vec) where - T: Default + Clone + PartialEq + VoxelData, + T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, { debug_assert_eq!( self.render_data.voxels.len() % (DIM * DIM * DIM), @@ -621,7 +614,22 @@ impl OctreeGPUDataHandler { ) } BrickData::Parted(brick) => { - let brick_index = self.go_to_first_available_brick(); + if let Some(brick_index) = self + .map_to_brick_maybe_owned_by_node + .get(&(node_key, target_octant as u8)) + { + if self.brick_ownership[*brick_index] == BrickOwnedBy::NotOwned { + self.brick_ownership[*brick_index] = + BrickOwnedBy::Node(node_key as u32, target_octant as u8); + return (*brick_index as u32, Vec::new(), Vec::new()); + } else { + // remove from index if it is owned by another node already + self.map_to_brick_maybe_owned_by_node + .remove(&(node_key, target_octant as u8)); + } + } + + let brick_index = self.first_available_brick(); let (modified_nodes, modified_bricks) = if let BrickOwnedBy::Node(key, octant) = self.brick_ownership[brick_index] { debug_assert!( diff --git a/src/octree/raytracing/bevy/data.rs b/src/octree/raytracing/bevy/data.rs index ac4e787..d2a7008 100644 --- a/src/octree/raytracing/bevy/data.rs +++ b/src/octree/raytracing/bevy/data.rs @@ -67,7 +67,6 @@ where ) -> Handle { let mut gpu_data_handler = OctreeGPUDataHandler { render_data: OctreeRenderData { - debug_gpu_interface: 0, octree_meta: OctreeMetaData { octree_size: self.tree.octree_size, voxel_brick_dim: DIM as u32, @@ -93,24 +92,13 @@ where victim_node: VictimPointer::new(size), victim_brick: 0, map_to_color_index_in_palette: HashMap::new(), + map_to_brick_maybe_owned_by_node: HashMap::new(), node_key_vs_meta_index: BiHashMap::new(), brick_ownership: vec![BrickOwnedBy::NotOwned; size * 8], uploaded_color_palette_size: 0, }; - // Push root node and its contents - // gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, true); - // +++ DEBUG +++ - gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, false); - - //delete some random bricks from leaf nodes - // for i in 15..20 { - // if 0 != (gpu_data_handler.render_data.metadata[i] & 0x00000004) { - // gpu_data_handler.render_data.node_children[i * 8 + 3] = empty_marker(); - // } - // } - - // --- DEBUG --- + gpu_data_handler.add_node(&self.tree, Octree::::ROOT_NODE_KEY as usize, true); let mut output_texture = Image::new_fill( Extent3d { @@ -129,7 +117,6 @@ where let output_texture = images.add(output_texture); svx_view_set.views.push(Arc::new(Mutex::new(OctreeGPUView { - do_the_thing: false, data_handler: gpu_data_handler, spyglass: OctreeSpyGlass { node_requests: vec![empty_marker(); 4], @@ -141,6 +128,7 @@ where } } +/// Handles data sync between Bevy main(CPU) world and rendering world pub(crate) fn sync_with_main_world(// tree_view: Option>, // mut world: ResMut, ) { @@ -150,11 +138,6 @@ pub(crate) fn sync_with_main_world(// tree_view: Option>, // in order to enable data flow in the opposite direction, extractresource plugin // needs to be disabled, and the sync logic (both ways) needs to be implemented here // refer to: https://www.reddit.com/r/bevy/comments/1ay50ee/copy_from_render_world_to_main_world/ - // if let Some(tree_view) = tree_view { - // let mut tree_view_mainworld = world.get_resource_mut::().unwrap(); - // tree_view_mainworld.read_back = tree_view.read_back; - // println!("Read back in render world: {:?}", tree_view.read_back); - // } } //############################################################################## @@ -175,17 +158,16 @@ pub(crate) fn sync_with_main_world(// tree_view: Option>, // █████ █████ ██████████ █████ █████ ██████████ // ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ //############################################################################## -/// Handles data reads from GPU every loop. +/// Handles data reads from GPU every loop, mainly data requests and usaage updates. /// Based on https://docs.rs/bevy/latest/src/gpu_readback/gpu_readback.rs.html pub(crate) fn handle_gpu_readback( render_device: Res, - tree_gpu_host: Option>>, svx_view_set: ResMut, - svx_pipeline: Option>, + mut svx_pipeline: Option>, ) where T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, { - if let (Some(ref mut tree_gpu_host), Some(ref mut pipeline)) = (tree_gpu_host, svx_pipeline) { + if let Some(ref mut pipeline) = svx_pipeline { let resources = pipeline.resources.as_ref().unwrap(); let node_requests_buffer_slice = resources.readable_node_requests_buffer.slice(..); @@ -250,40 +232,6 @@ pub(crate) fn handle_gpu_readback( } resources.readable_metadata_buffer.unmap(); } - - // +++ DEBUG +++ - // Data updates triggered by debug interface - // let data_handler = tree_gpu_view.data_handler.lock().unwrap(); - // if tree_gpu_view.do_the_thing { - // let buffer_slice = resources.readable_debug_gpu_interface.slice(..); - // let (s, r) = crossbeam::channel::unbounded::<()>(); - // buffer_slice.map_async( - // bevy::render::render_resource::MapMode::Read, - // move |d| match d { - // Ok(_) => s.send(()).expect("Failed to send map update"), - // Err(err) => panic!("Couldn't map debug interface buffer!: {err}"), - // }, - // ); - - // render_device - // .poll(bevy::render::render_resource::Maintain::wait()) - // .panic_on_timeout(); - - // r.recv().expect("Failed to receive the map_async message"); - // { - // let buffer_view = buffer_slice.get_mapped_range(); - - // let data = buffer_view - // .chunks(std::mem::size_of::()) - // .map(|chunk| u32::from_ne_bytes(chunk.try_into().expect("should be a u32"))) - // .collect::>(); - // // println!("received_data: {:?}", data); - // } - // resources.readable_debug_gpu_interface.unmap(); - // std::mem::drop(data_handler); - // tree_gpu_view.do_the_thing = false; - // } - // --- DEBUG --- } } @@ -307,6 +255,8 @@ pub(crate) fn handle_gpu_readback( // ░░░ ░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ //############################################################################## +/// Converts the given array to `&[u8]` on the given range, +/// and schedules it to be written to the given buffer in the GPU fn write_range_to_buffer( array: &Vec, range: std::ops::Range, @@ -332,6 +282,7 @@ fn write_range_to_buffer( } } +/// Handles Data Streaming to the GPU based on incoming requests from the view(s) pub(crate) fn write_to_gpu( tree_gpu_host: Option>>, svx_pipeline: Option>, @@ -479,7 +430,6 @@ pub(crate) fn write_to_gpu( brick_index as usize * (DIM * DIM * DIM) + (DIM * DIM * DIM), ); } - } } NodeContent::Leaf(bricks) => { @@ -611,18 +561,5 @@ pub(crate) fn write_to_gpu( &render_queue, ); } - - /*// +++ DEBUG +++ - // Data updates triggered by debug interface - if tree_gpu_host.views[0].do_the_thing { - // GPU buffer Write - let data_buffer = StorageBuffer::new(vec![0, 0, 0, 1]); - render_queue.write_buffer( - &pipeline.resources.as_ref().unwrap().debug_gpu_interface, - 0, - &data_buffer.into_inner(), - ); - } - */// --- DEBUG --- } } diff --git a/src/octree/raytracing/bevy/pipeline.rs b/src/octree/raytracing/bevy/pipeline.rs index d718717..0c1fe4d 100644 --- a/src/octree/raytracing/bevy/pipeline.rs +++ b/src/octree/raytracing/bevy/pipeline.rs @@ -138,16 +138,6 @@ impl render_graph::Node for SvxRenderNode { (std::mem::size_of_val(¤t_view.spyglass.node_requests[0]) * current_view.spyglass.node_requests.len()) as u64, ); - - // +++ DEBUG +++ - command_encoder.copy_buffer_to_buffer( - &resources.debug_gpu_interface, - 0, - &resources.readable_debug_gpu_interface, - 0, - std::mem::size_of::() as u64, - ) - // --- DEBUG --- } Ok(()) } @@ -179,6 +169,7 @@ impl render_graph::Node for SvxRenderNode { // ░░█████████ █████ █████ ░░░███████░ ░░████████ █████ ░░█████████ // ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░░ //############################################################################## +/// Constructs buffers, bing groups and uploads rendering data at initialization and whenever prompted pub(crate) fn prepare_bind_groups( gpu_images: Res>, render_device: Res, @@ -258,20 +249,6 @@ pub(crate) fn prepare_bind_groups( usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, }); - // +++ DEBUG +++ - let buffer = UniformBuffer::new(vec![0u8; 4]); - let debug_gpu_interface = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("Octree Debug Buffer"), - contents: &buffer.into_inner(), - usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, - }); - let readable_debug_gpu_interface = render_device.create_buffer(&BufferDescriptor { - mapped_at_creation: false, - size: 4, - label: Some("Octree Debug interface Buffer"), - usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, - }); - // --- DEBUG --- let mut buffer = UniformBuffer::new(Vec::::new()); buffer.write(&render_data.octree_meta).unwrap(); let octree_meta_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { @@ -349,12 +326,6 @@ pub(crate) fn prepare_bind_groups( binding: 5, resource: color_palette_buffer.as_entire_binding(), }, - // +++ DEBUG +++ - bevy::render::render_resource::BindGroupEntry { - binding: 6, - resource: debug_gpu_interface.as_entire_binding(), - }, - // --- DEBUG --- ], ); @@ -441,15 +412,12 @@ pub(crate) fn prepare_bind_groups( spyglass_bind_group, tree_bind_group, viewport_buffer, - octree_meta_buffer, metadata_buffer, node_children_buffer, node_ocbits_buffer, voxels_buffer, color_palette_buffer, - debug_gpu_interface, readable_node_requests_buffer, - readable_debug_gpu_interface, readable_metadata_buffer, }); } diff --git a/src/octree/raytracing/bevy/types.rs b/src/octree/raytracing/bevy/types.rs index 3f7ffdf..5841190 100644 --- a/src/octree/raytracing/bevy/types.rs +++ b/src/octree/raytracing/bevy/types.rs @@ -46,7 +46,7 @@ where T: Default + Clone + PartialEq + VoxelData + Send + Sync + 'static, { pub(crate) dummy: std::marker::PhantomData, - pub resolution: [u32; 2], + pub(crate) resolution: [u32; 2], } #[derive(Resource, Clone, TypePath, ExtractResource)] @@ -64,19 +64,14 @@ pub struct SvxViewSet { pub views: Vec>>, } -#[derive(Resource, Clone, AsBindGroup, TypePath)] -#[type_path = "shocovox::gpu::OctreeGPUView"] +#[derive(Resource, Clone, AsBindGroup)] pub struct OctreeGPUView { - // +++ DEBUG +++ - pub do_the_thing: bool, - // --- DEBUG --- pub spyglass: OctreeSpyGlass, pub(crate) data_handler: OctreeGPUDataHandler, } #[derive(Debug, Clone)] pub(crate) struct VictimPointer { - //TODO: most fields should be private to type pub(crate) max_meta_len: usize, pub(crate) loop_count: usize, pub(crate) stored_items: usize, @@ -90,8 +85,7 @@ pub(crate) enum BrickOwnedBy { Node(u32, u8), } -#[derive(Resource, Clone, AsBindGroup, TypePath)] -#[type_path = "shocovox::gpu::OctreeGPUDataHandler"] +#[derive(Resource, Clone, AsBindGroup)] pub struct OctreeGPUDataHandler { pub(crate) render_data: OctreeRenderData, pub(crate) victim_node: VictimPointer, @@ -99,6 +93,7 @@ pub struct OctreeGPUDataHandler { pub(crate) node_key_vs_meta_index: BiHashMap, pub(crate) map_to_color_index_in_palette: HashMap, pub(crate) brick_ownership: Vec, + pub(crate) map_to_brick_maybe_owned_by_node: HashMap<(usize, u8), usize>, pub(crate) uploaded_color_palette_size: usize, } @@ -111,22 +106,18 @@ pub(crate) struct OctreeRenderDataResources { // Octree render data group pub(crate) tree_bind_group: BindGroup, - pub(crate) octree_meta_buffer: Buffer, pub(crate) metadata_buffer: Buffer, pub(crate) node_children_buffer: Buffer, pub(crate) node_ocbits_buffer: Buffer, pub(crate) voxels_buffer: Buffer, pub(crate) color_palette_buffer: Buffer, - pub(crate) debug_gpu_interface: Buffer, // Staging buffers for data reads pub(crate) readable_node_requests_buffer: Buffer, pub(crate) readable_metadata_buffer: Buffer, - pub(crate) readable_debug_gpu_interface: Buffer, } -#[derive(Clone, AsBindGroup, TypePath)] -#[type_path = "shocovox::gpu::ShocoVoxViewingGlass"] +#[derive(Clone, AsBindGroup)] pub struct OctreeSpyGlass { #[storage_texture(0, image_format = Rgba8Unorm, access = ReadWrite)] pub output_texture: Handle, @@ -141,10 +132,6 @@ pub struct OctreeSpyGlass { #[derive(Clone, AsBindGroup, TypePath)] #[type_path = "shocovox::gpu::ShocoVoxRenderData"] pub struct OctreeRenderData { - // +++ DEBUG +++ - #[storage(6, visibility(compute))] - pub(crate) debug_gpu_interface: u32, - // --- DEBUG --- /// Contains the properties of the Octree #[uniform(0, visibility(compute))] pub(crate) octree_meta: OctreeMetaData, @@ -196,7 +183,7 @@ pub struct OctreeRenderData { /// index of where the voxel brick start inside the @voxels buffer. /// Leaf node might contain 1 or 8 bricks according to @sized_node_meta, while #[storage(2, visibility(compute))] - pub node_children: Vec, + pub(crate) node_children: Vec, /// Buffer of Node occupancy bitmaps. Each node has a 64 bit bitmap, /// which is stored in 2 * u32 values diff --git a/src/octree/raytracing/raytracing_on_cpu.rs b/src/octree/raytracing/raytracing_on_cpu.rs index 2ef673f..b1e5e3a 100644 --- a/src/octree/raytracing/raytracing_on_cpu.rs +++ b/src/octree/raytracing/raytracing_on_cpu.rs @@ -8,7 +8,7 @@ use crate::{ BITMAP_INDEX_LUT, BITMAP_MASK_FOR_OCTANT_LUT, OOB_OCTANT, RAY_TO_NODE_OCCUPANCY_BITMASK_LUT, }, - math::{hash_direction, hash_region, position_in_bitmap_64bits, BITMAP_DIMENSION}, + math::{hash_direction, hash_region, BITMAP_DIMENSION}, raytracing::{cube_impact_normal, step_octant, Ray, FLOAT_ERROR_TOLERANCE}, }, }; diff --git a/src/octree/types.rs b/src/octree/types.rs index e5611f7..ebebfdd 100644 --- a/src/octree/types.rs +++ b/src/octree/types.rs @@ -4,9 +4,6 @@ use std::error::Error; #[cfg(feature = "serialization")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "bevy_wgpu")] -use {crate::octree::raytracing::bevy::types::OctreeGPUDataHandler, std::sync::Arc}; - #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub(crate) enum BrickData From f4fb98ff2eeebaea1b3c3e336db4a96b433c52c4 Mon Sep 17 00:00:00 2001 From: davids91 Date: Sun, 8 Dec 2024 18:00:21 +0100 Subject: [PATCH 19/19] Updated Readme --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 78374ed..088ed7f 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,18 @@ Roadmap: Issue spotlight: - -These are the issues I will work on until 2025 Q1. I am eliminating them in a sequential manner. -- #54 - Occupied bitmap structure update - Restructure of the per-node and per-brick occupied bitmaps to be more efficient(gotta go fast) -- #56 - Rework user data handling - Trimming the fat in Voxel storage, broadening the possibilities with user data -- #45 - GPU cache - To make it possible to display octrees of limitless size on the GPU by streaming only what one can see +These are the issues I will work on until 2025 Q2. I am eliminating them in a sequential manner. +- #56 - Introduce Palettes - Trimming the fat in Voxel storage, broadening the possibilities with user data and eliminating some data conversion overhead with bricks. +- #65 - Flatten brick storage: trimming some additional overhead, and eliminating some possible techDebt (`DIM` generic argument with Octree) - #3 - to make it possible to have a limitless octree: so it's not bound by the RAM size anymore +- #17 Beam Optimization - Pre-render a small resolution image to optimally initialize ray distances, and help with deciding which bricks to load pre-emptively. GOTTA GO FAST - #28, #6 - Level of Detail implementation to render large scenes more efficiently +If you feel adventurous: +- + +I have marked some issues with the help needed flag, which I think would be a good addition to the library, but I can not focus on it as I am but a single person with limited time and resources. Feel free to try to tackle any of the marked issues (Or basically anything you'd like), I will provide any needed help and support if I can. + Special thanks to contributors and supporters! -