From d1f76fe9f015f7b7aacf7bd30bdd29ee19e6a6d5 Mon Sep 17 00:00:00 2001 From: Albin Chaboissier Date: Sun, 13 Sep 2026 23:06:00 +0200 Subject: [PATCH] WTF --- shaders/voxel.slang | 124 ++++++++++++++++++++++++++++++++++---- shaders/voxel.spv | Bin 13568 -> 23904 bytes src/main.rs | 144 +++++++++++++++++++++++++++++++++++++++++--- src/voxel_cache.rs | 14 ++--- 4 files changed, 253 insertions(+), 29 deletions(-) diff --git a/shaders/voxel.slang b/shaders/voxel.slang index 6d4c36c..7cf7cc8 100644 --- a/shaders/voxel.slang +++ b/shaders/voxel.slang @@ -3,6 +3,12 @@ struct PushConstants float4x4 view_proj; float3 cam_pos; uint32_t frame_timestamp; + uint32_t width; + uint32_t height; + + uint32_t chunk_width; + uint32_t chunk_height; + uint32_t chunk_alt; } public struct VertexOutput @@ -180,7 +186,14 @@ float3 floor_scale(float3 position, uint32_t scale_exp) return asfloat(asuint(position) & mask); } -float4 ray_march(float3 ray_direction, float3 ray_origin, uint32_t root_id, float dist_offset, out float3 hit_pos) +struct HitInformation +{ + bool hit; + float3 hit_pos; + float4 color; +} + +HitInformation ray_march(float3 ray_direction, float3 ray_origin, uint32_t root_id, float dist_offset) { float fov_deg = 100. / 1920.; float fov_rad = (float.getPi() * fov_deg) / 180.; @@ -189,14 +202,18 @@ float4 ray_march(float3 ray_direction, float3 ray_origin, uint32_t root_id, floa let st_pointer = structure_table_pointer[root_id]; if(!st_pointer.subdivided()) { - discard; + var hit: HitInformation; + hit.hit = false; + return hit; } if(!st_pointer.pointer_valid()) { // Record request structure_table_request_buffer[root_id].add(1); - discard; + var hit: HitInformation; + hit.hit = false; + return hit; } ray_origin += float3(1.); @@ -259,8 +276,13 @@ float4 ray_march(float3 ray_direction, float3 ray_origin, uint32_t root_id, floa if(color_pool[current_node_index].colors[child_index].byte_a != 0) { - hit_pos = pos - float3(1.); - return color_pool[current_node_index].colors[child_index].float_color; + //hit_pos = pos - float3(1.); + //return color_pool[current_node_index].colors[child_index].float_color; + var hit: HitInformation; + hit.hit = true; + hit.hit_pos = pos - float3(1.); + hit.color = color_pool[current_node_index].colors[child_index].float_color; + return hit; } //uint64_t occupancy = get_child_mask(structure_pool[current_node_index].occupancy_low, structure_pool[current_node_index].occupancy_high); @@ -301,7 +323,7 @@ float4 ray_march(float3 ray_direction, float3 ray_origin, uint32_t root_id, floa int32_t common_depth = (1 + (22 - firstbithigh(diff)) / 2) * 2; if(common_depth <= 0) { - discard; + break; } scale_exp = 23 - common_depth; @@ -309,8 +331,9 @@ float4 ray_march(float3 ray_direction, float3 ray_origin, uint32_t root_id, floa } - hit_pos = pos - float3(1.); - return float4(1., 0., 1., 1.); + var hit: HitInformation; + hit.hit = false; + return hit; } struct FragmentOutput @@ -327,19 +350,96 @@ FragmentOutput fragment(VertexOutput vertex_out) let intersection_t = box_intersect(vertex_out.cam_position, ray_direction, vertex_out.chunk_position, vertex_out.chunk_position + float3(1.)); let local_ray_origin = max(intersection_t.x, 0.) * ray_direction + vertex_out.cam_position - vertex_out.chunk_position; // Figure out intersection - var hit_pos : float3; - let color = ray_march(ray_direction, local_ray_origin, vertex_out.structure_id, max(0., intersection_t.x), hit_pos); - let world_hit_pos = hit_pos + vertex_out.chunk_position; + let hit = ray_march(ray_direction, local_ray_origin, vertex_out.structure_id, max(0., intersection_t.x)); + let world_hit_pos = hit.hit_pos + vertex_out.chunk_position; let clip = mul(constants.view_proj, float4(world_hit_pos, 1.)); let depth = clip.z / clip.w; var frag_out : FragmentOutput; frag_out.depth = depth; - frag_out.color = color; + frag_out.color = hit.color; return frag_out; } +bool3 min_mask(float3 val) +{ + let min_val = min(val.x, min(val.y, val.z)); + return val == min_val; +} + +[[vk::binding(0, 1)]] +[[format("rgba32f")]] +WTexture2D output_texture; + +[shader("compute")] +[numthreads(16, 16, 1)] +void ray_march_compute(uint32_t3 location : SV_DispatchThreadID) +{ + if(location.x >= constants.width || location.y >= constants.height) + { + return; + } + + uint32_t2 pixel_loc = uint32_t2(location.x, location.y); + + let ndc_loc_x = (float)location.x / (float)constants.width * 2. - 1.; + let ndc_loc_y = 1. - (float)location.y / (float)constants.height * 2.; + + var world_loc = mul(constants.view_proj, float4(ndc_loc_x, ndc_loc_y, 1., 1.)); + world_loc /= world_loc.w; + let ray_direction = normalize(world_loc.xyz - constants.cam_pos); + + var inter = box_intersect(constants.cam_pos, ray_direction, float3(0.), float3(constants.chunk_width, constants.chunk_alt, constants.chunk_height)); + if(inter.y <= inter.x || inter.y <= 0.) + { + output_texture.Store(pixel_loc, float4(0.)); + return; + } + inter.x = max(0., inter.x); + + + let start_position = inter.x * ray_direction + constants.cam_pos; + // FVT + int32_t3 current_voxel = clamp( + int32_t3(floor(start_position)), + int32_t3(0), + int32_t3(constants.chunk_width - 1, constants.chunk_alt - 1, constants.chunk_height - 1) + ); + int32_t3 offset = int32_t3(sign(ray_direction)); + float3 delta = abs(1. / ray_direction); + float3 position = start_position; + float3 t = select(ray_direction > 0., current_voxel + int32_t3(1) - start_position, start_position - current_voxel) * delta; + + var color = float4(0.); + var hit_pos = float3(0.); + for(int32_t i = 0; i < 256; i++) + { + let min_mask = min_mask(t); + let t_adv = select(min_mask, t, float3(0.)); + let t_ray = t_adv.x + t_adv.y + t_adv.z; + + + let chunk_index = current_voxel.y + current_voxel.z * constants.chunk_alt + current_voxel.x * constants.chunk_alt * constants.chunk_height; + let hit = ray_march(ray_direction, position - float3(current_voxel), chunk_index, length(position - constants.cam_pos)); + position = start_position + ray_direction * t_ray; + if(hit.hit) + { + color = hit.color; + break; + } + t += select(min_mask, delta, float3(0.)); + current_voxel += select(min_mask, offset, int32_t3(0)); + + if(any(current_voxel < 0) || any(current_voxel >= int32_t3(constants.chunk_width, constants.chunk_alt, constants.chunk_height))) + { + color = float4(0.); + break; + } + } + output_texture.Store(pixel_loc, float4(color)); +} + float2 box_intersect(float3 origin, float3 ray_direction, float3 box_min, float3 box_max) { let min_ts = (box_min - origin) / ray_direction; diff --git a/shaders/voxel.spv b/shaders/voxel.spv index 39858e8492d0924c4d2b91346ef84d4300e4f163..98e889eab1844c8d33e0dc8d8f4f435b7d6eee4d 100644 GIT binary patch literal 23904 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 Yu{Q?58LQ7fUk*x~cre&^B>t`C|AUMDSpWb4 diff --git a/src/main.rs b/src/main.rs index 8ee1163..aa26968 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #![feature(float_algebraic)] use std::collections::HashMap; +use std::ops::Div; use std::sync::Arc; use std::sync::mpsc::sync_channel; @@ -19,8 +20,10 @@ use itertools::Itertools; use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; +use wgpu::BindGroupLayout; use wgpu::Buffer; use wgpu::BufferUsages; +use wgpu::ComputePipeline; use wgpu::Device; use wgpu::Extent3d; use wgpu::Features; @@ -28,7 +31,9 @@ use wgpu::InstanceDescriptor; use wgpu::InstanceFlags; use wgpu::MemoryBudgetThresholds; use wgpu::Operations; +use wgpu::Origin3d; use wgpu::RenderPipeline; +use wgpu::ShaderStages; use wgpu::Texture; use wgpu::TextureUsages; use wgpu::TextureView; @@ -82,10 +87,14 @@ struct State size: winit::dpi::PhysicalSize, surface: wgpu::Surface<'static>, depth_buffer: (wgpu::Texture, wgpu::TextureView), + target_texture: (wgpu::Texture, wgpu::TextureView), surface_format: wgpu::TextureFormat, + target_blitter: wgpu::util::TextureBlitter, egui_renderer: EguiRenderer, - pipeline: RenderPipeline, + //pipeline: RenderPipeline, + pipeline: ComputePipeline, + surface_bg_layout: BindGroupLayout, voxel_cache: Arc>>, cache_interface: Arc>, terrain_generator: Arc>, @@ -111,6 +120,12 @@ struct Immediates view_proj: Mat4, cam_pos: Vec3, frame_timestamp: u32, + width: u32, + height: u32, + + chunk_width: u32, + chunk_height: u32, + chunk_alt: u32, } #[derive(Debug, Clone, Copy, Zeroable, Pod)] @@ -166,19 +181,19 @@ impl State 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, "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 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) @@ -202,7 +217,7 @@ impl State 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, + usage: BufferUsages::COPY_DST | BufferUsages::VERTEX | BufferUsages::STORAGE, }); // let shader_module = unsafe { @@ -229,13 +244,41 @@ impl State //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 surface_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("surface_bg_layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: wgpu::TextureFormat::Rgba32Float, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Voxel pipeline layout"), - bind_group_layouts: &[Some(&voxel_cache.bind_group_layout())], + bind_group_layouts: &[ + Some(&voxel_cache.bind_group_layout()), + Some(&surface_bind_group_layout), + ], immediate_size: size_of::() as u32, }); + let chunk_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("Compute render"), + layout: Some(&pipeline_layout), + module: &shader_module, + entry_point: Some("ray_march_compute"), + compilation_options: wgpu::PipelineCompilationOptions::default(), + cache: None, + }); + + /* let chunk_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render pipeline"), layout: Some(&pipeline_layout), @@ -290,6 +333,7 @@ impl State multiview_mask: None, cache: None, }); + */ let state = State { instance, @@ -299,8 +343,13 @@ impl State surface_format, egui_renderer, depth_buffer: Self::create_depth_buffer(&device, size.width, size.height), + target_texture: Self::create_target_texture(&device, size.width, size.height), + target_blitter: wgpu::util::TextureBlitterBuilder::new(&device, surface_format) + .sample_type(wgpu::FilterMode::Nearest) + .build(), queue, device, + surface_bg_layout: surface_bind_group_layout, usage_vec: Arc::new(Mutex::new(vec![])), pipeline: chunk_pipeline, voxel_cache: Arc::new(Mutex::new(voxel_cache)), @@ -320,6 +369,27 @@ impl State state } + fn create_target_texture(device: &Device, width: u32, height: u32) -> (Texture, TextureView) + { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Target texture"), + size: Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba32Float, + usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + + let texture_view = texture.create_view(&wgpu::wgt::TextureViewDescriptor::default()); + (texture, texture_view) + } + fn get_window(&self) -> &Window { &self.window @@ -395,6 +465,8 @@ impl State self.configure_surface(); self.depth_buffer = Self::create_depth_buffer(&self.device, new_size.width, new_size.height); + self.target_texture = + Self::create_target_texture(&self.device, new_size.width, new_size.height); } fn render(&mut self) @@ -442,6 +514,15 @@ impl State ..Default::default() }); + let surface_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("surface_bg"), + layout: &self.surface_bg_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&self.target_texture.1), + }], + }); + // ~~ Ray-marching timestamp query setup ~~ let timestamp_query = self.device.create_query_set(&wgpu::QuerySetDescriptor { label: Some("timestamp_query_set"), @@ -458,6 +539,48 @@ impl State // ~~ Main render pass ~~ let mut encoder = self.device.create_command_encoder(&Default::default()); + { + let mut renderpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("compute_render_pass"), + timestamp_writes: Some(wgpu::ComputePassTimestampWrites { + query_set: ×tamp_query, + beginning_of_pass_write_index: Some(0), + end_of_pass_write_index: Some(1), + }), + }); + + renderpass.set_pipeline(&self.pipeline); + renderpass.set_bind_group(0, Some(&self.voxel_cache.lock().bind_group()), &[]); + renderpass.set_bind_group(1, Some(&surface_bg), &[]); + let imm = [Immediates { + view_proj: self.camera.view_proj().inverse(), + cam_pos: self.camera.position, + frame_timestamp: self.voxel_cache.lock().current_timestamp(), + width: self.size.width, + height: self.size.height, + + chunk_width: self.terrain_generator.chunk_width as u32, + chunk_height: self.terrain_generator.chunk_height as u32, + chunk_alt: self.terrain_generator.chunk_alt as u32, + }]; + renderpass.set_immediates(0, unsafe { as_raw_bytes(&imm) }); + renderpass.dispatch_workgroups( + self.size.width.div_ceil(16), + self.size.height.div_ceil(16), + 1, + ); + drop(renderpass); + + encoder.resolve_query_set(×tamp_query, 0..2, ×tamp_buffer, 0); + } + + self.target_blitter.copy( + &self.device, + &mut encoder, + &self.target_texture.1, + &texture_view, + ); + /* { let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, @@ -508,6 +631,7 @@ impl State encoder.resolve_query_set(×tamp_query, 0..2, ×tamp_buffer, 0); } + */ // ~~ EGUI Render pass ~~ { diff --git a/src/voxel_cache.rs b/src/voxel_cache.rs index d2fa63c..22d31c5 100644 --- a/src/voxel_cache.rs +++ b/src/voxel_cache.rs @@ -95,7 +95,7 @@ where // Structure pool wgpu::BindGroupLayoutEntry { binding: 0, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, @@ -106,7 +106,7 @@ where // Color pool wgpu::BindGroupLayoutEntry { binding: 1, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, @@ -117,7 +117,7 @@ where // Location pool wgpu::BindGroupLayoutEntry { binding: 2, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, @@ -128,7 +128,7 @@ where // Request buffer wgpu::BindGroupLayoutEntry { binding: 3, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, @@ -139,7 +139,7 @@ where // Usage buffer wgpu::BindGroupLayoutEntry { binding: 4, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, @@ -152,7 +152,7 @@ where // Structure table pointers wgpu::BindGroupLayoutEntry { binding: 5, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, @@ -164,7 +164,7 @@ where // Structure table request buffer wgpu::BindGroupLayoutEntry { binding: 6, - visibility: ShaderStages::FRAGMENT, + visibility: ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false,