@deck.gl/core 9.3.0-alpha.3 → 9.3.0-alpha.6

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 (88) hide show
  1. package/dist/controllers/terrain-controller.d.ts +7 -6
  2. package/dist/controllers/terrain-controller.d.ts.map +1 -1
  3. package/dist/controllers/terrain-controller.js +58 -39
  4. package/dist/controllers/terrain-controller.js.map +1 -1
  5. package/dist/dist.dev.js +3748 -1377
  6. package/dist/index.cjs +545 -219
  7. package/dist/index.cjs.map +4 -4
  8. package/dist/lib/attribute/gl-utils.d.ts +1 -2
  9. package/dist/lib/attribute/gl-utils.d.ts.map +1 -1
  10. package/dist/lib/attribute/gl-utils.js +2 -2
  11. package/dist/lib/attribute/gl-utils.js.map +1 -1
  12. package/dist/lib/deck-picker.d.ts +3 -2
  13. package/dist/lib/deck-picker.d.ts.map +1 -1
  14. package/dist/lib/deck-picker.js +74 -17
  15. package/dist/lib/deck-picker.js.map +1 -1
  16. package/dist/lib/deck.d.ts +62 -0
  17. package/dist/lib/deck.d.ts.map +1 -1
  18. package/dist/lib/deck.js +219 -77
  19. package/dist/lib/deck.js.map +1 -1
  20. package/dist/lib/init.js +2 -2
  21. package/dist/lib/layer.d.ts.map +1 -1
  22. package/dist/lib/layer.js +60 -9
  23. package/dist/lib/layer.js.map +1 -1
  24. package/dist/lib/view-manager.js +1 -1
  25. package/dist/lib/view-manager.js.map +1 -1
  26. package/dist/passes/layers-pass.d.ts.map +1 -1
  27. package/dist/passes/layers-pass.js +13 -0
  28. package/dist/passes/layers-pass.js.map +1 -1
  29. package/dist/passes/pick-layers-pass.d.ts.map +1 -1
  30. package/dist/passes/pick-layers-pass.js +7 -2
  31. package/dist/passes/pick-layers-pass.js.map +1 -1
  32. package/dist/passes/screen-pass-uniforms.d.ts +1 -1
  33. package/dist/passes/screen-pass-uniforms.js +1 -1
  34. package/dist/shaderlib/color/color.d.ts +1 -4
  35. package/dist/shaderlib/color/color.d.ts.map +1 -1
  36. package/dist/shaderlib/color/color.js +0 -12
  37. package/dist/shaderlib/color/color.js.map +1 -1
  38. package/dist/shaderlib/index.d.ts +1 -2
  39. package/dist/shaderlib/index.d.ts.map +1 -1
  40. package/dist/shaderlib/index.js +1 -2
  41. package/dist/shaderlib/index.js.map +1 -1
  42. package/dist/shaderlib/misc/layer-uniforms.d.ts +3 -2
  43. package/dist/shaderlib/misc/layer-uniforms.d.ts.map +1 -1
  44. package/dist/shaderlib/misc/layer-uniforms.js +10 -1
  45. package/dist/shaderlib/misc/layer-uniforms.js.map +1 -1
  46. package/dist/shaderlib/picking/picking.d.ts +5 -3
  47. package/dist/shaderlib/picking/picking.d.ts.map +1 -1
  48. package/dist/shaderlib/picking/picking.js +29 -0
  49. package/dist/shaderlib/picking/picking.js.map +1 -1
  50. package/dist/shaderlib/project/project.glsl.js +1 -1
  51. package/dist/shaderlib/project/project.wgsl.d.ts.map +1 -1
  52. package/dist/shaderlib/project/project.wgsl.js +4 -6
  53. package/dist/shaderlib/project/project.wgsl.js.map +1 -1
  54. package/dist/shaderlib/shadow/shadow.d.ts +2 -2
  55. package/dist/shaderlib/shadow/shadow.js +1 -1
  56. package/dist/transitions/gpu-interpolation-transition.js +2 -2
  57. package/dist/transitions/gpu-interpolation-transition.js.map +1 -1
  58. package/dist/transitions/gpu-spring-transition.js +1 -1
  59. package/dist/transitions/gpu-transition-utils.d.ts.map +1 -1
  60. package/dist/transitions/gpu-transition-utils.js +3 -4
  61. package/dist/transitions/gpu-transition-utils.js.map +1 -1
  62. package/dist/utils/texture.d.ts.map +1 -1
  63. package/dist/utils/texture.js +3 -0
  64. package/dist/utils/texture.js.map +1 -1
  65. package/dist/utils/typed-array-manager.js.map +1 -1
  66. package/dist.min.js +582 -247
  67. package/package.json +8 -9
  68. package/src/controllers/terrain-controller.ts +60 -51
  69. package/src/lib/attribute/gl-utils.ts +2 -2
  70. package/src/lib/deck-picker.ts +98 -17
  71. package/src/lib/deck.ts +334 -86
  72. package/src/lib/layer.ts +98 -8
  73. package/src/lib/view-manager.ts +1 -1
  74. package/src/passes/layers-pass.ts +21 -1
  75. package/src/passes/pick-layers-pass.ts +6 -2
  76. package/src/passes/screen-pass-uniforms.ts +1 -1
  77. package/src/shaderlib/color/color.ts +0 -12
  78. package/src/shaderlib/index.ts +1 -3
  79. package/src/shaderlib/misc/layer-uniforms.ts +11 -1
  80. package/src/shaderlib/picking/picking.ts +30 -0
  81. package/src/shaderlib/project/project.glsl.ts +1 -1
  82. package/src/shaderlib/project/project.wgsl.ts +4 -6
  83. package/src/shaderlib/shadow/shadow.ts +1 -1
  84. package/src/transitions/gpu-interpolation-transition.ts +2 -2
  85. package/src/transitions/gpu-spring-transition.ts +1 -1
  86. package/src/transitions/gpu-transition-utils.ts +4 -5
  87. package/src/utils/texture.ts +2 -0
  88. package/src/utils/typed-array-manager.ts +3 -3
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "deck.gl core library",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "version": "9.3.0-alpha.3",
6
+ "version": "9.3.0-alpha.6",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -40,13 +40,12 @@
40
40
  "prepublishOnly": "npm run build-debugger && npm run build-bundle && npm run build-bundle -- --env=dev"
41
41
  },
42
42
  "dependencies": {
43
- "@loaders.gl/core": "^4.4.0-alpha.18",
44
- "@loaders.gl/images": "^4.4.0-alpha.18",
45
- "@luma.gl/constants": "^9.3.0-alpha.6",
46
- "@luma.gl/core": "^9.3.0-alpha.6",
47
- "@luma.gl/engine": "^9.3.0-alpha.6",
48
- "@luma.gl/shadertools": "^9.3.0-alpha.6",
49
- "@luma.gl/webgl": "^9.3.0-alpha.6",
43
+ "@loaders.gl/core": "^4.4.1",
44
+ "@loaders.gl/images": "^4.4.1",
45
+ "@luma.gl/core": "^9.3.2",
46
+ "@luma.gl/engine": "^9.3.2",
47
+ "@luma.gl/shadertools": "^9.3.2",
48
+ "@luma.gl/webgl": "^9.3.2",
50
49
  "@math.gl/core": "^4.1.0",
51
50
  "@math.gl/sun": "^4.1.0",
52
51
  "@math.gl/types": "^4.1.0",
@@ -58,5 +57,5 @@
58
57
  "gl-matrix": "^3.0.0",
59
58
  "mjolnir.js": "^3.0.0"
60
59
  },
61
- "gitHead": "25b39cfc26abd50aa900796fae75c2b5fedada69"
60
+ "gitHead": "6aff0d4eccd01a06a388ab1c07042a94daa3eb31"
62
61
  }
@@ -16,6 +16,10 @@ export default class TerrainController extends MapController {
16
16
  private _terrainAltitude?: number = undefined;
17
17
  /** Raw (unsmoothed) terrain altitude from latest pick */
18
18
  private _terrainAltitudeTarget?: number = undefined;
19
+ /** rAF handle for periodic terrain altitude picking */
20
+ private _pickFrameId: number | null = null;
21
+ /** Timestamp of last pick */
22
+ private _lastPickTime: number = 0;
19
23
 
20
24
  setProps(
21
25
  props: ControllerProps &
@@ -26,20 +30,50 @@ export default class TerrainController extends MapController {
26
30
  ) {
27
31
  super.setProps({rotationPivot: '3d', ...props});
28
32
 
29
- // Drive smoothing animation when terrain altitude hasn't converged yet.
30
- if (
31
- this._terrainAltitude !== undefined &&
32
- this._terrainAltitudeTarget !== undefined &&
33
- Math.abs(this._terrainAltitudeTarget - this._terrainAltitude) > 0.01
34
- ) {
35
- this.updateViewport(
36
- new this.ControllerState({
37
- makeViewport: this.makeViewport,
38
- ...this.props,
39
- ...this.state
40
- } as any)
41
- );
33
+ // Periodically pick terrain altitude at the viewport center using rAF.
34
+ // Keeps the altitude cache warm so interactions don't need expensive
35
+ // synchronous GPU readbacks. rAF naturally pauses when tab is backgrounded.
36
+ if (this._pickFrameId === null) {
37
+ const loop = () => {
38
+ const now = Date.now();
39
+ if (now - this._lastPickTime > 500 && !this.isDragging()) {
40
+ this._lastPickTime = now;
41
+ this._pickTerrainCenterAltitude();
42
+ // On first successful pick, rebase viewport to terrain altitude.
43
+ // Runs from rAF (outside React render) so onViewStateChange won't loop.
44
+ if (this._terrainAltitude === undefined && this._terrainAltitudeTarget !== undefined) {
45
+ this._terrainAltitude = this._terrainAltitudeTarget;
46
+ const controllerState = new this.ControllerState({
47
+ makeViewport: this.makeViewport,
48
+ ...this.props,
49
+ ...this.state
50
+ } as any);
51
+ const rebaseProps = this._rebaseViewport(this._terrainAltitudeTarget, controllerState);
52
+ if (rebaseProps) {
53
+ // Build a controllerState that includes the rebase adjustments so
54
+ // internal state matches the rebased viewState after React round-trip.
55
+ const rebasedState = new this.ControllerState({
56
+ makeViewport: this.makeViewport,
57
+ ...this.props,
58
+ ...this.state,
59
+ ...rebaseProps
60
+ } as any);
61
+ super.updateViewport(rebasedState);
62
+ }
63
+ }
64
+ }
65
+ this._pickFrameId = requestAnimationFrame(loop);
66
+ };
67
+ this._pickFrameId = requestAnimationFrame(loop);
68
+ }
69
+ }
70
+
71
+ finalize() {
72
+ if (this._pickFrameId !== null) {
73
+ cancelAnimationFrame(this._pickFrameId);
74
+ this._pickFrameId = null;
42
75
  }
76
+ super.finalize();
43
77
  }
44
78
 
45
79
  protected updateViewport(
@@ -47,24 +81,16 @@ export default class TerrainController extends MapController {
47
81
  extraProps: Record<string, any> | null = null,
48
82
  interactionState: InteractionState = {}
49
83
  ): void {
50
- const SMOOTHING = 0.05;
51
-
52
- // No interactions yet, do not update
53
- if (this._terrainAltitudeTarget === undefined) return;
54
-
84
+ // Not initialized yet — pass through to MapController
55
85
  if (this._terrainAltitude === undefined) {
56
- // First interaction, rebase to avoid jump
57
- this._terrainAltitude = this._terrainAltitudeTarget;
58
- extraProps = this._rebaseViewport(
59
- this._terrainAltitudeTarget,
60
- newControllerState,
61
- extraProps
62
- );
63
- } else {
64
- // Standard interaction, smoothly blend target into actual altitude
65
- this._terrainAltitude += (this._terrainAltitudeTarget - this._terrainAltitude) * SMOOTHING;
86
+ super.updateViewport(newControllerState, extraProps, interactionState);
87
+ return;
66
88
  }
67
89
 
90
+ // Smoothly blend toward target altitude
91
+ const SMOOTHING = 0.05;
92
+ this._terrainAltitude += (this._terrainAltitudeTarget! - this._terrainAltitude) * SMOOTHING;
93
+
68
94
  const viewportProps = newControllerState.getViewportProps();
69
95
  const pos = viewportProps.position || [0, 0, 0];
70
96
  extraProps = {
@@ -75,23 +101,7 @@ export default class TerrainController extends MapController {
75
101
  super.updateViewport(newControllerState, extraProps, interactionState);
76
102
  }
77
103
 
78
- protected _onPanStart(event: MjolnirGestureEvent): boolean {
79
- this._pickTerrainCenterAltitude();
80
- return super._onPanStart(event);
81
- }
82
-
83
- protected _onWheel(event: MjolnirWheelEvent): boolean {
84
- this._pickTerrainCenterAltitude();
85
- return super._onWheel(event);
86
- }
87
-
88
- protected _onDoubleClick(event: MjolnirGestureEvent): boolean {
89
- this._pickTerrainCenterAltitude();
90
- return super._onDoubleClick(event);
91
- }
92
-
93
104
  private _pickTerrainCenterAltitude(): void {
94
- // TODO use async picking?
95
105
  if (!this.pickPosition) {
96
106
  return;
97
107
  }
@@ -103,13 +113,12 @@ export default class TerrainController extends MapController {
103
113
  }
104
114
 
105
115
  /**
106
- * Utility function to return viewport that looks the same, but with
107
- * a position shifted to [0, 0, altitude]
116
+ * Compute viewport adjustments to keep the view visually the same
117
+ * when shifting position to [0, 0, altitude].
108
118
  */
109
119
  private _rebaseViewport(
110
120
  altitude: number,
111
- newControllerState: MapState,
112
- extraProps: Record<string, any> | null
121
+ newControllerState: MapState
113
122
  ): Record<string, any> | null {
114
123
  const viewportProps = newControllerState.getViewportProps();
115
124
  const oldViewport = this.makeViewport({...viewportProps, position: [0, 0, 0]});
@@ -119,7 +128,7 @@ export default class TerrainController extends MapController {
119
128
  const cameraHeightAboveOldCenter = oldCameraPos[2];
120
129
  const newCameraHeightAboveCenter = cameraHeightAboveOldCenter - centerZOffset;
121
130
  if (newCameraHeightAboveCenter <= 0) {
122
- return extraProps;
131
+ return null;
123
132
  }
124
133
 
125
134
  const zoomDelta = Math.log2(cameraHeightAboveOldCenter / newCameraHeightAboveCenter);
@@ -139,8 +148,8 @@ export default class TerrainController extends MapController {
139
148
  typeof newViewport.panByPosition3D === 'function'
140
149
  ) {
141
150
  const adjusted = newViewport.panByPosition3D(worldPoint, screenCenter);
142
- return {...extraProps, position: [0, 0, altitude], zoom: newZoom, ...adjusted};
151
+ return {position: [0, 0, altitude], zoom: newZoom, ...adjusted};
143
152
  }
144
- return extraProps;
153
+ return null;
145
154
  }
146
155
  }
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {getTypedArrayConstructor, getDataType} from '@luma.gl/core';
5
+ import {dataTypeDecoder, getTypedArrayConstructor} from '@luma.gl/core';
6
6
  import type {BufferAttributeLayout, VertexFormat} from '@luma.gl/core';
7
7
  import type {TypedArrayConstructor} from '../../types/types';
8
8
  import type {BufferAccessor, DataColumnSettings, LogicalDataType} from './data-column';
@@ -20,7 +20,7 @@ export function typedArrayFromDataType(type: LogicalDataType): TypedArrayConstru
20
20
  }
21
21
  }
22
22
 
23
- export const dataTypeFromTypedArray = getDataType;
23
+ export const dataTypeFromTypedArray = dataTypeDecoder.getDataType.bind(dataTypeDecoder);
24
24
 
25
25
  export function getBufferAttributeLayout(
26
26
  name: string,
@@ -2,8 +2,10 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import {Buffer, Texture} from '@luma.gl/core';
5
6
  import type {Device} from '@luma.gl/core';
6
7
  import PickLayersPass, {PickingColorDecoder} from '../passes/pick-layers-pass';
8
+ import log from '../utils/log';
7
9
  import {getClosestObject, getUniqueObjects, PickedPixel} from './picking/query-object';
8
10
  import {
9
11
  processPickInfo,
@@ -116,7 +118,7 @@ export default class DeckPicker {
116
118
  /**
117
119
  * Pick the closest info at given coordinate
118
120
  * @returns picking info
119
- * @deprecated WebGL only - use pickObjectAsync instead
121
+ * @note WebGL only - use pickObjectAsync instead
120
122
  */
121
123
  pickObject(opts: PickByPointOptions & PickOperationContext) {
122
124
  return this._pickClosestObject(opts);
@@ -125,7 +127,7 @@ export default class DeckPicker {
125
127
  /**
126
128
  * Get all unique infos within a bounding box
127
129
  * @returns all unique infos within a bounding box
128
- * @deprecated WebGL only - use pickObjectAsync instead
130
+ * @note WebGL only - use pickObjectAsync instead
129
131
  */
130
132
  pickObjects(opts: PickByRectOptions & PickOperationContext) {
131
133
  return this._pickVisibleObjects(opts);
@@ -158,14 +160,26 @@ export default class DeckPicker {
158
160
  _resizeBuffer() {
159
161
  // Create a frame buffer if not already available
160
162
  if (!this.pickingFBO) {
163
+ const pickingColorTexture = this.device.createTexture({
164
+ format: 'rgba8unorm',
165
+ width: 1,
166
+ height: 1,
167
+ usage: Texture.RENDER_ATTACHMENT | Texture.COPY_SRC
168
+ });
161
169
  this.pickingFBO = this.device.createFramebuffer({
162
- colorAttachments: ['rgba8unorm'],
170
+ colorAttachments: [pickingColorTexture],
163
171
  depthStencilAttachment: 'depth16unorm'
164
172
  });
165
173
 
166
174
  if (this.device.isTextureFormatRenderable('rgba32float')) {
175
+ const depthColorTexture = this.device.createTexture({
176
+ format: 'rgba32float',
177
+ width: 1,
178
+ height: 1,
179
+ usage: Texture.RENDER_ATTACHMENT | Texture.COPY_SRC
180
+ });
167
181
  const depthFBO = this.device.createFramebuffer({
168
- colorAttachments: ['rgba32float'],
182
+ colorAttachments: [depthColorTexture],
169
183
  depthStencilAttachment: 'depth16unorm'
170
184
  });
171
185
  this.depthFBO = depthFBO;
@@ -258,7 +272,7 @@ export default class DeckPicker {
258
272
  let pickInfo: PickedPixel;
259
273
 
260
274
  if (deviceRect) {
261
- const pickedResult = this._drawAndSample({
275
+ const pickedResult = await this._drawAndSampleAsync({
262
276
  layers: pickableLayers,
263
277
  views,
264
278
  viewports,
@@ -286,7 +300,7 @@ export default class DeckPicker {
286
300
  let z;
287
301
  const depthLayers = this._getDepthLayers(pickInfo, pickableLayers, unproject3D);
288
302
  if (depthLayers.length > 0) {
289
- const {pickedColors: pickedColors2} = this._drawAndSample(
303
+ const {pickedColors: pickedColors2} = await this._drawAndSampleAsync(
290
304
  {
291
305
  layers: depthLayers,
292
306
  views,
@@ -566,7 +580,7 @@ export default class DeckPicker {
566
580
  height: deviceTop - deviceBottom
567
581
  };
568
582
 
569
- const pickedResult = this._drawAndSample({
583
+ const pickedResult = await this._drawAndSampleAsync({
570
584
  layers: pickableLayers,
571
585
  views,
572
586
  viewports,
@@ -814,21 +828,88 @@ export default class DeckPicker {
814
828
  const {decodePickingColor, stats} = this.pickLayersPass.render(opts);
815
829
  this._updateStats(stats);
816
830
 
817
- // Read from an already rendered picking buffer
818
- // Returns an Uint8ClampedArray of picked pixels
819
831
  const {x, y, width, height} = deviceRect;
820
- const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
821
- this.device.readPixelsToArrayWebGL(pickingFBO as Framebuffer, {
822
- sourceX: x,
823
- sourceY: y,
824
- sourceWidth: width,
825
- sourceHeight: height,
826
- target: pickedColors
827
- });
832
+ const texture = (pickingFBO as Framebuffer).colorAttachments[0]?.texture;
833
+ if (!texture) {
834
+ throw new Error('Picking framebuffer color attachment is missing');
835
+ }
836
+
837
+ const pickedColors = await this._readTextureDataAsync(
838
+ texture,
839
+ {x, y, width, height},
840
+ pickZ ? Float32Array : Uint8Array
841
+ );
842
+
843
+ if (!pickZ) {
844
+ let hasNonZeroAlpha = false;
845
+ for (let i = 3; i < pickedColors.length; i += 4) {
846
+ if (pickedColors[i] !== 0) {
847
+ hasNonZeroAlpha = true;
848
+ break;
849
+ }
850
+ }
851
+ if (!hasNonZeroAlpha && pickedColors.length > 0) {
852
+ log.warn('Async pick readback returned only zero alpha values', {
853
+ deviceRect,
854
+ bytes: Array.from(pickedColors.subarray(0, Math.min(pickedColors.length, 16)))
855
+ })();
856
+ }
857
+ }
828
858
 
829
859
  return {pickedColors, decodePickingColor};
830
860
  }
831
861
 
862
+ private async _readTextureDataAsync<T extends Uint8Array | Float32Array>(
863
+ texture: Texture,
864
+ options: {x: number; y: number; width: number; height: number},
865
+ ArrayType: Uint8ArrayConstructor | Float32ArrayConstructor
866
+ ): Promise<T> {
867
+ const {width, height} = options;
868
+ const layout = texture.computeMemoryLayout(options);
869
+ const readBuffer = this.device.createBuffer({
870
+ byteLength: layout.byteLength,
871
+ usage: Buffer.COPY_DST | Buffer.MAP_READ
872
+ });
873
+
874
+ try {
875
+ texture.readBuffer(options, readBuffer);
876
+ const readData = await readBuffer.readAsync(0, layout.byteLength);
877
+ const bytesPerElement = ArrayType.BYTES_PER_ELEMENT;
878
+ if (layout.bytesPerRow % bytesPerElement !== 0) {
879
+ throw new Error(
880
+ `Texture readback row stride ${layout.bytesPerRow} is not aligned to ${bytesPerElement}-byte elements.`
881
+ );
882
+ }
883
+ const source = new ArrayType(
884
+ readData.buffer,
885
+ readData.byteOffset,
886
+ layout.byteLength / bytesPerElement
887
+ );
888
+ // Picking textures are RGBA. WebGPU rows may be padded to satisfy GPU alignment
889
+ // requirements, so repack each row into a tightly packed CPU array before decode.
890
+ const packedRowLength = width * 4;
891
+ const sourceRowLength = layout.bytesPerRow / bytesPerElement;
892
+ if (sourceRowLength < packedRowLength) {
893
+ throw new Error(
894
+ `Texture readback row stride ${sourceRowLength} is smaller than packed row length ${packedRowLength}.`
895
+ );
896
+ }
897
+ const packed = new ArrayType(width * height * 4);
898
+
899
+ for (let row = 0; row < height; row++) {
900
+ const sourceStart = row * sourceRowLength;
901
+ packed.set(
902
+ source.subarray(sourceStart, sourceStart + packedRowLength),
903
+ row * packedRowLength
904
+ );
905
+ }
906
+
907
+ return packed as T;
908
+ } finally {
909
+ readBuffer.destroy();
910
+ }
911
+ }
912
+
832
913
  /**
833
914
  * Renders layers into the picking buffer with picking colors and read the pixels.
834
915
  * @deprecated WebGL only, use _drawAndSampleAsync instead