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.
Files changed (181) hide show
  1. package/build/readme.md +0 -2
  2. package/dist/maplibre-gl-dev.mjs +433 -573
  3. package/dist/maplibre-gl-dev.mjs.map +1 -1
  4. package/dist/maplibre-gl-shared-dev.mjs +984 -850
  5. package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
  6. package/dist/maplibre-gl-shared.mjs +2 -2
  7. package/dist/maplibre-gl-shared.mjs.map +1 -1
  8. package/dist/maplibre-gl-worker-dev.mjs +14 -12
  9. package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
  10. package/dist/maplibre-gl-worker.mjs +2 -2
  11. package/dist/maplibre-gl-worker.mjs.map +1 -1
  12. package/dist/maplibre-gl.css +1 -1
  13. package/dist/maplibre-gl.d.ts +119 -42
  14. package/dist/maplibre-gl.mjs +22 -22
  15. package/dist/maplibre-gl.mjs.map +1 -1
  16. package/package.json +20 -22
  17. package/src/css/maplibre-gl.css +4 -0
  18. package/src/data/bucket/circle_bucket.ts +3 -2
  19. package/src/data/bucket/fill_bucket.ts +3 -2
  20. package/src/data/bucket/fill_extrusion_bucket.test.ts +22 -0
  21. package/src/data/bucket/fill_extrusion_bucket.ts +17 -4
  22. package/src/data/bucket/line_bucket.test.ts +5 -5
  23. package/src/data/bucket/line_bucket.ts +3 -2
  24. package/src/data/bucket/round_polygon_corners.test.ts +159 -0
  25. package/src/data/bucket/round_polygon_corners.ts +121 -0
  26. package/src/data/bucket/symbol_bucket.test.ts +7 -7
  27. package/src/data/dem_data.test.ts +23 -23
  28. package/src/data/feature_position_map.test.ts +1 -1
  29. package/src/data/segment.test.ts +14 -14
  30. package/src/geo/bounds.test.ts +4 -4
  31. package/src/geo/lng_lat_bounds.test.ts +1 -1
  32. package/src/geo/projection/covering_tiles.bench.ts +39 -0
  33. package/src/geo/projection/covering_tiles.test.ts +3 -7
  34. package/src/geo/projection/covering_tiles.ts +37 -2
  35. package/src/geo/projection/globe_covering_tiles_details_provider.ts +4 -4
  36. package/src/geo/projection/globe_projection.test.ts +2 -2
  37. package/src/geo/projection/globe_projection.ts +6 -15
  38. package/src/geo/projection/globe_transform.test.ts +3 -6
  39. package/src/geo/projection/globe_transform.ts +4 -7
  40. package/src/geo/projection/mercator_covering_tiles_details_provider.ts +4 -4
  41. package/src/geo/projection/mercator_projection.ts +1 -13
  42. package/src/geo/projection/mercator_transform.test.ts +1 -1
  43. package/src/geo/projection/mercator_transform.ts +2 -1
  44. package/src/geo/projection/mercator_utils.test.ts +8 -8
  45. package/src/geo/projection/projection.ts +0 -27
  46. package/src/geo/projection/projection_data.ts +8 -0
  47. package/src/geo/projection/projection_factory.ts +3 -3
  48. package/src/geo/projection/vertical_perspective_projection.ts +3 -60
  49. package/src/geo/projection/vertical_perspective_transform.ts +16 -23
  50. package/src/geo/transform_interface.ts +1 -2
  51. package/src/index.test.ts +7 -12
  52. package/src/index.ts +4 -2
  53. package/src/render/glyph_manager.test.ts +7 -22
  54. package/src/render/painter.test.ts +2 -0
  55. package/src/render/painter.ts +8 -11
  56. package/src/render/subdivision.bench.ts +39 -0
  57. package/src/render/subdivision.test.ts +22 -23
  58. package/src/render/subdivision.ts +46 -0
  59. package/src/render/terrain.test.ts +68 -34
  60. package/src/render/terrain.ts +69 -26
  61. package/src/shaders/glsl/_projection_globe.fragment.glsl +12 -0
  62. package/src/shaders/glsl/_projection_globe.fragment.glsl.g.ts +2 -0
  63. package/src/shaders/glsl/_projection_globe.vertex.glsl +29 -10
  64. package/src/shaders/glsl/_projection_globe.vertex.glsl.g.ts +1 -1
  65. package/src/shaders/glsl/_projection_mercator.fragment.glsl +4 -0
  66. package/src/shaders/glsl/_projection_mercator.fragment.glsl.g.ts +2 -0
  67. package/src/shaders/glsl/line.fragment.glsl +2 -0
  68. package/src/shaders/glsl/line.fragment.glsl.g.ts +1 -1
  69. package/src/shaders/glsl/line_gradient.fragment.glsl +2 -0
  70. package/src/shaders/glsl/line_gradient.fragment.glsl.g.ts +1 -1
  71. package/src/shaders/glsl/line_gradient_sdf.fragment.glsl +2 -0
  72. package/src/shaders/glsl/line_gradient_sdf.fragment.glsl.g.ts +1 -1
  73. package/src/shaders/glsl/line_pattern.fragment.glsl +2 -0
  74. package/src/shaders/glsl/line_pattern.fragment.glsl.g.ts +1 -1
  75. package/src/shaders/glsl/line_sdf.fragment.glsl +2 -0
  76. package/src/shaders/glsl/line_sdf.fragment.glsl.g.ts +1 -1
  77. package/src/shaders/shaders.ts +4 -6
  78. package/src/source/canvas_source.test.ts +1 -1
  79. package/src/source/geojson_source.test.ts +32 -4
  80. package/src/source/geojson_source.ts +41 -0
  81. package/src/source/geojson_source_diff.test.ts +2 -2
  82. package/src/source/geojson_worker_source.test.ts +9 -5
  83. package/src/source/image_source.test.ts +137 -62
  84. package/src/source/image_source.ts +49 -12
  85. package/src/source/raster_dem_tile_source.test.ts +2 -2
  86. package/src/source/raster_tile_source.test.ts +64 -9
  87. package/src/source/raster_tile_source.ts +2 -1
  88. package/src/source/rtl_text_plugin_main_thread.test.ts +14 -14
  89. package/src/source/source.test.ts +8 -8
  90. package/src/source/vector_tile_source.test.ts +1 -1
  91. package/src/source/vector_tile_worker_source.test.ts +7 -5
  92. package/src/source/video_source.test.ts +1 -1
  93. package/src/source/worker.test.ts +1 -1
  94. package/src/source/worker_tile.test.ts +10 -8
  95. package/src/style/light.test.ts +9 -9
  96. package/src/style/light.ts +3 -3
  97. package/src/style/load_glyph_range.test.ts +8 -8
  98. package/src/style/load_sprite.test.ts +1 -1
  99. package/src/style/properties.test.ts +94 -2
  100. package/src/style/properties.ts +30 -1
  101. package/src/style/sky.test.ts +9 -9
  102. package/src/style/sky.ts +2 -2
  103. package/src/style/style.test.ts +38 -36
  104. package/src/style/style.ts +3 -3
  105. package/src/style/style_layer/color_relief_style_layer.test.ts +1 -1
  106. package/src/style/style_layer/fill_extrusion_style_layer.ts +6 -3
  107. package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +14 -1
  108. package/src/style/style_layer/raster_style_layer.test.ts +2 -2
  109. package/src/style/style_layer.test.ts +5 -51
  110. package/src/style/validate_style.test.ts +1 -1
  111. package/src/symbol/collision_index.test.ts +1 -1
  112. package/src/symbol/grid_index.test.ts +1 -1
  113. package/src/tile/terrain_tile_manager.test.ts +2 -2
  114. package/src/tile/tile_id.test.ts +5 -5
  115. package/src/tile/tile_manager.test.ts +113 -72
  116. package/src/tile/tile_manager.ts +2 -2
  117. package/src/tile/tile_manager_in_view_tiles.test.ts +2 -2
  118. package/src/ui/camera.test.ts +138 -135
  119. package/src/ui/control/geolocate_control.test.ts +7 -30
  120. package/src/ui/handler/dblclick_zoom.test.ts +2 -2
  121. package/src/ui/handler/mouse.ts +15 -7
  122. package/src/ui/handler/mouse_handler_interface.test.ts +21 -1
  123. package/src/ui/handler/scroll_zoom.test.ts +2 -0
  124. package/src/ui/handler_manager.test.ts +7 -7
  125. package/src/ui/hash.test.ts +1 -0
  126. package/src/ui/map.ts +45 -20
  127. package/src/ui/map_tests/map_basic.test.ts +11 -23
  128. package/src/ui/map_tests/map_calculate_camera_options.test.ts +6 -6
  129. package/src/ui/map_tests/map_events.test.ts +5 -7
  130. package/src/ui/map_tests/map_global_state.test.ts +3 -3
  131. package/src/ui/map_tests/map_images.test.ts +4 -3
  132. package/src/ui/map_tests/map_is_moving.test.ts +1 -1
  133. package/src/ui/map_tests/map_is_rotating.test.ts +1 -1
  134. package/src/ui/map_tests/map_layer.test.ts +2 -1
  135. package/src/ui/map_tests/map_options.test.ts +1 -5
  136. package/src/ui/map_tests/map_pitch.test.ts +6 -6
  137. package/src/ui/map_tests/map_refresh_tiles.test.ts +2 -2
  138. package/src/ui/map_tests/map_render.test.ts +1 -1
  139. package/src/ui/map_tests/map_set_lod_params.test.ts +1 -1
  140. package/src/ui/map_tests/map_style.test.ts +9 -1
  141. package/src/ui/map_tests/map_terrain.test.ts +41 -1
  142. package/src/ui/map_tests/map_zoom.test.ts +15 -15
  143. package/src/ui/marker.test.ts +71 -14
  144. package/src/ui/marker.ts +30 -6
  145. package/src/ui/popup.test.ts +3 -3
  146. package/src/util/actor.test.ts +6 -6
  147. package/src/util/ajax.test.ts +27 -39
  148. package/src/util/browser.test.ts +7 -9
  149. package/src/util/create_tile_mesh.test.ts +2 -2
  150. package/src/util/dispatcher.test.ts +1 -1
  151. package/src/util/evented.test.ts +4 -1
  152. package/src/util/fast_maths.test.ts +4 -4
  153. package/src/util/image_request.test.ts +6 -4
  154. package/src/util/primitives/bounding_volume_cache.ts +3 -1
  155. package/src/util/struct_array.test.ts +2 -2
  156. package/src/util/task_queue.test.ts +1 -1
  157. package/src/util/test/util.ts +1 -0
  158. package/src/util/time_control.test.ts +2 -2
  159. package/src/util/transferable_grid_index.test.ts +1 -1
  160. package/src/util/util.test.ts +27 -13
  161. package/src/util/verticalize_punctuation.test.ts +4 -4
  162. package/src/util/web_worker_transfer.test.ts +5 -5
  163. package/src/webgl/draw/draw_custom.test.ts +13 -13
  164. package/src/webgl/draw/draw_debug.test.ts +1 -1
  165. package/src/webgl/draw/draw_fill.test.ts +13 -12
  166. package/src/webgl/draw/draw_symbol.test.ts +17 -16
  167. package/src/webgl/program/program_uniforms.ts +0 -3
  168. package/src/webgl/program/projection_program.ts +12 -10
  169. package/src/webgl/program/terrain_program.test.ts +2 -2
  170. package/src/webgl/program.ts +6 -4
  171. package/src/webgl/render_to_texture.test.ts +5 -5
  172. package/src/webgl/state.test.ts +1 -1
  173. package/src/webgl/texture.test.ts +3 -3
  174. package/src/webgl/vertex_buffer.test.ts +1 -1
  175. package/build/rolldown_plugins.ts +0 -18
  176. package/src/geo/projection/globe_projection_error_measurement.ts +0 -229
  177. package/src/shaders/glsl/projection_error_measurement.fragment.glsl +0 -5
  178. package/src/shaders/glsl/projection_error_measurement.fragment.glsl.g.ts +0 -2
  179. package/src/shaders/glsl/projection_error_measurement.vertex.glsl +0 -22
  180. package/src/shaders/glsl/projection_error_measurement.vertex.glsl.g.ts +0 -2
  181. 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
- * Maps a field name in {@link ProjectionData} to its corresponding uniform name in {@link ProjectionPreludeUniformsType}.
25
+ * Converts a {@link ProjectionData} object into the values expected by the projection prelude's uniforms.
25
26
  */
26
- export const projectionObjectToUniformMap: {[field in keyof ProjectionData]: keyof ProjectionPreludeUniformsType} = {
27
- mainMatrix: 'u_projection_matrix',
28
- tileMercatorCoords: 'u_projection_tile_mercator_coords',
29
- clippingPlane: 'u_projection_clipping_plane',
30
- projectionTransition: 'u_projection_transition',
31
- fallbackMatrix: 'u_projection_fallback_matrix',
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);
@@ -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 {projectionObjectToUniformMap, type ProjectionPreludeUniformsType, projectionUniforms} from './program/projection_program.ts';
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
- for (const fieldName in projectionData) {
206
- const uniformName = projectionObjectToUniformMap[fieldName];
207
- this.projectionUniforms[uniformName].set(projectionData[fieldName]);
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, type Mock} from 'vitest';
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 as Mock).mockReturnValueOnce({[tile.tileID.key]: otherTileID});
159
- (painter.releaseRTT as Mock).mockClear();
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 as Mock).mockReturnValue(state);
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 as Mock).mockClear();
292
+ (vi.mocked(terrain.tileManager.getTerrainCoords)).mockClear();
293
293
  rtt.prepareForRender(testStyle, 0);
294
294
 
295
295
  expect(maineTileManager.getVisibleCoordinates).toHaveBeenCalledTimes(1);
@@ -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(typeof currentV).not.toBe('undefined');
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 checkPixelStoreState(context: Context): void {
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
- checkPixelStoreState(context);
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
- checkPixelStoreState(context);
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.length).toBe(0);
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,5 +0,0 @@
1
- flat in vec4 v_output_error_encoded;
2
-
3
- void main() {
4
- fragColor = v_output_error_encoded;
5
- }
@@ -1,2 +0,0 @@
1
- // This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
2
- export default 'flat in vec4 v_output_error_encoded;void main() {fragColor=v_output_error_encoded;}';
@@ -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};