feat(runtime): add semantics command queue api (#12198) bd587cb83f * wip: semantic system * feat: semantic state (#11888) * wip: semantic state * docs: semantic steps * test: initial state tests * chore: live region sample and set on inferred text component * fix: component list propegation * fix: visiblity changes exclude semantics and list examples * chore: update keys * refactor: remove semantic cursor and query artboard directly * refactor(flutter): semantic painter mixin * chore: remove redundant semanticsChanged call * chore: clangy clank * docs: cleanup * refactor: atomic node id * refactor: remove node id type alias * feat: focus and semantic handshake * refactor: opt in semantics for flutter * chore: cleanup * fix: tmp step around failing editor tests * chore: clang clank * refactor: cleanup public API * feat: add focusable as a state * chore: TEMP REMOVE TESTS RESTORE LATER * feat: semantic inference from subtree * refactor: expanded definition and state changes * feat: add support for passthrough bit flags (#11992) * add support for passthrough bit flags (cherry picked from commit de4a5504a448df8f051fadae2abf154ea1fad9fd) * chore: post cherry pick rerun code generator * feat: expose more passthrough properties * feat: add all passthrough properties as editor options and labels * chore: update keys after merge * chore: resolve merge issues and other post merge updates * chore: updated example with new core props * chore: remove expanded from state * feat: phase2 semantic roles and states * chore: clang clank * feat: add semantic actions * refactor: remove numericX * refactor: remove heading and progressIndicator from Roles * fix: missing potential semantic configurations * feat: add semantic traits for accessibility tristate support (#12148) * feat: add semantic traits * fix: semantic action dispatch for listeners targeting nested shapes * fix: core generator call onPropertyChanged to update editor state * refactor: editor UI for trait selection * chore: updated tests and examples * chore: clang clank * chore: updated tests * fix: pass correct from/to mask values in bitmask passthrough notifications * feat: add semantic artboard boundary (#12180) * feat: add semantic artboard boundary nodes for targeted collapse and bounds dirtying * fix: copy vector before iterating * feat: make semantics runtime opt-in * refactor: move around the entry point for semantics * fix: flutter check attach, correct comments, expanded test * fix(flutter): radio button isButton true (untested) * chore: additional null checks * chore: format def file * chore: uint32 for traits and states diff * feat: add semantics command queue api * fix(semantics): order nodes by visual position instead of static graph order (#12200) * fix(semantics): order nodes by visual position instead of static graph order * fix: use isBoundaryNode() instead of semanticData() for container bounds check * perf(semantics): specialize updatedGeometry as POD bounds struct (#12205) * refactor: dedicated SemanticBoundsUpdate struct * perf(semantics): tighten FFI diff readers and lock WASM layouts * chore: update code comment * chore(semantics): test and sample cleanup * chore(flutter): other cleanup and analyzer fixes * fix(semantics): emit diff arrays in deterministic tree pre-order * fix(semantics): scope semantic node id allocation per-manager * fix(semantics): static_assert FFI struct sizes on 64-bit native * test(semantic): SemanticTreeModel.applyDiff and the incremental refresh() paths * docs(semantics): document Mixed-wins precedence for Checked state * refactor(semantics): derive trait/state bit constants from JSON * refactor(semantics): tighten SemanticNode encapsulation * fix(semantics): (refactor) route fireSemanticAction through the manager index * refactor(semantics): bind semantic listeners strictly to target's SD * test: update .riv files to have listener on SD * perf(semantics): drop std::function closure in label derivation * perf(semantics): fuse post-order bounds walk with reorder check * chore(semantics): simplify and tighten the /simplify review findings Aggregated review cleanups from three parallel audits of this session's work. No behaviour changes outside the applyDiff no-op guard (which tightens defense-in-depth without changing semantics for correctly-formed diffs). Reuse - SemanticManager::reconcileBoundsForSubtree: replace field-by-field bounds compare with AABB::operator!=. - Delete unused C++ effectiveChecked/effectiveMixed helpers from semantic_state.hpp; rewrite the Mixed-precedence docstring as plain prose. Dart helpers stay (used by the Flutter overlay). Efficiency - SemanticTreeModel.applyDiff: thread a `changed` flag through all six mutation loops so no-op diffs (identical payload) no longer bump version or notify listeners. New unit test pins the behaviour across updatedSemantic, updatedGeometry, and childrenUpdated. - SemanticManager::refresh() incremental path: when containerBoundsChangedIds is empty (common case), skip building the boundsDirtyUnion hashset and check m_dirtyBoundsNodes directly. - deriveLabelVisit: introduce LabelDerivationScratch struct owned at the top-level call and clear()-ed between interactive nodes, so per-node set and string allocations are reused across the walk. Comment hygiene - Strip narration / session / what-describing comments across semantic_manager.cpp, semantic_data.cpp, state_machine_instance.cpp, rive_semantics_overlay.dart, and rive_binding.cpp. - Trim the speculative 32-bit Dart FFI sizing note and the redundant inline "see precedence note below" breadcrumb on SemanticState. * chore(semantics): apply /simplify review findings across runtime and FFI Per-frame wins: - Return nullptr from stateMachineSemanticsTakeDiff when the diff is empty so idle frames skip the FFI struct allocation (Dart/WASM already handle null). - Skip markNodeDirty(Bounds) in updateWorldBounds when the computed AABB matches the cached value. - Short-circuit applyInferredSemanticsIfNeeded when the inferred role+label are unchanged. - Guard every *Changed() handler (role/label/stateFlags/traitFlags/value/hint/headingLevel) so same-value writes don't dirty the node. - Drop ComponentDirt::Paint from the visibility re-sync; opacity does not affect tree membership (per shouldExcludeFromSemanticTree). - toSemanticNodeFFI emits nullptr for empty value/hint instead of a 1-byte "\0" buffer each. Reuse: - SemanticDirt now uses RIVE_MAKE_ENUM_BITSET; hand-rolled |, &, and hasSemanticDirt removed. - Replace hand-rolled "find first child of T" loops with ContainerComponent::firstChild<T>() (semantic_data.cpp, semantic_manager.cpp, focus_data.cpp, semantic_provider.cpp). - Replace two hasChildBounds two-branch merges with AABB::expand(). - Dart _mapToWidgetRect uses AABB.transform() (four-corner transform) — also a correctness fix under rotation/skew. - Dart _listEquals -> foundation.listEquas. Cleanup: - Delete dead m_dirtyStructureNodes field (written, never read). - Fix SemanticsChildrenUpdateFFI.childCount from @Int32 to @Uint32 to match the C++ uint32_t. - Trim the inference registry: drop redundant null-checks on table entries, drop per-rule type re-check inside the infer fn, drop the POD struct constructor. Registry shape preserved for upcoming rules. - Remove a commented-out dropdown_list demo line. * chore: clang clank * chore: address copilot review comments * chore(semantics): update core definitions after merge * chore: update to new enum util functions after merge * chore: updated .riv files after merge new prop keys * chore: clang clank * chore: remove accessibility_tools package * test(semantic): expanded c++ test suite * test: simpsons list filtering test * test(semantic): fixed list scroll focus * chore: clang clank * fix: clang * chore: test cleanup * chore: cleanup before merge * wip: tmp integration guides * fix(semantics): tear down old painter when RiveSemanticsWidget swaps painter/artboard * fix(semantics): gate Text inference registry on WITH_RIVE_TEXT * docs(semantics): clarify that semanticBounds walks the full subtree * feat(rive_native): expose frame/tree debugger * perf(rive_native): hoist artboard→widget transform onto a single SemanticsNode * refactor(flutter): rename public API to 'drainSemanticsDiff' and 'focusSemanticNode' * refactor!(semantics): rename 'takeDiff' to 'drainDiff' * chore(editor): use uistrings for semantics * feat: Add drainSemanticsDiff operation to command queue (#12247) * fix: Use new drainDiff function * chore(flutter): rename ffi bindings to match C++ names exactly * feat(editor): show warning on role change * chore(semantics): change warning icon * chore: cleanup * chore: readd lost files after merge * chore: clang formating * docs: update guides on latest changes * fix(command-queue): consistent semantic error handling + harden drain * fix(command-queue): lock per-state-machine mutex for semantic commands * perf(command-queue): move semantics diff to global listener when sole recipient * fix(command-queue): map semantics bounds with the real transform, not bounds-equality) * chore: per-state-machine lock now covers only what needs the instance and comment cleanup * chore: more comment cleanup * chore: move docs to semantics subfolder Co-authored-by: David Skuza <david@rive.app> Co-authored-by: Erik <erik@rive.app> Co-authored-by: Gordon <pggordonhayes@gmail.com> Co-authored-by: Hernan Torrisi <hernantorrisi@gmail.com>

Rive's C++ runtime — the lowest-level Rive runtime. Loads .riv files, advances state machines and animations, and draws via the abstract Renderer interface. The built-in GPU renderer (RiveRenderer) has RenderContextImpl backends for Metal, Vulkan, D3D11, D3D12, and OpenGL/WebGL.
Rive's Apple, Android, Flutter, Unity, Unreal, and web runtimes all wrap this library.
Features:
.riv files.Artboard::advance.Renderer interface for hooking up an external vector renderer.xcode-select --install).apt install clang).premake5 on first run. On Windows, install Git for Windows and during setup pick “Use Git and optional Unix tools from the Command Prompt” so that sh.exe ends up on PATH.The build is driven by premake5 wrapped by a helper script:
build/build_rive.sh — macOS / Linux (bash) / Windows (MinGW).build/build_rive.ps1 — a PowerShell convenience wrapper for Windows; it shells out to the bash script, so a bash environment (Git for Windows / MinGW, see Prerequisites) must still be on PATH.The helper self-installs the pinned premake version on first run and dispatches to the right build system for your platform (gmake2 on macOS/Linux, MSBuild on Windows). It must be run from a directory that contains a premake5.lua — typically tests/, which builds the core library, the GPU renderer, and the player sample app.
git clone https://github.com/rive-app/rive-runtime.git cd rive-runtime/tests ../build/build_rive.sh release
git clone https://github.com/rive-app/rive-runtime.git cd rive-runtime\tests ..\build\build_rive.ps1 release
(On Windows, substitute build_rive.ps1 for build_rive.sh.)
build_rive.sh (no args) — debug build for the host.build_rive.sh release clean — clean then build release.build_rive.sh ninja release — use Ninja instead of make/MSBuild.build_rive.sh ios release — cross-compile for iOS.build_rive.sh android release — cross-compile for Android (defaults to arm64).build_rive.sh ninja release wasm — cross-compile for WebAssembly.build_rive.sh --toolset=msc release (Windows) — build with MSVC's cl.exe instead of clang-cl. The build supports both toolchains; the lua warning suppressions cover MSVC too.See the comment header at the top of build/build_rive.sh for the complete flag reference.
Artifacts land in out/<config>/ relative to the directory you built from (typically tests/). The config directory encodes any OS/arch flags you passed:
out/release, out/debug — host build.out/ios_release, out/android_arm64_release, out/wasm_release — cross-compile builds.| Artifact | Description |
|---|---|
librive.a / rive.lib | Core runtime library. |
librive_pls_renderer.a / rive_pls_renderer.lib | GPU renderer. |
player / player.exe | Sample app — loads and renders a .riv file. |
out/<config>/goldens, out/<config>/gms, out/<config>/bench | Test harness binaries (regression, golden-image, benchmarking). |
librive_decoders.a, librive_harfbuzz.a, … | Supporting libraries. |
The runtime's primary form of testing is golden testing — rendering known scenes and diffing the output against checked-in reference images via the goldens and gms test harness binaries (built into out/<config>/goldens and out/<config>/gms). See tests/ for how to run and rebaseline goldens.
Unit tests are secondary and use the Catch2 framework. From the repo root:
cd tests/unit_tests ./test.sh
Unit tests live in tests/unit_tests/runtime/ (core runtime) and tests/unit_tests/renderer/ (renderer). To add a test, create an xxx_test.cpp file in the appropriate directory — the harness picks it up automatically.
rive-runtime uses clang-format.
brew install clang-format.clang-format.exe alongside clang-cl.exe at C:\Program Files\Microsoft Visual Studio\2022\<edition>\VC\Tools\Llvm\x64\bin\. You can also install LLVM standalone from llvm.org or via winget install LLVM.LLVM.apt install clang-format).To run the tests under macOS's built-in leaks tool:
cd tests/unit_tests ./test.sh memory
This wraps the test binary with leaks --atExit, which ships with macOS — no install required. The memory flag is ignored on Linux and Windows.
To inspect generated assembly per cpp file, install the Disassembly Explorer VSCode extension. A disassemble task is provided in .vscode/tasks.json. The underlying gen assembly task invokes clang++ directly, so it needs clang++ on PATH — works out of the box on macOS (Xcode CLI tools) and most Linux distros, but not on a default Windows + VS install (which provides clang-cl.exe, not clang++.exe).
Reach the task from Tasks: Run Task, or bind a key in keybindings.json:
[ { "key": "cmd+d", "command": "workbench.action.tasks.runTask", "args": "disassemble" } ]