Skip to main content

BatchDraw

Struct BatchDraw 

Source
pub struct BatchDraw {
    pub first_index: u32,
    pub index_count: u32,
    pub material: Material,
    pub filter: ColorFilter,
    pub blend: BlendMode,
    pub clip: Option<Scissor>,
    pub stencil: ClipState,
    pub tint_blend: BlendMode,
    pub paint_at_texture_coords: bool,
}
Expand description

One draw within a batch.

Fields§

§first_index: u32§index_count: u32§material: Material§filter: ColorFilter

A function applied to the material’s color before the blend.

Beside the material rather than inside it, for the same reason the blend mode is: it applies to every kind of material equally and belongs to none of them. It is packed into the same uniform the material is, because the shader reads one block per draw.

§blend: BlendMode§clip: Option<Scissor>

The region of the target this draw may write to.

None is the whole target. It is distinct from a rectangle that happens to cover the target so a backend can tell “this draw was never clipped” from “this draw’s clip works out to everything”, and skip the state change in the first case without having to know the target’s size.

Independent of Self::stencil, and both apply. An axis-aligned clip stays here even where a stencil is already in play, because a scissor is exact and costs nothing while a stencil pass costs a draw.

§stencil: ClipState

What this draw does with the stencil buffer.

§tint_blend: BlendMode

How a color the caller attached to a vertex or a sprite combines with what the material produced.

BlendMode::Modulate multiplies them, which is what every draw did before this existed and is what a paint with no per-vertex color wants: white is the identity under it. Distinct from Self::blend, which is how the result then reaches the target – these two colors are both in the shader, so this one needs no extension and every mode is available.

§paint_at_texture_coords: bool

Read the paint at this draw’s texture coordinates rather than at the position of the fragment.

A property of the geometry rather than of the material, which is why it is here: a mesh that states a coordinate per vertex has said where each one sits in the paint’s space, and there is nothing left to derive. An image already worked this way and had its own material for it; this is what lets a gradient or a caller’s program do the same.

False everywhere else, and it costs those draws nothing: the flag lands in a slot no material that could set it uses, and the shader’s select is one instruction on a value it has already computed.

Implementations§

Source§

impl BatchDraw

Source

pub fn covered( &self, vertices: &[Vertex], indices: &[u32], extent: Extent2D, ) -> Option<Scissor>

Whether this draw may be moved ahead of earlier draws it covers.

A draw that answers yes replaces every sample it touches, so nothing underneath it can show through and the painter’s-order guarantee this batch otherwise relies on does not apply to it. That is what makes an opaque reordering possible: docs/non-parity.md 21 has the measurement, and on both boards here the covered part of a frame’s background is around forty per cent of the frame.

Conservative on purpose, and every condition below is load-bearing. A wrong yes is not a slow frame, it is a wrong picture – a background showing through where it should not, or showing when it should not – so each test is for a property that can be read off the draw rather than reasoned about, and anything this cannot prove answers no.

  • Material::Solid with an opaque alpha, and nothing else. A solid fill takes its coverage from the rasterizer, so a sample is either inside the geometry or outside it and there is no partial result. The analytic materials are the case this exists to exclude: RoundedRect, Ellipse and RoundedRectBlur compute coverage in the shader and blend it, so an opaque color still leaves a soft edge, and writing depth there would hide the background behind a half-covered pixel. Gradients and images could be opaque and are refused anyway: proving it means reading every stop or every texel.
  • Alpha at or above one. Premultiplied and straight color agree there, so the form the material carries does not have to be known.
  • Src or SrcOver. Both put an opaque source through unchanged. Every other mode reads the destination, which is the thing being reordered away.
  • No color filter. A matrix or a blend filter can take alpha below one after the material produced it.
  • Modulate tinting. It is the identity against the white a solid fill carries; another mode is a second color this cannot see.
  • Every vertex carrying white. A vertex color multiplies the material, so a translucent one makes a translucent draw out of an opaque material – and Material::Solid is exactly the pairing draw_vertices produces for a caller’s mesh. This is why the vertex buffer is a parameter: the material cannot answer it, and the draw does not hold it. Refused for any non-white color rather than only a translucent one, since a colored-but-opaque vertex still has to be read to know that, and it costs nothing to say no.
  • Unclipped. A Narrow or Widen draw writes the stencil rather than color and is sequencing, not content. A clipped Content draw writes color but depends on stencil state that the draws around it establish, so moving it past them would change what it is clipped to.

Antialiasing does not appear here, and that is the point rather than an omission. It is multisampling in this renderer – Canvas::pass_samples raises the whole pass’s sample count and no draw blends its own coverage – so an opaque solid fill is binary at every sample whether the pass is multisampled or not, which is exactly the case a depth test is built for. A renderer that antialiased by blending coverage could not use this predicate at all. The whole pixels this draw certainly covers, where that is knowable exactly.

None unless the geometry is a quad standing on its own bounding box: four vertices at the four corners, six indices forming two triangles that share the quad’s diagonal. That is what an axis-aligned rectangle fill tessellates to, and it is the one shape whose covered area is its bounding box rather than something strictly inside it. Everything else – a rotated rectangle, a path, a stroke, a glyph run – is refused rather than approximated, because the answer is used to stop drawing something underneath and a rectangle too large leaves a hole in the frame.

Every test here is discrete, with no tolerance anywhere. A quad one part in ten thousand short of its bounding box would pass an area comparison and leave a sub-pixel notch, and at four samples a notch is a visible seam. Exact corners or nothing.

Two triangles sharing a side rather than the diagonal are refused too. They have six indices over four vertices and cover half the box, so nothing short of looking at which pair is shared tells them apart.

The positions are homogeneous clip coordinates, so this converts. w must be exactly one on all four vertices, which refuses perspective rather than dividing by a quantity that varies across the quad, and the normalized range maps onto the target with y running downward – the orientation Scissor fixes and that both backends already agree on. Scissor::covered_device_bounds then rounds inward.

Source

pub fn splits_safely(&self, vertices: &[Vertex], indices: &[u32]) -> bool

Whether writing this draw’s pixels twice gives what writing them once gives.

The condition for splitting a draw into several, and it is not the same question as Self::occludes. That one asks whether a draw hides what is under it; this asks whether it is safe to draw overlapping copies of it – which matters because a driver may write a pixel outside the scissor it was given.

Measured, not hypothetical. lavapipe on Mesa 25.2.8 and 15.0.6 writes the pixel to the left of a scissor at half coverage when the pass is multisampled; 26.1.7, RADV and PanVK are clean. public_api.rs probes for it. Where it happens, two pieces of one draw overlap by a column – and a translucent draw blends there twice, which reads 90 against 121 on a half-transparent wash under an opaque bar. An opaque one writes the same color twice and cannot tell.

So a draw splits only where a second write is a no-op: Src replaces whatever the alpha, and SrcOver replaces only where the source is opaque – which means the material, the absence of a color filter, an identity tint, and the vertex colors the tint multiplies in, all four.

Source

pub fn occludes(&self, vertices: &[Vertex], indices: &[u32]) -> bool

Source

pub fn to_uniform(&self, target: PixelFormat) -> [f32; 64]

The uniform block this draw’s shader reads.

The material and the filter are packed together because the shader takes one block per draw, and separately here because they are separate things: a filter applies to any material, and a material knows nothing about being filtered. target is the format this draw is about to be written into, which only the backend knows: a recording is built without one, and the same recording is drawn into an eight-bit surface and a float one. It decides the dither, and nothing else here.

Trait Implementations§

Source§

impl Clone for BatchDraw

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 BatchDraw

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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.