Runtime material compilation & hot-reload (Phases 0-1) - #252
Open
local-sandbox-agent[bot] wants to merge 6 commits into
Open
Runtime material compilation & hot-reload (Phases 0-1)#252local-sandbox-agent[bot] wants to merge 6 commits into
local-sandbox-agent[bot] wants to merge 6 commits into
Conversation
Maps the current matc/resgen build-time pipeline and runtime createMaterial path, verifies Filament v1.75.0's runtime compile stack (matp + filamat::MaterialBuilder) against thermion's own R2 artifacts (end-to-end .mat -> filamat proof on Linux), and proposes a phased plan: reload-only MVP, desktop runtime compile, then Android/iOS artifact work. Research only; no code changes. Co-Authored-By: Claude <noreply@anthropic.com>
Add reloadMaterialFromBytes + findRenderablesUsingMaterial to FilamentApp, per docs/research/runtime-material-compile.md section 4.1. Filament cannot re-parent a MaterialInstance to a different Material, and instances are write-only (no parameter getters). So: - FFIMaterialInstance now records every setParameter*/raster-state call in a MaterialInstanceShadowState, and applyTo() replays the recording onto a fresh instance. - Scene_scanForMaterial (new C API) walks each scene's renderables and reports the primitives whose instance belongs to a material. MaterialInstance_getMaterial (new C API) exposes instance->material. - FFIFilamentApp.reloadMaterialFromBytes builds the new material, creates one replacement instance per distinct old instance, replays recorded state, re-points primitives, then destroys the old instances and material on the render thread. - findRenderablesUsingMaterial unions the native scene scan with an app-level attachment registry (records from setMaterialInstanceAt) so renderables not yet in a scene are still found; stale records are pruned by re-validating against the renderable's current instance. - MaterialInstance wrappers are cached per native pointer so repeated getMaterialInstanceAt calls return the same wrapper and keep the shadow recording intact; destroy() is idempotent and unregisters. Tests: test/material_reload_tests.dart (scan, replay + pixel check, instance-sharing preservation, destroyOld=false ownership). Co-Authored-By: Claude <noreply@anthropic.com>
Implements Phase 1 of docs/research/runtime-material-compile.md: compile
.mat sources to .filamat packages inside the running engine, so the
Phase 0 reload path can be driven by source edits without matc.
Native (desktop builds only, guarded by THERMION_RUNTIME_MATERIAL_COMPILE):
- Engine_compileMaterial: matp::MaterialParser::parse feeding
filamat::MaterialBuilder::build(engine->getJobSystem()).
MaterialBuilder::init() runs once behind a mutex (glslang init is not
thread-safe). Platform/targetApi/optimization are validated and mapped
to the builder enums; targetApi can be derived from the engine backend
via filamat::targetApiFromBackend; defines arrive as a flat JSON object
parsed in C++. Every other platform gets a stub that fails with an
explanatory message.
- Engine_compileMaterialRenderThread follows the readPixels idiom: the
task fills outData/outSize then notifies the Dart callback.
- utils/JobSystem.h is not shipped in the artifact headers; a forward
declaration replaces the include.
Link config: matp added to the link libraries on linux/macOS (before
utils, which it references); on Windows via #pragma comment(lib) in
ThermionWin32.h, including the glslang/SPIRV/spirv-cross libs that the
Windows filamat/matp do not bundle.
Dart:
- FilamentApp.compileMaterial(matSource, {platform, targetApi,
optimization, defines, includePaths, embedSource}) plus the
MaterialCompilePlatform/MaterialTargetApi/MaterialOptimization enums
and MaterialCompileException. Web throws UnsupportedError.
- #include directives are flattened in Dart before compiling (matp has
no include callback): resolved against includePaths then the bare
path via the resource loader, with circular/over-deep chain detection.
- FFIMaterial.destroy now retires its created instances first — a
material destroyed with live instances strands the render thread.
- ffigen bindings regenerated (both flavors).
Tests (material_compile_tests): golden compile of every material
build.sh ships (11 workspace + 3 example .mat) accepted by
Material::Builder with parameters intact; parse errors surface the
compiler message; explicit target API/optimization selection; defines
reach the preprocessor; nested/missing/circular include handling; and
an end-to-end compile -> attach -> render -> recompile -> hot-swap
loop. materials/vdtm.mat is excluded: its source is broken ('z'
declared inside an if block) and it is not in the shipped set.
Verified in container (linux): new suites + reload/material smoke
suites pass; flutter analyze reports no new issues; libthermion_dart.so
exports the compile API and links the filamat/matp/glslang stack
(94 exported filamat/matp symbols, 2695 in symtab). macOS/Windows link
config and the web stub are untested here — CI must confirm those
platforms build.
local-sandbox-agent
Bot
force-pushed
the
asb/runtime-material-compile
branch
from
August 23, 2026 08:52
3bc1f42 to
fb09f4a
Compare
The research plan served its purpose as the implementation blueprint; keeping it out of the tree keeps the PR scoped to the runtime material compile feature itself. (Local copy retained outside git.)
…-reload) examples/flutter/material_editor: a reference app for the Phase 0/1 APIs. A sphere renders in a ViewerWidget; below it, a TextField holds Filament .mat source. Debounced edits (or the Apply button) run FilamentApp.compileMaterial in the running engine and hot-swap the package onto the sphere via reloadMaterialFromBytes, which replays instance parameters (baseTint/roughness/metallic survive every swap). MaterialCompileException messages are shown inline under the editor. Ships materials/starter.mat (lit, three parameters) so the app opens with an editable material that renders immediately; the starter and the README's suggested edits were verified to compile through the runtime API. Non-desktop platforms keep running with the default material and an explanatory status, matching where the compiler is linked. flutter analyze: clean. flutter build linux --debug: builds; the container has no GPU (/dev/dri absent) so the render context cannot start there — verified on-screen behavior needs a real machine. Co-Authored-By: Claude <noreply@anthropic.com>
The PR's auto-merge branch carries a CI commit that ran dart format over the new material files and regenerated the ffigen bindings (same symbol set, different declaration order). Take those exact versions so the pushed head matches what CI already produced and the next CI run has nothing to reformat. Verified after adopting: flutter analyze at the 48-issue baseline, material_compile_tests + material_reload_tests 12/12. Co-Authored-By: Claude <noreply@anthropic.com>
local-sandbox-agent
Bot
force-pushed
the
asb/runtime-material-compile
branch
from
August 23, 2026 09:09
31c78c7 to
4645e8e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 0 — reload materials at runtime
FilamentApp.reloadMaterialFromBytes(oldMaterial, bytes)swaps a new compiled material into a running scene.findRenderablesUsingMaterialfinds every primitive that uses a material. It scans the scene natively, plus a registry for renderables not yet attached.Phase 1 — compile materials at runtime
FilamentApp.compileMaterial(matSource, ...)compiles.matsource inside the running engine and returns the.filamatbytes thatcreateMaterialandreloadMaterialFromBytesaccept.Engine_compileMaterial(desktop only) runs the same matc pipeline:matpparse thenfilamat::MaterialBuilder::build. Other platforms get a stub that explains it is not supported; web throws from Dart.#includeflattening is done in Dart. Circular or too-deep includes are errors.matpon linux/macOS, plus glslang/SPIRV/spirv-cross libs on Windows.Example — live material editor
examples/flutter/material_editoris a reference app for both phases: a sphere renders in aViewerWidget, and a text field below holds.matsource.compileMaterial, thenreloadMaterialFromByteshot-swaps the package onto the sphere — instance parameters (baseTint/roughness/metallic) survive every swap.MaterialCompileExceptionmessages render inline under the editor; the sphere keeps the last good material.materials/starter.mat(lit, three parameters) so the app opens with something editable that renders immediately. The starter and the README's suggested edits were verified to compile through the runtime API.Tests
material_reload_tests(4): scan, reload and replay, instance sharing, destroyOld.material_compile_tests(8): every shipped.matruntime-compiles and loads with parameters intact; parse errors surface the message; defines and includes work; end-to-end compile, render, recompile, hot-swap. (materials/vdtm.matis excluded — broken source, not shipped.)Verified
xvfb-run dart test -j1in the container.flutter analyze: no new issues (package at the 48-issue pre-existing baseline; example analyzes clean).flutter build linux --debugsucceeds; the container has no GPU (/dev/driabsent), so the on-screen edit/swap loop still needs a real machine.CI must confirm