#![feature(generic_const_exprs)] #![feature(float_algebraic)] use std::collections::HashMap; use std::sync::Arc; use std::sync::mpsc::sync_channel; use bytemuck::Pod; use bytemuck::Zeroable; use bytemuck::cast_slice; use crevice::std140::AsStd140; use egui::Color32; use egui::mutex::Mutex; use egui_plot::BarChart; use glam::Mat4; use glam::Vec3; use glam::Vec4; use itertools::Itertools; use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use wgpu::Buffer; use wgpu::BufferUsages; use wgpu::Device; use wgpu::Extent3d; use wgpu::Features; use wgpu::InstanceDescriptor; use wgpu::InstanceFlags; use wgpu::MemoryBudgetThresholds; use wgpu::Operations; use wgpu::RenderPipeline; use wgpu::Texture; use wgpu::TextureUsages; use wgpu::TextureView; use wgpu::include_spirv; use wgpu::include_wgsl; use wgpu::util::BufferInitDescriptor; use wgpu::util::DeviceExt; use wgpu::util::DownloadBuffer; use winit::application::ApplicationHandler; use winit::event::DeviceEvent; use winit::event::MouseScrollDelta; use winit::event::WindowEvent; use winit::event_loop; use winit::event_loop::ActiveEventLoop; use winit::event_loop::ControlFlow; use winit::event_loop::EventLoop; use winit::event_loop::OwnedDisplayHandle; use winit::platform::x11::EventLoopBuilderExtX11; use winit::window::Window; use winit::window::WindowId; use crate::camera::Camera; use crate::egui_renderer::EguiRenderer; use crate::producers::ChunkedProducer; use crate::producers::SineGenerator; use crate::producers::TerrainGenerator; use crate::sparse_tree::NTreeNodeLocator; use crate::voxel_cache::VoxelCache; use crate::voxel_cache::data::CacheNodeRequest; use crate::voxel_cache::data::CacheResponse; use crate::voxel_cache::data::ColorBytes; use crate::voxel_cache::data::DestinationElement; use crate::voxel_cache::data::LocationPoolElement; use crate::voxel_cache::data::StructurePoolElement; use crate::voxel_cache::producer_interface::CacheProducerInterface; use crate::voxel_cache::producer_interface::CacheRequest; mod camera; mod egui_renderer; mod producers; mod sparse_tree; mod voxel_cache; // struct State { instance: wgpu::Instance, window: Arc, device: wgpu::Device, queue: wgpu::Queue, size: winit::dpi::PhysicalSize, surface: wgpu::Surface<'static>, depth_buffer: (wgpu::Texture, wgpu::TextureView), surface_format: wgpu::TextureFormat, egui_renderer: EguiRenderer, pipeline: RenderPipeline, voxel_cache: Arc>>, cache_interface: Arc>, terrain_generator: Arc>, chunk_pos_map: Arc>, instance_buffer: Buffer, instance_count: usize, usage_vec: Arc>>, rm_time: Arc>, insertion_debounce: bool, camera: Camera, } pub unsafe fn as_raw_bytes(slice: &[T]) -> &[u8] { let size = slice.len() * size_of::(); unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const u8, size) } } #[derive(Clone, Copy)] struct Immediates { view_proj: Mat4, cam_pos: Vec3, frame_timestamp: u32, } #[derive(Debug, Clone, Copy, Zeroable, Pod)] #[repr(C)] struct InstanceAttribute { x: f32, y: f32, z: f32, id: u32, } impl State { async fn new(display: OwnedDisplayHandle, window: Arc) -> State { let instance = wgpu::Instance::new(InstanceDescriptor { backends: wgpu::Backends::VULKAN, display: Some(Box::new(display)), flags: InstanceFlags::default(), memory_budget_thresholds: MemoryBudgetThresholds::default(), backend_options: Default::default(), }); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions::default()) .await .unwrap(); let (device, queue) = adapter .request_device(&wgpu::DeviceDescriptor { required_features: Features::IMMEDIATES | Features::SHADER_EARLY_DEPTH_TEST | Features::TIMESTAMP_QUERY | Features::SHADER_INT64, required_limits: wgpu::Limits { max_immediate_size: 112, max_storage_buffers_per_shader_stage: 16, ..Default::default() }, ..Default::default() }) .await .unwrap(); let size = window.inner_size(); let surface = instance.create_surface(window.clone()).unwrap(); let cap = surface.get_capabilities(&adapter); let surface_format = cap.formats[0]; let egui_renderer = EguiRenderer::new(&device, surface_format, &window); let mut voxel_cache = VoxelCache::<4>::new(100_000, device.clone(), queue.clone()); let cache_interface = CacheProducerInterface::new(1024, &device); //let terrain_generator = TerrainGenerator::<4>::new(5, "vxls_height.tif", 0.2, "img.jpg"); // let terrain_generator = TerrainGenerator::<4>::new( // 5, // "./pointe_percee/height.tif", // 0.2, // "./pointe_percee/ortho.jpg", // ); let terrain_generator = TerrainGenerator::<4>::new( 5, "/home/albin/Documents/vxls_maps/lapiz/height.tif", 0.2, "/home/albin/Documents/vxls_maps/lapiz/ortho.jpg", ); let mut chunk_pos_map = HashMap::new(); let chunk_instances = (0..terrain_generator.chunk_width) .cartesian_product(0..terrain_generator.chunk_height) .cartesian_product(0..terrain_generator.chunk_alt) .map(|((x, z), y)| { let chunk_id = voxel_cache.structure_table.allocate_structure(true); //dbg!(chunk_id); chunk_pos_map.insert(chunk_id, (x, y, z)); InstanceAttribute { x: x as f32, y: y as f32, z: z as f32, id: chunk_id, } }) .collect::>(); let instance_count = chunk_instances.len(); let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Instance buffer"), contents: bytemuck::cast_slice(chunk_instances.as_slice()), usage: BufferUsages::COPY_DST | BufferUsages::VERTEX, }); // let shader_module = unsafe { // device.create_shader_module_trusted( // wgpu::ShaderModuleDescriptor { // label: Some("Main shader module"), // source: wgpu::ShaderSource::Wgsl( // std::fs::read_to_string("shaders/voxel.wgsl") // .unwrap() // .into(), // ), // }, // wgpu::ShaderRuntimeChecks { // bounds_checks: false, // force_loop_bounding: false, // ray_query_initialization_tracking: false, // task_shader_dispatch_tracking: false, // mesh_shader_primitive_indices_clamp: false, // int_div_checks: false, // }, // ) // }; //let shader_module = device.create_shader_module(include_wgsl!("../shaders/voxel.wgsl")); let shader_module = device.create_shader_module(include_spirv!("../shaders/voxel.spv")); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Voxel pipeline layout"), bind_group_layouts: &[Some(&voxel_cache.bind_group_layout())], immediate_size: size_of::() as u32, }); let chunk_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render pipeline"), layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: &shader_module, entry_point: Some("chunk"), compilation_options: Default::default(), buffers: &[Some(wgpu::VertexBufferLayout { array_stride: (size_of::() * 3 + size_of::()) as u64, step_mode: wgpu::VertexStepMode::Instance, attributes: &[ wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x3, offset: 0, shader_location: 0, }, wgpu::VertexAttribute { format: wgpu::VertexFormat::Uint32, offset: (size_of::() * 3) as u64, shader_location: 1, }, ], })], }, primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, front_face: wgpu::FrontFace::Ccw, cull_mode: Some(wgpu::Face::Front), unclipped_depth: false, polygon_mode: wgpu::PolygonMode::Fill, conservative: false, }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, depth_write_enabled: Some(true), depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: wgpu::StencilState::default(), bias: wgpu::DepthBiasState::default(), }), multisample: wgpu::MultisampleState::default(), fragment: Some(wgpu::FragmentState { module: &shader_module, entry_point: Some("fragment"), compilation_options: wgpu::PipelineCompilationOptions::default(), targets: &[Some(wgpu::ColorTargetState { format: surface_format, blend: None, write_mask: wgpu::ColorWrites::default(), })], }), multiview_mask: None, cache: None, }); let state = State { instance, window, size, surface, surface_format, egui_renderer, depth_buffer: Self::create_depth_buffer(&device, size.width, size.height), queue, device, usage_vec: Arc::new(Mutex::new(vec![])), pipeline: chunk_pipeline, voxel_cache: Arc::new(Mutex::new(voxel_cache)), cache_interface: cache_interface.into(), insertion_debounce: false, camera: Default::default(), instance_buffer, instance_count, terrain_generator: Arc::new(terrain_generator), chunk_pos_map: chunk_pos_map.into(), rm_time: Arc::new(Mutex::new(0.)), }; // Configure surface for the first time state.configure_surface(); state } fn get_window(&self) -> &Window { &self.window } fn handle_event(&mut self, event: &WindowEvent) { self.egui_renderer.handle_input(&self.window, event); self.camera.handle_input(event); } fn handle_device_event(&mut self, event: &DeviceEvent) { #[allow(clippy::single_match)] match event { winit::event::DeviceEvent::MouseMotion { delta } => { self.camera.cursor_moved(delta.0 as f32, delta.1 as f32); } _ => {} } } fn create_depth_buffer(device: &Device, width: u32, height: u32) -> (Texture, TextureView) { let texture = device.create_texture(&wgpu::wgt::TextureDescriptor { label: Some("Depth buffer"), size: Extent3d { width, height, depth_or_array_layers: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Depth24PlusStencil8, usage: TextureUsages::RENDER_ATTACHMENT, view_formats: &[wgpu::TextureFormat::Depth24PlusStencil8.add_srgb_suffix()], }); let texture_view = texture.create_view(&wgpu::wgt::TextureViewDescriptor { label: Some("depth view"), ..Default::default() }); (texture, texture_view) } fn configure_surface(&self) { let surface_config = wgpu::SurfaceConfiguration { color_space: wgpu::SurfaceColorSpace::Auto, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: self.surface_format, // Request compatibility with the sRGB-format texture view we‘re going to create later. view_formats: vec![self.surface_format.add_srgb_suffix()], alpha_mode: wgpu::CompositeAlphaMode::Auto, width: self.size.width, height: self.size.height, desired_maximum_frame_latency: 2, present_mode: wgpu::PresentMode::AutoVsync, }; self.surface.configure(&self.device, &surface_config); } fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { self.size = new_size; self.camera.aspect = new_size.width as f32 / new_size.height as f32; // reconfigure the surface self.configure_surface(); self.depth_buffer = Self::create_depth_buffer(&self.device, new_size.width, new_size.height); } fn render(&mut self) { self.camera.update(); self.voxel_cache.lock().next_frame(); // Create texture view. // NOTE: We must handle Timeout because the surface may be unavailable // (e.g., when the window is occluded on macOS). // ~~ Texture view creation ~~ let surface_texture = match self.surface.get_current_texture() { wgpu::CurrentSurfaceTexture::Success(texture) => texture, wgpu::CurrentSurfaceTexture::Occluded | wgpu::CurrentSurfaceTexture::Timeout => return, wgpu::CurrentSurfaceTexture::Suboptimal(texture) => { drop(texture); self.configure_surface(); return; } wgpu::CurrentSurfaceTexture::Outdated => { self.configure_surface(); return; } wgpu::CurrentSurfaceTexture::Validation => { unreachable!("No error scope registered, so validation errors will panic") } wgpu::CurrentSurfaceTexture::Lost => { self.surface = self.instance.create_surface(self.window.clone()).unwrap(); self.configure_surface(); return; } }; let texture_view = surface_texture .texture .create_view(&wgpu::TextureViewDescriptor { // Without add_srgb_suffix() the image we will be working with // might not be "gamma correct". format: Some(self.surface_format.add_srgb_suffix()), ..Default::default() }); // ~~ Ray-marching timestamp query setup ~~ let timestamp_query = self.device.create_query_set(&wgpu::QuerySetDescriptor { label: Some("timestamp_query_set"), ty: wgpu::QueryType::Timestamp, count: 2, }); let timestamp_buffer = self.device.create_buffer(&wgpu::BufferDescriptor { label: Some("timestamp_buffer"), size: (size_of::() * 2) as u64, usage: BufferUsages::QUERY_RESOLVE | BufferUsages::COPY_SRC, mapped_at_creation: false, }); // ~~ Main render pass ~~ let mut encoder = self.device.create_command_encoder(&Default::default()); { let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &texture_view, depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 0., g: 2. / 255., b: 15. / 255., a: 1., }), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { view: &self.depth_buffer.1, depth_ops: Some(Operations { load: wgpu::LoadOp::Clear(1.), store: wgpu::StoreOp::Discard, }), stencil_ops: None, }), timestamp_writes: Some(wgpu::RenderPassTimestampWrites { query_set: ×tamp_query, beginning_of_pass_write_index: Some(0), end_of_pass_write_index: Some(1), }), occlusion_query_set: None, multiview_mask: None, }); renderpass.set_pipeline(&self.pipeline); renderpass.set_vertex_buffer(0, self.instance_buffer.slice(..)); renderpass.set_bind_group(0, Some(&self.voxel_cache.lock().bind_group()), &[]); let imm = [Immediates { view_proj: self.camera.view_proj(), cam_pos: self.camera.position, frame_timestamp: self.voxel_cache.lock().current_timestamp(), }]; renderpass.set_immediates(0, unsafe { as_raw_bytes(&imm) }); renderpass.draw(0..36, 0..(self.instance_count as u32)); // End the renderpass. drop(renderpass); encoder.resolve_query_set(×tamp_query, 0..2, ×tamp_buffer, 0); } // ~~ EGUI Render pass ~~ { self.egui_renderer.begin_frame(&self.window); egui::Window::new("Window ! ").resizable(true).show( self.egui_renderer.context(), |ui| { if self .camera .pressed_keyset .contains(&winit::keyboard::KeyCode::KeyF) { ui.label( egui::RichText::new("Cache paused") .color(Color32::RED) .size(28.), ); } ui.label(format!( "Ray-marching time: {}", *self.rm_time.lock() / 1_000_000. )); egui_plot::Plot::new("Plot").show(ui, |plot_ui| { plot_ui.bar_chart(BarChart::new( "histo", self.usage_vec .lock() .iter() .enumerate() .map(|(i, x)| egui_plot::Bar::new(i as f64, *x as f64)) .collect(), )); }); ui.label("Hello !") }, ); let screen_descriptor = egui_wgpu::ScreenDescriptor { size_in_pixels: [self.size.width, self.size.height], pixels_per_point: self.window.as_ref().scale_factor() as f32, }; self.egui_renderer.end_frame_and_draw( &self.device, &self.queue, &mut encoder, &self.window, &texture_view, screen_descriptor, ); } // ~~ Build usage buffer histogram let time_stamp = self.voxel_cache.lock().current_timestamp(); let cloned_usage_histogram = self.usage_vec.clone(); DownloadBuffer::read_buffer( &self.device, &self.queue, &self.voxel_cache.lock().usage_buffer.usage_buffer.slice(..), move |slice| { let slice = slice.unwrap(); let cache_slice: &[u32] = bytemuck::cast_slice(&slice); let mut v = vec![0; 100]; for x in cache_slice.iter() { let diff = (time_stamp - x).min(99); v[diff as usize] += 1; } *cloned_usage_histogram.lock() = v; }, ); // ~~ Do cache managment if !self .camera .pressed_keyset .contains(&winit::keyboard::KeyCode::KeyF) { self.voxel_cache .lock() .cache_post_render(&mut encoder, &self.cache_interface); } // ~~ Submit command buffer ~~ self.queue.submit([encoder.finish()]); self.window.pre_present_notify(); self.queue.present(surface_texture); // ~~ Get Ray-marching timestamps, report time ~~ let cloned_rm_time = self.rm_time.clone(); let cloned_queue = self.queue.clone(); DownloadBuffer::read_buffer( &self.device, &self.queue, ×tamp_buffer.slice(..), move |buffer| { let buffer_slice = buffer.unwrap(); let slice: &[u64] = cast_slice(&buffer_slice); let time = (slice[1] - slice[0]) as f32 * cloned_queue.get_timestamp_period(); *cloned_rm_time.lock() = time; }, ); // ~~ Do cache managment if !self .camera .pressed_keyset .contains(&winit::keyboard::KeyCode::KeyF) { self.insertion_debounce = true; let request_count = self .cache_interface .total_request_count(&self.device, &self.queue); let cloned_cache = self.voxel_cache.clone(); let cloned_device = self.device.clone(); let cloned_queue = self.queue.clone(); let cloned_generator = self.terrain_generator.clone(); let cloned_map = self.chunk_pos_map.clone(); let cloned_cache_interface = self.cache_interface.clone(); let (tx, rx) = sync_channel(1); // ~~ Download request buffer, fullfill requests, writeback to cache ~~ DownloadBuffer::read_buffer( &self.device.clone(), &self.queue.clone(), &self.cache_interface.requests_buffer().slice(..), move |buffer| { if tx.try_send(()).is_err() { return; } let cache_node_requests: Vec = bytemuck::pod_collect_to_vec(&buffer.unwrap()); let generator = cloned_generator; let gen_test = SineGenerator::<4>::new(5); let mut structure_nodes: Vec> = vec![]; let mut color_nodes: Vec<[ColorBytes; 64]> = vec![]; let mut location_nodes = vec![]; cache_node_requests .par_iter() .take(request_count as usize) .map(|request| { // Unpack structure id let (chunk_x, chunk_y, chunk_z) = *cloned_map.get(&request.structure_id).unwrap(); let location; let node; if request.locator == 0 { // Produce root node node = generator.produce_node(0, 0, 0, 0, (chunk_x, chunk_y, chunk_z)); location = LocationPoolElement { structure_id: request.structure_id, structure_locator: NTreeNodeLocator::<4>::root().as_usize() as u32, }; } else { // Figure out depth of request let locator = NTreeNodeLocator::<4>::from_usize(request.locator as usize); let depth = locator.depth(); //let (x, y, z) = locator.node_location(); let child_z = request.child_index / (4 * 4); let child_y = (request.child_index % (4 * 4)) / 4; let child_x = request.child_index % 4; let child_locator = locator.child( child_x as usize, child_y as usize, child_z as usize, ); let (nx, ny, nz) = child_locator.node_location(); node = generator.produce_node( depth + 1, nx, ny, nz, (chunk_x, chunk_y, chunk_z), ); location = LocationPoolElement { structure_id: request.structure_id, structure_locator: child_locator.as_usize() as u32, }; } ( StructurePoolElement { occupancy_low: 0, occupancy_high: 0, pointers: node.structure, }, node.colors, location, ) }) .collect::>() .into_iter() .for_each(|data| { structure_nodes.push(data.0); color_nodes.push(std::array::from_fn(|i| data.1[i].into())); location_nodes.push(data.2); }); if structure_nodes.len() != 0 { cloned_queue.write_buffer( cloned_cache_interface.structure_nodes_buffer(), 0, unsafe { as_raw_bytes(structure_nodes.as_slice()) }, ); cloned_queue.write_buffer( cloned_cache_interface.color_nodes_buffer(), 0, unsafe { as_raw_bytes(&color_nodes.as_slice()) }, ); cloned_queue.write_buffer( cloned_cache_interface.location_nodes_buffer(), 0, unsafe { as_raw_bytes(&&location_nodes.as_slice()) }, ); let mut encoder = cloned_device.create_command_encoder(&Default::default()); cloned_cache .lock() .cache_insert(&mut encoder, &cloned_cache_interface); cloned_queue.submit([encoder.finish()]); } }, ); //println!("Total request count: {}", request_count); loop { if rx.try_recv().is_ok() { break; } let _ = self.device.poll(wgpu::wgt::PollType::Poll); } drop(rx); } } pub fn mouse_wheel(&mut self, delta: MouseScrollDelta) { if let MouseScrollDelta::LineDelta(_, y) = delta { self.camera.speed += y * (self.camera.speed * 0.05); } } } #[derive(Default)] struct App { state: Option, } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { // Create window object let window = Arc::new( event_loop .create_window(Window::default_attributes()) .unwrap(), ); let state = pollster::block_on(State::new( event_loop.owned_display_handle(), window.clone(), )); self.state = Some(state); window.request_redraw(); window.set_cursor_visible(false); // window // .set_cursor_grab(winit::window::CursorGrabMode::Locked) // .unwrap(); let _ = window .set_cursor_grab(winit::window::CursorGrabMode::Locked) .or_else(|_| window.set_cursor_grab(winit::window::CursorGrabMode::Confined)); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { let state = self.state.as_mut().unwrap(); state.handle_event(&event); match event { WindowEvent::CloseRequested => { println!("The close button was pressed; stopping"); event_loop.exit(); } WindowEvent::RedrawRequested => { state.render(); // Emits a new redraw requested event. state.get_window().request_redraw(); } WindowEvent::Resized(size) => { // Reconfigures the size of the surface. We do not re-render // here as this event is always followed up by redraw request. state.resize(size); } WindowEvent::MouseWheel { delta, .. } => { state.mouse_wheel(delta); } _ => (), } } fn device_event( &mut self, _event_loop: &event_loop::ActiveEventLoop, _device_id: winit::event::DeviceId, event: winit::event::DeviceEvent, ) { let state = self.state.as_mut().unwrap(); state.handle_device_event(&event); } } fn main() { // wgpu uses `log` for all of our logging, so we initialize a logger with the `env_logger` crate. // // To change the log level, set the `RUST_LOG` environment variable. See the `env_logger` // documentation for more information. env_logger::init(); let event_loop = EventLoop::builder().build().unwrap(); //let event_loop = EventLoop::builder().with_x11().build().unwrap(); // When the current loop iteration finishes, immediately begin a new // iteration regardless of whether or not new events are available to // process. Preferred for applications that want to render as fast as // possible, like games. event_loop.set_control_flow(ControlFlow::Poll); // When the current loop iteration finishes, suspend the thread until // another event arrives. Helps keeping CPU utilization low if nothing // is happening, which is preferred if the application might be idling in // the background. // event_loop.set_control_flow(ControlFlow::Wait); let mut app = App::default(); event_loop.run_app(&mut app).unwrap(); } #[derive(AsStd140)] pub struct ChunkImmediate { mvp: Mat4, eye_pos: Vec3, root_color: Vec4, root_subdiv: bool, frame_timestamp: u32, }