simvx.core._drawable2d¶
The two dirty bits are deliberately SEPARATE (design §2.7, P0 gate 3b)¶
- attr:
_render_dirty– “this drawable’s geometry/appearance changed; its item(s) must be re-collected/re-uploaded.” Its lifetime is owned by the render layer: it persists until the upload step clears it (:meth:_clear_render_dirty), and is NEVER cleared by aworld_positionread.
- attr:
_transform_render_dirty– “this drawable moved; its transform row must be rewritten (geometry untouched – the scroll-by-translation fast path).” Also render-owned, also persists until the upload step clears it.
Neither is Node2D._transform_dirty: that flag is cleared lazily by any
world_position/world_transform read (scene-adapter / collision / audio /
culling all read it mid-frame) and _invalidate_transform short-circuits its
descendant recursion if not self._transform_dirty. A render-dirty bit layered
on it would inherit that early-return and leave descendants stale after a
read-then-move sequence (the exact P0 gate 3b adversarial case). So the
transform-render bit gets its OWN propagation with no such short-circuit.
Retention bits¶
These render-retention bits are read by the 2D item pipeline’s
- class:
RenderItemCache(P2) to decide what to re-collect, re-capture, or patch in place each frame.
The shared 2D-drawable concept (design §7.6 / §2.7, P2 retention keystone).
P0 gate 3 proved the blanket Property.__set__ -> queue_redraw hook
(descriptors.py) is silently a no-op for every Node2D today: only
Control defined queue_redraw, so a Sprite2D colour / texture / visible
change marked nothing dirty. The build-once retention design (§2.7) rides that
same blanket hook to set a per-item dirty bit, so item-level invalidation for
sprites is dead until the hook actually fires on a Node2D.
This module resolves the §7.6 “shared drawable base-class vs mixin vs hoist”
question as a small mixin (:class:Drawable2D) carrying the render-retention
state + queue_redraw. The mixin (not “hoist onto Node2D”) is chosen
because the drawable concept must also cover CanvasLayer, which is a Node,
not a Node2D (design §5.6 / §7.6: “a plain hoist onto Node2D would miss
it”). A mixin lets Node2D and CanvasLayer share one implementation
without forcing CanvasLayer through Node2D’s 2D transform cache. Control
inherits it via Node2D and extends queue_redraw additively (its legacy
_DrawRecorder path keeps working).
Module Contents¶
Classes¶
Mixin: render-retention dirty state + |
API¶
- class simvx.core._drawable2d.Drawable2D[source]¶
Mixin: render-retention dirty state +
queue_redrawfor 2D drawables.Mixed into :class:
~simvx.core.nodes_2d.node2d.Node2D(so every sprite / shape / Text2D / Control inherits it) and- Class:
~simvx.core.nodes_2d.canvas.CanvasLayer(aNode, covered by the mixin rather than aNode2Dhoist – design §7.6).
The mixin owns no transform: it never reads
position/world_transform. It only flips render-retention flags. The item pipeline reads the flags + drains them after upload; the legacy path ignores them.- hdr¶
‘Property(…)’
- queue_redraw() None[source]¶
Mark this drawable’s item(s) dirty (re-collect / re-upload next frame).
The blanket
Property.__set__hook (descriptors.py) calls this on any changed Property when the owner defines it – which is now everyNode2D(colour, text, font_scale, visible, texture refs, size/anchor/ margin on Controls, …). It is also the manual escape hatch for anon_drawbody that reads non-Property state (thedynamiccases).Idempotent and cheap: a no-op once already dirty.