OrbPro2 a Cesium distribution

ProviderAccessPort

new Cesium.ProviderAccessPort(scene, options)

The engine half of the provider-access ABI: native, read-only access to the terrain and imagery bytes the engine has ALREADY decoded. WHY THIS EXISTS (owner directive 2026-08-07, verbatim): "Also, why is it so fucking slow when rendered? … did we do the C++ / WASM access to terrain and imagery, or are we doing some crazy loop around / sampling through the Cesium engine? The solve should be way faster" It was a loop around. Every RF terrain solve read heights through sampleTerrain, which re-requests EVERY tile through `terrainProvider.requestTileGeometry` — network round trip, gunzip and quantized-mesh parse per tile, serialized behind the RequestScheduler with a 100 ms retry delay — for tiles the globe was already rendering from its own decoded quadtree cache a few pointers away. This port reads that cache instead. No request, no decode, no re-parse. THE PART THAT IS NOT OBVIOUS, and the reason this is a real engine surface rather than a three-line helper: the decoded cache is NOT randomly addressable. `QuantizedMeshTerrainData.interpolateHeight` — the call `sampleTerrain` makes once it has paid for the tile — is a LINEAR SCAN over every triangle in the tile until one contains the point (`QuantizedMeshTerrainData.js:635-676` and the `_mesh` variant at 579-631). A tile carries 5k-25k triangles. A 512x512 coverage field is 262,144 samples. Calling it per sample is O(samples x triangles) — billions of barycentric tests, far slower than the network path it was meant to replace. So the port builds a bucketed triangle index over the tile's own quantized u/v arrays ONCE (O(triangles), counting sort, no allocation per sample) and caches it against the `TerrainData` instance. Reads are then O(1) per sample with the SAME arithmetic the engine's own `interpolateHeight` uses, so the answers are bit-identical to the slow path rather than merely close. ZERO COPY WHERE THE DATA ALREADY IS. `_uValues`, `_vValues` and `_heightValues` are zero-copy `Uint16Array` subarrays of the tile's `_quantizedVertices` (`QuantizedMeshTerrainData.js:127-139`). The index holds references to them; it never copies height data. The only allocation is the index itself (two typed arrays sized by triangle count) and the caller's output buffer. PRIVATE FIELDS LIVE HERE, NOT IN THE SDK. `_surface`, `_levelZeroTiles`, `_southwestChild`, `_quantizedVertices`, `_structure`, `_imageryCache` move on every upstream pin advance. The engine owns them and keeps them under its own specs; the module SDK owns the wasm ABI and calls only ProviderAccessPort#adapters.
Name Type Description
scene Scene The scene whose providers are exposed.
options object optional
Name Type Default Description
maxImageryTaps number 64 optional Bound on the pre-upload imagery pixel cache, in tiles. The tap is OFF until a consumer arms it.
See:

Classes

PortError

Members

static Cesium.ProviderAccessPort.Cost : number

What an acquire actually cost. Reported per result, never assumed.

static Cesium.ProviderAccessPort.Encoding : number

Plane encodings.

static Cesium.ProviderAccessPort.Error : number

ABI error codes. An adapter throws ProviderAccessPort.PortError with one of these; anything else becomes SDM_PROVIDER_E_HOST at the SDK boundary.

static Cesium.ProviderAccessPort.Flags : number

Descriptor flags.

static constant Cesium.ProviderAccessPort.NO_DATA : number

No-data sentinel for f64 planes. NaN is deliberately not used: WebAssembly does not canonicalize NaN payloads across every producing operation, so two runtimes can hold different bits for "a NaN" and a byte-identical-output assertion would fail on values that are semantically equal. -DBL_MAX has exactly one encoding and is never a real terrain height.

static constant Cesium.ProviderAccessPort.ROW_ORDER_NORTH_FIRST : string

The row order of every imagery record the pre-upload tap stores: row 0 is the northern edge of the tile, column 0 the western edge. Carried on each record as rowOrder so a consumer can assert it instead of assuming it.

static Cesium.ProviderAccessPort.Strategy : number

How the level was chosen. Travels in the descriptor because the consumers that most need the provenance are wasm modules.
Raised once per imagery tile that passes through the armed pre-upload tap, as (record, layer), synchronously from inside ImageryLayer#_createTexture AFTER the record has been stored and FIFO eviction has run — so by the time a listener sees a record, ProviderAccessPort#imageryTiles already returns it. The record is the same object the port holds (see ProviderAccessPort#imageryTiles for its shape); consumers must not mutate it. Rows are north-first: row 0 is the northern edge of the tile, column 0 the western edge. Listeners run inside the render loop, on the frame that uploads the tile. They must be cheap: enqueue the record and return. Decoding, inference or any allocation beyond a queue push belongs outside the listener. Never raised while the tap is disarmed, and never raised for compressed (KTX2) sources, which are not decoded on the CPU and are counted in stats.imageryCompressedSkipped instead.

readonly imageryTapArmed : boolean

True when a consumer has armed the pre-upload imagery pixel tap. Off by default: the tap costs a GPU-free canvas readback per tile at upload time, and nothing in the render loop should pay for a feature nobody asked for.
Bound on the pre-upload imagery pixel cache, in tiles PER LAYER. Records beyond the bound are evicted oldest-first. Lowering the bound evicts immediately, for every layer, so the cache never holds more than the current value. Must be an integer of at least 1. Default 64; a consumer that wants to replay a wider set of tiles through ProviderAccessPort#imageryTiles raises it, paying width * height * 4 bytes per held tile.
The scene this port reads.
Counters for the resident read path and the imagery tap. Cumulative for the port's lifetime; a consumer that wants a per-solve delta snapshots them either side. imageryCompressedSkipped counts tiles the armed tap declined because they were compressed (KTX2) sources with no CPU-decoded pixels.

readonly terrainProvider : TerrainProvider|undefined

The terrain provider currently backing the globe, or undefined.

Methods

static Cesium.ProviderAccessPort.levelForSpacing(spacingMeters, options)number

Coarsest level whose sample spacing satisfies a target spacing in metres. Replicated bit-for-bit from the SDK's `providerLevelForSpacing` on purpose. If the browser port and the host tile store picked levels by their own arithmetic they would sample different ground, and byte parity would be lost for a reason nobody could see in a diff.
Name Type Description
spacingMeters number Target sample spacing.
options object optional
Name Type Default Description
tileWidth number 65 optional Samples across a tile.
minLevel number 0 optional
maxLevel number 20 optional
Returns:
The chosen level.

adapters()Array.<object>

The adapter set handed to the module SDK. `createEngineProviderAdapters({ scene })` uses this VERBATIM when `scene.providerAccessPort` exists, so the list is complete on its own: resident terrain first (it becomes the default selection for `kind: "terrain"`), the sampler tier second, then one adapter per imagery layer.
Returns:
Adapter objects.

armImageryTap(armed)boolean

Arm or disarm the pre-upload imagery pixel tap. ImageryLayer#_createTexture nulls `imagery.image` the instant the texture is uploaded, so a tile the renderer has finished with has no CPU pixels left anywhere. The only place zero-re-fetch imagery exists is the moment before that upload, so that is where the tap sits. Off by default and deliberately explicit: armed, every imagery tile pays a canvas readback during load. Nothing in the render loop should pay for a capability no consumer asked for.
Name Type Description
armed boolean
Returns:
The new state.

awaitRegion(options)Promise.<object>

Resolve once the globe reports its load queue empty, or the deadline passes. Bounded on purpose. An unbounded wait on a camera-driven loader is a hang whenever the region is not reachable at the requested detail, and a solve that hangs is worse than a solve that reports PARTIAL.
Name Type Description
options object optional
Name Type Default Description
timeoutMs number 4000 optional
Returns:
`{ready, pending, waitedMs}`.
Release everything the port holds. Pins are released explicitly because a leaked pin keeps terrain resident forever.

imageryTiles(layer)Array.<object>

The records currently held by the pre-upload imagery tap. Each record is the object stored by the tap and handed to ProviderAccessPort#imageryCaptured; consumers must not mutate it:
{
  pixels: Uint8Array,   // RGBA8, width * height * 4 bytes, north-first rows
  width: number,
  height: number,
  level: number,
  x: number,
  y: number,
  key: string,          // "level/x/y"
  rectangle: Rectangle, // the imagery tile's full rectangle, radians
  layer: ImageryLayer,
  encoding: ProviderAccessPort.Encoding.RGBA8,
  rowOrder: "north-first"
}
Returns a NEW array on every call, oldest record first, so a caller can replay tiles captured before it subscribed without holding a reference into the port's cache. Empty while the tap is disarmed.
Name Type Description
layer ImageryLayer optional Restrict to one layer. When omitted, the records of every layer are returned.
Returns:
The held records.

prefetchRegion(request)object

Ask the engine to load terrain over a rectangle at a target detail. The engine loads camera-driven; `loadTileDataAvailability` loads metadata, not geometry. The ONE native surface that makes the quadtree keep and refine tiles for a region regardless of where the camera is pointing is OrbPro's own terrain pinning (Globe#pinBoundingSphere), which both exempts the region from culling and lowers its screen-space error. This method is that surface, addressed by rectangle instead of by bounding sphere, and it is why the ABI can now answer `supported: true` instead of reporting a LACK. It is a REQUEST, not a guarantee: loading still happens on render frames. ProviderAccessPort#awaitRegion is the completion half.
Name Type Description
request object
Name Type Description
rectangle Rectangle | Array.<number> Region, or `[w, s, e, n]` radians.
spacing number optional Target sample spacing in metres.
level number optional Target level, when spacing is not given.
id string optional Pin id, so a caller can re-target its own pin.
Returns:
`{supported, requested, pending, id, level}`.

readHeights(positions, options)object

Read terrain heights straight out of the loaded quadtree. This is the engine-facing entry point; engine consumers (RfTerrainAnalysis) call it directly and get the provenance object, while wasm guests reach the same code through ProviderAccessPort#adapters and the SDK's ABI. Positions are read as `[longitudeRadians, latitudeRadians]` pairs or as anything exposing `longitude`/`latitude` in radians — a `Cartographic` array works unchanged, which is what makes the solver cutover a body swap instead of a rewrite. Samples with no resident tile are written as ProviderAccessPort.NO_DATA and counted in `missing`. The port NEVER quietly falls back to a request: a caller that wants the sampler asks for it, at its own cost class.
Name Type Description
positions Array Positions in radians.
options object optional
Name Type Description
level number optional Deepest level to descend to. Defaults to the deepest loaded.
spacing number optional Target sample spacing in metres; wins over `level`, because a caller knows the stride it intends to march at and does not know a provider's level scheme.
out Float64Array optional Destination. Allocated when absent.
Returns:
`{heights, missing, resident, minLevel, maxLevel, minValue, maxValue, level, strategy, strategyCode, costClass}`.

readHeightsAtLevel(positions, level, options)Promise.<object>

Heights for a whole field at ONE level, using the indexed gather for every tile — resident or not. WHY THIS EXISTS, measured rather than assumed. On the live Mount Rainier scene the coverage solve asks for a GRID-MATCHED level (7 for a 1.1 km raster). The renderer has no reason to hold level 7: near the camera it refines well past it, far away it stops short. Measured residency for that field was 0 of 36,864 samples — a strictly-correct resident read is a guaranteed miss, and a lenient one silently answers from whatever the camera loaded, which erased the terrain shadowing entirely. So residency is the wrong axis. The expensive part of `sampleTerrain` is not only the fetch: once it HAS the tile it calls `interpolateHeight` per point, and that is the O(triangles) linear scan. 36,865 points cost 55 ms of a 138 ms solve with the tiles already in the browser cache. This method keeps the engine's tile acquisition and replaces only the gather: group the field by tile, take a resident tile when the quadtree has one at exactly this level, request the rest ONCE each, then answer every point through the bucket index. Same tiles, same arithmetic, bit-identical output — and the per-point cost stops scaling with triangle count.
Name Type Description
positions Array `[lonRadians, latRadians]` pairs or Cartographics.
level number The level to read. Required: this method exists to serve a level the camera did not choose.
options object optional
Name Type Description
out Float64Array optional
Returns:
`{heights, missing, resident, fetched, tiles, level, strategy, costClass}`.

releaseRegion(id)boolean

Release a pin taken by ProviderAccessPort#prefetchRegion.
Name Type Description
id string
Returns:
True when a pin was released.
Need help? The fastest way to get answers is from the community and team on the Cesium Forum.