arst
This commit is contained in:
164
src/client/render/mod.rs
Normal file
164
src/client/render/mod.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
mod tile;
|
||||
mod util;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use tile::TilePipeline;
|
||||
use util::GPUTimer;
|
||||
use winit::{dpi::PhysicalSize, window::Window};
|
||||
|
||||
use super::camera::Camera;
|
||||
|
||||
pub struct Renderer<'a> {
|
||||
size: Vector2<u32>,
|
||||
surface: wgpu::Surface<'a>,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
encoder: wgpu::CommandEncoder,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
staging_belt: wgpu::util::StagingBelt,
|
||||
timer: GPUTimer,
|
||||
|
||||
tile_pipeline: TilePipeline,
|
||||
}
|
||||
|
||||
impl Renderer<'_> {
|
||||
pub fn new(window: Arc<Window>) -> Self {
|
||||
let size = window.inner_size();
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::PRIMARY,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let surface = instance
|
||||
.create_surface(window)
|
||||
.expect("Could not create window surface!");
|
||||
|
||||
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
}))
|
||||
.expect("Could not get adapter!");
|
||||
|
||||
let buf_size = (10f32.powi(9) * 1.5) as u32;
|
||||
let (device, queue) = pollster::block_on(adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
required_features: wgpu::Features::PUSH_CONSTANTS
|
||||
| wgpu::Features::TIMESTAMP_QUERY
|
||||
| wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS
|
||||
| wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
||||
required_limits: wgpu::Limits {
|
||||
max_storage_buffer_binding_size: buf_size,
|
||||
max_buffer_size: buf_size as u64,
|
||||
max_push_constant_size: 4,
|
||||
..Default::default()
|
||||
},
|
||||
memory_hints: wgpu::MemoryHints::default(),
|
||||
},
|
||||
None,
|
||||
))
|
||||
.expect("Could not get device!");
|
||||
|
||||
let info = adapter.get_info();
|
||||
println!("Adapter: {}", info.name);
|
||||
println!("Backend: {:?}", info.backend);
|
||||
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
let surface_format = surface_caps
|
||||
.formats
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|f| f.is_srgb())
|
||||
.unwrap_or(surface_caps.formats[0]);
|
||||
|
||||
let config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||
alpha_mode: surface_caps.alpha_modes[0],
|
||||
view_formats: vec![],
|
||||
desired_maximum_frame_latency: 2,
|
||||
};
|
||||
|
||||
surface.configure(&device, &config);
|
||||
let staging_belt = wgpu::util::StagingBelt::new(1024);
|
||||
let timer = GPUTimer::new(&device, queue.get_timestamp_period(), 1);
|
||||
|
||||
Self {
|
||||
tile_pipeline: TilePipeline::init(&device, &config),
|
||||
size: Vector2::new(size.width, size.height),
|
||||
staging_belt,
|
||||
surface,
|
||||
encoder: Self::create_encoder(&device),
|
||||
timer,
|
||||
device,
|
||||
config,
|
||||
queue,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, camera: &Camera) {
|
||||
self.tile_pipeline.update(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
camera,
|
||||
&self.size,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device));
|
||||
let output = self.surface.get_current_texture().unwrap();
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
self.timer.start(&mut encoder, 0);
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
self.tile_pipeline.draw(&mut render_pass);
|
||||
drop(render_pass);
|
||||
self.timer.stop(&mut encoder, 0);
|
||||
|
||||
self.timer.resolve(&mut encoder);
|
||||
|
||||
self.staging_belt.finish();
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
self.staging_belt.recall();
|
||||
|
||||
self.timer.finish(&self.device);
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: PhysicalSize<u32>) {
|
||||
self.size = Vector2::new(size.width, size.height);
|
||||
self.config.width = size.width;
|
||||
self.config.height = size.height;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
}
|
||||
|
||||
fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder {
|
||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Render Encoder"),
|
||||
})
|
||||
}
|
||||
}
|
||||
30
src/client/render/tile/data.rs
Normal file
30
src/client/render/tile/data.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use nalgebra::Vector2;
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct View {
|
||||
pub scale: Vector2<f32>,
|
||||
}
|
||||
|
||||
impl Default for View {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scale: Vector2::zeros(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn new(size: &Vector2<u32>) -> Self {
|
||||
let fsize: Vector2<f32> = size.cast();
|
||||
let scale = if size.x < size.y {
|
||||
Vector2::new(fsize.x / fsize.y, 1.0)
|
||||
} else {
|
||||
Vector2::new(1.0, fsize.y / fsize.x)
|
||||
};
|
||||
View { scale }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for View {}
|
||||
unsafe impl bytemuck::Zeroable for View {}
|
||||
86
src/client/render/tile/layout.rs
Normal file
86
src/client/render/tile/layout.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use super::{util::Uniform, View};
|
||||
|
||||
pub struct TileLayout {
|
||||
render_bind_layout: wgpu::BindGroupLayout,
|
||||
render_pipeline_layout: wgpu::PipelineLayout,
|
||||
format: wgpu::TextureFormat,
|
||||
pub view: Uniform<View>,
|
||||
}
|
||||
|
||||
impl TileLayout {
|
||||
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
||||
let view = Uniform::init(device, "view");
|
||||
|
||||
let render_bind_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[view.bind_group_layout_entry(0)],
|
||||
label: Some("voxel render"),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Tile Pipeline Layout"),
|
||||
bind_group_layouts: &[&render_bind_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
Self {
|
||||
view,
|
||||
render_bind_layout,
|
||||
render_pipeline_layout,
|
||||
format: config.format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_bind_group(&self, device: &wgpu::Device) -> wgpu::BindGroup {
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.render_bind_layout,
|
||||
entries: &[self.view.bind_group_entry(0)],
|
||||
label: Some("voxel render"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render_pipeline(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
shader: wgpu::ShaderModule,
|
||||
) -> wgpu::RenderPipeline {
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Voxel Pipeline"),
|
||||
layout: Some(&self.render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: self.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: true,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
61
src/client/render/tile/mod.rs
Normal file
61
src/client/render/tile/mod.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use wgpu::include_wgsl;
|
||||
|
||||
mod data;
|
||||
mod layout;
|
||||
|
||||
use super::*;
|
||||
pub use data::*;
|
||||
use layout::*;
|
||||
|
||||
pub struct TilePipeline {
|
||||
layout: TileLayout,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
render_bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
const RENDER_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("render.wgsl");
|
||||
|
||||
impl TilePipeline {
|
||||
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
||||
let layout = TileLayout::init(device, config);
|
||||
let render_shader = device.create_shader_module(RENDER_SHADER);
|
||||
Self {
|
||||
render_pipeline: layout.render_pipeline(device, render_shader),
|
||||
render_bind_group: layout.render_bind_group(device),
|
||||
layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
camera: &Camera,
|
||||
size: &Vector2<u32>,
|
||||
) {
|
||||
self.view.update(device, encoder, belt, View::new(size));
|
||||
}
|
||||
|
||||
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
render_pass.set_bind_group(0, &self.render_bind_group, &[]);
|
||||
render_pass.draw(0..4, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TilePipeline {
|
||||
type Target = TileLayout;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.layout
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TilePipeline {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.layout
|
||||
}
|
||||
}
|
||||
110
src/client/render/tile/render.wgsl
Normal file
110
src/client/render/tile/render.wgsl
Normal file
@@ -0,0 +1,110 @@
|
||||
// Vertex shader
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) vertex_pos: vec4<f32>,
|
||||
@location(0) world_pos: vec2<f32>,
|
||||
};
|
||||
|
||||
struct View {
|
||||
scale: vec2<f32>,
|
||||
// x_dec: i32,
|
||||
// y_dec: i32,
|
||||
// prec: u32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
// @group(0) @binding(1)
|
||||
// var<storage> vx: array<u32>;
|
||||
// @group(0) @binding(2)
|
||||
// var<storage> vy: array<u32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) vi: u32,
|
||||
@builtin(instance_index) ii: u32,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
let pos = vec2<f32>(
|
||||
f32(vi % 2u),
|
||||
f32(vi / 2u),
|
||||
) * 2.0 - 1.0;
|
||||
out.vertex_pos = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
|
||||
out.world_pos = pos;
|
||||
out.world_pos.y *= -1.0;
|
||||
out.world_pos *= view.scale;
|
||||
return out;
|
||||
}
|
||||
|
||||
// const PREC = 2;
|
||||
|
||||
@fragment
|
||||
fn fs_main(
|
||||
in: VertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let dec = i32(1) << 13;
|
||||
let c = vec2<i32>(in.world_pos * f32(dec));
|
||||
let cx = c.x;
|
||||
let cy = c.y;
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var i = 0u;
|
||||
let thresh = 2 * dec;
|
||||
let thresh2 = thresh * thresh;
|
||||
let max = 50u;
|
||||
loop {
|
||||
let x2 = x * x;
|
||||
let y2 = y * y;
|
||||
if x2 + y2 > thresh2 || i >= max {
|
||||
break;
|
||||
}
|
||||
y = (2 * x * y) / dec + c.y;
|
||||
x = (x2 - y2) / dec + c.x;
|
||||
i += 1u;
|
||||
}
|
||||
var color = vec3<f32>(0.0, 0.0, 0.0);
|
||||
if i != max {
|
||||
let pi = 3.1415;
|
||||
let hue = f32(i) / 30.0;
|
||||
color.r = cos(hue);
|
||||
color.g = cos(hue - 2.0 * pi / 3.0);
|
||||
color.b = cos(hue - 4.0 * pi / 3.0);
|
||||
}
|
||||
return vec4(color, 1.0);
|
||||
}
|
||||
|
||||
// @fragment
|
||||
// fn fs_main(
|
||||
// in: VertexOutput,
|
||||
// ) -> @location(0) vec4<f32> {
|
||||
// let dec = i32(1) << 13;
|
||||
// let c = vec2<i32>(in.world_pos * f32(dec));
|
||||
// let cx = c.x;
|
||||
// let cy = c.y;
|
||||
// var x = 0;
|
||||
// var y = 0;
|
||||
// var i = 0u;
|
||||
// let thresh = 2 * dec;
|
||||
// let thresh2 = thresh * thresh;
|
||||
// let max = 50u + u32(in.zoom);
|
||||
// loop {
|
||||
// let x2 = x * x;
|
||||
// let y2 = y * y;
|
||||
// if x2 + y2 > thresh2 || i >= max {
|
||||
// break;
|
||||
// }
|
||||
// y = (2 * x * y) / dec + c.y;
|
||||
// x = (x2 - y2) / dec + c.x;
|
||||
// i += 1u;
|
||||
// }
|
||||
// var color = vec3<f32>(0.0, 0.0, 0.0);
|
||||
// if i != max {
|
||||
// let pi = 3.1415;
|
||||
// let hue = f32(i) / 30.0;
|
||||
// color.r = cos(hue);
|
||||
// color.g = cos(hue - 2.0 * pi / 3.0);
|
||||
// color.b = cos(hue - 4.0 * pi / 3.0);
|
||||
// }
|
||||
// return vec4(color, 1.0);
|
||||
// }
|
||||
146
src/client/render/util/array.rs
Normal file
146
src/client/render/util/array.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use wgpu::{util::DeviceExt, BufferAddress, BufferUsages};
|
||||
|
||||
pub struct ArrayBuffer<T: bytemuck::Pod> {
|
||||
len: usize,
|
||||
new_len: usize,
|
||||
buffer: wgpu::Buffer,
|
||||
label: String,
|
||||
usage: BufferUsages,
|
||||
updates: Vec<ArrBufUpdate<T>>,
|
||||
}
|
||||
|
||||
impl<T: bytemuck::Pod> ArrayBuffer<T> {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
) -> bool {
|
||||
let mut resized = false;
|
||||
if self.new_len != self.len {
|
||||
let new = Self::init_buf(device, &self.label, self.new_len, self.usage);
|
||||
let cpy_len = self.len.min(self.new_len);
|
||||
if cpy_len != 0 {
|
||||
encoder.copy_buffer_to_buffer(
|
||||
&self.buffer,
|
||||
0,
|
||||
&new,
|
||||
0,
|
||||
(cpy_len * std::mem::size_of::<T>()) as u64,
|
||||
);
|
||||
}
|
||||
self.len = self.new_len;
|
||||
resized = true;
|
||||
self.buffer = new;
|
||||
}
|
||||
if self.len == 0 {
|
||||
return resized;
|
||||
}
|
||||
for update in &self.updates {
|
||||
let mut view = belt.write_buffer(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
(update.offset * std::mem::size_of::<T>()) as BufferAddress,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked(
|
||||
std::mem::size_of_val(&update.data[..]) as u64
|
||||
)
|
||||
},
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(bytemuck::cast_slice(&update.data));
|
||||
}
|
||||
self.updates.clear();
|
||||
resized
|
||||
}
|
||||
|
||||
pub fn add(&mut self, data: Vec<T>) -> usize {
|
||||
let data_len = data.len();
|
||||
let pos = self.new_len;
|
||||
self.updates.push(ArrBufUpdate { offset: pos, data });
|
||||
self.new_len += data_len;
|
||||
pos
|
||||
}
|
||||
|
||||
pub fn set(&mut self, offset: usize, data: Vec<T>) {
|
||||
self.updates.push(ArrBufUpdate { offset, data });
|
||||
}
|
||||
|
||||
pub fn init(device: &wgpu::Device, label: &str, usage: BufferUsages) -> Self {
|
||||
let label = &(label.to_owned() + " Buffer");
|
||||
Self::init_with(device, label, usage, &[])
|
||||
}
|
||||
|
||||
pub fn init_with(device: &wgpu::Device, label: &str, usage: BufferUsages, data: &[T]) -> Self {
|
||||
let label = &(label.to_owned() + " Buffer");
|
||||
Self {
|
||||
len: data.len(),
|
||||
new_len: data.len(),
|
||||
buffer: if data.is_empty() {
|
||||
Self::init_buf(device, label, 0, usage)
|
||||
} else {
|
||||
Self::init_buf_with(device, label, usage, data)
|
||||
},
|
||||
label: label.to_string(),
|
||||
updates: Vec::new(),
|
||||
usage,
|
||||
}
|
||||
}
|
||||
|
||||
fn init_buf(
|
||||
device: &wgpu::Device,
|
||||
label: &str,
|
||||
size: usize,
|
||||
usage: BufferUsages,
|
||||
) -> wgpu::Buffer {
|
||||
device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some(label),
|
||||
usage: usage | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
|
||||
size: (size.max(1) * std::mem::size_of::<T>()) as u64,
|
||||
mapped_at_creation: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn init_buf_with(
|
||||
device: &wgpu::Device,
|
||||
label: &str,
|
||||
usage: BufferUsages,
|
||||
data: &[T],
|
||||
) -> wgpu::Buffer {
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(label),
|
||||
usage: usage | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
|
||||
contents: bytemuck::cast_slice(data),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bind_group_layout_entry(
|
||||
&self,
|
||||
binding: u32,
|
||||
visibility: wgpu::ShaderStages,
|
||||
ty: wgpu::BufferBindingType,
|
||||
) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: self.buffer.as_entire_binding(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrBufUpdate<T> {
|
||||
pub offset: usize,
|
||||
pub data: Vec<T>,
|
||||
}
|
||||
12
src/client/render/util/mod.rs
Normal file
12
src/client/render/util/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod texture;
|
||||
mod timer;
|
||||
mod uniform;
|
||||
mod array;
|
||||
|
||||
pub use texture::*;
|
||||
pub use timer::*;
|
||||
pub use uniform::*;
|
||||
pub use array::*;
|
||||
91
src/client/render/util/texture.rs
Normal file
91
src/client/render/util/texture.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
pub struct Texture {
|
||||
texture_desc: wgpu::TextureDescriptor<'static>,
|
||||
view_desc: wgpu::TextureViewDescriptor<'static>,
|
||||
sampler_desc: wgpu::SamplerDescriptor<'static>,
|
||||
pub texture: wgpu::Texture,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
|
||||
|
||||
pub fn init_depth(
|
||||
device: &wgpu::Device,
|
||||
config: &wgpu::SurfaceConfiguration,
|
||||
label: &'static str,
|
||||
) -> Self {
|
||||
let size = wgpu::Extent3d {
|
||||
width: config.width + 1,
|
||||
height: config.height + 1,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let texture_desc = wgpu::TextureDescriptor {
|
||||
label: Some(label),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: Self::DEPTH_FORMAT,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
};
|
||||
Self::init(
|
||||
device,
|
||||
texture_desc,
|
||||
wgpu::TextureViewDescriptor::default(),
|
||||
wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
lod_min_clamp: 0.0,
|
||||
lod_max_clamp: 100.0,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
device: &wgpu::Device,
|
||||
texture_desc: wgpu::TextureDescriptor<'static>,
|
||||
view_desc: wgpu::TextureViewDescriptor<'static>,
|
||||
sampler_desc: wgpu::SamplerDescriptor<'static>,
|
||||
) -> Self {
|
||||
let texture = device.create_texture(&texture_desc);
|
||||
let view = texture.create_view(&view_desc);
|
||||
let sampler = device.create_sampler(&sampler_desc);
|
||||
Self {
|
||||
texture_desc,
|
||||
view_desc,
|
||||
sampler_desc,
|
||||
texture,
|
||||
view,
|
||||
sampler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, device: &wgpu::Device, size: wgpu::Extent3d) {
|
||||
self.texture_desc.size = size;
|
||||
self.texture = device.create_texture(&self.texture_desc);
|
||||
self.view = self.texture.create_view(&self.view_desc);
|
||||
}
|
||||
pub fn view_bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: wgpu::BindingResource::TextureView(&self.view),
|
||||
}
|
||||
}
|
||||
pub fn sampler_bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: wgpu::BindingResource::Sampler(&self.sampler),
|
||||
}
|
||||
}
|
||||
pub fn format(&self) -> wgpu::TextureFormat {
|
||||
self.texture_desc.format
|
||||
}
|
||||
}
|
||||
89
src/client/render/util/timer.rs
Normal file
89
src/client/render/util/timer.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct GPUTimer {
|
||||
resolve_buf: wgpu::Buffer,
|
||||
map_buf: wgpu::Buffer,
|
||||
query_set: wgpu::QuerySet,
|
||||
timestamps: Vec<u64>,
|
||||
period: f32,
|
||||
}
|
||||
|
||||
impl GPUTimer {
|
||||
pub fn new(device: &wgpu::Device, period: f32, count: u32) -> Self {
|
||||
let count = count * 2;
|
||||
let timestamp_set = device.create_query_set(&wgpu::QuerySetDescriptor {
|
||||
count,
|
||||
label: Some("voxel timestamp"),
|
||||
ty: wgpu::QueryType::Timestamp,
|
||||
});
|
||||
let timestamp_resolve_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("voxel timestamp"),
|
||||
mapped_at_creation: false,
|
||||
size: 8 * count as u64,
|
||||
usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
|
||||
});
|
||||
|
||||
let timestamp_mapped_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("voxel timestamp"),
|
||||
mapped_at_creation: false,
|
||||
size: 8 * count as u64,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
||||
});
|
||||
|
||||
Self {
|
||||
query_set: timestamp_set,
|
||||
resolve_buf: timestamp_resolve_buf,
|
||||
map_buf: timestamp_mapped_buf,
|
||||
timestamps: vec![0; count as usize],
|
||||
period,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(&self, encoder: &mut wgpu::CommandEncoder) {
|
||||
encoder.resolve_query_set(&self.query_set, 0..2, &self.resolve_buf, 0);
|
||||
encoder.copy_buffer_to_buffer(&self.resolve_buf, 0, &self.map_buf, 0, self.map_buf.size());
|
||||
}
|
||||
|
||||
pub fn duration(&self, i: u32) -> Duration {
|
||||
let i = i as usize * 2;
|
||||
let diff = self.timestamps[i + 1] - self.timestamps[i];
|
||||
Duration::from_nanos((diff as f32 * self.period) as u64)
|
||||
}
|
||||
|
||||
pub fn finish(&mut self, device: &wgpu::Device) {
|
||||
let (s, r) = std::sync::mpsc::channel();
|
||||
self.map_buf
|
||||
.slice(..)
|
||||
.map_async(wgpu::MapMode::Read, move |v| {
|
||||
s.send(v).expect("what");
|
||||
});
|
||||
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
|
||||
if let Ok(Ok(())) = r.recv() {
|
||||
let data = self.map_buf.slice(..).get_mapped_range();
|
||||
self.timestamps.copy_from_slice(bytemuck::cast_slice(&data));
|
||||
drop(data);
|
||||
self.map_buf.unmap();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start(&self, encoder: &mut wgpu::CommandEncoder, i: u32) {
|
||||
encoder.write_timestamp(&self.query_set, i * 2);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn stop(&self, encoder: &mut wgpu::CommandEncoder, i: u32) {
|
||||
encoder.write_timestamp(&self.query_set, i * 2 + 1);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start_compute(&self, pass: &mut wgpu::ComputePass, i: u32) {
|
||||
pass.write_timestamp(&self.query_set, i * 2);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn stop_compute(&self, pass: &mut wgpu::ComputePass, i: u32) {
|
||||
pass.write_timestamp(&self.query_set, i * 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
76
src/client/render/util/uniform.rs
Normal file
76
src/client/render/util/uniform.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
pub struct Uniform<T> {
|
||||
buffer: wgpu::Buffer,
|
||||
ty: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Default + bytemuck::Pod> Uniform<T> {
|
||||
pub fn init(device: &wgpu::Device, name: &str) -> Self {
|
||||
Self {
|
||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||
contents: bytemuck::cast_slice(&[T::default()]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: bytemuck::Pod> Uniform<T> {
|
||||
pub fn init_with(device: &wgpu::Device, name: &str, data: &[T]) -> Self {
|
||||
Self {
|
||||
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&(name.to_owned() + " Uniform Buf")),
|
||||
contents: bytemuck::cast_slice(data),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
data: T,
|
||||
) {
|
||||
let slice = &[data];
|
||||
let mut view = belt.write_buffer(
|
||||
encoder,
|
||||
&self.buffer,
|
||||
0,
|
||||
unsafe {
|
||||
std::num::NonZeroU64::new_unchecked((slice.len() * std::mem::size_of::<T>()) as u64)
|
||||
},
|
||||
device,
|
||||
);
|
||||
view.copy_from_slice(bytemuck::cast_slice(slice));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uniform<T> {
|
||||
pub fn bind_group_layout_entry(&self, binding: u32) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility: wgpu::ShaderStages::VERTEX
|
||||
| wgpu::ShaderStages::FRAGMENT
|
||||
| wgpu::ShaderStages::COMPUTE,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}
|
||||
pub fn bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
|
||||
wgpu::BindGroupEntry {
|
||||
binding,
|
||||
resource: self.buffer.as_entire_binding(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user