Source code for simvx.graphics.draw2d_ops
"""Ordered op record for Draw2D.
Every Draw2D submission (fill, line, text, textured quad) appends a single
``Op`` to ``Draw2D._ops``. Draw2DPass walks that list in order and coalesces
adjacent same-(kind, clip, tex_id) ops into one GPU draw, so submission order
is the GPU order. This is the canonical 2D ordering contract; there is no
other mechanism.
Verts are stored as raw 8-float tuples ``(x, y, u, v, r, g, b, a)`` and
indices as raw ``int`` lists; the pass concatenates per-run and builds the
structured numpy buffer once at upload time. Carrying numpy arrays on every
op was hot in the bullet-hell HUD workload (1000+ rects/frame).
Each op carries a ``blend`` mode (``"alpha"`` default, ``"add"``, or
``"multiply"``); the pass splits coalesced runs on a blend change exactly as
it splits on a pipeline-kind change, so FILL/TEX runs pick the matching
blend-state pipeline. LINE and TEXT are always alpha-blended.
The reserved ``material_id`` field is not used today: it marks where per-call
ShaderMaterial extensions land without re-shaping callers.
``post_layer_id`` tags an op with the band of a per-post CanvasLayer
(``CanvasLayer.environment`` set) it was emitted under, mirroring the desktop
item pipeline's ``layer`` column for the WEB backend (which renders via ``Op``,
not the item pipeline). It defaults to ``-1`` (no post layer): the desktop path
ignores the field entirely (it bands via the item ``layer`` column), and the web
serializer only peels tagged ops when at least one carries a ``>= 0`` value, so
the field is purely additive and the unused wire stays byte-identical.
``item_id`` carries the retained item pipeline's stable cross-frame identity
(``id(node) ^ (seq << 1)``, the :class:`ItemList` ``item_id`` column) for the WEB
per-sub-batch delta wire (P4b): the serializer folds the item_ids of the ops a
sub-batch co-batches into a stable wire id and ships only the geometry of
sub-batches whose items changed this frame. It defaults to ``-1`` (no retained
identity): the immediate producer and the desktop path leave it unset, so the
serializer falls back to a per-frame ordinal id (a full keyframe) and the unused
wire is delta-free.
"""
from enum import IntEnum
from typing import NamedTuple
__all__ = ["OpKind", "Op"]
[docs]
class OpKind(IntEnum):
FILL = 0 # fill pipeline, indexed triangles
LINE = 1 # line pipeline, non-indexed line list
TEXT = 2 # MSDF text pipeline, indexed triangles
TEX = 3 # textured-quad pipeline, indexed triangles, bindless tex_id
BLEND_MODES = ("alpha", "add", "multiply")
[docs]
class Op(NamedTuple):
kind: OpKind
clip: tuple[int, int, int, int] | None
verts: list[tuple] # 8-float tuples: (x, y, u, v, r, g, b, a)
indices: list[int] | None # None for LINE (non-indexed)
tex_id: int # -1 for non-TEX; bindless slot for TEX
blend: str = "alpha" # "alpha" | "add" | "multiply" (FILL/TEX only)
screen_space: bool = False # True = HUD/UI lane (LDR, post-tonemap); False = world lane
post_layer_id: int = -1 # >=0 = per-post CanvasLayer band (web); -1 = none (desktop ignores)
item_id: int = -1 # retained stable item identity (web P4b delta); -1 = none