This commit is contained in:
2026-08-31 15:25:03 +02:00
parent dbda39b61a
commit 194cbf04ef
21 changed files with 1252658 additions and 325 deletions
+692 -2
View File
@@ -1,3 +1,693 @@
fn main() -> anyhow::Result<()> {
wgpu_template::run()
#![feature(generic_const_exprs)]
use core::sync;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::mpsc::sync_channel;
use bytemuck::Pod;
use bytemuck::Zeroable;
use crevice::std140::AsStd140;
use crevice::std430::AsStd430;
use egui::emath::fast_midpoint;
use egui::mutex::Mutex;
use glam::Mat4;
use glam::Vec3;
use glam::Vec4;
use itertools::Itertools;
use rand::random;
use wgpu::BindGroup;
use wgpu::BindGroupEntry;
use wgpu::BindGroupLayoutDescriptor;
use wgpu::BindGroupLayoutEntry;
use wgpu::Buffer;
use wgpu::BufferUsages;
use wgpu::DepthBiasState;
use wgpu::Device;
use wgpu::Extent3d;
use wgpu::Features;
use wgpu::FragmentState;
use wgpu::InstanceDescriptor;
use wgpu::InstanceFlags;
use wgpu::MemoryBudgetThresholds;
use wgpu::NoopBackendOptions;
use wgpu::Operations;
use wgpu::PrimitiveState;
use wgpu::RenderPassDepthStencilAttachment;
use wgpu::RenderPipeline;
use wgpu::RenderPipelineDescriptor;
use wgpu::ShaderModuleDescriptor;
use wgpu::ShaderStages;
use wgpu::StencilState;
use wgpu::Texture;
use wgpu::TextureUsages;
use wgpu::TextureView;
use wgpu::VertexState;
use wgpu::util::BufferInitDescriptor;
use wgpu::util::DeviceExt;
use wgpu::util::DownloadBuffer;
use wgpu::util::StagingBelt;
use winit::application::ApplicationHandler;
use winit::event::DeviceEvent;
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::SineGenerator;
use crate::voxel::cache::CacheNodeRequest;
use crate::voxel::cache::CacheResponse;
use crate::voxel::cache::DestinationElement;
use crate::voxel::cache::LocationPoolElement;
use crate::voxel::cache::RequestBuffer;
use crate::voxel::cache::UsageBuffer;
use crate::voxel::cache::VoxelCache;
use crate::voxel::gpu::ExplicitNTreeNode;
use crate::voxel::pipeline::ChunkHandle;
use crate::voxel::pipeline::ChunkObject;
use crate::voxel::pipeline::VoxelPipeline;
use crate::voxel::sparse::Color;
use crate::voxel::sparse::NTree;
use crate::voxel::sparse::NTreeNodeLocator;
mod camera;
mod egui_renderer;
mod producers;
mod voxel;
//mod tree;
//
struct State
{
instance: wgpu::Instance,
window: Arc<Window>,
device: wgpu::Device,
queue: wgpu::Queue,
size: winit::dpi::PhysicalSize<u32>,
surface: wgpu::Surface<'static>,
depth_buffer: (wgpu::Texture, wgpu::TextureView),
surface_format: wgpu::TextureFormat,
egui_renderer: EguiRenderer,
pipeline: RenderPipeline,
voxel_cache: Arc<Mutex<VoxelCache<4>>>,
insertion_debounce: bool,
camera: Camera,
}
pub unsafe fn as_raw_bytes<T: Sized>(slice: &[T]) -> &[u8]
{
let size = slice.len() * size_of::<T>();
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,
}
impl State
{
async fn new(display: OwnedDisplayHandle, window: Arc<Window>) -> 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,
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(4096, device.clone(), queue.clone());
let id = voxel_cache.structure_table.allocate_structure(true);
println!("id: {id}");
let shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Main shader module"),
source: wgpu::ShaderSource::Wgsl(
std::fs::read_to_string("shaders/voxel.wgsl")
.unwrap()
.into(),
),
});
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::<Immediates>() 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: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
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,
pipeline: chunk_pipeline,
voxel_cache: Arc::new(Mutex::new(voxel_cache)),
insertion_debounce: false,
camera: Default::default(),
};
// 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 were 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<u32>)
{
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();
// Write random shit in request buffer
let mut belt = StagingBelt::new(self.device.clone(), size_of::<u32>() as u64);
// Create texture view.
// NOTE: We must handle Timeout because the surface may be unavailable
// (e.g., when the window is occluded on macOS).
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()
});
// Renders a GREEN screen
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::BLACK),
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: None,
occlusion_query_set: None,
multiview_mask: None,
});
renderpass.set_pipeline(&self.pipeline);
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..1);
// End the renderpass.
drop(renderpass);
}
let requests = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("dummy_dumb_dinky_aaaahhh_buffer"),
size: 16 * 64,
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
self.voxel_cache
.lock()
.cache_post_render(&mut encoder, requests.clone());
// If you wanted to call any drawing commands, they would go here.
{
self.egui_renderer.begin_frame(&self.window);
egui::Window::new("Window ! ")
.resizable(true)
.show(self.egui_renderer.context(), |ui| 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,
);
}
// Submit the command in the queue to execute
self.queue.submit([encoder.finish()]);
self.window.pre_present_notify();
self.queue.present(surface_texture);
if !self
.camera
.pressed_keyset
.contains(&winit::keyboard::KeyCode::KeyF)
{
self.insertion_debounce = false;
}
if (!self
.camera
.pressed_keyset
.contains(&winit::keyboard::KeyCode::KeyF))
{
self.insertion_debounce = true;
let request_count = self.voxel_cache.lock().total_request_count();
let cloned_cache = self.voxel_cache.clone();
let cloned_device = self.device.clone();
let cloned_queue = self.queue.clone();
let (tx, rx) = sync_channel(1);
DownloadBuffer::read_buffer(
&self.device.clone(),
&self.queue.clone(),
&requests.slice(0..),
move |buffer| {
if tx.try_send(()).is_err()
{
return;
}
let cache_node_requests: Vec<CacheNodeRequest> =
bytemuck::pod_collect_to_vec(&buffer.unwrap());
let mut sine_gen = SineGenerator::<4>::new(4);
let mut structure_nodes = vec![];
let mut color_nodes = vec![];
let mut location_nodes = vec![];
let mut destinations = vec![];
for request in cache_node_requests.iter().take(request_count as usize)
{
let location;
let node;
if request.child_index == u32::MAX
{
// Produce root node
node = sine_gen.produce_node(0, 0, 0, 0);
location = LocationPoolElement {
structure_id: 0,
structure_locator: NTreeNodeLocator::<4>::root().as_usize() as u32,
};
}
else
{
// Figure out depth of request
let locator = NTreeNodeLocator::<4>::from_usize(
request.structure_locator as usize,
);
let depth = locator.depth();
//let (x, y, z) = locator.node_location();
let child_x = request.child_index / (4 * 4);
let child_y = (request.child_index % (4 * 4)) / 4;
let child_z = 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 = sine_gen.produce_node(depth + 1, nx, ny, nz);
location = LocationPoolElement {
structure_id: 0,
structure_locator: child_locator.as_usize() as u32,
};
}
structure_nodes.push(node.structure);
color_nodes.push(node.colors);
location_nodes.push(location);
destinations.push(DestinationElement {
node: request.node_index,
child: request.child_index,
});
}
if structure_nodes.len() != 0
{
let structre_node_buffer =
cloned_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: unsafe { as_raw_bytes(structure_nodes.as_slice()) },
usage: BufferUsages::STORAGE,
});
let color_node_buffer =
cloned_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: unsafe { as_raw_bytes(color_nodes.as_slice()) },
usage: BufferUsages::STORAGE,
});
let location_node_buffer =
cloned_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: unsafe { as_raw_bytes(location_nodes.as_slice()) },
usage: BufferUsages::STORAGE,
});
let destinations_buffer =
cloned_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: unsafe { as_raw_bytes(destinations.as_slice()) },
usage: BufferUsages::STORAGE,
});
let mut encoder = cloned_device.create_command_encoder(&Default::default());
cloned_cache.lock().cache_insert(
&mut encoder,
&CacheResponse {
structure_nodes: structre_node_buffer,
color_nodes: color_node_buffer,
locations: location_node_buffer,
parents: destinations_buffer,
},
);
cloned_queue.submit([encoder.finish()]);
}
cloned_cache.lock().next_frame();
},
);
println!("Total request count: {}", request_count);
loop
{
if rx.try_recv().is_ok()
{
break;
}
let _ = self.device.poll(wgpu::wgt::PollType::Poll);
}
drop(rx);
}
}
}
#[derive(Default)]
struct App
{
state: Option<State>,
}
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);
}
_ => (),
}
}
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,
}