@luma.gl/webgl 9.2.6 → 9.3.0-alpha.4
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/get-shader-layout-from-glsl.js +11 -8
- package/dist/adapter/helpers/get-shader-layout-from-glsl.js.map +1 -1
- package/dist/adapter/helpers/parse-shader-compiler-log.d.ts +1 -1
- package/dist/adapter/helpers/parse-shader-compiler-log.d.ts.map +1 -1
- package/dist/adapter/helpers/parse-shader-compiler-log.js +20 -0
- package/dist/adapter/helpers/parse-shader-compiler-log.js.map +1 -1
- package/dist/adapter/resources/webgl-command-buffer.d.ts +2 -3
- package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-command-buffer.js +9 -5
- package/dist/adapter/resources/webgl-command-buffer.js.map +1 -1
- package/dist/adapter/resources/webgl-fence.d.ts +14 -0
- package/dist/adapter/resources/webgl-fence.d.ts.map +1 -0
- package/dist/adapter/resources/webgl-fence.js +49 -0
- package/dist/adapter/resources/webgl-fence.js.map +1 -0
- package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-render-pass.js +9 -6
- package/dist/adapter/resources/webgl-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-render-pipeline.js +1 -2
- package/dist/adapter/resources/webgl-render-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgl-texture.d.ts +21 -4
- package/dist/adapter/resources/webgl-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-texture.js +148 -22
- package/dist/adapter/resources/webgl-texture.js.map +1 -1
- package/dist/adapter/resources/webgl-transform-feedback.js +5 -5
- package/dist/adapter/resources/webgl-transform-feedback.js.map +1 -1
- package/dist/adapter/webgl-adapter.d.ts.map +1 -1
- package/dist/adapter/webgl-adapter.js +22 -23
- package/dist/adapter/webgl-adapter.js.map +1 -1
- package/dist/adapter/webgl-canvas-context.d.ts +2 -2
- package/dist/adapter/webgl-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgl-canvas-context.js +16 -6
- package/dist/adapter/webgl-canvas-context.js.map +1 -1
- package/dist/adapter/webgl-device.d.ts +4 -2
- package/dist/adapter/webgl-device.d.ts.map +1 -1
- package/dist/adapter/webgl-device.js +39 -24
- package/dist/adapter/webgl-device.js.map +1 -1
- package/dist/context/debug/spector.d.ts.map +1 -1
- package/dist/context/debug/spector.js +4 -4
- package/dist/context/debug/spector.js.map +1 -1
- package/dist/context/debug/webgl-developer-tools.js +6 -6
- package/dist/context/debug/webgl-developer-tools.js.map +1 -1
- package/dist/context/helpers/create-browser-context.d.ts.map +1 -1
- package/dist/context/helpers/create-browser-context.js +46 -36
- package/dist/context/helpers/create-browser-context.js.map +1 -1
- package/dist/context/helpers/webgl-context-data.d.ts +5 -1
- package/dist/context/helpers/webgl-context-data.d.ts.map +1 -1
- package/dist/context/helpers/webgl-context-data.js +9 -10
- package/dist/context/helpers/webgl-context-data.js.map +1 -1
- package/dist/context/parameters/unified-parameter-api.d.ts +1 -1
- package/dist/context/parameters/unified-parameter-api.js +2 -2
- package/dist/context/parameters/unified-parameter-api.js.map +1 -1
- package/dist/context/state-tracker/webgl-state-tracker.js +2 -2
- package/dist/context/state-tracker/webgl-state-tracker.js.map +1 -1
- package/dist/dist.dev.js +567 -318
- package/dist/dist.min.js +2 -2
- package/dist/index.cjs +550 -314
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/fill-array.js +1 -1
- package/dist/utils/fill-array.js.map +1 -1
- package/package.json +5 -5
- package/src/adapter/helpers/get-shader-layout-from-glsl.ts +11 -9
- package/src/adapter/helpers/parse-shader-compiler-log.ts +23 -1
- package/src/adapter/resources/webgl-command-buffer.ts +18 -22
- package/src/adapter/resources/webgl-fence.ts +55 -0
- package/src/adapter/resources/webgl-render-pass.ts +10 -6
- package/src/adapter/resources/webgl-render-pipeline.ts +1 -2
- package/src/adapter/resources/webgl-texture.ts +209 -37
- package/src/adapter/resources/webgl-transform-feedback.ts +5 -5
- package/src/adapter/webgl-adapter.ts +26 -24
- package/src/adapter/webgl-canvas-context.ts +19 -8
- package/src/adapter/webgl-device.ts +41 -29
- package/src/context/debug/spector.ts +4 -4
- package/src/context/debug/webgl-developer-tools.ts +15 -6
- package/src/context/helpers/create-browser-context.ts +54 -43
- package/src/context/helpers/webgl-context-data.ts +17 -11
- package/src/context/parameters/unified-parameter-api.ts +2 -2
- package/src/context/state-tracker/webgl-state-tracker.ts +2 -2
- package/src/index.ts +1 -0
- package/src/utils/fill-array.ts +1 -1
|
@@ -2,33 +2,45 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type Device,
|
|
9
|
+
type TextureProps,
|
|
10
|
+
type TextureViewProps,
|
|
11
|
+
type Sampler,
|
|
12
|
+
type SamplerProps,
|
|
13
|
+
type CopyExternalImageOptions,
|
|
14
|
+
type CopyImageDataOptions,
|
|
15
|
+
type TextureReadOptions,
|
|
16
|
+
type TextureWriteOptions,
|
|
17
|
+
type TextureFormat,
|
|
18
|
+
type TypedArray,
|
|
19
|
+
Buffer,
|
|
20
|
+
Texture,
|
|
21
|
+
log
|
|
14
22
|
} from '@luma.gl/core';
|
|
15
|
-
|
|
23
|
+
|
|
16
24
|
import {
|
|
25
|
+
GLSamplerParameters,
|
|
26
|
+
GLValueParameters,
|
|
17
27
|
GL,
|
|
18
28
|
GLTextureTarget,
|
|
19
29
|
GLTextureCubeMapTarget,
|
|
20
30
|
GLTexelDataFormat,
|
|
21
|
-
GLPixelType
|
|
22
|
-
// GLDataType,
|
|
23
|
-
GLSamplerParameters,
|
|
24
|
-
GLValueParameters
|
|
31
|
+
GLPixelType
|
|
25
32
|
} from '@luma.gl/constants';
|
|
33
|
+
|
|
26
34
|
import {getTextureFormatWebGL} from '../converters/webgl-texture-table';
|
|
27
35
|
import {convertSamplerParametersToWebGL} from '../converters/sampler-parameters';
|
|
28
36
|
import {withGLParameters} from '../../context/state-tracker/with-parameters';
|
|
29
37
|
import {WebGLDevice} from '../webgl-device';
|
|
38
|
+
import {WEBGLFramebuffer} from './webgl-framebuffer';
|
|
30
39
|
import {WEBGLSampler} from './webgl-sampler';
|
|
31
40
|
import {WEBGLTextureView} from './webgl-texture-view';
|
|
41
|
+
import {convertDataTypeToGLDataType} from '../converters/webgl-shadertypes';
|
|
42
|
+
import {convertGLDataTypeToDataType} from '../converters/shader-formats';
|
|
43
|
+
import {getTypedArrayConstructor, getDataType} from '@luma.gl/core';
|
|
32
44
|
|
|
33
45
|
/**
|
|
34
46
|
* WebGL... the texture API from hell... hopefully made simpler
|
|
@@ -65,9 +77,12 @@ export class WEBGLTexture extends Texture {
|
|
|
65
77
|
// state
|
|
66
78
|
/** Texture binding slot - TODO - move to texture view? */
|
|
67
79
|
_textureUnit: number = 0;
|
|
80
|
+
/** Chached framebuffer */
|
|
81
|
+
_framebuffer: WEBGLFramebuffer | null = null;
|
|
68
82
|
|
|
69
83
|
constructor(device: Device, props: TextureProps) {
|
|
70
|
-
|
|
84
|
+
// const byteAlignment = this._getRowByteAlignment(props.format, props.width);
|
|
85
|
+
super(device, props, {byteAlignment: 1});
|
|
71
86
|
|
|
72
87
|
this.device = device as WebGLDevice;
|
|
73
88
|
this.gl = this.device.gl;
|
|
@@ -119,6 +134,10 @@ export class WEBGLTexture extends Texture {
|
|
|
119
134
|
|
|
120
135
|
override destroy(): void {
|
|
121
136
|
if (this.handle) {
|
|
137
|
+
// Destroy any cached framebuffer
|
|
138
|
+
this._framebuffer?.destroy();
|
|
139
|
+
this._framebuffer = null;
|
|
140
|
+
|
|
122
141
|
this.gl.deleteTexture(this.handle);
|
|
123
142
|
this.removeStats();
|
|
124
143
|
this.trackDeallocatedMemory('Texture');
|
|
@@ -138,12 +157,51 @@ export class WEBGLTexture extends Texture {
|
|
|
138
157
|
this._setSamplerParameters(parameters);
|
|
139
158
|
}
|
|
140
159
|
|
|
160
|
+
copyExternalImage(options_: CopyExternalImageOptions): {width: number; height: number} {
|
|
161
|
+
const options = this._normalizeCopyExternalImageOptions(options_);
|
|
162
|
+
|
|
163
|
+
if (options.sourceX || options.sourceY) {
|
|
164
|
+
// requires copyTexSubImage2D from a framebuffer'
|
|
165
|
+
throw new Error('WebGL does not support sourceX/sourceY)');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const {glFormat, glType} = this;
|
|
169
|
+
const {image, depth, mipLevel, x, y, z, width, height} = options;
|
|
170
|
+
|
|
171
|
+
// WebGL cube maps specify faces by overriding target instead of using the z parameter
|
|
172
|
+
const glTarget = getWebGLCubeFaceTarget(this.glTarget, this.dimension, z);
|
|
173
|
+
const glParameters: GLValueParameters = options.flipY ? {[GL.UNPACK_FLIP_Y_WEBGL]: true} : {};
|
|
174
|
+
|
|
175
|
+
this.gl.bindTexture(this.glTarget, this.handle);
|
|
176
|
+
|
|
177
|
+
withGLParameters(this.gl, glParameters, () => {
|
|
178
|
+
switch (this.dimension) {
|
|
179
|
+
case '2d':
|
|
180
|
+
case 'cube':
|
|
181
|
+
// prettier-ignore
|
|
182
|
+
this.gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, image);
|
|
183
|
+
break;
|
|
184
|
+
case '2d-array':
|
|
185
|
+
case '3d':
|
|
186
|
+
// prettier-ignore
|
|
187
|
+
this.gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, glType, image);
|
|
188
|
+
break;
|
|
189
|
+
default:
|
|
190
|
+
// Can never happen in WebGL
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
this.gl.bindTexture(this.glTarget, null);
|
|
195
|
+
|
|
196
|
+
return {width: options.width, height: options.height};
|
|
197
|
+
}
|
|
198
|
+
|
|
141
199
|
copyImageData(options_: CopyImageDataOptions): void {
|
|
142
200
|
const options = this._normalizeCopyImageDataOptions(options_);
|
|
143
201
|
|
|
144
202
|
const typedArray = options.data as TypedArray;
|
|
145
|
-
const {width, height, depth} =
|
|
146
|
-
const {mipLevel = 0, byteOffset = 0, x = 0, y = 0
|
|
203
|
+
const {width, height, depth, z = 0} = options;
|
|
204
|
+
const {mipLevel = 0, byteOffset = 0, x = 0, y = 0} = options;
|
|
147
205
|
const {glFormat, glType, compressed} = this;
|
|
148
206
|
|
|
149
207
|
// Target used for face updates, but not for binding
|
|
@@ -164,12 +222,13 @@ export class WEBGLTexture extends Texture {
|
|
|
164
222
|
|
|
165
223
|
const glParameters: GLValueParameters = !this.compressed
|
|
166
224
|
? {
|
|
225
|
+
[GL.UNPACK_ALIGNMENT]: this.byteAlignment,
|
|
167
226
|
...(unpackRowLength !== undefined ? {[GL.UNPACK_ROW_LENGTH]: unpackRowLength} : {}),
|
|
168
227
|
[GL.UNPACK_IMAGE_HEIGHT]: options.rowsPerImage
|
|
169
228
|
}
|
|
170
229
|
: {};
|
|
171
230
|
|
|
172
|
-
this.gl.bindTexture(glTarget, this.handle);
|
|
231
|
+
this.gl.bindTexture(this.glTarget, this.handle);
|
|
173
232
|
|
|
174
233
|
withGLParameters(this.gl, glParameters, () => {
|
|
175
234
|
switch (this.dimension) {
|
|
@@ -198,51 +257,163 @@ export class WEBGLTexture extends Texture {
|
|
|
198
257
|
}
|
|
199
258
|
});
|
|
200
259
|
|
|
201
|
-
this.gl.bindTexture(glTarget, null);
|
|
260
|
+
this.gl.bindTexture(this.glTarget, null);
|
|
202
261
|
}
|
|
203
262
|
|
|
204
|
-
|
|
205
|
-
|
|
263
|
+
readBuffer(options: TextureReadOptions = {}, buffer?: Buffer): Buffer {
|
|
264
|
+
throw new Error('readBuffer not implemented');
|
|
265
|
+
}
|
|
206
266
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
267
|
+
async readDataAsync(options: TextureReadOptions = {}): Promise<ArrayBuffer> {
|
|
268
|
+
return this.readDataSyncWebGL(options);
|
|
269
|
+
}
|
|
211
270
|
|
|
212
|
-
|
|
213
|
-
const {image, depth, mipLevel, x, y, z, width, height} = options;
|
|
271
|
+
writeBuffer(buffer: Buffer, options_: TextureWriteOptions = {}) {}
|
|
214
272
|
|
|
215
|
-
|
|
273
|
+
writeData(data: ArrayBuffer | ArrayBufferView, options_: TextureWriteOptions = {}): void {
|
|
274
|
+
const options = this._normalizeTextureWriteOptions(options_);
|
|
275
|
+
|
|
276
|
+
const typedArray = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
|
|
277
|
+
const {} = this;
|
|
278
|
+
const {width, height, mipLevel, x, y, z} = options;
|
|
279
|
+
const {glFormat, glType, compressed} = this;
|
|
280
|
+
const depth = 0; // TODO - fix
|
|
216
281
|
const glTarget = getWebGLCubeFaceTarget(this.glTarget, this.dimension, depth);
|
|
217
|
-
const glParameters: GLValueParameters = options.flipY ? {[GL.UNPACK_FLIP_Y_WEBGL]: true} : {};
|
|
218
282
|
|
|
219
|
-
|
|
283
|
+
// const byteOffset = 0;
|
|
284
|
+
// const {bytesPerRow, rowsPerImage} = this.computeMemoryLayout(options);
|
|
285
|
+
|
|
286
|
+
const glParameters: GLValueParameters = !this.compressed
|
|
287
|
+
? {
|
|
288
|
+
// WebGL does not require byte alignment, but allows it to be specified
|
|
289
|
+
[GL.UNPACK_ALIGNMENT]: this.byteAlignment
|
|
290
|
+
// [GL.UNPACK_ROW_LENGTH]: bytesPerRow,
|
|
291
|
+
// [GL.UNPACK_IMAGE_HEIGHT]: rowsPerImage
|
|
292
|
+
}
|
|
293
|
+
: {};
|
|
294
|
+
|
|
295
|
+
this.gl.bindTexture(glTarget, this.handle);
|
|
296
|
+
this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
|
|
220
297
|
|
|
221
298
|
withGLParameters(this.gl, glParameters, () => {
|
|
222
299
|
switch (this.dimension) {
|
|
223
300
|
case '2d':
|
|
224
301
|
case 'cube':
|
|
225
|
-
|
|
226
|
-
|
|
302
|
+
if (compressed) {
|
|
303
|
+
// prettier-ignore
|
|
304
|
+
this.gl.compressedTexSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, typedArray);
|
|
305
|
+
} else {
|
|
306
|
+
// prettier-ignore
|
|
307
|
+
this.gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, typedArray);
|
|
308
|
+
}
|
|
227
309
|
break;
|
|
228
310
|
case '2d-array':
|
|
229
311
|
case '3d':
|
|
230
|
-
|
|
231
|
-
|
|
312
|
+
if (compressed) {
|
|
313
|
+
// prettier-ignore
|
|
314
|
+
this.gl.compressedTexSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, typedArray);
|
|
315
|
+
} else {
|
|
316
|
+
// prettier-ignore
|
|
317
|
+
this.gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, glType, typedArray);
|
|
318
|
+
}
|
|
232
319
|
break;
|
|
233
320
|
default:
|
|
234
321
|
// Can never happen in WebGL
|
|
235
322
|
}
|
|
236
323
|
});
|
|
237
324
|
|
|
238
|
-
this.gl.bindTexture(
|
|
325
|
+
this.gl.bindTexture(glTarget, null);
|
|
326
|
+
}
|
|
239
327
|
|
|
240
|
-
|
|
328
|
+
// IMPLEMENTATION SPECIFIC
|
|
329
|
+
|
|
330
|
+
/** @todo - for now we always use 1 for maximum compatibility, we can fine tune later */
|
|
331
|
+
private _getRowByteAlignment(format: TextureFormat, width: number): 1 | 2 | 4 | 8 {
|
|
332
|
+
// For best texture data read/write performance, calculate the biggest pack/unpack alignment
|
|
333
|
+
// that fits with the provided texture row byte length
|
|
334
|
+
// Note: Any RGBA or 32 bit type will be at least 4 bytes, which should result in good performance.
|
|
335
|
+
// const info = this.device.getTextureFormatInfo(format);
|
|
336
|
+
// const rowByteLength = width * info.bytesPerPixel;
|
|
337
|
+
// if (rowByteLength % 8 === 0) return 8;
|
|
338
|
+
// if (rowByteLength % 4 === 0) return 4;
|
|
339
|
+
// if (rowByteLength % 2 === 0) return 2;
|
|
340
|
+
return 1;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Wraps a given texture into a framebuffer object, that can be further used
|
|
345
|
+
* to read data from the texture object.
|
|
346
|
+
*/
|
|
347
|
+
_getFramebuffer() {
|
|
348
|
+
this._framebuffer ||= this.device.createFramebuffer({
|
|
349
|
+
id: `framebuffer-for-${this.id}`,
|
|
350
|
+
width: this.width,
|
|
351
|
+
height: this.height,
|
|
352
|
+
colorAttachments: [this]
|
|
353
|
+
});
|
|
354
|
+
return this._framebuffer;
|
|
241
355
|
}
|
|
242
356
|
|
|
243
357
|
// WEBGL SPECIFIC
|
|
244
358
|
|
|
245
|
-
|
|
359
|
+
override readDataSyncWebGL(options_: TextureReadOptions = {}): ArrayBuffer {
|
|
360
|
+
const options = this._normalizeTextureReadOptions(options_);
|
|
361
|
+
|
|
362
|
+
const memoryLayout = this.computeMemoryLayout(options);
|
|
363
|
+
|
|
364
|
+
// const formatInfo = getTextureFormatInfo(format);
|
|
365
|
+
// Allocate pixel array if not already available, using supplied type
|
|
366
|
+
const shaderType = convertGLDataTypeToDataType(this.glType);
|
|
367
|
+
|
|
368
|
+
const ArrayType = getTypedArrayConstructor(shaderType);
|
|
369
|
+
// const components = glFormatToComponents(this.glFormat);
|
|
370
|
+
// TODO - check for composite type (components = 1).
|
|
371
|
+
|
|
372
|
+
const targetArray = new ArrayType(memoryLayout.byteLength) as
|
|
373
|
+
| Uint8Array
|
|
374
|
+
| Uint16Array
|
|
375
|
+
| Float32Array;
|
|
376
|
+
|
|
377
|
+
// Pixel array available, if necessary, deduce type from it.
|
|
378
|
+
const signedType = getDataType(targetArray);
|
|
379
|
+
const sourceType = convertDataTypeToGLDataType(signedType);
|
|
380
|
+
|
|
381
|
+
// There is a lot of hedging in the WebGL2 spec about what formats are guaranteed to be readable
|
|
382
|
+
// (It should always be possible to read RGBA/UNSIGNED_BYTE, but most other combinations are not guaranteed)
|
|
383
|
+
// Querying is possible but expensive:
|
|
384
|
+
// const {device} = framebuffer;
|
|
385
|
+
// texture.glReadFormat ||= gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
|
|
386
|
+
// texture.glReadType ||= gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
|
|
387
|
+
// console.log('params', device.getGLKey(texture.glReadFormat), device.getGLKey(texture.glReadType));
|
|
388
|
+
|
|
389
|
+
const framebuffer = this._getFramebuffer();
|
|
390
|
+
|
|
391
|
+
// Note: luma.gl overrides bindFramebuffer so that we can reliably restore the previous framebuffer (this is the only function for which we do that)
|
|
392
|
+
const prevHandle = this.gl.bindFramebuffer(
|
|
393
|
+
GL.FRAMEBUFFER,
|
|
394
|
+
framebuffer.handle
|
|
395
|
+
) as unknown as WebGLFramebuffer | null;
|
|
396
|
+
|
|
397
|
+
// Select the color attachment to read from
|
|
398
|
+
this.gl.readBuffer(GL.COLOR_ATTACHMENT0);
|
|
399
|
+
this.gl.readPixels(
|
|
400
|
+
options.x,
|
|
401
|
+
options.y,
|
|
402
|
+
options.width,
|
|
403
|
+
options.height,
|
|
404
|
+
this.glFormat,
|
|
405
|
+
sourceType,
|
|
406
|
+
targetArray
|
|
407
|
+
);
|
|
408
|
+
this.gl.bindFramebuffer(GL.FRAMEBUFFER, prevHandle || null);
|
|
409
|
+
|
|
410
|
+
return targetArray.buffer as ArrayBuffer;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @note - this is used by the DynamicTexture class to generate mipmaps on WebGL
|
|
415
|
+
*/
|
|
416
|
+
override generateMipmapsWebGL(options?: {force?: boolean}): void {
|
|
246
417
|
const isFilterableAndRenderable =
|
|
247
418
|
this.device.isTextureFormatRenderable(this.props.format) &&
|
|
248
419
|
this.device.isTextureFormatFilterable(this.props.format);
|
|
@@ -312,6 +483,7 @@ export class WEBGLTexture extends Texture {
|
|
|
312
483
|
|
|
313
484
|
this.gl.bindTexture(this.glTarget, null);
|
|
314
485
|
}
|
|
486
|
+
|
|
315
487
|
_getActiveUnit(): number {
|
|
316
488
|
return this.gl.getParameter(GL.ACTIVE_TEXTURE) - GL.TEXTURE0;
|
|
317
489
|
}
|
|
@@ -69,8 +69,8 @@ export class WEBGLTransformFeedback extends TransformFeedback {
|
|
|
69
69
|
this.unusedBuffers = {};
|
|
70
70
|
|
|
71
71
|
this.bind(() => {
|
|
72
|
-
for (const bufferName
|
|
73
|
-
this.setBuffer(bufferName,
|
|
72
|
+
for (const [bufferName, buffer] of Object.entries(buffers)) {
|
|
73
|
+
this.setBuffer(bufferName, buffer);
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
}
|
|
@@ -99,7 +99,7 @@ export class WEBGLTransformFeedback extends TransformFeedback {
|
|
|
99
99
|
return this.buffers[locationOrName] || null;
|
|
100
100
|
}
|
|
101
101
|
const location = this._getVaryingIndex(locationOrName);
|
|
102
|
-
return
|
|
102
|
+
return this.buffers[location] ?? null;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
bind(funcOrHandle: (() => void) | WebGLTransformFeedback | null = this.handle) {
|
|
@@ -162,8 +162,8 @@ export class WEBGLTransformFeedback extends TransformFeedback {
|
|
|
162
162
|
* cannot be bound to 'TRANSFORM_FEEDBACK_BUFFER' target.
|
|
163
163
|
*/
|
|
164
164
|
protected _bindBuffers(): void {
|
|
165
|
-
for (const bufferIndex
|
|
166
|
-
const {buffer, byteLength, byteOffset} = this._getBufferRange(
|
|
165
|
+
for (const [bufferIndex, bufferEntry] of Object.entries(this.buffers)) {
|
|
166
|
+
const {buffer, byteLength, byteOffset} = this._getBufferRange(bufferEntry);
|
|
167
167
|
this._bindBuffer(Number(bufferIndex), buffer, byteOffset, byteLength);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -55,10 +55,9 @@ export class WebGLAdapter extends Adapter {
|
|
|
55
55
|
if (gl instanceof WebGLDevice) {
|
|
56
56
|
return gl;
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
return gl.device as WebGLDevice;
|
|
58
|
+
const existingDevice = WebGLDevice.getDeviceFromContext(gl as WebGL2RenderingContext | null);
|
|
59
|
+
if (existingDevice) {
|
|
60
|
+
return existingDevice;
|
|
62
61
|
}
|
|
63
62
|
if (!isWebGL(gl)) {
|
|
64
63
|
throw new Error('Invalid WebGL2RenderingContext');
|
|
@@ -78,40 +77,44 @@ export class WebGLAdapter extends Adapter {
|
|
|
78
77
|
async create(props: DeviceProps = {}): Promise<WebGLDevice> {
|
|
79
78
|
const {WebGLDevice} = await import('./webgl-device');
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const promises: Promise<unknown>[] = [];
|
|
80
|
+
const promises: Promise<unknown>[] = [];
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
// Load webgl and spector debug scripts from CDN if requested
|
|
83
|
+
if (props.debugWebGL || props.debug) {
|
|
84
|
+
promises.push(loadWebGLDeveloperTools());
|
|
85
|
+
}
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
if (props.debugSpectorJS) {
|
|
88
|
+
promises.push(loadSpectorJS(props));
|
|
89
|
+
}
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
91
|
+
// Wait for all the loads to settle before creating the context.
|
|
92
|
+
// The Device.create() functions are async, so in contrast to the constructor, we can `await` here.
|
|
93
|
+
const results = await Promise.allSettled(promises);
|
|
94
|
+
for (const result of results) {
|
|
95
|
+
if (result.status === 'rejected') {
|
|
96
|
+
log.error(`Failed to initialize debug libraries ${result.reason}`)();
|
|
101
97
|
}
|
|
98
|
+
}
|
|
102
99
|
|
|
100
|
+
try {
|
|
103
101
|
const device = new WebGLDevice(props);
|
|
104
102
|
|
|
103
|
+
log.groupCollapsed(LOG_LEVEL, `WebGLDevice ${device.id} created`)();
|
|
105
104
|
// Log some debug info about the newly created context
|
|
106
105
|
const message = `\
|
|
107
106
|
${device._reused ? 'Reusing' : 'Created'} device with WebGL2 ${device.props.debug ? 'debug ' : ''}context: \
|
|
108
107
|
${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContext.id}`;
|
|
109
108
|
log.probe(LOG_LEVEL, message)();
|
|
110
109
|
log.table(LOG_LEVEL, device.info)();
|
|
111
|
-
|
|
112
110
|
return device;
|
|
113
111
|
} finally {
|
|
114
112
|
log.groupEnd(LOG_LEVEL)();
|
|
113
|
+
log.info(
|
|
114
|
+
LOG_LEVEL,
|
|
115
|
+
`%cWebGL call tracing: luma.log.set('debug-webgl') `,
|
|
116
|
+
'color: white; background: blue; padding: 2px 6px; border-radius: 3px;'
|
|
117
|
+
)();
|
|
115
118
|
}
|
|
116
119
|
}
|
|
117
120
|
}
|
|
@@ -121,8 +124,7 @@ function isWebGL(gl: any): gl is WebGL2RenderingContext {
|
|
|
121
124
|
if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) {
|
|
122
125
|
return true;
|
|
123
126
|
}
|
|
124
|
-
|
|
125
|
-
return Boolean(gl && Number.isFinite(gl._version));
|
|
127
|
+
return Boolean(gl && typeof gl.createVertexArray === 'function');
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
export const webgl2Adapter = new WebGLAdapter();
|
|
@@ -27,16 +27,27 @@ export class WebGLCanvasContext extends CanvasContext {
|
|
|
27
27
|
|
|
28
28
|
// Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
|
|
29
29
|
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
|
|
30
|
-
this.
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
getCurrentFramebuffer(): WEBGLFramebuffer {
|
|
34
|
-
// Setting handle to null returns a reference to the default framebuffer
|
|
35
|
-
this._framebuffer = this._framebuffer || new WEBGLFramebuffer(this.device, {handle: null});
|
|
36
|
-
return this._framebuffer;
|
|
30
|
+
this._configureDevice();
|
|
37
31
|
}
|
|
38
32
|
|
|
39
33
|
// IMPLEMENTATION OF ABSTRACT METHODS
|
|
40
34
|
|
|
41
|
-
|
|
35
|
+
_configureDevice(): void {
|
|
36
|
+
const shouldResize =
|
|
37
|
+
this.drawingBufferWidth !== this._framebuffer?.width ||
|
|
38
|
+
this.drawingBufferHeight !== this._framebuffer?.height;
|
|
39
|
+
if (shouldResize) {
|
|
40
|
+
this._framebuffer?.resize([this.drawingBufferWidth, this.drawingBufferHeight]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_getCurrentFramebuffer(): WEBGLFramebuffer {
|
|
45
|
+
this._framebuffer ||= new WEBGLFramebuffer(this.device, {
|
|
46
|
+
id: 'canvas-context-framebuffer',
|
|
47
|
+
handle: null, // Setting handle to null returns a reference to the default WebGL framebuffer
|
|
48
|
+
width: this.drawingBufferWidth,
|
|
49
|
+
height: this.drawingBufferHeight
|
|
50
|
+
});
|
|
51
|
+
return this._framebuffer;
|
|
52
|
+
}
|
|
42
53
|
}
|
|
@@ -36,6 +36,7 @@ import {Device, CanvasContext, log} from '@luma.gl/core';
|
|
|
36
36
|
import type {GLExtensions} from '@luma.gl/constants';
|
|
37
37
|
import {WebGLStateTracker} from '../context/state-tracker/webgl-state-tracker';
|
|
38
38
|
import {createBrowserContext} from '../context/helpers/create-browser-context';
|
|
39
|
+
import {getWebGLContextData} from '../context/helpers/webgl-context-data';
|
|
39
40
|
import {getDeviceInfo} from './device-helpers/webgl-device-info';
|
|
40
41
|
import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
|
|
41
42
|
import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
|
|
@@ -57,6 +58,7 @@ import {WEBGLCommandBuffer} from './resources/webgl-command-buffer';
|
|
|
57
58
|
import {WEBGLVertexArray} from './resources/webgl-vertex-array';
|
|
58
59
|
import {WEBGLTransformFeedback} from './resources/webgl-transform-feedback';
|
|
59
60
|
import {WEBGLQuerySet} from './resources/webgl-query-set';
|
|
61
|
+
import {WEBGLFence} from './resources/webgl-fence';
|
|
60
62
|
|
|
61
63
|
import {readPixelsToArray, readPixelsToBuffer} from './helpers/webgl-texture-utils';
|
|
62
64
|
import {
|
|
@@ -69,6 +71,13 @@ import {getWebGLExtension} from '../context/helpers/webgl-extensions';
|
|
|
69
71
|
|
|
70
72
|
/** WebGPU style Device API for a WebGL context */
|
|
71
73
|
export class WebGLDevice extends Device {
|
|
74
|
+
static getDeviceFromContext(gl: WebGL2RenderingContext | null): WebGLDevice | null {
|
|
75
|
+
if (!gl) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
// @ts-expect-error Ingore WebGL2RenderingContext type
|
|
79
|
+
return gl.luma?.device ?? null;
|
|
80
|
+
}
|
|
72
81
|
// Public `Device` API
|
|
73
82
|
|
|
74
83
|
/** type of this device */
|
|
@@ -99,7 +108,7 @@ export class WebGLDevice extends Device {
|
|
|
99
108
|
_constants: (TypedArray | null)[];
|
|
100
109
|
|
|
101
110
|
/** State used by luma.gl classes - TODO - not used? */
|
|
102
|
-
readonly
|
|
111
|
+
readonly extensions!: GLExtensions;
|
|
103
112
|
_polyfilled: boolean = false;
|
|
104
113
|
|
|
105
114
|
/** Instance of Spector.js (if initialized) */
|
|
@@ -140,7 +149,8 @@ export class WebGLDevice extends Device {
|
|
|
140
149
|
// Note that this can be avoided in webgl2adapter.create() if
|
|
141
150
|
// DeviceProps._reuseDevices is set.
|
|
142
151
|
// @ts-expect-error device is attached to context
|
|
143
|
-
|
|
152
|
+
const existingContext = canvasContextProps.canvas?.gl ?? null;
|
|
153
|
+
let device: WebGLDevice | null = WebGLDevice.getDeviceFromContext(existingContext);
|
|
144
154
|
if (device) {
|
|
145
155
|
throw new Error(`WebGL context already attached to device ${device.id}`);
|
|
146
156
|
}
|
|
@@ -160,6 +170,9 @@ export class WebGLDevice extends Device {
|
|
|
160
170
|
if (props.powerPreference !== undefined) {
|
|
161
171
|
webglContextAttributes.powerPreference = props.powerPreference;
|
|
162
172
|
}
|
|
173
|
+
if (props.failIfMajorPerformanceCaveat !== undefined) {
|
|
174
|
+
webglContextAttributes.failIfMajorPerformanceCaveat = props.failIfMajorPerformanceCaveat;
|
|
175
|
+
}
|
|
163
176
|
|
|
164
177
|
// Check if we should attach to an externally created context or create a new context
|
|
165
178
|
const externalGLContext = this.props._handle as WebGL2RenderingContext | null;
|
|
@@ -186,8 +199,7 @@ export class WebGLDevice extends Device {
|
|
|
186
199
|
|
|
187
200
|
// Note that the browser will only create one WebGL context per canvas.
|
|
188
201
|
// This means that a newly created gl context may already have a device attached to it.
|
|
189
|
-
|
|
190
|
-
device = gl.device;
|
|
202
|
+
device = WebGLDevice.getDeviceFromContext(gl);
|
|
191
203
|
if (device) {
|
|
192
204
|
if (props._reuseDevices) {
|
|
193
205
|
log.log(
|
|
@@ -195,6 +207,9 @@ export class WebGLDevice extends Device {
|
|
|
195
207
|
`Not creating a new Device, instead returning a reference to Device ${device.id} already attached to WebGL context`,
|
|
196
208
|
device
|
|
197
209
|
)();
|
|
210
|
+
// Destroy the orphaned canvas context that was created above (line 149)
|
|
211
|
+
// to prevent its ResizeObserver from firing callbacks with undefined device
|
|
212
|
+
this.canvasContext.destroy();
|
|
198
213
|
device._reused = true;
|
|
199
214
|
return device;
|
|
200
215
|
}
|
|
@@ -210,18 +225,15 @@ export class WebGLDevice extends Device {
|
|
|
210
225
|
this.spectorJS = initializeSpectorJS({...this.props, gl: this.handle});
|
|
211
226
|
|
|
212
227
|
// Instrument context
|
|
213
|
-
|
|
214
|
-
//
|
|
215
|
-
|
|
228
|
+
const contextData = getWebGLContextData(this.handle);
|
|
229
|
+
contextData.device = this; // Update GL context: Link webgl context back to device
|
|
230
|
+
|
|
231
|
+
this.extensions = contextData.extensions || (contextData.extensions = {});
|
|
216
232
|
|
|
217
233
|
// initialize luma Device fields
|
|
218
|
-
this.info = getDeviceInfo(this.gl, this.
|
|
234
|
+
this.info = getDeviceInfo(this.gl, this.extensions);
|
|
219
235
|
this.limits = new WebGLDeviceLimits(this.gl);
|
|
220
|
-
this.features = new WebGLDeviceFeatures(
|
|
221
|
-
this.gl,
|
|
222
|
-
this._extensions,
|
|
223
|
-
this.props._disabledFeatures
|
|
224
|
-
);
|
|
236
|
+
this.features = new WebGLDeviceFeatures(this.gl, this.extensions, this.props._disabledFeatures);
|
|
225
237
|
if (this.props._initializeFeatures) {
|
|
226
238
|
this.features.initializeFeatures();
|
|
227
239
|
}
|
|
@@ -232,15 +244,14 @@ export class WebGLDevice extends Device {
|
|
|
232
244
|
});
|
|
233
245
|
glState.trackState(this.gl, {copyState: false});
|
|
234
246
|
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
this.gl = makeDebugContext(this.gl, {debugWebGL, traceWebGL});
|
|
247
|
+
// props.debug - instrument the WebGL context with Khronos debug tools
|
|
248
|
+
// props.debugWebGL - activate WebGL context tracing, force log level to at least 1
|
|
249
|
+
if (props.debug || props.debugWebGL) {
|
|
250
|
+
this.gl = makeDebugContext(this.gl, {debugWebGL: true, traceWebGL: props.debugWebGL});
|
|
240
251
|
log.warn('WebGL debug mode activated. Performance reduced.')();
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
252
|
+
}
|
|
253
|
+
if (props.debugWebGL) {
|
|
254
|
+
log.level = Math.max(log.level, 1);
|
|
244
255
|
}
|
|
245
256
|
|
|
246
257
|
this.commandEncoder = new WEBGLCommandEncoder(this, {id: `${this}-command-encoder`});
|
|
@@ -264,7 +275,8 @@ export class WebGLDevice extends Device {
|
|
|
264
275
|
// Therefore we must do nothing in destroy() if props._reuseDevices is true
|
|
265
276
|
if (!this.props._reuseDevices && !this._reused) {
|
|
266
277
|
// Delete the reference to the device that we store on the WebGL context
|
|
267
|
-
|
|
278
|
+
const contextData = getWebGLContextData(this.handle);
|
|
279
|
+
contextData.device = null;
|
|
268
280
|
}
|
|
269
281
|
}
|
|
270
282
|
|
|
@@ -274,10 +286,6 @@ export class WebGLDevice extends Device {
|
|
|
274
286
|
|
|
275
287
|
// IMPLEMENTATION OF ABSTRACT DEVICE
|
|
276
288
|
|
|
277
|
-
getTextureByteAlignment(): number {
|
|
278
|
-
return 4;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
289
|
createCanvasContext(props?: CanvasContextProps): CanvasContext {
|
|
282
290
|
throw new Error('WebGL only supports a single canvas');
|
|
283
291
|
}
|
|
@@ -319,6 +327,10 @@ export class WebGLDevice extends Device {
|
|
|
319
327
|
return new WEBGLQuerySet(this, props);
|
|
320
328
|
}
|
|
321
329
|
|
|
330
|
+
override createFence(): WEBGLFence {
|
|
331
|
+
return new WEBGLFence(this);
|
|
332
|
+
}
|
|
333
|
+
|
|
322
334
|
createRenderPipeline(props: RenderPipelineProps): WEBGLRenderPipeline {
|
|
323
335
|
return new WEBGLRenderPipeline(this, props);
|
|
324
336
|
}
|
|
@@ -406,7 +418,7 @@ export class WebGLDevice extends Device {
|
|
|
406
418
|
override _getDeviceSpecificTextureFormatCapabilities(
|
|
407
419
|
capabilities: DeviceTextureFormatCapabilities
|
|
408
420
|
): DeviceTextureFormatCapabilities {
|
|
409
|
-
return getTextureFormatCapabilitiesWebGL(this.gl, capabilities, this.
|
|
421
|
+
return getTextureFormatCapabilitiesWebGL(this.gl, capabilities, this.extensions);
|
|
410
422
|
}
|
|
411
423
|
|
|
412
424
|
//
|
|
@@ -509,8 +521,8 @@ export class WebGLDevice extends Device {
|
|
|
509
521
|
|
|
510
522
|
/** Ensure extensions are only requested once */
|
|
511
523
|
getExtension(name: keyof GLExtensions): GLExtensions {
|
|
512
|
-
getWebGLExtension(this.gl, name, this.
|
|
513
|
-
return this.
|
|
524
|
+
getWebGLExtension(this.gl, name, this.extensions);
|
|
525
|
+
return this.extensions;
|
|
514
526
|
}
|
|
515
527
|
|
|
516
528
|
// INTERNAL SUPPORT METHODS FOR WEBGL RESOURCES
|