maplibre-gl 6.0.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/readme.md +0 -2
- package/dist/maplibre-gl-dev.mjs +433 -573
- package/dist/maplibre-gl-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared-dev.mjs +984 -850
- package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared.mjs +2 -2
- package/dist/maplibre-gl-shared.mjs.map +1 -1
- package/dist/maplibre-gl-worker-dev.mjs +14 -12
- package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
- package/dist/maplibre-gl-worker.mjs +2 -2
- package/dist/maplibre-gl-worker.mjs.map +1 -1
- package/dist/maplibre-gl.css +1 -1
- package/dist/maplibre-gl.d.ts +119 -42
- package/dist/maplibre-gl.mjs +22 -22
- package/dist/maplibre-gl.mjs.map +1 -1
- package/package.json +20 -22
- package/src/css/maplibre-gl.css +4 -0
- package/src/data/bucket/circle_bucket.ts +3 -2
- package/src/data/bucket/fill_bucket.ts +3 -2
- package/src/data/bucket/fill_extrusion_bucket.test.ts +22 -0
- package/src/data/bucket/fill_extrusion_bucket.ts +17 -4
- package/src/data/bucket/line_bucket.test.ts +5 -5
- package/src/data/bucket/line_bucket.ts +3 -2
- package/src/data/bucket/round_polygon_corners.test.ts +159 -0
- package/src/data/bucket/round_polygon_corners.ts +121 -0
- package/src/data/bucket/symbol_bucket.test.ts +7 -7
- package/src/data/dem_data.test.ts +23 -23
- package/src/data/feature_position_map.test.ts +1 -1
- package/src/data/segment.test.ts +14 -14
- package/src/geo/bounds.test.ts +4 -4
- package/src/geo/lng_lat_bounds.test.ts +1 -1
- package/src/geo/projection/covering_tiles.bench.ts +39 -0
- package/src/geo/projection/covering_tiles.test.ts +3 -7
- package/src/geo/projection/covering_tiles.ts +37 -2
- package/src/geo/projection/globe_covering_tiles_details_provider.ts +4 -4
- package/src/geo/projection/globe_projection.test.ts +2 -2
- package/src/geo/projection/globe_projection.ts +6 -15
- package/src/geo/projection/globe_transform.test.ts +3 -6
- package/src/geo/projection/globe_transform.ts +4 -7
- package/src/geo/projection/mercator_covering_tiles_details_provider.ts +4 -4
- package/src/geo/projection/mercator_projection.ts +1 -13
- package/src/geo/projection/mercator_transform.test.ts +1 -1
- package/src/geo/projection/mercator_transform.ts +2 -1
- package/src/geo/projection/mercator_utils.test.ts +8 -8
- package/src/geo/projection/projection.ts +0 -27
- package/src/geo/projection/projection_data.ts +8 -0
- package/src/geo/projection/projection_factory.ts +3 -3
- package/src/geo/projection/vertical_perspective_projection.ts +3 -60
- package/src/geo/projection/vertical_perspective_transform.ts +16 -23
- package/src/geo/transform_interface.ts +1 -2
- package/src/index.test.ts +7 -12
- package/src/index.ts +4 -2
- package/src/render/glyph_manager.test.ts +7 -22
- package/src/render/painter.test.ts +2 -0
- package/src/render/painter.ts +8 -11
- package/src/render/subdivision.bench.ts +39 -0
- package/src/render/subdivision.test.ts +22 -23
- package/src/render/subdivision.ts +46 -0
- package/src/render/terrain.test.ts +68 -34
- package/src/render/terrain.ts +69 -26
- package/src/shaders/glsl/_projection_globe.fragment.glsl +12 -0
- package/src/shaders/glsl/_projection_globe.fragment.glsl.g.ts +2 -0
- package/src/shaders/glsl/_projection_globe.vertex.glsl +29 -10
- package/src/shaders/glsl/_projection_globe.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/_projection_mercator.fragment.glsl +4 -0
- package/src/shaders/glsl/_projection_mercator.fragment.glsl.g.ts +2 -0
- package/src/shaders/glsl/line.fragment.glsl +2 -0
- package/src/shaders/glsl/line.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient.fragment.glsl +2 -0
- package/src/shaders/glsl/line_gradient.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient_sdf.fragment.glsl +2 -0
- package/src/shaders/glsl/line_gradient_sdf.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_pattern.fragment.glsl +2 -0
- package/src/shaders/glsl/line_pattern.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_sdf.fragment.glsl +2 -0
- package/src/shaders/glsl/line_sdf.fragment.glsl.g.ts +1 -1
- package/src/shaders/shaders.ts +4 -6
- package/src/source/canvas_source.test.ts +1 -1
- package/src/source/geojson_source.test.ts +32 -4
- package/src/source/geojson_source.ts +41 -0
- package/src/source/geojson_source_diff.test.ts +2 -2
- package/src/source/geojson_worker_source.test.ts +9 -5
- package/src/source/image_source.test.ts +137 -62
- package/src/source/image_source.ts +49 -12
- package/src/source/raster_dem_tile_source.test.ts +2 -2
- package/src/source/raster_tile_source.test.ts +64 -9
- package/src/source/raster_tile_source.ts +2 -1
- package/src/source/rtl_text_plugin_main_thread.test.ts +14 -14
- package/src/source/source.test.ts +8 -8
- package/src/source/vector_tile_source.test.ts +1 -1
- package/src/source/vector_tile_worker_source.test.ts +7 -5
- package/src/source/video_source.test.ts +1 -1
- package/src/source/worker.test.ts +1 -1
- package/src/source/worker_tile.test.ts +10 -8
- package/src/style/light.test.ts +9 -9
- package/src/style/light.ts +3 -3
- package/src/style/load_glyph_range.test.ts +8 -8
- package/src/style/load_sprite.test.ts +1 -1
- package/src/style/properties.test.ts +94 -2
- package/src/style/properties.ts +30 -1
- package/src/style/sky.test.ts +9 -9
- package/src/style/sky.ts +2 -2
- package/src/style/style.test.ts +38 -36
- package/src/style/style.ts +3 -3
- package/src/style/style_layer/color_relief_style_layer.test.ts +1 -1
- package/src/style/style_layer/fill_extrusion_style_layer.ts +6 -3
- package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +14 -1
- package/src/style/style_layer/raster_style_layer.test.ts +2 -2
- package/src/style/style_layer.test.ts +5 -51
- package/src/style/validate_style.test.ts +1 -1
- package/src/symbol/collision_index.test.ts +1 -1
- package/src/symbol/grid_index.test.ts +1 -1
- package/src/tile/terrain_tile_manager.test.ts +2 -2
- package/src/tile/tile_id.test.ts +5 -5
- package/src/tile/tile_manager.test.ts +113 -72
- package/src/tile/tile_manager.ts +2 -2
- package/src/tile/tile_manager_in_view_tiles.test.ts +2 -2
- package/src/ui/camera.test.ts +138 -135
- package/src/ui/control/geolocate_control.test.ts +7 -30
- package/src/ui/handler/dblclick_zoom.test.ts +2 -2
- package/src/ui/handler/mouse.ts +15 -7
- package/src/ui/handler/mouse_handler_interface.test.ts +21 -1
- package/src/ui/handler/scroll_zoom.test.ts +2 -0
- package/src/ui/handler_manager.test.ts +7 -7
- package/src/ui/hash.test.ts +1 -0
- package/src/ui/map.ts +45 -20
- package/src/ui/map_tests/map_basic.test.ts +11 -23
- package/src/ui/map_tests/map_calculate_camera_options.test.ts +6 -6
- package/src/ui/map_tests/map_events.test.ts +5 -7
- package/src/ui/map_tests/map_global_state.test.ts +3 -3
- package/src/ui/map_tests/map_images.test.ts +4 -3
- package/src/ui/map_tests/map_is_moving.test.ts +1 -1
- package/src/ui/map_tests/map_is_rotating.test.ts +1 -1
- package/src/ui/map_tests/map_layer.test.ts +2 -1
- package/src/ui/map_tests/map_options.test.ts +1 -5
- package/src/ui/map_tests/map_pitch.test.ts +6 -6
- package/src/ui/map_tests/map_refresh_tiles.test.ts +2 -2
- package/src/ui/map_tests/map_render.test.ts +1 -1
- package/src/ui/map_tests/map_set_lod_params.test.ts +1 -1
- package/src/ui/map_tests/map_style.test.ts +9 -1
- package/src/ui/map_tests/map_terrain.test.ts +41 -1
- package/src/ui/map_tests/map_zoom.test.ts +15 -15
- package/src/ui/marker.test.ts +71 -14
- package/src/ui/marker.ts +30 -6
- package/src/ui/popup.test.ts +3 -3
- package/src/util/actor.test.ts +6 -6
- package/src/util/ajax.test.ts +27 -39
- package/src/util/browser.test.ts +7 -9
- package/src/util/create_tile_mesh.test.ts +2 -2
- package/src/util/dispatcher.test.ts +1 -1
- package/src/util/evented.test.ts +4 -1
- package/src/util/fast_maths.test.ts +4 -4
- package/src/util/image_request.test.ts +6 -4
- package/src/util/primitives/bounding_volume_cache.ts +3 -1
- package/src/util/struct_array.test.ts +2 -2
- package/src/util/task_queue.test.ts +1 -1
- package/src/util/test/util.ts +1 -0
- package/src/util/time_control.test.ts +2 -2
- package/src/util/transferable_grid_index.test.ts +1 -1
- package/src/util/util.test.ts +27 -13
- package/src/util/verticalize_punctuation.test.ts +4 -4
- package/src/util/web_worker_transfer.test.ts +5 -5
- package/src/webgl/draw/draw_custom.test.ts +13 -13
- package/src/webgl/draw/draw_debug.test.ts +1 -1
- package/src/webgl/draw/draw_fill.test.ts +13 -12
- package/src/webgl/draw/draw_symbol.test.ts +17 -16
- package/src/webgl/program/program_uniforms.ts +0 -3
- package/src/webgl/program/projection_program.ts +12 -10
- package/src/webgl/program/terrain_program.test.ts +2 -2
- package/src/webgl/program.ts +6 -4
- package/src/webgl/render_to_texture.test.ts +5 -5
- package/src/webgl/state.test.ts +1 -1
- package/src/webgl/texture.test.ts +3 -3
- package/src/webgl/vertex_buffer.test.ts +1 -1
- package/build/rolldown_plugins.ts +0 -18
- package/src/geo/projection/globe_projection_error_measurement.ts +0 -229
- package/src/shaders/glsl/projection_error_measurement.fragment.glsl +0 -5
- package/src/shaders/glsl/projection_error_measurement.fragment.glsl.g.ts +0 -2
- package/src/shaders/glsl/projection_error_measurement.vertex.glsl +0 -22
- package/src/shaders/glsl/projection_error_measurement.vertex.glsl.g.ts +0 -2
- package/src/webgl/program/projection_error_measurement_program.ts +0 -23
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {Uniform1f, Uniform4f, type UniformLocations, UniformMatrix4f} from '../uniform_binding.ts';
|
|
1
|
+
import {Uniform1f, Uniform1i, Uniform4f, type UniformLocations, type UniformValues, UniformMatrix4f} from '../uniform_binding.ts';
|
|
2
2
|
import {type Context} from '../../webgl/context.ts';
|
|
3
|
-
// This next import is needed for the "@link" in the documentation to work properly.
|
|
4
3
|
|
|
5
4
|
import type {ProjectionData} from '../../geo/projection/projection_data.ts';
|
|
6
5
|
|
|
@@ -10,6 +9,7 @@ export type ProjectionPreludeUniformsType = {
|
|
|
10
9
|
'u_projection_clipping_plane': Uniform4f;
|
|
11
10
|
'u_projection_transition': Uniform1f;
|
|
12
11
|
'u_projection_fallback_matrix': UniformMatrix4f;
|
|
12
|
+
'u_projection_clip_antimeridian': Uniform1i;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const projectionUniforms = (context: Context, locations: UniformLocations): ProjectionPreludeUniformsType => ({
|
|
@@ -18,15 +18,17 @@ export const projectionUniforms = (context: Context, locations: UniformLocations
|
|
|
18
18
|
'u_projection_clipping_plane': new Uniform4f(context, locations.u_projection_clipping_plane),
|
|
19
19
|
'u_projection_transition': new Uniform1f(context, locations.u_projection_transition),
|
|
20
20
|
'u_projection_fallback_matrix': new UniformMatrix4f(context, locations.u_projection_fallback_matrix),
|
|
21
|
+
'u_projection_clip_antimeridian': new Uniform1i(context, locations.u_projection_clip_antimeridian),
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* Converts a {@link ProjectionData} object into the values expected by the projection prelude's uniforms.
|
|
25
26
|
*/
|
|
26
|
-
export const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
export const projectionUniformValues = (projectionData: ProjectionData): UniformValues<ProjectionPreludeUniformsType> => ({
|
|
28
|
+
'u_projection_matrix': projectionData.mainMatrix,
|
|
29
|
+
'u_projection_tile_mercator_coords': projectionData.tileMercatorCoords,
|
|
30
|
+
'u_projection_clipping_plane': projectionData.clippingPlane,
|
|
31
|
+
'u_projection_transition': projectionData.projectionTransition,
|
|
32
|
+
'u_projection_fallback_matrix': projectionData.fallbackMatrix,
|
|
33
|
+
'u_projection_clip_antimeridian': projectionData.clipAntimeridian ? 1 : 0,
|
|
34
|
+
});
|
|
@@ -7,7 +7,7 @@ describe('terrainUniformValues', () => {
|
|
|
7
7
|
test('disables fog when in globe projection mode', () => {
|
|
8
8
|
const eleDelta = 1.0;
|
|
9
9
|
const fogMatrix = mat4.create();
|
|
10
|
-
const sky = new Sky({});
|
|
10
|
+
const sky = new Sky({}, {});
|
|
11
11
|
const pitch = 45;
|
|
12
12
|
const isGlobeMode = true;
|
|
13
13
|
const uniformValues = terrainUniformValues(eleDelta, fogMatrix, sky, pitch, isGlobeMode);
|
|
@@ -22,7 +22,7 @@ describe('terrainUniformValues', () => {
|
|
|
22
22
|
test('enables fog when not in globe projection mode', () => {
|
|
23
23
|
const eleDelta = 1.0;
|
|
24
24
|
const fogMatrix = mat4.create();
|
|
25
|
-
const sky = new Sky({});
|
|
25
|
+
const sky = new Sky({}, {});
|
|
26
26
|
const pitch = 45;
|
|
27
27
|
const isGlobeMode = false;
|
|
28
28
|
const uniformValues = terrainUniformValues(eleDelta, fogMatrix, sky, pitch, isGlobeMode);
|
package/src/webgl/program.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type {UniformBindings, UniformValues, UniformLocations} from './uniform_b
|
|
|
14
14
|
import type {BinderUniform} from '../data/program_configuration.ts';
|
|
15
15
|
import {terrainPreludeUniforms, type TerrainPreludeUniformsType} from './program/terrain_program.ts';
|
|
16
16
|
import type {TerrainData} from '../render/terrain.ts';
|
|
17
|
-
import {
|
|
17
|
+
import {type ProjectionPreludeUniformsType, projectionUniforms, projectionUniformValues} from './program/projection_program.ts';
|
|
18
18
|
import type {ProjectionData} from '../geo/projection/projection_data.ts';
|
|
19
19
|
|
|
20
20
|
export type DrawMode = WebGLRenderingContextBase['LINES'] | WebGLRenderingContextBase['TRIANGLES'] | WebGL2RenderingContext['LINE_STRIP'];
|
|
@@ -135,6 +135,8 @@ export class Program<Us extends UniformBindings> {
|
|
|
135
135
|
const actual = gl.getAttribLocation(this.program, name);
|
|
136
136
|
if (actual >= 0) {
|
|
137
137
|
this.attributes[name] = actual;
|
|
138
|
+
} else {
|
|
139
|
+
delete this.attributes[name];
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -202,9 +204,9 @@ export class Program<Us extends UniformBindings> {
|
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
if (projectionData) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.projectionUniforms[
|
|
207
|
+
const values = projectionUniformValues(projectionData);
|
|
208
|
+
for (const name in this.projectionUniforms) {
|
|
209
|
+
this.projectionUniforms[name].set(values[name]);
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
212
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {beforeEach, describe, test, expect, vi
|
|
1
|
+
import {beforeEach, describe, test, expect, vi} from 'vitest';
|
|
2
2
|
import {RenderToTexture} from './render_to_texture.ts';
|
|
3
3
|
import type {Painter, RTTObject} from '../render/painter.ts';
|
|
4
4
|
import type {LineStyleLayer} from '../style/style_layer/line_style_layer.ts';
|
|
@@ -155,8 +155,8 @@ describe('render to texture', () => {
|
|
|
155
155
|
tile.rttObjects[0] = obj;
|
|
156
156
|
|
|
157
157
|
const otherTileID = new OverscaledTileID(3, 0, 2, 2, 2);
|
|
158
|
-
(terrain.tileManager.getTerrainCoords
|
|
159
|
-
(painter.releaseRTT
|
|
158
|
+
(vi.mocked(terrain.tileManager.getTerrainCoords)).mockReturnValueOnce({[tile.tileID.key]: otherTileID});
|
|
159
|
+
(vi.mocked(painter.releaseRTT)).mockClear();
|
|
160
160
|
|
|
161
161
|
rtt.prepareForRender(style, 0);
|
|
162
162
|
|
|
@@ -218,7 +218,7 @@ describe('render to texture', () => {
|
|
|
218
218
|
|
|
219
219
|
test('should clear tile cache on source state update', () => {
|
|
220
220
|
const state = {revision: 0};
|
|
221
|
-
(style.tileManagers['maine'].getState
|
|
221
|
+
(vi.mocked(style.tileManagers['maine'].getState)).mockReturnValue(state as any);
|
|
222
222
|
|
|
223
223
|
tile.rttObjects[0] = {texture: {}, size: 512} as unknown as RTTObject;
|
|
224
224
|
tile.rttFingerprint = {maine: '923#0'};
|
|
@@ -289,7 +289,7 @@ describe('render to texture', () => {
|
|
|
289
289
|
}
|
|
290
290
|
} as any as Style;
|
|
291
291
|
|
|
292
|
-
(terrain.tileManager.getTerrainCoords
|
|
292
|
+
(vi.mocked(terrain.tileManager.getTerrainCoords)).mockClear();
|
|
293
293
|
rtt.prepareForRender(testStyle, 0);
|
|
294
294
|
|
|
295
295
|
expect(maineTileManager.getVisibleCoordinates).toHaveBeenCalledTimes(1);
|
package/src/webgl/state.test.ts
CHANGED
|
@@ -20,7 +20,7 @@ describe('Value classes', () => {
|
|
|
20
20
|
const v = new Constructor(context);
|
|
21
21
|
expect(v).toBeTruthy();
|
|
22
22
|
const currentV = v.get();
|
|
23
|
-
expect(
|
|
23
|
+
expect(currentV).not.toBeTypeOf('undefined');
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('set', () => {
|
|
@@ -15,7 +15,7 @@ describe('Texture', () => {
|
|
|
15
15
|
return new Context(createNullGL());
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function
|
|
18
|
+
function expectPixelStoreState(context: Context): void {
|
|
19
19
|
expect(context.pixelStoreUnpack.current).toEqual(context.pixelStoreUnpack.default);
|
|
20
20
|
expect(context.pixelStoreUnpackFlipY.current).toEqual(context.pixelStoreUnpackFlipY.default);
|
|
21
21
|
expect(context.pixelStoreUnpackPremultiplyAlpha.current).toEqual(context.pixelStoreUnpackPremultiplyAlpha.default);
|
|
@@ -26,7 +26,7 @@ describe('Texture', () => {
|
|
|
26
26
|
// We test the Texture constructor's side effects
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
28
28
|
const _texture = new Texture(context, testImage, context.gl.RGBA, {premultiply: false});
|
|
29
|
-
|
|
29
|
+
expectPixelStoreState(context);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
test('premultiply=true', () => {
|
|
@@ -34,7 +34,7 @@ describe('Texture', () => {
|
|
|
34
34
|
// We test the Texture constructor's side effects
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
36
|
const _texture = new Texture(context, testImage, context.gl.RGBA, {premultiply: true});
|
|
37
|
-
|
|
37
|
+
expectPixelStoreState(context);
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -71,7 +71,7 @@ describe('VertexBuffer', () => {
|
|
|
71
71
|
|
|
72
72
|
expect(array.arrayBuffer.byteLength).toBe(0);
|
|
73
73
|
expect(array.int16.buffer).not.toBe(originalBuffer);
|
|
74
|
-
expect(array.int16
|
|
74
|
+
expect(array.int16).toHaveLength(0);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
test('dynamic buffer preserves StructArray data after upload', () => {
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {type Plugin} from 'rolldown';
|
|
2
|
-
import {visualizer} from 'rollup-plugin-visualizer';
|
|
3
|
-
|
|
4
|
-
const stats = process.env.BUNDLE === 'stats';
|
|
5
|
-
|
|
6
|
-
export const plugins = (_production: boolean): Plugin[] => stats
|
|
7
|
-
? (['treemap', 'sunburst', 'flamegraph', 'network'] as const).map(template =>
|
|
8
|
-
visualizer({
|
|
9
|
-
template,
|
|
10
|
-
title: `gl-js-${template}`,
|
|
11
|
-
filename: `staging/${template}.html`,
|
|
12
|
-
gzipSize: true,
|
|
13
|
-
brotliSize: true,
|
|
14
|
-
sourcemap: true,
|
|
15
|
-
open: true,
|
|
16
|
-
}) as unknown as Plugin
|
|
17
|
-
)
|
|
18
|
-
: [];
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
2
|
-
import {ColorMode} from '../../webgl/color_mode.ts';
|
|
3
|
-
import {CullFaceMode} from '../../webgl/cull_face_mode.ts';
|
|
4
|
-
import {DepthMode} from '../../webgl/depth_mode.ts';
|
|
5
|
-
import {StencilMode} from '../../webgl/stencil_mode.ts';
|
|
6
|
-
import {warnOnce} from '../../util/util.ts';
|
|
7
|
-
import {projectionErrorMeasurementUniformValues} from '../../webgl/program/projection_error_measurement_program.ts';
|
|
8
|
-
import {Mesh} from '../../render/mesh.ts';
|
|
9
|
-
import {SegmentVector} from '../../data/segment.ts';
|
|
10
|
-
import {PosArray, TriangleIndexArray} from '../../data/array_types.g.ts';
|
|
11
|
-
import posAttributes from '../../data/pos_attributes.ts';
|
|
12
|
-
import {type Framebuffer} from '../../webgl/framebuffer.ts';
|
|
13
|
-
import {type ProjectionGPUContext} from './projection.ts';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* For vector globe the vertex shader projects mercator coordinates to angular coordinates on a sphere.
|
|
17
|
-
* This projection requires some inverse trigonometry `atan(exp(...))`, which is inaccurate on some GPUs (mainly on AMD and Nvidia).
|
|
18
|
-
* The inaccuracy is severe enough to require a workaround. The uncorrected map is shifted north-south by up to several hundred meters in some latitudes.
|
|
19
|
-
* Since the inaccuracy is hardware-dependant and may change in the future, we need to measure the error at runtime.
|
|
20
|
-
*
|
|
21
|
-
* Our approach relies on several assumptions:
|
|
22
|
-
*
|
|
23
|
-
* - the error is only present in the "latitude" component (longitude doesn't need any inverse trigonometry)
|
|
24
|
-
* - the error is continuous and changes slowly with latitude
|
|
25
|
-
* - at zoom levels where the error is noticeable, the error is more-or-less the same across the entire visible map area (and thus can be described with a single number)
|
|
26
|
-
*
|
|
27
|
-
* Solution:
|
|
28
|
-
*
|
|
29
|
-
* Every few frames, launch a GPU shader that measures the error for the current map center latitude, and writes it to a 1x1 texture.
|
|
30
|
-
* Read back that texture, and offset the globe projection matrix according to the error (interpolating smoothly from old error to new error if needed).
|
|
31
|
-
* The texture readback is done asynchronously using Pixel Pack Buffers (WebGL2) when possible, and has a few frames of latency, but that should not be a problem.
|
|
32
|
-
*
|
|
33
|
-
* General operation of this class each frame is:
|
|
34
|
-
*
|
|
35
|
-
* - render the error shader into a fbo, read that pixel into a PBO, place a fence
|
|
36
|
-
* - wait a few frames to allow the GPU (and driver) to actually execute the shader
|
|
37
|
-
* - wait for the fence to be signalled (guaranteeing the shader to actually be executed)
|
|
38
|
-
* - read back the PBO's contents
|
|
39
|
-
* - wait a few more frames
|
|
40
|
-
* - repeat
|
|
41
|
-
*/
|
|
42
|
-
export class ProjectionErrorMeasurement {
|
|
43
|
-
// We wait at least this many frames after measuring until we read back the value.
|
|
44
|
-
// After this period, we might wait more frames until a fence is signalled to make sure the rendering is completed.
|
|
45
|
-
private readonly _readbackWaitFrames = 4;
|
|
46
|
-
// We wait this many frames after *reading back* a measurement until we trigger measure again.
|
|
47
|
-
// We could in theory render the measurement pixel immediately, but we wait to make sure
|
|
48
|
-
// no pipeline stall happens.
|
|
49
|
-
private readonly _measureWaitFrames = 6;
|
|
50
|
-
private readonly _texWidth = 1;
|
|
51
|
-
private readonly _texHeight = 1;
|
|
52
|
-
private readonly _texFormat: number;
|
|
53
|
-
private readonly _texType: number;
|
|
54
|
-
|
|
55
|
-
private _fullscreenTriangle: Mesh;
|
|
56
|
-
private _fbo: Framebuffer;
|
|
57
|
-
private _resultBuffer: Uint8Array;
|
|
58
|
-
private _pbo: WebGLBuffer;
|
|
59
|
-
private _cachedRenderContext: ProjectionGPUContext;
|
|
60
|
-
|
|
61
|
-
private _measuredError: number = 0; // Result of last measurement
|
|
62
|
-
private _updateCount: number = 0;
|
|
63
|
-
private _lastReadbackFrame: number = -1000;
|
|
64
|
-
|
|
65
|
-
get awaitingQuery(): boolean {
|
|
66
|
-
return !!this._readbackQueue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// There is never more than one readback waiting
|
|
70
|
-
private _readbackQueue: {
|
|
71
|
-
frameNumberIssued: number; // Frame number when the data was first computed
|
|
72
|
-
sync: WebGLSync;
|
|
73
|
-
} = null;
|
|
74
|
-
|
|
75
|
-
public constructor(renderContext: ProjectionGPUContext) {
|
|
76
|
-
this._cachedRenderContext = renderContext;
|
|
77
|
-
|
|
78
|
-
const context = renderContext.context;
|
|
79
|
-
const gl = context.gl;
|
|
80
|
-
|
|
81
|
-
this._texFormat = gl.RGBA;
|
|
82
|
-
this._texType = gl.UNSIGNED_BYTE;
|
|
83
|
-
|
|
84
|
-
const vertexArray = new PosArray();
|
|
85
|
-
vertexArray.emplaceBack(-1, -1);
|
|
86
|
-
vertexArray.emplaceBack(2, -1);
|
|
87
|
-
vertexArray.emplaceBack(-1, 2);
|
|
88
|
-
const indexArray = new TriangleIndexArray();
|
|
89
|
-
indexArray.emplaceBack(0, 1, 2);
|
|
90
|
-
|
|
91
|
-
this._fullscreenTriangle = new Mesh(
|
|
92
|
-
context.createVertexBuffer(vertexArray, posAttributes.members),
|
|
93
|
-
context.createIndexBuffer(indexArray),
|
|
94
|
-
SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length)
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
this._resultBuffer = new Uint8Array(4);
|
|
98
|
-
|
|
99
|
-
context.activeTexture.set(gl.TEXTURE1);
|
|
100
|
-
|
|
101
|
-
const texture = gl.createTexture();
|
|
102
|
-
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
103
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
104
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
105
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
106
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
107
|
-
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, this._texWidth, this._texHeight);
|
|
108
|
-
|
|
109
|
-
this._fbo = context.createFramebuffer(this._texWidth, this._texHeight, false, false);
|
|
110
|
-
this._fbo.colorAttachment.set(texture);
|
|
111
|
-
|
|
112
|
-
this._pbo = gl.createBuffer();
|
|
113
|
-
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
|
|
114
|
-
gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.STREAM_READ);
|
|
115
|
-
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public destroy(): void {
|
|
119
|
-
const gl = this._cachedRenderContext.context.gl;
|
|
120
|
-
this._fullscreenTriangle.destroy();
|
|
121
|
-
this._fbo.destroy();
|
|
122
|
-
gl.deleteBuffer(this._pbo);
|
|
123
|
-
this._fullscreenTriangle = null;
|
|
124
|
-
this._fbo = null;
|
|
125
|
-
this._pbo = null;
|
|
126
|
-
this._resultBuffer = null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
public updateErrorLoop(normalizedMercatorY: number, expectedAngleY: number): number {
|
|
130
|
-
const currentFrame = this._updateCount;
|
|
131
|
-
|
|
132
|
-
if (this._readbackQueue) {
|
|
133
|
-
// Try to read back if enough frames elapsed. Otherwise do nothing, just wait another frame.
|
|
134
|
-
if (currentFrame >= this._readbackQueue.frameNumberIssued + this._readbackWaitFrames) {
|
|
135
|
-
// Try to read back - it is possible that this method does nothing, then
|
|
136
|
-
// the readback queue will not be cleared and we will retry next frame.
|
|
137
|
-
this._tryReadback();
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
if (currentFrame >= this._lastReadbackFrame + this._measureWaitFrames) {
|
|
141
|
-
this._renderErrorTexture(normalizedMercatorY, expectedAngleY);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
this._updateCount++;
|
|
146
|
-
return this._measuredError;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private _bindFramebuffer() {
|
|
150
|
-
const context = this._cachedRenderContext.context;
|
|
151
|
-
const gl = context.gl;
|
|
152
|
-
context.activeTexture.set(gl.TEXTURE1);
|
|
153
|
-
gl.bindTexture(gl.TEXTURE_2D, this._fbo.colorAttachment.get());
|
|
154
|
-
context.bindFramebuffer.set(this._fbo.framebuffer);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private _renderErrorTexture(input: number, outputExpected: number): void {
|
|
158
|
-
const context = this._cachedRenderContext.context;
|
|
159
|
-
const gl = context.gl;
|
|
160
|
-
|
|
161
|
-
// Update framebuffer contents
|
|
162
|
-
this._bindFramebuffer();
|
|
163
|
-
context.viewport.set([0, 0, this._texWidth, this._texHeight]);
|
|
164
|
-
context.clear({color: Color.transparent});
|
|
165
|
-
|
|
166
|
-
const program = this._cachedRenderContext.useProgram('projectionErrorMeasurement');
|
|
167
|
-
|
|
168
|
-
program.draw(context, gl.TRIANGLES,
|
|
169
|
-
DepthMode.disabled, StencilMode.disabled,
|
|
170
|
-
ColorMode.unblended, CullFaceMode.disabled,
|
|
171
|
-
projectionErrorMeasurementUniformValues(input, outputExpected), null, null,
|
|
172
|
-
'$clipping', this._fullscreenTriangle.vertexBuffer, this._fullscreenTriangle.indexBuffer,
|
|
173
|
-
this._fullscreenTriangle.segments);
|
|
174
|
-
|
|
175
|
-
// Read back into PBO
|
|
176
|
-
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
|
|
177
|
-
gl.readBuffer(gl.COLOR_ATTACHMENT0);
|
|
178
|
-
gl.readPixels(0, 0, this._texWidth, this._texHeight, this._texFormat, this._texType, 0);
|
|
179
|
-
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
180
|
-
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
181
|
-
gl.flush();
|
|
182
|
-
|
|
183
|
-
this._readbackQueue = {
|
|
184
|
-
frameNumberIssued: this._updateCount,
|
|
185
|
-
sync,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private _tryReadback(): void {
|
|
190
|
-
const gl = this._cachedRenderContext.context.gl;
|
|
191
|
-
|
|
192
|
-
if (!this._readbackQueue) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const waitResult = gl.clientWaitSync(this._readbackQueue.sync, 0, 0);
|
|
197
|
-
|
|
198
|
-
if (waitResult === gl.WAIT_FAILED) {
|
|
199
|
-
warnOnce('WebGL2 clientWaitSync failed.');
|
|
200
|
-
this._readbackQueue = null;
|
|
201
|
-
this._lastReadbackFrame = this._updateCount;
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (waitResult === gl.TIMEOUT_EXPIRED) {
|
|
206
|
-
return; // Wait one more frame
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
|
|
210
|
-
gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, this._resultBuffer, 0, 4);
|
|
211
|
-
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
212
|
-
|
|
213
|
-
// If we made it here, _resultBuffer contains the new measurement
|
|
214
|
-
this._readbackQueue = null;
|
|
215
|
-
this._measuredError = ProjectionErrorMeasurement._parseRGBA8float(this._resultBuffer);
|
|
216
|
-
this._lastReadbackFrame = this._updateCount;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private static _parseRGBA8float(buffer: Uint8Array): number {
|
|
220
|
-
let result = 0;
|
|
221
|
-
result += buffer[0] / 256.0;
|
|
222
|
-
result += buffer[1] / 65536.0;
|
|
223
|
-
result += buffer[2] / 16777216.0;
|
|
224
|
-
if (buffer[3] < 127.0) {
|
|
225
|
-
result = -result;
|
|
226
|
-
}
|
|
227
|
-
return result / 128.0;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
layout(location = 0) in vec2 a_pos;
|
|
2
|
-
|
|
3
|
-
uniform highp float u_input;
|
|
4
|
-
uniform highp float u_output_expected;
|
|
5
|
-
|
|
6
|
-
flat out vec4 v_output_error_encoded;
|
|
7
|
-
|
|
8
|
-
void main() {
|
|
9
|
-
float real_output = 2.0 * atan(exp(PI - (u_input * PI * 2.0))) - PI * 0.5;
|
|
10
|
-
// If we assume that the error visible on the map is never more than 1 km,
|
|
11
|
-
// then the angular error is always smaller than 1/6378 * 2PI = ~0.00098513
|
|
12
|
-
float error = real_output - u_output_expected;
|
|
13
|
-
float abs_error = abs(error) * 128.0; // Scale error by some large value for extra precision
|
|
14
|
-
// abs_error is assumed to be in range 0..1
|
|
15
|
-
v_output_error_encoded.x = min(floor(abs_error * 256.0), 255.0) / 255.0;
|
|
16
|
-
abs_error -= v_output_error_encoded.x;
|
|
17
|
-
v_output_error_encoded.y = min(floor(abs_error * 65536.0), 255.0) / 255.0;
|
|
18
|
-
abs_error -= v_output_error_encoded.x / 255.0;
|
|
19
|
-
v_output_error_encoded.z = min(floor(abs_error * 16777216.0), 255.0) / 255.0;
|
|
20
|
-
v_output_error_encoded.w = error >= 0.0 ? 1.0 : 0.0; // sign
|
|
21
|
-
gl_Position = vec4(a_pos, 0.0, 1.0);
|
|
22
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
|
2
|
-
export default 'layout(location=0) in vec2 a_pos;uniform highp float u_input;uniform highp float u_output_expected;flat out vec4 v_output_error_encoded;void main() {float real_output=2.0*atan(exp(PI-(u_input*PI*2.0)))-PI*0.5;float error=real_output-u_output_expected;float abs_error=abs(error)*128.0;v_output_error_encoded.x=min(floor(abs_error*256.0),255.0)/255.0;abs_error-=v_output_error_encoded.x;v_output_error_encoded.y=min(floor(abs_error*65536.0),255.0)/255.0;abs_error-=v_output_error_encoded.x/255.0;v_output_error_encoded.z=min(floor(abs_error*16777216.0),255.0)/255.0;v_output_error_encoded.w=error >=0.0 ? 1.0 : 0.0;gl_Position=vec4(a_pos,0.0,1.0);}';
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {Uniform1f} from '../uniform_binding.ts';
|
|
2
|
-
import type {Context} from '../../webgl/context.ts';
|
|
3
|
-
import type {UniformValues, UniformLocations} from '../uniform_binding.ts';
|
|
4
|
-
|
|
5
|
-
export type ProjectionErrorMeasurementUniformsType = {
|
|
6
|
-
'u_input': Uniform1f;
|
|
7
|
-
'u_output_expected': Uniform1f;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const projectionErrorMeasurementUniforms = (context: Context, locations: UniformLocations): ProjectionErrorMeasurementUniformsType => ({
|
|
11
|
-
'u_input': new Uniform1f(context, locations.u_input),
|
|
12
|
-
'u_output_expected': new Uniform1f(context, locations.u_output_expected),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const projectionErrorMeasurementUniformValues = (
|
|
16
|
-
input: number,
|
|
17
|
-
outputExpected: number
|
|
18
|
-
): UniformValues<ProjectionErrorMeasurementUniformsType> => ({
|
|
19
|
-
'u_input': input,
|
|
20
|
-
'u_output_expected': outputExpected,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export {projectionErrorMeasurementUniforms, projectionErrorMeasurementUniformValues};
|