Skip to content
Open
86 changes: 86 additions & 0 deletions test/bench/benchmarks/mlt_parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import Benchmark from '../lib/benchmark.ts';
import TileParser from '../lib/tile_parser.ts';
import {OverscaledTileID} from '../../../src/tile/tile_id.ts';
import type {LayerSpecification, StyleSpecification} from '@maplibre/maplibre-gl-style-spec';

type MLTParseOptions = {
/**
* Tile directory under `test/integration/assets/tiles`.
* `mlt` ships a pre-baked triangle mesh
* `mlt-untessellated` does not, forcing MapLibre to re-tessellate with earcut.
*/
tilesDir: 'mlt' | 'mlt-untessellated';
tileID: OverscaledTileID;
/** Layers rendered on top of the shared MLT source. */
layers: LayerSpecification[];
};

/**
* Isolates the worker-side cost of turning an MLT tile into renderable geometry.
* It drives {@link TileParser} so it covers decode + `loadGeometry` + bucket population + tessellation
*/
abstract class MLTParse extends Benchmark {
parser: TileParser;
tile: {tileID: OverscaledTileID; buffer: ArrayBuffer};

abstract get options(): MLTParseOptions;

async setup(): Promise<void> {
const style: StyleSpecification = {
version: 8,
sources: {
openmaptiles: {
type: 'vector',
encoding: 'mlt',
maxzoom: 14,
tiles: [`/test/integration/assets/tiles/${this.options.tilesDir}/{z}/{x}/{y}.mlt`]
} as any
},
layers: this.options.layers
};

this.parser = new TileParser(style, 'openmaptiles');
await this.parser.setup();
this.tile = await this.parser.fetchTile(this.options.tileID);
// Warm up: run one parse so the benchmarked loop measures steady state.
await this.parser.parseTile(this.tile);
}

async bench(): Promise<void> {
await this.parser.parseTile(this.tile);
}
}

const fillLayers: LayerSpecification[] = [
{id: 'landcover', type: 'fill', source: 'openmaptiles', 'source-layer': 'landcover', paint: {'fill-color': '#008000'}}
];
const fillExtrusionLayers: LayerSpecification[] = [
{id: 'background', type: 'background', paint: {'background-color': '#ffffff'}},
{id: 'building', type: 'fill-extrusion', source: 'openmaptiles', 'source-layer': 'building', paint: {'fill-extrusion-color': '#c86432', 'fill-extrusion-height': 20}}
];
const fillTileID = new OverscaledTileID(5, 0, 5, 22, 12);
const buildingTileID = new OverscaledTileID(14, 0, 14, 8716, 5685);

export class MLTFillTessellated extends MLTParse {
get options(): MLTParseOptions {
return {tilesDir: 'mlt', tileID: fillTileID, layers: fillLayers};
}
}

export class MLTFillUntessellated extends MLTParse {
get options(): MLTParseOptions {
return {tilesDir: 'mlt-untessellated', tileID: fillTileID, layers: fillLayers};
}
}

export class MLTFillExtrusionTessellated extends MLTParse {
get options(): MLTParseOptions {
return {tilesDir: 'mlt', tileID: buildingTileID, layers: fillExtrusionLayers};
}
}

export class MLTFillExtrusionUntessellated extends MLTParse {
get options(): MLTParseOptions {
return {tilesDir: 'mlt-untessellated', tileID: buildingTileID, layers: fillExtrusionLayers};
}
}
17 changes: 15 additions & 2 deletions test/bench/lib/tile_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {PbfReader} from 'pbf';
import {VectorTile} from '@mapbox/vector-tile';

import {derefLayers} from '@maplibre/maplibre-gl-style-spec';
import {MLTVectorTile} from '../../../src/source/vector_tile_mlt.ts';
import {Style} from '../../../src/style/style.ts';
import {type IReadonlyTransform} from '../../../src/geo/transform_interface.ts';
import {Evented} from '../../../src/util/evented.ts';
Expand All @@ -15,6 +16,7 @@ import type {OverscaledTileID} from '../../../src/tile/tile_id.ts';
import type {TileJSON} from '../../../src/util/util.ts';
import type {Map} from '../../../src/ui/map.ts';
import type {IActor} from '../../../src/util/actor.ts';
import type {TileEncoding} from '../../../src/source/worker_source.ts';
import {SubdivisionGranularitySetting} from '../../../src/render/subdivision_granularity_settings.ts';
import {MessageType, type ActorMessage, type GetImagesParameters, type GetImagesResponse, type GetGlyphsParameters, type GetGlyphsResponse, type GetDashesParameters, type GetDashesResponse} from '../../../src/util/actor_messages.ts';

Expand Down Expand Up @@ -68,11 +70,13 @@ export default class TileParser {
dashes: Record<string, GetDashesResponse>;
style: Style;
actor: IActor;
encoding: TileEncoding;

constructor(styleJSON: StyleSpecification, sourceID: string) {
this.styleJSON = styleJSON;
this.sourceID = sourceID;
this.layerIndex = new StyleLayerIndex(derefLayers(this.styleJSON.layers));
this.encoding = (this.styleJSON.sources[sourceID] as any).encoding ?? 'mvt';
this.glyphs = {};
this.icons = {};
}
Expand Down Expand Up @@ -119,9 +123,16 @@ export default class TileParser {
}
};

const source = this.styleJSON.sources[this.sourceID] as any;
// Sources may point at a TileJSON `url`, or inline the `tiles` templates
// directly (used by local-fixture benchmarks that don't serve a TileJSON).
const tileJSONPromise: Promise<TileJSON> = source.url
? fetch(source.url).then(response => response.json())
: Promise.resolve(source as TileJSON);

return Promise.all([
createStyle(this.styleJSON),
fetch((this.styleJSON.sources[this.sourceID] as any).url).then(response => response.json())
tileJSONPromise
]).then(([style, tileJSON]) => {
this.style = style;
this.tileJSON = tileJSON;
Expand Down Expand Up @@ -160,7 +171,9 @@ export default class TileParser {
subdivisionGranularity: SubdivisionGranularitySetting.noSubdivision
});

const vectorTile = new VectorTile(new PbfReader(tile.buffer));
const vectorTile = this.encoding === 'mlt'
? new MLTVectorTile(tile.buffer)
: new VectorTile(new PbfReader(tile.buffer));

return workerTile.parse(vectorTile, this.layerIndex, [], this.actor, SubdivisionGranularitySetting.noSubdivision);
}
Expand Down
5 changes: 5 additions & 0 deletions test/bench/versions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import CoveringTilesMercator from '../benchmarks/covering_tiles_mercator.ts';
import GeoJSONSourceUpdateData from '../benchmarks/geojson_source_update_data.ts';
import GeoJSONSourceSetData from '../benchmarks/geojson_source_set_data.ts';
import {Terrain3DGlobe, Terrain3DMercator, Terrain2DGlobe, Terrain2DMercator} from '../benchmarks/terrain.ts';
import {MLTFillTessellated, MLTFillUntessellated, MLTFillExtrusionTessellated, MLTFillExtrusionUntessellated} from '../benchmarks/mlt_parse.ts';

const styleLocations = locationsWithTileID(styleBenchmarkLocations.features as Array<GeoJSON.Feature<GeoJSON.Point>>).filter(v => v.zoom < 15); // the used maptiler sources have a maxzoom of 14

Expand Down Expand Up @@ -114,6 +115,10 @@ register('Terrain3DGlobe', new Terrain3DGlobe());
register('Terrain3DMercator', new Terrain3DMercator());
register('Terrain2DGlobe', new Terrain2DGlobe());
register('Terrain2DMercator', new Terrain2DMercator());
register('MLTFillTessellated', new MLTFillTessellated());
register('MLTFillUntessellated', new MLTFillUntessellated());
register('MLTFillExtrusionTessellated', new MLTFillExtrusionTessellated());
register('MLTFillExtrusionUntessellated', new MLTFillExtrusionUntessellated());

Promise.resolve().then(() => {
// Ensure the global worker pool is never drained. Browsers have resource limits
Expand Down
Binary file not shown.
Binary file not shown.
Binary file modified test/integration/assets/tiles/mlt/14/8716/5685.mlt
Binary file not shown.
Binary file modified test/integration/assets/tiles/mlt/14/8717/5679.mlt

@CommanderStorm CommanderStorm Jul 8, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to be reencoded using the rust encoder because otherwise they are very weird (early java enoder I guess) and also not a good benchmark in comparison to the smaller, tesselated tiles

Binary file not shown.
Binary file modified test/integration/assets/tiles/mlt/5/17/10.mlt
Binary file not shown.
Binary file modified test/integration/assets/tiles/mlt/5/22/12.mlt
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256
}
},
"center": [
11.525101,
48.144716
],
"zoom": 14.92,
"pitch": 55,
"bearing": -20,
"sources": {
"openmaptiles": {
"type": "vector",
"encoding": "mlt",
"tiles": ["local://tiles/mlt-untessellated/{z}/{x}/{y}.mlt"]
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "#ffffff"
}
},
{
"id": "building",
"type": "fill-extrusion",
"source": "openmaptiles",
"source-layer": "building",
"paint": {
"fill-extrusion-color": "#c86432",
"fill-extrusion-height": 20
}
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256
}
},
"center": [
11.525101,
48.144716
],
"zoom": 14.92,
"pitch": 55,
"bearing": -20,
"sources": {
"openmaptiles": {
"type": "vector",
"encoding": "mlt",
"tiles": ["local://tiles/mlt/{z}/{x}/{y}.mlt"]
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "#ffffff"
}
},
{
"id": "building",
"type": "fill-extrusion",
"source": "openmaptiles",
"source-layer": "building",
"paint": {
"fill-extrusion-color": "#c86432",
"fill-extrusion-height": 20
}
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"version": 8,
"metadata": {
"test": {
"debug": true,
"height": 256
}
},
"center": [
73,
36
],
"zoom": 5.99,
"sources": {
"openmaptiles": {
"type": "vector",
"encoding": "mlt",
"tiles": ["local://tiles/mlt-untessellated/{z}/{x}/{y}.mlt"],
"maxzoom": 14
}
},
"sprite": "local://sprites/sprite",
"layers": [
{
"id": "landcover",
"type": "fill",
"source": "openmaptiles",
"source-layer": "landcover",
"paint": {
"fill-color": "#008000"
}
}
]
}
Loading