pub struct DataTextureSource<'a, T: Pod + Send + Sync> {
ctx: &'a RenderContext,
buffers: Vec<CpuWriteGpuReadBuffer<T>>,
active_buffer_index: usize,
}
Expand description
Utility for writing data to a dynamically sized “data textures”.
For WebGL compatibility we sometimes have to use textures instead of buffers.
We call these textures “data textures”.
This construct allows to write data directly to gpu readable memory which
then upon finishing is automatically copied into an appropriately sized
texture which receives all data written to DataTextureSource
.
Each texel in the data texture represents a single element of the type T
.
This is implemented by dynamically allocating cpu-write-gpu-read buffers from the
central super::CpuWriteGpuReadBelt
and copying all of them to the texture in the end.
Fields§
§ctx: &'a RenderContext
§buffers: Vec<CpuWriteGpuReadBuffer<T>>
Buffers that need to be transferred to the data texture in the end.
We have two options on how to deal with buffer allocation and filling:
- fill the last buffer to its maximum capacity before starting writing to the next, allow arbitrary amount of empty buffers -> Pro: makes the final gpu copies easy, we don’t have to juggle weird offsets and several copies per buffer! -> Con: may need to spread writes over several buffers
- create a new buffer whenever a write doesn’t fully fit into the active buffer, even if the last buffer has some remaining capacity, allow arbitrary amount of half-filled buffers -> Pro: All writes can go to a single buffer, which is simpler & faster. -> Con: We waste space and copying to the texture is harder
We’re going with option (1)!
This means that there might be ‘n’ full buffers followed by a single active buffer, followed by ‘m’ empty buffers.
active_buffer_index: usize
Buffer in which data is currently written.
Between all public calls:
- all buffers before are full
- all buffers after are empty (if any)
- the buffer at this index either does not exist or has remaining capacity
At the end of any operation that adds new elements, call
ensure_active_buffer_invariant_after_adding_elements
to ensure this invariant.
Implementations§
source§impl<'a, T: Pod + Send + Sync> DataTextureSource<'a, T>
impl<'a, T: Pod + Send + Sync> DataTextureSource<'a, T>
sourcepub fn new(ctx: &'a RenderContext) -> Self
pub fn new(ctx: &'a RenderContext) -> Self
Creates a new DataTextureSource
with the given RenderContext
.
This operation itself will not allocate any memory, empty DataTextureSource
are not a concern.
sourcepub fn capacity(&self) -> usize
pub fn capacity(&self) -> usize
The number of elements that can be written without allocating more memory.
sourcepub fn remaining_capacity(&self) -> usize
pub fn remaining_capacity(&self) -> usize
The number of elements that can be written without allocating more memory.
sourcefn ensure_active_buffer_invariant_after_adding_elements(&mut self)
fn ensure_active_buffer_invariant_after_adding_elements(&mut self)
Ensure invariant that the active buffer has some remaining capacity or is the next buffer that needs to be allocated.
Since elements were just added to the active buffer, this function assumes that the active_buffer_index
points to a valid buffer.
sourcepub fn reserve(
&mut self,
num_elements: usize
) -> Result<usize, CpuWriteGpuReadError>
pub fn reserve( &mut self, num_elements: usize ) -> Result<usize, CpuWriteGpuReadError>
Ensures that there’s space internally for at least num_elements
more elements.
Returns the number of elements that are currently reserved.
This value is smaller than the requested number of elements if the maximum number of
elements that can be stored is reached, see max_num_elements_per_data_texture
.
Creating new buffers is a relatively expensive operation, so it’s best to reserve gratuitously and often. Ideally, you know exactly how many elements you’re going to write and reserve accordingly at the start.
If there’s no more space, a new buffer is allocated such that:
- have a total capacity for at least as many elements as requested, clamping total size to
max_num_elements_per_data_texture
- be at least double the size of the last buffer
- keep it easy to copy to textures by always being a multiple of the maximum row size we use for data textures -> this massively simplifies the buffer->texture copy logic!
fn error_on_clamped_write( &self, num_elements_attempted_to_add: usize, num_elements_actually_added: usize ) -> Result<(), DataTextureSourceWriteError>
sourcepub fn extend_from_slice(
&mut self,
elements: &[T]
) -> Result<(), DataTextureSourceWriteError>
pub fn extend_from_slice( &mut self, elements: &[T] ) -> Result<(), DataTextureSourceWriteError>
Pushes a slice of elements into the data texture.
sourcepub fn add_n(
&mut self,
element: T,
num_elements: usize
) -> Result<(), DataTextureSourceWriteError>
pub fn add_n( &mut self, element: T, num_elements: usize ) -> Result<(), DataTextureSourceWriteError>
Fills the data texture with n instances of an element.
pub fn push(&mut self, element: T) -> Result<(), DataTextureSourceWriteError>
fn data_texture_size(&self, max_texture_dimension_2d: u32) -> Extent3d
sourcepub fn finish(
self,
texture_format: TextureFormat,
texture_label: impl Into<DebugLabel>
) -> Result<Arc<DynamicResource<GpuTextureHandle, TextureDesc, GpuTextureInternal>>, CpuWriteGpuReadError>
pub fn finish( self, texture_format: TextureFormat, texture_label: impl Into<DebugLabel> ) -> Result<Arc<DynamicResource<GpuTextureHandle, TextureDesc, GpuTextureInternal>>, CpuWriteGpuReadError>
Schedules copies of all previous writes to this DataTextureSource
to a GpuTexture
.
The format has to be uncompressed, not a depth/stencil format and have the exact same block size of the size of type T
.
The resulting GpuTexture
is ready to be bound as a data texture in a shader.
Auto Trait Implementations§
impl<'a, T> Freeze for DataTextureSource<'a, T>
impl<'a, T> !RefUnwindSafe for DataTextureSource<'a, T>
impl<'a, T> Send for DataTextureSource<'a, T>
impl<'a, T> Sync for DataTextureSource<'a, T>
impl<'a, T> Unpin for DataTextureSource<'a, T>where
T: Unpin,
impl<'a, T> !UnwindSafe for DataTextureSource<'a, T>
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
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more