Add offscreen render targets and rename AssetRegistry to ResourceRegistry

Add a RenderTarget resource so the scene can render offscreen instead of
straight to the swapchain. The editor uses it to show the scene inside the
Viewport panel.

- Rename AssetRegistry to ResourceRegistry (AssetTraits -> ResourceTraits,
  assets() -> resources()) and move the pools from engine/src/assets to
  engine/src/resources. It now covers non-asset GPU resources as well.
- Replace loadMesh() with create<T>(...), and add create<RenderTarget>(),
  requestResize(), getExtent() and isAlive().
- Add RenderTarget and RenderTargetPool with RenderTargetHandle.
  Resizes are deferred and applied at the start of the next frame, when the
  GPU is idle.
- Add Application::setSceneTarget(). With no target set, the scene still
  renders to the swapchain. With a target set, the scene renders offscreen
  and the swapchain receives only the UI. Frame extent and camera projection
  follow the target's size.
- Add sophia::UI (Application::ui()) with image() and getTextureId() to draw
  a render target in an ImGui window, sizing the target to the available
  space. ImGuiRenderer now registers the target textures for this.
- ViewportPanel takes the UI and a target, and draws the scene image in place
  of the flat background fill.
- Update the testbed, the boids example, and the glossary for the rename.
This commit is contained in:
2026-09-20 16:20:28 -07:00
parent 1485ce01f3
commit 9d2b167f70
26 changed files with 743 additions and 189 deletions
+8 -8
View File
@@ -1,15 +1,15 @@
# Glossary
## AssetRegistry
## ResourceRegistry
An AssetRegistry owns GPU-backed assets on the caller's behalf and hands out
A ResourceRegistry owns GPU-backed resources on the caller's behalf and hands out
lightweight Handles instead of pointers or ownership.
Internally it's a collection of per-type pools, each responsible for one
asset type:
resource type:
```
AssetRegistry
ResourceRegistry
├── MeshPool → MeshHandle
├── TexturePool → TextureHandle (planned)
└── MaterialPool → MaterialHandle (planned)
@@ -18,19 +18,19 @@ AssetRegistry
A Handle is a small (index, generation) pair. The index locates the slot in
the pool; the generation is bumped when that slot is released, so a Handle
kept around after release() is detected as stale instead of resolving to
someone else's asset (or freed memory).
someone else's resource (or freed memory).
Typical lifecycle:
```
load<Mesh>(path) / loadMesh(...) → MeshHandle
load<Mesh>(path) / create<Mesh>(...) → MeshHandle
│
get<Mesh>(handle) → const Mesh&
│
release<Mesh>(handle)
```
Think: "Where do I get or release a GPU asset by handle, without owning its
Think: "Where do I get or release a GPU resource by handle, without owning its
lifetime directly?"
## Buffer
@@ -256,7 +256,7 @@ This is primarily a UI concept, rather than a GPU concept.
A PanelRegistry owns Panels on the caller's behalf and hands out lightweight
PanelHandles instead of pointers or ownership.
Internally it's a generation-checked slot pool, the same shape AssetRegistry's
Internally it's a generation-checked slot pool, the same shape ResourceRegistry's
per-type pools use:
```