Skip to main content

Batch

Struct Batch 

Source
pub struct Batch { /* private fields */ }
Expand description

Geometry and paint for a sequence of draws sharing one target.

Draws are kept in submission order rather than sorted by pipeline. Sorting would cut pipeline binds, but 2D drawing is painter’s-algorithm ordered: reordering two overlapping draws changes which one ends up on top. Deciding when a reorder is safe needs either overlap analysis or a depth buffer, and that belongs to the layer that knows what the draws represent.

Implementations§

Source§

impl Batch

Source

pub const MAX_CLIP_DEPTH: u32 = 255

The deepest clip stack an eight-bit stencil can distinguish.

Eight bits is the only stencil depth every device is required to offer, on either graphics API, so this is the portable limit rather than any one device’s.

Source

pub fn new() -> Self

Source

pub fn push( &mut self, vertices: &[[f32; 2]], indices: &[u32], material: Material, blend: BlendMode, ) -> Result<()>

Append a draw covering the whole target.

Indices are relative to vertices and are rebased onto the batch’s shared buffer, so a caller need not know what came before it.

Source

pub fn push_clipped( &mut self, vertices: &[[f32; 2]], indices: &[u32], material: Material, blend: BlendMode, clip: Option<Scissor>, ) -> Result<()>

Append a draw confined to a region of the target.

A separate entry point rather than an extra parameter on Self::push: most draws are unclipped, and threading None through every call site makes the ones that do carry a clip harder to pick out, not easier.

An empty scissor drops the draw. Recording something that provably writes no pixel would cost a pipeline bind and a draw call to produce the same target, and a clip stack that has narrowed to nothing is a normal state for a scrolled-away subtree rather than an error.

Source

pub fn push_with( &mut self, positions: &[[f32; 2]], indices: &[u32], material: Material, filter: ColorFilter, blend: BlendMode, clip: Option<Scissor>, stencil: ClipState, ) -> Result<()>

Append a draw with an explicit stencil role.

The general form the other two delegate to. A caller reaches for this only when building or unwinding a clip, or when drawing content inside one; everything else is confined by a scissor or not confined at all.

Source

pub fn push_mesh( &mut self, vertices: &[Vertex], indices: &[u32], material: Material, filter: ColorFilter, blend: BlendMode, clip: Option<Scissor>, stencil: ClipState, ) -> Result<()>

Append a draw whose vertices carry texture coordinates.

The form a glyph run takes: one draw over many quads, each reading a different part of the same atlas.

Source

pub fn push_mesh_tinted( &mut self, vertices: &[Vertex], indices: &[u32], material: Material, filter: ColorFilter, blend: BlendMode, clip: Option<Scissor>, stencil: ClipState, tint_blend: BlendMode, paint_at_texture_coords: bool, ) -> Result<()>

Append a mesh, saying how its vertex colors combine with the material.

Separate from Self::push_mesh rather than an extra parameter on it, for the reason Self::push_clipped is separate: the mode is Modulate for everything that does not ask, white being the identity under it, and threading a parameter through every call site to say so would be noise at all of them and a decision at none.

Source

pub fn clear(&mut self)

Drop the contents but keep the allocations, for reuse next frame.

Source

pub fn draw_count(&self) -> usize

Source

pub fn is_empty(&self) -> bool

Source

pub fn uses_stencil(&self) -> bool

Whether recording this needs a stencil attachment.

Derived from the draws rather than declared alongside them, so a batch cannot ask for a clip and forget to say it needs somewhere to put it. Most batches clip nothing, and those pay for no attachment.

Source

pub fn check_clip_depth(&self) -> Result<()>

Refuse a batch whose clip stack is deeper than a stencil can hold.

Here rather than in each backend because the limit is a property of the stencil format both are required to offer, and the failure it prevents is one neither can detect afterwards: past the limit the value wraps or saturates, and either way a later test for a depth that no longer fits admits every pixel the clip was meant to exclude. Nothing about that looks like an error – it draws content the caller clipped away.

It was in one backend and not the other, so the same recording was refused on Vulkan and silently rendered wrong on GLES.

Source

pub fn max_clip_depth(&self) -> u32

The largest stencil value this batch can produce.

Source

pub fn texture_slots(&self) -> Vec<u32>

The texture slots this batch samples, in ascending order without repeats.

A backend uses this to size its bindings before recording, and to check the table it was given covers what the draws ask for.

Source

pub fn pipeline_binds(&self) -> usize

How many times a pipeline will be bound when this batch is recorded.

Consecutive draws sharing a blend mode reuse the bound pipeline, so this counts transitions rather than draws.

Source§

impl Batch

Source

pub fn rebase_scissors(&mut self, dx: u32, dy: u32, extent: Extent2D)

Shared vertex buffer, positions in clip space. Move every scissor into a target whose origin moved by (dx, dy).

For a layer whose target was narrowed after its draws were recorded. The geometry is left alone – it is in clip space and the pass’s viewport is what places it – but a scissor is in target pixels, so it is the one recorded thing the move does reach.

Source

pub fn vertices(&self) -> &[Vertex]

Source

pub fn indices(&self) -> &[u32]

Shared index buffer, already rebased onto Batch::vertices.

Source

pub fn draws(&self) -> &[BatchDraw]

The draws, in submission order.

Source

pub fn cull_occluded(&mut self, extent: Extent2D) -> usize

Stop each draw writing pixels a later opaque draw will overwrite.

Returns how many draws were narrowed or dropped, which is what a test asserts on – a pass that quietly did nothing would otherwise look like a pass.

§Why this is not reordering

Draw order is untouched. Each draw is confined, by scissor, to the pixels no later opaque draw replaces. docs/non-parity.md 21 wanted a depth buffer to reorder opaque draws and docs/on-a-board.md records why that is closed here: at four samples the attachment costs four times the pass on V3D, and the frame worth reordering is four samples. A scissor costs nothing and needs no attachment.

It is also pixel-identical rather than approximately right. For draws i before j, if j replaces every sample of a pixel then nothing i wrote there can reach the frame – including by way of something between them that blended against it, since that result is replaced too. BatchDraw::occludes is exactly the “replaces every sample it touches” predicate, and BatchDraw::covered is where it does so.

§What limits it

Only the occluder needs known coverage. The draw being narrowed needs nothing at all, because a scissor restricts any geometry – which is what makes this worth doing, since the thing being saved is usually a gradient or an image and neither is a shape this could reason about.

Two caps keep the work bounded on a batch that is nothing like a frame of interface. MAX_BLOCKERS is how many occluders are carried at once, and crate::occlusion::MAX_PIECES is how many rectangles a remainder may need before the draw is left alone. Both failures are safe: drawing more than necessary is slow, never wrong.

Trait Implementations§

Source§

impl Clone for Batch

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Batch

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Batch

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Batch

§

impl RefUnwindSafe for Batch

§

impl Send for Batch

§

impl Sync for Batch

§

impl Unpin for Batch

§

impl UnsafeUnpin for Batch

§

impl UnwindSafe for Batch

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.