Skip to content

fix(linux): harden EGL texture ownership - #262

Open
nmfisher wants to merge 2 commits into
developfrom
fix/linux-egl-ownership-hardening
Open

fix(linux): harden EGL texture ownership#262
nmfisher wants to merge 2 commits into
developfrom
fix/linux-egl-ownership-hardening

Conversation

@nmfisher

@nmfisher nmfisher commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up ownership hardening for the Linux EGL bootstrap and DMA-BUF transport introduced by #273, rebased onto the timing-only scheduler refactor from #278.

  • selects the DRM render node backing Flutter's captured EGLDisplay instead of assuming renderD128
  • retains every deferred texture-readiness call and cancels or resolves each call safely
  • releases Flutter GL/EGL consumer resources before OpenGL or Vulkan producer contexts
  • unreferences texture objects on registrar failures and during normal teardown
  • clears the exported plugin instance before texture/context disposal begins
  • uses one texture-publication helper for both the platform-channel and direct Dart notifier paths
  • performs NeedsBlit / BlitToExport for Linux Vulkan before either path marks a texture available to Flutter

Multi-viewer lifecycle hardening (3a7b54f)

Follow-up commit addressing scenarios where several ViewerWidgets bootstrap concurrently and every viewer resize destroys and recreates its platform texture:

  • keeps the thermion_flutter_render_* raster-context capture across destroy_all_contexts — it describes Flutter's context, which outlives plugin-owned contexts; resetting it on a bootstrap-only teardown leaked sibling bootstrap GL names and could fail a sibling viewer's pending getDriverPlatform with CONTEXT_NOT_READY. Cleared only in plugin dispose, re-captured by populate() on context change.
  • serializes populate() (raster thread) against release_texture() (platform thread) with a per-texture GMutex. Flutter's Linux registrar lookup hands populate() a raw pointer with no reference and no synchronization with unregistration, so a destroy issued mid-import could close the producer's DMA-BUF fd during eglCreateImageKHR, and pending_ready_calls was mutated from both threads. populate() now bails on destroyed textures.
  • retains the small GObject shell of destroyed textures until plugin teardown instead of unreffing eagerly, so a late populate() can never touch a finalized object or its mutex; all heavyweight native resources are still released eagerly
  • dup()s the DMA-BUF fd into the consumer texture so producer teardown can never invalidate a concurrent import
  • dispatches producer teardown by external_images ownership rather than opengl_context presence — surface ids are per-context counters, so a misroute in a mixed-backend state could destroy an unrelated live surface

Integration with #278

This does not restore the removed native render loop or couple FrameScheduler back to rendering. The exported plugin handle is used only by Dart's post-render Linux texture notifier.

The direct notifier now has the same Vulkan publication semantics as markTextureFrameAvailable: any intermediate Vulkan image is copied to its exported DMA-BUF before Flutter is notified. The plugin handle is cleared before disposal so a late Dart notification cannot acquire an object whose native resources are being torn down.

Validation

  • rebased onto current develop
  • flutter analyze in thermion_flutter: clean (22e1740)
  • full thermion_flutter test suite: 29 tests passed (22e1740)
  • git diff --check: clean
  • full GitHub Actions matrix passed on 22e1740, including Linux native compilation, Linux Dart/OpenGL tests, Linux Flutter builds, Windows, macOS, and Android
  • 3a7b54f (follow-up hardening): Linux native compilation is CI-gated — no Linux toolchain on macOS; flutter analyze unchanged from 22e1740 (no Dart files touched); git diff --check clean

@nmfisher
nmfisher force-pushed the fix/linux-egl-ownership-hardening branch from 8c30601 to 0498cd3 Compare August 22, 2026 12:16
@nmfisher nmfisher changed the title fix(linux): harden EGL texture ownership and fallback fix(linux): harden EGL texture ownership Aug 22, 2026
@nmfisher
nmfisher changed the base branch from feat/linux-same-display-eglimage to develop August 22, 2026 12:17
@nmfisher
nmfisher force-pushed the fix/linux-egl-ownership-hardening branch from 0498cd3 to 74169a6 Compare August 22, 2026 12:22
@nmfisher
nmfisher force-pushed the fix/linux-egl-ownership-hardening branch from 74169a6 to 22e1740 Compare August 23, 2026 08:00
Follow-up hardening motivated by multi-viewer use, where several
ViewerWidgets bootstrap concurrently on Linux OpenGL and every viewer
resize destroys and recreates its platform texture.

- Keep the thermion_flutter_render_* raster-context capture across
  destroy_all_contexts. The capture describes Flutter's context, which
  outlives plugin-owned contexts; resetting it on a bootstrap-only
  teardown leaked every sibling bootstrap's GL name and could fail a
  sibling viewer's pending getDriverPlatform with CONTEXT_NOT_READY.
  The capture is now cleared only in plugin dispose and re-captured by
  populate() whenever Flutter's context differs.
- Serialize populate() (raster thread) against release_texture()
  (platform thread) with a per-texture GMutex. Flutter's Linux
  registrar lookup hands populate() a raw pointer with no reference and
  no synchronization with unregistration, so a destroy issued mid-import
  could close the producer's DMA-BUF fd while eglCreateImageKHR was
  using it, and pending_ready_calls was mutated from both threads.
  populate() now bails on destroyed textures, and destroyed textures'
  GObject shells are retained until plugin teardown instead of being
  unreffed, so a late populate can never touch a finalized object or
  its mutex. All heavyweight native resources are still released
  eagerly.
- dup() the DMA-BUF fd into the consumer texture at creation (closed on
  release), so producer teardown can never invalidate a concurrent
  import independently of the mutex.
- Dispatch producer teardown by external_images ownership rather than
  opengl_context presence. Surface ids are per-context counters, so if
  both producer contexts ever coexist (backend switch without
  destroyContext) the old dispatch could destroy an unrelated live
  surface in the wrong context.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant