Moves the per-frame GPU work (PM4 drain: draws, YUV texture decodes, resolves — ~12 ms/swap) AND the per-swap UI publish off the emulation thread onto the GPU worker thread when --ui is combined with XENIA_UI_GPU_THREAD=1. This is the fix for the dominant remaining --ui cost identified after A.1 (the inline drain at exports.rs:3145). Default OFF: plain --ui still forces the inline backend (the safe, milestone-2-verified path), so this cannot regress the shipped result. - xenia-gpu/handle.rs: new UiPublishHooks + WorkerSwapInfo (pure xenia-gpu types so the worker needs no kernel dep); GpuCommand::InstallUiHooks; GpuWorker gains ui_hooks + last_published_swaps; the worker runs GpuSystem::run_ui_publish (blobs/constants/texture/geometry + bulk frontbuffer detile + notify) level-triggered on stats.swaps_seen. - xenia-kernel/exports.rs: vd_swap returns immediately in threaded+ui mode (no blocking drain, no emulation-thread publish); the worker discovers the in-stream PM4_XE_SWAP and publishes. Inline/headless paths untouched. - xenia-app/main.rs: XENIA_UI_GPU_THREAD knob; run_with_ui takes Arc<GuestMemory> (removes the old --ui+gpu-thread try_unwrap panic); builds the publish hooks from the live UiBridge and installs them on the worker; enables frame capture on the worker's GpuSystem before spawn. Headless golden sylpheed_n200m byte-identical (inline path unchanged); threaded headless smoke clean. Threaded --ui visual correctness pending human verification (no self-screenshots). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>