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
impl Batch
Sourcepub const MAX_CLIP_DEPTH: u32 = 255
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.
pub fn new() -> Self
Sourcepub fn push(
&mut self,
vertices: &[[f32; 2]],
indices: &[u32],
material: Material,
blend: BlendMode,
) -> Result<()>
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.
Sourcepub fn push_clipped(
&mut self,
vertices: &[[f32; 2]],
indices: &[u32],
material: Material,
blend: BlendMode,
clip: Option<Scissor>,
) -> Result<()>
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.
Sourcepub fn push_with(
&mut self,
positions: &[[f32; 2]],
indices: &[u32],
material: Material,
filter: ColorFilter,
blend: BlendMode,
clip: Option<Scissor>,
stencil: ClipState,
) -> Result<()>
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.
Sourcepub fn push_mesh(
&mut self,
vertices: &[Vertex],
indices: &[u32],
material: Material,
filter: ColorFilter,
blend: BlendMode,
clip: Option<Scissor>,
stencil: ClipState,
) -> Result<()>
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.
Sourcepub 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<()>
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.
pub fn draw_count(&self) -> usize
pub fn is_empty(&self) -> bool
Sourcepub fn uses_stencil(&self) -> bool
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.
Sourcepub fn check_clip_depth(&self) -> Result<()>
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.
Sourcepub fn max_clip_depth(&self) -> u32
pub fn max_clip_depth(&self) -> u32
The largest stencil value this batch can produce.
Sourcepub fn texture_slots(&self) -> Vec<u32>
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.
Sourcepub fn pipeline_binds(&self) -> usize
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
impl Batch
Sourcepub fn rebase_scissors(&mut self, dx: u32, dy: u32, extent: Extent2D)
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.
pub fn vertices(&self) -> &[Vertex]
Sourcepub fn indices(&self) -> &[u32]
pub fn indices(&self) -> &[u32]
Shared index buffer, already rebased onto Batch::vertices.
Sourcepub fn cull_occluded(&mut self, extent: Extent2D) -> usize
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.