Skip to main content

Module vertex_pool

Module vertex_pool 

Source
Expand description

A pool that packs many vertex streams into one buffer per layout.

Uploading a mesh’s vertices per buffer is wasteful: every wgpu::Buffer carries driver overhead, and a draw can only bind a handful of them. A VertexStreamPool instead owns one large buffer per vertex layout and hands out element ranges inside them, so every mesh that shares a layout shares a buffer.

§Why one allocator for every layout

Every vertex stream a draw binds reads the same element index: a draw’s firstVertex, or its baseVertex plus an index, picks one element out of each stream at once. So a mesh whose vertices live in a pool has to land at the same element index in all of them, and one allocation has to cover all of its streams.

That is why the pool hands out elements, not bytes, and keeps a single allocator for every layout: one VertexAllocation is one range of element indices, and each layout’s buffer is that many elements times its stride. Giving each layout its own allocator would not do, because two allocators asked for the same count in different units — bytes, scaled by different strides — drift apart: the allocator’s size bins are a logarithmic approximation, so n * 8 and n * 12 can round into different bins and two streams of the same mesh end up at different indices, which silently reads the wrong vertices.

A byte offset inside a layout’s buffer is its element index times that layout’s stride. Since a stride is always a multiple of [wgpu::VERTEX_ALIGNMENT], every offset is a multiple of it too, which is what [wgpu::Queue::write_buffer] asks for.

§Example

use unlit_wgpu::specialize::{VertexAttributes, VertexBufferLayoutDesc};
use unlit_wgpu::vertex_pool::VertexStreamPool;

let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
let layout = VertexBufferLayoutDesc {
    array_stride: 8,
    step_mode: wgpu::VertexStepMode::Vertex,
    attributes: VertexAttributes::new(),
};

let mut pool = VertexStreamPool::new(
    &device,
    "example::vertices",
    wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
    1024,
);

let vertices = pool
    .allocate(&device, &queue, &[layout.clone()], 3)
    .expect("the pool has room");
let buffer = pool.buffer(&layout).expect("the layout has a buffer");
let offset = VertexStreamPool::byte_offset(&layout, vertices.allocation.offset);
queue.write_buffer(buffer, offset, &[0u8; 24]);
pool.release(vertices.allocation);

Re-exports§

use core::num::NonZeroU32; 🔒
use hashbrown::HashMap; 🔒
use crate::offset_allocator::Allocation; 🔒
use crate::offset_allocator::Allocator; 🔒
use crate::offset_allocator::min_allocator_size; 🔒
use crate::specialize::VertexBufferLayoutDesc; 🔒

Structs§

Stream 🔒
One layout’s buffer and how much of it is in use.
VertexAllocation
One mesh’s vertices in a VertexStreamPool: one range of element indices, shared by every stream the mesh binds.
VertexStreamPool
One large buffer per vertex layout, sub-allocated into element ranges.

Constants§

DEFAULT_MAX_ALLOCATIONS 🔒
How many allocations a pool tracks unless the caller says otherwise.
ELEMENT_ALIGNMENT 🔒
The alignment the allocator hands out element indices at.
MIN_ELEMENTS 🔒
The step an element count grows by, and the floor for the first one.

Functions§

copy_into_larger 🔒
Replaces buffer with a larger one, copying old_size bytes across.
create_buffer 🔒
Creates a buffer of size bytes with the usages the pool needs.
growth_step 🔒
The element count to grow current to so it covers count.