Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
  •  
  •  
  •  
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
5 changes: 5 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,11 @@
"configurePreset": "ios-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
324 changes: 324 additions & 0 deletions api/execution-manifest.json

Large diffs are not rendered by default.

55 changes: 19 additions & 36 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, common operation handles, resource
callbacks, and the render backend descriptors used by host integrations.

## Build and test

Expand Down Expand Up @@ -69,40 +69,23 @@ 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
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).
Runtime, map, and projection handles have an idempotent asynchronous `close()`.
Await close so that native execution finishes before dependencies and callback
roots are released. 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.

Create runtimes and maps with `await`. Runtime and map commands copy their input
and return a command ID immediately. Snapshot methods synchronously copy
immutable state. Ordered queries and lifecycle operations return `Future`
values. A runtime's notification source completes those futures and reports
event readiness, so native execution progresses without blocking the UI isolate.
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 @@ -17,6 +17,6 @@ export 'src/runtime/runtime.dart'
CustomGeometryCallbackLifecycleProbe,
customGeometryCallbackProbeForTesting,
decodeRuntimeEventBatchForTesting,
mapAttachRefIdForTesting,
mapHandleIdForTesting,
runtimeHandleIdForTesting;
export 'src/style/style.dart';
17 changes: 17 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
4 changes: 4 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,9 @@ 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);

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

Expand Down
Loading