simvx.graphics.render2d.item_builder¶
What the collection pass is (design §2.2, §2.3, §2.5)¶
The pass walks the tree once in the same order the live _draw_recursive
visits it (depth-first, parent-before-children, siblings partitioned into
below / self / above z-bands, CanvasLayers last by layer, the YSort
y-order policy) and, per node, emits one or more Items into an ItemList:
seq– a monotonic counter assigned in emission order, so a stable(layer, seq)sort of the produced list reproduces walk order exactly (design §2.2: the walk folds per-sibling-group z intoseq; the global sort is not a z lexsort – P0 gate 9).zis recorded as data only.layer– the CanvasLayer band (world content defaults to 0; aCanvasLayer(layer=N)shifts its whole subtree to bandN, §5.5).z–absolute_z_indexof the emitting node (data, never a sort input).clip_scope– the active :class:ClipScopeTablescope, opened/closed around clipped subtrees and the synthesised Control per-child wrap (§2.5).transform– a per-item LOCAL transform row = the node’sworld_transform, not composed with any camera (the view lives in a UBO, §2.3). Camera-free verts + a local transform row is the keystone that lets a later camera pan touch one UBO row instead of N item rows (Decision B).pipeline/blend/texture/flags/geometry– taken from the captured draw op.
The transitional op-adapter bridge (replaced in P3a)¶
Real nodes (Sprite2D, the shape primitives, Text2D’s family) still draw through
the immediate-mode Draw2D op API in their on_draw bodies. Rewriting every
node to emit Items natively is P3a work. To stay isolated and make real nodes
produce Items today, this builder runs each node’s on_draw against a
lightweight recording renderer (:class:_OpRecorder, the _DrawRecorder /
DrawLog pattern) that captures the ops a node would draw – with an
identity transform so the captured geometry is camera-free and parent-free –
then converts each captured op into one Item. The op’s kind maps to a
- class:
PipelineKind, itsblendto a :class:BlendMode, itstex_idto a texture slot, and its verts/indices are stowed in the :class:GeometryStorebehind a handle the Item references. This adapter is the seam P3a replaces with native per-node item emission; the surrounding walk/sort/clip/transform machinery stays.
Deferred (explicitly out of this increment):
P1.3 collapses the six live
_draw_recursivevariants into THIS one walk (this builder reproduces their order so the collapse is a drop-in); today the live walkers are untouched.P1.4 adds dirty/retention on top of :class:
GeometryStore(per-item reuse, frame-level skip). Here the store is a plain handle list rebuilt each call.
The collection seam: walk a Node tree and emit render Items (design §2.2).
The collection pass of the build-once 2D pipeline (design P1.2): it walks a scene
tree and produces an :class:~simvx.graphics.render2d.item_list.ItemList. The
- class:
RenderItemCachedrives it each frame on the live render path.
Module Contents¶
Classes¶
Captured vert/index arrays an Item’s |
|
A handle-indexed store of captured :class: |
|
The retained per-node record an incremental patch (P2) locates items by. |
|
The product of one :meth: |
|
Walks a Node tree and emits render Items into an :class: |
Functions¶
Return |
|
Resolve a node’s |
|
Collect |
Data¶
API¶
- simvx.graphics.render2d.item_builder.__all__¶
[‘CollectResult’, ‘Geometry’, ‘GeometryStore’, ‘ItemBuilder’, ‘NodeEntry’, ‘affine_row’, ‘build_item…
- simvx.graphics.render2d.item_builder.affine_row(node: Any) simvx.graphics.render2d.item_builder._AffineRow[source]¶
Return
node’sworld_transformas a camera-free LOCAL affine row.The compact
(a, b, c, d, tx, ty)row draw2d’s_xfuses (x' = a*x + b*y + tx). Camera-free by construction:world_transformcomposes only the node’s own + ancestor 2D transforms, never a Camera2D (the view lives in a per-frame UBO, design §2.3), so a camera pan rewrites one UBO row, not N item rows (Decision B). Nodes without a 2D transform (plainNode,Control,CanvasLayer) record identity. Shared by the collection walk and the P2 transform-only patch so both produce identical rows.
- class simvx.graphics.render2d.item_builder.Geometry[source]¶
Bases:
typing.NamedTupleCaptured vert/index arrays an Item’s
geometryhandle resolves to.The unit of the retained geometry store.
vertsare 8-float tuples(x, y, u, v, r, g, b, a)in the node’s LOCAL space (camera-free, parent-free);indicesisNonefor line lists. P1.4 layers dirty/reuse on top, keying handles byitem_idso a clean item keeps its slice; here every handle is freshly appended.- verts: list[tuple]¶
None
- indices: list[int] | None¶
None
- class simvx.graphics.render2d.item_builder.GeometryStore[source]¶
A handle-indexed store of captured :class:
Geometry(design §2.1).The seed of the retained geometry store. Today it is a plain growable list:
- Meth:
addappends and returns the handle an Item records in itsgeometrycolumn; :meth:getresolves it. P1.4 adds dirty tracking, item_id keying and arena slices on top of this surface.
Initialization
- __slots__¶
(‘_geoms’,)
- get(handle: int) simvx.graphics.render2d.item_builder.Geometry[source]¶
- set(handle: int, verts: list[tuple], indices: list[int] | None) None[source]¶
Overwrite the geometry behind an existing handle (P2 item-level patch).
Lets a render-dirty node re-capture its
on_drawand replace its geometry slice in place, keeping the handle stable so the Item column and every consumer keying by handle stay valid (design §2.7 item granularity).
- class simvx.graphics.render2d.item_builder.NodeEntry[source]¶
Bases:
typing.NamedTupleThe retained per-node record an incremental patch (P2) locates items by.
Built during collection so a later per-item / transform-only update finds the exact rows + transform slot + geometry handles a single node produced, without a full re-walk (design §2.7 item / transform-only granularity).
Attributes
transform_id The node’s row index in :attr:
CollectResult.transforms(its LOCAL affine). A transform-only change rewrites just this row. rows The physical :class:ItemListrow indices the node’s ops produced. geometry The geometry handles (parallel torows) the node’s ops produced. A render-dirty change re-captures the node’son_drawand overwrites these in place. canvas_affine The enclosing CanvasLayer affine baked into this node’s captured geometry (Nonefor world content). A P2 in-place re-capture re-bakes with this so the canvas offset/rotation/scale_val survives an item-level patch (design §5.5, P4).- transform_id: int¶
None
- rows: list[int]¶
None
- geometry: list[int]¶
None
- canvas_affine: simvx.graphics.render2d.item_builder._AffineRow | None¶
None
- class simvx.graphics.render2d.item_builder.CollectResult[source]¶
Bases:
typing.NamedTupleThe product of one :meth:
ItemBuilder.collect(design §2.2).Bundles the three artefacts the later sort/batch/submit consume: the SoA
- Class:
ItemList, the :class:ClipScopeTableitsclip_scopeindices point into, the :class:GeometryStoreitsgeometryhandles resolve against, and the per-item LOCAL transform rows itstransformindices select.node_indexmaps each emitting node (byid) to its- Class:
NodeEntry, the substrate for P2’s in-place per-item / transform-only patching (design §2.7).
- items: simvx.graphics.render2d.item_list.ItemList¶
None
- geometry: simvx.graphics.render2d.item_builder.GeometryStore¶
None
- transforms: list[simvx.graphics.render2d.item_builder._AffineRow]¶
None
- node_index: dict[int, simvx.graphics.render2d.item_builder.NodeEntry]¶
None
- class simvx.graphics.render2d.item_builder.ItemBuilder[source]¶
Walks a Node tree and emits render Items into an :class:
ItemList.Reproduces the live
_draw_recursivevisit order (so a stable(layer, seq)sort of the result equals walk order, design §2.2) while keeping geometry camera-free and recording per-item LOCAL transforms + nested clip scopes. See the module docstring for the op-adapter bridge.One builder is single-use per :meth:
collect; construct a fresh one (or just call :func:build_item_list) per collection.Initialization
- __slots__¶
(‘_items’, ‘_clips’, ‘_geom’, ‘_transforms’, ‘_seq’, ‘_node_index’, ‘_canvas_affine’, ‘_screen_space…
- collect(root: Any, *, layer: int = 0) simvx.graphics.render2d.item_builder.CollectResult[source]¶
Walk
rootand return the produced :class:CollectResult.layeris the starting CanvasLayer band (0 = world content). ACanvasLayerchild shifts its subtree’s band; nested CanvasLayers are absolute (the layer does not add), matching §5.5.
- simvx.graphics.render2d.item_builder.hdr_flag(node: Any) simvx.graphics.render2d.item_list.ItemFlags[source]¶
Resolve a node’s
hdroverride to its HDR-lane flag (N1, 2D-in-HDR).hdr=True-> :attr:ItemFlags.HDR_OPT_IN(force the HDR lane),hdr=False-> :attr:ItemFlags.HDR_OPT_OUT(force the LDR lane),None/absent ->- Attr:
ItemFlags.NONE(by role: world->HDR, screen->LDR). Read per node and applied to all its items; the submit lane logic (submit.item_in_hdr_lane) combines this with the screen-space role. Resolved fresh in both the build walk and the in-place re-capture patch, so togglinghdrtakes effect next frame without a full re-collect.
- simvx.graphics.render2d.item_builder.build_item_list(root: Any, *, layer: int = 0) simvx.graphics.render2d.item_builder.CollectResult[source]¶
Collect
rootinto a :class:CollectResult(one-shot convenience).Equivalent to
ItemBuilder().collect(root, layer=layer). This is the on-demand entry point for tests and the (default-OFF) flagged path; it does not touch the live render path.