mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add metadata to buffer pool
This commit is contained in:
parent
bc5b12159d
commit
956bca24b8
@ -10,9 +10,6 @@ use wgpu::BufferAddress;
|
||||
use crate::coords::TileCoords;
|
||||
use crate::tesselation::OverAlignedVertexBuffer;
|
||||
|
||||
/// Buffer and its size
|
||||
pub struct BackingBufferDescriptor<B>(pub B, pub wgpu::BufferAddress);
|
||||
|
||||
pub trait Queue<B> {
|
||||
fn write_buffer(&self, buffer: &B, offset: wgpu::BufferAddress, data: &[u8]);
|
||||
}
|
||||
@ -26,36 +23,66 @@ 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> {
|
||||
pub struct BufferPool<Q, B, V, I, M> {
|
||||
vertices: BackingBuffer<B>,
|
||||
indices: BackingBuffer<B>,
|
||||
metadata: BackingBuffer<B>,
|
||||
|
||||
pub index: VecDeque<IndexEntry>,
|
||||
phantom_v: PhantomData<V>,
|
||||
phantom_i: PhantomData<I>,
|
||||
phantom_q: PhantomData<Q>,
|
||||
phantom_m: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I> {
|
||||
pub fn new(vertices: BackingBufferDescriptor<B>, indices: BackingBufferDescriptor<B>) -> Self {
|
||||
#[derive(Debug)]
|
||||
enum BackingBufferType {
|
||||
VERTICES,
|
||||
INDICES,
|
||||
METADATA,
|
||||
}
|
||||
|
||||
impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, M: bytemuck::Pod>
|
||||
BufferPool<Q, B, V, I, M>
|
||||
{
|
||||
pub fn new(
|
||||
vertices: BackingBufferDescriptor<B>,
|
||||
indices: BackingBufferDescriptor<B>,
|
||||
metadata: BackingBufferDescriptor<B>,
|
||||
) -> Self {
|
||||
Self {
|
||||
vertices: BackingBuffer::new(vertices.0, vertices.1),
|
||||
indices: BackingBuffer::new(indices.0, indices.1),
|
||||
vertices: BackingBuffer::new(
|
||||
vertices.buffer,
|
||||
vertices.inner_size,
|
||||
BackingBufferType::VERTICES,
|
||||
),
|
||||
indices: BackingBuffer::new(
|
||||
indices.buffer,
|
||||
indices.inner_size,
|
||||
BackingBufferType::INDICES,
|
||||
),
|
||||
metadata: BackingBuffer::new(
|
||||
metadata.buffer,
|
||||
metadata.inner_size,
|
||||
BackingBufferType::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(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn available_space(&self, vertices: bool) -> wgpu::BufferAddress {
|
||||
let gap = if vertices {
|
||||
&self.vertices
|
||||
} else {
|
||||
&self.indices
|
||||
fn available_space(&self, typ: BackingBufferType) -> wgpu::BufferAddress {
|
||||
let gap = match typ {
|
||||
BackingBufferType::VERTICES => &self.vertices,
|
||||
BackingBufferType::INDICES => &self.indices,
|
||||
BackingBufferType::METADATA => &self.metadata,
|
||||
}
|
||||
.find_largest_gap(&self.index, vertices);
|
||||
.find_largest_gap(&self.index);
|
||||
|
||||
gap.end - gap.start
|
||||
}
|
||||
|
||||
@ -67,6 +94,10 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
&self.indices.inner
|
||||
}
|
||||
|
||||
pub fn metadata(&self) -> &B {
|
||||
&self.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(
|
||||
@ -93,9 +124,11 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
id: u32,
|
||||
coords: TileCoords,
|
||||
over_aligned: &OverAlignedVertexBuffer<V, I>,
|
||||
metadata: M,
|
||||
) {
|
||||
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 (vertices_bytes, aligned_vertices_bytes) = Self::align(
|
||||
vertices_stride,
|
||||
@ -107,28 +140,17 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
over_aligned.buffer.indices.len() as BufferAddress,
|
||||
over_aligned.usable_indices as BufferAddress,
|
||||
);
|
||||
let (metadata_bytes, aligned_metadata_bytes) = Self::align(metadata_stride, 1, 1);
|
||||
|
||||
let maybe_entry = IndexEntry {
|
||||
id,
|
||||
coords,
|
||||
buffer_vertices: self
|
||||
.vertices
|
||||
.make_room(vertices_bytes, &mut self.index, true),
|
||||
buffer_indices: self
|
||||
.indices
|
||||
.make_room(indices_bytes, &mut self.index, false),
|
||||
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),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
maybe_entry.buffer_vertices.end - &maybe_entry.buffer_vertices.start,
|
||||
vertices_bytes
|
||||
);
|
||||
assert_eq!(
|
||||
maybe_entry.buffer_indices.end - &maybe_entry.buffer_indices.start,
|
||||
indices_bytes
|
||||
);
|
||||
|
||||
// write_buffer() is the preferred method for WASM: https://toji.github.io/webgpu-best-practices/buffer-uploads.html#when-in-doubt-writebuffer
|
||||
queue.write_buffer(
|
||||
&self.vertices.inner,
|
||||
@ -141,6 +163,11 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
maybe_entry.buffer_indices.start,
|
||||
&bytemuck::cast_slice(&over_aligned.buffer.indices)[0..aligned_indices_bytes as usize],
|
||||
);
|
||||
queue.write_buffer(
|
||||
&self.metadata.inner,
|
||||
maybe_entry.buffer_metadata.start,
|
||||
&bytemuck::cast_slice(&[metadata])[0..aligned_metadata_bytes as usize],
|
||||
);
|
||||
self.index.push_back(maybe_entry);
|
||||
}
|
||||
|
||||
@ -149,35 +176,52 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BackingBufferDescriptor<B> {
|
||||
/// The buffer which is used
|
||||
buffer: B,
|
||||
/// The size of buffer
|
||||
inner_size: wgpu::BufferAddress,
|
||||
}
|
||||
|
||||
impl<B> BackingBufferDescriptor<B> {
|
||||
pub fn new(buffer: B, inner_size: wgpu::BufferAddress) -> Self {
|
||||
Self { buffer, inner_size }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BackingBuffer<B> {
|
||||
/// The internal structure which is used for storage
|
||||
inner: B,
|
||||
/// The size of the `inner` buffer
|
||||
inner_size: wgpu::BufferAddress,
|
||||
typ: BackingBufferType,
|
||||
}
|
||||
|
||||
impl<B> BackingBuffer<B> {
|
||||
fn new(inner: B, inner_size: wgpu::BufferAddress) -> Self {
|
||||
Self { inner, inner_size }
|
||||
fn new(inner: B, inner_size: wgpu::BufferAddress, typ: BackingBufferType) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
inner_size,
|
||||
typ,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_room(
|
||||
&mut self,
|
||||
new_data: wgpu::BufferAddress,
|
||||
index: &mut VecDeque<IndexEntry>,
|
||||
vertices: bool,
|
||||
) -> Range<wgpu::BufferAddress> {
|
||||
if new_data > self.inner_size {
|
||||
panic!("can not allocate because backing buffers are too small")
|
||||
}
|
||||
|
||||
let mut available_gap = self.find_largest_gap(index, vertices);
|
||||
let mut available_gap = self.find_largest_gap(index);
|
||||
|
||||
while new_data > available_gap.end - available_gap.start {
|
||||
// no more space, we need to evict items
|
||||
if index.pop_front().is_some() {
|
||||
available_gap = self.find_largest_gap(index, vertices);
|
||||
available_gap = self.find_largest_gap(index);
|
||||
} else {
|
||||
panic!("evicted even though index is empty")
|
||||
}
|
||||
@ -186,24 +230,16 @@ impl<B> BackingBuffer<B> {
|
||||
available_gap.start..available_gap.start + new_data
|
||||
}
|
||||
|
||||
fn find_largest_gap(
|
||||
&self,
|
||||
index: &VecDeque<IndexEntry>,
|
||||
vertices: bool,
|
||||
) -> Range<wgpu::BufferAddress> {
|
||||
let start = index.front().map(|first| {
|
||||
if vertices {
|
||||
first.buffer_vertices.start
|
||||
} else {
|
||||
first.buffer_indices.start
|
||||
}
|
||||
fn find_largest_gap(&self, index: &VecDeque<IndexEntry>) -> Range<wgpu::BufferAddress> {
|
||||
let start = index.front().map(|first| match self.typ {
|
||||
BackingBufferType::VERTICES => first.buffer_vertices.start,
|
||||
BackingBufferType::INDICES => first.buffer_indices.start,
|
||||
BackingBufferType::METADATA => first.buffer_metadata.start,
|
||||
});
|
||||
let end = index.back().map(|first| {
|
||||
if vertices {
|
||||
first.buffer_vertices.end
|
||||
} else {
|
||||
first.buffer_indices.end
|
||||
}
|
||||
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,
|
||||
});
|
||||
|
||||
if let Some(start) = start {
|
||||
@ -242,6 +278,8 @@ pub struct IndexEntry {
|
||||
buffer_vertices: Range<wgpu::BufferAddress>,
|
||||
// Range of bytes within the backing buffer for indices
|
||||
buffer_indices: Range<wgpu::BufferAddress>,
|
||||
// Range of bytes within the backing buffer for metadata
|
||||
buffer_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,
|
||||
@ -259,6 +297,10 @@ impl IndexEntry {
|
||||
pub fn vertices_buffer_range(&self) -> Range<wgpu::BufferAddress> {
|
||||
self.buffer_vertices.clone()
|
||||
}
|
||||
|
||||
pub fn metadata_buffer_range(&self) -> Range<wgpu::BufferAddress> {
|
||||
self.buffer_metadata.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -266,7 +308,9 @@ mod tests {
|
||||
use lyon::tessellation::VertexBuffers;
|
||||
use wgpu::BufferAddress;
|
||||
|
||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool, Queue};
|
||||
use crate::render::buffer_pool::{
|
||||
BackingBuffer, BackingBufferDescriptor, BackingBufferType, BufferPool, Queue,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestBuffer {
|
||||
@ -298,9 +342,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_allocate() {
|
||||
let mut pool: BufferPool<TestQueue, TestBuffer, TestVertex, u32> = BufferPool::new(
|
||||
BackingBufferDescriptor(TestBuffer { size: 128 }, 128),
|
||||
BackingBufferDescriptor(TestBuffer { size: 1024 }, 1024),
|
||||
let mut pool: BufferPool<TestQueue, TestBuffer, TestVertex, u32, u32> = BufferPool::new(
|
||||
BackingBuffer::new(TestBuffer { size: 128 }, 128, BackingBufferType::VERTICES),
|
||||
BackingBuffer::new(TestBuffer { size: 128 }, 128, BackingBufferType::INDICES),
|
||||
BackingBuffer::new(TestBuffer { size: 128 }, 128, BackingBufferType::METADATA),
|
||||
);
|
||||
|
||||
let queue = TestQueue {};
|
||||
@ -316,29 +361,35 @@ mod tests {
|
||||
let data24bytes_aligned = data24bytes.into();
|
||||
|
||||
for _ in 0..2 {
|
||||
pool.allocate_geometry(&queue, 0, (0, 0, 0).into(), &data48bytes_aligned);
|
||||
pool.allocate_geometry(&queue, 0, (0, 0, 0).into(), &data48bytes_aligned, 2);
|
||||
}
|
||||
assert_eq!(128 - 2 * 48, pool.available_space(true));
|
||||
assert_eq!(
|
||||
128 - 2 * 48,
|
||||
pool.available_space(BackingBufferType::VERTICES)
|
||||
);
|
||||
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned);
|
||||
assert_eq!(128 - 2 * 48 - 24, pool.available_space(true));
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned, 2);
|
||||
assert_eq!(
|
||||
128 - 2 * 48 - 24,
|
||||
pool.available_space(BackingBufferType::VERTICES)
|
||||
);
|
||||
println!("{:?}", &pool.index);
|
||||
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned);
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned, 2);
|
||||
// appended now at the beginning
|
||||
println!("{:?}", &pool.index);
|
||||
assert_eq!(24, pool.available_space(true));
|
||||
assert_eq!(24, pool.available_space(BackingBufferType::VERTICES));
|
||||
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned);
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned, 2);
|
||||
println!("{:?}", &pool.index);
|
||||
assert_eq!(0, pool.available_space(true));
|
||||
assert_eq!(0, pool.available_space(BackingBufferType::VERTICES));
|
||||
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned);
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned, 2);
|
||||
println!("{:?}", &pool.index);
|
||||
assert_eq!(24, pool.available_space(true));
|
||||
assert_eq!(24, pool.available_space(BackingBufferType::VERTICES));
|
||||
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned);
|
||||
pool.allocate_geometry(&queue, 1, (0, 0, 0).into(), &data24bytes_aligned, 2);
|
||||
println!("{:?}", &pool.index);
|
||||
assert_eq!(0, pool.available_space(true));
|
||||
assert_eq!(0, pool.available_space(BackingBufferType::VERTICES));
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ 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 = 512; // FIXME: Move this to BufferPool
|
||||
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 {
|
||||
@ -48,10 +48,9 @@ pub struct RenderState {
|
||||
|
||||
depth_texture: Texture,
|
||||
|
||||
tiles_uniform_buffer: wgpu::Buffer,
|
||||
globals_uniform_buffer: wgpu::Buffer,
|
||||
|
||||
buffer_pool: BufferPool<Queue, Buffer, ShaderVertex, IndexDataType>,
|
||||
buffer_pool: BufferPool<Queue, Buffer, ShaderVertex, IndexDataType, ShaderTileMetadata>,
|
||||
|
||||
tile_mask_pattern: TileMaskPattern,
|
||||
tile_mask_instances_buffer: wgpu::Buffer,
|
||||
@ -268,15 +267,15 @@ impl RenderState {
|
||||
depth_texture,
|
||||
sample_count,
|
||||
globals_uniform_buffer,
|
||||
tiles_uniform_buffer,
|
||||
fps_meter: FPSMeter::new(),
|
||||
tile_mask_instances_buffer: tile_mask_instances,
|
||||
camera,
|
||||
perspective: projection,
|
||||
suspended: false, // Initially the app is not suspended
|
||||
buffer_pool: BufferPool::new(
|
||||
BackingBufferDescriptor(vertex_uniform_buffer, VERTEX_BUFFER_SIZE),
|
||||
BackingBufferDescriptor(indices_uniform_buffer, INDICES_BUFFER_SIZE),
|
||||
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),
|
||||
),
|
||||
tile_mask_pattern: TileMaskPattern::new(),
|
||||
}
|
||||
@ -342,15 +341,10 @@ impl RenderState {
|
||||
tile.id,
|
||||
tile.coords,
|
||||
&tile.over_aligned,
|
||||
);
|
||||
|
||||
self.queue.write_buffer(
|
||||
&self.tiles_uniform_buffer,
|
||||
std::mem::size_of::<ShaderTileMetadata>() as u64 * tile.id as u64,
|
||||
bytemuck::cast_slice(&[ShaderTileMetadata::new(
|
||||
ShaderTileMetadata::new(
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
world_coords.into_world(4096.0).into(),
|
||||
)]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -451,10 +445,9 @@ impl RenderState {
|
||||
let id = entry.id as BufferAddress;
|
||||
pass.set_vertex_buffer(
|
||||
1,
|
||||
self.tiles_uniform_buffer.slice(
|
||||
std::mem::size_of::<ShaderTileMetadata>() as u64 * id
|
||||
..std::mem::size_of::<ShaderTileMetadata>() as u64 * (id + 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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user