Comparing rive-app:main...ElloTechnology:ello/main · rive-app/rive-runtime · GitHub
Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rive-app/rive-runtime
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: ElloTechnology/rive-runtime
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ello/main
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 16 commits
  • 3 files changed
  • 1 contributor

Commits on Apr 10, 2026

  1. threaded scene rendering

    tamirh committed Apr 10, 2026
    Configuration menu
    Copy the full SHA
    ef768fd View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    00af02f View commit details
    Browse the repository at this point in the history
  3. simplifyst

    tamirh committed Apr 10, 2026
    Configuration menu
    Copy the full SHA
    d2d7a80 View commit details
    Browse the repository at this point in the history
  4. event handling

    tamirh committed Apr 10, 2026
    Configuration menu
    Copy the full SHA
    c683e5b View commit details
    Browse the repository at this point in the history

Commits on May 14, 2026

  1. ThreadedScene: fix lost-wakeup race, contain callback exceptions, tig…

    …hten memory ordering
    
    - Add m_wakeFlag protected by m_wakeMutex; producers set it under the
      lock before notify_one(), and threadMain's wait_for now uses a
      predicate that re-checks the flag and m_running. Closes a window where
      a notify between queue drain and wait_for slept for the full 100ms.
    - Wrap runOneFrame body in try/catch. An uncaught exception from the
      render callback (EGL/GL surfaced as exception, bad_alloc, etc.) used
      to call std::terminate via threadMain. Now it sets m_fatalError and
      stops the loop, surfacing through hasFatalError() so callers can fall
      back to a synchronous path.
    - Clear m_pendingSnapshot after std::move. The standard only guarantees
      a moved-from unordered_map is in a 'valid but unspecified' state.
    - Use acquire/release ordering on m_running and m_fatalError so the
      background thread's view of companion state is consistent with the
      stop and fatal signals.
    tamirh committed May 14, 2026
    Configuration menu
    Copy the full SHA
    457fa43 View commit details
    Browse the repository at this point in the history
  2. ThreadedScene: combined acquireFrame for snapshot + events under one …

    …lock
    
    Replaces the lock-free m_outputQueue with m_pendingEvents (bg-only) plus
    m_readyEvents (protected by m_cachedImageMutex). The bg thread's end-of-cycle
    swap now moves snapshot, image, AND events into the shared slot in a single
    lock acquisition, so the UI thread observes a coherent triple from the same
    cycle — closing the temporal gap where event A and the snapshot reflecting
    A's transition could be acquired one bg cycle apart.
    
    - acquireFrame(snapshot, events): new method that reads both under
      m_cachedImageMutex once. Replaces the previous two-call pattern.
    - pollReportedEvents still works; now drains m_readyEvents under the same
      mutex (compat with existing callers).
    - collectReportedEvents pushes into m_pendingEvents instead of the queue.
    - runOneFrame's swap block extended to move m_pendingEvents into
      m_readyEvents alongside the snapshot/image swap.
    - m_outputQueue removed.
    tamirh committed May 14, 2026
    Configuration menu
    Copy the full SHA
    7685b62 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    78471e4 View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    f784a38 View commit details
    Browse the repository at this point in the history

Commits on May 15, 2026

  1. COR-4027: split [vmi-null]/[prop-null] in threaded scene VM input wri…

    …tes + backport -fno-exceptions comment
    
    The threaded scene's logMissing string for setViewModelEnum/Number/Bool/String
    /fireViewModelTrigger conflated three failure modes:
      1. m_viewModelInstance == nullptr  (worker never got a VMI)
      2. propertyEnum(name) returned null (property doesn't exist on the VMI)
      3. wrong type cast (the rive-native instantiation handles this)
    
    Split the prefix into `[vmi-null]` and `[prop-null]` so the worker-side logs
    disambiguate root cause without affecting the happy path. The original COR-4027
    investigation traced a Dart-side widget unmount race that was leaving
    m_viewModelInstance null on the worker; the conflated message made the
    diagnosis substantially harder.
    
    Also backports a 4-line comment block in runOneFrame describing the Android
    -fno-exceptions configuration. The Ello-vendored copy in rive-native had this
    note but it was missing here; reconciles the editorial drift before further
    cherry-picks.
    
    The corresponding fix landed in ElloTechnology/rive-native@ello-2026-05-review
    (commit 31a5416) and ElloTechnology/rive-flutter@ello-2026-05 (commit f549115).
    tamirh committed May 15, 2026
    Configuration menu
    Copy the full SHA
    85c9b20 View commit details
    Browse the repository at this point in the history

Commits on May 19, 2026

  1. COR-3538: ThreadedScene rapid-fire trigger regression test

    Verifies the threading layer preserves rapid-fire fireViewModelTrigger
    calls across the unbounded ThreadedEventQueue and survives concurrent
    fires from a non-main thread (mirrors CharacterRig._onWorkerPost dispatch).
    
    Two bursts:
      1. Single-threaded N=256 fires queued before any postElapsedTime.
         applyInputEvents() drains all 256 in one pass; no silent drop.
      2. Concurrent fires from a separate thread while the worker advances.
         Exercises queue-mutex contention with the advance loop.
    
    Inline comment documents the Rive runtime semantic that one advance
    consumes a trigger at most once (rising-edge), so N rapid fires within a
    single advance window typically drive at most one transition. That is
    intentional Rive behavior, not a threading-layer drop, and is benign for
    the rig (fidget, gesture-react, drawing-end are one-shot triggers).
    
    Note: this test (and every other background-thread ThreadedScene test —
    e.g. "renders on background thread", "ViewModel concurrent stress")
    currently SEGVs in the unit_tests harness on macOS. The crash is
    pre-existing and unrelated to this test — the harness build of
    threaded_scene.cpp was broken from the try/catch in 457fa43 until
    85c9b20 unblocked it, so no background-thread test in this file has
    been exercised in CI/local until now. Tracked separately for fork debug.
    tamirh committed May 19, 2026
    Configuration menu
    Copy the full SHA
    5087075 View commit details
    Browse the repository at this point in the history
  2. ThreadedScene: remove dead try/catch path and m_fatalError

    The try/catch in runOneFrame was gated by
    #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
    so it could compile under -fno-exceptions. Every build that consumes
    this code is -fno-exceptions (Flutter Android, unit_tests harness), so
    all three macros evaluate to false and the catch never compiles in.
    m_fatalError was only set inside that dead catch, so it can never
    become true in any shipping configuration.
    
    Fatal-error reporting is already done correctly by callers via the
    render-callback return value: the Android binding's lambda detects
    EGL/GL failure (clear/makeRenderer/flush) and sets its own atomic
    before returning nullptr. Application code reads that atomic through
    the binding-level hasFatalError(), not the scene-level one.
    
    Replace the gated try/catch with a comment explaining where fatal-error
    reporting actually flows, and drop ThreadedScene::m_fatalError +
    ThreadedScene::hasFatalError(). If a future build configuration enables
    exceptions, route fatal-error reporting through return values rather
    than reintroducing a catch-all here.
    tamirh committed May 19, 2026
    Configuration menu
    Copy the full SHA
    2ecfca4 View commit details
    Browse the repository at this point in the history

Commits on May 20, 2026

  1. COR-3538: ThreadedScene self-paced bg loop (targetFrameIntervalUs)

    Adds Config::targetFrameIntervalUs so the worker can render at an
    internally-paced rate using steady_clock dt instead of waiting on
    postElapsedTime from an external Ticker. When > 0:
      * threadMain waits up to targetFrameIntervalUs (vs the legacy 100 ms)
        and the predicate ignores m_wakeFlag — postElapsedTime can't
        short-circuit the interval and pin bg to the UI ticker rate.
      * dt is computed from steady_clock between cycles. Any
        m_accumulatedTime posted by legacy callers is still folded in so
        paused/resumed transitions don't double-count. dt is capped at
        250 ms so a long background pause doesn't teleport the SM forward.
      * runOneFrame runs every cycle (even 0-dt) so the bg path always
        produces a fresh frame the compositor can pick up after the
        binding-specific wake (e.g. Android's SurfaceProducer.scheduleFrame).
    
    When 0 (default), behavior is unchanged from the previous loop. Input
    events still wake the worker in both modes via the existing
    m_inputQueue path; self-paced just defers them by at most one interval
    boundary (≤ 16 ms at 60 Hz).
    
    Validated end-to-end via the Android binding in ElloTechnology/
    rive-native (PR-equivalent ello-2026-05-review @ bc9e884): bg loop
    back-pressures to actual GPU capability instead of being rate-locked
    to the Dart Ticker. Tier-1 Infinix vsync_p95 dropped 100.4 → 55.6 ms
    median (Ello client bench).
    tamirh committed May 20, 2026
    Configuration menu
    Copy the full SHA
    b7a35e7 View commit details
    Browse the repository at this point in the history
  2. Merge tamir/threaded-scene-self-paced-loop into ello/main

    ThreadedScene::Config::targetFrameIntervalUs (Patch E) — lets bindings
    configure a self-paced bg render rate instead of being driven by an
    external Ticker. Already deployed and validated through
    ElloTechnology/rive-native ello-2026-05-review @ bc9e884.
    tamirh committed May 20, 2026
    Configuration menu
    Copy the full SHA
    7f843b7 View commit details
    Browse the repository at this point in the history
  3. COR-3538: ThreadedScene push-not-poll + cycle counters + zero-size guard

    Brings rive-runtime's ThreadedScene up to the feature set that
    ElloTechnology/rive-native has been carrying in its vendored copy
    (runtime/), so the next `make update_cpp_runtime` rsync from rive-
    runtime to rive-native preserves the work instead of regressing it.
    
    Adds to Config:
      * externalCycleOutputFlag — pointer to an optional atomic<bool> the
        binding owns. ThreadedScene writes "this cycle published Dart-
        visible output" to it BEFORE invoking the render callback. The
        Android binding uses this to gate Dart_PostInteger_DL pushes so
        quiet animation cycles (worker advanced but no event queued, no
        snapshot diff) don't trigger spurious UI-thread wakes.
    
    Adds to public API:
      * advanceCount()  — total runOneFrame cycles
      * renderedCount() — cycles that produced a non-null RenderImage
      * lastCycleProducedOutput() — for callbacks that need the flag value
        after the swap on the same bg-thread cycle
    
    Adds inside runOneFrame:
      * Compute producedDartVisibleOutput from m_pendingEvents +
        m_pendingSnapshot diff vs the previous snapshot, publish to the
        internal atomic and the external pointer BEFORE the render
        callback runs.
      * Zero-size guard: when the surface is 0×0, skip the render and
        log a rate-limited warning (once per ~60 cycles, ~1s at 60 Hz)
        via the existing m_logWarning hook.
      * Bump m_advanceCount on every cycle and m_renderedCount when the
        callback produced an image.
    
    These were carried as drift in rive-native's vendor; this lands them
    in the canonical source. No behavioral change for callers that don't
    set externalCycleOutputFlag or read the new counters.
    tamirh committed May 20, 2026
    Configuration menu
    Copy the full SHA
    7d460fd View commit details
    Browse the repository at this point in the history
  4. Merge tamir/threaded-scene-push-not-poll into ello/main

    Brings the push-not-poll, cycle-counter, and zero-size-guard features
    into the rive-runtime canonical source. They were already deployed via
    ElloTechnology/rive-native's vendored runtime/ (ello-2026-05-review
    @ bc9e884); this commit lands them upstream so the next
    `make update_cpp_runtime` doesn't regress them.
    tamirh committed May 20, 2026
    Configuration menu
    Copy the full SHA
    4ceb7d8 View commit details
    Browse the repository at this point in the history

Commits on May 29, 2026

  1. Sync threaded_scene.cpp comments to shipping vendored copy (COR-4254)

    Comment-only: the rive-native vendored copy carried the newer predicate-semantics, dt-computation, and -fno-exceptions notes; bring editorial up to match. No code change; compiled output unaffected.
    tamirh committed May 29, 2026
    Configuration menu
    Copy the full SHA
    911aea3 View commit details
    Browse the repository at this point in the history
Loading