@pirireis/webglobeplugins 0.15.19-alpha → 0.15.21-alpha
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/package.json +1 -1
- package/point-tracks/plugin.js +32 -11
- package/range-tools-on-terrain/circle-line-chain/plugin.js +1 -1
- package/semiplugins/lightweight/line-plugin.js +35 -40
- package/util/account/single-attribute-buffer-management/buffer-manager.js +1 -1
- package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +121 -70
- package/util/account/single-attribute-buffer-management/buffer-orchestrator1.js +159 -0
- package/util/account/single-attribute-buffer-management/object-store.js +1 -1
- package/util/account/single-attribute-buffer-management/buffer-orchestrator copy.js +0 -161
- package/util/account/single-attribute-buffer-management/chunked-buffer-manager.js +0 -75
- package/util/account/single-attribute-buffer-management/chunked-buffer-orchestrator.js +0 -195
package/package.json
CHANGED
package/point-tracks/plugin.js
CHANGED
|
@@ -224,12 +224,19 @@ class PointTracksPlugin {
|
|
|
224
224
|
return;
|
|
225
225
|
this._isFreed = true;
|
|
226
226
|
this._pickerDisplayer.free();
|
|
227
|
+
this._tracksToPointMap.clear();
|
|
227
228
|
PointOnGlobeProgramCache.release(this.globe);
|
|
228
229
|
this.gl.deleteBuffer(this._focusParams.elementBuffer);
|
|
229
230
|
this._bufferManagersMap.forEach(({ bufferManager, adaptor }) => bufferManager.free());
|
|
230
231
|
}
|
|
231
232
|
draw3D() {
|
|
232
233
|
const { gl, _pointProgram, _pickerDisplayer, _bufferOrchestrator, _vao } = this;
|
|
234
|
+
if (!gl) {
|
|
235
|
+
throw new Error("GL is not loaded, PointTracks Plugin");
|
|
236
|
+
}
|
|
237
|
+
if (this._isFreed) {
|
|
238
|
+
throw new Error("Plugin is unregistered, PointTracks Plugin");
|
|
239
|
+
}
|
|
233
240
|
this.resize();
|
|
234
241
|
_pickerDisplayer.bindFBO();
|
|
235
242
|
_pickerDisplayer.clearTextures();
|
|
@@ -270,13 +277,17 @@ class PointTracksPlugin {
|
|
|
270
277
|
const trackID = track.trackID;
|
|
271
278
|
const points = track.points;
|
|
272
279
|
if (!this._tracksToPointsMap.has(trackID)) {
|
|
273
|
-
this._tracksToPointsMap.set(trackID,
|
|
274
|
-
// this._tracksToPointsMap.set(trackID, new Set()); // TODO: alternative to Set?
|
|
280
|
+
this._tracksToPointsMap.set(trackID, new Set());
|
|
275
281
|
}
|
|
276
|
-
const
|
|
282
|
+
const pointSet = this._tracksToPointsMap.get(trackID);
|
|
277
283
|
for (let p = 0; p < points.length; p++) {
|
|
278
284
|
const pointID = points[p].ID;
|
|
279
|
-
|
|
285
|
+
if (!pointSet.has(pointID)) {
|
|
286
|
+
pointSet.add(pointID);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
console.warn(`Point with ID ${pointID} already exists in track ${trackID}. Skipping duplicate.`);
|
|
290
|
+
}
|
|
280
291
|
}
|
|
281
292
|
}
|
|
282
293
|
}
|
|
@@ -286,15 +297,22 @@ class PointTracksPlugin {
|
|
|
286
297
|
}
|
|
287
298
|
}
|
|
288
299
|
_deletePointsFromTracksMap(trackID, pointIDs) {
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
300
|
+
const trackIDs = this._tracksToPointsMap.get(trackID);
|
|
301
|
+
if (trackIDs === undefined) {
|
|
302
|
+
console.warn(`Track with ID ${trackID} not found.`);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
for (const pointID of pointIDs) {
|
|
306
|
+
if (!trackIDs.has(pointID)) {
|
|
307
|
+
console.warn(`Point with ID ${pointID} not found in track ${trackID}.`);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
trackIDs.delete(pointID);
|
|
295
311
|
}
|
|
296
312
|
}
|
|
297
|
-
|
|
313
|
+
if (trackIDs.size === 0) {
|
|
314
|
+
this._tracksToPointsMap.delete(trackID);
|
|
315
|
+
}
|
|
298
316
|
}
|
|
299
317
|
_selfSelect() {
|
|
300
318
|
const { globe } = this;
|
|
@@ -324,6 +342,9 @@ class PointTracksPlugin {
|
|
|
324
342
|
for (const pointID of pointList) {
|
|
325
343
|
const key = keyMethod(trackID, pointID);
|
|
326
344
|
const index = this._bufferOrchestrator.offsetMap.get(key);
|
|
345
|
+
if (index === undefined) {
|
|
346
|
+
throw new Error;
|
|
347
|
+
}
|
|
327
348
|
indexes.push(index);
|
|
328
349
|
length++;
|
|
329
350
|
}
|
|
@@ -47,7 +47,7 @@ export class CircleLineChainPlugin {
|
|
|
47
47
|
dashedLineOpacityVariativeOn: false,
|
|
48
48
|
dashedLineRatioVariativeOn: false,
|
|
49
49
|
bufferType: "DYNAMIC_DRAW",
|
|
50
|
-
opacity: 1.0
|
|
50
|
+
opacity: 1.0,
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
constructor(id, { drawCircleOn, textWritersMap, textDataPreAdaptor, opacities, lineAdaptor, arcAdaptor, circleAdaptor, arcOnTerrainOptions, circleOnTerrainOptions, lineOptions } = {}) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LineOnGlobeCache } from "../../programs/line-on-globe/naive-accurate-flexible";
|
|
2
|
-
import {
|
|
2
|
+
import { BufferOrchestrator } from "../../util/account/single-attribute-buffer-management/buffer-orchestrator";
|
|
3
|
+
import { BufferManager } from "../../util/account/single-attribute-buffer-management/buffer-manager";
|
|
3
4
|
import { sphereCoord } from "../../util/geometry/index";
|
|
4
5
|
import { constraintFloat } from "../../util/check/typecheck";
|
|
5
6
|
export class LinePlugin {
|
|
@@ -10,20 +11,23 @@ export class LinePlugin {
|
|
|
10
11
|
vao = null;
|
|
11
12
|
_options;
|
|
12
13
|
_uboHandler = null;
|
|
13
|
-
_bufferOrchestrator
|
|
14
|
+
_bufferOrchestrator;
|
|
15
|
+
_bufferManagersMap = new Map();
|
|
14
16
|
_freed = false;
|
|
15
|
-
constructor(id, { flatViewOn = true, globeViewOn = true, variativeColorsOn = false, defaultColor = [1, 1, 1, 1], dashedLineOpacityVariativeOn = false, dashedLineRatioVariativeOn = false, bufferType = "DYNAMIC_DRAW", opacity = 1.0 } = {}) {
|
|
17
|
+
constructor(id, { flatViewOn = true, globeViewOn = true, variativeColorsOn = false, defaultColor = [1, 1, 1, 1], dashedLineOpacityVariativeOn = false, dashedLineRatioVariativeOn = false, bufferType = "DYNAMIC_DRAW", opacity = 1.0, initialCapacity = 10 } = {}) {
|
|
16
18
|
this.id = id;
|
|
17
19
|
this._options = {
|
|
18
|
-
flatViewOn,
|
|
19
|
-
globeViewOn,
|
|
20
|
-
variativeColorsOn,
|
|
21
|
-
defaultColor,
|
|
22
|
-
dashedLineOpacityVariativeOn,
|
|
23
|
-
dashedLineRatioVariativeOn,
|
|
24
|
-
bufferType,
|
|
25
|
-
opacity
|
|
20
|
+
flatViewOn: flatViewOn ?? true,
|
|
21
|
+
globeViewOn: globeViewOn ?? true,
|
|
22
|
+
variativeColorsOn: variativeColorsOn ?? false,
|
|
23
|
+
defaultColor: defaultColor ?? [1, 1, 1, 1],
|
|
24
|
+
dashedLineOpacityVariativeOn: dashedLineOpacityVariativeOn ?? false,
|
|
25
|
+
dashedLineRatioVariativeOn: dashedLineRatioVariativeOn ?? false,
|
|
26
|
+
bufferType: bufferType ?? "DYNAMIC_DRAW",
|
|
27
|
+
opacity: opacity ?? 1.0,
|
|
28
|
+
initialCapacity: initialCapacity ?? 10
|
|
26
29
|
};
|
|
30
|
+
this._bufferOrchestrator = new BufferOrchestrator({ capacity: initialCapacity ?? 10 });
|
|
27
31
|
}
|
|
28
32
|
init(globe, gl) {
|
|
29
33
|
this.globe = globe;
|
|
@@ -31,13 +35,12 @@ export class LinePlugin {
|
|
|
31
35
|
this.program = LineOnGlobeCache.get(globe);
|
|
32
36
|
this._uboHandler = this.program.createUBO();
|
|
33
37
|
this.setDefaultColor(this._options.defaultColor);
|
|
34
|
-
this.
|
|
35
|
-
);
|
|
38
|
+
this._fillManagerMap();
|
|
36
39
|
this.vao = this.program.createVAO(...this._createSelectBufferNames().map((key) => {
|
|
37
40
|
if (key === null) {
|
|
38
41
|
return null;
|
|
39
42
|
}
|
|
40
|
-
const bufferManagerComp = this.
|
|
43
|
+
const bufferManagerComp = this._bufferManagersMap.get(key);
|
|
41
44
|
if (bufferManagerComp === undefined) {
|
|
42
45
|
throw new Error(`Buffer key ${key} does not exist in bufferManagersMap`);
|
|
43
46
|
}
|
|
@@ -51,19 +54,19 @@ export class LinePlugin {
|
|
|
51
54
|
}
|
|
52
55
|
// PLUGIN INTERFACE METHODS
|
|
53
56
|
insertBulk(items) {
|
|
54
|
-
const { _bufferOrchestrator, globe } = this;
|
|
57
|
+
const { _bufferOrchestrator, _bufferManagersMap, globe } = this;
|
|
55
58
|
if (!_bufferOrchestrator || !globe) {
|
|
56
59
|
throw new Error("Buffer orchestrator or globe is not initialized.");
|
|
57
60
|
}
|
|
58
|
-
_bufferOrchestrator.insertBulk(items);
|
|
61
|
+
_bufferOrchestrator.insertBulk(items, _bufferManagersMap);
|
|
59
62
|
this.globe?.DrawRender();
|
|
60
63
|
}
|
|
61
64
|
deleteBulk(keys) {
|
|
62
|
-
const { _bufferOrchestrator, globe } = this;
|
|
65
|
+
const { _bufferOrchestrator, _bufferManagersMap, globe } = this;
|
|
63
66
|
if (!_bufferOrchestrator || !globe) {
|
|
64
67
|
throw new Error("Buffer orchestrator or globe is not initialized.");
|
|
65
68
|
}
|
|
66
|
-
_bufferOrchestrator.deleteBulk(keys);
|
|
69
|
+
_bufferOrchestrator.deleteBulk(keys, _bufferManagersMap);
|
|
67
70
|
this.globe?.DrawRender();
|
|
68
71
|
}
|
|
69
72
|
setPluginOpacity(opacity, drawRender = false) {
|
|
@@ -108,12 +111,11 @@ export class LinePlugin {
|
|
|
108
111
|
if (!_bufferOrchestrator || !_bufferOrchestrator || !_uboHandler) {
|
|
109
112
|
throw new Error("Buffer orchestrator is not initialized.");
|
|
110
113
|
}
|
|
111
|
-
const { bufferOrchestrator } = _bufferOrchestrator;
|
|
112
114
|
const { flatViewOn, globeViewOn, opacity } = _options;
|
|
113
115
|
const drawOptions = {
|
|
114
116
|
drawRange: {
|
|
115
117
|
first: 0,
|
|
116
|
-
count:
|
|
118
|
+
count: _bufferOrchestrator.length
|
|
117
119
|
}
|
|
118
120
|
};
|
|
119
121
|
this.gl.disable(this.gl.DEPTH_TEST); // Disable depth test for lines
|
|
@@ -132,67 +134,60 @@ export class LinePlugin {
|
|
|
132
134
|
if (this._freed)
|
|
133
135
|
return;
|
|
134
136
|
// TODO: FILL
|
|
135
|
-
this.
|
|
137
|
+
this._bufferManagersMap.forEach((manager) => {
|
|
138
|
+
manager.bufferManager.free();
|
|
139
|
+
});
|
|
136
140
|
this._uboHandler?.free();
|
|
137
141
|
LineOnGlobeCache.release(this.globe);
|
|
138
142
|
this._freed = true;
|
|
139
143
|
}
|
|
140
144
|
// IMPLICIT INTERFACE METHODS
|
|
141
|
-
|
|
145
|
+
_fillManagerMap() {
|
|
142
146
|
const globe = this.globe;
|
|
143
147
|
const { flatViewOn, globeViewOn, variativeColorsOn, dashedLineOpacityVariativeOn, dashedLineRatioVariativeOn, bufferType = "DYNAMIC_DRAW" } = this._options;
|
|
144
|
-
const m =
|
|
148
|
+
const m = this._bufferManagersMap;
|
|
149
|
+
const initialCapacity = this._options.initialCapacity;
|
|
145
150
|
if (flatViewOn) {
|
|
146
151
|
m.set("start_position", {
|
|
147
|
-
|
|
148
|
-
bufferType: bufferType, // TODO: Ask User?
|
|
149
|
-
buffer: null,
|
|
152
|
+
bufferManager: new BufferManager(globe.gl, 2, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
150
153
|
adaptor: (item) => {
|
|
151
154
|
const result = new Float32Array(globe.api_GetMercator2DPoint(item.start[0], item.start[1]));
|
|
152
155
|
return result;
|
|
153
156
|
}
|
|
154
157
|
});
|
|
155
158
|
m.set("end_position", {
|
|
156
|
-
|
|
157
|
-
bufferType: bufferType,
|
|
158
|
-
buffer: null,
|
|
159
|
+
bufferManager: new BufferManager(globe.gl, 2, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
159
160
|
adaptor: (item) => new Float32Array(globe.api_GetMercator2DPoint(item.end[0], item.end[1]))
|
|
160
161
|
});
|
|
161
162
|
}
|
|
162
163
|
if (globeViewOn) {
|
|
163
164
|
m.set("start_position_3d", {
|
|
164
|
-
|
|
165
|
-
bufferType: bufferType,
|
|
165
|
+
bufferManager: new BufferManager(globe.gl, 3, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
166
166
|
adaptor: (item) => sphereCoord(item.start[0], item.start[1], globe, item.start_altitude || 0)
|
|
167
167
|
});
|
|
168
168
|
m.set("end_position_3d", {
|
|
169
|
-
|
|
170
|
-
bufferType: bufferType,
|
|
169
|
+
bufferManager: new BufferManager(globe.gl, 3, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
171
170
|
adaptor: (item) => sphereCoord(item.end[0], item.end[1], globe, item.end_altitude || 0)
|
|
172
171
|
});
|
|
173
172
|
}
|
|
174
173
|
if (variativeColorsOn) {
|
|
175
174
|
m.set("color", {
|
|
176
|
-
|
|
177
|
-
bufferType: bufferType,
|
|
175
|
+
bufferManager: new BufferManager(globe.gl, 4, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
178
176
|
adaptor: (item) => new Float32Array(item.color !== undefined ? item.color : [-1, -1, -1, -1])
|
|
179
177
|
});
|
|
180
178
|
}
|
|
181
179
|
if (dashedLineOpacityVariativeOn) {
|
|
182
180
|
m.set("dashed_line_opacity", {
|
|
183
|
-
|
|
184
|
-
bufferType: bufferType,
|
|
181
|
+
bufferManager: new BufferManager(globe.gl, 1, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
185
182
|
adaptor: (item) => new Float32Array(item.dashed_line_opacity !== undefined ? [item.dashed_line_opacity] : [-1])
|
|
186
183
|
});
|
|
187
184
|
}
|
|
188
185
|
if (dashedLineRatioVariativeOn) {
|
|
189
186
|
m.set("dashed_line_ratio", {
|
|
190
|
-
|
|
191
|
-
bufferType: bufferType,
|
|
187
|
+
bufferManager: new BufferManager(globe.gl, 1, { bufferType, initialCapacity: this._options.initialCapacity }),
|
|
192
188
|
adaptor: (item) => new Float32Array(item.dashed_line_ratio !== undefined ? [item.dashed_line_ratio] : [-1])
|
|
193
189
|
});
|
|
194
190
|
}
|
|
195
|
-
return m;
|
|
196
191
|
}
|
|
197
192
|
_createSelectBufferNames() {
|
|
198
193
|
const { flatViewOn, globeViewOn, variativeColorsOn, dashedLineOpacityVariativeOn, dashedLineRatioVariativeOn } = this._options;
|
|
@@ -60,7 +60,7 @@ export class BufferManager {
|
|
|
60
60
|
}
|
|
61
61
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
insertBlock(items, offset, adapter, Constructor = Float32Array) {
|
|
64
64
|
const { gl, buffer, itemSize } = this;
|
|
65
65
|
const cpuBuffer = new Constructor(itemSize * items.length);
|
|
66
66
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -19,6 +19,92 @@ export class BufferOrchestrator {
|
|
|
19
19
|
this.tombstoneOffsets = [];
|
|
20
20
|
this._length = 0;
|
|
21
21
|
}
|
|
22
|
+
// ...existing code...
|
|
23
|
+
// TODO: CLEAN THIS
|
|
24
|
+
// insertBulk2(items: any[], bufferManagersMap: BufferManagersMap, bufferKeys: string[] | null = null) {
|
|
25
|
+
// this.ensureSpace(items.length, bufferManagersMap);
|
|
26
|
+
// const { offsetMap } = this;
|
|
27
|
+
// // For block insert (truly new, consecutive offsets)
|
|
28
|
+
// const blockItems: any[] = [];
|
|
29
|
+
// let blockStart: number | null = null;
|
|
30
|
+
// let lastBlockOffset: number | null = null;
|
|
31
|
+
// // For single insert (existing or tombstone-assigned)
|
|
32
|
+
// const singleItems: any[] = [];
|
|
33
|
+
// const singleOffsets: number[] = [];
|
|
34
|
+
// for (const item of items) {
|
|
35
|
+
// let offset = offsetMap.get(item.key);
|
|
36
|
+
// if (offset !== undefined) {
|
|
37
|
+
// // Already present, update in place
|
|
38
|
+
// singleItems.push(item);
|
|
39
|
+
// singleOffsets.push(offset);
|
|
40
|
+
// } else {
|
|
41
|
+
// // Assign offset (tombstone or new)
|
|
42
|
+
// if (this.tombstoneOffsets.length > 0) {
|
|
43
|
+
// offset = this.tombstoneOffsets.pop() as number;
|
|
44
|
+
// offsetMap.set(item.key, offset);
|
|
45
|
+
// singleItems.push(item);
|
|
46
|
+
// singleOffsets.push(offset);
|
|
47
|
+
// } else {
|
|
48
|
+
// offset = this._length++;
|
|
49
|
+
// if (offset >= this._capacity) throw new Error("The Size Should Be Increased!!");
|
|
50
|
+
// offsetMap.set(item.key, offset);
|
|
51
|
+
// // Check for consecutive block
|
|
52
|
+
// if (blockStart === null) {
|
|
53
|
+
// blockStart = offset;
|
|
54
|
+
// lastBlockOffset = offset;
|
|
55
|
+
// blockItems.push(item);
|
|
56
|
+
// } else if (lastBlockOffset !== null && offset === lastBlockOffset + 1) {
|
|
57
|
+
// lastBlockOffset = offset;
|
|
58
|
+
// blockItems.push(item);
|
|
59
|
+
// } else {
|
|
60
|
+
// // Not consecutive, flush current block
|
|
61
|
+
// if (blockItems.length > 0) {
|
|
62
|
+
// this._insertBlock(blockItems, blockStart, bufferManagersMap, bufferKeys);
|
|
63
|
+
// blockItems.length = 0;
|
|
64
|
+
// }
|
|
65
|
+
// blockStart = offset;
|
|
66
|
+
// lastBlockOffset = offset;
|
|
67
|
+
// blockItems.push(item);
|
|
68
|
+
// }
|
|
69
|
+
// }
|
|
70
|
+
// }
|
|
71
|
+
// }
|
|
72
|
+
// // Flush any remaining block
|
|
73
|
+
// if (blockItems.length > 0 && blockStart !== null) {
|
|
74
|
+
// this._insertBlock(blockItems, blockStart, bufferManagersMap, bufferKeys);
|
|
75
|
+
// }
|
|
76
|
+
// // Insert singles
|
|
77
|
+
// if (singleItems.length > 0) {
|
|
78
|
+
// if (bufferKeys) {
|
|
79
|
+
// for (const key of bufferKeys) {
|
|
80
|
+
// const bufferManagerComp = bufferManagersMap.get(key);
|
|
81
|
+
// if (!bufferManagerComp) throw new Error("insertBulk bufferKey does not exist");
|
|
82
|
+
// const { bufferManager, adaptor } = bufferManagerComp;
|
|
83
|
+
// bufferManager.insertBulk(singleItems.map(adaptor), singleOffsets);
|
|
84
|
+
// }
|
|
85
|
+
// } else {
|
|
86
|
+
// for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
87
|
+
// bufferManager.insertBulk(singleItems.map(adaptor), singleOffsets);
|
|
88
|
+
// }
|
|
89
|
+
// }
|
|
90
|
+
// }
|
|
91
|
+
// }
|
|
92
|
+
// // Helper for block insert
|
|
93
|
+
// private _insertBlock(blockItems: any[], blockStart: number, bufferManagersMap: BufferManagersMap, bufferKeys: string[] | null) {
|
|
94
|
+
// if (bufferKeys) {
|
|
95
|
+
// for (const key of bufferKeys) {
|
|
96
|
+
// const bufferManagerComp = bufferManagersMap.get(key);
|
|
97
|
+
// if (!bufferManagerComp) throw new Error("insertBulk bufferKey does not exist");
|
|
98
|
+
// const { bufferManager, adaptor } = bufferManagerComp;
|
|
99
|
+
// bufferManager.insertBlock(blockItems, blockStart, adaptor, Float32Array);
|
|
100
|
+
// }
|
|
101
|
+
// } else {
|
|
102
|
+
// for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
103
|
+
// bufferManager.insertBlock(blockItems, blockStart, adaptor, Float32Array);
|
|
104
|
+
// }
|
|
105
|
+
// }
|
|
106
|
+
// }
|
|
107
|
+
// ...existing code...
|
|
22
108
|
// want to add stack load to this method
|
|
23
109
|
// if offsetMap.has(item.key) === false get next offset, add the item to stack
|
|
24
110
|
// create a single big float32array and fill it with the items
|
|
@@ -27,10 +113,18 @@ export class BufferOrchestrator {
|
|
|
27
113
|
this.ensureSpace(items.length, bufferManagersMap);
|
|
28
114
|
const { offsetMap } = this;
|
|
29
115
|
let offsetStart = null;
|
|
30
|
-
const
|
|
116
|
+
const offsets = [];
|
|
117
|
+
const blockLoad = [];
|
|
31
118
|
for (const item of items) {
|
|
32
119
|
let o = offsetMap.get(item.key);
|
|
120
|
+
if (o === undefined && this.tombstoneOffsets.length > 0) {
|
|
121
|
+
// If there is a tombstone, use it
|
|
122
|
+
o = this.tombstoneOffsets.pop();
|
|
123
|
+
offsetMap.set(item.key, o);
|
|
124
|
+
}
|
|
33
125
|
if (o !== undefined) {
|
|
126
|
+
// Already exist or there is a random empty space in earlear part of memory.
|
|
127
|
+
// insert for single slots
|
|
34
128
|
if (bufferKeys) {
|
|
35
129
|
for (const key of bufferKeys) {
|
|
36
130
|
const bufferManagerComp = bufferManagersMap.get(key);
|
|
@@ -45,88 +139,42 @@ export class BufferOrchestrator {
|
|
|
45
139
|
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
46
140
|
}
|
|
47
141
|
}
|
|
48
|
-
continue;
|
|
49
142
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
143
|
+
else {
|
|
144
|
+
// offset comes from increment function
|
|
145
|
+
// this part stacks data to be load a a single big block.
|
|
146
|
+
const offset = this.nextOffset();
|
|
147
|
+
if (offset === undefined) {
|
|
148
|
+
throw new Error("The Size Should Be Increased!!");
|
|
149
|
+
}
|
|
150
|
+
offsets.push(offset);
|
|
151
|
+
offsetMap.set(item.key, offset);
|
|
152
|
+
blockLoad.push(item);
|
|
153
|
+
if (offsetStart === null) {
|
|
154
|
+
offsetStart = offset;
|
|
155
|
+
}
|
|
55
156
|
}
|
|
56
157
|
}
|
|
57
158
|
if (offsetStart === null)
|
|
58
159
|
return;
|
|
160
|
+
for (let i = 0; i < offsets.length - 1; i++) {
|
|
161
|
+
if (offsets[i] - offsets[i + 1] !== -1) {
|
|
162
|
+
// TODO: DELETE THIS
|
|
163
|
+
throw new Error("consecutive is not consecutive");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
59
166
|
if (bufferKeys) {
|
|
60
167
|
for (const key of bufferKeys) {
|
|
61
168
|
const bufferManagerComp = bufferManagersMap.get(key);
|
|
62
169
|
if (bufferManagerComp === undefined)
|
|
63
170
|
throw new Error("insertBulk bufferKey does not exist");
|
|
64
171
|
const { bufferManager, adaptor } = bufferManagerComp;
|
|
65
|
-
bufferManager.
|
|
172
|
+
bufferManager.insertBlock(blockLoad, offsetStart, adaptor, Float32Array);
|
|
66
173
|
}
|
|
67
174
|
}
|
|
68
175
|
else {
|
|
69
176
|
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
70
|
-
bufferManager.
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
insertBulk2(items, bufferManagersMap, bufferKeys = null) {
|
|
75
|
-
this.ensureSpace(items.length, bufferManagersMap);
|
|
76
|
-
const { offsetMap } = this;
|
|
77
|
-
// Pre-allocate arrays only if needed
|
|
78
|
-
const newItemsCount = items.reduce((acc, item) => acc + (offsetMap.has(item.key) ? 0 : 1), 0);
|
|
79
|
-
if (newItemsCount === 0)
|
|
80
|
-
return;
|
|
81
|
-
// Only allocate if there are new items
|
|
82
|
-
const newItems = new Array(newItemsCount);
|
|
83
|
-
const newOffsets = new Array(newItemsCount);
|
|
84
|
-
let idx = 0;
|
|
85
|
-
for (const item of items) {
|
|
86
|
-
let o = offsetMap.get(item.key);
|
|
87
|
-
if (o !== undefined) {
|
|
88
|
-
// Update existing
|
|
89
|
-
if (bufferKeys) {
|
|
90
|
-
for (const key of bufferKeys) {
|
|
91
|
-
const bufferManagerComp = bufferManagersMap.get(key);
|
|
92
|
-
if (!bufferManagerComp)
|
|
93
|
-
throw new Error("insertBulk bufferKey does not exist");
|
|
94
|
-
const { bufferManager, adaptor } = bufferManagerComp;
|
|
95
|
-
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
100
|
-
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
// New item
|
|
106
|
-
const offset = this.nextOffset();
|
|
107
|
-
offsetMap.set(item.key, offset);
|
|
108
|
-
newItems[idx] = item;
|
|
109
|
-
newOffsets[idx] = offset;
|
|
110
|
-
idx++;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (idx > 0) {
|
|
114
|
-
// Only slice the arrays to the actual number of new items
|
|
115
|
-
const itemsToInsert = newItems.slice(0, idx);
|
|
116
|
-
const offsetsToInsert = newOffsets.slice(0, idx);
|
|
117
|
-
if (bufferKeys) {
|
|
118
|
-
for (const key of bufferKeys) {
|
|
119
|
-
const bufferManagerComp = bufferManagersMap.get(key);
|
|
120
|
-
if (!bufferManagerComp)
|
|
121
|
-
throw new Error("insertBulk bufferKey does not exist");
|
|
122
|
-
const { bufferManager, adaptor } = bufferManagerComp;
|
|
123
|
-
bufferManager.insertConsecutiveBulk(itemsToInsert, offsetsToInsert[0], adaptor, Float32Array);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
128
|
-
bufferManager.insertConsecutiveBulk(itemsToInsert, offsetsToInsert[0], adaptor, Float32Array);
|
|
129
|
-
}
|
|
177
|
+
bufferManager.insertBlock(blockLoad, offsetStart, adaptor, Float32Array);
|
|
130
178
|
}
|
|
131
179
|
}
|
|
132
180
|
}
|
|
@@ -175,15 +223,18 @@ export class BufferOrchestrator {
|
|
|
175
223
|
getOffset(key) {
|
|
176
224
|
return this.offsetMap.get(key);
|
|
177
225
|
}
|
|
178
|
-
|
|
226
|
+
nextTombstone() {
|
|
179
227
|
if (this.tombstoneOffsets.length > 0) {
|
|
180
228
|
const offset = this.tombstoneOffsets.pop();
|
|
181
229
|
return offset;
|
|
182
230
|
}
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
nextOffset() {
|
|
183
234
|
if (this._length < this._capacity) {
|
|
184
235
|
return this._length++;
|
|
185
236
|
}
|
|
186
|
-
return
|
|
237
|
+
return undefined;
|
|
187
238
|
}
|
|
188
239
|
ensureSpace(itemsLength, bufferManagersMap) {
|
|
189
240
|
if (itemsLength <= this.emptySpace)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// import { BufferOrchestrator as BufferOrchestratorI, BufferManagersMap, BufferManager } from "./types";
|
|
3
|
+
// const EXTRA_SIZE = 10;
|
|
4
|
+
// export class BufferOrchestrator implements BufferOrchestratorI {
|
|
5
|
+
// _capacity: number;
|
|
6
|
+
// offsetMap: Map<string, number>;
|
|
7
|
+
// tombstoneOffsets: number[];
|
|
8
|
+
// _length: number;
|
|
9
|
+
// constructor({ capacity = 10 } = {}) {
|
|
10
|
+
// this._capacity = capacity;
|
|
11
|
+
// this.offsetMap = new Map();
|
|
12
|
+
// this.tombstoneOffsets = [];
|
|
13
|
+
// this._length = 0;
|
|
14
|
+
// }
|
|
15
|
+
// resetWithCapacity(bufferManagersMap: BufferManagersMap, capacity: number | null = null) {
|
|
16
|
+
// this._capacity = capacity !== null ? capacity : this._capacity;
|
|
17
|
+
// for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
18
|
+
// bufferManager.resetWithCapacity(this._capacity);
|
|
19
|
+
// }
|
|
20
|
+
// this.offsetMap.clear();
|
|
21
|
+
// this.tombstoneOffsets = [];
|
|
22
|
+
// this._length = 0;
|
|
23
|
+
// }
|
|
24
|
+
// insertBulk(items: any[], bufferManagersMap: BufferManagersMap, bufferKeys: string[] | null = null) {
|
|
25
|
+
// this.ensureSpace(items.length, bufferManagersMap);
|
|
26
|
+
// const { offsetMap } = this;
|
|
27
|
+
// const offsets = [];
|
|
28
|
+
// for (const item of items) {
|
|
29
|
+
// let o = offsetMap.get(item.key);
|
|
30
|
+
// const offset = o !== undefined ? o : this.nextOffset() as number;
|
|
31
|
+
// if (offset === undefined) {
|
|
32
|
+
// throw new Error("The Size Should Be Increased!!")
|
|
33
|
+
// }
|
|
34
|
+
// offsetMap.set(item.key, offset);
|
|
35
|
+
// offsets.push(offset);
|
|
36
|
+
// }
|
|
37
|
+
// if (bufferKeys) {
|
|
38
|
+
// for (const key of bufferKeys) {
|
|
39
|
+
// const bufferManagerComp = bufferManagersMap.get(key);
|
|
40
|
+
// if (bufferManagerComp === undefined) throw new Error("insertBulk bufferKey does not exist");
|
|
41
|
+
// const { bufferManager, adaptor } = bufferManagerComp;
|
|
42
|
+
// bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
43
|
+
// }
|
|
44
|
+
// } else {
|
|
45
|
+
// for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
46
|
+
// bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
47
|
+
// }
|
|
48
|
+
// }
|
|
49
|
+
// }
|
|
50
|
+
// // does not assign offset to the new items.
|
|
51
|
+
// updateBulk(items: any[], bufferManagersMap: BufferManagersMap, bufferKeys: string[] | null = null) {
|
|
52
|
+
// const { offsetMap } = this;
|
|
53
|
+
// const offsets = [];
|
|
54
|
+
// for (const item of items) {
|
|
55
|
+
// const offset = offsetMap.get(item.key);
|
|
56
|
+
// if (offset !== undefined) {
|
|
57
|
+
// offsets.push(offset);
|
|
58
|
+
// } else {
|
|
59
|
+
// throw new Error("updateBulk item Key does not exist");
|
|
60
|
+
// }
|
|
61
|
+
// }
|
|
62
|
+
// if (bufferKeys) {
|
|
63
|
+
// for (const key of bufferKeys) {
|
|
64
|
+
// const bufferManagerComp = bufferManagersMap.get(key);
|
|
65
|
+
// if (bufferManagerComp === undefined) throw new Error("updateBulk bufferKey does not exist");
|
|
66
|
+
// const { bufferManager, adaptor } = bufferManagerComp;
|
|
67
|
+
// bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
68
|
+
// }
|
|
69
|
+
// } else {
|
|
70
|
+
// for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
71
|
+
// bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
72
|
+
// }
|
|
73
|
+
// }
|
|
74
|
+
// }
|
|
75
|
+
// deleteBulk(keys: string[], bufferManagersMap: BufferManagersMap) {
|
|
76
|
+
// const offsets = [];
|
|
77
|
+
// for (const key of keys) {
|
|
78
|
+
// const offset = this.getOffset(key);
|
|
79
|
+
// if (offset !== undefined) {
|
|
80
|
+
// offsets.push(offset);
|
|
81
|
+
// this.offsetMap.delete(key);
|
|
82
|
+
// this.tombstoneOffsets.push(offset);
|
|
83
|
+
// }
|
|
84
|
+
// }
|
|
85
|
+
// for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
86
|
+
// bufferManager.deleteBulk(offsets);
|
|
87
|
+
// }
|
|
88
|
+
// }
|
|
89
|
+
// getOffset(key: string) {
|
|
90
|
+
// return this.offsetMap.get(key);
|
|
91
|
+
// }
|
|
92
|
+
// nextOffset(): number | undefined {
|
|
93
|
+
// if (this.tombstoneOffsets.length > 0) {
|
|
94
|
+
// const offset = this.tombstoneOffsets.pop() as number;
|
|
95
|
+
// return offset;
|
|
96
|
+
// }
|
|
97
|
+
// if (this._length < this._capacity) {
|
|
98
|
+
// return this._length++;
|
|
99
|
+
// }
|
|
100
|
+
// return undefined;
|
|
101
|
+
// }
|
|
102
|
+
// ensureSpace(itemsLength: number, bufferManagersMap: BufferManagersMap) {
|
|
103
|
+
// if (itemsLength <= this.emptySpace) return;
|
|
104
|
+
// const newCapacity = this.length + itemsLength;
|
|
105
|
+
// for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
106
|
+
// bufferManager.extendBuffer(this.length, newCapacity);
|
|
107
|
+
// }
|
|
108
|
+
// this._capacity = newCapacity;
|
|
109
|
+
// }
|
|
110
|
+
// defrag(bufferManagers: BufferManagersMap, bufferKeys: string[]) { // TODO defrag and leave some empty space
|
|
111
|
+
// const offsetMap = this.offsetMap;
|
|
112
|
+
// const newCapacity = offsetMap.size + EXTRA_SIZE;
|
|
113
|
+
// if (bufferKeys) {
|
|
114
|
+
// for (const key of bufferKeys) {
|
|
115
|
+
// const offset = offsetMap.get(key);
|
|
116
|
+
// if (offset !== undefined) {
|
|
117
|
+
// for (const [key, { bufferManager }] of bufferManagers) {
|
|
118
|
+
// bufferManager.defrag([offset], this.length, newCapacity);
|
|
119
|
+
// }
|
|
120
|
+
// }
|
|
121
|
+
// }
|
|
122
|
+
// } else {
|
|
123
|
+
// for (const [key, { bufferManager }] of bufferManagers) {
|
|
124
|
+
// bufferManager.defrag(offsetMap.values(), this.length, newCapacity);
|
|
125
|
+
// }
|
|
126
|
+
// }
|
|
127
|
+
// this._defrag();
|
|
128
|
+
// this._length = offsetMap.size;
|
|
129
|
+
// this._capacity = newCapacity;
|
|
130
|
+
// this.tombstoneOffsets = [];
|
|
131
|
+
// }
|
|
132
|
+
// /**
|
|
133
|
+
// * Flushes metadata and sets length to 0 without actualize change on buffers
|
|
134
|
+
// * This method created for cases in which data is loaded on each frame
|
|
135
|
+
// */
|
|
136
|
+
// flush({ capacity = 10 } = {}) {
|
|
137
|
+
// this._length = 0;
|
|
138
|
+
// this._capacity = capacity;
|
|
139
|
+
// this.tombstoneOffsets = []
|
|
140
|
+
// this.offsetMap.clear();
|
|
141
|
+
// }
|
|
142
|
+
// _defrag() {
|
|
143
|
+
// const newOffsetMap = new Map();
|
|
144
|
+
// let newOffset = 0;
|
|
145
|
+
// for (const [key, offset] of this.offsetMap) {
|
|
146
|
+
// newOffsetMap.set(key, newOffset++);
|
|
147
|
+
// }
|
|
148
|
+
// this.offsetMap = newOffsetMap
|
|
149
|
+
// }
|
|
150
|
+
// get length() {
|
|
151
|
+
// return this._length;
|
|
152
|
+
// }
|
|
153
|
+
// get emptySpace() {
|
|
154
|
+
// return this._capacity - this.offsetMap.size;
|
|
155
|
+
// }
|
|
156
|
+
// get capacity() {
|
|
157
|
+
// return this._capacity;
|
|
158
|
+
// }
|
|
159
|
+
// }
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
const EXTRA_SIZE = 10;
|
|
2
|
-
export class BufferOrchestrator {
|
|
3
|
-
_capacity;
|
|
4
|
-
offsetMap;
|
|
5
|
-
tombstoneOffsets;
|
|
6
|
-
_length;
|
|
7
|
-
constructor({ capacity = 10 } = {}) {
|
|
8
|
-
this._capacity = capacity;
|
|
9
|
-
this.offsetMap = new Map();
|
|
10
|
-
this.tombstoneOffsets = [];
|
|
11
|
-
this._length = 0;
|
|
12
|
-
}
|
|
13
|
-
resetWithCapacity(bufferManagersMap, capacity = null) {
|
|
14
|
-
this._capacity = capacity !== null ? capacity : this._capacity;
|
|
15
|
-
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
16
|
-
bufferManager.resetWithCapacity(this._capacity);
|
|
17
|
-
}
|
|
18
|
-
this.offsetMap.clear();
|
|
19
|
-
this.tombstoneOffsets = [];
|
|
20
|
-
this._length = 0;
|
|
21
|
-
}
|
|
22
|
-
insertBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
23
|
-
this.ensureSpace(items.length, bufferManagersMap);
|
|
24
|
-
const { offsetMap } = this;
|
|
25
|
-
const offsets = [];
|
|
26
|
-
for (const item of items) {
|
|
27
|
-
let o = offsetMap.get(item.key);
|
|
28
|
-
const offset = o !== undefined ? o : this.nextOffset();
|
|
29
|
-
offsetMap.set(item.key, offset);
|
|
30
|
-
offsets.push(offset);
|
|
31
|
-
}
|
|
32
|
-
if (bufferKeys) {
|
|
33
|
-
for (const key of bufferKeys) {
|
|
34
|
-
const bufferManagerComp = bufferManagersMap.get(key);
|
|
35
|
-
if (bufferManagerComp === undefined)
|
|
36
|
-
throw new Error("insertBulk bufferKey does not exist");
|
|
37
|
-
const { bufferManager, adaptor } = bufferManagerComp;
|
|
38
|
-
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
43
|
-
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// does not assign offset to the new items.
|
|
48
|
-
updateBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
49
|
-
const { offsetMap } = this;
|
|
50
|
-
const offsets = [];
|
|
51
|
-
for (const item of items) {
|
|
52
|
-
const offset = offsetMap.get(item.key);
|
|
53
|
-
if (offset !== undefined) {
|
|
54
|
-
offsets.push(offset);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
throw new Error("updateBulk item Key does not exist");
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (bufferKeys) {
|
|
61
|
-
for (const key of bufferKeys) {
|
|
62
|
-
const bufferManagerComp = bufferManagersMap.get(key);
|
|
63
|
-
if (bufferManagerComp === undefined)
|
|
64
|
-
throw new Error("updateBulk bufferKey does not exist");
|
|
65
|
-
const { bufferManager, adaptor } = bufferManagerComp;
|
|
66
|
-
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
71
|
-
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
deleteBulk(keys, bufferManagersMap) {
|
|
76
|
-
const offsets = [];
|
|
77
|
-
for (const key of keys) {
|
|
78
|
-
const offset = this.getOffset(key);
|
|
79
|
-
if (offset !== undefined) {
|
|
80
|
-
offsets.push(offset);
|
|
81
|
-
this.offsetMap.delete(key);
|
|
82
|
-
this.tombstoneOffsets.push(offset);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
86
|
-
bufferManager.deleteBulk(offsets);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
getOffset(key) {
|
|
90
|
-
return this.offsetMap.get(key);
|
|
91
|
-
}
|
|
92
|
-
nextOffset() {
|
|
93
|
-
if (this.tombstoneOffsets.length > 0) {
|
|
94
|
-
const offset = this.tombstoneOffsets.pop();
|
|
95
|
-
return offset;
|
|
96
|
-
}
|
|
97
|
-
if (this._length < this._capacity) {
|
|
98
|
-
return this._length++;
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
ensureSpace(itemsLength, bufferManagersMap) {
|
|
103
|
-
if (itemsLength <= this.emptySpace)
|
|
104
|
-
return;
|
|
105
|
-
const newCapacity = this.length + itemsLength;
|
|
106
|
-
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
107
|
-
bufferManager.extendBuffer(this.length, newCapacity);
|
|
108
|
-
}
|
|
109
|
-
this._capacity = newCapacity;
|
|
110
|
-
}
|
|
111
|
-
defrag(bufferManagers, bufferKeys) {
|
|
112
|
-
const offsetMap = this.offsetMap;
|
|
113
|
-
const newCapacity = offsetMap.size + EXTRA_SIZE;
|
|
114
|
-
if (bufferKeys) {
|
|
115
|
-
for (const key of bufferKeys) {
|
|
116
|
-
const offset = offsetMap.get(key);
|
|
117
|
-
if (offset !== undefined) {
|
|
118
|
-
for (const [key, { bufferManager }] of bufferManagers) {
|
|
119
|
-
bufferManager.defrag([offset], this.length, newCapacity);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
for (const [key, { bufferManager }] of bufferManagers) {
|
|
126
|
-
bufferManager.defrag(offsetMap.values(), this.length, newCapacity);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
this._defrag();
|
|
130
|
-
this._length = offsetMap.size;
|
|
131
|
-
this._capacity = newCapacity;
|
|
132
|
-
this.tombstoneOffsets = [];
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Flushes metadata and sets length to 0 without actualize change on buffers
|
|
136
|
-
* This method created for cases in which data is loaded on each frame
|
|
137
|
-
*/
|
|
138
|
-
flush({ capacity = 10 } = {}) {
|
|
139
|
-
this._length = 0;
|
|
140
|
-
this._capacity = capacity;
|
|
141
|
-
this.tombstoneOffsets = [];
|
|
142
|
-
this.offsetMap.clear();
|
|
143
|
-
}
|
|
144
|
-
_defrag() {
|
|
145
|
-
const newOffsetMap = new Map();
|
|
146
|
-
let newOffset = 0;
|
|
147
|
-
for (const [key, offset] of this.offsetMap) {
|
|
148
|
-
newOffsetMap.set(key, newOffset++);
|
|
149
|
-
}
|
|
150
|
-
this.offsetMap = newOffsetMap;
|
|
151
|
-
}
|
|
152
|
-
get length() {
|
|
153
|
-
return this._length;
|
|
154
|
-
}
|
|
155
|
-
get emptySpace() {
|
|
156
|
-
return this._capacity - this.offsetMap.size;
|
|
157
|
-
}
|
|
158
|
-
get capacity() {
|
|
159
|
-
return this._capacity;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { BufferManager } from "./buffer-manager";
|
|
2
|
-
export class ChunkedBufferManager extends BufferManager {
|
|
3
|
-
// Chunked insert that batches multiple items into fewer GPU calls
|
|
4
|
-
insertBulkChunked(blocks, offsets, chunkSize = 1000) {
|
|
5
|
-
const { gl, buffer, itemSize } = this;
|
|
6
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
7
|
-
const offsetMultiplier = itemSize * 4;
|
|
8
|
-
// Process in chunks to reduce GPU call overhead
|
|
9
|
-
for (let i = 0; i < blocks.length; i += chunkSize) {
|
|
10
|
-
const chunkEnd = Math.min(i + chunkSize, blocks.length);
|
|
11
|
-
const chunkBlocks = blocks.slice(i, chunkEnd);
|
|
12
|
-
const chunkOffsets = offsets.slice(i, chunkEnd);
|
|
13
|
-
// Create a single large buffer for this chunk
|
|
14
|
-
const totalItems = chunkBlocks.length;
|
|
15
|
-
const chunkBuffer = new Float32Array(totalItems * itemSize);
|
|
16
|
-
// Copy all chunk data into one buffer
|
|
17
|
-
for (let j = 0; j < chunkBlocks.length; j++) {
|
|
18
|
-
chunkBuffer.set(chunkBlocks[j], j * itemSize);
|
|
19
|
-
}
|
|
20
|
-
// Find contiguous ranges for efficient uploads
|
|
21
|
-
const ranges = this.findContiguousRanges(chunkOffsets, offsetMultiplier);
|
|
22
|
-
for (const range of ranges) {
|
|
23
|
-
const startOffset = range.startOffset;
|
|
24
|
-
const dataStart = range.indices[0] * itemSize;
|
|
25
|
-
const dataLength = range.indices.length * itemSize;
|
|
26
|
-
const rangeData = chunkBuffer.subarray(dataStart, dataStart + dataLength);
|
|
27
|
-
gl.bufferSubData(gl.ARRAY_BUFFER, startOffset, rangeData);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
31
|
-
}
|
|
32
|
-
// Optimized delete that batches operations
|
|
33
|
-
deleteBulkChunked(offsets, chunkSize = 1000) {
|
|
34
|
-
const { gl, buffer, itemSize } = this;
|
|
35
|
-
const emptyBlock = new Float32Array(itemSize).fill(NaN);
|
|
36
|
-
const offsetMultiplier = itemSize * 4;
|
|
37
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
38
|
-
// Sort offsets for better cache coherency
|
|
39
|
-
const sortedOffsets = [...offsets].sort((a, b) => a - b);
|
|
40
|
-
// Process in chunks
|
|
41
|
-
for (let i = 0; i < sortedOffsets.length; i += chunkSize) {
|
|
42
|
-
const chunkEnd = Math.min(i + chunkSize, sortedOffsets.length);
|
|
43
|
-
for (let j = i; j < chunkEnd; j++) {
|
|
44
|
-
const offset = sortedOffsets[j];
|
|
45
|
-
if (offset !== undefined) {
|
|
46
|
-
gl.bufferSubData(gl.ARRAY_BUFFER, offset * offsetMultiplier, emptyBlock);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
51
|
-
}
|
|
52
|
-
// Find contiguous memory ranges for batch uploads
|
|
53
|
-
findContiguousRanges(offsets, offsetMultiplier) {
|
|
54
|
-
const ranges = [];
|
|
55
|
-
const sortedIndices = offsets
|
|
56
|
-
.map((offset, index) => ({ offset: offset * offsetMultiplier, index }))
|
|
57
|
-
.sort((a, b) => a.offset - b.offset);
|
|
58
|
-
let currentRange = null;
|
|
59
|
-
for (const { offset, index } of sortedIndices) {
|
|
60
|
-
if (!currentRange || offset !== currentRange.startOffset + currentRange.indices.length * offsetMultiplier) {
|
|
61
|
-
// Start new range
|
|
62
|
-
if (currentRange)
|
|
63
|
-
ranges.push(currentRange);
|
|
64
|
-
currentRange = { startOffset: offset, indices: [index] };
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
// Extend current range
|
|
68
|
-
currentRange.indices.push(index);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (currentRange)
|
|
72
|
-
ranges.push(currentRange);
|
|
73
|
-
return ranges;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
export class ChunkedBufferOrchestrator {
|
|
2
|
-
_capacity;
|
|
3
|
-
_length;
|
|
4
|
-
offsetMap;
|
|
5
|
-
tombstoneOffsets;
|
|
6
|
-
// Chunking configuration
|
|
7
|
-
chunkSize;
|
|
8
|
-
maxPendingOperations;
|
|
9
|
-
autoFlushInterval;
|
|
10
|
-
// Pending operations batching
|
|
11
|
-
pendingInserts;
|
|
12
|
-
pendingUpdates;
|
|
13
|
-
pendingDeletes;
|
|
14
|
-
operationCount;
|
|
15
|
-
flushTimer;
|
|
16
|
-
constructor({ capacity = 10, chunkSize = 1000, maxPendingOperations = 5000, autoFlushInterval = 16 // ~60fps
|
|
17
|
-
} = {}) {
|
|
18
|
-
this._capacity = capacity;
|
|
19
|
-
this._length = 0;
|
|
20
|
-
this.offsetMap = new Map();
|
|
21
|
-
this.tombstoneOffsets = [];
|
|
22
|
-
this.chunkSize = chunkSize;
|
|
23
|
-
this.maxPendingOperations = maxPendingOperations;
|
|
24
|
-
this.autoFlushInterval = autoFlushInterval;
|
|
25
|
-
this.pendingInserts = new Map();
|
|
26
|
-
this.pendingUpdates = new Map();
|
|
27
|
-
this.pendingDeletes = new Set();
|
|
28
|
-
this.operationCount = 0;
|
|
29
|
-
this.flushTimer = null;
|
|
30
|
-
this.startAutoFlush();
|
|
31
|
-
}
|
|
32
|
-
resetWithCapacity(bufferManagersMap, capacity = null) {
|
|
33
|
-
this.flushPendingOperations(bufferManagersMap);
|
|
34
|
-
this._capacity = capacity !== null ? capacity : this._capacity;
|
|
35
|
-
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
36
|
-
bufferManager.resetWithCapacity(this._capacity);
|
|
37
|
-
}
|
|
38
|
-
this.offsetMap.clear();
|
|
39
|
-
this.tombstoneOffsets = [];
|
|
40
|
-
this._length = 0;
|
|
41
|
-
}
|
|
42
|
-
// Batch insert operations
|
|
43
|
-
insertBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
44
|
-
const targetKeys = bufferKeys || Array.from(bufferManagersMap.keys());
|
|
45
|
-
// Process items in chunks
|
|
46
|
-
for (let i = 0; i < items.length; i += this.chunkSize) {
|
|
47
|
-
const chunk = items.slice(i, i + this.chunkSize);
|
|
48
|
-
this.queueInsertChunk(chunk, targetKeys);
|
|
49
|
-
}
|
|
50
|
-
this.operationCount += items.length;
|
|
51
|
-
this.checkAutoFlush(bufferManagersMap);
|
|
52
|
-
}
|
|
53
|
-
// Batch update operations
|
|
54
|
-
updateBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
55
|
-
const targetKeys = bufferKeys || Array.from(bufferManagersMap.keys());
|
|
56
|
-
for (let i = 0; i < items.length; i += this.chunkSize) {
|
|
57
|
-
const chunk = items.slice(i, i + this.chunkSize);
|
|
58
|
-
this.queueUpdateChunk(chunk, targetKeys);
|
|
59
|
-
}
|
|
60
|
-
this.operationCount += items.length;
|
|
61
|
-
this.checkAutoFlush(bufferManagersMap);
|
|
62
|
-
}
|
|
63
|
-
// Batch delete operations
|
|
64
|
-
deleteBulk(keys, bufferManagersMap) {
|
|
65
|
-
for (const key of keys) {
|
|
66
|
-
const offset = this.offsetMap.get(key);
|
|
67
|
-
if (offset !== undefined) {
|
|
68
|
-
this.pendingDeletes.add(offset);
|
|
69
|
-
this.offsetMap.delete(key);
|
|
70
|
-
this.tombstoneOffsets.push(offset);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
this.operationCount += keys.length;
|
|
74
|
-
this.checkAutoFlush(bufferManagersMap);
|
|
75
|
-
}
|
|
76
|
-
queueInsertChunk(items, bufferKeys) {
|
|
77
|
-
this.ensureSpaceForItems(items.length);
|
|
78
|
-
const offsets = [];
|
|
79
|
-
for (const item of items) {
|
|
80
|
-
let offset = this.offsetMap.get(item.key);
|
|
81
|
-
if (offset === undefined) {
|
|
82
|
-
offset = this.nextOffset();
|
|
83
|
-
this.offsetMap.set(item.key, offset);
|
|
84
|
-
}
|
|
85
|
-
offsets.push(offset);
|
|
86
|
-
}
|
|
87
|
-
for (const bufferKey of bufferKeys) {
|
|
88
|
-
if (!this.pendingInserts.has(bufferKey)) {
|
|
89
|
-
this.pendingInserts.set(bufferKey, { items: [], offsets: [] });
|
|
90
|
-
}
|
|
91
|
-
const pending = this.pendingInserts.get(bufferKey);
|
|
92
|
-
pending.items.push(...items);
|
|
93
|
-
pending.offsets.push(...offsets);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
queueUpdateChunk(items, bufferKeys) {
|
|
97
|
-
const offsets = [];
|
|
98
|
-
for (const item of items) {
|
|
99
|
-
const offset = this.offsetMap.get(item.key);
|
|
100
|
-
if (offset === undefined) {
|
|
101
|
-
throw new Error(`Update item key '${item.key}' does not exist`);
|
|
102
|
-
}
|
|
103
|
-
offsets.push(offset);
|
|
104
|
-
}
|
|
105
|
-
for (const bufferKey of bufferKeys) {
|
|
106
|
-
if (!this.pendingUpdates.has(bufferKey)) {
|
|
107
|
-
this.pendingUpdates.set(bufferKey, { items: [], offsets: [] });
|
|
108
|
-
}
|
|
109
|
-
const pending = this.pendingUpdates.get(bufferKey);
|
|
110
|
-
pending.items.push(...items);
|
|
111
|
-
pending.offsets.push(...offsets);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
checkAutoFlush(bufferManagersMap) {
|
|
115
|
-
if (this.operationCount >= this.maxPendingOperations) {
|
|
116
|
-
this.flushPendingOperations(bufferManagersMap);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// Force flush all pending operations to GPU
|
|
120
|
-
flushPendingOperations(bufferManagersMap) {
|
|
121
|
-
// Process deletes first
|
|
122
|
-
if (this.pendingDeletes.size > 0) {
|
|
123
|
-
const deleteOffsets = Array.from(this.pendingDeletes);
|
|
124
|
-
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
125
|
-
bufferManager.deleteBulk(deleteOffsets);
|
|
126
|
-
}
|
|
127
|
-
this.pendingDeletes.clear();
|
|
128
|
-
}
|
|
129
|
-
// Process inserts
|
|
130
|
-
for (const [bufferKey, { items, offsets }] of this.pendingInserts) {
|
|
131
|
-
const bufferManagerComp = bufferManagersMap.get(bufferKey);
|
|
132
|
-
if (bufferManagerComp) {
|
|
133
|
-
const { bufferManager, adaptor } = bufferManagerComp;
|
|
134
|
-
const adaptedData = items.map(adaptor);
|
|
135
|
-
bufferManager.insertBulkChunked(adaptedData, offsets, this.chunkSize);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
this.pendingInserts.clear();
|
|
139
|
-
// Process updates
|
|
140
|
-
for (const [bufferKey, { items, offsets }] of this.pendingUpdates) {
|
|
141
|
-
const bufferManagerComp = bufferManagersMap.get(bufferKey);
|
|
142
|
-
if (bufferManagerComp) {
|
|
143
|
-
const { bufferManager, adaptor } = bufferManagerComp;
|
|
144
|
-
const adaptedData = items.map(adaptor);
|
|
145
|
-
bufferManager.insertBulkChunked(adaptedData, offsets, this.chunkSize);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
this.pendingUpdates.clear();
|
|
149
|
-
this.operationCount = 0;
|
|
150
|
-
}
|
|
151
|
-
ensureSpaceForItems(itemCount) {
|
|
152
|
-
if (itemCount <= this.emptySpace)
|
|
153
|
-
return;
|
|
154
|
-
const newCapacity = Math.max(this._capacity * 2, this._length + itemCount + this.chunkSize);
|
|
155
|
-
this._capacity = newCapacity;
|
|
156
|
-
}
|
|
157
|
-
nextOffset() {
|
|
158
|
-
if (this.tombstoneOffsets.length > 0) {
|
|
159
|
-
return this.tombstoneOffsets.pop();
|
|
160
|
-
}
|
|
161
|
-
if (this._length < this._capacity) {
|
|
162
|
-
return this._length++;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
startAutoFlush() {
|
|
167
|
-
if (this.autoFlushInterval > 0) {
|
|
168
|
-
this.flushTimer = setInterval(() => {
|
|
169
|
-
if (this.operationCount > 0) {
|
|
170
|
-
// Note: You'll need to pass bufferManagersMap here
|
|
171
|
-
// This requires restructuring to maintain reference
|
|
172
|
-
console.log(`Auto-flush triggered with ${this.operationCount} pending operations`);
|
|
173
|
-
}
|
|
174
|
-
}, this.autoFlushInterval);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
stopAutoFlush() {
|
|
178
|
-
if (this.flushTimer) {
|
|
179
|
-
clearInterval(this.flushTimer);
|
|
180
|
-
this.flushTimer = null;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Getters
|
|
184
|
-
get length() { return this._length; }
|
|
185
|
-
get emptySpace() { return this._capacity - this.offsetMap.size; }
|
|
186
|
-
get capacity() { return this._capacity; }
|
|
187
|
-
get pendingOperationCount() { return this.operationCount; }
|
|
188
|
-
// Manual control
|
|
189
|
-
forceMajorFlush(bufferManagersMap) {
|
|
190
|
-
this.flushPendingOperations(bufferManagersMap);
|
|
191
|
-
}
|
|
192
|
-
getOffset(key) {
|
|
193
|
-
return this.offsetMap.get(key);
|
|
194
|
-
}
|
|
195
|
-
}
|