simvx.graphics.renderer.sub_viewport¶
SubViewport render-to-texture: offscreen targets for core.SubViewport nodes.
A :class:~simvx.core.SubViewport renders its own subtree (with its own
camera) into an offscreen texture that other nodes in the main scene can
sample: a “live monitor in the world”, a security-camera feed, a minimap, a
render-to-texture portal, etc.
Architecture mirrors :class:~simvx.graphics.renderer.game_viewport.GameViewportRenderer
(the proven editor game-preview path): each SubViewport owns a
- class:
RenderTarget, registered once as a bindless texture whose slot id stays stable across resizes so consumers that capturedsubviewport.texturekeep working. :class:SubViewportManagerkeys one renderer per SubViewport byid(node), creates it on first sight, debounce-resizes on size changes, and unregisters on exit-tree / teardown.
Frame ordering: all SubViewports are recorded before the main scene pass, in
- meth:
SubViewportManager.render_all(driven from the enginepre_rendercallback) into the same primary command buffer the whole frame uses. Each is its own scene-render unit (SRU): it reserves a slice of the shared transform SSBO and records its draws with absolutefirst_instanceindices, so a singlevkQueueSubmitcovers every SubViewport plus the main scene with no per-viewportvkQueueWaitIdle. Each offscreen pass ends inSHADER_READ_ONLY(the render pass’s outgoing colour dependency makes the write visible to a later sample in the same command buffer).
The per-frame render list is topologically ordered by the shared core
helper :func:simvx.core._subviewport_order.order_subviewports: when one
SubViewport samples another’s offscreen texture, the producer renders first, so
the consumer sees this frame’s content (no lag). The producer’s offscreen pass
ends in SHADER_READ_ONLY and its render pass’s outgoing colour dependency
already makes that write visible to the later same-cmd sample, so no extra
inter-SubViewport barrier is needed. Genuine cycles (mirror facing mirror)
degrade gracefully: the minimal back-edge is broken and only that one consumer
lags a frame; the helper never raises. The recursion depth is bounded by
WorldEnvironment.scene_feedback_max_depth (default 1 = render each once).
Module Contents¶
Classes¶
One offscreen render target for a single SubViewport node. |
|
Owns one :class: |
Data¶
API¶
- simvx.graphics.renderer.sub_viewport.log¶
‘getLogger(…)’
- simvx.graphics.renderer.sub_viewport.__all__¶
[‘SubViewportRenderer’, ‘SubViewportManager’]
- class simvx.graphics.renderer.sub_viewport.SubViewportRenderer(engine: Any)[source]¶
One offscreen render target for a single SubViewport node.
Wraps a :class:
RenderTargetand exposes theready/width/height/begin_pass/end_pass/render_draw2dsurface that- Meth:
SceneAdapter.render_to_targetexpects: the same contract- Class:
GameViewportRenderersatisfies.
Initialization
- create(width: int, height: int) None[source]¶
Create the offscreen target and register its colour view as bindless.
- resize(width: int, height: int) None[source]¶
Recreate the target at a new size, preserving the bindless slot.
The slot id handed out as
subviewport.texturestays stable; only the backing image view changes, so a Sprite2D / Material that captured the slot keeps sampling the live feed after a resize.
- ensure_gbuffer() None[source]¶
Recreate the target if the engine’s thin-G-buffer activation changed.
SSR/SSGI toggling the G-buffer rebuilds the forward pipelines with a second colour output; a single-attachment offscreen pass would then be render-pass-incompatible with them. Called from the manager’s prepare phase (before any offscreen draw is recorded), it rebuilds the target to match, preserving the bindless slot. A no-op on the common path.
- render_draw2d(cmd: Any, ops: list) None[source]¶
Overlay pre-extracted Draw2D ops on top of the 3D content (LOAD_OP_LOAD).
- render_items(cmd: Any, view: Any, camera: Any) None[source]¶
Render a published 2D item view into this target (RTT-2D).
The item-pipeline counterpart of :meth:
render_draw2d: the SubViewport’s own 2D subtree is collected + published (with the viewport’s own Camera2D affine,camera) on the game thread, and this submits that frozen- Class:
~simvx.graphics.render2d.publish.PublishedItemViewinto the offscreen colour target through the existing 2D pipelines. It reuses the colour-onlyoverlay_render_pass(LOAD_OP_LOAD, no depth): 2D needs no depth attachment, which resolves the 2D-only target depth-attachment incompatibility: the SubViewport keeps its depth attachment for 3D content, and the 2D overlay pass simply ignores it.
The submit is render-target-agnostic: same collection / sort / batch as the main framebuffer; only the target framebuffer + the per-target Camera2D differ. Text2D flows through as first-class GLYPH items (the legacy
render_draw2dpath never saw Text2D because itson_drawonly emits through the item builder).
- class simvx.graphics.renderer.sub_viewport.SubViewportManager(engine: Any, adapter: Any)[source]¶
Owns one :class:
SubViewportRendererper live SubViewport node.Created once per :class:
Apprun and invoked from the enginepre_rendercallback via :meth:render_all. Discovers SubViewports each frame by walking the main scene tree, lazily creating a target on first sight, debounce-resizing whenSubViewport.sizechanges, and reaping targets whose nodes have left the tree.Initialization
- render_all(cmd: Any, tree: Any) bool[source]¶
Record every SubViewport in tree into the frame’s primary command buffer.
Called from the engine
pre_rendercallback, before the main scene pass, with the frame’s primarycmd. Each SubViewport renders its own subtree with its own camera as a scene-render unit (SRU): it reserves a slice of the shared transform SSBO and records its draws with absolutefirst_instanceindices intocmd. No per-viewport command buffer, novkQueueWaitIdle: the whole frame ends in onevkQueueSubmit.Each offscreen pass leaves its colour target in
SHADER_READ_ONLYand the offscreen render pass’s outgoing colour dependency makes that write visible to a later sample in the same command buffer.The list is topologically ordered so a producer renders before any SubViewport that samples it (see :meth:
_order_live); a consumer therefore sees this frame’s content. Genuine cycles degrade to a one-frame-lagged back-edge and never raise.Returns
Trueif at least one SubViewport rendered this frame (purely informational now: there is no longer a re-submit, since the main scene’s submission lists are preserved across each SRU).
- prepare_all(tree: Any) list[tuple[Any, simvx.graphics.renderer.sub_viewport.SubViewportRenderer, simvx.graphics.renderer.sub_viewport._SubTreeView, Any, Any]][source]¶
Pass 1 of :meth:
render_all: create/resize/publish, NO cmd recording.Discovers + topologically orders the live SubViewports, creates or debounce-resizes each renderer, honours
render_target_update_mode, publishes each viewport’s 2D items, and plans multi-GPU offload. All bindless-texture descriptor updates (register_texture on create, update_texture on resize) happen HERE, so a caller can interleave the returned entries’ recording with other offscreen targets (RenderViews) without mutating a descriptor set an already-recorded draw bound.Returns the ordered entries for :meth:
record_one; empty when nothing renders this frame.
- record_one(cmd: Any, entry: tuple[Any, simvx.graphics.renderer.sub_viewport.SubViewportRenderer, simvx.graphics.renderer.sub_viewport._SubTreeView, Any, Any]) None[source]¶
Pass 2 of :meth:
render_all: record ONE prepared SubViewport SRU.No descriptor mutation happens here, so binds recorded for an earlier offscreen target stay valid. Each SRU reserves its own transform-SSBO slice and records absolute first_instance draws; the main scene’s base-0 slice is untouched. sru_id = id(node) keeps the visibility cache from colliding.
- build_srus(tree: Any) list[source]¶
Snapshot each live SubViewport into an owned :class:
SubViewportSRUplan.The pipelined-extract counterpart of :meth:
render_all: same discovery, topological ordering, create/resize, and update-mode gating, but instead of recording into a command buffer it captures, on the MAIN thread, the owned inputs the render thread needs to record the SRU offscreen WITHOUT walking the live tree, the submission lists, camera matrices, clear colour, and isolated Draw2D ops.Returns the plans producer-first (so a consumer SRU follows the producer it samples). Empty when no SubViewport is present. Create/resize + bindless descriptor updates still happen here (on the main thread, before the render thread records), exactly as in
render_allpass 1.