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"
|
||||
|
||||
[dependencies]
|
||||
winit = "0.30.5"
|
||||
wgpu = "22.1.0"
|
||||
nalgebra = "0.33.1"
|
||||
winit = "0.30.9"
|
||||
pollster = "0.3.0"
|
||||
bytemuck = "1.19.0"
|
||||
num-traits = "0.2.19"
|
||||
nalgebra = "0.33.2"
|
||||
wgpu = "24.0.1"
|
||||
|
||||
@@ -50,6 +50,9 @@ impl Zoom {
|
||||
pub fn level(&self) -> i32 {
|
||||
self.level
|
||||
}
|
||||
pub fn rel_zoom(&self) -> f32 {
|
||||
self.exp.exp2()
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<f32> for Zoom {
|
||||
|
||||
@@ -16,7 +16,6 @@ impl Client<'_> {
|
||||
}
|
||||
|
||||
let speed = FixedDec::from(per_sec * 0.5) * camera.zoom.mult();
|
||||
let old = f32::from(&camera.pos.x);
|
||||
if input.pressed(K::KeyW) {
|
||||
camera.pos.y += &speed;
|
||||
}
|
||||
@@ -29,9 +28,5 @@ impl Client<'_> {
|
||||
if input.pressed(K::KeyD) {
|
||||
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::Resized(size) => self.renderer.resize(size),
|
||||
WindowEvent::RedrawRequested => {
|
||||
self.renderer.update(&self.camera);
|
||||
self.renderer.draw();
|
||||
self.renderer.render(&self.camera);
|
||||
self.window.request_redraw();
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
|
||||
@@ -6,28 +6,36 @@ use super::Camera;
|
||||
|
||||
const VIEW_ALIGN: usize = 4 * 2;
|
||||
|
||||
pub struct View {
|
||||
pub struct ComputeView {
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl View {
|
||||
impl ComputeView {
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for View {
|
||||
impl Default for ComputeView {
|
||||
fn default() -> Self {
|
||||
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 {
|
||||
fn new(stretch: Vector2<f32>, level: i32, scale: &FixedDec, x: &FixedDec, y: &FixedDec) -> Self {
|
||||
impl ComputeView {
|
||||
fn new(
|
||||
reset: bool,
|
||||
dims: Vector2<u32>,
|
||||
level: i32,
|
||||
scale: &FixedDec,
|
||||
x: &FixedDec,
|
||||
y: &FixedDec,
|
||||
) -> Self {
|
||||
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(bytemuck::cast_slice(&[dims.x, dims.y]));
|
||||
scale.to_bytes(&mut bytes);
|
||||
x.to_bytes(&mut bytes);
|
||||
y.to_bytes(&mut bytes);
|
||||
@@ -35,16 +43,16 @@ impl View {
|
||||
if rem != 0 {
|
||||
bytes.extend((0..(VIEW_ALIGN - rem)).map(|_| 0));
|
||||
}
|
||||
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();
|
||||
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();
|
||||
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 stretch = if size.x < size.y {
|
||||
@@ -54,8 +62,14 @@ impl View {
|
||||
};
|
||||
|
||||
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 ILEN: i32 = 3;
|
||||
const LEN2: u32 = LEN * 2;
|
||||
|
||||
struct View {
|
||||
stretch: vec2<f32>,
|
||||
level: i32,
|
||||
scale: FixedDec,
|
||||
x: FixedDec,
|
||||
y: FixedDec,
|
||||
}
|
||||
const POS: u32 = 0u;
|
||||
const NEG: u32 = 1u;
|
||||
|
||||
struct FixedDec {
|
||||
sign: u32,
|
||||
@@ -16,63 +7,18 @@ struct FixedDec {
|
||||
parts: array<u32, LEN>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<storage> view: View;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) vertex_pos: vec4<f32>,
|
||||
@location(0) world_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;
|
||||
out.world_pos.y *= -1.0;
|
||||
out.world_pos *= view.stretch;
|
||||
return out;
|
||||
}
|
||||
|
||||
@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;
|
||||
fn shr(lhs: FixedDec, rhs: i32) -> FixedDec {
|
||||
var parts = array<u32, LEN>();
|
||||
let sr = u32(rem_euclid(rhs, 32));
|
||||
let sl = 32 - sr;
|
||||
let mask = (1u << sr) - 1;
|
||||
var rem = 0u;
|
||||
for (var i = 0; i < ILEN; i += 1) {
|
||||
let part = lhs.parts[i];
|
||||
parts[i] = (part >> sr) ^ rem;
|
||||
rem = (part & mask) << sl;
|
||||
}
|
||||
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);
|
||||
return FixedDec(lhs.sign, lhs.dec, parts);
|
||||
}
|
||||
|
||||
fn add(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||
@@ -136,9 +82,6 @@ fn at(dec: FixedDec, i: i32) -> u32 {
|
||||
return parts[i];
|
||||
}
|
||||
|
||||
const POS: u32 = 0u;
|
||||
const NEG: u32 = 1u;
|
||||
|
||||
fn mul(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||
let sign = u32(lhs.sign != rhs.sign);
|
||||
var parts = array<u32, LEN2>();
|
||||
@@ -173,7 +116,7 @@ fn mul(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||
let res2 = res + carry;
|
||||
let carry2 = res2 < res;
|
||||
parts[k] = res2;
|
||||
carry = u32(carry1 || carry2) + msb;
|
||||
carry = u32(carry1) + u32(carry2) + msb;
|
||||
}
|
||||
parts[i] = carry;
|
||||
}
|
||||
@@ -202,6 +145,25 @@ fn gt(x: FixedDec, y: FixedDec) -> bool {
|
||||
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 {
|
||||
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 view;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use compute::ComputePipeline;
|
||||
use nalgebra::Vector2;
|
||||
use tile::TilePipeline;
|
||||
use output::RenderPipeline;
|
||||
use util::GPUTimer;
|
||||
use view::ChunkView;
|
||||
use winit::{dpi::PhysicalSize, window::Window};
|
||||
|
||||
use super::camera::Camera;
|
||||
|
||||
const CHUNK_POW: u32 = 7;
|
||||
const CHUNK_WIDTH: u32 = 2u32.pow(CHUNK_POW);
|
||||
|
||||
pub struct Renderer<'a> {
|
||||
size: Vector2<u32>,
|
||||
surface: wgpu::Surface<'a>,
|
||||
@@ -19,15 +26,18 @@ pub struct Renderer<'a> {
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
staging_belt: wgpu::util::StagingBelt,
|
||||
timer: GPUTimer,
|
||||
chunk_view: ChunkView,
|
||||
len: usize,
|
||||
|
||||
tile_pipeline: TilePipeline,
|
||||
compute_pipeline: ComputePipeline,
|
||||
render_pipeline: RenderPipeline,
|
||||
}
|
||||
|
||||
impl Renderer<'_> {
|
||||
pub fn new(window: Arc<Window>) -> Self {
|
||||
let size = window.inner_size();
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::PRIMARY,
|
||||
..Default::default()
|
||||
});
|
||||
@@ -90,8 +100,14 @@ impl Renderer<'_> {
|
||||
let staging_belt = wgpu::util::StagingBelt::new(1024);
|
||||
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 {
|
||||
tile_pipeline: TilePipeline::init(&device, &config),
|
||||
render_pipeline,
|
||||
compute_pipeline,
|
||||
size: Vector2::new(size.width, size.height),
|
||||
staging_belt,
|
||||
surface,
|
||||
@@ -100,47 +116,44 @@ impl Renderer<'_> {
|
||||
device,
|
||||
config,
|
||||
queue,
|
||||
chunk_view: ChunkView::new(),
|
||||
len,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, camera: &Camera) {
|
||||
self.tile_pipeline.update(
|
||||
pub fn render(&mut self, camera: &Camera) {
|
||||
|
||||
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,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
camera,
|
||||
&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 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.compute_pipeline.run(&mut encoder);
|
||||
self.timer.stop(&mut encoder, 0);
|
||||
|
||||
self.timer.resolve(&mut encoder);
|
||||
|
||||
self.render_pipeline.draw(&mut encoder, &output);
|
||||
|
||||
self.staging_belt.finish();
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
@@ -154,6 +167,8 @@ impl Renderer<'_> {
|
||||
self.config.width = size.width;
|
||||
self.config.height = size.height;
|
||||
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 {
|
||||
|
||||
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,
|
||||
label: String,
|
||||
usage: BufferUsages,
|
||||
updates: Vec<ArrBufUpdate<T>>,
|
||||
update: Option<Vec<T>>,
|
||||
}
|
||||
|
||||
impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
||||
@@ -36,34 +36,24 @@ impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
||||
if self.len == 0 {
|
||||
return resized;
|
||||
}
|
||||
for update in &self.updates {
|
||||
if let Some(update) = self.update.take() {
|
||||
let mut view = belt.write_buffer(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
(update.offset * std::mem::size_of::<T>()) as BufferAddress,
|
||||
0,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked(
|
||||
std::mem::size_of_val(&update.data[..]) as u64
|
||||
)
|
||||
std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(&update[..]) as u64)
|
||||
},
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(bytemuck::cast_slice(&update.data));
|
||||
view.copy_from_slice(bytemuck::cast_slice(&update));
|
||||
}
|
||||
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 set(&mut self, data: Vec<T>) {
|
||||
self.new_len = data.len();
|
||||
self.update = Some(data);
|
||||
}
|
||||
|
||||
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)
|
||||
},
|
||||
label: label.to_string(),
|
||||
updates: Vec::new(),
|
||||
update: None,
|
||||
usage,
|
||||
}
|
||||
}
|
||||
@@ -117,8 +107,8 @@ impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
||||
pub fn bind_group_layout_entry(
|
||||
&self,
|
||||
binding: u32,
|
||||
visibility: wgpu::ShaderStages,
|
||||
ty: wgpu::BufferBindingType,
|
||||
visibility: wgpu::ShaderStages,
|
||||
) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
|
||||
@@ -4,26 +4,34 @@ use wgpu::util::DeviceExt;
|
||||
|
||||
pub struct Storage {
|
||||
buffer: wgpu::Buffer,
|
||||
old_len: usize,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn init<T: Default + bytemuck::Pod>(device: &wgpu::Device, name: &str) -> Self {
|
||||
let def = [T::default()];
|
||||
let default = bytemuck::cast_slice(&def);
|
||||
Self {
|
||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||
contents: bytemuck::cast_slice(&[T::default()]),
|
||||
contents: default,
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
old_len: default.len(),
|
||||
}
|
||||
}
|
||||
pub fn init_with(device: &wgpu::Device, name: &str, data: &[u8]) -> 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")),
|
||||
contents: data,
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
@@ -31,29 +39,33 @@ impl Storage {
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
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(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
0,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(data) as u64)
|
||||
},
|
||||
unsafe { std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(data) as u64) },
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(data);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_group_layout_entry(
|
||||
&self,
|
||||
binding: u32,
|
||||
read_only: bool,
|
||||
visibility: wgpu::ShaderStages,
|
||||
) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility: wgpu::ShaderStages::VERTEX
|
||||
| wgpu::ShaderStages::FRAGMENT
|
||||
| wgpu::ShaderStages::COMPUTE,
|
||||
visibility,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only },
|
||||
has_dynamic_offset: false,
|
||||
|
||||
@@ -89,3 +89,39 @@ impl Texture {
|
||||
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 {
|
||||
fn from(value: f32) -> Self {
|
||||
if value.is_zero() {
|
||||
return Self::zero();
|
||||
}
|
||||
let raw = value.to_bits() & INV_SIGN_MASK;
|
||||
let mut exp = (raw >> 23) as i32 - 127;
|
||||
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
|
||||
// of growth, or use a VecDeque, but doesn't seem worth for
|
||||
// now
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FixedDec {
|
||||
sign: bool,
|
||||
dec: i32,
|
||||
@@ -25,6 +25,14 @@ pub struct 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 {
|
||||
Self { sign, dec, parts }
|
||||
}
|
||||
@@ -62,18 +70,26 @@ impl FixedDec {
|
||||
} else {
|
||||
self.dec -= rem_beg as i32;
|
||||
}
|
||||
if self.parts.is_empty() {
|
||||
self.sign = POS;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_whole_len(&mut self, len: i32) {
|
||||
let diff = len - self.dec;
|
||||
self.parts
|
||||
.splice(0..usize::try_from(-diff).unwrap_or(0), (0..diff).map(|_| 0));
|
||||
let remove = 0..usize::try_from(-diff).unwrap_or(0).min(self.parts.len());
|
||||
self.parts.splice(remove, (0..diff).map(|_| 0));
|
||||
self.dec += diff;
|
||||
if self.parts.is_empty() {
|
||||
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) {
|
||||
let len = usize::try_from(len + self.dec).unwrap_or(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) {
|
||||
self.parts.resize(prec, 0);
|
||||
if self.parts.is_empty() {
|
||||
@@ -94,6 +135,10 @@ impl FixedDec {
|
||||
bytes.extend(self.dec.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 {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
let mut parts = Vec::with_capacity(self.parts.len());
|
||||
@@ -26,14 +50,14 @@ impl Shr<i32> for FixedDec {
|
||||
let mask = (1 << sr) - 1;
|
||||
let dec = self.dec - rhs.div_floor(32);
|
||||
let mut rem = 0;
|
||||
for part in self.parts {
|
||||
for part in &self.parts {
|
||||
parts.push((part >> sr) ^ rem);
|
||||
rem = (part & mask).unbounded_shl(sl);
|
||||
}
|
||||
if rem != 0 {
|
||||
parts.push(rem);
|
||||
}
|
||||
Self {
|
||||
Self::Output {
|
||||
dec,
|
||||
parts,
|
||||
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) {
|
||||
@@ -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 {
|
||||
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) {
|
||||
let lsb = x.wrapping_mul(y);
|
||||
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;
|
||||
(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 $(,)?) => {
|
||||
assert!(
|
||||
($left - $right).abs() < EPSILON,
|
||||
"\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}",
|
||||
"\n expect: {:032b} = {:?}\n found: {:032b} = {:?}\n from: {:?}",
|
||||
$left_bits,
|
||||
$left,
|
||||
$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)+) => {
|
||||
assert!(
|
||||
($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)+,
|
||||
$left_bits,
|
||||
$left,
|
||||
@@ -71,6 +71,7 @@ fn add_sub() {
|
||||
let dec = FixedDec::from(x) + FixedDec::from(y);
|
||||
assert_eq_f32!(a, f32::from(&dec), dec, "{} + {}", x, y);
|
||||
}
|
||||
test(-0.0, 1.0);
|
||||
test(0.25, 0.75);
|
||||
test(1.25, 0.125);
|
||||
test(1.25, -0.125);
|
||||
@@ -130,3 +131,23 @@ fn shr() {
|
||||
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