simvx.graphics.scene_adapter

Adapter to bridge SceneTree nodes to Renderer submissions.

Module Contents

Classes

SceneAdapter

Bridges SceneTree to Renderer (Vulkan).

Functions

find_all_outside_subviewports

find_all that does NOT descend into SubViewport subtrees.

Data

API

simvx.graphics.scene_adapter.__all__

[‘SceneAdapter’]

simvx.graphics.scene_adapter.log

‘getLogger(…)’

simvx.graphics.scene_adapter.find_all_outside_subviewports(root: simvx.core.Node, node_type: type) list[source]

find_all that does NOT descend into SubViewport subtrees.

A SubViewport’s children render into the viewport’s own offscreen target (driven by SubViewportManager), so a main-pass collection must not also pick them up: a plain root.find_all would double-submit them (a MultiMeshInstance3D inside a SubViewport would draw in both the main pass and the offscreen pass). Mirrors the SubViewport boundary in _collect_nodes. The root itself is never skipped, so collecting a SubViewport’s own contents (root == that SubViewport) still works; nested SubViewports are pruned.

class simvx.graphics.scene_adapter.SceneAdapter(engine: simvx.graphics.engine_surface.EngineSurface, renderer: Any)[source]

Bridges SceneTree to Renderer (Vulkan).

Responsibilities:

  • Register meshes with the GPU

  • Convert materials to Vulkan format

  • Traverse scene tree and submit instances

Initialization

property offload_coordinator: Any[source]

The D8 :class:SRUOffloadCoordinator, or None (single-GPU/unopted).

Built once, lazily, from engine.multi_device: a coordinator exists only when that manager reports an active multi-device renderer. On every single-GPU box (and any box that did not opt in) this is None, so the SRU recording seam’s coordinator is None branch keeps the path byte-identical to today.

plan_sru_offload(srus: list, *, sru_id: Any = None, cost: Any = None) None[source]

Compute this frame’s per-SRU device routes (no-op without a coordinator).

Called once per frame on the ORDERED SRU list (producer-first) before the SRUs are recorded, so the per-SRU :meth:render_to_target /

Meth:

render_sru_from_plan can look up their route. Pure decision work; no Vulkan. On the single-GPU / unopted path there is no coordinator and this returns immediately, leaving every SRU on the primary (today’s path).

sru_id / cost accessors are forwarded to the coordinator for the synchronous path, whose ordered items are live SubViewport nodes rather than :class:SubViewportSRU plans.

register_mesh(mesh: Any) Any | None[source]

Register mesh with Vulkan engine, return MeshHandle.

Returns None if the mesh has no vertex data (skipped gracefully).

register_skinned_mesh(mesh: Any) Any | None[source]

Register skinned mesh with Vulkan engine, return MeshHandle.

Returns None if the mesh has no vertex data.

submit_scene(tree: simvx.core.SceneTree, *, submit_lights: bool = True, submit_2d: bool = True) None[source]

Walk scene tree and submit instances to renderer.

Performs a single tree traversal to collect all renderable node types, then passes the pre-collected lists to individual submission methods.

Gracefully handles scenes with no Camera3D (e.g. editor UI-only scenes) by skipping 3D submission while still processing 2D overlays.

submit_lights is False for offscreen scene-render units (SubViewport / reflection-probe faces): the engine keeps a single shared light SSBO, so in the single-submit multi-scene frame only one light set can survive. The main scene owns it; offscreen SRUs render with the main scene’s lights (a known limitation). Skipping the upload also keeps the main scene’s light buffer intact without a re-submit.

submit_2d is False for a RenderView SRU: it re-submits the MAIN tree (already submitted this frame), and the 2D overlay submits are per-frame APPENDS to shared renderer lists (particles, tilemap layers, 2D lights), so running them a second time would double that content in the main pass. A RenderView captures the 3D world only.

resolve_2d_textures(root: simvx.core.Node) None[source]

Resolve Sprite2D / NinePatchRect textures under root.

The item-pipeline RTT-2D collector needs a SubViewport subtree’s sprite _texture_id populated BEFORE it captures on_draw (a sprite whose texture is still -1 emits nothing, so the collected item view would be empty and stay cached empty – _texture_id is not a Property, so it fires no invalidation). The main pass resolves its own sprites in submit_scene AFTER the item collection runs, and that walk prunes SubViewports anyway – so a SubViewport’s 2D sprites are resolved here, from the viewport’s own root. Idempotent: an already-resolved sprite (_texture_id >= 0) and a live SubViewport source are both skipped.

render_to_target(cmd, target, tree: simvx.core.SceneTree, *, camera=None, screen_size: tuple[float, float] | None = None, draw2d_ops: list | None = None, item_2d: tuple | None = None, sru_id: int = 0, submit_2d: bool = True, occlusion: Any = None) None[source]

Record one offscreen scene-render unit (SRU) into the shared command buffer.

Single-submit slice model (no per-scene begin_frame / submit / wait): the offscreen scene’s instances are submitted into an isolated list, a contiguous slice of the shared transform SSBO is reserved for them, their transforms are written into that slice, and their draws are recorded with absolute first_instance indices into the same primary cmd that the main frame uses. The main scene’s per-frame submission lists are saved and restored around this call so its base-0 slice is untouched: the whole frame still ends in exactly ONE vkQueueSubmit.

The offscreen colour target is written here and sampled by the main pass later in the same command buffer; RenderTarget leaves it in SHADER_READ_ONLY_OPTIMAL (its end-of-pass store + image-layout transition is an in-cmd barrier), so the within-frame write-then-sample ordering is valid without any queue wait.

Args: cmd: The frame’s primary Vulkan command buffer (from pre_render). target: Offscreen render target with begin_pass/end_pass/width/height/ready. tree: SceneTree (or _SubTreeView) to render. camera: Optional camera override (e.g. probe face / editor orbit camera). screen_size: Optional 2D screen size override (for 2D viewport rendering). draw2d_ops: Pre-extracted Draw2D ops to render as a 2D overlay. sru_id: Stable identity of this SRU (SubViewport node id / probe-face id), used to key the frustum visibility cache so SRUs never collide. submit_2d: Forwarded to :meth:submit_scene. False for a RenderView SRU, whose re-submit of the already-submitted MAIN tree must not append the tree’s 2D overlays into the shared per-frame lists a second time (they would double in the main pass). occlusion: Optional per-view :class:~.renderer.view_occlusion.ViewOcclusion bundle (use_occlusion=True on the node). When given, the SRU’s transforms upload their AABBs and the view’s own two-phase Hi-Z cull is recorded before its colour pass, so the SRU render draws from the culled indirect batches. None (the default) records exactly the historical un-culled SRU.

render_sru_from_plan(cmd, sru) None[source]

Record one SubViewport SRU from an OWNED packet plan (pipelined render thread).

The render-thread counterpart of :meth:render_to_target: instead of walking the live tree (illegal on the render thread, the main thread owns it) it replays a :class:~.renderer.render_packet.SubViewportSRU snapshot captured on the main thread by :meth:SubViewportManager.build_srus.

It isolates the renderer’s main-scene per-frame instance lists + viewport (already installed from the packet for the main pass), binds the SRU’s OWNED instances + single camera viewport, reserves a slice of the shared transform SSBO, writes those transforms, records render_scene_content into the SRU’s offscreen target with absolute first_instance indices, overlays the SRU’s isolated Draw2D ops, and restores the main-scene state. The whole frame still ends in one vkQueueSubmit; the offscreen colour write is made visible to the main pass’s later sample by the render target’s end-of-pass layout transition (same as render_to_target).

snapshot_sru(tree: simvx.core.SceneTree, *, camera=None, screen_size: tuple[float, float] | None = None) tuple[list, list, Any, Any, list, list][source]

Submit an offscreen SRU’s scene into isolated lists and return OWNED copies.

The CPU-only, no-GPU counterpart of :meth:render_to_target: it sets the same tree overrides (viewport rect / camera / screen size), runs submit_scene into temporarily-isolated per-frame lists, then copies the resulting opaque / skinned instances and the SRU camera’s view + projection matrices, and restores the renderer + tree state. No slice reservation, no transform upload, no draw recording happens here: that is the render thread’s job from the returned plan. Used by

Meth:

SubViewportManager.build_srus to packetise SubViewports.

Returns (instances, skinned_instances, camera_view, camera_proj, multimesh_blocks, billboard_submissions) where the instance lists, MultiMesh blocks, and billboard rows own copies of their transform / joint arrays and the matrices are owned copies (or None for a 2D-only SRU with no 3D camera viewport).