arst
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
2410
Cargo.lock
generated
Normal file
2410
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "fractal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
winit = "0.30.5"
|
||||
wgpu = "22.1.0"
|
||||
nalgebra = "0.33.1"
|
||||
pollster = "0.3.0"
|
||||
bytemuck = "1.19.0"
|
||||
num-traits = "0.2.19"
|
||||
50
src/client/app.rs
Normal file
50
src/client/app.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use winit::{application::ApplicationHandler, event_loop::ControlFlow};
|
||||
|
||||
use super::Client;
|
||||
|
||||
pub struct ClientApp<'a> {
|
||||
client: Option<Client<'a>>,
|
||||
}
|
||||
|
||||
impl ClientApp<'_> {
|
||||
pub fn new() -> Self {
|
||||
Self { client: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for ClientApp<'_> {
|
||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
if self.client.is_none() {
|
||||
self.client = Some(Client::new(event_loop));
|
||||
}
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: winit::event::WindowEvent,
|
||||
) {
|
||||
if let Some(c) = self.client.as_mut() {
|
||||
c.window_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
_device_id: winit::event::DeviceId,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
if let Some(c) = self.client.as_mut() {
|
||||
c.input.update_device(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
if let Some(c) = self.client.as_mut() {
|
||||
c.update(event_loop)
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/client/camera.rs
Normal file
59
src/client/camera.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use nalgebra::Vector2;
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use crate::util::FixedDec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Zoom {
|
||||
scale: f32,
|
||||
mult: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Camera {
|
||||
pub pos: Vector2<FixedDec>,
|
||||
pub zoom: Zoom,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn scale(&self, size: &Vector2<u32>) -> Vector2<f32> {
|
||||
let fsize: Vector2<f32> = size.cast();
|
||||
if size.x < size.y {
|
||||
Vector2::new(fsize.x / fsize.y, 1.0)
|
||||
} else {
|
||||
Vector2::new(1.0, fsize.y / fsize.x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Camera {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pos: Vector2::zeros(),
|
||||
zoom: Zoom::new(0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Zoom {
|
||||
pub fn new(scale: f32) -> Self {
|
||||
Self {
|
||||
scale,
|
||||
mult: mult(scale),
|
||||
}
|
||||
}
|
||||
pub fn mult(&self) -> f32 {
|
||||
self.mult
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<f32> for Zoom {
|
||||
fn add_assign(&mut self, rhs: f32) {
|
||||
self.scale += rhs;
|
||||
self.mult = mult(self.scale);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mult(scale: f32) -> f32 {
|
||||
(-scale).exp2()
|
||||
}
|
||||
32
src/client/handle_input.rs
Normal file
32
src/client/handle_input.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use winit::keyboard::KeyCode as K;
|
||||
|
||||
use crate::util::FixedDec;
|
||||
|
||||
use super::Client;
|
||||
|
||||
impl Client<'_> {
|
||||
pub fn handle_input(&mut self, delta: Duration) {
|
||||
let Client { input, camera, .. } = self;
|
||||
let per_sec = delta.as_secs_f32();
|
||||
|
||||
if input.scroll_delta != 0.0 {
|
||||
// camera.zoom += input.scroll_delta / 5.0;
|
||||
}
|
||||
|
||||
let speed = FixedDec::from(per_sec * 0.5);
|
||||
if input.pressed(K::KeyW) {
|
||||
camera.pos.y += &speed;
|
||||
}
|
||||
if input.pressed(K::KeyA) {
|
||||
camera.pos.x -= &speed;
|
||||
}
|
||||
if input.pressed(K::KeyS) {
|
||||
camera.pos.y -= &speed;
|
||||
}
|
||||
if input.pressed(K::KeyD) {
|
||||
camera.pos.x += &speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/client/input.rs
Normal file
134
src/client/input.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use winit::{
|
||||
event::{DeviceEvent, ElementState, MouseButton, MouseScrollDelta, WindowEvent},
|
||||
keyboard::{KeyCode, PhysicalKey},
|
||||
};
|
||||
|
||||
pub struct Input {
|
||||
pub mouse_pixel_pos: Vector2<f32>,
|
||||
pub mouse_delta: Vector2<f32>,
|
||||
|
||||
pressed: HashSet<KeyCode>,
|
||||
just_pressed: HashSet<KeyCode>,
|
||||
|
||||
mouse_pressed: HashSet<MouseButton>,
|
||||
mouse_just_pressed: HashSet<MouseButton>,
|
||||
mouse_just_released: HashSet<MouseButton>,
|
||||
|
||||
pub scroll_delta: f32,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mouse_pixel_pos: Vector2::zeros(),
|
||||
mouse_delta: Vector2::zeros(),
|
||||
pressed: HashSet::new(),
|
||||
just_pressed: HashSet::new(),
|
||||
mouse_pressed: HashSet::new(),
|
||||
mouse_just_pressed: HashSet::new(),
|
||||
mouse_just_released: HashSet::new(),
|
||||
scroll_delta: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn update_device(&mut self, event: DeviceEvent) {
|
||||
match event {
|
||||
DeviceEvent::MouseWheel { delta } => {
|
||||
self.scroll_delta = match delta {
|
||||
MouseScrollDelta::LineDelta(_, v) => v,
|
||||
MouseScrollDelta::PixelDelta(v) => (v.y / 2.0) as f32,
|
||||
};
|
||||
}
|
||||
DeviceEvent::MouseMotion { delta } => {
|
||||
self.mouse_delta += Vector2::new(delta.0 as f32, delta.1 as f32);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_window(&mut self, event: WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
let code = if let PhysicalKey::Code(code) = event.physical_key {
|
||||
code
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
match event.state {
|
||||
ElementState::Pressed => {
|
||||
self.just_pressed.insert(code);
|
||||
self.pressed.insert(code);
|
||||
}
|
||||
ElementState::Released => {
|
||||
self.pressed.remove(&code);
|
||||
}
|
||||
};
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
self.pressed.clear();
|
||||
self.mouse_pressed.clear();
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
self.mouse_pixel_pos = Vector2::new(position.x as f32, position.y as f32);
|
||||
}
|
||||
WindowEvent::MouseInput { button, state, .. } => match state {
|
||||
ElementState::Pressed => {
|
||||
self.mouse_just_pressed.insert(button);
|
||||
self.mouse_pressed.insert(button);
|
||||
}
|
||||
ElementState::Released => {
|
||||
self.mouse_pressed.remove(&button);
|
||||
self.mouse_just_released.insert(button);
|
||||
}
|
||||
},
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
self.scroll_delta = match delta {
|
||||
MouseScrollDelta::LineDelta(_, v) => v,
|
||||
MouseScrollDelta::PixelDelta(v) => (v.y / 2.0) as f32,
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&mut self) {
|
||||
self.scroll_delta = 0.0;
|
||||
self.mouse_delta = Vector2::zeros();
|
||||
self.just_pressed.clear();
|
||||
self.mouse_just_pressed.clear();
|
||||
self.mouse_just_released.clear();
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.pressed.clear();
|
||||
self.mouse_pressed.clear();
|
||||
self.end();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, key: KeyCode) -> bool {
|
||||
self.pressed.contains(&key)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn just_pressed(&self, key: KeyCode) -> bool {
|
||||
self.just_pressed.contains(&key)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn mouse_pressed(&self, button: MouseButton) -> bool {
|
||||
self.mouse_pressed.contains(&button)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn mouse_just_pressed(&self, button: MouseButton) -> bool {
|
||||
self.mouse_just_pressed.contains(&button)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn mouse_just_released(&self, button: MouseButton) -> bool {
|
||||
self.mouse_just_released.contains(&button)
|
||||
}
|
||||
}
|
||||
73
src/client/mod.rs
Normal file
73
src/client/mod.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use camera::Camera;
|
||||
use input::Input;
|
||||
use render::Renderer;
|
||||
use winit::{
|
||||
event::WindowEvent,
|
||||
window::{Window, WindowAttributes},
|
||||
};
|
||||
|
||||
mod app;
|
||||
mod camera;
|
||||
mod handle_input;
|
||||
mod input;
|
||||
mod render;
|
||||
|
||||
pub use app::*;
|
||||
|
||||
pub struct Client<'a> {
|
||||
window: Arc<Window>,
|
||||
camera: Camera,
|
||||
input: Input,
|
||||
exit: bool,
|
||||
prev_update: Instant,
|
||||
renderer: Renderer<'a>,
|
||||
}
|
||||
|
||||
impl Client<'_> {
|
||||
pub fn new(event_loop: &winit::event_loop::ActiveEventLoop) -> Self {
|
||||
let window = Arc::new(
|
||||
event_loop
|
||||
.create_window(WindowAttributes::default())
|
||||
.expect("failed to create window"),
|
||||
);
|
||||
let renderer = Renderer::new(window.clone());
|
||||
Self {
|
||||
window,
|
||||
camera: Camera::default(),
|
||||
input: Input::new(),
|
||||
exit: false,
|
||||
prev_update: Instant::now(),
|
||||
renderer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
if self.exit {
|
||||
event_loop.exit();
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
self.handle_input(now - self.prev_update);
|
||||
self.input.end();
|
||||
|
||||
self.prev_update = now;
|
||||
}
|
||||
|
||||
pub fn window_event(&mut self, event: WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => self.exit = true,
|
||||
WindowEvent::Resized(size) => self.renderer.resize(size),
|
||||
WindowEvent::RedrawRequested => {
|
||||
self.renderer.update(&self.camera);
|
||||
self.renderer.draw();
|
||||
self.window.request_redraw();
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
self.input.clear();
|
||||
}
|
||||
_ => self.input.update_window(event),
|
||||
}
|
||||
}
|
||||
}
|
||||
164
src/client/render/mod.rs
Normal file
164
src/client/render/mod.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
mod tile;
|
||||
mod util;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use tile::TilePipeline;
|
||||
use util::GPUTimer;
|
||||
use winit::{dpi::PhysicalSize, window::Window};
|
||||
|
||||
use super::camera::Camera;
|
||||
|
||||
pub struct Renderer<'a> {
|
||||
size: Vector2<u32>,
|
||||
surface: wgpu::Surface<'a>,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
encoder: wgpu::CommandEncoder,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
staging_belt: wgpu::util::StagingBelt,
|
||||
timer: GPUTimer,
|
||||
|
||||
tile_pipeline: TilePipeline,
|
||||
}
|
||||
|
||||
impl Renderer<'_> {
|
||||
pub fn new(window: Arc<Window>) -> Self {
|
||||
let size = window.inner_size();
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::PRIMARY,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let surface = instance
|
||||
.create_surface(window)
|
||||
.expect("Could not create window surface!");
|
||||
|
||||
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
}))
|
||||
.expect("Could not get adapter!");
|
||||
|
||||
let buf_size = (10f32.powi(9) * 1.5) as u32;
|
||||
let (device, queue) = pollster::block_on(adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
required_features: wgpu::Features::PUSH_CONSTANTS
|
||||
| wgpu::Features::TIMESTAMP_QUERY
|
||||
| wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS
|
||||
| wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
||||
required_limits: wgpu::Limits {
|
||||
max_storage_buffer_binding_size: buf_size,
|
||||
max_buffer_size: buf_size as u64,
|
||||
max_push_constant_size: 4,
|
||||
..Default::default()
|
||||
},
|
||||
memory_hints: wgpu::MemoryHints::default(),
|
||||
},
|
||||
None,
|
||||
))
|
||||
.expect("Could not get device!");
|
||||
|
||||
let info = adapter.get_info();
|
||||
println!("Adapter: {}", info.name);
|
||||
println!("Backend: {:?}", info.backend);
|
||||
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
let surface_format = surface_caps
|
||||
.formats
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|f| f.is_srgb())
|
||||
.unwrap_or(surface_caps.formats[0]);
|
||||
|
||||
let config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||
alpha_mode: surface_caps.alpha_modes[0],
|
||||
view_formats: vec![],
|
||||
desired_maximum_frame_latency: 2,
|
||||
};
|
||||
|
||||
surface.configure(&device, &config);
|
||||
let staging_belt = wgpu::util::StagingBelt::new(1024);
|
||||
let timer = GPUTimer::new(&device, queue.get_timestamp_period(), 1);
|
||||
|
||||
Self {
|
||||
tile_pipeline: TilePipeline::init(&device, &config),
|
||||
size: Vector2::new(size.width, size.height),
|
||||
staging_belt,
|
||||
surface,
|
||||
encoder: Self::create_encoder(&device),
|
||||
timer,
|
||||
device,
|
||||
config,
|
||||
queue,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, camera: &Camera) {
|
||||
self.tile_pipeline.update(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
camera,
|
||||
&self.size,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device));
|
||||
let output = self.surface.get_current_texture().unwrap();
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
self.timer.start(&mut encoder, 0);
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
self.tile_pipeline.draw(&mut render_pass);
|
||||
drop(render_pass);
|
||||
self.timer.stop(&mut encoder, 0);
|
||||
|
||||
self.timer.resolve(&mut encoder);
|
||||
|
||||
self.staging_belt.finish();
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
self.staging_belt.recall();
|
||||
|
||||
self.timer.finish(&self.device);
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: PhysicalSize<u32>) {
|
||||
self.size = Vector2::new(size.width, size.height);
|
||||
self.config.width = size.width;
|
||||
self.config.height = size.height;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
}
|
||||
|
||||
fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder {
|
||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Render Encoder"),
|
||||
})
|
||||
}
|
||||
}
|
||||
30
src/client/render/tile/data.rs
Normal file
30
src/client/render/tile/data.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use nalgebra::Vector2;
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct View {
|
||||
pub scale: Vector2<f32>,
|
||||
}
|
||||
|
||||
impl Default for View {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scale: Vector2::zeros(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn new(size: &Vector2<u32>) -> Self {
|
||||
let fsize: Vector2<f32> = size.cast();
|
||||
let scale = if size.x < size.y {
|
||||
Vector2::new(fsize.x / fsize.y, 1.0)
|
||||
} else {
|
||||
Vector2::new(1.0, fsize.y / fsize.x)
|
||||
};
|
||||
View { scale }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for View {}
|
||||
unsafe impl bytemuck::Zeroable for View {}
|
||||
86
src/client/render/tile/layout.rs
Normal file
86
src/client/render/tile/layout.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use super::{util::Uniform, View};
|
||||
|
||||
pub struct TileLayout {
|
||||
render_bind_layout: wgpu::BindGroupLayout,
|
||||
render_pipeline_layout: wgpu::PipelineLayout,
|
||||
format: wgpu::TextureFormat,
|
||||
pub view: Uniform<View>,
|
||||
}
|
||||
|
||||
impl TileLayout {
|
||||
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
||||
let view = Uniform::init(device, "view");
|
||||
|
||||
let render_bind_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[view.bind_group_layout_entry(0)],
|
||||
label: Some("voxel render"),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Tile Pipeline Layout"),
|
||||
bind_group_layouts: &[&render_bind_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
Self {
|
||||
view,
|
||||
render_bind_layout,
|
||||
render_pipeline_layout,
|
||||
format: config.format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_bind_group(&self, device: &wgpu::Device) -> wgpu::BindGroup {
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.render_bind_layout,
|
||||
entries: &[self.view.bind_group_entry(0)],
|
||||
label: Some("voxel render"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render_pipeline(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
shader: wgpu::ShaderModule,
|
||||
) -> wgpu::RenderPipeline {
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Voxel Pipeline"),
|
||||
layout: Some(&self.render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: self.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: true,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
61
src/client/render/tile/mod.rs
Normal file
61
src/client/render/tile/mod.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use wgpu::include_wgsl;
|
||||
|
||||
mod data;
|
||||
mod layout;
|
||||
|
||||
use super::*;
|
||||
pub use data::*;
|
||||
use layout::*;
|
||||
|
||||
pub struct TilePipeline {
|
||||
layout: TileLayout,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
render_bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
const RENDER_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("render.wgsl");
|
||||
|
||||
impl TilePipeline {
|
||||
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
||||
let layout = TileLayout::init(device, config);
|
||||
let render_shader = device.create_shader_module(RENDER_SHADER);
|
||||
Self {
|
||||
render_pipeline: layout.render_pipeline(device, render_shader),
|
||||
render_bind_group: layout.render_bind_group(device),
|
||||
layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
camera: &Camera,
|
||||
size: &Vector2<u32>,
|
||||
) {
|
||||
self.view.update(device, encoder, belt, View::new(size));
|
||||
}
|
||||
|
||||
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
render_pass.set_bind_group(0, &self.render_bind_group, &[]);
|
||||
render_pass.draw(0..4, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TilePipeline {
|
||||
type Target = TileLayout;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.layout
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TilePipeline {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.layout
|
||||
}
|
||||
}
|
||||
110
src/client/render/tile/render.wgsl
Normal file
110
src/client/render/tile/render.wgsl
Normal file
@@ -0,0 +1,110 @@
|
||||
// Vertex shader
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) vertex_pos: vec4<f32>,
|
||||
@location(0) world_pos: vec2<f32>,
|
||||
};
|
||||
|
||||
struct View {
|
||||
scale: vec2<f32>,
|
||||
// x_dec: i32,
|
||||
// y_dec: i32,
|
||||
// prec: u32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
// @group(0) @binding(1)
|
||||
// var<storage> vx: array<u32>;
|
||||
// @group(0) @binding(2)
|
||||
// var<storage> vy: array<u32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) vi: u32,
|
||||
@builtin(instance_index) ii: u32,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
let pos = vec2<f32>(
|
||||
f32(vi % 2u),
|
||||
f32(vi / 2u),
|
||||
) * 2.0 - 1.0;
|
||||
out.vertex_pos = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
|
||||
out.world_pos = pos;
|
||||
out.world_pos.y *= -1.0;
|
||||
out.world_pos *= view.scale;
|
||||
return out;
|
||||
}
|
||||
|
||||
// const PREC = 2;
|
||||
|
||||
@fragment
|
||||
fn fs_main(
|
||||
in: VertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let dec = i32(1) << 13;
|
||||
let c = vec2<i32>(in.world_pos * f32(dec));
|
||||
let cx = c.x;
|
||||
let cy = c.y;
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var i = 0u;
|
||||
let thresh = 2 * dec;
|
||||
let thresh2 = thresh * thresh;
|
||||
let max = 50u;
|
||||
loop {
|
||||
let x2 = x * x;
|
||||
let y2 = y * y;
|
||||
if x2 + y2 > thresh2 || i >= max {
|
||||
break;
|
||||
}
|
||||
y = (2 * x * y) / dec + c.y;
|
||||
x = (x2 - y2) / dec + c.x;
|
||||
i += 1u;
|
||||
}
|
||||
var color = vec3<f32>(0.0, 0.0, 0.0);
|
||||
if i != max {
|
||||
let pi = 3.1415;
|
||||
let hue = f32(i) / 30.0;
|
||||
color.r = cos(hue);
|
||||
color.g = cos(hue - 2.0 * pi / 3.0);
|
||||
color.b = cos(hue - 4.0 * pi / 3.0);
|
||||
}
|
||||
return vec4(color, 1.0);
|
||||
}
|
||||
|
||||
// @fragment
|
||||
// fn fs_main(
|
||||
// in: VertexOutput,
|
||||
// ) -> @location(0) vec4<f32> {
|
||||
// let dec = i32(1) << 13;
|
||||
// let c = vec2<i32>(in.world_pos * f32(dec));
|
||||
// let cx = c.x;
|
||||
// let cy = c.y;
|
||||
// var x = 0;
|
||||
// var y = 0;
|
||||
// var i = 0u;
|
||||
// let thresh = 2 * dec;
|
||||
// let thresh2 = thresh * thresh;
|
||||
// let max = 50u + u32(in.zoom);
|
||||
// loop {
|
||||
// let x2 = x * x;
|
||||
// let y2 = y * y;
|
||||
// if x2 + y2 > thresh2 || i >= max {
|
||||
// break;
|
||||
// }
|
||||
// y = (2 * x * y) / dec + c.y;
|
||||
// x = (x2 - y2) / dec + c.x;
|
||||
// i += 1u;
|
||||
// }
|
||||
// var color = vec3<f32>(0.0, 0.0, 0.0);
|
||||
// if i != max {
|
||||
// let pi = 3.1415;
|
||||
// let hue = f32(i) / 30.0;
|
||||
// color.r = cos(hue);
|
||||
// color.g = cos(hue - 2.0 * pi / 3.0);
|
||||
// color.b = cos(hue - 4.0 * pi / 3.0);
|
||||
// }
|
||||
// return vec4(color, 1.0);
|
||||
// }
|
||||
146
src/client/render/util/array.rs
Normal file
146
src/client/render/util/array.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use wgpu::{util::DeviceExt, BufferAddress, BufferUsages};
|
||||
|
||||
pub struct ArrayBuffer<T: bytemuck::Pod> {
|
||||
len: usize,
|
||||
new_len: usize,
|
||||
buffer: wgpu::Buffer,
|
||||
label: String,
|
||||
usage: BufferUsages,
|
||||
updates: Vec<ArrBufUpdate<T>>,
|
||||
}
|
||||
|
||||
impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
) -> bool {
|
||||
let mut resized = false;
|
||||
if self.new_len != self.len {
|
||||
let new = Self::init_buf(device, &self.label, self.new_len, self.usage);
|
||||
let cpy_len = self.len.min(self.new_len);
|
||||
if cpy_len != 0 {
|
||||
encoder.copy_buffer_to_buffer(
|
||||
&self.buffer,
|
||||
0,
|
||||
&new,
|
||||
0,
|
||||
(cpy_len * std::mem::size_of::<T>()) as u64,
|
||||
);
|
||||
}
|
||||
self.len = self.new_len;
|
||||
resized = true;
|
||||
self.buffer = new;
|
||||
}
|
||||
if self.len == 0 {
|
||||
return resized;
|
||||
}
|
||||
for update in &self.updates {
|
||||
let mut view = belt.write_buffer(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
(update.offset * std::mem::size_of::<T>()) as BufferAddress,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked(
|
||||
std::mem::size_of_val(&update.data[..]) as u64
|
||||
)
|
||||
},
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(bytemuck::cast_slice(&update.data));
|
||||
}
|
||||
self.updates.clear();
|
||||
resized
|
||||
}
|
||||
|
||||
pub fn add(&mut self, data: Vec<T>) -> usize {
|
||||
let data_len = data.len();
|
||||
let pos = self.new_len;
|
||||
self.updates.push(ArrBufUpdate { offset: pos, data });
|
||||
self.new_len += data_len;
|
||||
pos
|
||||
}
|
||||
|
||||
pub fn set(&mut self, offset: usize, data: Vec<T>) {
|
||||
self.updates.push(ArrBufUpdate { offset, data });
|
||||
}
|
||||
|
||||
pub fn init(device: &wgpu::Device, label: &str, usage: BufferUsages) -> Self {
|
||||
let label = &(label.to_owned() + " Buffer");
|
||||
Self::init_with(device, label, usage, &[])
|
||||
}
|
||||
|
||||
pub fn init_with(device: &wgpu::Device, label: &str, usage: BufferUsages, data: &[T]) -> Self {
|
||||
let label = &(label.to_owned() + " Buffer");
|
||||
Self {
|
||||
len: data.len(),
|
||||
new_len: data.len(),
|
||||
buffer: if data.is_empty() {
|
||||
Self::init_buf(device, label, 0, usage)
|
||||
} else {
|
||||
Self::init_buf_with(device, label, usage, data)
|
||||
},
|
||||
label: label.to_string(),
|
||||
updates: Vec::new(),
|
||||
usage,
|
||||
}
|
||||
}
|
||||
|
||||
fn init_buf(
|
||||
device: &wgpu::Device,
|
||||
label: &str,
|
||||
size: usize,
|
||||
usage: BufferUsages,
|
||||
) -> wgpu::Buffer {
|
||||
device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some(label),
|
||||
usage: usage | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
|
||||
size: (size.max(1) * std::mem::size_of::<T>()) as u64,
|
||||
mapped_at_creation: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn init_buf_with(
|
||||
device: &wgpu::Device,
|
||||
label: &str,
|
||||
usage: BufferUsages,
|
||||
data: &[T],
|
||||
) -> wgpu::Buffer {
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(label),
|
||||
usage: usage | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
|
||||
contents: bytemuck::cast_slice(data),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bind_group_layout_entry(
|
||||
&self,
|
||||
binding: u32,
|
||||
visibility: wgpu::ShaderStages,
|
||||
ty: wgpu::BufferBindingType,
|
||||
) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: self.buffer.as_entire_binding(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrBufUpdate<T> {
|
||||
pub offset: usize,
|
||||
pub data: Vec<T>,
|
||||
}
|
||||
12
src/client/render/util/mod.rs
Normal file
12
src/client/render/util/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod texture;
|
||||
mod timer;
|
||||
mod uniform;
|
||||
mod array;
|
||||
|
||||
pub use texture::*;
|
||||
pub use timer::*;
|
||||
pub use uniform::*;
|
||||
pub use array::*;
|
||||
91
src/client/render/util/texture.rs
Normal file
91
src/client/render/util/texture.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
pub struct Texture {
|
||||
texture_desc: wgpu::TextureDescriptor<'static>,
|
||||
view_desc: wgpu::TextureViewDescriptor<'static>,
|
||||
sampler_desc: wgpu::SamplerDescriptor<'static>,
|
||||
pub texture: wgpu::Texture,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
|
||||
|
||||
pub fn init_depth(
|
||||
device: &wgpu::Device,
|
||||
config: &wgpu::SurfaceConfiguration,
|
||||
label: &'static str,
|
||||
) -> Self {
|
||||
let size = wgpu::Extent3d {
|
||||
width: config.width + 1,
|
||||
height: config.height + 1,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let texture_desc = wgpu::TextureDescriptor {
|
||||
label: Some(label),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: Self::DEPTH_FORMAT,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
};
|
||||
Self::init(
|
||||
device,
|
||||
texture_desc,
|
||||
wgpu::TextureViewDescriptor::default(),
|
||||
wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
lod_min_clamp: 0.0,
|
||||
lod_max_clamp: 100.0,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
device: &wgpu::Device,
|
||||
texture_desc: wgpu::TextureDescriptor<'static>,
|
||||
view_desc: wgpu::TextureViewDescriptor<'static>,
|
||||
sampler_desc: wgpu::SamplerDescriptor<'static>,
|
||||
) -> Self {
|
||||
let texture = device.create_texture(&texture_desc);
|
||||
let view = texture.create_view(&view_desc);
|
||||
let sampler = device.create_sampler(&sampler_desc);
|
||||
Self {
|
||||
texture_desc,
|
||||
view_desc,
|
||||
sampler_desc,
|
||||
texture,
|
||||
view,
|
||||
sampler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, device: &wgpu::Device, size: wgpu::Extent3d) {
|
||||
self.texture_desc.size = size;
|
||||
self.texture = device.create_texture(&self.texture_desc);
|
||||
self.view = self.texture.create_view(&self.view_desc);
|
||||
}
|
||||
pub fn view_bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: wgpu::BindingResource::TextureView(&self.view),
|
||||
}
|
||||
}
|
||||
pub fn sampler_bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: wgpu::BindingResource::Sampler(&self.sampler),
|
||||
}
|
||||
}
|
||||
pub fn format(&self) -> wgpu::TextureFormat {
|
||||
self.texture_desc.format
|
||||
}
|
||||
}
|
||||
89
src/client/render/util/timer.rs
Normal file
89
src/client/render/util/timer.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct GPUTimer {
|
||||
resolve_buf: wgpu::Buffer,
|
||||
map_buf: wgpu::Buffer,
|
||||
query_set: wgpu::QuerySet,
|
||||
timestamps: Vec<u64>,
|
||||
period: f32,
|
||||
}
|
||||
|
||||
impl GPUTimer {
|
||||
pub fn new(device: &wgpu::Device, period: f32, count: u32) -> Self {
|
||||
let count = count * 2;
|
||||
let timestamp_set = device.create_query_set(&wgpu::QuerySetDescriptor {
|
||||
count,
|
||||
label: Some("voxel timestamp"),
|
||||
ty: wgpu::QueryType::Timestamp,
|
||||
});
|
||||
let timestamp_resolve_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("voxel timestamp"),
|
||||
mapped_at_creation: false,
|
||||
size: 8 * count as u64,
|
||||
usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
|
||||
});
|
||||
|
||||
let timestamp_mapped_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("voxel timestamp"),
|
||||
mapped_at_creation: false,
|
||||
size: 8 * count as u64,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
||||
});
|
||||
|
||||
Self {
|
||||
query_set: timestamp_set,
|
||||
resolve_buf: timestamp_resolve_buf,
|
||||
map_buf: timestamp_mapped_buf,
|
||||
timestamps: vec![0; count as usize],
|
||||
period,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(&self, encoder: &mut wgpu::CommandEncoder) {
|
||||
encoder.resolve_query_set(&self.query_set, 0..2, &self.resolve_buf, 0);
|
||||
encoder.copy_buffer_to_buffer(&self.resolve_buf, 0, &self.map_buf, 0, self.map_buf.size());
|
||||
}
|
||||
|
||||
pub fn duration(&self, i: u32) -> Duration {
|
||||
let i = i as usize * 2;
|
||||
let diff = self.timestamps[i + 1] - self.timestamps[i];
|
||||
Duration::from_nanos((diff as f32 * self.period) as u64)
|
||||
}
|
||||
|
||||
pub fn finish(&mut self, device: &wgpu::Device) {
|
||||
let (s, r) = std::sync::mpsc::channel();
|
||||
self.map_buf
|
||||
.slice(..)
|
||||
.map_async(wgpu::MapMode::Read, move |v| {
|
||||
s.send(v).expect("what");
|
||||
});
|
||||
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
|
||||
if let Ok(Ok(())) = r.recv() {
|
||||
let data = self.map_buf.slice(..).get_mapped_range();
|
||||
self.timestamps.copy_from_slice(bytemuck::cast_slice(&data));
|
||||
drop(data);
|
||||
self.map_buf.unmap();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start(&self, encoder: &mut wgpu::CommandEncoder, i: u32) {
|
||||
encoder.write_timestamp(&self.query_set, i * 2);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn stop(&self, encoder: &mut wgpu::CommandEncoder, i: u32) {
|
||||
encoder.write_timestamp(&self.query_set, i * 2 + 1);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start_compute(&self, pass: &mut wgpu::ComputePass, i: u32) {
|
||||
pass.write_timestamp(&self.query_set, i * 2);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn stop_compute(&self, pass: &mut wgpu::ComputePass, i: u32) {
|
||||
pass.write_timestamp(&self.query_set, i * 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
76
src/client/render/util/uniform.rs
Normal file
76
src/client/render/util/uniform.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
pub struct Uniform<T> {
|
||||
buffer: wgpu::Buffer,
|
||||
ty: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Default + bytemuck::Pod> Uniform<T> {
|
||||
pub fn init(device: &wgpu::Device, name: &str) -> Self {
|
||||
Self {
|
||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||
contents: bytemuck::cast_slice(&[T::default()]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: bytemuck::Pod> Uniform<T> {
|
||||
pub fn init_with(device: &wgpu::Device, name: &str, data: &[T]) -> Self {
|
||||
Self {
|
||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||
contents: bytemuck::cast_slice(data),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
data: T,
|
||||
) {
|
||||
let slice = &[data];
|
||||
let mut view = belt.write_buffer(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
0,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked((slice.len() * std::mem::size_of::<T>()) as u64)
|
||||
},
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(bytemuck::cast_slice(slice));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uniform<T> {
|
||||
pub fn bind_group_layout_entry(&self, binding: u32) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility: wgpu::ShaderStages::VERTEX
|
||||
| wgpu::ShaderStages::FRAGMENT
|
||||
| wgpu::ShaderStages::COMPUTE,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}
|
||||
pub fn bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: self.buffer.as_entire_binding(),
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main.rs
Normal file
25
src/main.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#![feature(bigint_helper_methods)]
|
||||
|
||||
use client::ClientApp;
|
||||
use util::FixedDec;
|
||||
|
||||
mod client;
|
||||
mod util;
|
||||
|
||||
fn main() {
|
||||
let a = FixedDec::from(0.75);
|
||||
println!("a = {}", a);
|
||||
let b = FixedDec::from(1.75);
|
||||
println!("b = {}", b);
|
||||
println!("a + b = {}", &a + &b);
|
||||
let c = FixedDec::from(1.0 / 16.0);
|
||||
println!("c = {}", c);
|
||||
println!("a + c = {}", &a + &c);
|
||||
println!("-a = {}", -&a);
|
||||
println!("b - a = {}", &b - &a);
|
||||
println!("-c = {}", -&c);
|
||||
// let event_loop = winit::event_loop::EventLoop::new().expect("Failed to create event loop");
|
||||
// event_loop
|
||||
// .run_app(&mut ClientApp::new())
|
||||
// .expect("Failed to run event loop");
|
||||
}
|
||||
338
src/util/fixed.rs
Normal file
338
src/util/fixed.rs
Normal file
@@ -0,0 +1,338 @@
|
||||
use num_traits::Zero;
|
||||
use std::{
|
||||
fmt::{Binary, Debug, Display},
|
||||
ops::{Add, AddAssign, Mul, Neg, Shr, Sub, SubAssign},
|
||||
};
|
||||
|
||||
const POS: bool = false;
|
||||
const NEG: bool = true;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FixedDec {
|
||||
sign: bool,
|
||||
dec: i32,
|
||||
parts: Vec<u32>,
|
||||
}
|
||||
|
||||
impl FixedDec {
|
||||
pub fn zeros() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
pub fn dec_len(&self) -> i32 {
|
||||
self.parts.len() as i32 - self.dec
|
||||
}
|
||||
|
||||
pub fn part(&self, i: i32) -> u32 {
|
||||
let Ok(i): Result<usize, _> = i.try_into() else {
|
||||
return self.pre_padding();
|
||||
};
|
||||
self.parts.get(i).cloned().unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn is_pos(&self) -> bool {
|
||||
!self.sign
|
||||
}
|
||||
|
||||
pub fn is_neg(&self) -> bool {
|
||||
self.sign
|
||||
}
|
||||
|
||||
fn pre_padding(&self) -> u32 {
|
||||
match self.sign {
|
||||
POS => 0,
|
||||
NEG => !0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for FixedDec {
|
||||
fn zero() -> Self {
|
||||
Self {
|
||||
sign: POS,
|
||||
dec: 0,
|
||||
parts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.parts.iter().all(|&b| b == 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<u32> for FixedDec {
|
||||
type Output = Self;
|
||||
|
||||
fn shr(self, rhs: u32) -> Self::Output {
|
||||
let mut parts = Vec::new();
|
||||
let sr = rhs % 32;
|
||||
let sl = 32 - sr;
|
||||
let mask = (1 << sr) - 1;
|
||||
let dec = self.dec - (rhs / 32) as i32;
|
||||
let mut rem = 0;
|
||||
for part in self.parts {
|
||||
parts.push((part >> sr) ^ rem);
|
||||
rem = (part & mask) << sl;
|
||||
}
|
||||
if rem != 0 {
|
||||
parts.push(rem);
|
||||
}
|
||||
Self {
|
||||
dec,
|
||||
parts,
|
||||
sign: self.sign,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for FixedDec {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
&self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&FixedDec> for FixedDec {
|
||||
fn add_assign(&mut self, rhs: &FixedDec) {
|
||||
let dec = self.dec.max(rhs.dec);
|
||||
let left_i = -dec;
|
||||
let right_i = self.dec_len().max(rhs.dec_len());
|
||||
let len = (right_i - left_i) as usize;
|
||||
if dec != self.dec {
|
||||
let fill = self.pre_padding();
|
||||
let fill_len = rhs.dec - self.dec;
|
||||
self.parts.splice(0..0, (0..fill_len).map(|_| fill));
|
||||
self.dec += fill_len;
|
||||
}
|
||||
if self.parts.len() != len {
|
||||
self.parts.resize(len, 0);
|
||||
}
|
||||
let mut carry = false;
|
||||
let rhs_offset = rhs.dec - self.dec;
|
||||
for i in (0..self.parts.len()).rev() {
|
||||
let a = self.parts[i];
|
||||
let b = rhs.part(i as i32 + rhs_offset);
|
||||
let (res, c) = a.carrying_add(b, carry);
|
||||
self.parts[i] = res;
|
||||
carry = c;
|
||||
}
|
||||
let sign = if self.sign == rhs.sign {
|
||||
if self.sign == POS && carry {
|
||||
self.parts.insert(0, 1);
|
||||
self.dec += 1;
|
||||
} else if self.sign == NEG && !carry {
|
||||
self.parts.insert(0, !1);
|
||||
self.dec += 1;
|
||||
}
|
||||
self.sign
|
||||
} else if carry {
|
||||
POS
|
||||
} else {
|
||||
NEG
|
||||
};
|
||||
self.sign = sign;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<&FixedDec> for FixedDec {
|
||||
fn sub_assign(&mut self, rhs: &FixedDec) {
|
||||
*self += &-rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for &FixedDec {
|
||||
type Output = FixedDec;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
let mut dec = self.dec.max(rhs.dec);
|
||||
let left_i = -dec;
|
||||
let right_i = self.dec_len().max(rhs.dec_len());
|
||||
let mut parts = Vec::with_capacity((right_i - left_i) as usize);
|
||||
let mut carry = false;
|
||||
for i in (left_i..right_i).rev() {
|
||||
let a = self.part(i + self.dec);
|
||||
let b = rhs.part(i + rhs.dec);
|
||||
let (res, c) = a.carrying_add(b, carry);
|
||||
parts.push(res);
|
||||
carry = c;
|
||||
}
|
||||
let sign = if self.sign == rhs.sign {
|
||||
if self.is_pos() && carry {
|
||||
parts.push(1);
|
||||
dec += 1;
|
||||
} else if self.is_neg() && !carry {
|
||||
parts.push(!1);
|
||||
dec += 1;
|
||||
}
|
||||
self.sign
|
||||
} else if carry {
|
||||
POS
|
||||
} else {
|
||||
NEG
|
||||
};
|
||||
parts.reverse();
|
||||
FixedDec { parts, dec, sign }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for &FixedDec {
|
||||
type Output = FixedDec;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self + &(-rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for &FixedDec {
|
||||
type Output = FixedDec;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
let parts = self.parts.iter().map(|p| !p).collect();
|
||||
let mut res = FixedDec {
|
||||
parts,
|
||||
sign: !self.sign,
|
||||
dec: self.dec,
|
||||
};
|
||||
res += &Self::Output {
|
||||
parts: vec![1],
|
||||
dec: self.dec - (self.parts.len() as i32 - 1),
|
||||
sign: POS,
|
||||
};
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for &FixedDec {
|
||||
type Output = FixedDec;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
let mut parts: Vec<u32> = vec![0; self.parts.len() + rhs.parts.len()];
|
||||
for (i, &x) in self.parts.iter().enumerate().rev() {
|
||||
let mut carry: u32 = 0;
|
||||
for (j, &y) in rhs.parts.iter().enumerate().rev() {
|
||||
let (lsb, msb) = mul_lmsb(x, y);
|
||||
let k = i + j + 1;
|
||||
let (res, carry1) = parts[k].overflowing_add(lsb);
|
||||
parts[k] = res;
|
||||
let (res, carry2) = parts[k].overflowing_add(carry);
|
||||
parts[k] = res;
|
||||
// dude I have no clue if this can overflow; I know msb can take 1 without
|
||||
// overflowing, but I'm not sure if 2 can get here when it's max
|
||||
carry = (carry1 as u32) + (carry2 as u32) + msb;
|
||||
}
|
||||
if carry > 0 {
|
||||
parts[i] = carry;
|
||||
}
|
||||
}
|
||||
Self::Output {
|
||||
dec: self.dec + rhs.dec,
|
||||
parts,
|
||||
sign: self.sign == rhs.sign,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
|
||||
let lsb = x.wrapping_mul(y);
|
||||
let a = x & 0xffff;
|
||||
let b = x >> 16;
|
||||
let c = y & 0xffff;
|
||||
let d = y >> 16;
|
||||
let ad = a * d + ((a * c) >> 16);
|
||||
let bc = b * c;
|
||||
let car = ad > (0xffffffff - bc);
|
||||
let msb = ((ad + bc) >> 16) + ((car as u32) << 16) + b * d;
|
||||
(lsb, msb)
|
||||
}
|
||||
|
||||
const INV_SIGN_MASK: u32 = (1 << 31) - 1;
|
||||
const FRAC_BIT: u32 = 1 << 23;
|
||||
const FRAC_MASK: u32 = FRAC_BIT - 1;
|
||||
|
||||
impl From<f32> for FixedDec {
|
||||
fn from(value: f32) -> Self {
|
||||
let raw = value.to_bits() & INV_SIGN_MASK;
|
||||
let exp = (raw >> 23) as i32 - 127;
|
||||
let frac = (raw & FRAC_MASK) + FRAC_BIT;
|
||||
let start = -exp - 1;
|
||||
let end = -exp + 23;
|
||||
let start_i = start.div_euclid(32);
|
||||
let end_i = (end - 1).div_euclid(32);
|
||||
let parts = if start_i == end_i {
|
||||
vec![frac << (8 - start.rem_euclid(32))]
|
||||
} else {
|
||||
let s = end.rem_euclid(32);
|
||||
vec![frac >> s, frac << (32 - s)]
|
||||
};
|
||||
Self {
|
||||
sign: POS,
|
||||
dec: -start_i,
|
||||
parts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FixedDec> for f32 {
|
||||
fn from(value: &FixedDec) -> Self {
|
||||
let mut sign = 0;
|
||||
let value = if value.is_neg() {
|
||||
sign = 1 << 31;
|
||||
&-value
|
||||
} else {
|
||||
value
|
||||
};
|
||||
let mut skip_count = 0;
|
||||
let mut iter = value
|
||||
.parts
|
||||
.iter()
|
||||
.inspect(|_| skip_count += 1)
|
||||
.skip_while(|&&x| x == 0);
|
||||
|
||||
let Some(v) = iter.next() else {
|
||||
return 0.0;
|
||||
};
|
||||
let start = v.leading_zeros();
|
||||
let frac = if start > 9 {
|
||||
let sh = start - 9;
|
||||
(v << sh) + iter.next().copied().map(|v| v >> (32 - sh)).unwrap_or(0)
|
||||
} else {
|
||||
v >> (9 - start)
|
||||
};
|
||||
let exp = (127 - (skip_count * 32 + start)) << 23;
|
||||
let res = frac + exp + sign;
|
||||
println!();
|
||||
println!("res: {:032b}", res);
|
||||
println!("ans: {:032b}", 0.75f32.to_bits());
|
||||
f32::from_bits(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FixedDec {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", f32::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Binary for FixedDec {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.sign == NEG {
|
||||
write!(f, "-")?;
|
||||
}
|
||||
if self.dec < 0 {
|
||||
write!(f, ".")?;
|
||||
for _ in 0..(-self.dec) {
|
||||
write!(f, "00000000000000000000000000000000")?;
|
||||
}
|
||||
}
|
||||
for (i, part) in self.parts.iter().enumerate() {
|
||||
if i as i32 == self.dec {
|
||||
write!(f, ".")?;
|
||||
} else if i != 0 {
|
||||
write!(f, "_")?;
|
||||
}
|
||||
write!(f, "{:032b}", part)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
2
src/util/mod.rs
Normal file
2
src/util/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod fixed;
|
||||
pub use fixed::*;
|
||||
Reference in New Issue
Block a user