Dynamically render tiles and fix stencil buffer layout

This commit is contained in:
Maximilian Ammann 2021-12-30 19:21:37 +01:00
parent cb47623caf
commit 53903c5fbc
11 changed files with 94 additions and 112 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -12,7 +12,7 @@ fn main() {
.build(&event_loop)
.unwrap();
let cache_io = Cache::new();
let mut cache_io = Cache::new();
let cache_main = cache_io.clone();
std::thread::spawn(move || {

View File

@ -111,7 +111,7 @@ impl InputHandler {
scene.stroke_width + (self.target_stroke_width - scene.stroke_width) / 5.0;
// Animate the strokes of primitive
scene.cpu_primitives[0 as usize].width = scene.stroke_width;
/* scene.cpu_primitives[0 as usize].width = scene.stroke_width;*/
/*
scene.cpu_primitives[STROKE_PRIM_ID as usize].color = [
(time_secs * 0.8 - 1.6).sin() * 0.1 + 0.1,

View File

@ -14,12 +14,14 @@ use crate::tesselation::{IndexDataType, Tesselated};
#[derive(Clone)]
pub struct TesselatedTile {
pub id: u32,
pub coords: TileCoords,
pub geometry: VertexBuffers<GpuVertexUniform, IndexDataType>,
}
#[derive(Clone)]
pub struct Cache {
current_id: u32,
requests: Arc<WorkQueue<TileCoords>>,
responses: Arc<WorkQueue<TesselatedTile>>,
}
@ -27,6 +29,7 @@ pub struct Cache {
impl Cache {
pub fn new() -> Self {
Self {
current_id: 0,
requests: Arc::new(WorkQueue::new()),
responses: Arc::new(WorkQueue::new()),
}
@ -41,7 +44,7 @@ impl Cache {
self.responses.try_pop_all()
}
pub fn run_loop(&self) {
pub fn run_loop(&mut self) {
loop {
while let Some(coords) = self.requests.pop() {
if let Some(file) = static_database::get_tile(&coords) {
@ -51,7 +54,12 @@ impl Cache {
VertexBuffers::new();
tile.tesselate_stroke(&mut geometry, 1);
self.responses.push(TesselatedTile { coords, geometry });
self.responses.push(TesselatedTile {
id: self.current_id,
coords,
geometry,
});
self.current_id += 1;
info!("tile ready: {:?}", &coords);
} else {
info!("tile failed: {:?}", &coords);

View File

@ -63,7 +63,7 @@ pub async fn setup(window: winit::window::Window, event_loop: EventLoop<()>, cac
let dt = now - last_render_time;
last_render_time = now;
input.update_state(&mut state, dt);
state.update(&cache);
state.upload_tile_geometry(&cache);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if lost

View File

@ -64,6 +64,7 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
pub fn allocate_geometry(
&mut self,
queue: &Q,
id: u32,
coords: TileCoords,
geometry: &VertexBuffers<V, I>,
) {
@ -73,6 +74,7 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
let new_indices = (geometry.indices.len() * indices_stride) as wgpu::BufferAddress;
let maybe_entry = IndexEntry {
id,
coords,
indices_stride: indices_stride as u64,
vertices: self.vertices.make_room(new_vertices, &mut self.index, true),
@ -182,6 +184,7 @@ impl<B> BackingBuffer<B> {
#[derive(Debug)]
pub struct IndexEntry {
pub id: u32,
pub coords: TileCoords,
indices_stride: u64,
vertices: Range<wgpu::BufferAddress>,

View File

@ -37,7 +37,7 @@ pub fn create_map_render_pipeline_description<'a>(
}
} else {
wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Always,
compare: wgpu::CompareFunction::Equal,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Keep,

View File

@ -49,17 +49,17 @@ impl GlobalsUniform {
pub struct GpuVertexUniform {
pub position: Vec2f32,
pub normal: Vec2f32,
pub prim_id: u32,
pub tile_id: u32,
_pad1: i32, // _padX aligns it to 8 bytes = AlignOf(Vec2f32=vec2<f32>):
// https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
}
impl GpuVertexUniform {
pub fn new(position: Vec2f32, normal: Vec2f32, prim_id: u32) -> Self {
pub fn new(position: Vec2f32, normal: Vec2f32, tile_id: u32) -> Self {
Self {
position,
normal,
prim_id,
tile_id,
_pad1: Default::default(),
}
}
@ -98,33 +98,18 @@ impl MaskInstanceUniform {
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct PrimitiveUniform {
pub struct TileUniform {
pub color: Vec4f32,
pub translate: Vec2f32,
pub z_index: i32,
pub width: f32,
pub angle: f32,
pub scale: f32,
_pad1: i32, // _padX aligns it to 16 bytes = AlignOf(Vec4f32/vec4<f32>):
_pad2: i32, // https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
}
impl PrimitiveUniform {
pub fn new(
color: Vec4f32,
translate: Vec2f32,
z_index: i32,
width: f32,
angle: f32,
scale: f32,
) -> Self {
impl TileUniform {
pub fn new(color: Vec4f32, translate: Vec2f32) -> Self {
Self {
color,
translate,
z_index,
width,
angle,
scale,
_pad1: Default::default(),
_pad2: Default::default(),
}

View File

@ -151,7 +151,7 @@ pub mod tile_mask {
&[wgpu::ColorTargetState {
format: COLOR_TEXTURE_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
write_mask: wgpu::ColorWrites::empty(),
}],
);
}

View File

@ -8,23 +8,19 @@ struct GlobalsUniform {
camera: CameraUniform;
};
struct PrimitiveUniform {
struct TileUniform {
color: vec4<f32>;
translate: vec2<f32>;
z_index: i32;
width: f32;
angle: f32;
scale: f32;
pad1: i32;
pad2: i32;
};
struct Primitives {
primitives: [[stride(48)]] array<PrimitiveUniform, 256>;
struct Tiles {
tiles: [[stride(32)]] array<TileUniform, 128>;
};
[[group(0), binding(0)]] var<uniform> globals: GlobalsUniform;
[[group(0), binding(1)]] var<uniform> u_primitives: Primitives;
[[group(0), binding(1)]] var<uniform> tiles: Tiles;
struct VertexOutput {
[[location(0)]] v_color: vec4<f32>;
@ -33,17 +29,17 @@ struct VertexOutput {
[[stage(vertex)]]
fn main(
[[location(0)]] a_position: vec2<f32>,
[[location(1)]] a_normal: vec2<f32>,
[[location(2)]] a_prim_id: u32,
[[location(0)]] position: vec2<f32>,
[[location(1)]] normal: vec2<f32>,
[[location(2)]] tile_id: u32,
[[builtin(instance_index)]] instance_idx: u32 // instance_index is used when we have multiple instances of the same "object"
) -> VertexOutput {
let prim: PrimitiveUniform = u_primitives.primitives[a_prim_id];
let tile = tiles.tiles[instance_idx];
let z = 0.0;
let world_pos = a_position + prim.translate + a_normal * prim.width;
let world_pos = position + tile.translate + normal;
let position = globals.camera.view_proj * vec4<f32>(world_pos, z, 1.0);
return VertexOutput(prim.color, position);
return VertexOutput(tile.color, position);
}

View File

@ -1,4 +1,5 @@
use std::cmp;
use std::default::Default;
use std::io::Cursor;
use std::ops::Range;
@ -16,7 +17,7 @@ use vector_tile::parse_tile_reader;
use crate::fps_meter::FPSMeter;
use crate::io::cache::Cache;
use crate::io::static_database;
use crate::io::{static_database, TileCoords};
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
use crate::render::{camera, shaders};
@ -29,26 +30,16 @@ use super::texture::Texture;
pub struct SceneParams {
pub stroke_width: f32,
pub cpu_primitives: Vec<PrimitiveUniform>,
}
impl Default for SceneParams {
fn default() -> Self {
SceneParams {
stroke_width: 1.0,
cpu_primitives: vec![],
}
SceneParams { stroke_width: 1.0 }
}
}
const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32;
const PRIM_BUFFER_LEN: usize = 256;
const STROKE_PRIM_ID: u32 = 0;
const FILL_PRIM_ID: u32 = 1;
const SECOND_TILE_FILL_PRIM_ID: u32 = 2;
const SECOND_TILE_STROKE_PRIM_ID: u32 = 5;
pub struct State {
instance: wgpu::Instance,
@ -72,7 +63,7 @@ pub struct State {
depth_texture: Texture,
prims_uniform_buffer: wgpu::Buffer,
tiles_uniform_buffer: wgpu::Buffer,
globals_uniform_buffer: wgpu::Buffer,
buffer_pool: BufferPool<Queue, Buffer, GpuVertexUniform, IndexDataType>,
@ -87,31 +78,7 @@ pub struct State {
impl SceneParams {
pub fn new() -> Self {
let mut cpu_primitives = Vec::with_capacity(PRIM_BUFFER_LEN);
for _ in 0..PRIM_BUFFER_LEN {
cpu_primitives.push(PrimitiveUniform::new(
[1.0, 0.0, 0.0, 1.0],
[0.0, 0.0],
0,
0.0,
0.0,
1.0,
));
}
// Stroke primitive
cpu_primitives[STROKE_PRIM_ID as usize] =
PrimitiveUniform::new([1.0, 0.0, 1.0, 1.0], [0.0, 0.0], 0, 1.0, 0.0, 1.0);
cpu_primitives[SECOND_TILE_STROKE_PRIM_ID as usize] =
PrimitiveUniform::new([0.5, 0.8, 0.1, 1.0], [4096.0, 0.0], 0, 1.0, 0.0, 1.0);
// Main fill primitive
cpu_primitives[FILL_PRIM_ID as usize] =
PrimitiveUniform::new([0.0, 0.0, 1.0, 1.0], [0.0, 0.0], 0, 1.0, 0.0, 1.0);
cpu_primitives[SECOND_TILE_FILL_PRIM_ID as usize] =
PrimitiveUniform::new([0.0, 1.0, 1.0, 1.0], [4096.0, 0.0], 0, 1.0, 0.0, 1.0);
Self {
cpu_primitives,
..SceneParams::default()
}
}
@ -198,18 +165,14 @@ impl State {
MaskInstanceUniform::new([0.0, 0.0], 4.0, 4.0, [1.0, 0.0, 0.0, 1.0]), // horizontal
//MaskInstanceUniform::new([0.0, 2.0 * 4096.0], 4.0, 1.0, [1.0, 0.0, 0.0, 1.0]), // vertical
// Step 2
MaskInstanceUniform::new([1.0 * 4096.0, 0.0], 1.0, 4.0, [0.0, 0.0, 1.0, 1.0]), // vertical
MaskInstanceUniform::new([0.0, 1.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([3.0 * 4096.0, 0.0], 1.0, 4.0, [0.0, 0.0, 1.0, 1.0]), // vertical
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([1.0 * 4096.0, 0.0], 1.0, 4.0, [0.0, 1.0, 0.0, 1.0]), // vertical
MaskInstanceUniform::new([3.0 * 4096.0, 0.0], 1.0, 4.0, [0.0, 1.0, 0.0, 1.0]), // vertical
// Step 3
MaskInstanceUniform::new([0.0, 1.0 * 4096.0], 4.0, 1.0, [0.0, 1.0, 0.0, 1.0]), // horizontal
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 4.0, 1.0, [0.0, 1.0, 0.0, 1.0]), // horizontal
MaskInstanceUniform::new([0.0, 1.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
// Step 4
MaskInstanceUniform::new([0.0, 1.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([2.0 * 4096.0, 1.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([2.0 * 4096.0, 3.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([1.0 * 4096.0, 0.0], 1.0, 4.0, [0.5, 0.25, 0.5, 1.0]), // vertical
MaskInstanceUniform::new([3.0 * 4096.0, 0.0], 1.0, 4.0, [0.5, 0.25, 0.5, 1.0]), // vertical
];
let tile_mask_instances = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -218,18 +181,15 @@ impl State {
usage: wgpu::BufferUsages::VERTEX,
});
let prim_buffer_byte_size = cmp::max(
MIN_BUFFER_SIZE,
(PRIM_BUFFER_LEN * std::mem::size_of::<PrimitiveUniform>()) as u64,
);
let globals_buffer_byte_size = cmp::max(
MIN_BUFFER_SIZE,
std::mem::size_of::<GlobalsUniform>() as u64,
);
let prims_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Prims ubo"),
size: prim_buffer_byte_size,
let tiles_uniform_buffer_size = std::mem::size_of::<TileUniform>() as u64 * 128; // FIXME: Tile count?
let tiles_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Tiles ubo"),
size: tiles_uniform_buffer_size,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
@ -260,7 +220,7 @@ impl State {
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(prim_buffer_byte_size),
min_binding_size: wgpu::BufferSize::new(tiles_uniform_buffer_size),
},
count: None,
},
@ -280,7 +240,7 @@ impl State {
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Buffer(
prims_uniform_buffer.as_entire_buffer_binding(),
tiles_uniform_buffer.as_entire_buffer_binding(),
),
},
],
@ -367,7 +327,7 @@ impl State {
sample_count,
scene: SceneParams::new(),
globals_uniform_buffer,
prims_uniform_buffer,
tiles_uniform_buffer: tiles_uniform_buffer,
fps_meter: FPSMeter::new(),
tile_mask_instances,
camera,
@ -425,12 +385,34 @@ impl State {
}
}
pub fn update(&mut self, cache: &Cache) {
for tile in cache.pop_all().iter() {
pub fn upload_tile_geometry(&mut self, cache: &Cache) {
const OFFSET_X: u32 = 2179;
const OFFSET_Y: u32 = 1421;
let upload = cache.pop_all();
for tile in upload.iter() {
let new_coords = TileCoords {
x: tile.coords.x - OFFSET_X,
y: tile.coords.y - OFFSET_Y,
z: tile.coords.z,
};
self.buffer_pool
.allocate_geometry(&self.queue, tile.coords, &tile.geometry);
.allocate_geometry(&self.queue, tile.id, new_coords, &tile.geometry);
let uniform = TileUniform::new(
[0.0, 0.0, 0.0, 1.0],
[new_coords.x as f32 * 4096.0, new_coords.y as f32 * 4096.0],
);
self.queue.write_buffer(
&self.tiles_uniform_buffer,
std::mem::size_of::<TileUniform>() as u64 * tile.id as u64,
bytemuck::cast_slice(&[uniform]),
);
}
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let frame = self.surface.get_current_texture()?;
let scene = &mut self.scene;
@ -446,12 +428,6 @@ impl State {
self.camera.create_camera_uniform(&self.projection),
)]),
);
self.queue.write_buffer(
&self.prims_uniform_buffer,
0,
bytemuck::cast_slice(&scene.cpu_primitives),
);
}
let mut encoder = self
@ -504,12 +480,21 @@ impl State {
pass.set_pipeline(&self.mask_pipeline);
pass.set_vertex_buffer(0, self.tile_mask_instances.slice(..));
// Draw 11 squares each out of 6 vertices
pass.draw(0..6, 0..11);
pass.draw(0..6, 0..7);
}
{
for entry in self.buffer_pool.available_vertices() {
let TileCoords { x, y, .. } = entry.coords;
pass.set_pipeline(&self.render_pipeline);
pass.set_stencil_reference(1);
let reference = match (x, y) {
(x, y) if x % 2 == 0 && y % 2 == 0 => 1,
(x, y) if x % 2 == 0 && y % 2 != 0 => 2,
(x, y) if x % 2 != 0 && y % 2 == 0 => 3,
(x, y) if x % 2 != 0 && y % 2 != 0 => 4,
_ => 0,
};
pass.set_stencil_reference(reference);
pass.set_index_buffer(
self.buffer_pool
.indices()
@ -526,7 +511,12 @@ impl State {
pass.draw_indexed(self.tile_fill_range.clone(), 0, 0..1);
}*/
trace!("current buffer_pool index {:?}", self.buffer_pool.index);
pass.draw_indexed(entry.indices_range(), 0, 0..1);
// FIXME: Custom Instance index possibly breaks on Metal
pass.draw_indexed(
entry.indices_range(),
0,
entry.id as u32..(entry.id + 1) as u32,
);
}
}
}