@luma.gl/webgpu 9.3.0-alpha.2 → 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/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
- package/dist/adapter/helpers/get-bind-group.d.ts +2 -1
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +31 -21
- package/dist/adapter/helpers/get-bind-group.js.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
- package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.js +58 -15
- package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.d.ts +5 -4
- package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.js +23 -5
- package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.d.ts +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.js +14 -6
- package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.js +19 -3
- package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
- package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.js +1 -2
- package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
- package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.js +145 -4
- package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.d.ts +3 -0
- package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.js +74 -30
- package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts +7 -4
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.js +26 -15
- package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.js +4 -0
- package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
- package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.js +47 -11
- package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture.d.ts +10 -4
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +116 -57
- package/dist/adapter/resources/webgpu-texture.js.map +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
- package/dist/adapter/webgpu-canvas-context.d.ts +2 -0
- package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgpu-canvas-context.js +78 -19
- package/dist/adapter/webgpu-canvas-context.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +5 -1
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +113 -9
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
- package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
- package/dist/adapter/webgpu-presentation-context.js +144 -0
- package/dist/adapter/webgpu-presentation-context.js.map +1 -0
- package/dist/dist.dev.js +2864 -1702
- package/dist/dist.min.js +167 -8
- package/dist/index.cjs +1363 -225
- package/dist/index.cjs.map +4 -4
- package/package.json +5 -5
- package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
- package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
- package/src/adapter/helpers/get-bind-group.ts +37 -22
- package/src/adapter/resources/webgpu-buffer.ts +61 -15
- package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
- package/src/adapter/resources/webgpu-command-encoder.ts +32 -6
- package/src/adapter/resources/webgpu-compute-pass.ts +14 -6
- package/src/adapter/resources/webgpu-compute-pipeline.ts +21 -3
- package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
- package/src/adapter/resources/webgpu-pipeline-layout.ts +1 -2
- package/src/adapter/resources/webgpu-query-set.ts +185 -9
- package/src/adapter/resources/webgpu-render-pass.ts +82 -34
- package/src/adapter/resources/webgpu-render-pipeline.ts +36 -19
- package/src/adapter/resources/webgpu-sampler.ts +5 -0
- package/src/adapter/resources/webgpu-texture-view.ts +51 -11
- package/src/adapter/resources/webgpu-texture.ts +142 -93
- package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
- package/src/adapter/webgpu-canvas-context.ts +91 -26
- package/src/adapter/webgpu-device.ts +128 -9
- package/src/adapter/webgpu-presentation-context.ts +180 -0
package/dist/index.cjs
CHANGED
|
@@ -7,8 +7,8 @@ var __esm = (fn, res) => function __init() {
|
|
|
7
7
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
8
|
};
|
|
9
9
|
var __export = (target, all) => {
|
|
10
|
-
for (var
|
|
11
|
-
__defProp(target,
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
12
|
};
|
|
13
13
|
var __copyProps = (to, from, except, desc) => {
|
|
14
14
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -30,13 +30,15 @@ var init_webgpu_buffer = __esm({
|
|
|
30
30
|
device;
|
|
31
31
|
handle;
|
|
32
32
|
byteLength;
|
|
33
|
+
paddedByteLength;
|
|
33
34
|
constructor(device, props) {
|
|
34
35
|
var _a, _b;
|
|
35
36
|
super(device, props);
|
|
36
37
|
this.device = device;
|
|
37
38
|
this.byteLength = props.byteLength || ((_a = props.data) == null ? void 0 : _a.byteLength) || 0;
|
|
39
|
+
this.paddedByteLength = Math.ceil(this.byteLength / 4) * 4;
|
|
38
40
|
const mappedAtCreation = Boolean(this.props.onMapped || props.data);
|
|
39
|
-
const size =
|
|
41
|
+
const size = this.paddedByteLength;
|
|
40
42
|
this.device.pushErrorScope("out-of-memory");
|
|
41
43
|
this.device.pushErrorScope("validation");
|
|
42
44
|
this.handle = this.props.handle || this.device.handle.createBuffer({
|
|
@@ -72,11 +74,24 @@ var init_webgpu_buffer = __esm({
|
|
|
72
74
|
this.device.reportError(new Error(`${this} creation failed ${error.message}`), this)();
|
|
73
75
|
this.device.debug();
|
|
74
76
|
});
|
|
77
|
+
if (!this.props.handle) {
|
|
78
|
+
this.trackAllocatedMemory(size);
|
|
79
|
+
} else {
|
|
80
|
+
this.trackReferencedMemory(size, "Buffer");
|
|
81
|
+
}
|
|
75
82
|
}
|
|
76
83
|
destroy() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
if (!this.destroyed && this.handle) {
|
|
85
|
+
this.removeStats();
|
|
86
|
+
if (!this.props.handle) {
|
|
87
|
+
this.trackDeallocatedMemory();
|
|
88
|
+
this.handle.destroy();
|
|
89
|
+
} else {
|
|
90
|
+
this.trackDeallocatedReferencedMemory("Buffer");
|
|
91
|
+
}
|
|
92
|
+
this.destroyed = true;
|
|
93
|
+
this.handle = null;
|
|
94
|
+
}
|
|
80
95
|
}
|
|
81
96
|
write(data, byteOffset = 0) {
|
|
82
97
|
const arrayBuffer = ArrayBuffer.isView(data) ? data.buffer : data;
|
|
@@ -89,18 +104,21 @@ var init_webgpu_buffer = __esm({
|
|
|
89
104
|
});
|
|
90
105
|
}
|
|
91
106
|
async mapAndWriteAsync(callback, byteOffset = 0, byteLength = this.byteLength - byteOffset) {
|
|
107
|
+
const alignedByteLength = Math.ceil(byteLength / 4) * 4;
|
|
92
108
|
const isMappable = (this.usage & import_core.Buffer.MAP_WRITE) !== 0;
|
|
93
|
-
const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_WRITE | import_core.Buffer.COPY_SRC, 0, this.
|
|
109
|
+
const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_WRITE | import_core.Buffer.COPY_SRC, 0, this.paddedByteLength) : null;
|
|
94
110
|
const writeBuffer = mappableBuffer || this;
|
|
95
111
|
this.device.pushErrorScope("validation");
|
|
96
112
|
try {
|
|
97
113
|
await this.device.handle.queue.onSubmittedWorkDone();
|
|
98
|
-
await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset,
|
|
99
|
-
const
|
|
114
|
+
await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset, alignedByteLength);
|
|
115
|
+
const mappedRange = writeBuffer.handle.getMappedRange(byteOffset, alignedByteLength);
|
|
116
|
+
const arrayBuffer = mappedRange.slice(0, byteLength);
|
|
100
117
|
await callback(arrayBuffer, "mapped");
|
|
118
|
+
new Uint8Array(mappedRange).set(new Uint8Array(arrayBuffer), 0);
|
|
101
119
|
writeBuffer.handle.unmap();
|
|
102
120
|
if (mappableBuffer) {
|
|
103
|
-
this._copyBuffer(mappableBuffer, byteOffset,
|
|
121
|
+
this._copyBuffer(mappableBuffer, byteOffset, alignedByteLength);
|
|
104
122
|
}
|
|
105
123
|
} finally {
|
|
106
124
|
this.device.popErrorScope((error) => {
|
|
@@ -114,24 +132,37 @@ var init_webgpu_buffer = __esm({
|
|
|
114
132
|
return this.mapAndReadAsync((arrayBuffer) => new Uint8Array(arrayBuffer.slice(0)), byteOffset, byteLength);
|
|
115
133
|
}
|
|
116
134
|
async mapAndReadAsync(callback, byteOffset = 0, byteLength = this.byteLength - byteOffset) {
|
|
135
|
+
const requestedEnd = byteOffset + byteLength;
|
|
136
|
+
if (requestedEnd > this.byteLength) {
|
|
137
|
+
throw new Error("Mapping range exceeds buffer size");
|
|
138
|
+
}
|
|
139
|
+
let mappedByteOffset = byteOffset;
|
|
140
|
+
let mappedByteLength = byteLength;
|
|
141
|
+
let sliceByteOffset = 0;
|
|
142
|
+
let lifetime = "mapped";
|
|
117
143
|
if (byteOffset % 8 !== 0 || byteLength % 4 !== 0) {
|
|
118
|
-
|
|
144
|
+
mappedByteOffset = Math.floor(byteOffset / 8) * 8;
|
|
145
|
+
const alignedEnd = Math.ceil(requestedEnd / 4) * 4;
|
|
146
|
+
mappedByteLength = alignedEnd - mappedByteOffset;
|
|
147
|
+
sliceByteOffset = byteOffset - mappedByteOffset;
|
|
148
|
+
lifetime = "copied";
|
|
119
149
|
}
|
|
120
|
-
if (
|
|
150
|
+
if (mappedByteOffset + mappedByteLength > this.paddedByteLength) {
|
|
121
151
|
throw new Error("Mapping range exceeds buffer size");
|
|
122
152
|
}
|
|
123
153
|
const isMappable = (this.usage & import_core.Buffer.MAP_READ) !== 0;
|
|
124
|
-
const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_READ | import_core.Buffer.COPY_DST, 0, this.
|
|
154
|
+
const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_READ | import_core.Buffer.COPY_DST, 0, this.paddedByteLength) : null;
|
|
125
155
|
const readBuffer = mappableBuffer || this;
|
|
126
156
|
this.device.pushErrorScope("validation");
|
|
127
157
|
try {
|
|
128
158
|
await this.device.handle.queue.onSubmittedWorkDone();
|
|
129
159
|
if (mappableBuffer) {
|
|
130
|
-
mappableBuffer._copyBuffer(this);
|
|
160
|
+
mappableBuffer._copyBuffer(this, mappedByteOffset, mappedByteLength);
|
|
131
161
|
}
|
|
132
|
-
await readBuffer.handle.mapAsync(GPUMapMode.READ,
|
|
133
|
-
const arrayBuffer = readBuffer.handle.getMappedRange(
|
|
134
|
-
const
|
|
162
|
+
await readBuffer.handle.mapAsync(GPUMapMode.READ, mappedByteOffset, mappedByteLength);
|
|
163
|
+
const arrayBuffer = readBuffer.handle.getMappedRange(mappedByteOffset, mappedByteLength);
|
|
164
|
+
const mappedRange = lifetime === "mapped" ? arrayBuffer : arrayBuffer.slice(sliceByteOffset, sliceByteOffset + byteLength);
|
|
165
|
+
const result = await callback(mappedRange, lifetime);
|
|
135
166
|
readBuffer.handle.unmap();
|
|
136
167
|
return result;
|
|
137
168
|
} finally {
|
|
@@ -208,18 +239,47 @@ var init_webgpu_sampler = __esm({
|
|
|
208
239
|
this.handle.label = this.props.id;
|
|
209
240
|
}
|
|
210
241
|
destroy() {
|
|
242
|
+
if (this.destroyed) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.destroyResource();
|
|
211
246
|
this.handle = null;
|
|
212
247
|
}
|
|
213
248
|
};
|
|
214
249
|
}
|
|
215
250
|
});
|
|
216
251
|
|
|
252
|
+
// dist/adapter/helpers/cpu-hotspot-profiler.js
|
|
253
|
+
function getCpuHotspotProfiler(owner) {
|
|
254
|
+
const profiler = owner.userData[CPU_HOTSPOT_PROFILER_MODULE];
|
|
255
|
+
return (profiler == null ? void 0 : profiler.enabled) ? profiler : null;
|
|
256
|
+
}
|
|
257
|
+
function getCpuHotspotSubmitReason(owner) {
|
|
258
|
+
return owner.userData[CPU_HOTSPOT_SUBMIT_REASON] || null;
|
|
259
|
+
}
|
|
260
|
+
function setCpuHotspotSubmitReason(owner, submitReason) {
|
|
261
|
+
owner.userData[CPU_HOTSPOT_SUBMIT_REASON] = submitReason;
|
|
262
|
+
}
|
|
263
|
+
function getTimestamp() {
|
|
264
|
+
var _a, _b;
|
|
265
|
+
return ((_b = (_a = globalThis.performance) == null ? void 0 : _a.now) == null ? void 0 : _b.call(_a)) ?? Date.now();
|
|
266
|
+
}
|
|
267
|
+
var CPU_HOTSPOT_PROFILER_MODULE, CPU_HOTSPOT_SUBMIT_REASON;
|
|
268
|
+
var init_cpu_hotspot_profiler = __esm({
|
|
269
|
+
"dist/adapter/helpers/cpu-hotspot-profiler.js"() {
|
|
270
|
+
"use strict";
|
|
271
|
+
CPU_HOTSPOT_PROFILER_MODULE = "cpu-hotspot-profiler";
|
|
272
|
+
CPU_HOTSPOT_SUBMIT_REASON = "cpu-hotspot-submit-reason";
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
217
276
|
// dist/adapter/resources/webgpu-texture-view.js
|
|
218
277
|
var import_core3, WebGPUTextureView;
|
|
219
278
|
var init_webgpu_texture_view = __esm({
|
|
220
279
|
"dist/adapter/resources/webgpu-texture-view.js"() {
|
|
221
280
|
"use strict";
|
|
222
281
|
import_core3 = require("@luma.gl/core");
|
|
282
|
+
init_cpu_hotspot_profiler();
|
|
223
283
|
WebGPUTextureView = class extends import_core3.TextureView {
|
|
224
284
|
device;
|
|
225
285
|
handle;
|
|
@@ -229,8 +289,7 @@ var init_webgpu_texture_view = __esm({
|
|
|
229
289
|
this.device = device;
|
|
230
290
|
this.texture = props.texture;
|
|
231
291
|
this.device.pushErrorScope("validation");
|
|
232
|
-
this.handle =
|
|
233
|
-
this.texture.handle.createView({
|
|
292
|
+
this.handle = this.texture.handle.createView({
|
|
234
293
|
format: this.props.format || this.texture.format,
|
|
235
294
|
dimension: this.props.dimension || this.texture.dimension,
|
|
236
295
|
aspect: this.props.aspect,
|
|
@@ -246,8 +305,42 @@ var init_webgpu_texture_view = __esm({
|
|
|
246
305
|
this.handle.label = this.props.id;
|
|
247
306
|
}
|
|
248
307
|
destroy() {
|
|
308
|
+
if (this.destroyed) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
this.destroyResource();
|
|
249
312
|
this.handle = null;
|
|
250
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
|
|
316
|
+
* Rebuilds the default view when the per-frame canvas texture handle changes, without
|
|
317
|
+
* replacing the long-lived luma.gl wrapper object.
|
|
318
|
+
*/
|
|
319
|
+
_reinitialize(texture) {
|
|
320
|
+
this.texture = texture;
|
|
321
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
322
|
+
this.device.pushErrorScope("validation");
|
|
323
|
+
const createViewStartTime = profiler ? getTimestamp() : 0;
|
|
324
|
+
const handle = this.texture.handle.createView({
|
|
325
|
+
format: this.props.format || this.texture.format,
|
|
326
|
+
dimension: this.props.dimension || this.texture.dimension,
|
|
327
|
+
aspect: this.props.aspect,
|
|
328
|
+
baseMipLevel: this.props.baseMipLevel,
|
|
329
|
+
mipLevelCount: this.props.mipLevelCount,
|
|
330
|
+
baseArrayLayer: this.props.baseArrayLayer,
|
|
331
|
+
arrayLayerCount: this.props.arrayLayerCount
|
|
332
|
+
});
|
|
333
|
+
if (profiler) {
|
|
334
|
+
profiler.textureViewReinitializeCount = (profiler.textureViewReinitializeCount || 0) + 1;
|
|
335
|
+
profiler.textureViewReinitializeTimeMs = (profiler.textureViewReinitializeTimeMs || 0) + (getTimestamp() - createViewStartTime);
|
|
336
|
+
}
|
|
337
|
+
this.device.popErrorScope((error) => {
|
|
338
|
+
this.device.reportError(new Error(`TextureView constructor: ${error.message}`), this)();
|
|
339
|
+
this.device.debug();
|
|
340
|
+
});
|
|
341
|
+
handle.label = this.props.id;
|
|
342
|
+
this.handle = handle;
|
|
343
|
+
}
|
|
251
344
|
};
|
|
252
345
|
}
|
|
253
346
|
});
|
|
@@ -266,9 +359,18 @@ var init_webgpu_texture = __esm({
|
|
|
266
359
|
handle;
|
|
267
360
|
sampler;
|
|
268
361
|
view;
|
|
362
|
+
_allocatedByteLength = 0;
|
|
269
363
|
constructor(device, props) {
|
|
270
364
|
super(device, props, { byteAlignment: 256 });
|
|
271
365
|
this.device = device;
|
|
366
|
+
if (props.sampler instanceof WebGPUSampler) {
|
|
367
|
+
this.sampler = props.sampler;
|
|
368
|
+
} else if (props.sampler === void 0) {
|
|
369
|
+
this.sampler = this.device.getDefaultSampler();
|
|
370
|
+
} else {
|
|
371
|
+
this.sampler = new WebGPUSampler(this.device, props.sampler || {});
|
|
372
|
+
this.attachResource(this.sampler);
|
|
373
|
+
}
|
|
272
374
|
this.device.pushErrorScope("out-of-memory");
|
|
273
375
|
this.device.pushErrorScope("validation");
|
|
274
376
|
this.handle = this.props.handle || this.device.handle.createTexture({
|
|
@@ -297,7 +399,6 @@ var init_webgpu_texture = __esm({
|
|
|
297
399
|
this.width = this.handle.width;
|
|
298
400
|
this.height = this.handle.height;
|
|
299
401
|
}
|
|
300
|
-
this.sampler = props.sampler instanceof WebGPUSampler ? props.sampler : new WebGPUSampler(this.device, props.sampler || {});
|
|
301
402
|
this.view = new WebGPUTextureView(this.device, {
|
|
302
403
|
...this.props,
|
|
303
404
|
texture: this,
|
|
@@ -305,11 +406,26 @@ var init_webgpu_texture = __esm({
|
|
|
305
406
|
// Note: arrayLayerCount controls the view of array textures, but does not apply to 3d texture depths
|
|
306
407
|
arrayLayerCount: this.dimension !== "3d" ? this.depth : 1
|
|
307
408
|
});
|
|
409
|
+
this.attachResource(this.view);
|
|
308
410
|
this._initializeData(props.data);
|
|
411
|
+
this._allocatedByteLength = this.getAllocatedByteLength();
|
|
412
|
+
if (!this.props.handle) {
|
|
413
|
+
this.trackAllocatedMemory(this._allocatedByteLength, "Texture");
|
|
414
|
+
} else {
|
|
415
|
+
this.trackReferencedMemory(this._allocatedByteLength, "Texture");
|
|
416
|
+
}
|
|
309
417
|
}
|
|
310
418
|
destroy() {
|
|
311
|
-
|
|
312
|
-
|
|
419
|
+
if (this.destroyed) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (!this.props.handle && this.handle) {
|
|
423
|
+
this.trackDeallocatedMemory("Texture");
|
|
424
|
+
this.handle.destroy();
|
|
425
|
+
} else if (this.handle) {
|
|
426
|
+
this.trackDeallocatedReferencedMemory("Texture");
|
|
427
|
+
}
|
|
428
|
+
this.destroyResource();
|
|
313
429
|
this.handle = null;
|
|
314
430
|
}
|
|
315
431
|
createView(props) {
|
|
@@ -345,36 +461,6 @@ var init_webgpu_texture = __esm({
|
|
|
345
461
|
});
|
|
346
462
|
return { width: options.width, height: options.height };
|
|
347
463
|
}
|
|
348
|
-
copyImageData(options_) {
|
|
349
|
-
const { width, height, depth } = this;
|
|
350
|
-
const options = this._normalizeCopyImageDataOptions(options_);
|
|
351
|
-
this.device.pushErrorScope("validation");
|
|
352
|
-
this.device.handle.queue.writeTexture(
|
|
353
|
-
// destination: GPUImageCopyTexture
|
|
354
|
-
{
|
|
355
|
-
// texture subresource
|
|
356
|
-
texture: this.handle,
|
|
357
|
-
mipLevel: options.mipLevel,
|
|
358
|
-
aspect: options.aspect,
|
|
359
|
-
// origin to write to
|
|
360
|
-
origin: [options.x, options.y, options.z]
|
|
361
|
-
},
|
|
362
|
-
// data
|
|
363
|
-
options.data,
|
|
364
|
-
// dataLayout: GPUImageDataLayout
|
|
365
|
-
{
|
|
366
|
-
offset: options.byteOffset,
|
|
367
|
-
bytesPerRow: options.bytesPerRow,
|
|
368
|
-
rowsPerImage: options.rowsPerImage
|
|
369
|
-
},
|
|
370
|
-
// size: GPUExtent3D - extents of the content to write
|
|
371
|
-
[width, height, depth]
|
|
372
|
-
);
|
|
373
|
-
this.device.popErrorScope((error) => {
|
|
374
|
-
this.device.reportError(new Error(`copyImageData: ${error.message}`), this)();
|
|
375
|
-
this.device.debug();
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
464
|
generateMipmapsWebGL() {
|
|
379
465
|
import_core4.log.warn(`${this}: generateMipmaps not supported in WebGPU`)();
|
|
380
466
|
}
|
|
@@ -386,13 +472,16 @@ var init_webgpu_texture = __esm({
|
|
|
386
472
|
};
|
|
387
473
|
}
|
|
388
474
|
readBuffer(options = {}, buffer) {
|
|
389
|
-
const { x
|
|
390
|
-
const layout = this.computeMemoryLayout(
|
|
475
|
+
const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect } = this._getSupportedColorReadOptions(options);
|
|
476
|
+
const layout = this.computeMemoryLayout({ x, y, z, width, height, depthOrArrayLayers, mipLevel });
|
|
391
477
|
const { bytesPerRow, rowsPerImage, byteLength } = layout;
|
|
392
478
|
const readBuffer = buffer || this.device.createBuffer({
|
|
393
479
|
byteLength,
|
|
394
480
|
usage: import_core4.Buffer.COPY_DST | import_core4.Buffer.MAP_READ
|
|
395
481
|
});
|
|
482
|
+
if (readBuffer.byteLength < byteLength) {
|
|
483
|
+
throw new Error(`${this} readBuffer target is too small (${readBuffer.byteLength} < ${byteLength})`);
|
|
484
|
+
}
|
|
396
485
|
const gpuReadBuffer = readBuffer.handle;
|
|
397
486
|
const gpuDevice = this.device.handle;
|
|
398
487
|
this.device.pushErrorScope("validation");
|
|
@@ -436,16 +525,15 @@ var init_webgpu_texture = __esm({
|
|
|
436
525
|
buffer.destroy();
|
|
437
526
|
return data.buffer;
|
|
438
527
|
}
|
|
439
|
-
writeBuffer(buffer,
|
|
440
|
-
const
|
|
441
|
-
const
|
|
442
|
-
const { bytesPerRow, rowsPerImage } = layout;
|
|
528
|
+
writeBuffer(buffer, options_ = {}) {
|
|
529
|
+
const options = this._normalizeTextureWriteOptions(options_);
|
|
530
|
+
const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset, bytesPerRow, rowsPerImage } = options;
|
|
443
531
|
const gpuDevice = this.device.handle;
|
|
444
532
|
this.device.pushErrorScope("validation");
|
|
445
533
|
const commandEncoder = gpuDevice.createCommandEncoder();
|
|
446
534
|
commandEncoder.copyBufferToTexture({
|
|
447
535
|
buffer: buffer.handle,
|
|
448
|
-
offset:
|
|
536
|
+
offset: byteOffset,
|
|
449
537
|
bytesPerRow,
|
|
450
538
|
rowsPerImage
|
|
451
539
|
}, {
|
|
@@ -461,33 +549,88 @@ var init_webgpu_texture = __esm({
|
|
|
461
549
|
this.device.debug();
|
|
462
550
|
});
|
|
463
551
|
}
|
|
464
|
-
writeData(data,
|
|
552
|
+
writeData(data, options_ = {}) {
|
|
465
553
|
const device = this.device;
|
|
466
|
-
const
|
|
467
|
-
const
|
|
554
|
+
const options = this._normalizeTextureWriteOptions(options_);
|
|
555
|
+
const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset } = options;
|
|
556
|
+
const source = data;
|
|
557
|
+
const formatInfo = this.device.getTextureFormatInfo(this.format);
|
|
558
|
+
const packedSourceLayout = import_core4.textureFormatDecoder.computeMemoryLayout({
|
|
468
559
|
format: this.format,
|
|
469
|
-
width
|
|
470
|
-
height
|
|
471
|
-
depth:
|
|
472
|
-
byteAlignment:
|
|
560
|
+
width,
|
|
561
|
+
height,
|
|
562
|
+
depth: depthOrArrayLayers,
|
|
563
|
+
byteAlignment: 1
|
|
473
564
|
});
|
|
474
|
-
const
|
|
565
|
+
const bytesPerRow = options_.bytesPerRow ?? packedSourceLayout.bytesPerRow;
|
|
566
|
+
const rowsPerImage = options_.rowsPerImage ?? packedSourceLayout.rowsPerImage;
|
|
567
|
+
let copyWidth = width;
|
|
568
|
+
let copyHeight = height;
|
|
569
|
+
if (formatInfo.compressed) {
|
|
570
|
+
const blockWidth = formatInfo.blockWidth || 1;
|
|
571
|
+
const blockHeight = formatInfo.blockHeight || 1;
|
|
572
|
+
copyWidth = Math.ceil(width / blockWidth) * blockWidth;
|
|
573
|
+
copyHeight = Math.ceil(height / blockHeight) * blockHeight;
|
|
574
|
+
}
|
|
475
575
|
this.device.pushErrorScope("validation");
|
|
476
576
|
device.handle.queue.writeTexture({
|
|
477
577
|
texture: this.handle,
|
|
478
578
|
mipLevel,
|
|
479
579
|
aspect,
|
|
480
580
|
origin: { x, y, z }
|
|
481
|
-
},
|
|
482
|
-
offset:
|
|
581
|
+
}, source, {
|
|
582
|
+
offset: byteOffset,
|
|
483
583
|
bytesPerRow,
|
|
484
584
|
rowsPerImage
|
|
485
|
-
}, { width, height, depthOrArrayLayers });
|
|
585
|
+
}, { width: copyWidth, height: copyHeight, depthOrArrayLayers });
|
|
486
586
|
this.device.popErrorScope((error) => {
|
|
487
587
|
this.device.reportError(new Error(`${this} writeData: ${error.message}`), this)();
|
|
488
588
|
this.device.debug();
|
|
489
589
|
});
|
|
490
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
|
|
593
|
+
* Rebinds this handle-backed texture wrapper to the current per-frame canvas texture
|
|
594
|
+
* without allocating a new luma.gl Texture or TextureView wrapper.
|
|
595
|
+
*/
|
|
596
|
+
_reinitialize(handle, props) {
|
|
597
|
+
const nextWidth = (props == null ? void 0 : props.width) ?? handle.width ?? this.width;
|
|
598
|
+
const nextHeight = (props == null ? void 0 : props.height) ?? handle.height ?? this.height;
|
|
599
|
+
const nextDepth = (props == null ? void 0 : props.depth) ?? this.depth;
|
|
600
|
+
const nextFormat = (props == null ? void 0 : props.format) ?? this.format;
|
|
601
|
+
const allocationMayHaveChanged = nextWidth !== this.width || nextHeight !== this.height || nextDepth !== this.depth || nextFormat !== this.format;
|
|
602
|
+
handle.label ||= this.id;
|
|
603
|
+
this.handle = handle;
|
|
604
|
+
this.width = nextWidth;
|
|
605
|
+
this.height = nextHeight;
|
|
606
|
+
if ((props == null ? void 0 : props.depth) !== void 0) {
|
|
607
|
+
this.depth = nextDepth;
|
|
608
|
+
}
|
|
609
|
+
if ((props == null ? void 0 : props.format) !== void 0) {
|
|
610
|
+
this.format = nextFormat;
|
|
611
|
+
}
|
|
612
|
+
this.props.handle = handle;
|
|
613
|
+
if ((props == null ? void 0 : props.width) !== void 0) {
|
|
614
|
+
this.props.width = props.width;
|
|
615
|
+
}
|
|
616
|
+
if ((props == null ? void 0 : props.height) !== void 0) {
|
|
617
|
+
this.props.height = props.height;
|
|
618
|
+
}
|
|
619
|
+
if ((props == null ? void 0 : props.depth) !== void 0) {
|
|
620
|
+
this.props.depth = props.depth;
|
|
621
|
+
}
|
|
622
|
+
if ((props == null ? void 0 : props.format) !== void 0) {
|
|
623
|
+
this.props.format = props.format;
|
|
624
|
+
}
|
|
625
|
+
if (allocationMayHaveChanged) {
|
|
626
|
+
const nextAllocation = this.getAllocatedByteLength();
|
|
627
|
+
if (nextAllocation !== this._allocatedByteLength) {
|
|
628
|
+
this._allocatedByteLength = nextAllocation;
|
|
629
|
+
this.trackReferencedMemory(nextAllocation, "Texture");
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
this.view._reinitialize(this);
|
|
633
|
+
}
|
|
491
634
|
};
|
|
492
635
|
}
|
|
493
636
|
});
|
|
@@ -801,14 +944,12 @@ var init_webgpu_parameters = __esm({
|
|
|
801
944
|
function getBindGroup(device, bindGroupLayout, shaderLayout, bindings) {
|
|
802
945
|
const entries = getBindGroupEntries(bindings, shaderLayout);
|
|
803
946
|
device.pushErrorScope("validation");
|
|
804
|
-
const bindGroup = device.createBindGroup({
|
|
947
|
+
const bindGroup = device.handle.createBindGroup({
|
|
805
948
|
layout: bindGroupLayout,
|
|
806
949
|
entries
|
|
807
950
|
});
|
|
808
|
-
device.popErrorScope(
|
|
809
|
-
|
|
810
|
-
import_core8.log.error(`bindGroup creation: ${error.message}`, bindGroup)();
|
|
811
|
-
}
|
|
951
|
+
device.popErrorScope((error) => {
|
|
952
|
+
import_core8.log.error(`bindGroup creation: ${error.message}`, bindGroup)();
|
|
812
953
|
});
|
|
813
954
|
return bindGroup;
|
|
814
955
|
}
|
|
@@ -822,28 +963,28 @@ function getShaderLayoutBinding(shaderLayout, bindingName, options) {
|
|
|
822
963
|
function getBindGroupEntries(bindings, shaderLayout) {
|
|
823
964
|
const entries = [];
|
|
824
965
|
for (const [bindingName, value] of Object.entries(bindings)) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
966
|
+
const exactBindingLayout = shaderLayout.bindings.find((binding) => binding.name === bindingName);
|
|
967
|
+
const bindingLayout = exactBindingLayout || getShaderLayoutBinding(shaderLayout, bindingName);
|
|
968
|
+
const isShadowedAlias = !exactBindingLayout && bindingLayout ? bindingLayout.name in bindings : false;
|
|
969
|
+
if (!isShadowedAlias) {
|
|
970
|
+
const entry = bindingLayout ? getBindGroupEntry(value, bindingLayout.location, void 0, bindingName) : null;
|
|
828
971
|
if (entry) {
|
|
829
972
|
entries.push(entry);
|
|
830
973
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (entry) {
|
|
839
|
-
entries.push(entry);
|
|
974
|
+
if (value instanceof import_core8.Texture) {
|
|
975
|
+
const samplerBindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, {
|
|
976
|
+
ignoreWarnings: true
|
|
977
|
+
});
|
|
978
|
+
const samplerEntry = samplerBindingLayout ? getBindGroupEntry(value, samplerBindingLayout.location, { sampler: true }, bindingName) : null;
|
|
979
|
+
if (samplerEntry) {
|
|
980
|
+
entries.push(samplerEntry);
|
|
840
981
|
}
|
|
841
982
|
}
|
|
842
983
|
}
|
|
843
984
|
}
|
|
844
985
|
return entries;
|
|
845
986
|
}
|
|
846
|
-
function getBindGroupEntry(binding, index, options) {
|
|
987
|
+
function getBindGroupEntry(binding, index, options, bindingName = "unknown") {
|
|
847
988
|
if (binding instanceof import_core8.Buffer) {
|
|
848
989
|
return {
|
|
849
990
|
binding: index,
|
|
@@ -858,6 +999,12 @@ function getBindGroupEntry(binding, index, options) {
|
|
|
858
999
|
resource: binding.handle
|
|
859
1000
|
};
|
|
860
1001
|
}
|
|
1002
|
+
if (binding instanceof import_core8.TextureView) {
|
|
1003
|
+
return {
|
|
1004
|
+
binding: index,
|
|
1005
|
+
resource: binding.handle
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
861
1008
|
if (binding instanceof import_core8.Texture) {
|
|
862
1009
|
if (options == null ? void 0 : options.sampler) {
|
|
863
1010
|
return {
|
|
@@ -870,7 +1017,7 @@ function getBindGroupEntry(binding, index, options) {
|
|
|
870
1017
|
resource: binding.view.handle
|
|
871
1018
|
};
|
|
872
1019
|
}
|
|
873
|
-
import_core8.log.warn(`invalid binding ${
|
|
1020
|
+
import_core8.log.warn(`invalid binding ${bindingName}`, binding);
|
|
874
1021
|
return null;
|
|
875
1022
|
}
|
|
876
1023
|
var import_core8;
|
|
@@ -952,17 +1099,17 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
|
|
|
952
1099
|
});
|
|
953
1100
|
return vertexBufferLayouts;
|
|
954
1101
|
}
|
|
955
|
-
function findAttributeLayout(shaderLayout,
|
|
956
|
-
const attribute = shaderLayout.attributes.find((attribute_) => attribute_.name ===
|
|
1102
|
+
function findAttributeLayout(shaderLayout, name, attributeNames) {
|
|
1103
|
+
const attribute = shaderLayout.attributes.find((attribute_) => attribute_.name === name);
|
|
957
1104
|
if (!attribute) {
|
|
958
|
-
import_core9.log.warn(`Supplied attribute not present in shader layout: ${
|
|
1105
|
+
import_core9.log.warn(`Supplied attribute not present in shader layout: ${name}`)();
|
|
959
1106
|
return null;
|
|
960
1107
|
}
|
|
961
1108
|
if (attributeNames) {
|
|
962
|
-
if (attributeNames.has(
|
|
963
|
-
throw new Error(`Found multiple entries for attribute: ${
|
|
1109
|
+
if (attributeNames.has(name)) {
|
|
1110
|
+
throw new Error(`Found multiple entries for attribute: ${name}`);
|
|
964
1111
|
}
|
|
965
|
-
attributeNames.add(
|
|
1112
|
+
attributeNames.add(name);
|
|
966
1113
|
}
|
|
967
1114
|
return attribute;
|
|
968
1115
|
}
|
|
@@ -975,7 +1122,7 @@ var init_get_vertex_buffer_layout = __esm({
|
|
|
975
1122
|
});
|
|
976
1123
|
|
|
977
1124
|
// dist/adapter/resources/webgpu-render-pipeline.js
|
|
978
|
-
var import_core10, WebGPURenderPipeline;
|
|
1125
|
+
var import_core10, EMPTY_BINDINGS, WebGPURenderPipeline;
|
|
979
1126
|
var init_webgpu_render_pipeline = __esm({
|
|
980
1127
|
"dist/adapter/resources/webgpu-render-pipeline.js"() {
|
|
981
1128
|
"use strict";
|
|
@@ -984,13 +1131,15 @@ var init_webgpu_render_pipeline = __esm({
|
|
|
984
1131
|
init_convert_texture_format();
|
|
985
1132
|
init_get_bind_group();
|
|
986
1133
|
init_get_vertex_buffer_layout();
|
|
1134
|
+
EMPTY_BINDINGS = {};
|
|
987
1135
|
WebGPURenderPipeline = class extends import_core10.RenderPipeline {
|
|
988
1136
|
device;
|
|
989
1137
|
handle;
|
|
990
1138
|
vs;
|
|
991
1139
|
fs = null;
|
|
992
|
-
/**
|
|
1140
|
+
/** Compatibility path for direct pipeline.setBindings() usage */
|
|
993
1141
|
_bindings;
|
|
1142
|
+
/** For internal use to create BindGroups */
|
|
994
1143
|
_bindGroupLayout = null;
|
|
995
1144
|
_bindGroup = null;
|
|
996
1145
|
get [Symbol.toStringTag]() {
|
|
@@ -1016,26 +1165,36 @@ var init_webgpu_render_pipeline = __esm({
|
|
|
1016
1165
|
this.handle.label = this.props.id;
|
|
1017
1166
|
this.vs = props.vs;
|
|
1018
1167
|
this.fs = props.fs;
|
|
1019
|
-
this._bindings =
|
|
1168
|
+
this._bindings = props.bindings || EMPTY_BINDINGS;
|
|
1020
1169
|
}
|
|
1021
1170
|
destroy() {
|
|
1022
1171
|
this.handle = null;
|
|
1023
1172
|
}
|
|
1024
1173
|
/**
|
|
1025
|
-
*
|
|
1026
|
-
*
|
|
1174
|
+
* Compatibility shim for code paths that still set bindings on the pipeline.
|
|
1175
|
+
* The shared-model path passes bindings per draw and does not rely on this state.
|
|
1027
1176
|
*/
|
|
1028
1177
|
setBindings(bindings) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1178
|
+
let bindingsChanged = false;
|
|
1179
|
+
for (const [name, binding] of Object.entries(bindings)) {
|
|
1180
|
+
if (this._bindings[name] !== binding) {
|
|
1181
|
+
if (!bindingsChanged) {
|
|
1182
|
+
if (this._bindings === this.props.bindings || this._bindings === EMPTY_BINDINGS) {
|
|
1183
|
+
this._bindings = { ...this._bindings };
|
|
1184
|
+
}
|
|
1185
|
+
bindingsChanged = true;
|
|
1186
|
+
}
|
|
1187
|
+
this._bindings[name] = binding;
|
|
1032
1188
|
}
|
|
1033
1189
|
}
|
|
1034
|
-
|
|
1190
|
+
if (bindingsChanged) {
|
|
1191
|
+
this._bindGroup = null;
|
|
1192
|
+
}
|
|
1035
1193
|
}
|
|
1036
1194
|
/** @todo - should this be moved to renderpass? */
|
|
1037
1195
|
draw(options) {
|
|
1038
1196
|
const webgpuRenderPass = options.renderPass;
|
|
1197
|
+
const instanceCount = options.instanceCount && options.instanceCount > 0 ? options.instanceCount : 1;
|
|
1039
1198
|
this.device.pushErrorScope("validation");
|
|
1040
1199
|
webgpuRenderPass.handle.setPipeline(this.handle);
|
|
1041
1200
|
this.device.popErrorScope((error) => {
|
|
@@ -1043,31 +1202,29 @@ var init_webgpu_render_pipeline = __esm({
|
|
|
1043
1202
|
"${error.message}"`), this)();
|
|
1044
1203
|
this.device.debug();
|
|
1045
1204
|
});
|
|
1046
|
-
const bindGroup = this._getBindGroup();
|
|
1205
|
+
const bindGroup = this._getBindGroup(options.bindings);
|
|
1047
1206
|
if (bindGroup) {
|
|
1048
1207
|
webgpuRenderPass.handle.setBindGroup(0, bindGroup);
|
|
1049
1208
|
}
|
|
1050
1209
|
options.vertexArray.bindBeforeRender(options.renderPass);
|
|
1051
1210
|
if (options.indexCount) {
|
|
1052
|
-
webgpuRenderPass.handle.drawIndexed(options.indexCount,
|
|
1211
|
+
webgpuRenderPass.handle.drawIndexed(options.indexCount, instanceCount, options.firstIndex || 0, options.baseVertex || 0, options.firstInstance || 0);
|
|
1053
1212
|
} else {
|
|
1054
|
-
webgpuRenderPass.handle.draw(
|
|
1055
|
-
options.vertexCount || 0,
|
|
1056
|
-
options.instanceCount || 1,
|
|
1057
|
-
// If 0, nothing will be drawn
|
|
1058
|
-
options.firstInstance
|
|
1059
|
-
);
|
|
1213
|
+
webgpuRenderPass.handle.draw(options.vertexCount || 0, instanceCount, options.firstVertex || 0, options.firstInstance || 0);
|
|
1060
1214
|
}
|
|
1061
1215
|
options.vertexArray.unbindAfterRender(options.renderPass);
|
|
1062
1216
|
return true;
|
|
1063
1217
|
}
|
|
1064
1218
|
/** Return a bind group created by setBindings */
|
|
1065
|
-
_getBindGroup() {
|
|
1219
|
+
_getBindGroup(bindings) {
|
|
1066
1220
|
if (this.shaderLayout.bindings.length === 0) {
|
|
1067
1221
|
return null;
|
|
1068
1222
|
}
|
|
1069
1223
|
this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
|
|
1070
|
-
|
|
1224
|
+
if (bindings) {
|
|
1225
|
+
return getBindGroup(this.device, this._bindGroupLayout, this.shaderLayout, bindings);
|
|
1226
|
+
}
|
|
1227
|
+
this._bindGroup = this._bindGroup || getBindGroup(this.device, this._bindGroupLayout, this.shaderLayout, this._bindings);
|
|
1071
1228
|
return this._bindGroup;
|
|
1072
1229
|
}
|
|
1073
1230
|
/**
|
|
@@ -1134,17 +1291,33 @@ var init_webgpu_framebuffer = __esm({
|
|
|
1134
1291
|
}
|
|
1135
1292
|
updateAttachments() {
|
|
1136
1293
|
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
|
|
1296
|
+
* Rebinds the long-lived default framebuffer wrapper to the current per-frame color view
|
|
1297
|
+
* and optional depth attachment without allocating a new luma.gl Framebuffer object.
|
|
1298
|
+
*/
|
|
1299
|
+
_reinitialize(colorAttachment, depthStencilAttachment) {
|
|
1300
|
+
this.colorAttachments[0] = colorAttachment;
|
|
1301
|
+
this.depthStencilAttachment = depthStencilAttachment;
|
|
1302
|
+
this.width = colorAttachment.texture.width;
|
|
1303
|
+
this.height = colorAttachment.texture.height;
|
|
1304
|
+
this.props.width = this.width;
|
|
1305
|
+
this.props.height = this.height;
|
|
1306
|
+
this.props.colorAttachments = [colorAttachment.texture];
|
|
1307
|
+
this.props.depthStencilAttachment = (depthStencilAttachment == null ? void 0 : depthStencilAttachment.texture) || null;
|
|
1308
|
+
}
|
|
1137
1309
|
};
|
|
1138
1310
|
}
|
|
1139
1311
|
});
|
|
1140
1312
|
|
|
1141
1313
|
// dist/adapter/resources/webgpu-compute-pipeline.js
|
|
1142
|
-
var import_core12, WebGPUComputePipeline;
|
|
1314
|
+
var import_core12, EMPTY_BINDINGS2, WebGPUComputePipeline;
|
|
1143
1315
|
var init_webgpu_compute_pipeline = __esm({
|
|
1144
1316
|
"dist/adapter/resources/webgpu-compute-pipeline.js"() {
|
|
1145
1317
|
"use strict";
|
|
1146
1318
|
import_core12 = require("@luma.gl/core");
|
|
1147
1319
|
init_get_bind_group();
|
|
1320
|
+
EMPTY_BINDINGS2 = {};
|
|
1148
1321
|
WebGPUComputePipeline = class extends import_core12.ComputePipeline {
|
|
1149
1322
|
device;
|
|
1150
1323
|
handle;
|
|
@@ -1152,7 +1325,7 @@ var init_webgpu_compute_pipeline = __esm({
|
|
|
1152
1325
|
_bindGroupLayout = null;
|
|
1153
1326
|
_bindGroup = null;
|
|
1154
1327
|
/** For internal use to create BindGroups */
|
|
1155
|
-
_bindings
|
|
1328
|
+
_bindings;
|
|
1156
1329
|
constructor(device, props) {
|
|
1157
1330
|
super(device, props);
|
|
1158
1331
|
this.device = device;
|
|
@@ -1166,18 +1339,33 @@ var init_webgpu_compute_pipeline = __esm({
|
|
|
1166
1339
|
},
|
|
1167
1340
|
layout: "auto"
|
|
1168
1341
|
});
|
|
1342
|
+
this._bindings = EMPTY_BINDINGS2;
|
|
1169
1343
|
}
|
|
1170
1344
|
/**
|
|
1171
1345
|
* @todo Use renderpass.setBindings() ?
|
|
1172
1346
|
* @todo Do we want to expose BindGroups in the API and remove this?
|
|
1173
1347
|
*/
|
|
1174
1348
|
setBindings(bindings) {
|
|
1175
|
-
|
|
1349
|
+
let bindingsChanged = false;
|
|
1350
|
+
for (const [name, binding] of Object.entries(bindings)) {
|
|
1351
|
+
if (this._bindings[name] !== binding) {
|
|
1352
|
+
if (!bindingsChanged) {
|
|
1353
|
+
if (this._bindings === EMPTY_BINDINGS2) {
|
|
1354
|
+
this._bindings = {};
|
|
1355
|
+
}
|
|
1356
|
+
bindingsChanged = true;
|
|
1357
|
+
}
|
|
1358
|
+
this._bindings[name] = binding;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
if (bindingsChanged) {
|
|
1362
|
+
this._bindGroup = null;
|
|
1363
|
+
}
|
|
1176
1364
|
}
|
|
1177
1365
|
/** Return a bind group created by setBindings */
|
|
1178
1366
|
_getBindGroup() {
|
|
1179
1367
|
this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
|
|
1180
|
-
this._bindGroup = this._bindGroup || getBindGroup(this.device
|
|
1368
|
+
this._bindGroup = this._bindGroup || getBindGroup(this.device, this._bindGroupLayout, this.shaderLayout, this._bindings);
|
|
1181
1369
|
return this._bindGroup;
|
|
1182
1370
|
}
|
|
1183
1371
|
};
|
|
@@ -1193,7 +1381,7 @@ var init_webgpu_vertex_array = __esm({
|
|
|
1193
1381
|
import_env = require("@probe.gl/env");
|
|
1194
1382
|
WebGPUVertexArray = class extends import_core13.VertexArray {
|
|
1195
1383
|
get [Symbol.toStringTag]() {
|
|
1196
|
-
return "
|
|
1384
|
+
return "VertexArray";
|
|
1197
1385
|
}
|
|
1198
1386
|
device;
|
|
1199
1387
|
/** Vertex Array is just a helper class under WebGPU */
|
|
@@ -1256,10 +1444,13 @@ var init_webgpu_canvas_context = __esm({
|
|
|
1256
1444
|
"use strict";
|
|
1257
1445
|
import_core14 = require("@luma.gl/core");
|
|
1258
1446
|
init_webgpu_framebuffer();
|
|
1447
|
+
init_cpu_hotspot_profiler();
|
|
1259
1448
|
WebGPUCanvasContext = class extends import_core14.CanvasContext {
|
|
1260
1449
|
device;
|
|
1261
1450
|
handle;
|
|
1451
|
+
colorAttachment = null;
|
|
1262
1452
|
depthStencilAttachment = null;
|
|
1453
|
+
framebuffer = null;
|
|
1263
1454
|
get [Symbol.toStringTag]() {
|
|
1264
1455
|
return "WebGPUCanvasContext";
|
|
1265
1456
|
}
|
|
@@ -1273,9 +1464,22 @@ var init_webgpu_canvas_context = __esm({
|
|
|
1273
1464
|
this.handle = context;
|
|
1274
1465
|
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
|
|
1275
1466
|
this._configureDevice();
|
|
1467
|
+
this._startObservers();
|
|
1276
1468
|
}
|
|
1277
1469
|
/** Destroy any textures produced while configured and remove the context configuration. */
|
|
1278
1470
|
destroy() {
|
|
1471
|
+
if (this.framebuffer) {
|
|
1472
|
+
this.framebuffer.destroy();
|
|
1473
|
+
this.framebuffer = null;
|
|
1474
|
+
}
|
|
1475
|
+
if (this.colorAttachment) {
|
|
1476
|
+
this.colorAttachment.destroy();
|
|
1477
|
+
this.colorAttachment = null;
|
|
1478
|
+
}
|
|
1479
|
+
if (this.depthStencilAttachment) {
|
|
1480
|
+
this.depthStencilAttachment.destroy();
|
|
1481
|
+
this.depthStencilAttachment = null;
|
|
1482
|
+
}
|
|
1279
1483
|
this.handle.unconfigure();
|
|
1280
1484
|
super.destroy();
|
|
1281
1485
|
}
|
|
@@ -1294,41 +1498,78 @@ var init_webgpu_canvas_context = __esm({
|
|
|
1294
1498
|
colorSpace: this.props.colorSpace,
|
|
1295
1499
|
alphaMode: this.props.alphaMode
|
|
1296
1500
|
});
|
|
1501
|
+
this._createDepthStencilAttachment(this.device.preferredDepthFormat);
|
|
1297
1502
|
}
|
|
1298
1503
|
/** Update framebuffer with properly resized "swap chain" texture views */
|
|
1299
1504
|
_getCurrentFramebuffer(options = {
|
|
1300
1505
|
depthStencilFormat: "depth24plus"
|
|
1301
1506
|
}) {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1507
|
+
var _a;
|
|
1508
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
1509
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
1510
|
+
if (profiler) {
|
|
1511
|
+
profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
|
|
1512
|
+
profiler.activeDefaultFramebufferAcquireDepth = (profiler.activeDefaultFramebufferAcquireDepth || 0) + 1;
|
|
1513
|
+
}
|
|
1514
|
+
try {
|
|
1515
|
+
const currentColorAttachment = this._getCurrentTexture();
|
|
1516
|
+
if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
|
|
1517
|
+
const [oldWidth, oldHeight] = this.getDrawingBufferSize();
|
|
1518
|
+
this.drawingBufferWidth = currentColorAttachment.width;
|
|
1519
|
+
this.drawingBufferHeight = currentColorAttachment.height;
|
|
1520
|
+
import_core14.log.log(1, `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
|
|
1521
|
+
}
|
|
1522
|
+
if (options == null ? void 0 : options.depthStencilFormat) {
|
|
1523
|
+
this._createDepthStencilAttachment(options == null ? void 0 : options.depthStencilFormat);
|
|
1524
|
+
}
|
|
1525
|
+
this.framebuffer ||= new WebGPUFramebuffer(this.device, {
|
|
1526
|
+
id: `${this.id}#framebuffer`,
|
|
1527
|
+
colorAttachments: [currentColorAttachment],
|
|
1528
|
+
depthStencilAttachment: null
|
|
1529
|
+
});
|
|
1530
|
+
this.framebuffer._reinitialize(currentColorAttachment.view, (options == null ? void 0 : options.depthStencilFormat) ? ((_a = this.depthStencilAttachment) == null ? void 0 : _a.view) || null : null);
|
|
1531
|
+
return this.framebuffer;
|
|
1532
|
+
} finally {
|
|
1533
|
+
if (profiler) {
|
|
1534
|
+
profiler.activeDefaultFramebufferAcquireDepth = (profiler.activeDefaultFramebufferAcquireDepth || 1) - 1;
|
|
1535
|
+
profiler.framebufferAcquireTimeMs = (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1316
1538
|
}
|
|
1317
1539
|
// PRIMARY METHODS
|
|
1318
1540
|
/** Wrap the current canvas context texture in a luma.gl texture */
|
|
1319
1541
|
_getCurrentTexture() {
|
|
1542
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
1543
|
+
const currentTextureStartTime = profiler ? getTimestamp() : 0;
|
|
1320
1544
|
const handle = this.handle.getCurrentTexture();
|
|
1321
|
-
|
|
1322
|
-
|
|
1545
|
+
if (profiler) {
|
|
1546
|
+
profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
|
|
1547
|
+
profiler.currentTextureAcquireTimeMs = (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
|
|
1548
|
+
}
|
|
1549
|
+
if (!this.colorAttachment) {
|
|
1550
|
+
this.colorAttachment = this.device.createTexture({
|
|
1551
|
+
id: `${this.id}#color-texture`,
|
|
1552
|
+
handle,
|
|
1553
|
+
format: this.device.preferredColorFormat,
|
|
1554
|
+
width: handle.width,
|
|
1555
|
+
height: handle.height
|
|
1556
|
+
});
|
|
1557
|
+
return this.colorAttachment;
|
|
1558
|
+
}
|
|
1559
|
+
this.colorAttachment._reinitialize(handle, {
|
|
1323
1560
|
handle,
|
|
1324
1561
|
format: this.device.preferredColorFormat,
|
|
1325
1562
|
width: handle.width,
|
|
1326
1563
|
height: handle.height
|
|
1327
1564
|
});
|
|
1565
|
+
return this.colorAttachment;
|
|
1328
1566
|
}
|
|
1329
1567
|
/** We build render targets on demand (i.e. not when size changes but when about to render) */
|
|
1330
1568
|
_createDepthStencilAttachment(depthStencilFormat) {
|
|
1331
|
-
|
|
1569
|
+
var _a;
|
|
1570
|
+
const needsNewDepthStencilAttachment = !this.depthStencilAttachment || this.depthStencilAttachment.width !== this.drawingBufferWidth || this.depthStencilAttachment.height !== this.drawingBufferHeight || this.depthStencilAttachment.format !== depthStencilFormat;
|
|
1571
|
+
if (needsNewDepthStencilAttachment) {
|
|
1572
|
+
(_a = this.depthStencilAttachment) == null ? void 0 : _a.destroy();
|
|
1332
1573
|
this.depthStencilAttachment = this.device.createTexture({
|
|
1333
1574
|
id: `${this.id}#depth-stencil-texture`,
|
|
1334
1575
|
usage: import_core14.Texture.RENDER_ATTACHMENT,
|
|
@@ -1343,17 +1584,157 @@ var init_webgpu_canvas_context = __esm({
|
|
|
1343
1584
|
}
|
|
1344
1585
|
});
|
|
1345
1586
|
|
|
1587
|
+
// dist/adapter/webgpu-presentation-context.js
|
|
1588
|
+
var import_core15, WebGPUPresentationContext;
|
|
1589
|
+
var init_webgpu_presentation_context = __esm({
|
|
1590
|
+
"dist/adapter/webgpu-presentation-context.js"() {
|
|
1591
|
+
"use strict";
|
|
1592
|
+
import_core15 = require("@luma.gl/core");
|
|
1593
|
+
init_webgpu_framebuffer();
|
|
1594
|
+
init_cpu_hotspot_profiler();
|
|
1595
|
+
WebGPUPresentationContext = class extends import_core15.PresentationContext {
|
|
1596
|
+
device;
|
|
1597
|
+
handle;
|
|
1598
|
+
colorAttachment = null;
|
|
1599
|
+
depthStencilAttachment = null;
|
|
1600
|
+
framebuffer = null;
|
|
1601
|
+
get [Symbol.toStringTag]() {
|
|
1602
|
+
return "WebGPUPresentationContext";
|
|
1603
|
+
}
|
|
1604
|
+
constructor(device, props = {}) {
|
|
1605
|
+
super(props);
|
|
1606
|
+
const contextLabel = `${this[Symbol.toStringTag]}(${this.id})`;
|
|
1607
|
+
const context = this.canvas.getContext("webgpu");
|
|
1608
|
+
if (!context) {
|
|
1609
|
+
throw new Error(`${contextLabel}: Failed to create WebGPU presentation context`);
|
|
1610
|
+
}
|
|
1611
|
+
this.device = device;
|
|
1612
|
+
this.handle = context;
|
|
1613
|
+
this._setAutoCreatedCanvasId(`${this.device.id}-presentation-canvas`);
|
|
1614
|
+
this._configureDevice();
|
|
1615
|
+
this._startObservers();
|
|
1616
|
+
}
|
|
1617
|
+
destroy() {
|
|
1618
|
+
if (this.framebuffer) {
|
|
1619
|
+
this.framebuffer.destroy();
|
|
1620
|
+
this.framebuffer = null;
|
|
1621
|
+
}
|
|
1622
|
+
if (this.colorAttachment) {
|
|
1623
|
+
this.colorAttachment.destroy();
|
|
1624
|
+
this.colorAttachment = null;
|
|
1625
|
+
}
|
|
1626
|
+
if (this.depthStencilAttachment) {
|
|
1627
|
+
this.depthStencilAttachment.destroy();
|
|
1628
|
+
this.depthStencilAttachment = null;
|
|
1629
|
+
}
|
|
1630
|
+
this.handle.unconfigure();
|
|
1631
|
+
super.destroy();
|
|
1632
|
+
}
|
|
1633
|
+
present() {
|
|
1634
|
+
this.device.submit();
|
|
1635
|
+
}
|
|
1636
|
+
_configureDevice() {
|
|
1637
|
+
if (this.depthStencilAttachment) {
|
|
1638
|
+
this.depthStencilAttachment.destroy();
|
|
1639
|
+
this.depthStencilAttachment = null;
|
|
1640
|
+
}
|
|
1641
|
+
this.handle.configure({
|
|
1642
|
+
device: this.device.handle,
|
|
1643
|
+
format: this.device.preferredColorFormat,
|
|
1644
|
+
colorSpace: this.props.colorSpace,
|
|
1645
|
+
alphaMode: this.props.alphaMode
|
|
1646
|
+
});
|
|
1647
|
+
this._createDepthStencilAttachment(this.device.preferredDepthFormat);
|
|
1648
|
+
}
|
|
1649
|
+
_getCurrentFramebuffer(options = {
|
|
1650
|
+
depthStencilFormat: "depth24plus"
|
|
1651
|
+
}) {
|
|
1652
|
+
var _a;
|
|
1653
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
1654
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
1655
|
+
if (profiler) {
|
|
1656
|
+
profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
|
|
1657
|
+
}
|
|
1658
|
+
try {
|
|
1659
|
+
const currentColorAttachment = this._getCurrentTexture();
|
|
1660
|
+
if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
|
|
1661
|
+
const [oldWidth, oldHeight] = this.getDrawingBufferSize();
|
|
1662
|
+
this.drawingBufferWidth = currentColorAttachment.width;
|
|
1663
|
+
this.drawingBufferHeight = currentColorAttachment.height;
|
|
1664
|
+
import_core15.log.log(1, `${this[Symbol.toStringTag]}(${this.id}): Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
|
|
1665
|
+
}
|
|
1666
|
+
if (options == null ? void 0 : options.depthStencilFormat) {
|
|
1667
|
+
this._createDepthStencilAttachment(options.depthStencilFormat);
|
|
1668
|
+
}
|
|
1669
|
+
this.framebuffer ||= new WebGPUFramebuffer(this.device, {
|
|
1670
|
+
id: `${this.id}#framebuffer`,
|
|
1671
|
+
colorAttachments: [currentColorAttachment],
|
|
1672
|
+
depthStencilAttachment: null
|
|
1673
|
+
});
|
|
1674
|
+
this.framebuffer._reinitialize(currentColorAttachment.view, (options == null ? void 0 : options.depthStencilFormat) ? ((_a = this.depthStencilAttachment) == null ? void 0 : _a.view) || null : null);
|
|
1675
|
+
return this.framebuffer;
|
|
1676
|
+
} finally {
|
|
1677
|
+
if (profiler) {
|
|
1678
|
+
profiler.framebufferAcquireTimeMs = (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
_getCurrentTexture() {
|
|
1683
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
1684
|
+
const currentTextureStartTime = profiler ? getTimestamp() : 0;
|
|
1685
|
+
const handle = this.handle.getCurrentTexture();
|
|
1686
|
+
if (profiler) {
|
|
1687
|
+
profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
|
|
1688
|
+
profiler.currentTextureAcquireTimeMs = (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
|
|
1689
|
+
}
|
|
1690
|
+
if (!this.colorAttachment) {
|
|
1691
|
+
this.colorAttachment = this.device.createTexture({
|
|
1692
|
+
id: `${this.id}#color-texture`,
|
|
1693
|
+
handle,
|
|
1694
|
+
format: this.device.preferredColorFormat,
|
|
1695
|
+
width: handle.width,
|
|
1696
|
+
height: handle.height
|
|
1697
|
+
});
|
|
1698
|
+
return this.colorAttachment;
|
|
1699
|
+
}
|
|
1700
|
+
this.colorAttachment._reinitialize(handle, {
|
|
1701
|
+
handle,
|
|
1702
|
+
format: this.device.preferredColorFormat,
|
|
1703
|
+
width: handle.width,
|
|
1704
|
+
height: handle.height
|
|
1705
|
+
});
|
|
1706
|
+
return this.colorAttachment;
|
|
1707
|
+
}
|
|
1708
|
+
_createDepthStencilAttachment(depthStencilFormat) {
|
|
1709
|
+
var _a;
|
|
1710
|
+
const needsNewDepthStencilAttachment = !this.depthStencilAttachment || this.depthStencilAttachment.width !== this.drawingBufferWidth || this.depthStencilAttachment.height !== this.drawingBufferHeight || this.depthStencilAttachment.format !== depthStencilFormat;
|
|
1711
|
+
if (needsNewDepthStencilAttachment) {
|
|
1712
|
+
(_a = this.depthStencilAttachment) == null ? void 0 : _a.destroy();
|
|
1713
|
+
this.depthStencilAttachment = this.device.createTexture({
|
|
1714
|
+
id: `${this.id}#depth-stencil-texture`,
|
|
1715
|
+
usage: import_core15.Texture.RENDER_ATTACHMENT,
|
|
1716
|
+
format: depthStencilFormat,
|
|
1717
|
+
width: this.drawingBufferWidth,
|
|
1718
|
+
height: this.drawingBufferHeight
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
return this.depthStencilAttachment;
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
|
|
1346
1727
|
// dist/adapter/resources/webgpu-command-buffer.js
|
|
1347
|
-
var
|
|
1728
|
+
var import_core16, WebGPUCommandBuffer;
|
|
1348
1729
|
var init_webgpu_command_buffer = __esm({
|
|
1349
1730
|
"dist/adapter/resources/webgpu-command-buffer.js"() {
|
|
1350
1731
|
"use strict";
|
|
1351
|
-
|
|
1352
|
-
WebGPUCommandBuffer = class extends
|
|
1732
|
+
import_core16 = require("@luma.gl/core");
|
|
1733
|
+
WebGPUCommandBuffer = class extends import_core16.CommandBuffer {
|
|
1353
1734
|
device;
|
|
1354
1735
|
handle;
|
|
1355
1736
|
constructor(commandEncoder, props) {
|
|
1356
|
-
super(commandEncoder.device,
|
|
1737
|
+
super(commandEncoder.device, props);
|
|
1357
1738
|
this.device = commandEncoder.device;
|
|
1358
1739
|
this.handle = this.props.handle || commandEncoder.handle.finish({
|
|
1359
1740
|
label: (props == null ? void 0 : props.id) || "unnamed-command-buffer"
|
|
@@ -1367,52 +1748,85 @@ var init_webgpu_command_buffer = __esm({
|
|
|
1367
1748
|
function convertColor(color) {
|
|
1368
1749
|
return { r: color[0], g: color[1], b: color[2], a: color[3] };
|
|
1369
1750
|
}
|
|
1370
|
-
var
|
|
1751
|
+
var import_core17, WebGPURenderPass;
|
|
1371
1752
|
var init_webgpu_render_pass = __esm({
|
|
1372
1753
|
"dist/adapter/resources/webgpu-render-pass.js"() {
|
|
1373
1754
|
"use strict";
|
|
1374
|
-
|
|
1375
|
-
|
|
1755
|
+
import_core17 = require("@luma.gl/core");
|
|
1756
|
+
init_cpu_hotspot_profiler();
|
|
1757
|
+
WebGPURenderPass = class extends import_core17.RenderPass {
|
|
1376
1758
|
device;
|
|
1377
1759
|
handle;
|
|
1760
|
+
framebuffer;
|
|
1378
1761
|
/** Active pipeline */
|
|
1379
1762
|
pipeline = null;
|
|
1763
|
+
/** Latest bindings applied to this pass */
|
|
1764
|
+
bindings = {};
|
|
1380
1765
|
constructor(device, props = {}) {
|
|
1381
1766
|
super(device, props);
|
|
1382
1767
|
this.device = device;
|
|
1383
|
-
const
|
|
1384
|
-
|
|
1385
|
-
const
|
|
1386
|
-
if (
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
if (device.features.has("timestamp-query")) {
|
|
1390
|
-
const webgpuTSQuerySet = props.timestampQuerySet;
|
|
1391
|
-
renderPassDescriptor.timestampWrites = webgpuTSQuerySet ? {
|
|
1392
|
-
querySet: webgpuTSQuerySet.handle,
|
|
1393
|
-
beginningOfPassWriteIndex: props.beginTimestampIndex,
|
|
1394
|
-
endOfPassWriteIndex: props.endTimestampIndex
|
|
1395
|
-
} : void 0;
|
|
1396
|
-
}
|
|
1397
|
-
if (!device.commandEncoder) {
|
|
1398
|
-
throw new Error("commandEncoder not available");
|
|
1768
|
+
const { props: renderPassProps } = this;
|
|
1769
|
+
this.framebuffer = renderPassProps.framebuffer || device.getCanvasContext().getCurrentFramebuffer();
|
|
1770
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
1771
|
+
if (profiler) {
|
|
1772
|
+
const counterName = renderPassProps.framebuffer ? "explicitFramebufferRenderPassCount" : "defaultFramebufferRenderPassCount";
|
|
1773
|
+
profiler[counterName] = (profiler[counterName] || 0) + 1;
|
|
1399
1774
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
this.
|
|
1775
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
1776
|
+
try {
|
|
1777
|
+
const descriptorAssemblyStartTime = profiler ? getTimestamp() : 0;
|
|
1778
|
+
const renderPassDescriptor = this.getRenderPassDescriptor(this.framebuffer);
|
|
1779
|
+
if (renderPassProps.occlusionQuerySet) {
|
|
1780
|
+
renderPassDescriptor.occlusionQuerySet = renderPassProps.occlusionQuerySet.handle;
|
|
1781
|
+
}
|
|
1782
|
+
if (renderPassProps.timestampQuerySet) {
|
|
1783
|
+
const webgpuTSQuerySet = renderPassProps.timestampQuerySet;
|
|
1784
|
+
webgpuTSQuerySet == null ? void 0 : webgpuTSQuerySet._invalidateResults();
|
|
1785
|
+
renderPassDescriptor.timestampWrites = webgpuTSQuerySet ? {
|
|
1786
|
+
querySet: webgpuTSQuerySet.handle,
|
|
1787
|
+
beginningOfPassWriteIndex: renderPassProps.beginTimestampIndex,
|
|
1788
|
+
endOfPassWriteIndex: renderPassProps.endTimestampIndex
|
|
1789
|
+
} : void 0;
|
|
1790
|
+
}
|
|
1791
|
+
if (profiler) {
|
|
1792
|
+
profiler.renderPassDescriptorAssemblyCount = (profiler.renderPassDescriptorAssemblyCount || 0) + 1;
|
|
1793
|
+
profiler.renderPassDescriptorAssemblyTimeMs = (profiler.renderPassDescriptorAssemblyTimeMs || 0) + (getTimestamp() - descriptorAssemblyStartTime);
|
|
1794
|
+
}
|
|
1795
|
+
if (!device.commandEncoder) {
|
|
1796
|
+
throw new Error("commandEncoder not available");
|
|
1797
|
+
}
|
|
1798
|
+
this.device.pushErrorScope("validation");
|
|
1799
|
+
const beginRenderPassStartTime = profiler ? getTimestamp() : 0;
|
|
1800
|
+
this.handle = this.props.handle || device.commandEncoder.handle.beginRenderPass(renderPassDescriptor);
|
|
1801
|
+
if (profiler) {
|
|
1802
|
+
profiler.renderPassBeginCount = (profiler.renderPassBeginCount || 0) + 1;
|
|
1803
|
+
profiler.renderPassBeginTimeMs = (profiler.renderPassBeginTimeMs || 0) + (getTimestamp() - beginRenderPassStartTime);
|
|
1804
|
+
}
|
|
1805
|
+
this.device.popErrorScope((error) => {
|
|
1806
|
+
this.device.reportError(new Error(`${this} creation failed:
|
|
1404
1807
|
"${error.message}"`), this)();
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1808
|
+
this.device.debug();
|
|
1809
|
+
});
|
|
1810
|
+
this.handle.label = this.props.id;
|
|
1811
|
+
import_core17.log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)();
|
|
1812
|
+
import_core17.log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))();
|
|
1813
|
+
import_core17.log.groupEnd(3)();
|
|
1814
|
+
} finally {
|
|
1815
|
+
if (profiler) {
|
|
1816
|
+
profiler.renderPassSetupCount = (profiler.renderPassSetupCount || 0) + 1;
|
|
1817
|
+
profiler.renderPassSetupTimeMs = (profiler.renderPassSetupTimeMs || 0) + (getTimestamp() - startTime);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1411
1820
|
}
|
|
1412
1821
|
destroy() {
|
|
1822
|
+
this.destroyResource();
|
|
1413
1823
|
}
|
|
1414
1824
|
end() {
|
|
1825
|
+
if (this.destroyed) {
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1415
1828
|
this.handle.end();
|
|
1829
|
+
this.destroy();
|
|
1416
1830
|
}
|
|
1417
1831
|
setPipeline(pipeline) {
|
|
1418
1832
|
this.pipeline = pipeline;
|
|
@@ -1426,9 +1840,9 @@ var init_webgpu_render_pass = __esm({
|
|
|
1426
1840
|
}
|
|
1427
1841
|
/** Sets an array of bindings (uniform buffers, samplers, textures, ...) */
|
|
1428
1842
|
setBindings(bindings) {
|
|
1429
|
-
var _a
|
|
1430
|
-
|
|
1431
|
-
const bindGroup = (
|
|
1843
|
+
var _a;
|
|
1844
|
+
this.bindings = bindings;
|
|
1845
|
+
const bindGroup = (_a = this.pipeline) == null ? void 0 : _a._getBindGroup(bindings);
|
|
1432
1846
|
if (bindGroup) {
|
|
1433
1847
|
this.handle.setBindGroup(0, bindGroup);
|
|
1434
1848
|
}
|
|
@@ -1493,7 +1907,7 @@ var init_webgpu_render_pass = __esm({
|
|
|
1493
1907
|
return {
|
|
1494
1908
|
// clear values
|
|
1495
1909
|
loadOp: this.props.clearColor !== false ? "clear" : "load",
|
|
1496
|
-
clearValue: convertColor(((_a = this.props.clearColors) == null ? void 0 : _a[index]) || this.props.clearColor ||
|
|
1910
|
+
clearValue: convertColor(((_a = this.props.clearColors) == null ? void 0 : _a[index]) || this.props.clearColor || import_core17.RenderPass.defaultClearColor),
|
|
1497
1911
|
storeOp: this.props.discard ? "discard" : "store",
|
|
1498
1912
|
// ...colorAttachment,
|
|
1499
1913
|
view: colorAttachment.handle
|
|
@@ -1528,26 +1942,28 @@ var init_webgpu_render_pass = __esm({
|
|
|
1528
1942
|
});
|
|
1529
1943
|
|
|
1530
1944
|
// dist/adapter/resources/webgpu-compute-pass.js
|
|
1531
|
-
var
|
|
1945
|
+
var import_core18, WebGPUComputePass;
|
|
1532
1946
|
var init_webgpu_compute_pass = __esm({
|
|
1533
1947
|
"dist/adapter/resources/webgpu-compute-pass.js"() {
|
|
1534
1948
|
"use strict";
|
|
1535
|
-
|
|
1536
|
-
WebGPUComputePass = class extends
|
|
1949
|
+
import_core18 = require("@luma.gl/core");
|
|
1950
|
+
WebGPUComputePass = class extends import_core18.ComputePass {
|
|
1537
1951
|
device;
|
|
1538
1952
|
handle;
|
|
1539
1953
|
_webgpuPipeline = null;
|
|
1540
|
-
constructor(device, props) {
|
|
1954
|
+
constructor(device, props = {}) {
|
|
1541
1955
|
super(device, props);
|
|
1542
1956
|
this.device = device;
|
|
1957
|
+
const { props: computePassProps } = this;
|
|
1543
1958
|
let timestampWrites;
|
|
1544
|
-
if (
|
|
1545
|
-
const webgpuQuerySet =
|
|
1959
|
+
if (computePassProps.timestampQuerySet) {
|
|
1960
|
+
const webgpuQuerySet = computePassProps.timestampQuerySet;
|
|
1546
1961
|
if (webgpuQuerySet) {
|
|
1962
|
+
webgpuQuerySet._invalidateResults();
|
|
1547
1963
|
timestampWrites = {
|
|
1548
1964
|
querySet: webgpuQuerySet.handle,
|
|
1549
|
-
beginningOfPassWriteIndex:
|
|
1550
|
-
endOfPassWriteIndex:
|
|
1965
|
+
beginningOfPassWriteIndex: computePassProps.beginTimestampIndex,
|
|
1966
|
+
endOfPassWriteIndex: computePassProps.endTimestampIndex
|
|
1551
1967
|
};
|
|
1552
1968
|
}
|
|
1553
1969
|
}
|
|
@@ -1558,9 +1974,14 @@ var init_webgpu_compute_pass = __esm({
|
|
|
1558
1974
|
}
|
|
1559
1975
|
/** @note no WebGPU destroy method, just gc */
|
|
1560
1976
|
destroy() {
|
|
1977
|
+
this.destroyResource();
|
|
1561
1978
|
}
|
|
1562
1979
|
end() {
|
|
1980
|
+
if (this.destroyed) {
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1563
1983
|
this.handle.end();
|
|
1984
|
+
this.destroy();
|
|
1564
1985
|
}
|
|
1565
1986
|
setPipeline(pipeline) {
|
|
1566
1987
|
const wgpuPipeline = pipeline;
|
|
@@ -1610,15 +2031,15 @@ var init_webgpu_compute_pass = __esm({
|
|
|
1610
2031
|
});
|
|
1611
2032
|
|
|
1612
2033
|
// dist/adapter/resources/webgpu-command-encoder.js
|
|
1613
|
-
var
|
|
2034
|
+
var import_core19, WebGPUCommandEncoder;
|
|
1614
2035
|
var init_webgpu_command_encoder = __esm({
|
|
1615
2036
|
"dist/adapter/resources/webgpu-command-encoder.js"() {
|
|
1616
2037
|
"use strict";
|
|
1617
|
-
|
|
2038
|
+
import_core19 = require("@luma.gl/core");
|
|
1618
2039
|
init_webgpu_command_buffer();
|
|
1619
2040
|
init_webgpu_render_pass();
|
|
1620
2041
|
init_webgpu_compute_pass();
|
|
1621
|
-
WebGPUCommandEncoder = class extends
|
|
2042
|
+
WebGPUCommandEncoder = class extends import_core19.CommandEncoder {
|
|
1622
2043
|
device;
|
|
1623
2044
|
handle;
|
|
1624
2045
|
constructor(device, props = {}) {
|
|
@@ -1632,6 +2053,7 @@ var init_webgpu_command_encoder = __esm({
|
|
|
1632
2053
|
this.handle.label = this.props.id;
|
|
1633
2054
|
}
|
|
1634
2055
|
destroy() {
|
|
2056
|
+
this.destroyResource();
|
|
1635
2057
|
}
|
|
1636
2058
|
finish(props) {
|
|
1637
2059
|
this.device.pushErrorScope("validation");
|
|
@@ -1643,17 +2065,18 @@ var init_webgpu_command_encoder = __esm({
|
|
|
1643
2065
|
this.device.reportError(new Error(message), this)();
|
|
1644
2066
|
this.device.debug();
|
|
1645
2067
|
});
|
|
2068
|
+
this.destroy();
|
|
1646
2069
|
return commandBuffer;
|
|
1647
2070
|
}
|
|
1648
2071
|
/**
|
|
1649
2072
|
* Allows a render pass to begin against a canvas context
|
|
1650
2073
|
* @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
|
|
1651
2074
|
*/
|
|
1652
|
-
beginRenderPass(props) {
|
|
1653
|
-
return new WebGPURenderPass(this.device, props);
|
|
2075
|
+
beginRenderPass(props = {}) {
|
|
2076
|
+
return new WebGPURenderPass(this.device, this._applyTimeProfilingToPassProps(props));
|
|
1654
2077
|
}
|
|
1655
|
-
beginComputePass(props) {
|
|
1656
|
-
return new WebGPUComputePass(this.device, props);
|
|
2078
|
+
beginComputePass(props = {}) {
|
|
2079
|
+
return new WebGPUComputePass(this.device, this._applyTimeProfilingToPassProps(props));
|
|
1657
2080
|
}
|
|
1658
2081
|
// beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
|
|
1659
2082
|
// beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
|
|
@@ -1701,19 +2124,40 @@ var init_webgpu_command_encoder = __esm({
|
|
|
1701
2124
|
const webgpuBuffer = destination;
|
|
1702
2125
|
this.handle.resolveQuerySet(webgpuQuerySet.handle, (options == null ? void 0 : options.firstQuery) || 0, (options == null ? void 0 : options.queryCount) || querySet.props.count - ((options == null ? void 0 : options.firstQuery) || 0), webgpuBuffer.handle, (options == null ? void 0 : options.destinationOffset) || 0);
|
|
1703
2126
|
}
|
|
2127
|
+
writeTimestamp(querySet, queryIndex) {
|
|
2128
|
+
querySet._invalidateResults();
|
|
2129
|
+
const writeTimestamp = this.handle.writeTimestamp;
|
|
2130
|
+
if (writeTimestamp) {
|
|
2131
|
+
writeTimestamp.call(this.handle, querySet.handle, queryIndex);
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
const computePass = this.handle.beginComputePass({
|
|
2135
|
+
timestampWrites: {
|
|
2136
|
+
querySet: querySet.handle,
|
|
2137
|
+
beginningOfPassWriteIndex: queryIndex
|
|
2138
|
+
}
|
|
2139
|
+
});
|
|
2140
|
+
computePass.end();
|
|
2141
|
+
}
|
|
1704
2142
|
};
|
|
1705
2143
|
}
|
|
1706
2144
|
});
|
|
1707
2145
|
|
|
1708
2146
|
// dist/adapter/resources/webgpu-query-set.js
|
|
1709
|
-
var
|
|
2147
|
+
var import_core20, WebGPUQuerySet;
|
|
1710
2148
|
var init_webgpu_query_set = __esm({
|
|
1711
2149
|
"dist/adapter/resources/webgpu-query-set.js"() {
|
|
1712
2150
|
"use strict";
|
|
1713
|
-
|
|
1714
|
-
|
|
2151
|
+
import_core20 = require("@luma.gl/core");
|
|
2152
|
+
init_cpu_hotspot_profiler();
|
|
2153
|
+
WebGPUQuerySet = class extends import_core20.QuerySet {
|
|
1715
2154
|
device;
|
|
1716
2155
|
handle;
|
|
2156
|
+
_resolveBuffer = null;
|
|
2157
|
+
_readBuffer = null;
|
|
2158
|
+
_cachedResults = null;
|
|
2159
|
+
_readResultsPromise = null;
|
|
2160
|
+
_resultsPendingResolution = false;
|
|
1717
2161
|
constructor(device, props) {
|
|
1718
2162
|
super(device, props);
|
|
1719
2163
|
this.device = device;
|
|
@@ -1725,20 +2169,145 @@ var init_webgpu_query_set = __esm({
|
|
|
1725
2169
|
}
|
|
1726
2170
|
destroy() {
|
|
1727
2171
|
var _a;
|
|
1728
|
-
(
|
|
1729
|
-
|
|
2172
|
+
if (!this.destroyed) {
|
|
2173
|
+
(_a = this.handle) == null ? void 0 : _a.destroy();
|
|
2174
|
+
this.destroyResource();
|
|
2175
|
+
this.handle = null;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
isResultAvailable(queryIndex) {
|
|
2179
|
+
if (!this._cachedResults) {
|
|
2180
|
+
return false;
|
|
2181
|
+
}
|
|
2182
|
+
return queryIndex === void 0 ? true : queryIndex >= 0 && queryIndex < this._cachedResults.length;
|
|
2183
|
+
}
|
|
2184
|
+
async readResults(options) {
|
|
2185
|
+
const firstQuery = (options == null ? void 0 : options.firstQuery) || 0;
|
|
2186
|
+
const queryCount = (options == null ? void 0 : options.queryCount) || this.props.count - firstQuery;
|
|
2187
|
+
if (firstQuery < 0 || queryCount < 0 || firstQuery + queryCount > this.props.count) {
|
|
2188
|
+
throw new Error("Query read range is out of bounds");
|
|
2189
|
+
}
|
|
2190
|
+
let needsFreshResults = true;
|
|
2191
|
+
while (needsFreshResults) {
|
|
2192
|
+
if (!this._readResultsPromise) {
|
|
2193
|
+
this._readResultsPromise = this._readAllResults();
|
|
2194
|
+
}
|
|
2195
|
+
const readResultsPromise = this._readResultsPromise;
|
|
2196
|
+
const results = await readResultsPromise;
|
|
2197
|
+
needsFreshResults = this._resultsPendingResolution;
|
|
2198
|
+
if (!needsFreshResults) {
|
|
2199
|
+
return results.slice(firstQuery, firstQuery + queryCount);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
throw new Error("Query read unexpectedly failed to resolve");
|
|
2203
|
+
}
|
|
2204
|
+
async readTimestampDuration(beginIndex, endIndex) {
|
|
2205
|
+
if (this.props.type !== "timestamp") {
|
|
2206
|
+
throw new Error("Timestamp durations require a timestamp QuerySet");
|
|
2207
|
+
}
|
|
2208
|
+
if (beginIndex < 0 || endIndex <= beginIndex || endIndex >= this.props.count) {
|
|
2209
|
+
throw new Error("Timestamp duration range is out of bounds");
|
|
2210
|
+
}
|
|
2211
|
+
const results = await this.readResults({
|
|
2212
|
+
firstQuery: beginIndex,
|
|
2213
|
+
queryCount: endIndex - beginIndex + 1
|
|
2214
|
+
});
|
|
2215
|
+
return Number(results[results.length - 1] - results[0]) / 1e6;
|
|
2216
|
+
}
|
|
2217
|
+
/** Marks any cached query results as stale after new writes have been encoded. */
|
|
2218
|
+
_invalidateResults() {
|
|
2219
|
+
this._cachedResults = null;
|
|
2220
|
+
this._resultsPendingResolution = true;
|
|
2221
|
+
}
|
|
2222
|
+
async _readAllResults() {
|
|
2223
|
+
this._ensureBuffers();
|
|
2224
|
+
try {
|
|
2225
|
+
if (this._resultsPendingResolution) {
|
|
2226
|
+
const commandEncoder = this.device.createCommandEncoder({
|
|
2227
|
+
id: `${this.id}-read-results`
|
|
2228
|
+
});
|
|
2229
|
+
commandEncoder.resolveQuerySet(this, this._resolveBuffer);
|
|
2230
|
+
commandEncoder.copyBufferToBuffer({
|
|
2231
|
+
sourceBuffer: this._resolveBuffer,
|
|
2232
|
+
destinationBuffer: this._readBuffer,
|
|
2233
|
+
size: this._resolveBuffer.byteLength
|
|
2234
|
+
});
|
|
2235
|
+
const commandBuffer = commandEncoder.finish({
|
|
2236
|
+
id: `${this.id}-read-results-command-buffer`
|
|
2237
|
+
});
|
|
2238
|
+
const previousSubmitReason = getCpuHotspotSubmitReason(this.device) || void 0;
|
|
2239
|
+
setCpuHotspotSubmitReason(this.device, "query-readback");
|
|
2240
|
+
try {
|
|
2241
|
+
this.device.submit(commandBuffer);
|
|
2242
|
+
} finally {
|
|
2243
|
+
setCpuHotspotSubmitReason(this.device, previousSubmitReason);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
const data = await this._readBuffer.readAsync(0, this._readBuffer.byteLength);
|
|
2247
|
+
const resultView = new BigUint64Array(data.buffer, data.byteOffset, this.props.count);
|
|
2248
|
+
this._cachedResults = Array.from(resultView, (value) => value);
|
|
2249
|
+
this._resultsPendingResolution = false;
|
|
2250
|
+
return this._cachedResults;
|
|
2251
|
+
} finally {
|
|
2252
|
+
this._readResultsPromise = null;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
_ensureBuffers() {
|
|
2256
|
+
if (this._resolveBuffer && this._readBuffer) {
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
const byteLength = this.props.count * 8;
|
|
2260
|
+
this._resolveBuffer = this.device.createBuffer({
|
|
2261
|
+
id: `${this.id}-resolve-buffer`,
|
|
2262
|
+
usage: import_core20.Buffer.QUERY_RESOLVE | import_core20.Buffer.COPY_SRC,
|
|
2263
|
+
byteLength
|
|
2264
|
+
});
|
|
2265
|
+
this.attachResource(this._resolveBuffer);
|
|
2266
|
+
this._readBuffer = this.device.createBuffer({
|
|
2267
|
+
id: `${this.id}-read-buffer`,
|
|
2268
|
+
usage: import_core20.Buffer.COPY_DST | import_core20.Buffer.MAP_READ,
|
|
2269
|
+
byteLength
|
|
2270
|
+
});
|
|
2271
|
+
this.attachResource(this._readBuffer);
|
|
2272
|
+
}
|
|
2273
|
+
_encodeResolveToReadBuffer(commandEncoder, options) {
|
|
2274
|
+
if (!this._resultsPendingResolution) {
|
|
2275
|
+
return false;
|
|
2276
|
+
}
|
|
2277
|
+
if (this._readResultsPromise) {
|
|
2278
|
+
return false;
|
|
2279
|
+
}
|
|
2280
|
+
this._ensureBuffers();
|
|
2281
|
+
const firstQuery = (options == null ? void 0 : options.firstQuery) || 0;
|
|
2282
|
+
const queryCount = (options == null ? void 0 : options.queryCount) || this.props.count - firstQuery;
|
|
2283
|
+
const byteLength = queryCount * BigUint64Array.BYTES_PER_ELEMENT;
|
|
2284
|
+
const byteOffset = firstQuery * BigUint64Array.BYTES_PER_ELEMENT;
|
|
2285
|
+
commandEncoder.resolveQuerySet(this, this._resolveBuffer, {
|
|
2286
|
+
firstQuery,
|
|
2287
|
+
queryCount,
|
|
2288
|
+
destinationOffset: byteOffset
|
|
2289
|
+
});
|
|
2290
|
+
commandEncoder.copyBufferToBuffer({
|
|
2291
|
+
sourceBuffer: this._resolveBuffer,
|
|
2292
|
+
sourceOffset: byteOffset,
|
|
2293
|
+
destinationBuffer: this._readBuffer,
|
|
2294
|
+
destinationOffset: byteOffset,
|
|
2295
|
+
size: byteLength
|
|
2296
|
+
});
|
|
2297
|
+
this._resultsPendingResolution = false;
|
|
2298
|
+
return true;
|
|
1730
2299
|
}
|
|
1731
2300
|
};
|
|
1732
2301
|
}
|
|
1733
2302
|
});
|
|
1734
2303
|
|
|
1735
2304
|
// dist/adapter/resources/webgpu-pipeline-layout.js
|
|
1736
|
-
var
|
|
2305
|
+
var import_core21, WebGPUPipelineLayout, isStorageTextureBindingLayout;
|
|
1737
2306
|
var init_webgpu_pipeline_layout = __esm({
|
|
1738
2307
|
"dist/adapter/resources/webgpu-pipeline-layout.js"() {
|
|
1739
2308
|
"use strict";
|
|
1740
|
-
|
|
1741
|
-
WebGPUPipelineLayout = class extends
|
|
2309
|
+
import_core21 = require("@luma.gl/core");
|
|
2310
|
+
WebGPUPipelineLayout = class extends import_core21.PipelineLayout {
|
|
1742
2311
|
device;
|
|
1743
2312
|
handle;
|
|
1744
2313
|
constructor(device, props) {
|
|
@@ -1763,8 +2332,7 @@ var init_webgpu_pipeline_layout = __esm({
|
|
|
1763
2332
|
}
|
|
1764
2333
|
mapShaderLayoutToBindGroupEntries() {
|
|
1765
2334
|
const bindGroupEntries = [];
|
|
1766
|
-
for (
|
|
1767
|
-
const binding = this.props.shaderLayout.bindings[i];
|
|
2335
|
+
for (const binding of this.props.shaderLayout.bindings) {
|
|
1768
2336
|
const bindingTypeInfo = {};
|
|
1769
2337
|
switch (binding.type) {
|
|
1770
2338
|
case "uniform": {
|
|
@@ -1816,7 +2384,7 @@ var init_webgpu_pipeline_layout = __esm({
|
|
|
1816
2384
|
break;
|
|
1817
2385
|
}
|
|
1818
2386
|
default: {
|
|
1819
|
-
|
|
2387
|
+
import_core21.log.warn("unhandled binding type when creating pipeline descriptor")();
|
|
1820
2388
|
}
|
|
1821
2389
|
}
|
|
1822
2390
|
const VISIBILITY_ALL = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
|
|
@@ -1836,12 +2404,12 @@ var init_webgpu_pipeline_layout = __esm({
|
|
|
1836
2404
|
});
|
|
1837
2405
|
|
|
1838
2406
|
// dist/adapter/resources/webgpu-fence.js
|
|
1839
|
-
var
|
|
2407
|
+
var import_core22, WebGPUFence;
|
|
1840
2408
|
var init_webgpu_fence = __esm({
|
|
1841
2409
|
"dist/adapter/resources/webgpu-fence.js"() {
|
|
1842
2410
|
"use strict";
|
|
1843
|
-
|
|
1844
|
-
WebGPUFence = class extends
|
|
2411
|
+
import_core22 = require("@luma.gl/core");
|
|
2412
|
+
WebGPUFence = class extends import_core22.Fence {
|
|
1845
2413
|
device;
|
|
1846
2414
|
handle = null;
|
|
1847
2415
|
signaled;
|
|
@@ -1870,7 +2438,7 @@ function getShaderLayoutFromWGSL(source) {
|
|
|
1870
2438
|
try {
|
|
1871
2439
|
parsedWGSL = parseWGSL(source);
|
|
1872
2440
|
} catch (error) {
|
|
1873
|
-
|
|
2441
|
+
import_core23.log.error(error.message)();
|
|
1874
2442
|
return shaderLayout;
|
|
1875
2443
|
}
|
|
1876
2444
|
for (const uniform of parsedWGSL.uniforms) {
|
|
@@ -1964,25 +2532,508 @@ function getTextureBindingFromReflect(v, opts) {
|
|
|
1964
2532
|
}
|
|
1965
2533
|
return { viewDimension, sampleType, multisampled };
|
|
1966
2534
|
}
|
|
1967
|
-
var
|
|
2535
|
+
var import_core23, import_wgsl_reflect;
|
|
1968
2536
|
var init_get_shader_layout_wgsl = __esm({
|
|
1969
2537
|
"dist/wgsl/get-shader-layout-wgsl.js"() {
|
|
1970
2538
|
"use strict";
|
|
1971
|
-
|
|
2539
|
+
import_core23 = require("@luma.gl/core");
|
|
1972
2540
|
import_wgsl_reflect = require("wgsl_reflect");
|
|
1973
2541
|
}
|
|
1974
2542
|
});
|
|
1975
2543
|
|
|
2544
|
+
// dist/adapter/helpers/generate-mipmaps-webgpu.js
|
|
2545
|
+
function generateMipmapsWebGPU(device, texture) {
|
|
2546
|
+
if (texture.mipLevels <= 1) {
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
if (texture.dimension === "3d") {
|
|
2550
|
+
generateMipmaps3D(device, texture);
|
|
2551
|
+
return;
|
|
2552
|
+
}
|
|
2553
|
+
if (RENDER_DIMENSIONS.includes(texture.dimension)) {
|
|
2554
|
+
generateMipmapsRender(device, texture);
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
throw new Error(`Cannot generate mipmaps for texture dimension "${texture.dimension}" with WebGPU.`);
|
|
2558
|
+
}
|
|
2559
|
+
function generateMipmapsRender(device, texture) {
|
|
2560
|
+
validateFormatCapabilities(device, texture, ["render", "filter"], "render");
|
|
2561
|
+
const colorAttachmentFormat = getColorAttachmentFormat(texture.format, "render", texture.dimension);
|
|
2562
|
+
const viewDimension = texture.dimension;
|
|
2563
|
+
const shaderSource = getRenderMipmapWGSL(viewDimension);
|
|
2564
|
+
const sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
|
|
2565
|
+
const uniformsBuffer = device.createBuffer({
|
|
2566
|
+
byteLength: 16,
|
|
2567
|
+
usage: import_core24.Buffer.UNIFORM | import_core24.Buffer.COPY_DST
|
|
2568
|
+
});
|
|
2569
|
+
const uniformValues = new Uint32Array(1);
|
|
2570
|
+
const sourceTextureLayout = {
|
|
2571
|
+
type: "texture",
|
|
2572
|
+
name: "sourceTexture",
|
|
2573
|
+
group: 0,
|
|
2574
|
+
location: 1,
|
|
2575
|
+
viewDimension,
|
|
2576
|
+
sampleType: "float"
|
|
2577
|
+
};
|
|
2578
|
+
const uniformsLayout = {
|
|
2579
|
+
type: "uniform",
|
|
2580
|
+
name: "uniforms",
|
|
2581
|
+
group: 0,
|
|
2582
|
+
location: 2
|
|
2583
|
+
};
|
|
2584
|
+
const renderShaderLayout = {
|
|
2585
|
+
attributes: [],
|
|
2586
|
+
bindings: [RENDER_SOURCE_SAMPLER_LAYOUT, sourceTextureLayout, uniformsLayout]
|
|
2587
|
+
};
|
|
2588
|
+
const vertexShader = device.createShader({
|
|
2589
|
+
id: "mipmap-generation-render-vs",
|
|
2590
|
+
source: shaderSource,
|
|
2591
|
+
language: "wgsl",
|
|
2592
|
+
stage: "vertex"
|
|
2593
|
+
});
|
|
2594
|
+
const fragmentShader = device.createShader({
|
|
2595
|
+
id: "mipmap-generation-render-fs",
|
|
2596
|
+
source: shaderSource,
|
|
2597
|
+
language: "wgsl",
|
|
2598
|
+
stage: "fragment"
|
|
2599
|
+
});
|
|
2600
|
+
const renderPipeline = device.createRenderPipeline({
|
|
2601
|
+
id: `mipmap-generation-render:${texture.dimension}:${texture.format}`,
|
|
2602
|
+
vs: vertexShader,
|
|
2603
|
+
fs: fragmentShader,
|
|
2604
|
+
shaderLayout: renderShaderLayout,
|
|
2605
|
+
colorAttachmentFormats: [colorAttachmentFormat],
|
|
2606
|
+
topology: "triangle-list"
|
|
2607
|
+
});
|
|
2608
|
+
let sourceWidth = texture.width;
|
|
2609
|
+
let sourceHeight = texture.height;
|
|
2610
|
+
const layerCount = texture.dimension === "2d" ? 1 : texture.depth;
|
|
2611
|
+
function renderMipmapLayer(sourceView, baseMipLevel, baseArrayLayer, destinationWidth, destinationHeight) {
|
|
2612
|
+
uniformValues[0] = baseArrayLayer;
|
|
2613
|
+
uniformsBuffer.write(uniformValues);
|
|
2614
|
+
const destinationView = texture.createView({
|
|
2615
|
+
dimension: "2d",
|
|
2616
|
+
baseMipLevel,
|
|
2617
|
+
mipLevelCount: 1,
|
|
2618
|
+
baseArrayLayer,
|
|
2619
|
+
arrayLayerCount: 1
|
|
2620
|
+
});
|
|
2621
|
+
const framebuffer = device.createFramebuffer({
|
|
2622
|
+
colorAttachments: [destinationView]
|
|
2623
|
+
});
|
|
2624
|
+
const renderPass = device.beginRenderPass({
|
|
2625
|
+
id: `mipmap-generation:${texture.format}:${baseMipLevel}:${baseArrayLayer}`,
|
|
2626
|
+
framebuffer
|
|
2627
|
+
});
|
|
2628
|
+
try {
|
|
2629
|
+
renderPass.setPipeline(renderPipeline);
|
|
2630
|
+
renderPass.setBindings({
|
|
2631
|
+
sourceSampler: sampler,
|
|
2632
|
+
sourceTexture: sourceView,
|
|
2633
|
+
uniforms: uniformsBuffer
|
|
2634
|
+
});
|
|
2635
|
+
renderPass.setParameters({
|
|
2636
|
+
viewport: [0, 0, destinationWidth, destinationHeight, 0, 1],
|
|
2637
|
+
scissorRect: [0, 0, destinationWidth, destinationHeight]
|
|
2638
|
+
});
|
|
2639
|
+
renderPass.draw({ vertexCount: 3 });
|
|
2640
|
+
renderPass.end();
|
|
2641
|
+
device.submit();
|
|
2642
|
+
} finally {
|
|
2643
|
+
destinationView.destroy();
|
|
2644
|
+
framebuffer.destroy();
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
try {
|
|
2648
|
+
for (let baseMipLevel = 1; baseMipLevel < texture.mipLevels; ++baseMipLevel) {
|
|
2649
|
+
validateFormatCapabilities(device, texture, ["render", "filter"], "render");
|
|
2650
|
+
const sourceMipLevel = baseMipLevel - 1;
|
|
2651
|
+
const destinationWidth = Math.max(1, sourceWidth >> 1);
|
|
2652
|
+
const destinationHeight = Math.max(1, sourceHeight >> 1);
|
|
2653
|
+
const sourceView = texture.createView({
|
|
2654
|
+
dimension: viewDimension,
|
|
2655
|
+
baseMipLevel: sourceMipLevel,
|
|
2656
|
+
mipLevelCount: 1,
|
|
2657
|
+
baseArrayLayer: 0,
|
|
2658
|
+
arrayLayerCount: texture.depth
|
|
2659
|
+
});
|
|
2660
|
+
try {
|
|
2661
|
+
for (let baseArrayLayer = 0; baseArrayLayer < layerCount; ++baseArrayLayer) {
|
|
2662
|
+
renderMipmapLayer(sourceView, baseMipLevel, baseArrayLayer, destinationWidth, destinationHeight);
|
|
2663
|
+
}
|
|
2664
|
+
} finally {
|
|
2665
|
+
sourceView.destroy();
|
|
2666
|
+
}
|
|
2667
|
+
sourceWidth = destinationWidth;
|
|
2668
|
+
sourceHeight = destinationHeight;
|
|
2669
|
+
}
|
|
2670
|
+
} finally {
|
|
2671
|
+
renderPipeline.destroy();
|
|
2672
|
+
vertexShader.destroy();
|
|
2673
|
+
fragmentShader.destroy();
|
|
2674
|
+
sampler.destroy();
|
|
2675
|
+
uniformsBuffer.destroy();
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
function getColorAttachmentFormat(format, path, dimension) {
|
|
2679
|
+
if (import_core24.textureFormatDecoder.isColor(format)) {
|
|
2680
|
+
return format;
|
|
2681
|
+
}
|
|
2682
|
+
throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Only color textures can be used for this operation. Required capabilities: color. Actual capabilities: color=false.`);
|
|
2683
|
+
}
|
|
2684
|
+
function generateMipmaps3D(device, texture) {
|
|
2685
|
+
validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
|
|
2686
|
+
const format = getColorAttachmentFormat(texture.format, "compute", texture.dimension);
|
|
2687
|
+
const shaderSource = get3DComputeMipmapWGSL(format);
|
|
2688
|
+
const destinationTextureLayout = {
|
|
2689
|
+
type: "storage",
|
|
2690
|
+
name: "destinationTexture",
|
|
2691
|
+
group: 0,
|
|
2692
|
+
location: 1,
|
|
2693
|
+
format,
|
|
2694
|
+
viewDimension: "3d",
|
|
2695
|
+
access: "write-only"
|
|
2696
|
+
};
|
|
2697
|
+
const computeShaderLayout = {
|
|
2698
|
+
bindings: [COMPUTE_SOURCE_TEXTURE_LAYOUT, destinationTextureLayout, COMPUTE_UNIFORMS_LAYOUT]
|
|
2699
|
+
};
|
|
2700
|
+
const computeShader = device.createShader({
|
|
2701
|
+
id: "mipmap-generation-compute",
|
|
2702
|
+
source: shaderSource,
|
|
2703
|
+
language: "wgsl",
|
|
2704
|
+
stage: "compute"
|
|
2705
|
+
});
|
|
2706
|
+
const computePipeline = device.createComputePipeline({
|
|
2707
|
+
id: `mipmap-generation-compute:${texture.format}`,
|
|
2708
|
+
shader: computeShader,
|
|
2709
|
+
shaderLayout: computeShaderLayout
|
|
2710
|
+
});
|
|
2711
|
+
const uniformsBuffer = device.createBuffer({
|
|
2712
|
+
byteLength: 32,
|
|
2713
|
+
usage: import_core24.Buffer.UNIFORM | import_core24.Buffer.COPY_DST
|
|
2714
|
+
});
|
|
2715
|
+
const uniformValues = new Uint32Array(8);
|
|
2716
|
+
let sourceWidth = texture.width;
|
|
2717
|
+
let sourceHeight = texture.height;
|
|
2718
|
+
let sourceDepth = texture.depth;
|
|
2719
|
+
try {
|
|
2720
|
+
for (let destinationMipLevel = 1; destinationMipLevel < texture.mipLevels; ++destinationMipLevel) {
|
|
2721
|
+
validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
|
|
2722
|
+
const destinationWidth = Math.max(1, sourceWidth >> 1);
|
|
2723
|
+
const destinationHeight = Math.max(1, sourceHeight >> 1);
|
|
2724
|
+
const destinationDepth = Math.max(1, sourceDepth >> 1);
|
|
2725
|
+
uniformValues[0] = sourceWidth;
|
|
2726
|
+
uniformValues[1] = sourceHeight;
|
|
2727
|
+
uniformValues[2] = sourceDepth;
|
|
2728
|
+
uniformValues[3] = destinationWidth;
|
|
2729
|
+
uniformValues[4] = destinationHeight;
|
|
2730
|
+
uniformValues[5] = destinationDepth;
|
|
2731
|
+
uniformValues[6] = 0;
|
|
2732
|
+
uniformsBuffer.write(uniformValues);
|
|
2733
|
+
const sourceView = texture.createView({
|
|
2734
|
+
dimension: "3d",
|
|
2735
|
+
baseMipLevel: destinationMipLevel - 1,
|
|
2736
|
+
mipLevelCount: 1,
|
|
2737
|
+
baseArrayLayer: 0,
|
|
2738
|
+
arrayLayerCount: 1
|
|
2739
|
+
});
|
|
2740
|
+
const destinationView = texture.createView({
|
|
2741
|
+
dimension: "3d",
|
|
2742
|
+
baseMipLevel: destinationMipLevel,
|
|
2743
|
+
mipLevelCount: 1,
|
|
2744
|
+
baseArrayLayer: 0,
|
|
2745
|
+
arrayLayerCount: 1
|
|
2746
|
+
});
|
|
2747
|
+
computePipeline.setBindings({
|
|
2748
|
+
sourceTexture: sourceView,
|
|
2749
|
+
destinationTexture: destinationView,
|
|
2750
|
+
uniforms: uniformsBuffer
|
|
2751
|
+
});
|
|
2752
|
+
try {
|
|
2753
|
+
const workgroupsX = Math.ceil(destinationWidth / WORKGROUP_SIZE.x);
|
|
2754
|
+
const workgroupsY = Math.ceil(destinationHeight / WORKGROUP_SIZE.y);
|
|
2755
|
+
const workgroupsZ = Math.ceil(destinationDepth / WORKGROUP_SIZE.z);
|
|
2756
|
+
const computePass = device.beginComputePass({});
|
|
2757
|
+
computePass.setPipeline(computePipeline);
|
|
2758
|
+
computePass.dispatch(workgroupsX, workgroupsY, workgroupsZ);
|
|
2759
|
+
computePass.end();
|
|
2760
|
+
device.submit();
|
|
2761
|
+
} finally {
|
|
2762
|
+
sourceView.destroy();
|
|
2763
|
+
destinationView.destroy();
|
|
2764
|
+
}
|
|
2765
|
+
sourceWidth = destinationWidth;
|
|
2766
|
+
sourceHeight = destinationHeight;
|
|
2767
|
+
sourceDepth = destinationDepth;
|
|
2768
|
+
}
|
|
2769
|
+
} finally {
|
|
2770
|
+
computePipeline.destroy();
|
|
2771
|
+
computeShader.destroy();
|
|
2772
|
+
uniformsBuffer.destroy();
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
function validateFormatCapabilities(device, texture, requiredCapabilities, path) {
|
|
2776
|
+
const { format, dimension } = texture;
|
|
2777
|
+
const capabilities = device.getTextureFormatCapabilities(format);
|
|
2778
|
+
const missingCapabilities = requiredCapabilities.filter((capability) => !capabilities[capability]);
|
|
2779
|
+
if (missingCapabilities.length > 0) {
|
|
2780
|
+
const required = requiredCapabilities.join(" + ");
|
|
2781
|
+
const actual = requiredCapabilities.map((capability) => `${capability}=${capabilities[capability]}`).join(", ");
|
|
2782
|
+
throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Required capabilities: ${required}. Actual capabilities: ${actual}.`);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
function getSourceTextureType(dimension) {
|
|
2786
|
+
switch (dimension) {
|
|
2787
|
+
case "2d":
|
|
2788
|
+
return "texture_2d<f32>";
|
|
2789
|
+
case "2d-array":
|
|
2790
|
+
return "texture_2d_array<f32>";
|
|
2791
|
+
case "cube":
|
|
2792
|
+
return "texture_cube<f32>";
|
|
2793
|
+
case "cube-array":
|
|
2794
|
+
return "texture_cube_array<f32>";
|
|
2795
|
+
default:
|
|
2796
|
+
throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
function getRenderMipmapWGSL(dimension) {
|
|
2800
|
+
const sourceSnippet = getRenderMipmapSampleSnippet(dimension);
|
|
2801
|
+
return `
|
|
2802
|
+
struct MipmapUniforms {
|
|
2803
|
+
sourceLayer: u32,
|
|
2804
|
+
};
|
|
2805
|
+
|
|
2806
|
+
fn _touchUniform(uniforms: MipmapUniforms) {
|
|
2807
|
+
let unusedSourceLayer = uniforms.sourceLayer;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
const faceMat = array(
|
|
2811
|
+
mat3x3f(
|
|
2812
|
+
0.0, 0.0, -2.0,
|
|
2813
|
+
0.0, -2.0, 0.0,
|
|
2814
|
+
1.0, 1.0, 1.0
|
|
2815
|
+
), // pos-x
|
|
2816
|
+
mat3x3f(
|
|
2817
|
+
0.0, 0.0, 2.0,
|
|
2818
|
+
0.0, -2.0, 0.0,
|
|
2819
|
+
-1.0, 1.0, -1.0
|
|
2820
|
+
), // neg-x
|
|
2821
|
+
mat3x3f(
|
|
2822
|
+
2.0, 0.0, 0.0,
|
|
2823
|
+
0.0, 0.0, 2.0,
|
|
2824
|
+
-1.0, 1.0, -1.0
|
|
2825
|
+
), // pos-y
|
|
2826
|
+
mat3x3f(
|
|
2827
|
+
2.0, 0.0, 0.0,
|
|
2828
|
+
0.0, 0.0, -2.0,
|
|
2829
|
+
-1.0, -1.0, 1.0
|
|
2830
|
+
), // neg-y
|
|
2831
|
+
mat3x3f(
|
|
2832
|
+
2.0, 0.0, 0.0,
|
|
2833
|
+
0.0, -2.0, 0.0,
|
|
2834
|
+
-1.0, 1.0, 1.0
|
|
2835
|
+
), // pos-z
|
|
2836
|
+
mat3x3f(
|
|
2837
|
+
-2.0, 0.0, 0.0,
|
|
2838
|
+
0.0, -2.0, 0.0,
|
|
2839
|
+
1.0, 1.0, -1.0
|
|
2840
|
+
) // neg-z
|
|
2841
|
+
);
|
|
2842
|
+
|
|
2843
|
+
struct FragmentInputs {
|
|
2844
|
+
@builtin(position) position: vec4f,
|
|
2845
|
+
@location(0) texcoord: vec2f
|
|
2846
|
+
};
|
|
2847
|
+
|
|
2848
|
+
struct VertexOutput {
|
|
2849
|
+
@builtin(position) position: vec4f,
|
|
2850
|
+
@location(0) texcoord: vec2f
|
|
2851
|
+
};
|
|
2852
|
+
|
|
2853
|
+
@group(0) @binding(0) var sourceSampler: sampler;
|
|
2854
|
+
@group(0) @binding(1) var sourceTexture: ${getSourceTextureType(dimension)};
|
|
2855
|
+
@group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
|
|
2856
|
+
|
|
2857
|
+
@vertex
|
|
2858
|
+
fn vertexMain(
|
|
2859
|
+
@builtin(vertex_index) vertexIndex: u32
|
|
2860
|
+
) -> VertexOutput {
|
|
2861
|
+
const positions = array(
|
|
2862
|
+
vec2f(-1.0, -1.0),
|
|
2863
|
+
vec2f(-1.0, 3.0),
|
|
2864
|
+
vec2f( 3.0, -1.0)
|
|
2865
|
+
);
|
|
2866
|
+
|
|
2867
|
+
let xy = positions[vertexIndex];
|
|
2868
|
+
return VertexOutput(
|
|
2869
|
+
vec4f(xy, 0.0, 1.0),
|
|
2870
|
+
xy * vec2f(0.5, -0.5) + vec2f(0.5)
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
@fragment
|
|
2875
|
+
fn fragmentMain(fsInput: VertexOutput) -> @location(0) vec4f {
|
|
2876
|
+
_touchUniform(uniforms);
|
|
2877
|
+
return ${sourceSnippet};
|
|
2878
|
+
}
|
|
2879
|
+
`;
|
|
2880
|
+
}
|
|
2881
|
+
function getRenderMipmapSampleSnippet(dimension) {
|
|
2882
|
+
const layer = "uniforms.sourceLayer";
|
|
2883
|
+
switch (dimension) {
|
|
2884
|
+
case "2d":
|
|
2885
|
+
return "textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, 0.0)";
|
|
2886
|
+
case "2d-array":
|
|
2887
|
+
return `textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, i32(${layer}), 0.0)`;
|
|
2888
|
+
case "cube":
|
|
2889
|
+
return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer})] * vec3f(fract(fsInput.texcoord), 1.0), 0.0)`;
|
|
2890
|
+
case "cube-array":
|
|
2891
|
+
return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer} % 6u)] * vec3f(fract(fsInput.texcoord), 1.0), i32(${layer} / 6u), 0.0)`;
|
|
2892
|
+
default:
|
|
2893
|
+
throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
function get3DComputeMipmapWGSL(format) {
|
|
2897
|
+
return `
|
|
2898
|
+
struct MipmapUniforms {
|
|
2899
|
+
sourceWidth: u32,
|
|
2900
|
+
sourceHeight: u32,
|
|
2901
|
+
sourceDepth: u32,
|
|
2902
|
+
destinationWidth: u32,
|
|
2903
|
+
destinationHeight: u32,
|
|
2904
|
+
destinationDepth: u32,
|
|
2905
|
+
padding: u32,
|
|
2906
|
+
};
|
|
2907
|
+
|
|
2908
|
+
@group(0) @binding(0) var sourceTexture: texture_3d<f32>;
|
|
2909
|
+
@group(0) @binding(1) var destinationTexture: texture_storage_3d<${format}, write>;
|
|
2910
|
+
@group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
|
|
2911
|
+
|
|
2912
|
+
@compute @workgroup_size(${WORKGROUP_SIZE.x}, ${WORKGROUP_SIZE.y}, ${WORKGROUP_SIZE.z})
|
|
2913
|
+
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
2914
|
+
if (
|
|
2915
|
+
id.x >= uniforms.destinationWidth ||
|
|
2916
|
+
id.y >= uniforms.destinationHeight ||
|
|
2917
|
+
id.z >= uniforms.destinationDepth
|
|
2918
|
+
) {
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
let sourceBase = id * 2u;
|
|
2923
|
+
let sourceX0 = min(sourceBase.x, uniforms.sourceWidth - 1u);
|
|
2924
|
+
let sourceY0 = min(sourceBase.y, uniforms.sourceHeight - 1u);
|
|
2925
|
+
let sourceZ0 = min(sourceBase.z, uniforms.sourceDepth - 1u);
|
|
2926
|
+
|
|
2927
|
+
let sourceX1 = min(sourceBase.x + 1u, uniforms.sourceWidth - 1u);
|
|
2928
|
+
let sourceY1 = min(sourceBase.y + 1u, uniforms.sourceHeight - 1u);
|
|
2929
|
+
let sourceZ1 = min(sourceBase.z + 1u, uniforms.sourceDepth - 1u);
|
|
2930
|
+
|
|
2931
|
+
var sum = textureLoad(
|
|
2932
|
+
sourceTexture,
|
|
2933
|
+
vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ0)),
|
|
2934
|
+
0
|
|
2935
|
+
);
|
|
2936
|
+
sum += textureLoad(
|
|
2937
|
+
sourceTexture,
|
|
2938
|
+
vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ0)),
|
|
2939
|
+
0
|
|
2940
|
+
);
|
|
2941
|
+
sum += textureLoad(
|
|
2942
|
+
sourceTexture,
|
|
2943
|
+
vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ0)),
|
|
2944
|
+
0
|
|
2945
|
+
);
|
|
2946
|
+
sum += textureLoad(
|
|
2947
|
+
sourceTexture,
|
|
2948
|
+
vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ0)),
|
|
2949
|
+
0
|
|
2950
|
+
);
|
|
2951
|
+
sum += textureLoad(
|
|
2952
|
+
sourceTexture,
|
|
2953
|
+
vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ1)),
|
|
2954
|
+
0
|
|
2955
|
+
);
|
|
2956
|
+
sum += textureLoad(
|
|
2957
|
+
sourceTexture,
|
|
2958
|
+
vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ1)),
|
|
2959
|
+
0
|
|
2960
|
+
);
|
|
2961
|
+
sum += textureLoad(
|
|
2962
|
+
sourceTexture,
|
|
2963
|
+
vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ1)),
|
|
2964
|
+
0
|
|
2965
|
+
);
|
|
2966
|
+
sum += textureLoad(
|
|
2967
|
+
sourceTexture,
|
|
2968
|
+
vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ1)),
|
|
2969
|
+
0
|
|
2970
|
+
);
|
|
2971
|
+
|
|
2972
|
+
textureStore(
|
|
2973
|
+
destinationTexture,
|
|
2974
|
+
vec3<i32>(i32(id.x), i32(id.y), i32(id.z)),
|
|
2975
|
+
vec4<f32>(sum.xyz / 8.0, sum.w / 8.0)
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
`;
|
|
2979
|
+
}
|
|
2980
|
+
var import_core24, RENDER_DIMENSIONS, WORKGROUP_SIZE, RENDER_SOURCE_SAMPLER_LAYOUT, COMPUTE_SOURCE_TEXTURE_LAYOUT, COMPUTE_UNIFORMS_LAYOUT;
|
|
2981
|
+
var init_generate_mipmaps_webgpu = __esm({
|
|
2982
|
+
"dist/adapter/helpers/generate-mipmaps-webgpu.js"() {
|
|
2983
|
+
"use strict";
|
|
2984
|
+
import_core24 = require("@luma.gl/core");
|
|
2985
|
+
RENDER_DIMENSIONS = [
|
|
2986
|
+
"2d",
|
|
2987
|
+
"2d-array",
|
|
2988
|
+
"cube",
|
|
2989
|
+
"cube-array"
|
|
2990
|
+
];
|
|
2991
|
+
WORKGROUP_SIZE = {
|
|
2992
|
+
x: 4,
|
|
2993
|
+
y: 4,
|
|
2994
|
+
z: 4
|
|
2995
|
+
};
|
|
2996
|
+
RENDER_SOURCE_SAMPLER_LAYOUT = {
|
|
2997
|
+
type: "sampler",
|
|
2998
|
+
name: "sourceSampler",
|
|
2999
|
+
group: 0,
|
|
3000
|
+
location: 0
|
|
3001
|
+
};
|
|
3002
|
+
COMPUTE_SOURCE_TEXTURE_LAYOUT = {
|
|
3003
|
+
type: "texture",
|
|
3004
|
+
name: "sourceTexture",
|
|
3005
|
+
group: 0,
|
|
3006
|
+
location: 0,
|
|
3007
|
+
viewDimension: "3d",
|
|
3008
|
+
sampleType: "float"
|
|
3009
|
+
};
|
|
3010
|
+
COMPUTE_UNIFORMS_LAYOUT = {
|
|
3011
|
+
type: "uniform",
|
|
3012
|
+
name: "uniforms",
|
|
3013
|
+
group: 0,
|
|
3014
|
+
location: 2
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
});
|
|
3018
|
+
|
|
1976
3019
|
// dist/adapter/webgpu-device.js
|
|
1977
3020
|
var webgpu_device_exports = {};
|
|
1978
3021
|
__export(webgpu_device_exports, {
|
|
1979
3022
|
WebGPUDevice: () => WebGPUDevice
|
|
1980
3023
|
});
|
|
1981
|
-
|
|
3024
|
+
function scheduleMicrotask(callback) {
|
|
3025
|
+
if (globalThis.queueMicrotask) {
|
|
3026
|
+
globalThis.queueMicrotask(callback);
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
Promise.resolve().then(callback).catch(() => {
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
3032
|
+
var import_core25, WebGPUDevice;
|
|
1982
3033
|
var init_webgpu_device = __esm({
|
|
1983
3034
|
"dist/adapter/webgpu-device.js"() {
|
|
1984
3035
|
"use strict";
|
|
1985
|
-
|
|
3036
|
+
import_core25 = require("@luma.gl/core");
|
|
1986
3037
|
init_webgpu_buffer();
|
|
1987
3038
|
init_webgpu_texture();
|
|
1988
3039
|
init_webgpu_external_texture();
|
|
@@ -1993,12 +3044,15 @@ var init_webgpu_device = __esm({
|
|
|
1993
3044
|
init_webgpu_compute_pipeline();
|
|
1994
3045
|
init_webgpu_vertex_array();
|
|
1995
3046
|
init_webgpu_canvas_context();
|
|
3047
|
+
init_webgpu_presentation_context();
|
|
1996
3048
|
init_webgpu_command_encoder();
|
|
1997
3049
|
init_webgpu_query_set();
|
|
1998
3050
|
init_webgpu_pipeline_layout();
|
|
1999
3051
|
init_webgpu_fence();
|
|
2000
3052
|
init_get_shader_layout_wgsl();
|
|
2001
|
-
|
|
3053
|
+
init_generate_mipmaps_webgpu();
|
|
3054
|
+
init_cpu_hotspot_profiler();
|
|
3055
|
+
WebGPUDevice = class extends import_core25.Device {
|
|
2002
3056
|
/** The underlying WebGPU device */
|
|
2003
3057
|
handle;
|
|
2004
3058
|
/* The underlying WebGPU adapter */
|
|
@@ -2015,6 +3069,7 @@ var init_webgpu_device = __esm({
|
|
|
2015
3069
|
lost;
|
|
2016
3070
|
canvasContext = null;
|
|
2017
3071
|
_isLost = false;
|
|
3072
|
+
_defaultSampler = null;
|
|
2018
3073
|
commandEncoder;
|
|
2019
3074
|
get [Symbol.toStringTag]() {
|
|
2020
3075
|
return "WebGPUDevice";
|
|
@@ -2041,7 +3096,7 @@ var init_webgpu_device = __esm({
|
|
|
2041
3096
|
this._isLost = true;
|
|
2042
3097
|
resolve({ reason: "destroyed", message: lostInfo.message });
|
|
2043
3098
|
});
|
|
2044
|
-
const canvasContextProps =
|
|
3099
|
+
const canvasContextProps = import_core25.Device._getCanvasContextProps(props);
|
|
2045
3100
|
if (canvasContextProps) {
|
|
2046
3101
|
this.canvasContext = new WebGPUCanvasContext(this, this.adapter, canvasContextProps);
|
|
2047
3102
|
}
|
|
@@ -2052,6 +3107,10 @@ var init_webgpu_device = __esm({
|
|
|
2052
3107
|
// const {glsl = true} = props;
|
|
2053
3108
|
// this.glslang = glsl && await loadGlslangModule();
|
|
2054
3109
|
destroy() {
|
|
3110
|
+
var _a, _b;
|
|
3111
|
+
(_a = this.commandEncoder) == null ? void 0 : _a.destroy();
|
|
3112
|
+
(_b = this._defaultSampler) == null ? void 0 : _b.destroy();
|
|
3113
|
+
this._defaultSampler = null;
|
|
2055
3114
|
this.handle.destroy();
|
|
2056
3115
|
}
|
|
2057
3116
|
get isLost() {
|
|
@@ -2080,6 +3139,12 @@ var init_webgpu_device = __esm({
|
|
|
2080
3139
|
createSampler(props) {
|
|
2081
3140
|
return new WebGPUSampler(this, props);
|
|
2082
3141
|
}
|
|
3142
|
+
getDefaultSampler() {
|
|
3143
|
+
this._defaultSampler ||= new WebGPUSampler(this, {
|
|
3144
|
+
id: `${this.id}-default-sampler`
|
|
3145
|
+
});
|
|
3146
|
+
return this._defaultSampler;
|
|
3147
|
+
}
|
|
2083
3148
|
createRenderPipeline(props) {
|
|
2084
3149
|
return new WebGPURenderPipeline(this, props);
|
|
2085
3150
|
}
|
|
@@ -2108,32 +3173,106 @@ var init_webgpu_device = __esm({
|
|
|
2108
3173
|
createCanvasContext(props) {
|
|
2109
3174
|
return new WebGPUCanvasContext(this, this.adapter, props);
|
|
2110
3175
|
}
|
|
3176
|
+
createPresentationContext(props) {
|
|
3177
|
+
return new WebGPUPresentationContext(this, props);
|
|
3178
|
+
}
|
|
2111
3179
|
createPipelineLayout(props) {
|
|
2112
3180
|
return new WebGPUPipelineLayout(this, props);
|
|
2113
3181
|
}
|
|
3182
|
+
generateMipmapsWebGPU(texture) {
|
|
3183
|
+
generateMipmapsWebGPU(this, texture);
|
|
3184
|
+
}
|
|
2114
3185
|
submit(commandBuffer) {
|
|
3186
|
+
let submittedCommandEncoder = null;
|
|
2115
3187
|
if (!commandBuffer) {
|
|
2116
|
-
|
|
3188
|
+
submittedCommandEncoder = this.commandEncoder;
|
|
3189
|
+
if (submittedCommandEncoder.getTimeProfilingSlotCount() > 0 && submittedCommandEncoder.getTimeProfilingQuerySet() instanceof WebGPUQuerySet) {
|
|
3190
|
+
const querySet = submittedCommandEncoder.getTimeProfilingQuerySet();
|
|
3191
|
+
querySet._encodeResolveToReadBuffer(submittedCommandEncoder, {
|
|
3192
|
+
firstQuery: 0,
|
|
3193
|
+
queryCount: submittedCommandEncoder.getTimeProfilingSlotCount()
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
commandBuffer = submittedCommandEncoder.finish();
|
|
2117
3197
|
this.commandEncoder.destroy();
|
|
2118
|
-
this.commandEncoder = this.createCommandEncoder({
|
|
3198
|
+
this.commandEncoder = this.createCommandEncoder({
|
|
3199
|
+
id: submittedCommandEncoder.props.id,
|
|
3200
|
+
timeProfilingQuerySet: submittedCommandEncoder.getTimeProfilingQuerySet()
|
|
3201
|
+
});
|
|
3202
|
+
}
|
|
3203
|
+
const profiler = getCpuHotspotProfiler(this);
|
|
3204
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
3205
|
+
const submitReason = getCpuHotspotSubmitReason(this);
|
|
3206
|
+
try {
|
|
3207
|
+
this.pushErrorScope("validation");
|
|
3208
|
+
const queueSubmitStartTime = profiler ? getTimestamp() : 0;
|
|
3209
|
+
this.handle.queue.submit([commandBuffer.handle]);
|
|
3210
|
+
if (profiler) {
|
|
3211
|
+
profiler.queueSubmitCount = (profiler.queueSubmitCount || 0) + 1;
|
|
3212
|
+
profiler.queueSubmitTimeMs = (profiler.queueSubmitTimeMs || 0) + (getTimestamp() - queueSubmitStartTime);
|
|
3213
|
+
}
|
|
3214
|
+
this.popErrorScope((error) => {
|
|
3215
|
+
this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
|
|
3216
|
+
this.debug();
|
|
3217
|
+
});
|
|
3218
|
+
if (submittedCommandEncoder) {
|
|
3219
|
+
const submitResolveKickoffStartTime = profiler ? getTimestamp() : 0;
|
|
3220
|
+
scheduleMicrotask(() => {
|
|
3221
|
+
submittedCommandEncoder.resolveTimeProfilingQuerySet().then(() => {
|
|
3222
|
+
this.commandEncoder._gpuTimeMs = submittedCommandEncoder._gpuTimeMs;
|
|
3223
|
+
}).catch(() => {
|
|
3224
|
+
});
|
|
3225
|
+
});
|
|
3226
|
+
if (profiler) {
|
|
3227
|
+
profiler.submitResolveKickoffCount = (profiler.submitResolveKickoffCount || 0) + 1;
|
|
3228
|
+
profiler.submitResolveKickoffTimeMs = (profiler.submitResolveKickoffTimeMs || 0) + (getTimestamp() - submitResolveKickoffStartTime);
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
} finally {
|
|
3232
|
+
if (profiler) {
|
|
3233
|
+
profiler.submitCount = (profiler.submitCount || 0) + 1;
|
|
3234
|
+
profiler.submitTimeMs = (profiler.submitTimeMs || 0) + (getTimestamp() - startTime);
|
|
3235
|
+
const reasonCountKey = submitReason === "query-readback" ? "queryReadbackSubmitCount" : "defaultSubmitCount";
|
|
3236
|
+
const reasonTimeKey = submitReason === "query-readback" ? "queryReadbackSubmitTimeMs" : "defaultSubmitTimeMs";
|
|
3237
|
+
profiler[reasonCountKey] = (profiler[reasonCountKey] || 0) + 1;
|
|
3238
|
+
profiler[reasonTimeKey] = (profiler[reasonTimeKey] || 0) + (getTimestamp() - startTime);
|
|
3239
|
+
}
|
|
3240
|
+
const commandBufferDestroyStartTime = profiler ? getTimestamp() : 0;
|
|
3241
|
+
commandBuffer.destroy();
|
|
3242
|
+
if (profiler) {
|
|
3243
|
+
profiler.commandBufferDestroyCount = (profiler.commandBufferDestroyCount || 0) + 1;
|
|
3244
|
+
profiler.commandBufferDestroyTimeMs = (profiler.commandBufferDestroyTimeMs || 0) + (getTimestamp() - commandBufferDestroyStartTime);
|
|
3245
|
+
}
|
|
2119
3246
|
}
|
|
2120
|
-
this.pushErrorScope("validation");
|
|
2121
|
-
this.handle.queue.submit([commandBuffer.handle]);
|
|
2122
|
-
this.popErrorScope((error) => {
|
|
2123
|
-
this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
|
|
2124
|
-
this.debug();
|
|
2125
|
-
});
|
|
2126
3247
|
}
|
|
2127
3248
|
// WebGPU specific
|
|
2128
3249
|
pushErrorScope(scope) {
|
|
3250
|
+
if (!this.props.debug) {
|
|
3251
|
+
return;
|
|
3252
|
+
}
|
|
3253
|
+
const profiler = getCpuHotspotProfiler(this);
|
|
3254
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
2129
3255
|
this.handle.pushErrorScope(scope);
|
|
3256
|
+
if (profiler) {
|
|
3257
|
+
profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
|
|
3258
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
3259
|
+
}
|
|
2130
3260
|
}
|
|
2131
3261
|
popErrorScope(handler) {
|
|
3262
|
+
if (!this.props.debug) {
|
|
3263
|
+
return;
|
|
3264
|
+
}
|
|
3265
|
+
const profiler = getCpuHotspotProfiler(this);
|
|
3266
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
2132
3267
|
this.handle.popErrorScope().then((error) => {
|
|
2133
3268
|
if (error) {
|
|
2134
3269
|
handler(error);
|
|
2135
3270
|
}
|
|
2136
3271
|
});
|
|
3272
|
+
if (profiler) {
|
|
3273
|
+
profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
|
|
3274
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
3275
|
+
}
|
|
2137
3276
|
}
|
|
2138
3277
|
// PRIVATE METHODS
|
|
2139
3278
|
_getInfo() {
|
|
@@ -2174,7 +3313,6 @@ var init_webgpu_device = __esm({
|
|
|
2174
3313
|
features.add("snorm16-renderable-webgl");
|
|
2175
3314
|
}
|
|
2176
3315
|
const WEBGPU_ALWAYS_FEATURES = [
|
|
2177
|
-
"timer-query-webgl",
|
|
2178
3316
|
"compilation-status-async-webgl",
|
|
2179
3317
|
"float32-renderable-webgl",
|
|
2180
3318
|
"float16-renderable-webgl",
|
|
@@ -2185,7 +3323,7 @@ var init_webgpu_device = __esm({
|
|
|
2185
3323
|
for (const feature of WEBGPU_ALWAYS_FEATURES) {
|
|
2186
3324
|
features.add(feature);
|
|
2187
3325
|
}
|
|
2188
|
-
return new
|
|
3326
|
+
return new import_core25.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
|
|
2189
3327
|
}
|
|
2190
3328
|
_getDeviceSpecificTextureFormatCapabilities(capabilities) {
|
|
2191
3329
|
const { format } = capabilities;
|
|
@@ -2213,8 +3351,8 @@ __export(dist_exports, {
|
|
|
2213
3351
|
module.exports = __toCommonJS(dist_exports);
|
|
2214
3352
|
|
|
2215
3353
|
// dist/adapter/webgpu-adapter.js
|
|
2216
|
-
var
|
|
2217
|
-
var WebGPUAdapter = class extends
|
|
3354
|
+
var import_core26 = require("@luma.gl/core");
|
|
3355
|
+
var WebGPUAdapter = class extends import_core26.Adapter {
|
|
2218
3356
|
/** type of device's created by this adapter */
|
|
2219
3357
|
type = "webgpu";
|
|
2220
3358
|
isSupported() {
|
|
@@ -2261,14 +3399,14 @@ var WebGPUAdapter = class extends import_core24.Adapter {
|
|
|
2261
3399
|
requiredLimits
|
|
2262
3400
|
});
|
|
2263
3401
|
const { WebGPUDevice: WebGPUDevice2 } = await Promise.resolve().then(() => (init_webgpu_device(), webgpu_device_exports));
|
|
2264
|
-
|
|
3402
|
+
import_core26.log.groupCollapsed(1, "WebGPUDevice created")();
|
|
2265
3403
|
try {
|
|
2266
3404
|
const device = new WebGPUDevice2(props, gpuDevice, adapter, adapterInfo);
|
|
2267
|
-
|
|
2268
|
-
|
|
3405
|
+
import_core26.log.probe(1, "Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")();
|
|
3406
|
+
import_core26.log.table(1, device.info)();
|
|
2269
3407
|
return device;
|
|
2270
3408
|
} finally {
|
|
2271
|
-
|
|
3409
|
+
import_core26.log.groupEnd(1)();
|
|
2272
3410
|
}
|
|
2273
3411
|
}
|
|
2274
3412
|
async attach(handle) {
|