Skip to content

rive_native 0.2.0-dev.3: dispose clears session state before detach, retaining screen-target recorders #660

Open

Description

@Linaks

Submission checklist

  • I have confirmed the issue is present in the latest version of the rive Flutter package
  • I have searched the documentation and forums and could not find an answer
  • I have searched existing issues and this is not a duplicate

Description

The published rive_native 0.2.0-dev.3 source contains an unreachable deferred-session detach in _NativeRenderTexture.dispose() (lib/src/rive_native_ffi.dart, around line 203).

_rendererPtr is set to nullptr and _appliedSessionAddress to 0 before the conditional that calls _textureUseDeferredSession(_rendererPtr, nullptr). That conditional can therefore never succeed.

For a rendered texture attached to a deferred session that remains alive, normal disposal falls through to delayed native host destruction. DeferredTextureHost::~DeferredTextureHost() calls detach(this) but intentionally does not release the screen target. The normal release path is useDeferredSession(nullptr) -> leaveDeferredSession() -> releaseScreenTarget().

Consequently, the session retains the target's entry in m_screenRenderers, its target ID is not returned to m_freeScreenTargets, and attachedTargetCount() can stay overstated as textures are replaced.

Expected behavior: detach on the recording thread while the renderer/session state is still valid, using the existing native drain-and-release path, then clear the Dart pointer and session marker before scheduling platform removal.

This is a source-level report. I confirmed the control-flow defect in the unmodified published archive and checked the native ownership path; I am not claiming a new device reproduction, quantified memory growth, or an OOM/ANR caused by this particular defect.

Reproduction steps / code

Source-level reproduction:

  1. Download the exact published archive: https://pub.dev/api/archives/rive_native-0.2.0-dev.3.tar.gz
  2. Inspect _NativeRenderTexture.dispose() in lib/src/rive_native_ffi.dart:
_disposed = true;
_rendererPtr = nullptr;
_appliedSessionAddress = 0;
// listener removal and attachment bookkeeping
if (_appliedSessionAddress != 0 && _rendererPtr != nullptr) {
  _textureUseDeferredSession(_rendererPtr, nullptr);
}
_rendererPtr = nullptr;
_appliedSessionAddress = 0;

The detach guard is always false, regardless of the state when dispose began.

  1. Inspect native/src/deferred_host.cpp: the destructor only calls detach(this); leaveDeferredSession() calls releaseScreenTarget(m_screenTarget). In runtime/renderer/include/rive/renderer/cmd/deferred_session.hpp, that release erases the recorder and recycles the target ID.

Minimal proposed fix: remove the first _rendererPtr = nullptr and _appliedSessionAddress = 0 assignments. Keep the guarded detach and the later assignments, so native drain/release runs on the recording thread before Dart drops the addresses.

A downstream patch using this ordering matches the original archive and is idempotent. Runtime validation should repeatedly create, draw and dispose textures sharing a surviving deferred session and check that target/recorder counts return to baseline. That device validation has not been performed for this report.

Upload your reproduction files / stack trace

No response

Source .riv / .rev file

No response

Screenshots / video

No response

Rive Flutter package version

rive 0.15.0-dev.3 / rive_native 0.2.0-dev.3 (latest prerelease)

Flutter version

Flutter 3.47.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 6a19cca564 (13 days ago) • 2026-09-17 14:13:22 -0400
Engine • hash ab598368592da0064197e2bc15c7f5b0a2c6bb1f (revision af7e796e16) (13 days ago) • 2026-09-16 18:35:09.000Z
Tools • Dart 3.13.4 • DevTools 2.60.0

Device

Apple Silicon Mac: source inspection and patch-tool tests only; no new device reproduction

OS version

macOS 27.0 (inspection host); shared native deferred-texture path

Additional context

Verified against the unmodified published archive, SHA-256: 0acff64554fc0973a6624daa0b0c0261ec00cde744df4b9e3daea2fa8a7b85a8.

The minimal reorder uses the existing native synchronization. Synchronous detach can still wait for queued GPU work; this report does not establish its device performance.

The latest prerelease versions were checked on 2026-09-30; this report concerns the exact dev.3 source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    fixed-pending-releaseFixed and merged; will close when it ships

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions