Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
9f81a25
Plan native executor model
sargunv-bot Jul 29, 2026
1c4cbd6
Implement executor phase one operations and notifications
sargunv Aug 13, 2026
e0693ac
Implement executor phase two core-owned runtime
sargunv Aug 13, 2026
d2c5e27
Implement executor phase three render drivers
sargunv Aug 14, 2026
b711d92
Remove completed executor plan
sargunv Aug 14, 2026
6cfa83e
Address executor review cleanup
sargunv-bot Aug 14, 2026
b622d9e
Restore remaining render coverage
sargunv-bot Aug 14, 2026
778965e
Unify command generations with the snapshot and fold unkeyed reads in…
sargunv-bot Aug 15, 2026
d5d0d94
Demote style removals to commands with a NOT_FOUND disposition
sargunv-bot Aug 15, 2026
3eb2e8d
Consolidate per-layer scalar getters into a layer-info aggregate
sargunv-bot Aug 15, 2026
fc8ab58
Make projection calls synchronous after creation
sargunv-bot Aug 15, 2026
c4b3d30
Document the execution-form classification rule
sargunv-bot Aug 15, 2026
2248b07
Sweep the bindings onto the revised execution forms
sargunv-bot Aug 15, 2026
935754f
Restore Zig render lifecycle coverage
sargunv-bot Aug 14, 2026
4acf907
Fix the audit findings from the execution-form revision
sargunv-bot Aug 15, 2026
7aeb7d6
Derive execution classifications from the headers
sargunv-bot Aug 15, 2026
422f853
Apply the whole-PR cleanup sweep
sargunv-bot Aug 15, 2026
a2df525
Merge upstream main into the executor rearchitecture
sargunv-bot Aug 16, 2026
189cfc2
Merge upstream main to adopt typed queried-feature lists
sargunv-bot Aug 16, 2026
a57cc85
Streamline runtime event delivery
sargunv-bot Aug 17, 2026
bb5ce68
Keep runtime event queue simple
sargunv-bot Aug 17, 2026
18f5e25
Simplify runtime event queue ownership
sargunv-bot Aug 17, 2026
f15b276
Keep the prerelease ABI version at zero
sargunv-bot Aug 17, 2026
edac7af
Remove vestigial example command dispatch
sargunv-bot Aug 17, 2026
4f54727
Simplify callback, frame, and camera APIs
sargunv-bot Aug 17, 2026
f6c186c
Collapse relative camera commands
sargunv-bot Aug 17, 2026
819cd37
Run private OpenGL textures on core workers
sargunv-bot Aug 17, 2026
a87b1eb
Fix mechanical CI failures
sargunv-bot Aug 17, 2026
30505b4
Retire runtime handles when close starts
sargunv-bot Aug 17, 2026
50cb81d
Remove unused frame presentation timestamps
sargunv-bot Aug 17, 2026
55a8df2
Release notification sources independently
sargunv-bot Aug 17, 2026
f4db2e3
Release map and runtime handles synchronously
sargunv-bot Aug 17, 2026
621f420
Consume operations with typed results
sargunv-bot Aug 17, 2026
401e1b5
Create runtimes synchronously
sargunv-bot Aug 17, 2026
9591f0b
Detach projections from source handles
sargunv-bot Aug 17, 2026
8cea8aa
Simplify render barriers
sargunv-bot Aug 17, 2026
77d92b9
Use relative frame timeouts
sargunv-bot Aug 17, 2026
ffa16e8
Release acquired frames synchronously
sargunv-bot Aug 17, 2026
b957420
Simplify binding handle lifetimes
sargunv-bot Aug 17, 2026
57237a8
Release log callback state from native
sargunv-bot Aug 17, 2026
55c8c5b
Replace async machinery with native completions
sargunv-bot Aug 18, 2026
8fcd2b8
Merge upstream main into the executor rearchitecture
sargunv-bot Aug 22, 2026
cf27aa5
Report runtime teardown through a release completion
sargunv-bot Aug 23, 2026
c21259d
Restore Apple render coverage and lifecycle safety
sargunv Aug 23, 2026
e79bfd1
Document Dart macOS EGL exclusion
sargunv Aug 23, 2026
2dde408
Validate OpenHarmony and browser targets
sargunv Aug 23, 2026
d58b6bc
Merge upstream main into the executor rearchitecture
sargunv-bot Aug 23, 2026
cd44f60
Fix packaged macOS binding tests
sargunv Aug 23, 2026
7fbb6ed
Make map teardown explicitly awaitable
sargunv-bot Aug 23, 2026
4d8d4e9
Merge remote-tracking branch 'upstream/t3code/native-executor' into t…
sargunv-bot Aug 23, 2026
8eb85d2
Serialize runtime teardown off executor threads
sargunv-bot Aug 23, 2026
5aa7f3f
Reserve Emscripten teardown worker capacity
sargunv-bot Aug 23, 2026
268a600
Revert "Reserve Emscripten teardown worker capacity"
sargunv-bot Aug 23, 2026
36d365d
Revert "Serialize runtime teardown off executor threads"
sargunv-bot Aug 23, 2026
e457736
Await map teardown between Rust tests
sargunv-bot Aug 23, 2026
bb73230
Provision Emscripten workers for synchronous waits
sargunv-bot Aug 23, 2026
aa80578
Revert "Provision Emscripten workers for synchronous waits"
sargunv-bot Aug 23, 2026
5e7de1e
Run blocking map destruction on a shared teardown lane
sargunv-bot Aug 23, 2026
392a24e
Use deadlines for asynchronous render settling
sargunv-bot Aug 23, 2026
3d61505
Reserve shared browser teardown capacity
sargunv-bot Aug 23, 2026
c23c877
Isolate Emscripten binding tests by browser process
sargunv-bot Aug 23, 2026
be4393f
Separate map retirement from backend cleanup
sargunv-bot Aug 23, 2026
e6ee82a
Avoid blocking browser owner threads during test cleanup
sargunv-bot Aug 23, 2026
98dafe9
Keep browser map-close tests nonblocking
sargunv-bot Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
25 changes: 11 additions & 14 deletions .github/workflows/ci.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ bindings/openharmony/library/BuildProfile.ets
.kotlin
.devcontainer/mise-oci/
.devcontainer/mise-oci.tar
does-not-exist.db
1 change: 0 additions & 1 deletion .mise/bin/sync-submodules
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ mlt_vendor_paths=(
# pointing at maplibre/maplibre-native. See patches/maplibre-native/README.md.
mln_patches=(
"$repo_root/patches/maplibre-native/0002-windows-local-file-urls.patch"
"$repo_root/patches/maplibre-native/0003-run-loop-process-gate.patch"
)

# A patch counts as applied when it reverses cleanly, which is also what makes
Expand Down
5 changes: 5 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,11 @@
"configurePreset": "tvos-simulator-arm64-metal",
"inherits": "_test-base"
},
{
"name": "ohos-x64-egl",
"configurePreset": "ohos-x64-egl",
"inherits": "_test-base"
},
{
"name": "windows-x64-wgl",
"configurePreset": "windows-x64-wgl",
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ let testSourceFiles = [
"MaplibreNativeFFITests/SupportHelperTests.swift",
"MaplibreNativeFFITests/SyntheticHandles.swift",
"MaplibreNativeFFITests/ValueTests.swift",
"MaplibreNativeFFITests/WakeSourceTests.swift",
]

let products: [Product] = [
Expand Down
59 changes: 26 additions & 33 deletions bindings/dart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

`maplibre_native_ffi` is the low-level Dart binding for the public MapLibre
Native C API. The package exposes explicit native handle lifetimes, copied value
types, runtime event batches, resource callbacks, offline operations, and the
render backend descriptors used by host integrations.
types, owned runtime event batches, ordinary futures, resource callbacks, and
the render backend descriptors used by host integrations.

## Build and test

Expand All @@ -30,6 +30,12 @@ native preset, and verifies that Flutter packages its code asset. Device and
simulator iOS use separate presets because their dynamic libraries target
different Apple SDKs.

Use Metal or Vulkan for Dart on macOS. The macOS EGL target is temporarily
excluded because the prebuilt ANGLE libraries do not have enough Mach-O header
padding for Dart native assets to update their install names. The target can be
enabled after the ANGLE libraries are relinked with
`-headerpad_max_install_names` and the repository uses the new artifact.

The native library reaches Dart as a code asset that `hook/build.dart` declares,
which is how the generated `@Native` declarations resolve it. Build hooks run in
a semi-hermetic environment that strips arbitrary environment variables, so the
Expand Down Expand Up @@ -69,40 +75,27 @@ copy of the same library.

## Ownership and execution

Owned handles have an idempotent `close()` or `discard()` operation. Close child
maps, render sessions, frames, snapshots, request handles, and offline
Runtime and map handles have an idempotent `close()`. Runtime close remains
asynchronous in Dart so callback roots stay alive through native teardown. Close
child maps, render sessions, frames, snapshots, request handles, and offline
operations before their parent runtime. Scoped backend values remain valid only
until their frame or owner is closed.

Runtime and map work is synchronous and owner-thread-affine. Keep a handle and
all calls that use it on the isolate that created it. Run queued callbacks with
`RuntimeHandle.pump()`, then take the events it produced with
`RuntimeHandle.drainEvents()`. Narrow what a map or a runtime queues with
`setEventMask`.

A render session is the exception: it belongs to the isolate that attached it,
which need not be the map's. A `MapHandle` cannot cross isolates, so
`MapHandle.attachRef()` produces a `MapAttachRef` that can. It carries the
native address and attaches; every other map call stays on the map's isolate.

## Known draft deviation: do not await in an isolate that holds a handle

The C API keys owner-thread checks on the OS thread. This binding keys them on
`Isolate.current.hashCode`, and the two are not equivalent: the Dart VM moves an
isolate between OS threads, and it does so when an isolate resumes from awaited
I/O. The isolate hash does not change, so the binding's own check still passes
while the native check starts failing.

Until that is addressed, do not `await` I/O on an isolate that holds a runtime,
map, projection, or render session. Create the handles, use them, and close them
without yielding to I/O in between. Dart offers no equivalent of Go's
`runtime.LockOSThread()`, so the binding cannot pin the isolate on your behalf.

Exceeding this produces `wrongThread` from every call on the handle, including
`close()`. Because close fails too, the native runtime is never destroyed and
`mln_runtime_destroy` refuses for the rest of the process.

Tracked in [#412](https://github.com/maplibre/maplibre-native-ffi/issues/412).
Projection handles are created asynchronously and are synchronous after that:
every projection call, `close()` included, runs on the calling isolate's thread,
may be made from any isolate, and never observes map changes made after creation
and remains usable after its source map and runtime close.

Create runtimes and maps with `await`. Runtime and map commands copy their input
and return `Future` values. Snapshot methods synchronously copy immutable state.
Ordered queries and lifecycle operations also return `Future` values. Direct
wake callbacks report queued events without participating in future completion.
Read queued events with `RuntimeHandle.drainEvents()`. Narrow what a map or a
runtime queues with `setEventMask`.

Runtime, map, camera, and projection calls remain valid when Dart resumes an
isolate on another native thread after `await`. Attach a render session directly
from its map on the isolate that will own the graphics session.

Resource-request completion is one-shot. Calling `complete()` or `close()`
releases the provider reference even when completion reports a native error.
Expand Down
2 changes: 1 addition & 1 deletion bindings/dart/lib/maplibre_native_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export 'src/runtime/runtime.dart'
CustomMvtVectorCallbackLifecycleProbe,
customMvtVectorCallbackProbeForTesting,
decodeRuntimeEventBatchForTesting,
mapAttachRefIdForTesting,
mapHandleIdForTesting,
runtimeHandleIdForTesting;
export 'src/style/style.dart';
60 changes: 60 additions & 0 deletions bindings/dart/lib/src/camera/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ import '../geo/geo.dart';
export '../geo/geo.dart'
show EdgeInsets, LatLng, LatLngBounds, Quaternion, ScreenPoint, Vec3;

/// Native execution mode for one atomic camera update.
enum CameraUpdateMode {
/// Applies the camera immediately.
jump(0),

/// Animates with easing.
ease(1),

/// Animates along a flight path.
fly(2);

const CameraUpdateMode(this.rawValue);

/// Native camera update mode value.
final int rawValue;
}

/// Cubic easing curve for animated camera transitions.
final class UnitBezier {
/// Creates a cubic unit bezier.
Expand Down Expand Up @@ -148,6 +165,49 @@ final class AnimationOptions {
Object.hash(durationMs, velocity, minZoom, easing, transitionId);
}

/// Relative camera operation kind.
enum CameraDeltaKind {
move(0),
scale(1),
bearing(2),
pitch(3);

const CameraDeltaKind(this.rawValue);

/// Native camera delta kind value.
final int rawValue;
}

/// One relative camera operation.
final class CameraDelta {
/// Creates a relative camera operation.
const CameraDelta({
this.kind = CameraDeltaKind.move,
this.offset = const ScreenPoint(0, 0),
this.amount = 0,
this.anchor,
this.animation = const AnimationOptions(),
});

final CameraDeltaKind kind;
final ScreenPoint offset;
final double amount;
final ScreenPoint? anchor;
final AnimationOptions animation;

@override
bool operator ==(Object other) =>
other is CameraDelta &&
other.kind == kind &&
other.offset == offset &&
other.amount == amount &&
other.anchor == anchor &&
other.animation == animation;

@override
int get hashCode => Object.hash(kind, offset, amount, anchor, animation);
}

/// Optional fitting controls for camera-for-viewport queries.
final class CameraFitOptions {
/// Creates camera fit options.
Expand Down
28 changes: 28 additions & 0 deletions bindings/dart/lib/src/error/maplibre_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ final class MaplibreStatus {
/// A native MapLibre error or C++ exception was converted to status.
static const nativeError = MaplibreStatus._('nativeError', -5);

/// The operation completed after cancellation was requested.
static const cancelled = MaplibreStatus._('cancelled', -6);

/// The call raced a driver call in flight; retry once it returns.
static const busy = MaplibreStatus._('busy', -7);

/// The render target was lost and the session cannot continue with it.
static const targetLost = MaplibreStatus._('targetLost', -8);

/// No result was ready yet; poll or wait for the wake and retry.
static const notReady = MaplibreStatus._('notReady', -9);

/// No object has the requested ID.
static const notFound = MaplibreStatus._('notFound', -10);

/// An unknown status value returned by a newer or incompatible native build.
static MaplibreStatus unknown(int nativeStatusCode) =>
MaplibreStatus._('unknown', nativeStatusCode);
Expand All @@ -39,6 +54,11 @@ final class MaplibreStatus {
-3 => wrongThread,
-4 => unsupported,
-5 => nativeError,
-6 => cancelled,
-7 => busy,
-8 => targetLost,
-9 => notReady,
-10 => notFound,
_ => unknown(nativeStatusCode),
};

Expand Down Expand Up @@ -79,6 +99,7 @@ abstract final class MaplibreException implements Exception {
-3 => WrongThreadException(nativeStatusCode, diagnostic),
-4 => UnsupportedFeatureException(nativeStatusCode, diagnostic),
-5 => NativeErrorException(nativeStatusCode, diagnostic),
-10 => NotFoundException(nativeStatusCode, diagnostic),
_ => UnknownMaplibreException(status, nativeStatusCode, diagnostic),
};
}
Expand Down Expand Up @@ -149,6 +170,13 @@ final class NativeErrorException extends MaplibreException {
: super(MaplibreStatus.nativeError, nativeStatusCode, diagnostic);
}

/// Native not-found failure: no object has the requested ID.
final class NotFoundException extends MaplibreException {
/// Creates a not-found exception.
const NotFoundException(int? nativeStatusCode, String diagnostic)
: super(MaplibreStatus.notFound, nativeStatusCode, diagnostic);
}

/// Loaded native library uses a different C ABI contract version.
final class AbiVersionMismatchException extends MaplibreException {
/// Creates an ABI-version mismatch exception.
Expand Down
Loading
Loading