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