Files
vxls/src/main.rs
T

891 lines
32 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#![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::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::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;
mod camera;
mod egui_renderer;
mod producers;
mod sparse_tree;
mod voxel_cache;
//
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>>>,
terrain_generator: Arc<TerrainGenerator<4>>,
chunk_pos_map: Arc<HashMap<u32, (usize, usize, usize)>>,
instance_buffer: Buffer,
instance_count: usize,
usage_vec: Arc<Mutex<Vec<usize>>>,
rm_time: Arc<Mutex<f32>>,
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,
}
#[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<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
| Features::SHADER_EARLY_DEPTH_TEST
| Features::TIMESTAMP_QUERY,
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(200_000, device.clone(), queue.clone());
//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::<Vec<_>>();
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 = 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: &[Some(wgpu::VertexBufferLayout {
array_stride: (size_of::<f32>() * 3 + size_of::<u32>()) 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::<f32>() * 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::Less),
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)),
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 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();
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).
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 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::<u64>() * 2) as u64,
usage: BufferUsages::QUERY_RESOLVE | BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
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: Some(wgpu::RenderPassTimestampWrites {
query_set: &timestamp_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(&timestamp_query, 0..2, &timestamp_buffer, 0);
}
let requests = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("dummy_dumb_dinky_aaaahhh_buffer"),
size: 16 * 2000,
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
if !self
.camera
.pressed_keyset
.contains(&winit::keyboard::KeyCode::KeyF)
{
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| {
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,
);
}
// Report usage
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;
},
);
// 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
{
self.insertion_debounce = false;
}
// if (self
// .camera
// .pressed_keyset
// .contains(&winit::keyboard::KeyCode::KeyF))
// && !self.insertion_debounce
// {
// Report frame time
let cloned_rm_time = self.rm_time.clone();
let cloned_queue = self.queue.clone();
DownloadBuffer::read_buffer(
&self.device,
&self.queue,
&timestamp_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;
},
);
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 cloned_generator = self.terrain_generator.clone();
let cloned_map = self.chunk_pos_map.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 generator = cloned_generator;
let gen_test = SineGenerator::<4>::new(5);
let mut structure_nodes = vec![];
let mut color_nodes: Vec<[ColorBytes; 64]> = vec![];
let mut location_nodes = vec![];
let mut destinations = 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.child_index == u32::MAX
{
// 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.structure_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,
};
}
(
node.structure,
node.colors,
location,
DestinationElement {
node: request.node_index,
child: request.child_index,
},
)
})
.collect::<Vec<_>>()
.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);
destinations.push(data.3);
});
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()]);
}
},
);
//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<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);
}
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,
}