simvx.graphics.render2d.submit¶
What it does (design §2.6 / §3 Decision D / §10 P1 row)¶
Order. Read the published draw order (
view.order: physical row indices in back-to-front(layer, seq)order) – the global sort already ran on the game thread; nothing re-sorts here.Resolve + transform. For each item in order, resolve its captured local geometry (verts/indices) and emit an op tuple. The captured geometry is world-space, camera-free (the op-adapter bridge runs each node’s
on_drawwith an identityDraw2Dtransform, and nodes bake their own world position into the coordinates they pass – exactly as the legacy_draw_selfdoes, which never pushes the node transform either). So the per-itemtransformcolumn is redundant for the bridge geometry and is NOT re-applied here (re-applying it would double the world transform). The camera affine – the only thing the legacy_xfever carries during the tree walk (scene_tree.renderpushes(zoom,0,0,zoom, pan_x, pan_y)) – is applied uniformly to every world-content item’s verts, exactly mirroring the legacy bake. P3a’s native per-node emission makes the verts truly local and lets the transform column drive the GPU instead; until then this is the render-target-agnostic, behaviour-preserving submit.Adjacent batch + draw. Hand the ordered ops to the existing
- meth:
Draw2DPass.render(design §3 Decision D: “adjacent batcher first; bindless is P3b”). That reuses the legacy coalescer (consecutive items sharing(pipeline, clip, blend, texture)collapse into one GPU draw), the existing per-blend FILL/TEX pipelines, the LINE/TEXT pipelines, and the host-visible vertex/index buffers with their fence discipline – so a sprite/shape scene is byte-comparable with the legacy path and the draw-call count matches.
Version-keyed upload (the clean-frame fast path, design §4)¶
A :class:PublishedItemView carries a monotonic version that the publisher
only bumps on a dirty frame (a clean frame republishes the SAME view object).
- class:
ItemSubmittercaches the ops it built last frame keyed by(version, camera, screen); if the next frame’s view has the same version and the same camera/screen, the cached ops are reused verbatim – zero rebuild, zero re-resolve – and only the unchanged GPU buffers are re-uploaded by the reusedDraw2DPassmachinery (which itself skips work when the op list is identical in shape). This is the §4 “clean frame uploads nothing” contract realised at the submit boundary: a static scene with a still camera does no per-item CPU work.
Camera is not baked into the published verts (those stay camera-free, so a later camera pan rebuilds one affine, not N item rows – Decision B); it is applied here at submit, in the per-frame mechanism the legacy path uses.
Text (GLYPH) renders natively as of P3a: the op-adapter bridge runs the one 2D
text layout (kerned MSDF quads) for draw_text, so GLYPH items carry real
indexed glyph geometry and draw through the existing (non-bindless) TEXT pipeline
– the placeholder is gone. A SCREEN_SPACE-flagged GLYPH item (a screen-
pinned Text2D) skips the camera affine. Bindless co-batching of glyph runs with
sprites is P3b.
Desktop GPU submit for a :class:PublishedItemView (design §2.6, P1.6).
The submit path of the build-once 2D pipeline: it consumes a frozen, render-thread-readable
- class:
~simvx.graphics.render2d.publish.PublishedItemViewand draws it through the 2D Vulkan pipelines. :class:ItemSubmitterreuses theDraw2DPassadjacent coalescer (for SubViewport targets); :class:BindlessItemSubmitteris the co-batched main-framebuffer path (P3b).
Module Contents¶
Classes¶
Render-thread-owned submit of a :class: |
|
Render-thread-owned bindless co-batched submit of a published view (design §3 D). |
Functions¶
Build the ordered legacy- |
|
Return the active Camera2D’s submit affine for |
|
Return whether an item belongs to the HDR (world) lane (N1, design §13 N1). |
|
Build the co-batched geometry + batch list for a published view (design §3 D). |
Data¶
API¶
- simvx.graphics.render2d.submit.__all__¶
[‘ItemSubmitter’, ‘BindlessItemSubmitter’, ‘CameraAffine’, ‘build_item_ops’, ‘build_bindless_geometr…
- simvx.graphics.render2d.submit.CameraAffine¶
None
- simvx.graphics.render2d.submit.build_item_ops(view: simvx.graphics.render2d.publish.PublishedItemView, *, camera: simvx.graphics.render2d.submit.CameraAffine = _IDENTITY_CAMERA) list[simvx.graphics.draw2d_ops.Op][source]¶
Build the ordered legacy-
Oplist for a published view (design §2.6).Walks
view.order(the published back-to-front draw order), resolves each item’s captured local geometry, applies the camera affine to the vertex positions, and emits one :class:Opper item with its scissor (read straight off the published clip-scope table), blend mode, and texture slot. The resulting list is exactly the shape the legacyDraw2DPassadjacent coalescer consumes, so the same draws result.GLYPH (text) items now carry real kerned MSDF glyph geometry (P3a native emission) and render through the existing TEXT pipeline (the bindless co-batch is P3b). A
SCREEN_SPACE-flagged item (a screen-pinned Text2D) is exempt from the camera affine, mirroring the deleted overlay’s camera-free text.
- class simvx.graphics.render2d.submit.ItemSubmitter(draw2d_pass: Any)[source]¶
Render-thread-owned submit of a :class:
PublishedItemView(design §2.6, §4).Holds the version-keyed op cache and delegates the GPU work to a
- Class:
~simvx.graphics.renderer.draw2d_pass.Draw2DPass(its pipelines, buffers, and adjacent coalescer). One submitter per draw target (the main framebuffer; a SubViewport gets its own, mirroring how SRUs snapshot a per-target view).
The submitter is the seam where the published, immutable item columns meet the existing 2D GPU machinery: it never touches the live game-thread store (only the frozen view), and it caches the built ops by the published
version(plus the camera + screen the ops were built under) so a clean frame – same view object, same camera – does zero per-item CPU work (the §4 “clean frame uploads nothing” fast path at the submit boundary).Initialization
- __slots__¶
(‘_draw2d_pass’, ‘_cache_key’, ‘_cached_ops’, ‘_build_count’, ‘_reuse_count’)
- render(cmd: Any, view: simvx.graphics.render2d.publish.PublishedItemView, width: int, height: int, *, ui_width: int = 0, ui_height: int = 0, camera: simvx.graphics.render2d.submit.CameraAffine = _IDENTITY_CAMERA) None[source]¶
Submit
viewtocmdthrough the 2D pipelines (design §2.6).Builds (or reuses) the ordered op list for the view + camera, then hands it to :meth:
Draw2DPass.render, which coalesces adjacent ops and records the draws with its existing per-blend pipelines, host-visible buffers, and scissor discipline.width/heightare the framebuffer extent;ui_width/ui_heightthe UI coordinate space (HiDPI), exactly as the legacy pass takes them.
- simvx.graphics.render2d.submit.camera_affine_from_tree(tree: Any) simvx.graphics.render2d.submit.CameraAffine[source]¶
Return the active Camera2D’s submit affine for
tree(design §2.3 / §7.3).Delegates to the one Camera2D mapping, :meth:
Camera2D.canvas_transform, whichSceneTree.renderbakes andworld_to_screeninverts – so the item-pipeline view, the legacy bake, and hit-testing are provably the same matrix. With no active Camera2D, identity (the legacy walk pushes nothing).
- simvx.graphics.render2d.submit.item_in_hdr_lane(item_flags: int) bool[source]¶
Return whether an item belongs to the HDR (world) lane (N1, design §13 N1).
By role: a world-space item (
SCREEN_SPACEclear) is HDR-eligible; a screen-space item (HUD/UI) is not. The per-nodehdroverride forces it either way:HDR_OPT_IN-> always HDR,HDR_OPT_OUT-> always LDR. The override wins over the screen-space role; opt-in wins over opt-out (a node cannot meaningfully request both).
- simvx.graphics.render2d.submit.build_bindless_geometry(view: simvx.graphics.render2d.publish.PublishedItemView, *, camera: simvx.graphics.render2d.submit.CameraAffine = _IDENTITY_CAMERA, atlas_slot: int = -1, lane: str = _LANE_ALL, only_band: int | None = None, exclude_bands: frozenset[int] | None = None, min_band: int | None = None, max_band: int | None = None) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, list][source]¶
Build the co-batched geometry + batch list for a published view (design §3 D).
Returns
(tri_verts, tri_indices, line_verts, batches)wheretri_verts/line_vertsare :data:UI2D_VERTEX_DTYPEarrays (40-byte vertices with per-vertextex_id+flags, camera already applied),tri_indicesauint32index stream, andbatchesa list of- Class:
~simvx.graphics.renderer.bindless_draw2d_pass.BindlessBatchruns in draw order.
Grouping rule (the co-batch): consecutive items sharing
(topology, clip_scope, blend)merge into one batch even when their textures differ and even when sprite and glyph alternate. A LINE-topology item breaks the run (different pipeline) and emits a line batch.atlas_slotis the MSDF atlas’s bindless slot; an IS_MSDF item’s per-vertextex_idis set to it (its captured geometry already carries atlas UVs).Band filter (per-CanvasLayer post, design §5.6):
only_bandkeeps ONLY items whoselayercolumn equals it (a single post-processed CanvasLayer band);exclude_bandsdrops items in any of those bands (the global swapchain path skips post-processed bands, which are composited by their own chain);min_band/max_band(inclusive) keep only items in a band interval (the global path draws each plain-band SEGMENT between two post bands so the composites interleave at the right z-slot). Like the lane filter, banding only ever SHORTENS a co-batch run – it never reorders – so painter order within the kept set is intact. All defaultNone(no band filter) so the global path is byte-identical when the feature is unused.
- class simvx.graphics.render2d.submit.BindlessItemSubmitter(bindless_pass: Any)[source]¶
Render-thread-owned bindless co-batched submit of a published view (design §3 D).
The P3b counterpart of :class:
ItemSubmitter: it builds the co-batched geometry (one draw per(topology, clip, blend)run, across textures and sprite/glyph) and hands it to a- Class:
~simvx.graphics.renderer.bindless_draw2d_pass.BindlessDraw2DPass. The MSDF atlas is registered into the bindless array by the pass; the submitter asks the pass for the slot each frame (it is stable, refreshed only when the atlas re-uploads).
Version-keyed reuse: a clean frame republishes the same view object (same version + camera + atlas slot) so the built geometry is reused verbatim (zero rebuild) – the §4 “clean frame uploads nothing” fast path at the bindless submit boundary.
Initialization
- __slots__¶
(‘_pass’, ‘_cache_key’, ‘_cached’, ‘_build_count’, ‘_reuse_count’)
- render(cmd: Any, view: simvx.graphics.render2d.publish.PublishedItemView, width: int, height: int, *, ui_width: int = 0, ui_height: int = 0, camera: simvx.graphics.render2d.submit.CameraAffine = _IDENTITY_CAMERA, lane: str = _LANE_ALL, only_band: int | None = None, exclude_bands: frozenset[int] | None = None, min_band: int | None = None, max_band: int | None = None) None[source]¶
Submit
viewtocmdthrough the bindless co-batched pipeline.The MSDF atlas bindless slot is registered OUTSIDE the render pass (the renderer calls :meth:
BindlessDraw2DPass.sync_atlas_slotinpre_render, becauseregister_texturemust not run during command recording). Here we only READ the already-synced slot.lane(N1) selects which items to draw:"all"(post off, everything to the swapchain),"hdr"(world lane into the HDR target before tonemap), or"ldr"(screen lane onto the swapchain after tonemap). One submitter instance is bound to one render pass, so the HDR and LDR lanes use distinct submitters; the cache key carrieslaneso each reuses its own geometry.only_band/exclude_bands/min_band/max_band(per-CanvasLayer post) filter by thelayerband: the post chain submitsonly_band; the global path passesexclude_bands(or amin_band/max_bandsegment) to interleave with the composites. All defaultNone.