Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
71805e8
M2 Phase A
dantros Jun 20, 2026
0301909
M2 Phase B
dantros Jun 20, 2026
9cbf5a4
Merge branch 'main' into thread_aware_phase2
dantros Jun 20, 2026
3b9f702
enter imgui to the multi threaded world running within the sim thread
dantros Jun 20, 2026
e47836d
threaded imgui supporting all inputs
dantros Jun 20, 2026
8fedf5c
missing call in vulkan backend
dantros Jun 20, 2026
b90153f
demo fix
dantros Jun 20, 2026
fd9f2eb
demo fix pinning texture to avoid auto GC removal
dantros Jun 20, 2026
b3f0f40
fix(threaded): imguiWantsKeyboard() reflects active capture, not nav …
dantros Jun 20, 2026
c00b634
refactor(frame_runner): unify single-threaded and threaded frame paths
dantros Jun 20, 2026
4966785
Gamepad input now reaches the sim thread via the unified threaded core.
dantros Jun 22, 2026
13c7402
showing stats
dantros Jun 22, 2026
e16648a
using performance monitor
dantros Jun 22, 2026
9e44844
adding stats
dantros Jun 22, 2026
655ba41
Merge main into thread_aware_phase2_unification
dantros Jun 22, 2026
645c278
The duplicated bellota-mutation surface is gone — there's now one add…
dantros Jun 22, 2026
6b5d32a
keyboard + mouse game input now reaches the sim thread, mirroring the…
dantros Jun 23, 2026
d45063c
textures, meshes, and render targets can now be created/destroyed fro…
dantros Jun 23, 2026
4174e3e
Dense/Sparse land explorers now run on the threaded path, driven from…
dantros Jun 23, 2026
eb29bdf
setScreenSize is now race-safe on the threaded path.
dantros Jun 23, 2026
7a10d63
Merge branch 'main' into thread_aware_phase2_unification
dantros Jun 24, 2026
0fabc0f
Cursor-shape feedback on the threaded path: the sim UI's ImGui mouse …
dantros Jun 24, 2026
5ad2d95
ImGui gamepad navigation on the threaded path (B6): enable NavEnableG…
dantros Jun 24, 2026
43879a7
OS clipboard across the thread boundary on the threaded path (B7): ad…
dantros Jun 24, 2026
a1c38da
feat(threaded): carry screenSize in RenderSnapshot (C10)
dantros Jun 24, 2026
21534f9
feat(threaded): analog gamepad nav on the threaded ImGui path (C11)
dantros Jun 24, 2026
ab23b6f
feat(threaded): stats overlay into the sim-UI clone, render + sim rat…
dantros Jun 24, 2026
ccc11a3
feat(threaded): threaded-safe markTextureAsDirty / setTextureMin|MagF…
dantros Jun 24, 2026
14df731
Merge branch 'main' into thread_aware_phase2_unification
dantros Jun 24, 2026
24bfbaa
roadmap update
dantros Jul 5, 2026
556fe18
feat(threaded): thread-affinity debug guards (debugCheckRenderThread/…
dantros Jul 5, 2026
5f6192a
refactor(threaded): make the render-thread guard always-on
dantros Jul 5, 2026
6245a4c
feat(threaded): commit(dt, update, ui, Controller&) overload
dantros Jul 5, 2026
01a6c18
feat(threaded): run(update, ui, simController, renderController) conv…
dantros Jul 5, 2026
b466870
example(test_keyboard): drive via run(update, ui, simController, rend…
dantros Jul 5, 2026
c43539a
migrating hello_nothofagus to threaded
dantros Jul 5, 2026
2e6ea73
Merge remote-tracking branch 'origin/main' into thread_aware_phase2_u…
dantros Jul 5, 2026
36b432d
newer tracking doc
dantros Jul 5, 2026
0af7656
first commit hash recorded
dantros Jul 5, 2026
cd04dff
pending threaded diagetic imgui
dantros Jul 5, 2026
1588e71
examples(threaded): migrate no-UI/no-controller demos to run(update, ui)
dantros Jul 5, 2026
b4235e1
examples(threaded): split ImGui into ui callback for no-controller demos
dantros Jul 5, 2026
6c90f8c
examples(threaded): split controller into sim/render for single-contr…
dantros Jul 5, 2026
735e52e
examples(threaded): migrate dense/sparse land explorers to run(update…
dantros Jul 5, 2026
ec542a7
canvas(threaded): reroute run()/run(update) to the threaded path, dep…
dantros Jul 5, 2026
88be312
feat(threaded): registered ImGui images in the sim/render split
dantros Jul 5, 2026
62c96ae
docs(threaded): mark registered ImGui images supported; record Phase …
dantros Jul 5, 2026
b8c440c
feat(threaded): diegetic renderImguiTo in the sim/render split
dantros Jul 5, 2026
b44ef2e
docs(threaded): mark diegetic renderImguiTo supported; record Phase 2…
dantros Jul 5, 2026
f8ab7ca
perf(threaded): gate the diegetic imgui>asset lock on frames that use…
dantros Jul 5, 2026
88d24e9
perf(threaded): keep sprite work overlapping the sim on diegetic frames
dantros Jul 5, 2026
ec86a62
docs(threaded): fold THREADED_DIEGETIC_IMGUI.md into THREADED_MODE.md…
dantros Jul 5, 2026
e5d79ba
docs(threaded): track threaded screenshots as pending work
dantros Jul 5, 2026
a300e05
feat(threaded): marshal scheduled screenshots across the sim/render b…
dantros Jul 5, 2026
f491048
refactor(examples): migrate hello_screenshot to the threaded run over…
dantros Jul 5, 2026
23dc164
docs(threaded): record threaded screenshots as done
dantros Jul 5, 2026
addccdc
docs(threaded): verification recipe reflects direct-GPU runs + SwiftS…
dantros Jul 5, 2026
19c11d5
Merge remote-tracking branch 'origin/main' into thread_aware_phase2_u…
dantros Jul 5, 2026
cac5399
fix(threaded): make HeadlessBackend::mRunning atomic (data race found…
dantros Jul 5, 2026
e0d848e
test(sanitizers): threaded TSan/ASan fixture on SwiftShader headless
dantros Jul 5, 2026
147246d
docs(threaded): document the SwiftShader TSan/ASan sanitizer recipe
dantros Jul 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,10 @@ canvas.renderImguiTo(renderTargetId, canvas.defaultImguiFontId(), [&] { ... });
- The OpenGL ImGui backend is FBO-agnostic; the Vulkan backend's per-context pipeline is built against `mRttRenderPass`, so it is render-pass-compatible with `beginRttPass`. **No changes to `imgui_impl_opengl3.*` / `imgui_impl_vulkan.*` are required.**
- The platform backend (GLFW/SDL3) is skipped for secondary contexts — they run headless-style with `IO.DisplaySize` / `IO.DeltaTime` set manually. This means the feature works identically across GLFW+OpenGL, GLFW+Vulkan, SDL3+OpenGL, SDL3+Vulkan, and headless Vulkan.

**Threaded (sim/render split):** `renderImguiTo` works under the threaded `run(update, ui[, sim, render])` — call it from the `ui` callback. The user callback runs on the **sim** thread against a per-RTT secondary context and its draw data is deep-cloned into the frame snapshot (`RenderSnapshot::rttUi`); the render thread only replays the clone. See [THREADED_MODE.md](THREADED_MODE.md).

**Limitations (v1):**
- **Input is not forwarded** to the secondary context — widgets render correctly but mouse/keyboard events only reach the main context. Forwarding canvas-space mouse coords into the RTT's `IO.MousePos` is a natural follow-up.
- **Input is not forwarded** to the secondary context — widgets render correctly but mouse/keyboard events only reach the main context (true in both single-threaded and threaded modes). Forwarding canvas-space mouse coords into the RTT's `IO.MousePos` is a natural follow-up.
- Each secondary context has its own ID stack, window state, and widget values — widgets with the same name in different RTTs do not collide, and neither inherits state from the main UI.

#### Draw a Visual inside ImGui — `Canvas::registerImguiImage` / `imguiImage`
Expand Down Expand Up @@ -706,6 +708,7 @@ bool isImguiImageReady(ImguiImageId id) const;
- **Lifecycle.** Registered entries are **never** garbage-collected (only `unregisterImguiImage` frees them) and only re-render on frames they are displayed, are `updateImguiImage`-dirtied, or have not yet been populated — so off-screen registered images cost nothing yet keep a valid handle and last-rendered content. `updateImguiImage` keeps the id/handle stable for layer/opacity/mesh changes; it recreates the RTT (a one-tick re-warm) only when the resolved physical size or the source texture/mesh changes. Sim/render-split aware: the sim side bakes the stable handle into the ImGui draw list and all GPU work (RTT creation/draw, flat-2D handle creation) is render-side and id-driven — nothing is created from the user callback.
- **Backends.** OpenGL keeps a flat `GL_TEXTURE_2D` companion blitted (Y-flipped) from the RTT array texture each frame, since ImGui binds `GL_TEXTURE_2D`; Vulkan adds a `VK_IMAGE_VIEW_TYPE_2D` view of the RTT color image + `ImGui_ImplVulkan_AddTexture` (no copy). Both via three render-agnostic `RenderBackend` methods (`acquireFlat2DImguiHandle` / `resolveRenderTargetFlat2D` / `releaseFlat2DImguiHandle`).
- **Vulkan image budget.** Each on-screen registered image holds one ImGui descriptor set from a fixed Vulkan pool sized by `kImguiImageDescriptorPoolSize` ([source/backends/vulkan_backend.h](source/backends/vulkan_backend.h), default 1024; ~tens of KB reserved). Past that many *concurrent* images the backend logs an error once and no-ops the surplus (those images draw nothing) rather than aborting; it recovers as images leave the screen. Raise the constant if you display more at once. OpenGL has no such limit — it binds GL texture handles directly, with no descriptor pool.
- **Threaded (sim/render split):** registered images work under the threaded `run(update, ui[, sim, render])` — register/update/draw from `update`/`ui` on the sim thread; the manager is serialized under the asset mutex and GPU frees are deferred to the render thread. See [THREADED_MODE.md](THREADED_MODE.md).
- **v1 scope:** works in the main UI context. Calling `imguiImage` inside a `renderImguiTo` diegetic panel is a planned follow-up.

### ImGui fonts — `ImguiFontManager`, `ImguiFontSourceId`, `ImguiFontId`
Expand Down
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ file(GLOB_RECURSE NOTHOFAGUS_PUBLIC_HEADERS "include/*.h")
add_library(nothofagus
"source/canvas.cpp"
"source/frame_runner.cpp"
"source/render_snapshot.cpp"
"source/imgui_draw_clone.cpp"
"source/asset_registry.cpp"
"source/texture_container.cpp"
"source/mesh_container.cpp"
Expand Down Expand Up @@ -377,6 +379,14 @@ if (${NOTHOFAGUS_BUILD_TESTS})
add_subdirectory("tests")
endif()

# Sanitizer fixture: a small threaded workload for running TSan / ASan+UBSan against the
# SwiftShader Vulkan headless backend (Mesa is buggy under sanitizers). OFF by default;
# tools/sanitizers/run_sanitizers.sh turns it on with the right sanitizer + SwiftShader flags.
option(NOTHOFAGUS_BUILD_SANITIZER_FIXTURE "Build the threaded sanitizer smoke fixture?" OFF)
if (${NOTHOFAGUS_BUILD_SANITIZER_FIXTURE})
add_subdirectory("tools/sanitizers")
endif()

# Visual Tests Explorer: a windowed Nothofagus tool to inspect golden/actual/diff
# images and update goldens. Pulls in stb_image_plus for file I/O. OFF by default
# (interactive tool, not part of the headless CI lane).
Expand Down
173 changes: 173 additions & 0 deletions THREADED_MODE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Threaded mode — sim/render split (status)

> As-built status of nothofagus's optional two-thread sim/render path. `run()`/`tick()`
> remain the unchanged single-threaded default; the threaded path is additive and opt-in.
> Branch: `thread_aware_phase2_unification` (first commit `71805e8`).

## What it is

A sim thread mutates the scene and **commits** a POD frame snapshot; the render thread
draws the **previous** snapshot; the GPU is the implicit third stage. Render of frame N
overlaps sim of frame N+1. GPU-resource frees are **deferred** across the one-frame lag so
a resource dropped by sim this tick isn't freed while an in-flight snapshot still uses it.

Opt-in API (nothofagus spawns no threads unless you use a `run(...)` convenience overload):
`beginThreadedSession` / `commit(dt, update[, uiCallback][, simController])` / `renderFrame`
/ `isThreadedRunning`, plus the `run(update, ui[, simController, renderController])`
convenience overloads that own the sim thread for you.

## Architecture invariants (hold across the whole path)

- **Nothofagus stays driven, not driving** — the library doesn't spawn threads except in
the `run(...)` convenience; the app can drive both loops itself.
- **One mutable scene + one POD projection** — no second bellota container. The live
`bellota(id) -> Bellota&` mutation path is untouched; the render side consumes only the
POD `RenderSnapshot` and never touches a `Bellota`.
- **Snapshot references resources by id, not GPU handle** — `DrawItem` carries
`TextureId`/`MeshId`; the render side resolves id → GPU handle *after* upload.
- **The seam sits above `ActiveBackend`** — GL and Vulkan inherit the split; the invariant
is *all GPU create/destroy/draw on the single render thread*.
- **RTT passes ride in the same snapshot** — no extra-frame deferral; uniform one-frame lag.

## Done (verified in source)

### Foundation
- **Unified frame path** — one `produce(FrameMode)` + one `consume(FrameMode)`
(`Single` = run/tick, `Threaded` = commit/renderFrame). Behavior-preserving.
- **Triple-buffered snapshot store** (`snapshot_buffers.h`) — 3 slots + lock-free mailbox;
sim never blocks, render always gets the freshest, stale intermediates dropped.
- **Deferred GPU free** — `collectUnused*` / `freeRetired*` in `asset_registry.*`, gated on
`retireSeq <= lastRenderedSeq`; render-target deferred-free queue drains + releases the
per-RTT ImGui context.
- **Unified `addBellota`/`removeBellota`** — one mode-aware add/remove safe in both
single-threaded and live-threaded contexts (old `spawnBellota`/`despawnBellota` deleted).

### Game input on the sim thread (two-controller model)
- **Gamepad** — `commit(dt, update, Controller& simController)`; render-side `GamepadSnapshot`
harvest → POD → sim-side feed/replay against the sim controller.
- **Keyboard + mouse** — `Controller` gained `isKeyDown`/`isMouseButtonDown` + a scroll
accumulator; `GameInputSnapshot` harvest/feed. The 4-arg `commit` feeds gamepad + keyboard
+ mouse before `update`.
- **render controller vs sim controller** — render controller owns window input + `close`
(GLFW is main-thread-only); sim controller is the game's, consumed only on the sim thread.

### Interactive ImGui on the sim thread
- **Sim-UI context + `ImDrawData` deep-clone** carried in the snapshot; thread-local `GImGui`;
the shared 1.92 dynamic font atlas is serialized by `mImguiMutex` (short sections; game-sim
+ sprite render still overlap). `commit(dt, update, uiCallback)` splits lock-free game logic
from the locked UI frame.
- **Full input marshalling** render→sim — mouse, full keyboard state + modifiers + typed
characters + focus; `WantCaptureMouse/Keyboard` back-marshalled to
`imguiWantsMouse()`/`imguiWantsKeyboard()`.
- **Cursor-shape feedback** — sim `GetMouseCursor()` → atomic → render `SetMouseCursor`.
- **ImGui gamepad nav** — `NavEnableGamepad` on both contexts; digital D-pad/face-button plus
**analog** stick nav (`keyAnalog[]` side-channel + `AddKeyAnalogEvent`).
- **OS clipboard** across the thread boundary — `WindowBackend get/setClipboardText`
(GLFW/SDL3/headless); mutex-guarded marshal, writes immediate, OS reads throttled ~0.25 s.
- **Stats overlay** drawn into the sim-UI clone (so it survives even when the app commits its
own ImGui); render fps/ms marshalled to sim via atomics.

### Runtime asset mutation from the sim thread
- **Explorers (Dense/Sparse land)** run in `produce(Threaded)` under the asset mutex.
- **Create/destroy of textures / meshes / render targets** from `commit`'s update — mode-aware
asset wrappers (lazy adds + deferred removes), tolerant retire, RT deferred-free queue.
- **Threaded-safe `setScreenSize`** — `mScreenSize` is `std::atomic<ScreenSize>`; resizing the
logical canvas from the sim update is race-free and rebuilds the explorer pool next commit.
- **`screenSize` carried in `RenderSnapshot`** — each snapshot renders in the size it was built
for; no 1-frame letterbox transient on a threaded `setScreenSize`.
- **Threaded-safe `markTextureAsDirty` / `setTextureMin|MagFilter`** — now pure-CPU dirty flags
(`mContentDirty` / `mFilterDirty`) consumed by `syncToGpu` on the render thread.
- **Scheduled screenshots across the sim/render boundary** — `requestScreenshot()` can't touch the
render-owned backend from the sim thread, so it raises `std::atomic<bool> mScreenshotRequested`
(a sim→render channel modeled on `mThreadedCursor`); the render thread observes it at the top of
`consume(Threaded)` and does the `armScreenshot` there, sized to the snapshot being rendered — so
`mScreenshotArmed` stays render-thread-local. The captured pixels come back render→sim via
`mScreenshotResult` guarded by `mScreenshotResultMutex` (a heap-payload channel modeled on
`mClipboardFromOs`); `retrieveScreenshot()` consumes it under the same mutex. Single-threaded keeps
the eager arm (byte-identical). Demo: `hello_screenshot` (migrated to the 4-arg threaded `run`).

### Diegetic ImGui + registered ImGui images on the sim thread
- **Registered ImGui images** (`registerImguiImage`/`imguiImage`/`updateImguiImage`) — the
`ImguiImageManager` is threaded through the produce/consume arms (via `mThreadedImguiImages`); sim-side
`beginFrame`/`appendInternalPasses` run in the `produce(Threaded)` arm, registry access is serialized
under the asset mutex (the same one `resolveImages` holds render-side — the Canvas entry points wrap
`lockAssetsIfThreaded`), and GPU handle/RTT frees are deferred render-side via a two-phase retire queue
(`retireEntryGpu`/`drainRetiredGpu`). Demos: `hello_imgui_visual`/`_image_registry`/`hello_markdown`.
- **Diegetic `renderImguiTo`** — `ImguiRttManager::flushPending` split into sim-side **`produceClones`**
(run each `renderImguiTo` callback on its secondary context, deep-clone its draw data into the snapshot's
`rttUi` — the `mainUi` reuse template) + render-side **`replayClones`** (lazy per-RTT backend init +
`RenderDrawData` of the clones, no user code). Wired through `produce(Threaded)` via `mThreadedImguiRtt`.
Demos: `hello_imgui_rtt`/`hello_dpi_scaling`.
- **Lock order (both features).** The sim takes `imgui⊃asset` (a Canvas ImGui-image draw in `ui` grabs the
asset mutex while the ImGui mutex is held), so any render-side step that touches the shared font atlas
must take the pair in the same order. `renderSnapshotContents` is split so only the atlas-touching steps
hold the ImGui mutex: `renderSnapshotPreMain` (uploads + sprite RTT passes + `resolveImages`) and
`renderSnapshotMain` (main sprite draw) run **asset-only** and overlap the sim; `replayClones` (diegetic)
and the main `endFrame` run under **`imgui⊃asset`**. So a diegetic frame serializes only the ImGui draws,
not the sprite work. `drainPendingFrees` runs under `imgui⊃asset` too (its RT branch tears down secondary
ImGui contexts). No render path ever takes `asset⊃imgui`, so it's deadlock-free.
- **v1 limitation (both single-threaded and threaded):** diegetic panels take **no input** — mouse/keyboard
reach only the main context. Threaded support is render parity, not new input forwarding.

## Demo migration — done

Every example now runs on the threaded path via the `run(update, ui[, simController,
renderController])` convenience (split the single `run` lambda into a game-logic `update` + an ImGui
`ui`, and input into `simController` (game) vs `renderController` (window/`close`)). `run()`/`run(update)`
are rerouted to the threaded path and `run(update, Controller&)` is `[[deprecated]]`.

**hello_headless** deliberately stays off the threaded path: it uses `tick()` (single-step/headless
harness), so a threaded port isn't meaningful — it's the single-threaded/manual-tick reference. It is now
the **only** by-design single-thread demo; `hello_screenshot` was migrated to `run(update, ui,
simController, renderController)` once the threaded-screenshot channel landed (see above), so there are no
in-tree callers of the `[[deprecated]] run(update, Controller&)` overload left.

## Pending / deferred work

- _(none tracked — the threaded-screenshot gap that previously lived here is closed; see
"Scheduled screenshots across the sim/render boundary" under Done.)_

## Out of scope

- **Triple-buffer interpolation** — render interpolating between the two most recent snapshots
by stable id for extra smoothness. Deferred: needs per-drawable stable IDs in `DrawItem`,
previous-snapshot retention, ID-matching, and a render-time alpha.
- **Integrating a nothofagus consumer's game/scripting loop onto `commit`/`renderFrame`** —
out of nothofagus scope by design; not tracked here.
- **Full driver collapse** (`run`/`tick` → only `commit`/`renderFrame`) — deferred by choice;
it would force every ImGui-in-`update` demo onto the threaded ImGui model. The in-place demo
migration above is the incremental path toward this, without forcing it.

## Critical files

- `source/render_snapshot.h`/`.cpp` — POD draw list + `screenSize` + `mainUi` clone handle.
- `source/snapshot_buffers.h` — triple buffer + lock-free mailbox.
- `source/frame_runner.h`/`.cpp` — `produce`/`consume`, threaded `commitFrame`/`renderFrameThreaded`,
`runThreaded`, sim-UI context, input marshalling, the asset + ImGui mutexes, deferred frees.
- `source/imgui_draw_clone.h`/`.cpp` — `ClonedImDrawData` + thread-local `GImGui`.
- `source/asset_registry.*` — `collectUnused*` / `freeRetired*`.
- `include/canvas.h` + `source/canvas.cpp` — threaded API + `run(...)` convenience overloads.
- `examples/hello_threaded*.cpp` — threaded demos.

## Verification recipe (every change)

- Cross-backend builds: `linux-release-glfw-opengl-examples`, `-glfw-vulkan-examples`,
`-sdl3-opengl-examples`, `-sdl3-vulkan-examples`.
- Run the threaded demos **directly on the GPU** for each window×render combo (glfw/sdl3 ×
opengl/vulkan) and confirm they behave; on the Vulkan builds check the validation output is free of
`VUID-` / `SYNC-HAZARD` messages.
- SwiftShader Vulkan headless goldens unchanged (this is the CI lane): `linux-release-headless-vulkan-tests`
+ `NOTHOFAGUS_RENDER_BACKEND=swiftshader .../rendering_tests`.
- **TSan + ASan/UBSan on the threaded path — against SwiftShader, not the GPU:** `tools/sanitizers/run_sanitizers.sh`
(builds a headless-Vulkan + SwiftShader fixture, `tools/sanitizers/threaded_smoke.cpp`, with the sanitizer
flags and runs it). Expect **0 sanitizer reports** and a clean exit. Two things to know:
- **Mesa is buggy under sanitizers** — run the sanitizers on SwiftShader (deterministic CPU Vulkan) only;
do **not** point them at the system GPU.
- The validation layer is disabled for the run (`VK_LOADER_LAYERS_DISABLE`) and TSan uses
`tools/sanitizers/tsan.supp` to filter SwiftShader-ICD / ImGui-Vulkan init-teardown noise. That file is
for third-party/init noise **only** — a race in nothofagus code must be fixed, never suppressed.
- TSan under SwiftShader is slow (it instruments SwiftShader's worker pool), so the fixture is small and
self-closing; give it a generous timeout.
- Any new cross-thread shared state must be mutex- or atomic-guarded like the existing marshal channels
(`mThreadedGamepadState`/`mThreadedGameInputState`, the `mThreadedCursor` atomic, the `mClipboardMutex`
payload, `HeadlessBackend::mRunning`) — argue race-freedom by mirroring those, and confirm with TSan.
25 changes: 25 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,28 @@ add_executable(hello_dpi_scaling
)
set_property(TARGET hello_dpi_scaling PROPERTY CXX_STANDARD 20)
target_link_libraries(hello_dpi_scaling PRIVATE nothofagus)

add_executable(hello_threaded
"hello_threaded.cpp"
)
set_property(TARGET hello_threaded PROPERTY CXX_STANDARD 20)
find_package(Threads REQUIRED)
target_link_libraries(hello_threaded PRIVATE nothofagus Threads::Threads)

add_executable(hello_threaded_imgui
"hello_threaded_imgui.cpp"
)
set_property(TARGET hello_threaded_imgui PROPERTY CXX_STANDARD 20)
target_link_libraries(hello_threaded_imgui PRIVATE nothofagus Threads::Threads)

add_executable(hello_threaded_gamepad
"hello_threaded_gamepad.cpp"
)
set_property(TARGET hello_threaded_gamepad PROPERTY CXX_STANDARD 20)
target_link_libraries(hello_threaded_gamepad PRIVATE nothofagus Threads::Threads)

add_executable(hello_threaded_dense_land
"hello_threaded_dense_land.cpp"
)
set_property(TARGET hello_threaded_dense_land PROPERTY CXX_STANDARD 20)
target_link_libraries(hello_threaded_dense_land PRIVATE nothofagus Threads::Threads)
5 changes: 3 additions & 2 deletions examples/hello_animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ int main()
textureArrayAnimationTree.update(dt);
};

// Run the canvas with the update loop
canvas.run(update);
// Multithreaded convenience: the sim thread runs update; the main thread renders.
// No ImGui and no controllers, so the ui callback is empty.
canvas.run(update, [](float){});

return 0;
}
Loading
Loading