KIND OF ARBITRARY ZOOM (hangs at len 5 for some reason)
This commit is contained in:
680
Cargo.lock
generated
680
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = "0.30.5"
|
winit = "0.30.9"
|
||||||
wgpu = "22.1.0"
|
|
||||||
nalgebra = "0.33.1"
|
|
||||||
pollster = "0.3.0"
|
pollster = "0.3.0"
|
||||||
bytemuck = "1.19.0"
|
bytemuck = "1.19.0"
|
||||||
num-traits = "0.2.19"
|
num-traits = "0.2.19"
|
||||||
|
nalgebra = "0.33.2"
|
||||||
|
wgpu = "24.0.1"
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ impl Zoom {
|
|||||||
pub fn level(&self) -> i32 {
|
pub fn level(&self) -> i32 {
|
||||||
self.level
|
self.level
|
||||||
}
|
}
|
||||||
|
pub fn rel_zoom(&self) -> f32 {
|
||||||
|
self.exp.exp2()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<f32> for Zoom {
|
impl AddAssign<f32> for Zoom {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ impl Client<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let speed = FixedDec::from(per_sec * 0.5) * camera.zoom.mult();
|
let speed = FixedDec::from(per_sec * 0.5) * camera.zoom.mult();
|
||||||
let old = f32::from(&camera.pos.x);
|
|
||||||
if input.pressed(K::KeyW) {
|
if input.pressed(K::KeyW) {
|
||||||
camera.pos.y += &speed;
|
camera.pos.y += &speed;
|
||||||
}
|
}
|
||||||
@@ -29,9 +28,5 @@ impl Client<'_> {
|
|||||||
if input.pressed(K::KeyD) {
|
if input.pressed(K::KeyD) {
|
||||||
camera.pos.x += &speed;
|
camera.pos.x += &speed;
|
||||||
}
|
}
|
||||||
let new = f32::from(&camera.pos.x);
|
|
||||||
if (new - old).abs() > 0.5 {
|
|
||||||
println!("{} + {} = {}", old, f32::from(speed), new);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,7 @@ impl Client<'_> {
|
|||||||
WindowEvent::CloseRequested => self.exit = true,
|
WindowEvent::CloseRequested => self.exit = true,
|
||||||
WindowEvent::Resized(size) => self.renderer.resize(size),
|
WindowEvent::Resized(size) => self.renderer.resize(size),
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
self.renderer.update(&self.camera);
|
self.renderer.render(&self.camera);
|
||||||
self.renderer.draw();
|
|
||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
}
|
}
|
||||||
WindowEvent::CursorLeft { .. } => {
|
WindowEvent::CursorLeft { .. } => {
|
||||||
|
|||||||
@@ -6,28 +6,36 @@ use super::Camera;
|
|||||||
|
|
||||||
const VIEW_ALIGN: usize = 4 * 2;
|
const VIEW_ALIGN: usize = 4 * 2;
|
||||||
|
|
||||||
pub struct View {
|
pub struct ComputeView {
|
||||||
pub bytes: Vec<u8>,
|
pub bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl ComputeView {
|
||||||
pub fn bytes(&self) -> &[u8] {
|
pub fn bytes(&self) -> &[u8] {
|
||||||
&self.bytes
|
&self.bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for View {
|
impl Default for ComputeView {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let val = FixedDec::from_parts(false, 0, vec![0, 0, 0]);
|
let val = FixedDec::from_parts(false, 0, vec![0, 0, 0]);
|
||||||
Self::new(Vector2::zeros(), 0, &val, &val, &val)
|
Self::new(true, Vector2::zeros(), 0, &val, &val, &val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl ComputeView {
|
||||||
fn new(stretch: Vector2<f32>, level: i32, scale: &FixedDec, x: &FixedDec, y: &FixedDec) -> Self {
|
fn new(
|
||||||
|
reset: bool,
|
||||||
|
dims: Vector2<u32>,
|
||||||
|
level: i32,
|
||||||
|
scale: &FixedDec,
|
||||||
|
x: &FixedDec,
|
||||||
|
y: &FixedDec,
|
||||||
|
) -> Self {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
bytes.extend(bytemuck::cast_slice(&[stretch.x, stretch.y]));
|
bytes.extend((reset as u32).to_le_bytes());
|
||||||
bytes.extend(level.to_le_bytes());
|
bytes.extend(level.to_le_bytes());
|
||||||
|
bytes.extend(bytemuck::cast_slice(&[dims.x, dims.y]));
|
||||||
scale.to_bytes(&mut bytes);
|
scale.to_bytes(&mut bytes);
|
||||||
x.to_bytes(&mut bytes);
|
x.to_bytes(&mut bytes);
|
||||||
y.to_bytes(&mut bytes);
|
y.to_bytes(&mut bytes);
|
||||||
@@ -38,13 +46,13 @@ impl View {
|
|||||||
Self { bytes }
|
Self { bytes }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_camera_size(camera: &Camera, size: &Vector2<u32>) -> Self {
|
pub fn from_camera_size(camera: &Camera, size: &Vector2<u32>, reset: bool, len: usize) -> Self {
|
||||||
let mut x = camera.pos.x.clone();
|
let mut x = camera.pos.x.clone();
|
||||||
x.set_whole_len(1);
|
x.set_whole_len(1);
|
||||||
x.set_dec_len(2);
|
x.set_dec_len(len as i32 - 1);
|
||||||
let mut y = camera.pos.y.clone();
|
let mut y = camera.pos.y.clone();
|
||||||
y.set_whole_len(1);
|
y.set_whole_len(1);
|
||||||
y.set_dec_len(2);
|
y.set_dec_len(len as i32 - 1);
|
||||||
|
|
||||||
let fsize: Vector2<f32> = size.cast();
|
let fsize: Vector2<f32> = size.cast();
|
||||||
let stretch = if size.x < size.y {
|
let stretch = if size.x < size.y {
|
||||||
@@ -54,8 +62,14 @@ impl View {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut scale = camera.zoom.mult().clone();
|
let mut scale = camera.zoom.mult().clone();
|
||||||
scale.set_precision(3);
|
scale.set_precision(len);
|
||||||
|
|
||||||
Self::new(stretch, camera.zoom.level(), &scale, &x, &y)
|
Self::new(reset, *size, camera.zoom.level(), &scale, &x, &y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ComputeView {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.bytes[1..] == other.bytes[1..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,5 @@
|
|||||||
const LEN: u32 = 3;
|
const POS: u32 = 0u;
|
||||||
const ILEN: i32 = 3;
|
const NEG: u32 = 1u;
|
||||||
const LEN2: u32 = LEN * 2;
|
|
||||||
|
|
||||||
struct View {
|
|
||||||
stretch: vec2<f32>,
|
|
||||||
level: i32,
|
|
||||||
scale: FixedDec,
|
|
||||||
x: FixedDec,
|
|
||||||
y: FixedDec,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FixedDec {
|
struct FixedDec {
|
||||||
sign: u32,
|
sign: u32,
|
||||||
@@ -16,63 +7,18 @@ struct FixedDec {
|
|||||||
parts: array<u32, LEN>,
|
parts: array<u32, LEN>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(0) @binding(0)
|
fn shr(lhs: FixedDec, rhs: i32) -> FixedDec {
|
||||||
var<storage> view: View;
|
var parts = array<u32, LEN>();
|
||||||
|
let sr = u32(rem_euclid(rhs, 32));
|
||||||
struct VertexOutput {
|
let sl = 32 - sr;
|
||||||
@builtin(position) vertex_pos: vec4<f32>,
|
let mask = (1u << sr) - 1;
|
||||||
@location(0) world_pos: vec2<f32>,
|
var rem = 0u;
|
||||||
};
|
for (var i = 0; i < ILEN; i += 1) {
|
||||||
|
let part = lhs.parts[i];
|
||||||
@vertex
|
parts[i] = (part >> sr) ^ rem;
|
||||||
fn vs_main(
|
rem = (part & mask) << sl;
|
||||||
@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.stretch;
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
return FixedDec(lhs.sign, lhs.dec, parts);
|
||||||
@fragment
|
|
||||||
fn fs_main(
|
|
||||||
in: VertexOutput,
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
let cx = add(mul(from_f32(in.world_pos.x), view.scale), view.x);
|
|
||||||
let cy = add(mul(from_f32(in.world_pos.y), view.scale), view.y);
|
|
||||||
var x = zero();
|
|
||||||
var y = zero();
|
|
||||||
let two = from_f32(2.0);
|
|
||||||
let thresh = from_f32(2.0 * 2.0);
|
|
||||||
var i = 0u;
|
|
||||||
let max = 50u + (1u << u32(view.level));
|
|
||||||
loop {
|
|
||||||
let x2 = mul(x, x);
|
|
||||||
let y2 = mul(y, y);
|
|
||||||
if gt(add(x2, y2), thresh) || i >= max {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
y = add(mul(two, mul(x, y)), cy);
|
|
||||||
x = add(sub(x2, y2), cx);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
fn add(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||||
@@ -136,9 +82,6 @@ fn at(dec: FixedDec, i: i32) -> u32 {
|
|||||||
return parts[i];
|
return parts[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
const POS: u32 = 0u;
|
|
||||||
const NEG: u32 = 1u;
|
|
||||||
|
|
||||||
fn mul(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
fn mul(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||||
let sign = u32(lhs.sign != rhs.sign);
|
let sign = u32(lhs.sign != rhs.sign);
|
||||||
var parts = array<u32, LEN2>();
|
var parts = array<u32, LEN2>();
|
||||||
@@ -173,7 +116,7 @@ fn mul(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
|||||||
let res2 = res + carry;
|
let res2 = res + carry;
|
||||||
let carry2 = res2 < res;
|
let carry2 = res2 < res;
|
||||||
parts[k] = res2;
|
parts[k] = res2;
|
||||||
carry = u32(carry1 || carry2) + msb;
|
carry = u32(carry1) + u32(carry2) + msb;
|
||||||
}
|
}
|
||||||
parts[i] = carry;
|
parts[i] = carry;
|
||||||
}
|
}
|
||||||
@@ -202,6 +145,25 @@ fn gt(x: FixedDec, y: FixedDec) -> bool {
|
|||||||
return x.parts[0] > y.parts[0];
|
return x.parts[0] > y.parts[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eq(x: FixedDec, y: FixedDec) -> bool {
|
||||||
|
if x.sign != y.sign || x.dec != y.dec {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var i = 0u;
|
||||||
|
var xp = x.parts;
|
||||||
|
var yp = y.parts;
|
||||||
|
while i < LEN - 2 {
|
||||||
|
if xp[i] != yp[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 1u;
|
||||||
|
}
|
||||||
|
if abs(f32(xp[i]) - f32(yp[i])) > 1024 * 16 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
fn to_f32(value: FixedDec) -> f32 {
|
fn to_f32(value: FixedDec) -> f32 {
|
||||||
var parts = value.parts;
|
var parts = value.parts;
|
||||||
|
|
||||||
123
src/client/render/compute/layout.rs
Normal file
123
src/client/render/compute/layout.rs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
use wgpu::{PipelineCompilationOptions, ShaderStages};
|
||||||
|
|
||||||
|
use crate::client::render::util::ArrayBuffer;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
util::{Storage, Texture},
|
||||||
|
ComputeView,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Layout {
|
||||||
|
bind_layout: wgpu::BindGroupLayout,
|
||||||
|
pipeline_layout: wgpu::PipelineLayout,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
pub output: Texture,
|
||||||
|
pub view: Storage,
|
||||||
|
pub work: ArrayBuffer<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, len: usize) -> Self {
|
||||||
|
let view = Storage::init_with(device, "view", ComputeView::default().bytes());
|
||||||
|
let work = ArrayBuffer::init_with(
|
||||||
|
device,
|
||||||
|
"test",
|
||||||
|
wgpu::BufferUsages::STORAGE,
|
||||||
|
&work_vec(config.width, config.height, len),
|
||||||
|
);
|
||||||
|
|
||||||
|
let desc = wgpu::TextureDescriptor {
|
||||||
|
label: Some("compute output"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: config.width,
|
||||||
|
height: config.height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||||
|
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
|
view_formats: &[],
|
||||||
|
};
|
||||||
|
let output = Texture::init(
|
||||||
|
device,
|
||||||
|
desc,
|
||||||
|
wgpu::TextureViewDescriptor::default(),
|
||||||
|
wgpu::SamplerDescriptor::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let bind_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
view.bind_group_layout_entry(0, true, wgpu::ShaderStages::COMPUTE),
|
||||||
|
work.bind_group_layout_entry(
|
||||||
|
1,
|
||||||
|
wgpu::BufferBindingType::Storage { read_only: false },
|
||||||
|
wgpu::ShaderStages::COMPUTE,
|
||||||
|
),
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: ShaderStages::COMPUTE,
|
||||||
|
ty: wgpu::BindingType::StorageTexture {
|
||||||
|
access: wgpu::StorageTextureAccess::WriteOnly,
|
||||||
|
format: output.format(),
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: Some("compute"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("Tile Pipeline Layout"),
|
||||||
|
bind_group_layouts: &[&bind_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view,
|
||||||
|
output,
|
||||||
|
bind_layout,
|
||||||
|
pipeline_layout,
|
||||||
|
work,
|
||||||
|
format: config.format,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_group(&self, device: &wgpu::Device) -> wgpu::BindGroup {
|
||||||
|
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &self.bind_layout,
|
||||||
|
entries: &[
|
||||||
|
self.view.bind_group_entry(0),
|
||||||
|
self.work.bind_group_entry(1),
|
||||||
|
self.output.view_bind_group_entry(2),
|
||||||
|
],
|
||||||
|
label: Some("voxel render"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pipeline(
|
||||||
|
&self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
shader: &wgpu::ShaderModule,
|
||||||
|
) -> wgpu::ComputePipeline {
|
||||||
|
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
||||||
|
label: Some("Voxel Pipeline"),
|
||||||
|
layout: Some(&self.pipeline_layout),
|
||||||
|
entry_point: Some("main"),
|
||||||
|
module: shader,
|
||||||
|
cache: None,
|
||||||
|
compilation_options: PipelineCompilationOptions::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn work_size(width: u32, height: u32, len: usize) -> usize {
|
||||||
|
let varwidth = (2 + len) * 2;
|
||||||
|
(width * height) as usize * (varwidth * 2 + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn work_vec(width: u32, height: u32, len: usize) -> Vec<u32> {
|
||||||
|
vec![0u32; work_size(width, height, len)]
|
||||||
|
}
|
||||||
110
src/client/render/compute/mod.rs
Normal file
110
src/client/render/compute/mod.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
mod layout;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
pub use data::*;
|
||||||
|
use layout::*;
|
||||||
|
|
||||||
|
pub struct ComputePipeline {
|
||||||
|
layout: Layout,
|
||||||
|
pipeline: wgpu::ComputePipeline,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
old_view: ComputeView,
|
||||||
|
old_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
const FIXED_SHADER: &str = include_str!("fixed.wgsl");
|
||||||
|
const SHADER: &str = include_str!("shader.wgsl");
|
||||||
|
|
||||||
|
impl ComputePipeline {
|
||||||
|
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, len: usize) -> Self {
|
||||||
|
let layout = Layout::init(device, config, len);
|
||||||
|
Self {
|
||||||
|
pipeline: layout.pipeline(device, &Self::shader(device, len)),
|
||||||
|
bind_group: layout.bind_group(device),
|
||||||
|
layout,
|
||||||
|
old_view: ComputeView::default(),
|
||||||
|
old_len: len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
|
camera: &Camera,
|
||||||
|
size: &Vector2<u32>,
|
||||||
|
len: usize,
|
||||||
|
) {
|
||||||
|
let mut view = ComputeView::from_camera_size(camera, size, false, len);
|
||||||
|
if view != self.old_view {
|
||||||
|
for (i, b) in 1u32.to_le_bytes().iter().enumerate() {
|
||||||
|
view.bytes[i] = *b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len != self.old_len {
|
||||||
|
self.old_len = len;
|
||||||
|
self.pipeline = self.pipeline(device, &Self::shader(device, len));
|
||||||
|
self.work.set(work_vec(size.x, size.y, len));
|
||||||
|
}
|
||||||
|
let updated = self.work.update(device, encoder, belt)
|
||||||
|
| self.view.update(device, encoder, belt, view.bytes());
|
||||||
|
if updated {
|
||||||
|
self.bind_group = self.layout.bind_group(device);
|
||||||
|
}
|
||||||
|
self.old_view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&self, encoder: &mut wgpu::CommandEncoder) {
|
||||||
|
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());
|
||||||
|
pass.set_pipeline(&self.pipeline);
|
||||||
|
pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
pass.dispatch_workgroups(240, 135, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
|
size: Vector2<u32>,
|
||||||
|
len: usize,
|
||||||
|
) {
|
||||||
|
self.work.set(work_vec(size.x, size.y, len));
|
||||||
|
self.old_len = len;
|
||||||
|
self.output.resize(
|
||||||
|
device,
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: size.x,
|
||||||
|
height: size.y,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.bind_group = self.layout.bind_group(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shader(device: &wgpu::Device, len: usize) -> wgpu::ShaderModule {
|
||||||
|
let string = FIXED_SHADER.to_string() + &SHADER.replace("REPLACE_LEN", &format!("{}", len));
|
||||||
|
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: Some("compute"),
|
||||||
|
source: wgpu::ShaderSource::Wgsl(string.into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ComputePipeline {
|
||||||
|
type Target = Layout;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ComputePipeline {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.layout
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/client/render/compute/shader.wgsl
Normal file
102
src/client/render/compute/shader.wgsl
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
override CHUNK_POW: u32 = 10;
|
||||||
|
const LEN: u32 = REPLACE_LENu;
|
||||||
|
const ILEN: i32 = i32(LEN);
|
||||||
|
const LEN2: u32 = LEN * 2;
|
||||||
|
override WGX: u32 = 8;
|
||||||
|
override WGY: u32 = 8;
|
||||||
|
|
||||||
|
struct View {
|
||||||
|
reset: u32,
|
||||||
|
level: i32,
|
||||||
|
dims: vec2<u32>,
|
||||||
|
scale: FixedDec,
|
||||||
|
corner_x: FixedDec,
|
||||||
|
corner_y: FixedDec,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var<storage> view: View;
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var<storage, read_write> work: array<u32>;
|
||||||
|
@group(0) @binding(2)
|
||||||
|
var output: texture_storage_2d<rgba8unorm, write>;
|
||||||
|
|
||||||
|
@compute @workgroup_size(WGX, WGY, 1)
|
||||||
|
fn main(
|
||||||
|
@builtin(global_invocation_id) id: vec3<u32>
|
||||||
|
) {
|
||||||
|
if id.x > view.dims.x - 1 || id.y > view.dims.y - 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: actually use width
|
||||||
|
let varwidth = LEN + 2;
|
||||||
|
let workwidth = varwidth * 2 + 1;
|
||||||
|
let worki = (id.x * view.dims.y + id.y) * workwidth;
|
||||||
|
let xidx = worki + 1;
|
||||||
|
let yidx = xidx + varwidth;
|
||||||
|
|
||||||
|
// let dec = view.corner_x.dec;
|
||||||
|
// var rel_x = FixedDec(POS, dec, array<u32, LEN>());
|
||||||
|
// rel_x.parts[0] = id.x;
|
||||||
|
// rel_x = shr(rel_x, view.level);
|
||||||
|
// var rel_y = FixedDec(POS, dec, array<u32, LEN>());
|
||||||
|
// rel_y.parts[0] = id.y;
|
||||||
|
// rel_y = shr(rel_y, view.level);
|
||||||
|
// let cx = add(view.corner_x, rel_x);
|
||||||
|
// let cy = add(view.corner_y, rel_y);
|
||||||
|
let fdims = vec2<f32>(view.dims);
|
||||||
|
var stretch: vec2<f32>;
|
||||||
|
if fdims.x < fdims.y {
|
||||||
|
stretch = vec2(fdims.x / fdims.y, 1.0);
|
||||||
|
} else {
|
||||||
|
stretch = vec2(1.0, fdims.y / fdims.x);
|
||||||
|
}
|
||||||
|
let fpos = (vec2<f32>(id.xy) / fdims - 0.5) * stretch;
|
||||||
|
let cx = add(mul(from_f32(fpos.x), view.scale), view.corner_x);
|
||||||
|
let cy = add(mul(from_f32(fpos.y), view.scale), view.corner_y);
|
||||||
|
var x: FixedDec;
|
||||||
|
var y: FixedDec;
|
||||||
|
var i = work[worki];
|
||||||
|
if bool(view.reset) {
|
||||||
|
x = zero();
|
||||||
|
y = zero();
|
||||||
|
i = 0;
|
||||||
|
} else {
|
||||||
|
x = FixedDec(work[xidx + 0], bitcast<i32>(work[xidx + 1]), array<u32, LEN>());
|
||||||
|
y = FixedDec(work[yidx + 0], bitcast<i32>(work[yidx + 1]), array<u32, LEN>());
|
||||||
|
for (var j = 0u; j < LEN; j += 1u) {
|
||||||
|
x.parts[j] = work[xidx + 2 + j];
|
||||||
|
y.parts[j] = work[yidx + 2 + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let max = i + 1;
|
||||||
|
let thresh = from_f32(2.0 * 2.0);
|
||||||
|
loop {
|
||||||
|
let x2 = mul(x, x);
|
||||||
|
let y2 = mul(y, y);
|
||||||
|
if gt(add(x2, y2), thresh) || i >= max {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let xy = mul(x, y);
|
||||||
|
y = add(add(xy, xy), cy);
|
||||||
|
x = add(sub(x2, y2), cx);
|
||||||
|
i += 1u;
|
||||||
|
}
|
||||||
|
work[worki] = i;
|
||||||
|
work[xidx + 0] = x.sign; work[xidx + 1] = bitcast<u32>(x.dec);
|
||||||
|
work[yidx + 0] = y.sign; work[yidx + 1] = bitcast<u32>(y.dec);
|
||||||
|
for (var j = 0u; j < LEN; j += 1u) {
|
||||||
|
work[xidx + 2 + j] = x.parts[j];
|
||||||
|
work[yidx + 2 + j] = y.parts[j];
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
textureStore(output, id.xy, vec4(color, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
mod tile;
|
mod compute;
|
||||||
|
mod output;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod view;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use compute::ComputePipeline;
|
||||||
use nalgebra::Vector2;
|
use nalgebra::Vector2;
|
||||||
use tile::TilePipeline;
|
use output::RenderPipeline;
|
||||||
use util::GPUTimer;
|
use util::GPUTimer;
|
||||||
|
use view::ChunkView;
|
||||||
use winit::{dpi::PhysicalSize, window::Window};
|
use winit::{dpi::PhysicalSize, window::Window};
|
||||||
|
|
||||||
use super::camera::Camera;
|
use super::camera::Camera;
|
||||||
|
|
||||||
|
const CHUNK_POW: u32 = 7;
|
||||||
|
const CHUNK_WIDTH: u32 = 2u32.pow(CHUNK_POW);
|
||||||
|
|
||||||
pub struct Renderer<'a> {
|
pub struct Renderer<'a> {
|
||||||
size: Vector2<u32>,
|
size: Vector2<u32>,
|
||||||
surface: wgpu::Surface<'a>,
|
surface: wgpu::Surface<'a>,
|
||||||
@@ -19,15 +26,18 @@ pub struct Renderer<'a> {
|
|||||||
config: wgpu::SurfaceConfiguration,
|
config: wgpu::SurfaceConfiguration,
|
||||||
staging_belt: wgpu::util::StagingBelt,
|
staging_belt: wgpu::util::StagingBelt,
|
||||||
timer: GPUTimer,
|
timer: GPUTimer,
|
||||||
|
chunk_view: ChunkView,
|
||||||
|
len: usize,
|
||||||
|
|
||||||
tile_pipeline: TilePipeline,
|
compute_pipeline: ComputePipeline,
|
||||||
|
render_pipeline: RenderPipeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer<'_> {
|
impl Renderer<'_> {
|
||||||
pub fn new(window: Arc<Window>) -> Self {
|
pub fn new(window: Arc<Window>) -> Self {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||||
backends: wgpu::Backends::PRIMARY,
|
backends: wgpu::Backends::PRIMARY,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@@ -90,8 +100,14 @@ impl Renderer<'_> {
|
|||||||
let staging_belt = wgpu::util::StagingBelt::new(1024);
|
let staging_belt = wgpu::util::StagingBelt::new(1024);
|
||||||
let timer = GPUTimer::new(&device, queue.get_timestamp_period(), 1);
|
let timer = GPUTimer::new(&device, queue.get_timestamp_period(), 1);
|
||||||
|
|
||||||
|
let len = 2;
|
||||||
|
|
||||||
|
let compute_pipeline = ComputePipeline::init(&device, &config, len);
|
||||||
|
let render_pipeline = RenderPipeline::init(&device, &config, &compute_pipeline.output);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
tile_pipeline: TilePipeline::init(&device, &config),
|
render_pipeline,
|
||||||
|
compute_pipeline,
|
||||||
size: Vector2::new(size.width, size.height),
|
size: Vector2::new(size.width, size.height),
|
||||||
staging_belt,
|
staging_belt,
|
||||||
surface,
|
surface,
|
||||||
@@ -100,47 +116,44 @@ impl Renderer<'_> {
|
|||||||
device,
|
device,
|
||||||
config,
|
config,
|
||||||
queue,
|
queue,
|
||||||
|
chunk_view: ChunkView::new(),
|
||||||
|
len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, camera: &Camera) {
|
pub fn render(&mut self, camera: &Camera) {
|
||||||
self.tile_pipeline.update(
|
|
||||||
|
self.len = (camera.zoom.level() as f32 / 15.0 + 2.0).round() as usize;
|
||||||
|
println!("{}", self.len);
|
||||||
|
// let new = (camera.zoom.level() as f32 / 15.0 + 2.0).round() as usize;
|
||||||
|
// println!("{}", new);
|
||||||
|
|
||||||
|
self.compute_pipeline.update(
|
||||||
&self.device,
|
&self.device,
|
||||||
&mut self.encoder,
|
&mut self.encoder,
|
||||||
&mut self.staging_belt,
|
&mut self.staging_belt,
|
||||||
camera,
|
camera,
|
||||||
&self.size,
|
&self.size,
|
||||||
|
self.len,
|
||||||
|
);
|
||||||
|
self.chunk_view.update(camera, &self.size);
|
||||||
|
self.render_pipeline.update(
|
||||||
|
&self.device,
|
||||||
|
&mut self.encoder,
|
||||||
|
&mut self.staging_belt,
|
||||||
|
&self.chunk_view.render,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
|
||||||
let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device));
|
let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device));
|
||||||
let output = self.surface.get_current_texture().unwrap();
|
let output = self.surface.get_current_texture().unwrap();
|
||||||
let view = output
|
|
||||||
.texture
|
|
||||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
self.timer.start(&mut encoder, 0);
|
self.timer.start(&mut encoder, 0);
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
self.compute_pipeline.run(&mut encoder);
|
||||||
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.stop(&mut encoder, 0);
|
||||||
|
|
||||||
self.timer.resolve(&mut encoder);
|
self.timer.resolve(&mut encoder);
|
||||||
|
|
||||||
|
self.render_pipeline.draw(&mut encoder, &output);
|
||||||
|
|
||||||
self.staging_belt.finish();
|
self.staging_belt.finish();
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
output.present();
|
output.present();
|
||||||
@@ -154,6 +167,8 @@ impl Renderer<'_> {
|
|||||||
self.config.width = size.width;
|
self.config.width = size.width;
|
||||||
self.config.height = size.height;
|
self.config.height = size.height;
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(&self.device, &self.config);
|
||||||
|
self.compute_pipeline.resize(&self.device, &mut self.encoder, &mut self.staging_belt, self.size, self.len);
|
||||||
|
self.render_pipeline.resize(&self.device, self.size, &self.compute_pipeline.output);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder {
|
fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder {
|
||||||
|
|||||||
45
src/client/render/output/data.rs
Normal file
45
src/client/render/output/data.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use nalgebra::Vector2;
|
||||||
|
|
||||||
|
use crate::client::render::CHUNK_POW;
|
||||||
|
|
||||||
|
use super::{Camera, CHUNK_WIDTH};
|
||||||
|
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
|
pub struct WindowView {
|
||||||
|
pub stretch: Vector2<f32>,
|
||||||
|
pub pos: Vector2<f32>,
|
||||||
|
pub rendered_chunks: Vector2<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl bytemuck::Pod for WindowView {}
|
||||||
|
unsafe impl bytemuck::Zeroable for WindowView {}
|
||||||
|
|
||||||
|
impl WindowView {
|
||||||
|
pub fn from_camera_size(camera: &Camera, size: &Vector2<u32>) -> Self {
|
||||||
|
let visible_chunks = (size * 2 / CHUNK_WIDTH).add_scalar(1);
|
||||||
|
let rendered_chunks = Vector2::new(
|
||||||
|
visible_chunks.x.next_power_of_two(),
|
||||||
|
visible_chunks.y.next_power_of_two(),
|
||||||
|
);
|
||||||
|
let adj_zoom = camera.zoom.level() - CHUNK_POW as i32;
|
||||||
|
let pos = camera.pos.zip_map(&rendered_chunks, |pos, rc| {
|
||||||
|
let p = (pos << adj_zoom).with_lens(1, 1);
|
||||||
|
let (pw, pd) = p.split_whole_dec();
|
||||||
|
let mut chunk = (pw.parts().first().unwrap_or(&0) & (rc - 1)) as f32;
|
||||||
|
if pw.is_neg() {
|
||||||
|
chunk = rc as f32 - chunk;
|
||||||
|
}
|
||||||
|
let dec = f32::from(pd);
|
||||||
|
chunk + dec
|
||||||
|
});
|
||||||
|
|
||||||
|
let stretch = size.cast::<f32>() * camera.zoom.rel_zoom() / (CHUNK_WIDTH as f32);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pos,
|
||||||
|
stretch,
|
||||||
|
rendered_chunks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/client/render/output/layout.rs
Normal file
159
src/client/render/output/layout.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
use nalgebra::Vector2;
|
||||||
|
|
||||||
|
use crate::client::render::util::Texture;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
util::{ResizableTexture, Storage},
|
||||||
|
WindowView, CHUNK_WIDTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Layout {
|
||||||
|
render_bind_layout: wgpu::BindGroupLayout,
|
||||||
|
render_pipeline_layout: wgpu::PipelineLayout,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
pub view: Storage,
|
||||||
|
pub chunks: ResizableTexture,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const LABEL: &str = file!();
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub fn init(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
config: &wgpu::SurfaceConfiguration,
|
||||||
|
input: &Texture,
|
||||||
|
) -> Self {
|
||||||
|
let view = Storage::init_with(device, "view", bytemuck::bytes_of(&WindowView::default()));
|
||||||
|
|
||||||
|
let texture_desc = wgpu::TextureDescriptor {
|
||||||
|
label: Some("chunks"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: CHUNK_WIDTH,
|
||||||
|
height: CHUNK_WIDTH,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::R32Uint,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::STORAGE_BINDING,
|
||||||
|
view_formats: &[wgpu::TextureFormat::R32Uint],
|
||||||
|
};
|
||||||
|
let view_desc = wgpu::TextureViewDescriptor {
|
||||||
|
label: Some("chunk view"),
|
||||||
|
dimension: Some(wgpu::TextureViewDimension::D2Array),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let chunks = ResizableTexture::new(device, texture_desc, view_desc);
|
||||||
|
|
||||||
|
let render_bind_layout =
|
||||||
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
view.bind_group_layout_entry(
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::VERTEX,
|
||||||
|
),
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Uint,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2Array,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::VERTEX,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 3,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: Some(LABEL),
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline_layout =
|
||||||
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some(LABEL),
|
||||||
|
bind_group_layouts: &[&render_bind_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view,
|
||||||
|
chunks,
|
||||||
|
render_bind_layout,
|
||||||
|
render_pipeline_layout,
|
||||||
|
format: config.format,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_group(&self, device: &wgpu::Device, input: &Texture) -> wgpu::BindGroup {
|
||||||
|
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &self.render_bind_layout,
|
||||||
|
entries: &[
|
||||||
|
self.view.bind_group_entry(0),
|
||||||
|
self.chunks.view_entry(1),
|
||||||
|
input.view_bind_group_entry(2),
|
||||||
|
input.sampler_bind_group_entry(3),
|
||||||
|
],
|
||||||
|
label: Some(LABEL),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pipeline(
|
||||||
|
&self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
shader: &wgpu::ShaderModule,
|
||||||
|
) -> wgpu::RenderPipeline {
|
||||||
|
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some(LABEL),
|
||||||
|
layout: Some(&self.render_pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: shader,
|
||||||
|
entry_point: Some("vs_main"),
|
||||||
|
buffers: &[],
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: shader,
|
||||||
|
entry_point: Some("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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/client/render/output/mod.rs
Normal file
88
src/client/render/output/mod.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use wgpu::include_wgsl;
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
mod layout;
|
||||||
|
|
||||||
|
use super::{util::Texture, *};
|
||||||
|
pub use data::*;
|
||||||
|
use layout::*;
|
||||||
|
|
||||||
|
pub struct RenderPipeline {
|
||||||
|
layout: Layout,
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
size: Vector2<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("shader.wgsl");
|
||||||
|
|
||||||
|
impl RenderPipeline {
|
||||||
|
pub fn init(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
config: &wgpu::SurfaceConfiguration,
|
||||||
|
input: &Texture,
|
||||||
|
) -> Self {
|
||||||
|
let layout = Layout::init(device, config, input);
|
||||||
|
let shader = device.create_shader_module(SHADER);
|
||||||
|
Self {
|
||||||
|
pipeline: layout.pipeline(device, &shader),
|
||||||
|
bind_group: layout.bind_group(device, input),
|
||||||
|
size: Vector2::zeros(),
|
||||||
|
layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
|
view: &WindowView,
|
||||||
|
) {
|
||||||
|
self.view
|
||||||
|
.update(device, encoder, belt, bytemuck::bytes_of(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, encoder: &mut wgpu::CommandEncoder, output: &wgpu::SurfaceTexture) {
|
||||||
|
let view = output
|
||||||
|
.texture
|
||||||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
render_pass.draw(0..4, 0..1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, device: &wgpu::Device, size: Vector2<u32>, input: &Texture) {
|
||||||
|
self.bind_group = self.layout.bind_group(device, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for RenderPipeline {
|
||||||
|
type Target = Layout;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for RenderPipeline {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.layout
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/client/render/output/shader.wgsl
Normal file
76
src/client/render/output/shader.wgsl
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
struct View {
|
||||||
|
stretch: vec2<f32>,
|
||||||
|
pos: vec2<f32>,
|
||||||
|
rendered_chunks: vec2<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var<storage> view: View;
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var chunks: texture_2d_array<u32>;
|
||||||
|
|
||||||
|
@group(0) @binding(2)
|
||||||
|
var tex: texture_2d<f32>;
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var sam: sampler;
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) vertex_pos: vec4<f32>,
|
||||||
|
@location(0) world_pos: vec2<f32>,
|
||||||
|
@location(1) tex_pos: vec2<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@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 / 2.0;
|
||||||
|
out.world_pos.y *= -1.0;
|
||||||
|
out.world_pos *= view.stretch;
|
||||||
|
out.world_pos += view.pos;
|
||||||
|
|
||||||
|
let pos2 = vec2<f32>(
|
||||||
|
f32(vi % 2u),
|
||||||
|
f32(vi / 2u),
|
||||||
|
);
|
||||||
|
out.tex_pos = pos2;
|
||||||
|
out.tex_pos.y = 1.0 - out.tex_pos.y;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(
|
||||||
|
in: VertexOutput,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
|
// let a = textureLoad(chunks, vec2<u32>(0), 0, 0);
|
||||||
|
// let rc = vec2<i32>(view.rendered_chunks);
|
||||||
|
// let rcf = vec2<f32>(rc);
|
||||||
|
// let cposi = vec2<i32>(floor(in.world_pos));
|
||||||
|
// let cposu = vec2<i32>(
|
||||||
|
// rem_euclid(cposi.x, rc.x),
|
||||||
|
// rem_euclid(cposi.y, rc.y)
|
||||||
|
// );
|
||||||
|
// let cposf = vec2<f32>(cposu);
|
||||||
|
// return vec4(cposf / rcf, 0.0, 1.0);
|
||||||
|
return textureSample(tex, sam, in.tex_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn div_euclid(x: i32, y: i32) -> i32 {
|
||||||
|
if x < 0 {
|
||||||
|
return -((-x - 1) / y) - 1;
|
||||||
|
}
|
||||||
|
return x / y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rem_euclid(x: i32, y: i32) -> i32 {
|
||||||
|
return x - div_euclid(x, y) * y;
|
||||||
|
}
|
||||||
@@ -1,35 +0,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;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
use super::{util::Storage, View};
|
|
||||||
|
|
||||||
pub struct TileLayout {
|
|
||||||
render_bind_layout: wgpu::BindGroupLayout,
|
|
||||||
render_pipeline_layout: wgpu::PipelineLayout,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
pub view: Storage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TileLayout {
|
|
||||||
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
|
||||||
let view = Storage::init_with(
|
|
||||||
device,
|
|
||||||
"view",
|
|
||||||
View::default().bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let render_bind_layout =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[view.bind_group_layout_entry(0, true)],
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
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::from_camera_size(camera, size).bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ pub struct ArrayBuffer<T: bytemuck::Pod> {
|
|||||||
buffer: wgpu::Buffer,
|
buffer: wgpu::Buffer,
|
||||||
label: String,
|
label: String,
|
||||||
usage: BufferUsages,
|
usage: BufferUsages,
|
||||||
updates: Vec<ArrBufUpdate<T>>,
|
update: Option<Vec<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
||||||
@@ -36,34 +36,24 @@ impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
|||||||
if self.len == 0 {
|
if self.len == 0 {
|
||||||
return resized;
|
return resized;
|
||||||
}
|
}
|
||||||
for update in &self.updates {
|
if let Some(update) = self.update.take() {
|
||||||
let mut view = belt.write_buffer(
|
let mut view = belt.write_buffer(
|
||||||
encoder,
|
encoder,
|
||||||
&self.buffer,
|
&self.buffer,
|
||||||
(update.offset * std::mem::size_of::<T>()) as BufferAddress,
|
0,
|
||||||
unsafe {
|
unsafe {
|
||||||
std::num::NonZeroU64::new_unchecked(
|
std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(&update[..]) as u64)
|
||||||
std::mem::size_of_val(&update.data[..]) as u64
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
device,
|
device,
|
||||||
);
|
);
|
||||||
view.copy_from_slice(bytemuck::cast_slice(&update.data));
|
view.copy_from_slice(bytemuck::cast_slice(&update));
|
||||||
}
|
}
|
||||||
self.updates.clear();
|
|
||||||
resized
|
resized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, data: Vec<T>) -> usize {
|
pub fn set(&mut self, data: Vec<T>) {
|
||||||
let data_len = data.len();
|
self.new_len = data.len();
|
||||||
let pos = self.new_len;
|
self.update = Some(data);
|
||||||
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 {
|
pub fn init(device: &wgpu::Device, label: &str, usage: BufferUsages) -> Self {
|
||||||
@@ -82,7 +72,7 @@ impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
|||||||
Self::init_buf_with(device, label, usage, data)
|
Self::init_buf_with(device, label, usage, data)
|
||||||
},
|
},
|
||||||
label: label.to_string(),
|
label: label.to_string(),
|
||||||
updates: Vec::new(),
|
update: None,
|
||||||
usage,
|
usage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,8 +107,8 @@ impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
|||||||
pub fn bind_group_layout_entry(
|
pub fn bind_group_layout_entry(
|
||||||
&self,
|
&self,
|
||||||
binding: u32,
|
binding: u32,
|
||||||
visibility: wgpu::ShaderStages,
|
|
||||||
ty: wgpu::BufferBindingType,
|
ty: wgpu::BufferBindingType,
|
||||||
|
visibility: wgpu::ShaderStages,
|
||||||
) -> wgpu::BindGroupLayoutEntry {
|
) -> wgpu::BindGroupLayoutEntry {
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding,
|
binding,
|
||||||
|
|||||||
@@ -4,26 +4,34 @@ use wgpu::util::DeviceExt;
|
|||||||
|
|
||||||
pub struct Storage {
|
pub struct Storage {
|
||||||
buffer: wgpu::Buffer,
|
buffer: wgpu::Buffer,
|
||||||
|
old_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Storage {
|
impl Storage {
|
||||||
pub fn init<T: Default + bytemuck::Pod>(device: &wgpu::Device, name: &str) -> Self {
|
pub fn init<T: Default + bytemuck::Pod>(device: &wgpu::Device, name: &str) -> Self {
|
||||||
|
let def = [T::default()];
|
||||||
|
let default = bytemuck::cast_slice(&def);
|
||||||
Self {
|
Self {
|
||||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||||
contents: bytemuck::cast_slice(&[T::default()]),
|
contents: default,
|
||||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
}),
|
}),
|
||||||
|
old_len: default.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn init_with(device: &wgpu::Device, name: &str, data: &[u8]) -> Self {
|
pub fn init_with(device: &wgpu::Device, name: &str, data: &[u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
buffer: Self::init_buf(device, name, data),
|
||||||
|
old_len: data.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn init_buf(device: &wgpu::Device, name: &str, data: &[u8]) -> wgpu::Buffer {
|
||||||
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||||
contents: data,
|
contents: data,
|
||||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
}),
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn update(
|
pub fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -31,29 +39,33 @@ impl Storage {
|
|||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
belt: &mut wgpu::util::StagingBelt,
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) {
|
) -> bool {
|
||||||
|
if data.len() != self.old_len {
|
||||||
|
self.buffer = Self::init_buf(device, "too lazy", data);
|
||||||
|
self.old_len = data.len();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
let mut view = belt.write_buffer(
|
let mut view = belt.write_buffer(
|
||||||
encoder,
|
encoder,
|
||||||
&self.buffer,
|
&self.buffer,
|
||||||
0,
|
0,
|
||||||
unsafe {
|
unsafe { std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(data) as u64) },
|
||||||
std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(data) as u64)
|
|
||||||
},
|
|
||||||
device,
|
device,
|
||||||
);
|
);
|
||||||
view.copy_from_slice(data);
|
view.copy_from_slice(data);
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_group_layout_entry(
|
pub fn bind_group_layout_entry(
|
||||||
&self,
|
&self,
|
||||||
binding: u32,
|
binding: u32,
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
|
visibility: wgpu::ShaderStages,
|
||||||
) -> wgpu::BindGroupLayoutEntry {
|
) -> wgpu::BindGroupLayoutEntry {
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding,
|
binding,
|
||||||
visibility: wgpu::ShaderStages::VERTEX
|
visibility,
|
||||||
| wgpu::ShaderStages::FRAGMENT
|
|
||||||
| wgpu::ShaderStages::COMPUTE,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
ty: wgpu::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Storage { read_only },
|
ty: wgpu::BufferBindingType::Storage { read_only },
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
|
|||||||
@@ -89,3 +89,39 @@ impl Texture {
|
|||||||
self.texture_desc.format
|
self.texture_desc.format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ResizableTexture {
|
||||||
|
texture_desc: wgpu::TextureDescriptor<'static>,
|
||||||
|
view_desc: wgpu::TextureViewDescriptor<'static>,
|
||||||
|
pub texture: wgpu::Texture,
|
||||||
|
pub view: wgpu::TextureView,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResizableTexture {
|
||||||
|
pub fn new(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
texture_desc: wgpu::TextureDescriptor<'static>,
|
||||||
|
view_desc: wgpu::TextureViewDescriptor<'static>,
|
||||||
|
) -> Self {
|
||||||
|
let texture = device.create_texture(&texture_desc);
|
||||||
|
let view = texture.create_view(&view_desc);
|
||||||
|
Self {
|
||||||
|
texture,
|
||||||
|
view,
|
||||||
|
texture_desc,
|
||||||
|
view_desc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn resize_layers(&mut self, device: &wgpu::Device, layers: u32) {
|
||||||
|
self.texture_desc.size.depth_or_array_layers = layers;
|
||||||
|
self.texture = device.create_texture(&self.texture_desc);
|
||||||
|
self.view = self.texture.create_view(&self.view_desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&self.view),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
66
src/client/render/view.rs
Normal file
66
src/client/render/view.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use nalgebra::Vector2;
|
||||||
|
|
||||||
|
use crate::{client::camera::Camera, util::FixedDec};
|
||||||
|
|
||||||
|
use super::{output::WindowView, CHUNK_POW};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ChunkView {
|
||||||
|
pub render: WindowView,
|
||||||
|
pub chunk_queue: HashSet<Vector2<FixedDec>>,
|
||||||
|
pub visible_chunks: HashSet<Vector2<FixedDec>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChunkView {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, camera: &Camera, size: &Vector2<u32>) {
|
||||||
|
let render = WindowView::from_camera_size(camera, size);
|
||||||
|
|
||||||
|
if self.render == render {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.render = render;
|
||||||
|
|
||||||
|
let corner_offset = ((size / 2).cast() * camera.zoom.rel_zoom())
|
||||||
|
.map(|x| FixedDec::from(x) >> camera.zoom.level());
|
||||||
|
let bot_left = &camera.pos - &corner_offset;
|
||||||
|
let top_right = &camera.pos + &corner_offset;
|
||||||
|
let mult = FixedDec::one() >> (CHUNK_POW as i32 - camera.zoom.level());
|
||||||
|
let blc = bot_left
|
||||||
|
.component_mul(&Vector2::from_element(mult.clone()))
|
||||||
|
.map(FixedDec::floor);
|
||||||
|
let trc = top_right
|
||||||
|
.component_mul(&Vector2::from_element(mult))
|
||||||
|
.map(FixedDec::floor);
|
||||||
|
|
||||||
|
let mut visible = HashSet::new();
|
||||||
|
let mut x = blc.x.clone();
|
||||||
|
while x <= trc.x {
|
||||||
|
let mut y = blc.y.clone();
|
||||||
|
while y <= trc.y {
|
||||||
|
visible.insert(Vector2::new(x.clone(), y.clone()));
|
||||||
|
y += FixedDec::one();
|
||||||
|
}
|
||||||
|
x += FixedDec::one();
|
||||||
|
}
|
||||||
|
|
||||||
|
let new = visible
|
||||||
|
.difference(&self.visible_chunks)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let old = self
|
||||||
|
.visible_chunks
|
||||||
|
.difference(&visible)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.chunk_queue.retain(|p| !old.contains(p));
|
||||||
|
self.chunk_queue.extend(new);
|
||||||
|
self.visible_chunks = visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,6 +8,9 @@ const FRAC_MASK: u32 = FRAC_BIT - 1;
|
|||||||
|
|
||||||
impl From<f32> for FixedDec {
|
impl From<f32> for FixedDec {
|
||||||
fn from(value: f32) -> Self {
|
fn from(value: f32) -> Self {
|
||||||
|
if value.is_zero() {
|
||||||
|
return Self::zero();
|
||||||
|
}
|
||||||
let raw = value.to_bits() & INV_SIGN_MASK;
|
let raw = value.to_bits() & INV_SIGN_MASK;
|
||||||
let mut exp = (raw >> 23) as i32 - 127;
|
let mut exp = (raw >> 23) as i32 - 127;
|
||||||
let mut frac = raw & FRAC_MASK;
|
let mut frac = raw & FRAC_MASK;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const NEG: bool = true;
|
|||||||
// Might want to try to make it abstract over the direction
|
// Might want to try to make it abstract over the direction
|
||||||
// of growth, or use a VecDeque, but doesn't seem worth for
|
// of growth, or use a VecDeque, but doesn't seem worth for
|
||||||
// now
|
// now
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct FixedDec {
|
pub struct FixedDec {
|
||||||
sign: bool,
|
sign: bool,
|
||||||
dec: i32,
|
dec: i32,
|
||||||
@@ -25,6 +25,14 @@ pub struct FixedDec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FixedDec {
|
impl FixedDec {
|
||||||
|
pub fn one() -> Self {
|
||||||
|
Self {
|
||||||
|
sign: POS,
|
||||||
|
dec: 1,
|
||||||
|
parts: vec![1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_parts(sign: bool, dec: i32, parts: Vec<u32>) -> Self {
|
pub fn from_parts(sign: bool, dec: i32, parts: Vec<u32>) -> Self {
|
||||||
Self { sign, dec, parts }
|
Self { sign, dec, parts }
|
||||||
}
|
}
|
||||||
@@ -62,18 +70,26 @@ impl FixedDec {
|
|||||||
} else {
|
} else {
|
||||||
self.dec -= rem_beg as i32;
|
self.dec -= rem_beg as i32;
|
||||||
}
|
}
|
||||||
|
if self.parts.is_empty() {
|
||||||
|
self.sign = POS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_whole_len(&mut self, len: i32) {
|
pub fn set_whole_len(&mut self, len: i32) {
|
||||||
let diff = len - self.dec;
|
let diff = len - self.dec;
|
||||||
self.parts
|
let remove = 0..usize::try_from(-diff).unwrap_or(0).min(self.parts.len());
|
||||||
.splice(0..usize::try_from(-diff).unwrap_or(0), (0..diff).map(|_| 0));
|
self.parts.splice(remove, (0..diff).map(|_| 0));
|
||||||
self.dec += diff;
|
self.dec += diff;
|
||||||
if self.parts.is_empty() {
|
if self.parts.is_empty() {
|
||||||
self.dec = 0;
|
self.dec = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_whole_len(mut self, len: i32) -> Self {
|
||||||
|
self.set_whole_len(len);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_dec_len(&mut self, len: i32) {
|
pub fn set_dec_len(&mut self, len: i32) {
|
||||||
let len = usize::try_from(len + self.dec).unwrap_or(0);
|
let len = usize::try_from(len + self.dec).unwrap_or(0);
|
||||||
self.parts.resize(len, 0);
|
self.parts.resize(len, 0);
|
||||||
@@ -82,6 +98,31 @@ impl FixedDec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_dec_len(mut self, len: i32) -> Self {
|
||||||
|
self.set_dec_len(len);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_lens(self, whole_len: i32, dec_len: i32) -> Self {
|
||||||
|
self.with_whole_len(whole_len).with_dec_len(dec_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_whole_dec(&self) -> (FixedDec, FixedDec) {
|
||||||
|
let take_skip = usize::try_from(self.dec).unwrap_or(0);
|
||||||
|
let whole = FixedDec {
|
||||||
|
sign: self.sign,
|
||||||
|
dec: self.dec,
|
||||||
|
parts: self.parts.iter().take(take_skip).copied().collect(),
|
||||||
|
};
|
||||||
|
let dec = if self.dec >= 0 { 0 } else { self.dec };
|
||||||
|
let dec = FixedDec {
|
||||||
|
sign: self.sign,
|
||||||
|
dec,
|
||||||
|
parts: self.parts.iter().skip(take_skip).copied().collect(),
|
||||||
|
};
|
||||||
|
(whole, dec)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_precision(&mut self, prec: usize) {
|
pub fn set_precision(&mut self, prec: usize) {
|
||||||
self.parts.resize(prec, 0);
|
self.parts.resize(prec, 0);
|
||||||
if self.parts.is_empty() {
|
if self.parts.is_empty() {
|
||||||
@@ -94,6 +135,10 @@ impl FixedDec {
|
|||||||
bytes.extend(self.dec.to_le_bytes());
|
bytes.extend(self.dec.to_le_bytes());
|
||||||
bytes.extend(self.parts.iter().flat_map(|p| p.to_le_bytes()));
|
bytes.extend(self.parts.iter().flat_map(|p| p.to_le_bytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parts(&self) -> &[u32] {
|
||||||
|
&self.parts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for FixedDec {
|
impl Display for FixedDec {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::ops::{Add, AddAssign, Mul, Neg, Shr, Sub, SubAssign};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
ops::{Add, AddAssign, Mul, MulAssign, Neg, Shl, Shr, Sub, SubAssign},
|
||||||
|
};
|
||||||
|
|
||||||
impl Zero for FixedDec {
|
impl Zero for FixedDec {
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
@@ -16,8 +19,29 @@ impl Zero for FixedDec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Shl<i32> for FixedDec {
|
||||||
|
type Output = FixedDec;
|
||||||
|
fn shl(self, rhs: i32) -> Self::Output {
|
||||||
|
&self << rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shl<i32> for &FixedDec {
|
||||||
|
type Output = FixedDec;
|
||||||
|
fn shl(self, rhs: i32) -> Self::Output {
|
||||||
|
self >> -rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Shr<i32> for FixedDec {
|
impl Shr<i32> for FixedDec {
|
||||||
type Output = Self;
|
type Output = FixedDec;
|
||||||
|
fn shr(self, rhs: i32) -> Self::Output {
|
||||||
|
&self >> rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shr<i32> for &FixedDec {
|
||||||
|
type Output = FixedDec;
|
||||||
|
|
||||||
fn shr(self, rhs: i32) -> Self::Output {
|
fn shr(self, rhs: i32) -> Self::Output {
|
||||||
let mut parts = Vec::with_capacity(self.parts.len());
|
let mut parts = Vec::with_capacity(self.parts.len());
|
||||||
@@ -26,14 +50,14 @@ impl Shr<i32> for FixedDec {
|
|||||||
let mask = (1 << sr) - 1;
|
let mask = (1 << sr) - 1;
|
||||||
let dec = self.dec - rhs.div_floor(32);
|
let dec = self.dec - rhs.div_floor(32);
|
||||||
let mut rem = 0;
|
let mut rem = 0;
|
||||||
for part in self.parts {
|
for part in &self.parts {
|
||||||
parts.push((part >> sr) ^ rem);
|
parts.push((part >> sr) ^ rem);
|
||||||
rem = (part & mask).unbounded_shl(sl);
|
rem = (part & mask).unbounded_shl(sl);
|
||||||
}
|
}
|
||||||
if rem != 0 {
|
if rem != 0 {
|
||||||
parts.push(rem);
|
parts.push(rem);
|
||||||
}
|
}
|
||||||
Self {
|
Self::Output {
|
||||||
dec,
|
dec,
|
||||||
parts,
|
parts,
|
||||||
sign: self.sign,
|
sign: self.sign,
|
||||||
@@ -124,6 +148,7 @@ fn add(dest: &mut FixedDec, src: &impl Fn(usize) -> u32, rhs: &FixedDec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dest.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_dec(x: &FixedDec, y: &FixedDec) -> (i32, usize) {
|
fn new_dec(x: &FixedDec, y: &FixedDec) -> (i32, usize) {
|
||||||
@@ -157,6 +182,12 @@ impl SubAssign<&FixedDec> for FixedDec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SubAssign<FixedDec> for FixedDec {
|
||||||
|
fn sub_assign(&mut self, rhs: FixedDec) {
|
||||||
|
*self += &-rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Neg for &FixedDec {
|
impl Neg for &FixedDec {
|
||||||
type Output = FixedDec;
|
type Output = FixedDec;
|
||||||
|
|
||||||
@@ -230,6 +261,18 @@ impl Mul<FixedDec> for &FixedDec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MulAssign<&FixedDec> for FixedDec {
|
||||||
|
fn mul_assign(&mut self, rhs: &FixedDec) {
|
||||||
|
*self = &*self * rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MulAssign<FixedDec> for FixedDec {
|
||||||
|
fn mul_assign(&mut self, rhs: FixedDec) {
|
||||||
|
*self = &*self * rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
|
fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
|
||||||
let lsb = x.wrapping_mul(y);
|
let lsb = x.wrapping_mul(y);
|
||||||
let a = x & 0xffff;
|
let a = x & 0xffff;
|
||||||
@@ -242,3 +285,65 @@ fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
|
|||||||
let msb = ((ad + bc) >> 16) + ((carry as u32) << 16) + b * d;
|
let msb = ((ad + bc) >> 16) + ((carry as u32) << 16) + b * d;
|
||||||
(lsb, msb)
|
(lsb, msb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for FixedDec {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for FixedDec {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
match (self.sign, other.sign) {
|
||||||
|
(POS, NEG) => return Ordering::Greater,
|
||||||
|
(NEG, POS) => return Ordering::Less,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
match (self.dec.cmp(&other.dec), self.sign) {
|
||||||
|
(Ordering::Less, POS) => return Ordering::Less,
|
||||||
|
(Ordering::Less, NEG) => return Ordering::Greater,
|
||||||
|
(Ordering::Greater, POS) => return Ordering::Greater,
|
||||||
|
(Ordering::Greater, NEG) => return Ordering::Less,
|
||||||
|
(Ordering::Equal, _) => (),
|
||||||
|
}
|
||||||
|
match (self.parts.first().unwrap_or(&0).cmp(other.parts.first().unwrap_or(&0)), self.sign) {
|
||||||
|
(Ordering::Less, POS) => Ordering::Less,
|
||||||
|
(Ordering::Less, NEG) => Ordering::Greater,
|
||||||
|
(Ordering::Greater, POS) => Ordering::Greater,
|
||||||
|
(Ordering::Greater, NEG) => Ordering::Less,
|
||||||
|
(Ordering::Equal, _) => Ordering::Equal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FixedDec {
|
||||||
|
pub fn floor(mut self) -> Self {
|
||||||
|
if self.sign == NEG {
|
||||||
|
let diff = self.parts.len() as i32 - self.dec;
|
||||||
|
if diff > 0
|
||||||
|
&& self.parts[self.dec.max(0) as usize..]
|
||||||
|
.iter()
|
||||||
|
.any(|p| *p != 0)
|
||||||
|
{
|
||||||
|
self -= Self::one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.parts.truncate(self.dec.max(0) as usize);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn ceil(mut self) -> Self {
|
||||||
|
if self.sign == NEG {
|
||||||
|
let diff = self.parts.len() as i32 - self.dec;
|
||||||
|
if diff > 0
|
||||||
|
&& self.parts[self.dec.max(0) as usize..]
|
||||||
|
.iter()
|
||||||
|
.any(|p| *p != 0)
|
||||||
|
{
|
||||||
|
self -= Self::one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.parts.truncate(self.dec.max(0) as usize);
|
||||||
|
self += Self::one();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ macro_rules! assert_eq_bits {
|
|||||||
($left:expr, $left_bits:expr, $right:expr, $right_bits:expr, $dec:expr $(,)?) => {
|
($left:expr, $left_bits:expr, $right:expr, $right_bits:expr, $dec:expr $(,)?) => {
|
||||||
assert!(
|
assert!(
|
||||||
($left - $right).abs() < EPSILON,
|
($left - $right).abs() < EPSILON,
|
||||||
"\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}",
|
"\n expect: {:032b} = {:?}\n found: {:032b} = {:?}\n from: {:?}",
|
||||||
$left_bits,
|
$left_bits,
|
||||||
$left,
|
$left,
|
||||||
$right_bits,
|
$right_bits,
|
||||||
@@ -26,7 +26,7 @@ macro_rules! assert_eq_bits {
|
|||||||
($left:expr, $left_bits:expr, $right:expr, $right_bits:expr, $dec:expr, $arg:tt, $($args:tt)+) => {
|
($left:expr, $left_bits:expr, $right:expr, $right_bits:expr, $dec:expr, $arg:tt, $($args:tt)+) => {
|
||||||
assert!(
|
assert!(
|
||||||
($left - $right).abs() < EPSILON,
|
($left - $right).abs() < EPSILON,
|
||||||
concat!("\n expr: ", $arg, "\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}"),
|
concat!("\n expr: ", $arg, "\nexpect: {:032b} = {:?}\n found: {:032b} = {:?}\n from: {:?}"),
|
||||||
$($args)+,
|
$($args)+,
|
||||||
$left_bits,
|
$left_bits,
|
||||||
$left,
|
$left,
|
||||||
@@ -71,6 +71,7 @@ fn add_sub() {
|
|||||||
let dec = FixedDec::from(x) + FixedDec::from(y);
|
let dec = FixedDec::from(x) + FixedDec::from(y);
|
||||||
assert_eq_f32!(a, f32::from(&dec), dec, "{} + {}", x, y);
|
assert_eq_f32!(a, f32::from(&dec), dec, "{} + {}", x, y);
|
||||||
}
|
}
|
||||||
|
test(-0.0, 1.0);
|
||||||
test(0.25, 0.75);
|
test(0.25, 0.75);
|
||||||
test(1.25, 0.125);
|
test(1.25, 0.125);
|
||||||
test(1.25, -0.125);
|
test(1.25, -0.125);
|
||||||
@@ -130,3 +131,23 @@ fn shr() {
|
|||||||
test(1, 33);
|
test(1, 33);
|
||||||
test(1, -33);
|
test(1, -33);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ord() {
|
||||||
|
fn test(x: f32, y: f32) {
|
||||||
|
let l = FixedDec::from(x);
|
||||||
|
let r = FixedDec::from(y);
|
||||||
|
assert_eq!(x > y, l > r, "{:?} > {:?}", x, y);
|
||||||
|
assert_eq!(x < y, l < r, "{:?} > {:?}", x, y);
|
||||||
|
assert_eq!(x == y, l == r, "{:?} > {:?}", x, y);
|
||||||
|
}
|
||||||
|
test(1.0, 3.0);
|
||||||
|
test(1.0, -3.0);
|
||||||
|
test(-1.0, 3.0);
|
||||||
|
test(-1.0, -33.0);
|
||||||
|
test(-1.5, -33.0);
|
||||||
|
test(-1.5, 66.0);
|
||||||
|
test(-1.5, -1.5);
|
||||||
|
test(0.0, 0.0);
|
||||||
|
test(1.5000, -33.0);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user