Add feature metadata

This commit is contained in:
Maximilian Ammann 2022-01-15 15:14:26 +01:00
parent 635df81f1c
commit e044b81a72
4 changed files with 168 additions and 52 deletions

View File

@ -23,16 +23,18 @@ impl Queue<wgpu::Buffer> for wgpu::Queue {
/// This is inspired by the memory pool in Vulkan documented
/// [here](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/custom_memory_pools.html).
#[derive(Debug)]
pub struct BufferPool<Q, B, V, I, M> {
pub struct BufferPool<Q, B, V, I, M, FM> {
vertices: BackingBuffer<B>,
indices: BackingBuffer<B>,
metadata: BackingBuffer<B>,
feature_metadata: BackingBuffer<B>,
pub index: VecDeque<IndexEntry>,
phantom_v: PhantomData<V>,
phantom_i: PhantomData<I>,
phantom_q: PhantomData<Q>,
phantom_m: PhantomData<M>,
phantom_fm: PhantomData<FM>,
}
#[derive(Debug)]
@ -40,15 +42,17 @@ enum BackingBufferType {
VERTICES,
INDICES,
METADATA,
FEATURE_METADATA,
}
impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
BufferPool<Q, B, V, I, M>
impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod, FM: bytemuck::Pod>
BufferPool<Q, B, V, I, M, FM>
{
pub fn new(
vertices: BackingBufferDescriptor<B>,
indices: BackingBufferDescriptor<B>,
metadata: BackingBufferDescriptor<B>,
feature_metadata: BackingBufferDescriptor<B>,
) -> Self {
Self {
vertices: BackingBuffer::new(
@ -66,11 +70,17 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
metadata.inner_size,
BackingBufferType::METADATA,
),
feature_metadata: BackingBuffer::new(
feature_metadata.buffer,
feature_metadata.inner_size,
BackingBufferType::FEATURE_METADATA,
),
index: VecDeque::new(), // TODO: Approximate amount of buffers in pool
phantom_v: Default::default(),
phantom_i: Default::default(),
phantom_q: Default::default(),
phantom_m: Default::default(),
phantom_fm: Default::default(),
}
}
@ -80,6 +90,7 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
BackingBufferType::VERTICES => &self.vertices,
BackingBufferType::INDICES => &self.indices,
BackingBufferType::METADATA => &self.metadata,
BackingBufferType::FEATURE_METADATA => &self.feature_metadata,
}
.find_largest_gap(&self.index);
@ -98,6 +109,10 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
&self.metadata.inner
}
pub fn feature_metadata(&self) -> &B {
&self.feature_metadata.inner
}
/// The VertexBuffers can contain padding elements. Not everything from a VertexBuffers is useable.
/// The function returns the `bytes` and `aligned_bytes`. See [`OverAlignedVertexBuffer`].
fn align(
@ -121,14 +136,15 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
pub fn allocate_geometry(
&mut self,
queue: &Q,
id: u32,
coords: TileCoords,
over_aligned: &OverAlignedVertexBuffer<V, I>,
metadata: M,
feature_metadata: &Vec<FM>,
) {
let vertices_stride = size_of::<V>() as wgpu::BufferAddress;
let indices_stride = size_of::<I>() as wgpu::BufferAddress;
let metadata_stride = size_of::<M>() as wgpu::BufferAddress;
let feature_metadata_stride = size_of::<FM>() as wgpu::BufferAddress;
let (vertices_bytes, aligned_vertices_bytes) = Self::align(
vertices_stride,
@ -141,14 +157,28 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
over_aligned.usable_indices as BufferAddress,
);
let (metadata_bytes, aligned_metadata_bytes) = Self::align(metadata_stride, 1, 1);
let (feature_metadata_bytes, aligned_feature_metadata_bytes) = Self::align(
feature_metadata_stride,
feature_metadata.len() as BufferAddress,
feature_metadata.len() as BufferAddress,
);
if feature_metadata_bytes != aligned_feature_metadata_bytes {
// FIXME: align if not aligned?
panic!(
"feature_metadata is not aligned. This should not happen as long as size_of::<FM>() is a multiple of the alignment."
)
}
let maybe_entry = IndexEntry {
id,
coords,
buffer_vertices: self.vertices.make_room(vertices_bytes, &mut self.index),
buffer_indices: self.indices.make_room(indices_bytes, &mut self.index),
usable_indices: over_aligned.usable_indices as u32,
buffer_metadata: self.metadata.make_room(metadata_bytes, &mut self.index),
buffer_feature_metadata: self
.feature_metadata
.make_room(feature_metadata_bytes, &mut self.index),
};
// write_buffer() is the preferred method for WASM: https://toji.github.io/webgpu-best-practices/buffer-uploads.html#when-in-doubt-writebuffer
@ -168,6 +198,12 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
maybe_entry.buffer_metadata.start,
&bytemuck::cast_slice(&[metadata])[0..aligned_metadata_bytes as usize],
);
queue.write_buffer(
&self.feature_metadata.inner,
maybe_entry.buffer_feature_metadata.start,
&bytemuck::cast_slice(feature_metadata.as_slice())
[0..aligned_feature_metadata_bytes as usize],
);
self.index.push_back(maybe_entry);
}
@ -235,11 +271,13 @@ impl<B> BackingBuffer<B> {
BackingBufferType::VERTICES => first.buffer_vertices.start,
BackingBufferType::INDICES => first.buffer_indices.start,
BackingBufferType::METADATA => first.buffer_metadata.start,
BackingBufferType::FEATURE_METADATA => first.buffer_feature_metadata.start,
});
let end = index.back().map(|first| match self.typ {
BackingBufferType::VERTICES => first.buffer_vertices.end,
BackingBufferType::INDICES => first.buffer_indices.end,
BackingBufferType::METADATA => first.buffer_metadata.end,
BackingBufferType::FEATURE_METADATA => first.buffer_feature_metadata.end,
});
if let Some(start) = start {
@ -272,7 +310,6 @@ impl<B> BackingBuffer<B> {
#[derive(Debug)]
pub struct IndexEntry {
pub id: u32,
pub coords: TileCoords,
// Range of bytes within the backing buffer for vertices
buffer_vertices: Range<wgpu::BufferAddress>,
@ -280,6 +317,8 @@ pub struct IndexEntry {
buffer_indices: Range<wgpu::BufferAddress>,
// Range of bytes within the backing buffer for metadata
buffer_metadata: Range<wgpu::BufferAddress>,
// Range of bytes within the backing buffer for feature metadata
buffer_feature_metadata: Range<wgpu::BufferAddress>,
// Amount of actually usable indices. Each index has the size/format `IndexDataType`.
// Can be lower than size(buffer_indices) / indices_stride because of alignment.
usable_indices: u32,
@ -301,6 +340,10 @@ impl IndexEntry {
pub fn metadata_buffer_range(&self) -> Range<wgpu::BufferAddress> {
self.buffer_metadata.clone()
}
pub fn feature_metadata_buffer_range(&self) -> Range<wgpu::BufferAddress> {
self.buffer_feature_metadata.clone()
}
}
#[cfg(test)]

View File

@ -1,7 +1,8 @@
use std::cmp;
use std::default::Default;
use std::{cmp, iter};
use log::trace;
use vector_tile::tile::Layer;
use wgpu::{Buffer, BufferAddress, Limits, Queue};
use winit::dpi::PhysicalSize;
use winit::window::Window;
@ -10,6 +11,10 @@ use crate::io::worker_loop::WorkerLoop;
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
use crate::render::camera;
use crate::render::options::{
DEBUG_WIREFRAME, FEATURE_METADATA_BUFFER_SIZE, INDEX_FORMAT, INDICES_BUFFER_SIZE,
TILE_MASK_INSTANCE_COUNT, TILE_META_COUNT, VERTEX_BUFFER_SIZE,
};
use crate::render::tile_mask_pattern::TileMaskPattern;
use crate::tesselation::IndexDataType;
use crate::util::FPSMeter;
@ -19,12 +24,6 @@ use super::shaders;
use super::shaders::*;
use super::texture::Texture;
const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint16; // Must match IndexDataType
const VERTEX_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 8;
const INDICES_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 8;
const TILE_META_COUNT: BufferAddress = 1024 * 8; // FIXME: Move this to BufferPool
const TILE_MASK_INSTANCE_COUNT: BufferAddress = 512; // FIXME: Pick reasonable size
pub struct RenderState {
instance: wgpu::Instance,
@ -50,7 +49,14 @@ pub struct RenderState {
globals_uniform_buffer: wgpu::Buffer,
buffer_pool: BufferPool<Queue, Buffer, ShaderVertex, IndexDataType, ShaderTileMetadata>,
buffer_pool: BufferPool<
Queue,
Buffer,
ShaderVertex,
IndexDataType,
ShaderTileMetadata,
ShaderFeatureStyle,
>,
tile_mask_pattern: TileMaskPattern,
tile_mask_instances_buffer: wgpu::Buffer,
@ -103,11 +109,17 @@ impl RenderState {
};
// create a device and a queue
let features = if DEBUG_WIREFRAME {
wgpu::Features::default() | wgpu::Features::POLYGON_MODE_LINE
} else {
wgpu::Features::default()
};
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::default(),
features,
limits,
},
None,
@ -115,26 +127,33 @@ impl RenderState {
.await
.unwrap();
let vertex_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: VERTEX_BUFFER_SIZE,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let indices_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
let feature_metadata_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: FEATURE_METADATA_BUFFER_SIZE,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let indices_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: INDICES_BUFFER_SIZE,
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let tile_masks_uniform_buffer_size =
let tile_masks_instances_buffer_size =
std::mem::size_of::<ShaderTileMaskInstance>() as u64 * TILE_MASK_INSTANCE_COUNT;
let tile_mask_instances = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: tile_masks_uniform_buffer_size,
size: tile_masks_instances_buffer_size,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
@ -142,11 +161,11 @@ impl RenderState {
let globals_buffer_byte_size =
cmp::max(MIN_BUFFER_SIZE, std::mem::size_of::<ShaderGlobals>() as u64);
let tiles_uniform_buffer_size =
let metadata_buffer_size =
std::mem::size_of::<ShaderTileMetadata>() as u64 * TILE_META_COUNT;
let tiles_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
let metadata_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Tiles ubo"),
size: tiles_uniform_buffer_size,
size: metadata_buffer_size,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
@ -273,9 +292,10 @@ impl RenderState {
perspective: projection,
suspended: false, // Initially the app is not suspended
buffer_pool: BufferPool::new(
BackingBufferDescriptor::new(vertex_uniform_buffer, VERTEX_BUFFER_SIZE),
BackingBufferDescriptor::new(indices_uniform_buffer, INDICES_BUFFER_SIZE),
BackingBufferDescriptor::new(tiles_uniform_buffer, tiles_uniform_buffer_size),
BackingBufferDescriptor::new(vertex_buffer, VERTEX_BUFFER_SIZE),
BackingBufferDescriptor::new(indices_buffer, INDICES_BUFFER_SIZE),
BackingBufferDescriptor::new(metadata_buffer, metadata_buffer_size),
BackingBufferDescriptor::new(feature_metadata_buffer, FEATURE_METADATA_BUFFER_SIZE),
),
tile_mask_pattern: TileMaskPattern::new(),
}
@ -332,19 +352,47 @@ impl RenderState {
pub fn upload_tile_geometry(&mut self, worker_loop: &WorkerLoop) {
let upload = worker_loop.pop_all();
for tile in upload.iter() {
let world_coords = tile.coords.into_world_tile();
for layer in upload.iter() {
let world_coords = layer.coords.into_world_tile();
self.tile_mask_pattern.update_bounds(&world_coords);
/*match tile.layer_data.name() {
"transportation" => {}
"building" => {}
"boundary" => {}
"water" => {}
"waterway" => {}
_ => {
continue;
}
};*/
let feature_metadata = layer
.layer_data
.features()
.iter()
.enumerate()
.flat_map(|(i, feature)| {
iter::repeat(ShaderFeatureStyle {
color: match layer.layer_data.name() {
"transportation" => [1.0, 0.0, 0.0, 1.0],
"building" => [0.0, 1.0, 1.0, 1.0],
"boundary" => [0.0, 0.0, 0.0, 1.0],
"water" => [0.0, 0.0, 1.0, 1.0],
"waterway" => [0.0, 0.0, 1.0, 1.0],
_ => [0.0, 0.0, 0.0, 0.0],
},
})
.take(*layer.feature_vertices.get(i).unwrap() as usize)
})
.collect::<Vec<_>>();
self.buffer_pool.allocate_geometry(
&self.queue,
tile.id,
tile.coords,
&tile.over_aligned,
ShaderTileMetadata::new(
[0.0, 0.0, 0.0, 1.0],
world_coords.into_world(4096.0).into(),
),
layer.coords,
&layer.buffer,
ShaderTileMetadata::new(world_coords.into_world(4096.0).into()),
&feature_metadata,
);
}
@ -442,18 +490,18 @@ impl RenderState {
.vertices()
.slice(entry.vertices_buffer_range()),
);
let id = entry.id as BufferAddress;
pass.set_vertex_buffer(
1,
self.buffer_pool
.metadata()
.slice(entry.metadata_buffer_range()),
);
/* if !self.tile_fill_range.is_empty() {
pass.draw_indexed(self.tile_fill_range.clone(), 0, 0..1);
}*/
trace!("current buffer_pool index {:?}", self.buffer_pool.index);
// FIXME: Custom Instance index possibly breaks on Metal
pass.set_vertex_buffer(
2,
self.buffer_pool
.feature_metadata()
.slice(entry.feature_metadata_buffer_range()),
);
pass.draw_indexed(entry.indices_range(), 0, 0..1);
}
}

View File

@ -81,12 +81,14 @@ impl VertexShaderState {
pub mod tile {
use super::{ShaderTileMetadata, ShaderVertex};
use crate::platform::COLOR_TEXTURE_FORMAT;
use wgpu::BlendState;
use super::{FragmentShaderState, VertexShaderState};
pub const VERTEX: VertexShaderState = VertexShaderState::new(
include_str!("tile.vertex.wgsl"),
&[
// vertex data
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<ShaderVertex>() as u64,
step_mode: wgpu::VertexStepMode::Vertex,
@ -105,21 +107,29 @@ pub mod tile {
},
],
},
// tile metadata
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<ShaderTileMetadata>() as u64,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[
// translate
wgpu::VertexAttribute {
offset: 0,
format: wgpu::VertexFormat::Float32x3,
shader_location: 4,
},
],
},
// vertex style
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<ShaderTileMetadata>() as u64,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
// color
wgpu::VertexAttribute {
offset: 0,
format: wgpu::VertexFormat::Float32x4,
shader_location: 3,
},
// translate
wgpu::VertexAttribute {
offset: wgpu::VertexFormat::Float32x4.size(),
format: wgpu::VertexFormat::Float32x3,
shader_location: 4,
shader_location: 5,
},
],
},
@ -130,7 +140,18 @@ pub mod tile {
include_str!("tile.fragment.wgsl"),
&[wgpu::ColorTargetState {
format: COLOR_TEXTURE_FORMAT,
blend: None,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
}],
);
@ -281,18 +302,22 @@ impl ShaderTileMaskInstance {
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
pub struct ShaderFeatureStyle {
pub color: Vec4f32,
}
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct ShaderTileMetadata {
pub color: Vec4f32,
pub translate: Vec3f32,
_pad1: i32, // _padX aligns it to 16 bytes = AlignOf(Vec4f32/vec4<f32>): // https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
}
impl ShaderTileMetadata {
pub fn new(color: Vec4f32, translate: Vec3f32) -> Self {
pub fn new(translate: Vec3f32) -> Self {
Self {
color,
translate,
_pad1: Default::default(),
}

View File

@ -18,8 +18,8 @@ struct VertexOutput {
fn main(
[[location(0)]] position: vec2<f32>,
[[location(1)]] normal: vec2<f32>,
[[location(3)]] color: vec4<f32>,
[[location(4)]] translate: vec3<f32>,
[[location(5)]] color: vec4<f32>,
[[builtin(instance_index)]] instance_idx: u32 // instance_index is used when we have multiple instances of the same "object"
) -> VertexOutput {
let z = 0.0;