Source code for simvx.graphics.renderer.secondary_sru

"""Secondary-device SubViewport SRU recorder (D8 multi-GPU offload).

The cross-device offload coordinator (:class:`~..gpu.multi_device.SRUOffloadCoordinator`)
renders an offloaded SubViewport SRU on a *secondary* GPU and then transfers its
colour image to the primary for compositing. The per-device GPU recording is
delegated to this object so the shared :class:`~.forward.Renderer` is NOT made to
grow a multi-device entry point (the single-GPU path must stay byte-identical).

:class:`SecondarySRURenderer` wraps a ``Renderer`` already constructed + ``setup``
on a secondary device (via a :class:`~..gpu.secondary_engine.SecondaryRenderContext`
facade). It exposes one method, :meth:`render_sru_offscreen`, mirroring the primary
:meth:`~..scene_adapter.SceneAdapter.render_sru_from_plan` slice model but recording
into an OWN one-shot command buffer on the secondary device and submitting on the
secondary graphics queue (a secondary never shares the primary's frame command
buffer; its render must complete + signal before the cross-device transfer reads
the colour image).

Honest scope. This is rig-only: it runs on the 4x Arc Pro B70 rig, never on a
single-GPU box (no facade / secondary device is ever constructed there, so the
offload coordinator that would call this is never built). The code is import-safe
and structurally complete; the residency substitution it depends on (a secondary
``MeshHandle`` per primary mesh) is flagged where the primary CPU geometry must be
supplied (see :meth:`render_sru_offscreen`).
"""

from __future__ import annotations

import logging
from typing import Any

import vulkan as vk

from ..gpu.memory import begin_single_time_commands, end_single_time_commands

log = logging.getLogger(__name__)

__all__ = ["SecondarySRURenderer"]


[docs] class SecondarySRURenderer: """Records one offloaded SubViewport SRU on a secondary device. Constructed by the rig-side ``secondary_renderer_factory`` with the secondary :class:`~..gpu.secondary_engine.SecondaryRenderContext` facade and a ``Renderer(facade)`` already set up on that device. The offload coordinator calls :meth:`render_sru_offscreen` once per offloaded SRU per frame. """ def __init__(self, facade: Any, renderer: Any) -> None: self._facade = facade self._renderer = renderer
[docs] def set_residency(self, residency: Any) -> None: """Bind the :class:`~..gpu.secondary_engine.SecondaryResidency` for handle remap. The coordinator owns residency (it mirrors meshes/textures before calling this); binding it here lets the recorder substitute each instance's primary :class:`~..types.MeshHandle` for the secondary one. Optional: when unset the recorder assumes the SRU's handles are already secondary-resident. """ self._residency = residency
[docs] def render_sru_offscreen(self, sru: Any, target: Any) -> None: """Record + submit one SRU's draws into ``target`` on the secondary device. Mirrors the primary :meth:`SceneAdapter.render_sru_from_plan` slice model on the secondary renderer: 1. ``begin_frame`` the secondary renderer (clears its per-frame lists + resets its indirect batches; this device records its OWN frame). 2. Remap each instance's primary :class:`MeshHandle` to the secondary one via the bound residency (geometry was mirrored by the coordinator). FLAGGED: requires the secondary mesh to be resident; an instance whose mesh is not resident is skipped with a warning (it would index the wrong device's buffers otherwise). 3. Install the SRU's single camera viewport, reserve a transform-SSBO slice, upload the SRU transforms into it, and set materials/lights mirrored from the primary (set by the coordinator via :meth:`set_materials`). 4. Allocate a one-shot command buffer on the secondary command pool, begin the offscreen pass on ``target``, ``render_scene_content`` (``hdr_output`` 0 so the offscreen image holds LDR), end the pass, overlay any Draw2D ops, then submit on the secondary graphics queue and wait. The RenderTarget leaves the colour image in ``SHADER_READ_ONLY_OPTIMAL``, ready for the cross-device transfer's ``TRANSFER_SRC`` barrier. """ renderer = self._renderer renderer.begin_frame() instances = self._remap_instances(getattr(sru, "instances", [])) skinned = self._remap_instances(getattr(sru, "skinned_instances", [])) renderer._instances = instances renderer._skinned_instances = skinned vpm = renderer.viewport_manager vpm.clear() if sru.camera_view is not None and sru.camera_proj is not None: vpm.create_viewport(0, 0, sru.width, sru.height, sru.camera_view, sru.camera_proj, None) n_slots = len(instances) + len(skinned) base = renderer._buffers.reserve_slots(n_slots) renderer._first_instance_base = base renderer._sru_id = sru.sru_id renderer._buffers.upload_transforms(instances, upload_aabbs=False, base=base) cmd = begin_single_time_commands(self._facade.ctx.device, self._facade.ctx.command_pool) try: target_clear = list(getattr(sru, "clear_colour", (0.0, 0.0, 0.0, 1.0))) self._begin_target_pass(cmd, target, target_clear) renderer._scene_renderer.render_scene_content(cmd, hdr_output=0) vk.vkCmdEndRenderPass(cmd) finally: # Submit + wait on the secondary queue: the colour image is fully written # (and in SHADER_READ_ONLY_OPTIMAL) before the cross-device transfer runs. end_single_time_commands( self._facade.ctx.device, self._facade.ctx.graphics_queue, self._facade.ctx.command_pool, cmd, )
def _remap_instances(self, instances: list) -> list: """Substitute each instance's primary MeshHandle for the secondary one. Returns a new instance list with secondary handles. An instance whose mesh is not resident on the secondary device is dropped with a one-time warning (FLAGGED: the coordinator mirrors geometry via ``SecondaryResidency.ensure_meshes``; a miss means the primary CPU geometry was not available to mirror). """ residency = getattr(self, "_residency", None) if residency is None: return list(instances) out = [] for entry in instances: mh = entry[0] secondary = residency.secondary_mesh(id(mh)) if secondary is None: log.warning("secondary SRU: mesh %s not resident on secondary device, skipping", id(mh)) continue out.append((secondary, *entry[1:])) return out def _begin_target_pass(self, cmd: Any, target: Any, clear_colour: list) -> None: """Begin the offscreen colour+depth pass on a secondary-device RenderTarget.""" clear_values = [ vk.VkClearValue(color=vk.VkClearColorValue(float32=clear_colour)), vk.VkClearValue(depthStencil=vk.VkClearDepthStencilValue(depth=1.0, stencil=0)), ] rp_begin = vk.VkRenderPassBeginInfo( renderPass=target.render_pass, framebuffer=target.framebuffer, renderArea=vk.VkRect2D( offset=vk.VkOffset2D(x=0, y=0), extent=vk.VkExtent2D(width=target.width, height=target.height), ), clearValueCount=len(clear_values), pClearValues=clear_values, ) vk.vkCmdBeginRenderPass(cmd, rp_begin, vk.VK_SUBPASS_CONTENTS_INLINE) vk.vkCmdSetViewport(cmd, 0, 1, [vk.VkViewport( x=0.0, y=0.0, width=float(target.width), height=float(target.height), minDepth=0.0, maxDepth=1.0, )]) vk.vkCmdSetScissor(cmd, 0, 1, [vk.VkRect2D( offset=vk.VkOffset2D(x=0, y=0), extent=vk.VkExtent2D(width=target.width, height=target.height), )])
[docs] def set_materials(self, materials: Any) -> None: """Mirror the primary material SSBO contents onto the secondary renderer.""" self._renderer.set_materials(materials)
[docs] def set_lights(self, lights: Any) -> None: """Mirror the primary light SSBO contents onto the secondary renderer.""" self._renderer.set_lights(lights)