# 0003: The Unreal kit draws with SceneCaptures and copies them on the game's own GPU queue

- **Date:** 2026-10-03
- **Status:** accepted (step 1.4)

## Context

BUILD.md step 1.4 asks for VEIN Demo (Unreal 5.6, D3D12) as a Fusion guest: Fusion's
camera drives the game's view, and the picture plus a linear depth reach Fusion through
shared textures. The plan offered two paths: a `SceneCaptureComponent2D` first, or a
hook on the swap chain's `Present` that copies the back buffer and the scene depth.

What we found (experiments in VEIN, step 1.4):
- A SceneCapture with `SCS_FinalColorLDR` gives the game's finished picture
  (tonemapped, gamma-encoded), but **far too dark unless the capture keeps its history**
  (`bAlwaysPersistRenderingState`, which auto exposure and TAA need). With it, the
  picture matches the game's own view.
- `SCS_SceneDepth` into an R32F target gives view depth in cm, aligned with the color.
- The back buffer would contain the game's HUD and menus, and Unreal 5's scene depth
  lives in transient, aliased GPU memory, so it can be gone by `Present`.
- Normal UE4SS C++ mods need UE4SS's private UEPseudo submodule (Epic-linked GitHub
  account). UE4SS.dll exports its mod base class and `LuaMadeSimple::Lua` helpers, but
  not Lua's C API.

## Decision

- **Two SceneCaptures** (color, depth), moved to Fusion's camera every frame by the kit's
  Lua. The game's own view is switched off (`show Rendering`) once Fusion shows it.
- **The copy happens inside the game, on its own queue.** main.dll hooks D3D12 resource
  creation (to recognise the two render targets by size and format),
  `ResourceBarrier` (to know their state, applied in submission order) and
  `ExecuteCommandLists`. Right after the game submits the work that finishes both
  targets, the kit submits its own small command list on the same queue: barriers,
  `CopyResource` into a free shared buffer, barriers back, fence signal. The game's
  render targets never leave the game's process or queue, and Fusion's protocol and
  Godot side stay the same as for fake-guest (only BGRA color was added).
- **No UE4SS source needed:** `cpp/ue4ss_abi.hpp` re-declares `CppUserModBase` with the
  same layout and virtual function order, and imports the few `LuaMadeSimple::Lua`
  methods through an import library built from `cpp/UE4SS.def`. The C++ part only
  registers `FusionKit_*` Lua functions; everything Unreal-specific is Lua
  (UE4SS's Lua 5.4) and everything else is Rust.
- **Router scripts are UE4SS Lua 5.4 for now** (`routers/<game>/unreal.lua`), which
  answers VISION.md §15's question for v0. Decided for good in ADR 0004 (step 5.2): they
  stay Lua 5.4, now written against the Router API (`routers/<game>/router.lua`).

## Consequences

- Works for any Unreal game with UE4SS and D3D12 that follows legacy resource barriers on
  these targets. Enhanced barriers are detected and logged, not handled yet.
- The depth capture costs a second scene render (~5 ms at 1280x720 on an RTX 3050
  Laptop). Possible later savings: reuse the color capture's own depth buffer, or render
  depth at a lower resolution.
- If UE4SS changes `CppUserModBase`, `ue4ss_abi.hpp` must be updated to match.
- D3D11 Unreal games (Hello Neighbor Alpha 2) need the same hooks for D3D11.
