@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.
- package/dist/controllers/terrain-controller.d.ts +7 -6
- package/dist/controllers/terrain-controller.d.ts.map +1 -1
- package/dist/controllers/terrain-controller.js +58 -39
- package/dist/controllers/terrain-controller.js.map +1 -1
- package/dist/dist.dev.js +3748 -1377
- package/dist/index.cjs +545 -219
- package/dist/index.cjs.map +4 -4
- package/dist/lib/attribute/gl-utils.d.ts +1 -2
- package/dist/lib/attribute/gl-utils.d.ts.map +1 -1
- package/dist/lib/attribute/gl-utils.js +2 -2
- package/dist/lib/attribute/gl-utils.js.map +1 -1
- package/dist/lib/deck-picker.d.ts +3 -2
- package/dist/lib/deck-picker.d.ts.map +1 -1
- package/dist/lib/deck-picker.js +74 -17
- package/dist/lib/deck-picker.js.map +1 -1
- package/dist/lib/deck.d.ts +62 -0
- package/dist/lib/deck.d.ts.map +1 -1
- package/dist/lib/deck.js +219 -77
- package/dist/lib/deck.js.map +1 -1
- package/dist/lib/init.js +2 -2
- package/dist/lib/layer.d.ts.map +1 -1
- package/dist/lib/layer.js +60 -9
- package/dist/lib/layer.js.map +1 -1
- package/dist/lib/view-manager.js +1 -1
- package/dist/lib/view-manager.js.map +1 -1
- package/dist/passes/layers-pass.d.ts.map +1 -1
- package/dist/passes/layers-pass.js +13 -0
- package/dist/passes/layers-pass.js.map +1 -1
- package/dist/passes/pick-layers-pass.d.ts.map +1 -1
- package/dist/passes/pick-layers-pass.js +7 -2
- package/dist/passes/pick-layers-pass.js.map +1 -1
- package/dist/passes/screen-pass-uniforms.d.ts +1 -1
- package/dist/passes/screen-pass-uniforms.js +1 -1
- package/dist/shaderlib/color/color.d.ts +1 -4
- package/dist/shaderlib/color/color.d.ts.map +1 -1
- package/dist/shaderlib/color/color.js +0 -12
- package/dist/shaderlib/color/color.js.map +1 -1
- package/dist/shaderlib/index.d.ts +1 -2
- package/dist/shaderlib/index.d.ts.map +1 -1
- package/dist/shaderlib/index.js +1 -2
- package/dist/shaderlib/index.js.map +1 -1
- package/dist/shaderlib/misc/layer-uniforms.d.ts +3 -2
- package/dist/shaderlib/misc/layer-uniforms.d.ts.map +1 -1
- package/dist/shaderlib/misc/layer-uniforms.js +10 -1
- package/dist/shaderlib/misc/layer-uniforms.js.map +1 -1
- package/dist/shaderlib/picking/picking.d.ts +5 -3
- package/dist/shaderlib/picking/picking.d.ts.map +1 -1
- package/dist/shaderlib/picking/picking.js +29 -0
- package/dist/shaderlib/picking/picking.js.map +1 -1
- package/dist/shaderlib/project/project.glsl.js +1 -1
- package/dist/shaderlib/project/project.wgsl.d.ts.map +1 -1
- package/dist/shaderlib/project/project.wgsl.js +4 -6
- package/dist/shaderlib/project/project.wgsl.js.map +1 -1
- package/dist/shaderlib/shadow/shadow.d.ts +2 -2
- package/dist/shaderlib/shadow/shadow.js +1 -1
- package/dist/transitions/gpu-interpolation-transition.js +2 -2
- package/dist/transitions/gpu-interpolation-transition.js.map +1 -1
- package/dist/transitions/gpu-spring-transition.js +1 -1
- package/dist/transitions/gpu-transition-utils.d.ts.map +1 -1
- package/dist/transitions/gpu-transition-utils.js +3 -4
- package/dist/transitions/gpu-transition-utils.js.map +1 -1
- package/dist/utils/texture.d.ts.map +1 -1
- package/dist/utils/texture.js +3 -0
- package/dist/utils/texture.js.map +1 -1
- package/dist/utils/typed-array-manager.js.map +1 -1
- package/dist.min.js +582 -247
- package/package.json +8 -9
- package/src/controllers/terrain-controller.ts +60 -51
- package/src/lib/attribute/gl-utils.ts +2 -2
- package/src/lib/deck-picker.ts +98 -17
- package/src/lib/deck.ts +334 -86
- package/src/lib/layer.ts +98 -8
- package/src/lib/view-manager.ts +1 -1
- package/src/passes/layers-pass.ts +21 -1
- package/src/passes/pick-layers-pass.ts +6 -2
- package/src/passes/screen-pass-uniforms.ts +1 -1
- package/src/shaderlib/color/color.ts +0 -12
- package/src/shaderlib/index.ts +1 -3
- package/src/shaderlib/misc/layer-uniforms.ts +11 -1
- package/src/shaderlib/picking/picking.ts +30 -0
- package/src/shaderlib/project/project.glsl.ts +1 -1
- package/src/shaderlib/project/project.wgsl.ts +4 -6
- package/src/shaderlib/shadow/shadow.ts +1 -1
- package/src/transitions/gpu-interpolation-transition.ts +2 -2
- package/src/transitions/gpu-spring-transition.ts +1 -1
- package/src/transitions/gpu-transition-utils.ts +4 -5
- package/src/utils/texture.ts +2 -0
- 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.
|
|
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.
|
|
44
|
-
"@loaders.gl/images": "^4.4.
|
|
45
|
-
"@luma.gl/
|
|
46
|
-
"@luma.gl/
|
|
47
|
-
"@luma.gl/
|
|
48
|
-
"@luma.gl/
|
|
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": "
|
|
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
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
*
|
|
107
|
-
*
|
|
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
|
|
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 {
|
|
151
|
+
return {position: [0, 0, altitude], zoom: newZoom, ...adjusted};
|
|
143
152
|
}
|
|
144
|
-
return
|
|
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 {
|
|
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,
|
package/src/lib/deck-picker.ts
CHANGED
|
@@ -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
|
-
* @
|
|
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
|
-
* @
|
|
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: [
|
|
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: [
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|