Skip to content

0.5.0-pre.5 - #226

Merged
nmfisher merged 129 commits into
masterfrom
develop
Aug 9, 2026
Merged

0.5.0-pre.5#226
nmfisher merged 129 commits into
masterfrom
develop

Conversation

@nmfisher

@nmfisher nmfisher commented Aug 9, 2026

Copy link
Copy Markdown
Owner

No description provided.

nmfisher and others added 30 commits July 14, 2026 14:00
🤖 Generated with GitHub Actions
These are needed because we cannot use `dart test -p chrome` to run tests.
The web_test_runner.dart script sets up the correct proxy + WASM bootstrap.

Run web tests (from thermion_dart/):

  dart run tool/web_test_runner.dart \
      --assets=../examples/assets test/some_tests.dart

- tool/web_test_runner.dart: single entry point that generates a
  per-file HTML test wrapper (loads the multithreaded Filament WASM
  module), starts the COI proxy, runs `dart test -p chrome`, and
  prints a per-file pass/fail summary.
- tool/coi_proxy.dart: intercepting proxy that injects COOP/COEP/CORP
  headers so the browser is cross-origin isolated and exposes
  SharedArrayBuffer, which the pthreads Filament build requires.
- dart_test.yaml: configures the chrome platform used by the runner
  (headful + --proxy-server routing through coi_proxy on port 8899)
  and a 120s per-test timeout. Without it the runner's `dart test
  -p chrome` launches headless Chrome with no proxy and the WASM
  tests fail for lack of SharedArrayBuffer.
🤖 Generated with GitHub Actions
…yet.

- gizmo_tests.dart, projection_tests.dart,
  view_dependent_texture_mapping_tests.dart, and the 'unlit fixed size
  material' test in unlit_material_tests.dart reference APIs that were
  removed or left as `throw Exception("TODO")` by the refactor
  (GizmoAsset decoupled from ThermionAsset with no add/remove API;
  TextureProjection.create unimplemented; setImage3D /
  getProjectedPixelBuffer / getColorBuffer removed; createUnlitFixedSize
  materialInstance removed; setCameraPosition removed). These can't be
  made to pass until that work is finished, so their bodies are
  preserved as block-commented reference with TODO(c9b41bd) headers
  and main() left as a no-op so dart analyze is satisfied.
🤖 Generated with GitHub Actions
dart analyze fails CI on warnings by default (--fatal-warnings is on),
but the repo has ~390 long-standing warnings (unused vars/imports, long
lines, etc.) under CI's 3.12.2 analyzer — and ~82 under the local
3.13-dev toolchain — so it has never been warning-clean under any SDK,
and warning counts differ across versions (a treadmill). The codebase
is error-clean, so gate on errors only:

- lint.yml: `dart analyze --no-fatal-warnings` (errors still fail).
- analysis_options.yaml: exclude the generated FFI/JS-interop bindings
  (thermion_dart_ffi.g.dart / thermion_dart_js_interop.g.dart) from
  analysis — linting ffigen output is noise (dozens of unnecessary_cast).

This is the right policy for a repo that's never been lint-clean;
chasing version-specific warnings isn't worth it.

Co-Authored-By: Claude <noreply@anthropic.com>
Note we longer use melos (this was originally chosen
because we needed separate packages for each Flutter plugin
but this is no longer the case).

Also, thermion_dart & thermion_flutter will now be versioned in lock-step.
…evant for local development) and pin to current version.
🤖 Generated with GitHub Actions
Engine_createRenderer existed but had no matching destroy in the C API,
even though filament::Engine::destroy(Renderer*) is a documented public
API (Engine.h:950). Adds:

- Engine_destroyRenderer(TEngine*, TRenderer*) — sync, mirrors the other
  Engine_destroy* helpers in TEngine.cpp.
- Engine_destroyRendererRenderThread(TEngine*, TRenderer*, requestId,
  VoidCallback) — render-thread variant that proxies completion back to
  the calling thread, matching the pattern of Engine_destroyView /
  Engine_destroyScene / Engine_destroySwapChain.
stbi_load_from_memory returns a malloc'd buffer we own. LinearImage
above copies/converts from it; release the intermediate before returning
so it doesn't leak per decode (significant on repeated PNG decodes).

Also delete the LinearImage on a failed decode so it isn't leaked from
the error path.
generateMipmaps() requires BLIT_SRC | BLIT_DST per Filament's
Texture::generateMipmaps doc. Native backends often relax this, WebGL
does not (throws PreconditionPanic). Include the flags whenever the
caller asks for >1 level so the mip path works on every backend.
WebGL/ANGLE constrains the format/type combo for readPixels by the bound
framebuffer's color format, but swapchain vs. FLOAT render target differ.
Add a native Texture_getFormat() so capture() can keep FLOAT reads from a
FLOAT color attachment and only downgrade to UBYTE for RGBA8.

- native Texture_getFormat() in TTexture.cpp/.h returning InternalFormat
- FFITexture.getFormat() now calls native instead of throwing
- getFormat exposed through FFIFilamentApp for per-view readback
Ktx2Reader internally calls Texture::Builder().build, which on web must
happen on the engine's render thread; routing through the *RenderThread
variant avoids the abort.

- Ktx2Reader_createTextureRenderThread in ThermionDartRenderThreadApi.cpp/.h
- FFIFilamentApp.loadKtx2 awaits the render-thread variant
Adding Texture_getFormat + Ktx2Reader_createTextureRenderThread to the
native headers (via the capture/ktx2 fixes) caused ffigen to emit
RenderManager_setPaused and NativeLibrary into both ffi.g.dart and
js_interop.g.dart. thermion_flutter_plugin_web.dart pulls js_interop
directly and ffi.g.dart transitively via thermion_flutter.dart, so those
symbols collided (4x ambiguous_import).

Hide the two duplicated names from the thermion_flutter.dart re-export;
they are provided unambiguously by the js_interop import.
🤖 Generated with GitHub Actions
Move the frame-scheduling machinery (vsync callback dispatch, port-mode
debug transport, Flutter-synced Linux loop, diagnostic timing) out of
ThermionFlutterPluginImpl's static state and into a dedicated
FrameScheduler singleton (lib/src/platform/src/frame_scheduler.dart).

Pure refactor: the plugin now delegates to FrameScheduler.instance
(stop / start / startFlutterSynced / pause / resume / setOnFrame) and
the resize path uses pause()+isRendering instead of private _resizing/
_rendering flags, but runtime behaviour is unchanged. No lifecycle
listener, no new tests.

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with GitHub Actions
refactor: extract FrameScheduler into its own class
* feat: pause rendering on app background via WidgetsBindingObserver

Register ThermionFlutterPluginImpl as a WidgetsBindingObserver and stop
(or, on Linux, pause) the native FrameScheduler when the app is
backgrounded, restarting it on resume.

Also harden FrameScheduler against two real failure modes uncovered
while adding the listener:
- start()/startFlutterSynced() are now idempotent, so a rapid
  pause->resume can't double-register the callback/port or double-start
  the native scheduler.
- resume() re-arms the Flutter frame callback in Flutter-synced (Linux)
  mode; previously the persistent callback stopped re-arming itself once
  paused, freezing the Linux render loop after the first
  background->foreground transition.
- _onFrame now counts dropped vsyncs (was logged as drop=N but never
  incremented).

Adds an integration test (examples/flutter/quickstart/integration_test/
lifecycle_test.dart) covering the scheduler state machine and lifecycle
recovery, plus a macOS CI step to run it. Builds on the FrameScheduler
extraction in the base branch.
Fix the ordering of format/lint/generate bindings.

Fold format + analyze into Generate Artifacts, after `make dart-bindings`,
so analyze always sees current bindings and only one workflow pushes.

Add a workflow_dispatch `force_regenerate` input to recover a branch
whose checked-in bindings are already stale (the current develop state).
🤖 Generated with GitHub Actions
Previously would hang indefinitely, test synthesized
AppLifecycleState.paused via the binding and then pumped while still
in that state. SchedulerBinding flips framesEnabled=false on paused/
hidden/detached, making scheduleFrame() a no-op; the live
integration-test binding's pump() waits on a frame that never fires,
deadlocking the test until the GHA job ceiling.

Drive the full sequence (inactive->hidden->paused->hidden->inactive->
resumed) so states are distinct (Flutter ignores duplicates) and end
at resumed, which restores framesEnabled and re-arms the frame loop.
Pump only after resumed, while framesEnabled is true.
🤖 Generated with GitHub Actions
…atform compatibility

(cherry picked from commit 9487372)
- test/src/test_io{,_native,_web}.dart: conditional-import shim decoupling
  the harness from dart:io (native keeps File/Directory/Platform; web stubs
  file IO, selects OPENGL backend, calls NativeLibrary.initBindings).
- test/helpers.dart: route all File/Directory/Platform usage through the
  shim (outDirPath is now a String); setup() awaits initTestBindings.
- test/engine_tests.html: package:test custom-HTML wrapper.
- tool/web_test_runner.dart: Chrome test driver (--name / -N passthrough).
- tool/coi_proxy.dart: COOP/COEP proxy for cross-origin isolation.
- native (RenderThread, ThermionDartRenderThreadApi, TGltfAssetLoader,
  TNameComponentManager, TAnimationManager, CMakeLists): render-thread +
  c_api plumbing for the web build.
- test/{view,projection,view_dependent_texture_mapping}_tests.dart,
  viewer_lifecycle_test.dart: test updates for the shim + web paths.
- Replace dart:io File.readAsBytesSync() with loadResourceBytes() so the
  PNG/JPEG/KTX2 reads work under the browser harness (the COI proxy serves
  the bytes via the thermion.assets sentinel host).
- Re-export src/test_io.dart from helpers.dart so tests don't have to
  import the shim directly.
- Switch the generate-mipmaps test to RGBA32F + RGBA + FLOAT + requireAlpha
  so the texture format is color-renderable on WebGL (RGB32F isn't, even
  with EXT_color_buffer_float) and generateMipmaps' preconditions are met.
- Add getFormat round-trip tests for RGBA32F / RGBA16F / RGBA8 / R16F /
  DEPTH32F and a RenderTarget.getColorTexture format-readback test, to lock
  in the new Texture_getFormat binding.
- Document web test runner in README so new contributors
  invoke tool/web_test_runner.dart instead of `dart test -p chrome`
  directly (the wrapper stamps the per-file HTML host and manages the COI
  proxy lifecycle).
github-actions Bot and others added 29 commits August 7, 2026 03:41
🤖 Generated with GitHub Actions
The web multi-viewer feature (b48c421) added `late final int _maxBatch`
assigned in initState from webOptions.maxViewers, and the later "cap to 1
viewer on web" change (971f369) added a second `final int _maxBatch =
kIsWeb ? 1 : 64` without removing the first — a duplicate field declaration
that broke `flutter build windows` (error: '_maxBatch' is already declared
in this scope). Keep the newer cap-to-1 field; drop the stale late-final
declaration and its initState assignment.

Co-Authored-By: Claude <noreply@anthropic.com>
…n check

Releases can no longer ship without all dart + flutter-build checks green:
the publish job now needs both `validate` and a `tests` job that calls
run-tests.yml (only on a real tag push, so manual dry-run dispatch stays fast).

Also fix a latent bug in the already-published check: the bash `want` local
was passed into a single-quoted python -c script that referenced a bare
`want` name (undefined -> NameError -> function always returned "not found").
Pass the version via argv instead.

Co-Authored-By: Claude <noreply@anthropic.com>
The shared root README (symlinked into both thermion_dart and
thermion_flutter) referenced the logo via a relative path
(docs/logo.png). That works on GitHub, but on pub.dev relative
image paths resolve against the *package* root, and docs/ is not
included in either published package — so the logo disappeared
from the pub.dev page.

Restore an absolute raw.githubusercontent.com URL (SHA-pinned so
already-published versions stay stable), matching how the
pre-standardization per-package README referenced the logo.

Co-Authored-By: Claude <noreply@anthropic.com>
Replace the broken user-attachment video link in the top-level README
(also published to pub.dev via the thermion_dart / thermion_flutter
README symlinks) with a clickable YouTube thumbnail-link.

Add examples/dart/cli_headless/bin/render_demo.dart, the headless renderer
that produced the demo: BusterDrone under the the_sky_is_on_fire HDRI with
key/fill/rim point lights, ACES tone mapping, 120fps orbit+dolly over the
full glTF animation, advanced on the render thread via animationManager.

Co-Authored-By: Claude <noreply@anthropic.com>
Switch output resolution and framing with --preset=desktop|iphone.
- desktop: 1280x720 (16:9), unchanged.
- iphone: 1170x2532 (19.5:9 portrait, iPhone 15 Pro point resolution),
  with a larger orbit radius + gentler dolly so the wide drone stays framed
  without clipping the narrow portrait frame.
Each preset renders into its own output/frames_<preset>/ folder so they
coexist, and all framing (radius/height + their amplitudes) is per-preset.
Also adds --no-skybox to render the subject against black (keeping the IBL)
for verifying framing/silhouette without a busy background.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
EOF && \
echo "=== pushing ===" && git push origin develop 2>&1
* fix(animation): dispatch setGltfAnimationTime on the render thread
* fix: correct procedural geometry winding and sphere poles

* fix: preserve WebAssembly native output buffers

* fix(web): enable bloom options
Drop the `push: branches: [develop]` trigger from deploy.yml so Cloudflare
deploys fire only on a release tag (v*) or manual dispatch — not on every
commit to develop.

Co-authored-by: Claude <noreply@anthropic.com>
…rkflow

- publish-pub-dev: publish now waits on verify-artifacts and
  verify-swift-bindings gates plus the test suite (needs chain), so a
  release can never ship stale generated bindings or red tests
- generate-artifacts / generate-swift-bindings: gain a reusable
  workflow_call verify mode (regenerate + fail on stale diff); no longer
  self-trigger on v* tags, which previously tried to move the tag and
  landed nowhere
- release.yml (new): dispatch-driven release entry point - validates the
  version against both pubspecs, runs the verify gates + test matrix, then
  creates and pushes the annotated v<version> tag via the RELEASE_TOKEN
  PAT (GITHUB_TOKEN tag pushes don't trigger workflows)
- docs/RELEASING.md: runbook rewritten for the new flow + RELEASE_TOKEN
  one-time setup

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@nmfisher
nmfisher merged commit 4b5de4a into master Aug 9, 2026
1 of 6 checks passed
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.

4 participants