WORKING FIXED POINT
This commit is contained in:
35
src/client/render/tile/compute.wgsl
Normal file
35
src/client/render/tile/compute.wgsl
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
@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,30 +1,61 @@
|
||||
use nalgebra::Vector2;
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Clone, Copy)]
|
||||
use crate::util::FixedDec;
|
||||
|
||||
use super::Camera;
|
||||
|
||||
const VIEW_ALIGN: usize = 4 * 2;
|
||||
|
||||
pub struct View {
|
||||
pub scale: Vector2<f32>,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for View {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scale: Vector2::zeros(),
|
||||
}
|
||||
let val = FixedDec::from_parts(false, 0, vec![0, 0, 0]);
|
||||
Self::new(Vector2::zeros(), 0, &val, &val, &val)
|
||||
}
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn new(size: &Vector2<u32>) -> Self {
|
||||
fn new(stretch: Vector2<f32>, 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(level.to_le_bytes());
|
||||
scale.to_bytes(&mut bytes);
|
||||
x.to_bytes(&mut bytes);
|
||||
y.to_bytes(&mut bytes);
|
||||
let rem = bytes.len() % VIEW_ALIGN;
|
||||
if rem != 0 {
|
||||
bytes.extend((0..(VIEW_ALIGN - rem)).map(|_| 0));
|
||||
}
|
||||
Self{ bytes }
|
||||
}
|
||||
|
||||
pub fn from_camera_size(camera: &Camera, size: &Vector2<u32>) -> Self {
|
||||
let mut x = camera.pos.x.clone();
|
||||
x.set_whole_len(1);
|
||||
x.set_dec_len(2);
|
||||
let mut y = camera.pos.y.clone();
|
||||
y.set_whole_len(1);
|
||||
y.set_dec_len(2);
|
||||
|
||||
let fsize: Vector2<f32> = size.cast();
|
||||
let scale = if size.x < size.y {
|
||||
let stretch = if size.x < size.y {
|
||||
Vector2::new(fsize.x / fsize.y, 1.0)
|
||||
} else {
|
||||
Vector2::new(1.0, fsize.y / fsize.x)
|
||||
};
|
||||
View { scale }
|
||||
|
||||
let mut scale = camera.zoom.mult().clone();
|
||||
scale.set_precision(3);
|
||||
|
||||
Self::new(stretch, camera.zoom.level(), &scale, &x, &y)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for View {}
|
||||
unsafe impl bytemuck::Zeroable for View {}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
use super::{util::Uniform, View};
|
||||
use super::{util::Storage, View};
|
||||
|
||||
pub struct TileLayout {
|
||||
render_bind_layout: wgpu::BindGroupLayout,
|
||||
render_pipeline_layout: wgpu::PipelineLayout,
|
||||
format: wgpu::TextureFormat,
|
||||
pub view: Uniform<View>,
|
||||
pub view: Storage,
|
||||
}
|
||||
|
||||
impl TileLayout {
|
||||
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
||||
let view = Uniform::init(device, "view");
|
||||
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)],
|
||||
entries: &[view.bind_group_layout_entry(0, true)],
|
||||
label: Some("voxel render"),
|
||||
});
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ impl TilePipeline {
|
||||
camera: &Camera,
|
||||
size: &Vector2<u32>,
|
||||
) {
|
||||
self.view.update(device, encoder, belt, View::new(size));
|
||||
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>) {
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
// Vertex shader
|
||||
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,
|
||||
}
|
||||
|
||||
struct FixedDec {
|
||||
sign: u32,
|
||||
dec: i32,
|
||||
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>,
|
||||
};
|
||||
|
||||
struct View {
|
||||
scale: vec2<f32>,
|
||||
// x_dec: i32,
|
||||
// y_dec: i32,
|
||||
// prec: u32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
// @group(0) @binding(1)
|
||||
// var<storage> vx: array<u32>;
|
||||
// @group(0) @binding(2)
|
||||
// var<storage> vy: array<u32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) vi: u32,
|
||||
@@ -33,34 +38,30 @@ fn vs_main(
|
||||
out.vertex_pos = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
|
||||
out.world_pos = pos;
|
||||
out.world_pos.y *= -1.0;
|
||||
out.world_pos *= view.scale;
|
||||
out.world_pos *= view.stretch;
|
||||
return out;
|
||||
}
|
||||
|
||||
// const PREC = 2;
|
||||
|
||||
@fragment
|
||||
fn fs_main(
|
||||
in: VertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let dec = i32(1) << 13;
|
||||
let c = vec2<i32>(in.world_pos * f32(dec));
|
||||
let cx = c.x;
|
||||
let cy = c.y;
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
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 thresh = 2 * dec;
|
||||
let thresh2 = thresh * thresh;
|
||||
let max = 50u;
|
||||
let max = 50u + (1u << u32(view.level));
|
||||
loop {
|
||||
let x2 = x * x;
|
||||
let y2 = y * y;
|
||||
if x2 + y2 > thresh2 || i >= max {
|
||||
let x2 = mul(x, x);
|
||||
let y2 = mul(y, y);
|
||||
if gt(add(x2, y2), thresh) || i >= max {
|
||||
break;
|
||||
}
|
||||
y = (2 * x * y) / dec + c.y;
|
||||
x = (x2 - y2) / dec + c.x;
|
||||
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);
|
||||
@@ -74,37 +75,247 @@ fn fs_main(
|
||||
return vec4(color, 1.0);
|
||||
}
|
||||
|
||||
// @fragment
|
||||
// fn fs_main(
|
||||
// in: VertexOutput,
|
||||
// ) -> @location(0) vec4<f32> {
|
||||
// let dec = i32(1) << 13;
|
||||
// let c = vec2<i32>(in.world_pos * f32(dec));
|
||||
// let cx = c.x;
|
||||
// let cy = c.y;
|
||||
// var x = 0;
|
||||
// var y = 0;
|
||||
// var i = 0u;
|
||||
// let thresh = 2 * dec;
|
||||
// let thresh2 = thresh * thresh;
|
||||
// let max = 50u + u32(in.zoom);
|
||||
// loop {
|
||||
// let x2 = x * x;
|
||||
// let y2 = y * y;
|
||||
// if x2 + y2 > thresh2 || i >= max {
|
||||
// break;
|
||||
// }
|
||||
// y = (2 * x * y) / dec + c.y;
|
||||
// x = (x2 - y2) / dec + c.x;
|
||||
// i += 1u;
|
||||
// }
|
||||
// var color = vec3<f32>(0.0, 0.0, 0.0);
|
||||
// if i != max {
|
||||
// let pi = 3.1415;
|
||||
// let hue = f32(i) / 30.0;
|
||||
// color.r = cos(hue);
|
||||
// color.g = cos(hue - 2.0 * pi / 3.0);
|
||||
// color.b = cos(hue - 4.0 * pi / 3.0);
|
||||
// }
|
||||
// return vec4(color, 1.0);
|
||||
// }
|
||||
fn add(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||
var dest = FixedDec();
|
||||
dest.sign = lhs.sign;
|
||||
dest.dec = max(lhs.dec, rhs.dec);
|
||||
var carry = false;
|
||||
let rhs_offset = rhs.dec - dest.dec;
|
||||
let lhs_offset = lhs.dec - dest.dec;
|
||||
var i = ILEN;
|
||||
if lhs.sign == rhs.sign {
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
let a = at(lhs, i + lhs_offset);
|
||||
let b = at(rhs, i + rhs_offset);
|
||||
let res = a + b + u32(carry);
|
||||
dest.parts[i] = res;
|
||||
carry = res < a;
|
||||
}
|
||||
if carry {
|
||||
var i = ILEN - 1;
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
dest.parts[i + 1] = dest.parts[i];
|
||||
}
|
||||
dest.parts[0] = 1u;
|
||||
dest.dec += 1;
|
||||
}
|
||||
} else {
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
let a = at(lhs, i + lhs_offset);
|
||||
let b = at(rhs, i + rhs_offset);
|
||||
let res = a - b - u32(carry);
|
||||
dest.parts[i] = res;
|
||||
carry = a < res;
|
||||
}
|
||||
if carry {
|
||||
dest.sign = u32(dest.sign == 0);
|
||||
var i = 0;
|
||||
while i < ILEN {
|
||||
dest.parts[i] = ~dest.parts[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
fn sub(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
|
||||
var r = rhs;
|
||||
r.sign = u32(r.sign == 0);
|
||||
return add(lhs, r);
|
||||
}
|
||||
|
||||
fn at(dec: FixedDec, i: i32) -> u32 {
|
||||
if i < 0 || i >= ILEN {
|
||||
return 0u;
|
||||
}
|
||||
var parts = dec.parts;
|
||||
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>();
|
||||
var dec = lhs.dec + rhs.dec;
|
||||
var lparts = lhs.parts;
|
||||
var rparts = rhs.parts;
|
||||
|
||||
var i = LEN;
|
||||
while i > 0 {
|
||||
i -= 1u;
|
||||
let x = lparts[i];
|
||||
var carry: u32 = 0;
|
||||
var j = LEN;
|
||||
while j > 0 {
|
||||
j -= 1u;
|
||||
let y = rparts[j];
|
||||
|
||||
// widening mul
|
||||
let lsb = x * y;
|
||||
let a = x & 0xffff;
|
||||
let b = x >> 16;
|
||||
let c = y & 0xffff;
|
||||
let d = y >> 16;
|
||||
let ad = a * d + ((a * c) >> 16);
|
||||
let bc = b * c;
|
||||
let car = ad > (0xffffffff - bc);
|
||||
let msb = ((ad + bc) >> 16) + (u32(car) << 16) + b * d;
|
||||
|
||||
let k = i + j + 1;
|
||||
let res = parts[k] + lsb;
|
||||
let carry1 = res < lsb;
|
||||
let res2 = res + carry;
|
||||
let carry2 = res2 < res;
|
||||
parts[k] = res2;
|
||||
carry = u32(carry1 || carry2) + msb;
|
||||
}
|
||||
parts[i] = carry;
|
||||
}
|
||||
|
||||
var new_parts = array<u32, LEN>();
|
||||
i = 0u;
|
||||
while i < LEN2 && parts[i] == 0 {
|
||||
dec -= 1;
|
||||
i += 1u;
|
||||
}
|
||||
var j = 0u;
|
||||
while j < LEN && (i + j) < LEN2 {
|
||||
new_parts[j] = parts[i + j];
|
||||
j += 1u;
|
||||
}
|
||||
return FixedDec(sign, dec, new_parts);
|
||||
}
|
||||
|
||||
fn gt(x: FixedDec, y: FixedDec) -> bool {
|
||||
if x.dec > y.dec {
|
||||
return true;
|
||||
}
|
||||
if y.dec > x.dec {
|
||||
return false;
|
||||
}
|
||||
return x.parts[0] > y.parts[0];
|
||||
}
|
||||
|
||||
fn to_f32(value: FixedDec) -> f32 {
|
||||
var parts = value.parts;
|
||||
|
||||
var sign = value.sign * (1u << 31);
|
||||
var skip_count = 0;
|
||||
|
||||
while skip_count < ILEN && parts[skip_count] == 0 {
|
||||
skip_count += 1;
|
||||
}
|
||||
|
||||
if skip_count == ILEN {
|
||||
if value.sign == POS {
|
||||
return 0.0;
|
||||
} else {
|
||||
return -0.0;
|
||||
}
|
||||
}
|
||||
let v = parts[skip_count];
|
||||
var start = countLeadingZeros(v) + 1;
|
||||
let exp_i = (value.dec - skip_count) * 32 - i32(start);
|
||||
var frac_sh = 0u;
|
||||
var exp = 0u;
|
||||
if exp_i >= -127 {
|
||||
if exp_i == -127 {
|
||||
start -= 1u;
|
||||
}
|
||||
exp = u32(exp_i + 127);
|
||||
} else {
|
||||
frac_sh = u32(-(exp_i + 32 * 4 - 1));
|
||||
if frac_sh < 23 {
|
||||
start -= 1u;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
};
|
||||
var frac: u32;
|
||||
if start > 9 {
|
||||
let sh = start - 9;
|
||||
let next_i = skip_count + 1;
|
||||
var v2 = 0u;
|
||||
if next_i < ILEN {
|
||||
v2 = parts[next_i] >> (32 - sh);
|
||||
}
|
||||
frac = (v << sh) + v2;
|
||||
} else {
|
||||
frac = v >> (9 - start);
|
||||
};
|
||||
frac &= ~(1u << 23);
|
||||
let res = (frac >> frac_sh) + (exp << 23) + sign;
|
||||
return bitcast<f32>(res);
|
||||
}
|
||||
|
||||
const INV_SIGN_MASK: u32 = (1u << 31) - 1;
|
||||
const FRAC_BIT: u32 = 1u << 23;
|
||||
const FRAC_MASK: u32 = FRAC_BIT - 1;
|
||||
|
||||
fn from_f32(value: f32) -> FixedDec {
|
||||
let raw = bitcast<u32>(value) & INV_SIGN_MASK;
|
||||
var exp = i32(raw >> 23) - 127;
|
||||
var frac = raw & FRAC_MASK;
|
||||
var start = -exp;
|
||||
if exp == -127 {
|
||||
exp = -126;
|
||||
start = -exp;
|
||||
} else {
|
||||
frac += FRAC_BIT;
|
||||
start -= 1;
|
||||
}
|
||||
let end = -exp + 23;
|
||||
let start_i = div_euclid(start, 32);
|
||||
let end_i = div_euclid(end - 1, 32);
|
||||
var parts = array<u32, LEN>();
|
||||
var dec = -start_i;
|
||||
if start_i == end_i {
|
||||
let val = frac << u32(8 - rem_euclid(start, 32));
|
||||
if val != 0 {
|
||||
parts[0] = val;
|
||||
}
|
||||
} else {
|
||||
let s = u32(rem_euclid(end, 32));
|
||||
let val_high = frac >> s;
|
||||
let val_low = frac << (32 - s);
|
||||
var i = 0;
|
||||
if val_high != 0 {
|
||||
parts[0] = val_high;
|
||||
i += 1;
|
||||
} else {
|
||||
dec -= 1;
|
||||
}
|
||||
if val_low != 0 {
|
||||
parts[i] = val_low;
|
||||
}
|
||||
}
|
||||
if parts[0] == 0 && parts[1] == 0 {
|
||||
dec = 0;
|
||||
}
|
||||
return FixedDec(
|
||||
u32(value < 0.0),
|
||||
dec,
|
||||
parts,
|
||||
);
|
||||
}
|
||||
|
||||
fn zero() -> FixedDec {
|
||||
return FixedDec(0, 0, array<u32, LEN>());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ mod texture;
|
||||
mod timer;
|
||||
mod uniform;
|
||||
mod array;
|
||||
mod storage;
|
||||
|
||||
pub use texture::*;
|
||||
pub use timer::*;
|
||||
pub use uniform::*;
|
||||
pub use array::*;
|
||||
pub use storage::*;
|
||||
|
||||
71
src/client/render/util/storage.rs
Normal file
71
src/client/render/util/storage.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
pub struct Storage {
|
||||
buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn init<T: Default + bytemuck::Pod>(device: &wgpu::Device, name: &str) -> Self {
|
||||
Self {
|
||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||
contents: bytemuck::cast_slice(&[T::default()]),
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
}
|
||||
}
|
||||
pub fn init_with(device: &wgpu::Device, name: &str, data: &[u8]) -> Self {
|
||||
Self {
|
||||
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,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
data: &[u8],
|
||||
) {
|
||||
let mut view = belt.write_buffer(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
0,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(data) as u64)
|
||||
},
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(data);
|
||||
}
|
||||
|
||||
pub fn bind_group_layout_entry(
|
||||
&self,
|
||||
binding: u32,
|
||||
read_only: bool,
|
||||
) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility: wgpu::ShaderStages::VERTEX
|
||||
| wgpu::ShaderStages::FRAGMENT
|
||||
| wgpu::ShaderStages::COMPUTE,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}
|
||||
pub fn bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: self.buffer.as_entire_binding(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user