pub struct Buffer {
pub(crate) inner: DispatchBuffer,
pub(crate) map_context: Arc<Mutex<MapContext>>,
pub(crate) size: BufferAddress,
pub(crate) usage: BufferUsages,
}Expand description
Handle to a GPU-accessible buffer.
A Buffer is a memory allocation for use by the GPU, somewhat analogous to
Box<[u8]> in Rust.
The contents of buffers are untyped bytes; it is up to the application to
specify the interpretation of the bytes when the buffer is used, in ways
such as VertexBufferLayout.
A single buffer can be used to hold multiple independent pieces of data at
different offsets (e.g. both vertices and indices for one or more meshes).
A Buffer’s bytes have “interior mutability”: functions like
Queue::write_buffer or mapping a buffer for writing only require a
&Buffer, not a &mut Buffer, even though they modify its contents. wgpu
prevents simultaneous reads and writes of buffer contents using run-time
checks.
Created with Device::create_buffer() or
DeviceExt::create_buffer_init().
Corresponds to WebGPU GPUBuffer.
§How to get your data into a buffer
Every Buffer starts with all bytes zeroed.
There are many ways to load data into a Buffer:
- When creating a buffer, you may set the
mapped_at_creationflag, then write to itsget_mapped_range_mut(). This only works when the buffer is created and has not yet been used by the GPU, but it is all you need for buffers whose contents do not change after creation.- You may use
DeviceExt::create_buffer_init()as a convenient way to do that and copy data from a&[u8]you provide.
- You may use
- After creation, you may use
Buffer::map_async()to map it again; however, you then need to wait until the GPU is no longer using the buffer before you begin writing. - You may use
CommandEncoder::copy_buffer_to_buffer()to copy data into this buffer from another buffer. - You may use
Queue::write_buffer()to copy data into the buffer from a&[u8]. This uses a temporary “staging” buffer managed bywgputo hold the data.Queue::write_buffer_with()allows you to write directly into temporary storage instead of providing a slice you already prepared, which may allow your code to save the allocation of aVecor such.
- You may use
util::StagingBeltto manage a set of temporary buffers. This may be more efficient thanQueue::write_buffer_with()when you have many small copies to perform, but requires more steps to use, and tuning of the belt buffer size. - You may write your own staging buffer management customized to your
application, based on mapped buffers and
CommandEncoder::copy_buffer_to_buffer(). - A GPU computation’s results can be stored in a buffer:
- A compute shader may write to a buffer bound as a storage buffer.
- A render pass may render to a texture which is then copied to a buffer
using
CommandEncoder::copy_texture_to_buffer().
§Mapping buffers
If a Buffer is created with the appropriate usage, it can be mapped:
you can make its contents accessible to the CPU as an ordinary &[u8] or
&mut [u8] slice of bytes. Buffers created with the
mapped_at_creation flag set are also mapped initially.
Depending on the hardware, the buffer could be memory shared between CPU and GPU, so that the CPU has direct access to the same bytes the GPU will consult; or it may be ordinary CPU memory, whose contents the system must copy to/from the GPU as needed. This crate’s API is designed to work the same way in either case: at any given time, a buffer is either mapped and available to the CPU, or unmapped and ready for use by the GPU, but never both. This makes it impossible for either side to observe changes by the other immediately, and any necessary transfers can be carried out when the buffer transitions from one state to the other.
There are two ways to map a buffer:
-
If
BufferDescriptor::mapped_at_creationistrue, then the entire buffer is mapped when it is created. This is the easiest way to initialize a new buffer. You can setmapped_at_creationon any kind of buffer, regardless of itsusageflags. -
If the buffer’s
usageincludes theMAP_READorMAP_WRITEflags, then you can callbuffer.slice(range).map_async(mode, callback)to map the portion ofbuffergiven byrange. This waits for the GPU to finish using the buffer, and invokescallbackas soon as the buffer is safe for the CPU to access.
Once a buffer is mapped:
-
You can call
buffer.slice(range).get_mapped_range()to obtain aBufferView, which dereferences to a&[u8]that you can use to read the buffer’s contents. -
Or, you can call
buffer.slice(range).get_mapped_range_mut()to obtain aBufferViewMut, which dereferences to a&mut [u8]that you can use to read and write the buffer’s contents.
The given range must fall within the mapped portion of the buffer. If you
attempt to access overlapping ranges, even for shared access only, these
methods panic.
While a buffer is mapped, you may not submit any commands to the GPU that access it. You may record command buffers that use the buffer, but if you submit them while the buffer is mapped, submission will panic.
When you are done using the buffer on the CPU, you must call
Buffer::unmap to make it available for use by the GPU again. All
BufferView and BufferViewMut views referring to the buffer must be
dropped before you unmap it; otherwise, Buffer::unmap will panic.
§Example
If buffer was created with BufferUsages::MAP_WRITE, we could fill it
with f32 values like this:
let capturable = buffer.clone();
buffer.map_async(wgpu::MapMode::Write, .., move |result| {
if result.is_ok() {
let mut view = capturable.get_mapped_range_mut(..);
let floats: &mut [f32] = bytemuck::cast_slice_mut(&mut view);
floats.fill(42.0);
drop(view);
capturable.unmap();
}
});This code takes the following steps:
-
First, it makes a cloned handle to the buffer for capture by the callback passed to
map_async. Since amap_asynccallback may be invoked from another thread, interaction between the callback and the thread callingmap_asyncgenerally requires some sort of shared heap data like this. In real code, there might be anArcto some larger structure that itself ownsbuffer. -
Then, it calls
Buffer::sliceto make aBufferSlicereferring to the buffer’s entire contents. -
Next, it calls
BufferSlice::map_asyncto request that the bytes to which the slice refers be made accessible to the CPU (“mapped”). This may entail waiting for previously enqueued operations onbufferto finish. Althoughmap_asyncitself always returns immediately, it saves the callback function to be invoked later. -
When some later call to
Device::pollorInstance::poll_all(not shown in this example) determines that the buffer is mapped and ready for the CPU to use, it invokes the callback function. -
The callback function calls
Buffer::sliceand thenBufferSlice::get_mapped_range_mutto obtain aBufferViewMut, which dereferences to a&mut [u8]slice referring to the buffer’s bytes. -
It then uses the
bytemuckcrate to turn the&mut [u8]into a&mut [f32], and calls the slicefillmethod to fill the buffer with a useful value. -
Finally, the callback drops the view and calls
Buffer::unmapto unmap the buffer. In real code, the callback would also need to do some sort of synchronization to let the rest of the program know that it has completed its work.
If using map_async directly is awkward, you may find it more convenient to
use Queue::write_buffer and util::DownloadBuffer::read_buffer.
However, those each have their own tradeoffs; the asynchronous nature of GPU
execution makes it hard to avoid friction altogether.
§Mapping buffers on the web
When compiled to WebAssembly and running in a browser content process,
wgpu implements its API in terms of the browser’s WebGPU implementation.
In this context, wgpu is further isolated from the GPU:
-
Depending on the browser’s WebGPU implementation, mapping and unmapping buffers probably entails copies between WebAssembly linear memory and the graphics driver’s buffers.
-
All modern web browsers isolate web content in its own sandboxed process, which can only interact with the GPU via interprocess communication (IPC). Although most browsers’ IPC systems use shared memory for large data transfers, there will still probably need to be copies into and out of the shared memory buffers.
All of these copies contribute to the cost of buffer mapping in this configuration.
Fields§
§inner: DispatchBuffer§map_context: Arc<Mutex<MapContext>>§size: BufferAddress§usage: BufferUsagesImplementations§
Source§impl Buffer
impl Buffer
Sourcepub fn as_entire_binding(&self) -> BindingResource<'_>
pub fn as_entire_binding(&self) -> BindingResource<'_>
Return the binding view of the entire buffer.
Sourcepub fn as_entire_buffer_binding(&self) -> BufferBinding<'_>
pub fn as_entire_buffer_binding(&self) -> BufferBinding<'_>
Return the binding view of the entire buffer.
Sourcepub unsafe fn as_hal<A: Api>(
&self,
) -> Option<impl Deref<Target = A::Buffer> + WasmNotSendSync>
pub unsafe fn as_hal<A: Api>( &self, ) -> Option<impl Deref<Target = A::Buffer> + WasmNotSendSync>
Get the wgpu_hal buffer from this Buffer.
Find the Api struct corresponding to the active backend in wgpu_hal::api,
and pass that struct to the to the A type parameter.
Returns a guard that dereferences to the type of the hal backend
which implements A::Buffer.
§Types
The returned type depends on the backend:
hal::api::Vulkanuseshal::vulkan::Bufferhal::api::Metaluseshal::metal::Bufferhal::api::Dx12useshal::dx12::Bufferhal::api::Glesuseshal::gles::Buffer
§Deadlocks
- The returned guard holds a read-lock on a device-local “destruction”
lock, which will cause all calls to
destroyto block until the guard is released.
§Errors
This method will return None if:
- The buffer is not from the backend specified by
A. - The buffer is from the
webgpuorcustombackend. - The buffer has had
Self::destroy()called on it.
§Safety
- The returned resource must not be destroyed unless the guard is the last reference to it and it is not in use by the GPU. The guard and handle may be dropped at any time however.
- All the safety requirements of wgpu-hal must be upheld.
Sourcepub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'_>
pub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'_>
Returns a BufferSlice referring to the portion of self’s contents
indicated by bounds. Regardless of what sort of data self stores,
bounds start and end are given in bytes.
A BufferSlice can be used to supply vertex and index data, or to map
buffer contents for access from the CPU. See the BufferSlice
documentation for details.
The range argument can be half or fully unbounded: for example,
buffer.slice(..) refers to the entire buffer, and buffer.slice(n..)
refers to the portion starting at the nth byte and extending to the
end of the buffer.
§Panics
- If
boundsis outside of the bounds ofself. - If
boundshas a length less than 1.
Sourcepub fn unmap(&self)
pub fn unmap(&self)
Unmaps the buffer from host memory.
This terminates the effect of all previous map_async() operations and
makes the buffer available for use by the GPU again.
Sourcepub fn size(&self) -> BufferAddress
pub fn size(&self) -> BufferAddress
Returns the length of the buffer allocation in bytes.
This is always equal to the size that was specified when creating the buffer.
Sourcepub fn usage(&self) -> BufferUsages
pub fn usage(&self) -> BufferUsages
Returns the allowed usages for this Buffer.
This is always equal to the usage that was specified when creating the buffer.
Sourcepub fn map_async<S: RangeBounds<BufferAddress>>(
&self,
mode: MapMode,
bounds: S,
callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static,
)
pub fn map_async<S: RangeBounds<BufferAddress>>( &self, mode: MapMode, bounds: S, callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static, )
Map the buffer to host (CPU) memory, making it available for reading or writing via
get_mapped_range(). The buffer becomes accessible once the
callback is invoked with Ok.
Use this when you want to map the buffer immediately. If you need to submit GPU work that
uses the buffer before mapping it, use map_buffer_on_submit on
CommandEncoder, CommandBuffer, RenderPass, or
ComputePass to schedule the mapping after submission. This avoids extra calls to
Buffer::map_async() or BufferSlice::map_async() and lets you initiate mapping from a
more convenient place.
For the callback to run, either queue.submit(..), instance.poll_all(..),
or device.poll(..) must be called elsewhere in the runtime, possibly integrated into
an event loop or run on a separate thread.
The callback runs on the thread that first calls one of the above functions after the GPU work completes. There are no restrictions on the code you can run in the callback; however, on native the polling call will not return until the callback finishes, so keep callbacks short (set flags, send messages, etc.).
While a buffer is mapped, it cannot be used by other commands; at any time, either the GPU or the CPU has exclusive access to the buffer’s contents.
This can also be performed using BufferSlice::map_async().
§Panics
- If the buffer is already mapped.
- If the buffer’s
BufferUsagesdo not allow the requestedMapMode. - If
boundsis outside of the bounds ofself. - If
boundsdoes not start at a multiple ofMAP_ALIGNMENT. - If
boundshas a length that is not a multiple of 4 greater than 0.
Sourcepub fn get_mapped_range<S: RangeBounds<BufferAddress>>(
&self,
bounds: S,
) -> BufferView
pub fn get_mapped_range<S: RangeBounds<BufferAddress>>( &self, bounds: S, ) -> BufferView
Gain read-only access to the bytes of a mapped Buffer.
Returns a BufferView referring to the buffer range represented by
self. See the documentation for BufferView for details.
bounds may be less than the bounds passed to Self::map_async(),
and multiple views may be obtained and used simultaneously as long as they do not overlap.
This can also be performed using BufferSlice::get_mapped_range().
§Panics
- If
boundsis outside of the bounds ofself. - If
boundsdoes not start at a multiple ofMAP_ALIGNMENT. - If
boundshas a length that is not a multiple of 4 greater than 0. - If the buffer to which
selfrefers is not currently mapped. - If you try to create a view which overlaps an existing
BufferViewMut.
Sourcepub fn get_mapped_range_mut<S: RangeBounds<BufferAddress>>(
&self,
bounds: S,
) -> BufferViewMut
pub fn get_mapped_range_mut<S: RangeBounds<BufferAddress>>( &self, bounds: S, ) -> BufferViewMut
Gain write access to the bytes of a mapped Buffer.
Returns a BufferViewMut referring to the buffer range represented by
self. See the documentation for BufferViewMut for more details.
bounds may be less than the bounds passed to Self::map_async(),
and multiple views may be obtained and used simultaneously as long as they do not overlap.
This can also be performed using BufferSlice::get_mapped_range_mut().
§Panics
- If
boundsis outside of the bounds ofself. - If
boundsdoes not start at a multiple ofMAP_ALIGNMENT. - If
boundshas a length that is not a multiple of 4 greater than 0. - If the buffer to which
selfrefers is not currently mapped. - If you try to create a view which overlaps an existing
BufferVieworBufferViewMut.
Sourcepub fn as_custom<T: BufferInterface>(&self) -> Option<&T>
pub fn as_custom<T: BufferInterface>(&self) -> Option<&T>
Returns custom implementation of Buffer (if custom backend and is internally T)
Trait Implementations§
Source§impl Ord for Buffer
impl Ord for Buffer
Source§impl PartialOrd for Buffer
impl PartialOrd for Buffer
impl Eq for Buffer
Auto Trait Implementations§
impl Freeze for Buffer
impl !RefUnwindSafe for Buffer
impl Send for Buffer
impl Sync for Buffer
impl Unpin for Buffer
impl !UnwindSafe for Buffer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.