@luma.gl/webgl 9.3.0-alpha.2 → 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.
Files changed (64) hide show
  1. package/dist/adapter/helpers/get-shader-layout-from-glsl.js +11 -8
  2. package/dist/adapter/helpers/get-shader-layout-from-glsl.js.map +1 -1
  3. package/dist/adapter/helpers/parse-shader-compiler-log.d.ts +1 -1
  4. package/dist/adapter/helpers/parse-shader-compiler-log.d.ts.map +1 -1
  5. package/dist/adapter/helpers/parse-shader-compiler-log.js +20 -0
  6. package/dist/adapter/helpers/parse-shader-compiler-log.js.map +1 -1
  7. package/dist/adapter/resources/webgl-command-buffer.d.ts +2 -3
  8. package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
  9. package/dist/adapter/resources/webgl-command-buffer.js +9 -5
  10. package/dist/adapter/resources/webgl-command-buffer.js.map +1 -1
  11. package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
  12. package/dist/adapter/resources/webgl-render-pass.js +5 -0
  13. package/dist/adapter/resources/webgl-render-pass.js.map +1 -1
  14. package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
  15. package/dist/adapter/resources/webgl-render-pipeline.js +1 -2
  16. package/dist/adapter/resources/webgl-render-pipeline.js.map +1 -1
  17. package/dist/adapter/resources/webgl-transform-feedback.js +5 -5
  18. package/dist/adapter/resources/webgl-transform-feedback.js.map +1 -1
  19. package/dist/adapter/webgl-adapter.d.ts.map +1 -1
  20. package/dist/adapter/webgl-adapter.js +3 -4
  21. package/dist/adapter/webgl-adapter.js.map +1 -1
  22. package/dist/adapter/webgl-device.d.ts +2 -1
  23. package/dist/adapter/webgl-device.d.ts.map +1 -1
  24. package/dist/adapter/webgl-device.js +25 -11
  25. package/dist/adapter/webgl-device.js.map +1 -1
  26. package/dist/context/debug/spector.d.ts.map +1 -1
  27. package/dist/context/debug/spector.js +4 -4
  28. package/dist/context/debug/spector.js.map +1 -1
  29. package/dist/context/debug/webgl-developer-tools.js +2 -0
  30. package/dist/context/debug/webgl-developer-tools.js.map +1 -1
  31. package/dist/context/helpers/create-browser-context.d.ts.map +1 -1
  32. package/dist/context/helpers/create-browser-context.js +6 -8
  33. package/dist/context/helpers/create-browser-context.js.map +1 -1
  34. package/dist/context/helpers/webgl-context-data.d.ts +5 -1
  35. package/dist/context/helpers/webgl-context-data.d.ts.map +1 -1
  36. package/dist/context/helpers/webgl-context-data.js +9 -10
  37. package/dist/context/helpers/webgl-context-data.js.map +1 -1
  38. package/dist/context/parameters/unified-parameter-api.d.ts +1 -1
  39. package/dist/context/parameters/unified-parameter-api.js +2 -2
  40. package/dist/context/parameters/unified-parameter-api.js.map +1 -1
  41. package/dist/context/state-tracker/webgl-state-tracker.js +2 -2
  42. package/dist/context/state-tracker/webgl-state-tracker.js.map +1 -1
  43. package/dist/dist.dev.js +104 -49
  44. package/dist/dist.min.js +2 -2
  45. package/dist/index.cjs +108 -44
  46. package/dist/index.cjs.map +4 -4
  47. package/dist/utils/fill-array.js +1 -1
  48. package/dist/utils/fill-array.js.map +1 -1
  49. package/package.json +4 -4
  50. package/src/adapter/helpers/get-shader-layout-from-glsl.ts +11 -9
  51. package/src/adapter/helpers/parse-shader-compiler-log.ts +23 -1
  52. package/src/adapter/resources/webgl-command-buffer.ts +18 -22
  53. package/src/adapter/resources/webgl-render-pass.ts +6 -0
  54. package/src/adapter/resources/webgl-render-pipeline.ts +1 -2
  55. package/src/adapter/resources/webgl-transform-feedback.ts +5 -5
  56. package/src/adapter/webgl-adapter.ts +3 -4
  57. package/src/adapter/webgl-device.ts +26 -15
  58. package/src/context/debug/spector.ts +4 -4
  59. package/src/context/debug/webgl-developer-tools.ts +2 -0
  60. package/src/context/helpers/create-browser-context.ts +8 -8
  61. package/src/context/helpers/webgl-context-data.ts +17 -11
  62. package/src/context/parameters/unified-parameter-api.ts +2 -2
  63. package/src/context/state-tracker/webgl-state-tracker.ts +2 -2
  64. package/src/utils/fill-array.ts +1 -1
@@ -8,7 +8,7 @@ export function fillArray(options) {
8
8
  const total = count * length;
9
9
  let copied = 0;
10
10
  for (let i = start; copied < length; copied++) {
11
- target[i++] = source[copied];
11
+ target[i++] = source[copied] ?? 0;
12
12
  }
13
13
  while (copied < total) {
14
14
  // If we have copied less than half, copy everything we got
@@ -1 +1 @@
1
- {"version":3,"file":"fill-array.js","sourceRoot":"","sources":["../../src/utils/fill-array.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,+BAA+B;AAC/B,oCAAoC;AAIpC,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,OAKzB;IACC,MAAM,EAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAC,GAAG,OAAO,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,MAAM,GAAG,KAAK,EAAE,CAAC;QACtB,2DAA2D;QAC3D,uCAAuC;QACvC,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,UAAU,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;YACzD,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,UAAU,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;YACjE,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"fill-array.js","sourceRoot":"","sources":["../../src/utils/fill-array.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,+BAA+B;AAC/B,oCAAoC;AAIpC,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,OAKzB;IACC,MAAM,EAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAC,GAAG,OAAO,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,MAAM,GAAG,KAAK,EAAE,CAAC;QACtB,2DAA2D;QAC3D,uCAAuC;QACvC,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,UAAU,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;YACzD,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,UAAU,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;YACjE,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luma.gl/webgl",
3
- "version": "9.3.0-alpha.2",
3
+ "version": "9.3.0-alpha.4",
4
4
  "description": "WebGL2 adapter for the luma.gl core API",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,9 +43,9 @@
43
43
  "@luma.gl/core": "9.2.0-alpha.6"
44
44
  },
45
45
  "dependencies": {
46
- "@luma.gl/constants": "9.3.0-alpha.2",
46
+ "@luma.gl/constants": "9.3.0-alpha.4",
47
47
  "@math.gl/types": "^4.1.0",
48
- "@probe.gl/env": "^4.0.8"
48
+ "@probe.gl/env": "^4.1.1"
49
49
  },
50
- "gitHead": "7fedf8d8902f58490a4ffca9a873daee3c732f24"
50
+ "gitHead": "7486e7b0377fb6ab961b4499828681bede60f3b1"
51
51
  }
@@ -10,7 +10,7 @@ import type {
10
10
  VaryingBinding,
11
11
  AttributeShaderType
12
12
  } from '@luma.gl/core';
13
- import {getVariableShaderTypeInfo} from '@luma.gl/core';
13
+ import {getVariableShaderTypeInfo, assertDefined} from '@luma.gl/core';
14
14
 
15
15
  import {GL, GLUniformType} from '@luma.gl/constants';
16
16
  import {
@@ -252,7 +252,11 @@ function readUniformBlocks(
252
252
  // ); // Array of GLint indicating the strides between columns of a column-major matrix or a row-major matrix.
253
253
  // const uniformRowMajor = gl.getActiveUniforms(program, uniformIndices, GL.UNIFORM_IS_ROW_MAJOR);
254
254
  for (let i = 0; i < blockInfo.uniformCount; ++i) {
255
- const activeInfo = gl.getActiveUniform(program, uniformIndices[i]);
255
+ const uniformIndex = uniformIndices[i];
256
+ if (uniformIndex === undefined) {
257
+ continue;
258
+ }
259
+ const activeInfo = gl.getActiveUniform(program, uniformIndex);
256
260
  if (!activeInfo) {
257
261
  throw new Error('activeInfo');
258
262
  }
@@ -314,13 +318,11 @@ function parseUniformName(name: string): {name: string; length: number; isArray:
314
318
  // if array name then clean the array brackets
315
319
  const UNIFORM_NAME_REGEXP = /([^[]*)(\[[0-9]+\])?/;
316
320
  const matches = UNIFORM_NAME_REGEXP.exec(name);
317
- if (!matches || matches.length < 2) {
318
- throw new Error(`Failed to parse GLSL uniform name ${name}`);
319
- }
320
-
321
+ const uniformName = assertDefined(matches?.[1], `Failed to parse GLSL uniform name ${name}`);
321
322
  return {
322
- name: matches[1],
323
- length: matches[2] ? 1 : 0,
324
- isArray: Boolean(matches[2])
323
+ name: uniformName,
324
+ // TODO - is this a bug, shouldn't we return the value?
325
+ length: matches?.[2] ? 1 : 0,
326
+ isArray: Boolean(matches?.[2])
325
327
  };
326
328
  }
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {CompilerMessage} from '@luma.gl/core';
5
+ import {type CompilerMessage} from '@luma.gl/core';
6
6
 
7
7
  /**
8
8
  * Parse a WebGL-format GLSL compilation log into an array of WebGPU style message records.
@@ -20,21 +20,43 @@ export function parseShaderCompilerLog(errLog: string): readonly CompilerMessage
20
20
  continue; // eslint-disable-line no-continue
21
21
  }
22
22
 
23
+ const lineWithTrimmedWhitespace = line.trim();
24
+
23
25
  const segments: string[] = line.split(':');
26
+ const trimmedMessageType = segments[0]?.trim();
24
27
 
25
28
  // Check for messages with no line information `ERROR: unsupported shader version`
26
29
  if (segments.length === 2) {
27
30
  const [messageType, message] = segments;
31
+ if (!messageType || !message) {
32
+ messages.push({
33
+ message: lineWithTrimmedWhitespace,
34
+ type: getMessageType(trimmedMessageType || 'info'),
35
+ lineNum: 0,
36
+ linePos: 0
37
+ });
38
+ continue; // eslint-disable-line no-continue
39
+ }
28
40
  messages.push({
29
41
  message: message.trim(),
30
42
  type: getMessageType(messageType),
31
43
  lineNum: 0,
32
44
  linePos: 0
33
45
  });
46
+
34
47
  continue; // eslint-disable-line no-continue
35
48
  }
36
49
 
37
50
  const [messageType, linePosition, lineNumber, ...rest] = segments;
51
+ if (!messageType || !linePosition || !lineNumber) {
52
+ messages.push({
53
+ message: segments.slice(1).join(':').trim() || lineWithTrimmedWhitespace,
54
+ type: getMessageType(trimmedMessageType || 'info'),
55
+ lineNum: 0,
56
+ linePos: 0
57
+ });
58
+ continue; // eslint-disable-line no-continue
59
+ }
38
60
 
39
61
  let lineNum = parseInt(lineNumber, 10);
40
62
  if (isNaN(lineNum)) {
@@ -2,23 +2,19 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {
6
- CopyBufferToBufferOptions,
7
- CopyBufferToTextureOptions,
8
- CopyTextureToBufferOptions,
9
- CopyTextureToTextureOptions
10
- // ClearTextureOptions,
11
- // TextureReadOptions
12
- } from '@luma.gl/core';
13
- import {CommandBuffer, Texture, Framebuffer} from '@luma.gl/core';
14
5
  import {
15
- GL,
16
- GLTextureTarget,
17
- GLTextureCubeMapTarget
18
- // GLTexelDataFormat,
19
- // GLPixelType,
20
- // GLDataType
21
- } from '@luma.gl/constants';
6
+ type CopyBufferToBufferOptions,
7
+ type CopyBufferToTextureOptions,
8
+ type CopyTextureToBufferOptions,
9
+ type CopyTextureToTextureOptions,
10
+ // type ClearTextureOptions,
11
+ // type TextureReadOptions
12
+ CommandBuffer,
13
+ Texture,
14
+ Framebuffer,
15
+ assertDefined
16
+ } from '@luma.gl/core';
17
+ import {GL, type GLTextureTarget, type GLTextureCubeMapTarget} from '@luma.gl/constants';
22
18
 
23
19
  import {WebGLDevice} from '../webgl-device';
24
20
  import {WEBGLBuffer} from './webgl-buffer';
@@ -145,7 +141,7 @@ function _copyTextureToBuffer(device: WebGLDevice, options: CopyTextureToBufferO
145
141
  height = options.sourceTexture.height,
146
142
  depthOrArrayLayers = 0,
147
143
  /** Defines the origin of the copy - the minimum corner of the texture sub-region to copy to/from. */
148
- origin = [0, 0],
144
+ origin = [0, 0, 0],
149
145
 
150
146
  /** Destination buffer */
151
147
  destinationBuffer,
@@ -181,9 +177,9 @@ function _copyTextureToBuffer(device: WebGLDevice, options: CopyTextureToBufferO
181
177
  const webglBuffer = destinationBuffer as WEBGLBuffer;
182
178
  const sourceWidth = width || framebuffer.width;
183
179
  const sourceHeight = height || framebuffer.height;
184
- const sourceParams = getTextureFormatWebGL(
185
- framebuffer.colorAttachments[0].texture.props.format
186
- );
180
+ const colorAttachment0 = assertDefined(framebuffer.colorAttachments[0]);
181
+
182
+ const sourceParams = getTextureFormatWebGL(colorAttachment0.texture.props.format);
187
183
  const sourceFormat = sourceParams.format;
188
184
  const sourceType = sourceParams.type;
189
185
 
@@ -256,7 +252,7 @@ function _copyTextureToTexture(device: WebGLDevice, options: CopyTextureToTextur
256
252
  origin = [0, 0],
257
253
 
258
254
  /** Defines the origin of the copy - the minimum corner of the texture sub-region to copy to. */
259
- destinationOrigin = [0, 0],
255
+ destinationOrigin = [0, 0, 0],
260
256
 
261
257
  /** Texture to copy to/from. */
262
258
  destinationTexture
@@ -275,7 +271,7 @@ function _copyTextureToTexture(device: WebGLDevice, options: CopyTextureToTextur
275
271
  } = options;
276
272
 
277
273
  const {framebuffer, destroyFramebuffer} = getFramebuffer(sourceTexture);
278
- const [sourceX, sourceY] = origin;
274
+ const [sourceX = 0, sourceY = 0] = origin;
279
275
  const [destinationX, destinationY, destinationZ] = destinationOrigin;
280
276
 
281
277
  // @ts-expect-error native bindFramebuffer is overridden by our state tracker
@@ -24,6 +24,12 @@ export class WEBGLRenderPass extends RenderPass {
24
24
  super(device, props);
25
25
  this.device = device;
26
26
 
27
+ if (!props?.framebuffer) {
28
+ // Default-framebuffer rendering bypasses CanvasContext.getCurrentFramebuffer(),
29
+ // so flush any deferred canvas resize before deriving viewport state.
30
+ device.getDefaultCanvasContext()._resizeDrawingBufferIfNeeded();
31
+ }
32
+
27
33
  // If no viewport is provided, apply reasonably defaults
28
34
  let viewport: NumberArray4 | undefined;
29
35
  if (!props?.parameters?.viewport) {
@@ -452,8 +452,7 @@ export class WEBGLRenderPipeline extends RenderPipeline {
452
452
  if ((location as GL) === GL.INVALID_INDEX) {
453
453
  throw new Error(`Invalid uniform block name ${name}`);
454
454
  }
455
- gl.uniformBlockBinding(this.handle, uniformBufferIndex, location);
456
- // console.debug(binding, location);
455
+ gl.uniformBlockBinding(this.handle, location, uniformBufferIndex);
457
456
  if (value instanceof WEBGLBuffer) {
458
457
  gl.bindBufferBase(GL.UNIFORM_BUFFER, uniformBufferIndex, value.handle);
459
458
  } else {
@@ -69,8 +69,8 @@ export class WEBGLTransformFeedback extends TransformFeedback {
69
69
  this.unusedBuffers = {};
70
70
 
71
71
  this.bind(() => {
72
- for (const bufferName in buffers) {
73
- this.setBuffer(bufferName, buffers[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 location >= 0 ? this.buffers[location] : null;
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 in this.buffers) {
166
- const {buffer, byteLength, byteOffset} = this._getBufferRange(this.buffers[bufferIndex]);
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
- // @ts-expect-error
59
- if (gl?.device instanceof WebGLDevice) {
60
- // @ts-expect-error
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');
@@ -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';
@@ -70,6 +71,13 @@ import {getWebGLExtension} from '../context/helpers/webgl-extensions';
70
71
 
71
72
  /** WebGPU style Device API for a WebGL context */
72
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
+ }
73
81
  // Public `Device` API
74
82
 
75
83
  /** type of this device */
@@ -100,7 +108,7 @@ export class WebGLDevice extends Device {
100
108
  _constants: (TypedArray | null)[];
101
109
 
102
110
  /** State used by luma.gl classes - TODO - not used? */
103
- readonly _extensions: GLExtensions = {};
111
+ readonly extensions!: GLExtensions;
104
112
  _polyfilled: boolean = false;
105
113
 
106
114
  /** Instance of Spector.js (if initialized) */
@@ -141,7 +149,8 @@ export class WebGLDevice extends Device {
141
149
  // Note that this can be avoided in webgl2adapter.create() if
142
150
  // DeviceProps._reuseDevices is set.
143
151
  // @ts-expect-error device is attached to context
144
- let device: WebGLDevice | undefined = canvasContextProps.canvas?.gl?.device;
152
+ const existingContext = canvasContextProps.canvas?.gl ?? null;
153
+ let device: WebGLDevice | null = WebGLDevice.getDeviceFromContext(existingContext);
145
154
  if (device) {
146
155
  throw new Error(`WebGL context already attached to device ${device.id}`);
147
156
  }
@@ -190,8 +199,7 @@ export class WebGLDevice extends Device {
190
199
 
191
200
  // Note that the browser will only create one WebGL context per canvas.
192
201
  // This means that a newly created gl context may already have a device attached to it.
193
- // @ts-expect-error luma.gl stores a device reference on the context.
194
- device = gl.device;
202
+ device = WebGLDevice.getDeviceFromContext(gl);
195
203
  if (device) {
196
204
  if (props._reuseDevices) {
197
205
  log.log(
@@ -199,6 +207,9 @@ export class WebGLDevice extends Device {
199
207
  `Not creating a new Device, instead returning a reference to Device ${device.id} already attached to WebGL context`,
200
208
  device
201
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();
202
213
  device._reused = true;
203
214
  return device;
204
215
  }
@@ -214,16 +225,15 @@ export class WebGLDevice extends Device {
214
225
  this.spectorJS = initializeSpectorJS({...this.props, gl: this.handle});
215
226
 
216
227
  // Instrument context
217
- (this.gl as any).device = this; // Update GL context: Link webgl context back to device
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 = {});
218
232
 
219
233
  // initialize luma Device fields
220
- this.info = getDeviceInfo(this.gl, this._extensions);
234
+ this.info = getDeviceInfo(this.gl, this.extensions);
221
235
  this.limits = new WebGLDeviceLimits(this.gl);
222
- this.features = new WebGLDeviceFeatures(
223
- this.gl,
224
- this._extensions,
225
- this.props._disabledFeatures
226
- );
236
+ this.features = new WebGLDeviceFeatures(this.gl, this.extensions, this.props._disabledFeatures);
227
237
  if (this.props._initializeFeatures) {
228
238
  this.features.initializeFeatures();
229
239
  }
@@ -265,7 +275,8 @@ export class WebGLDevice extends Device {
265
275
  // Therefore we must do nothing in destroy() if props._reuseDevices is true
266
276
  if (!this.props._reuseDevices && !this._reused) {
267
277
  // Delete the reference to the device that we store on the WebGL context
268
- delete (this.gl as any).device;
278
+ const contextData = getWebGLContextData(this.handle);
279
+ contextData.device = null;
269
280
  }
270
281
  }
271
282
 
@@ -407,7 +418,7 @@ export class WebGLDevice extends Device {
407
418
  override _getDeviceSpecificTextureFormatCapabilities(
408
419
  capabilities: DeviceTextureFormatCapabilities
409
420
  ): DeviceTextureFormatCapabilities {
410
- return getTextureFormatCapabilitiesWebGL(this.gl, capabilities, this._extensions);
421
+ return getTextureFormatCapabilitiesWebGL(this.gl, capabilities, this.extensions);
411
422
  }
412
423
 
413
424
  //
@@ -510,8 +521,8 @@ export class WebGLDevice extends Device {
510
521
 
511
522
  /** Ensure extensions are only requested once */
512
523
  getExtension(name: keyof GLExtensions): GLExtensions {
513
- getWebGLExtension(this.gl, name, this._extensions);
514
- return this._extensions;
524
+ getWebGLExtension(this.gl, name, this.extensions);
525
+ return this.extensions;
515
526
  }
516
527
 
517
528
  // INTERNAL SUPPORT METHODS FOR WEBGL RESOURCES
@@ -4,6 +4,7 @@
4
4
 
5
5
  import {log} from '@luma.gl/core';
6
6
  import {loadScript} from '../../utils/load-script';
7
+ import {getWebGLContextData} from '../helpers/webgl-context-data';
7
8
 
8
9
  import type {Spector} from './spector-types';
9
10
 
@@ -91,11 +92,10 @@ export function initializeSpectorJS(props: SpectorProps): Spector | null {
91
92
  if (props.gl) {
92
93
  // capture startup
93
94
  const gl = props.gl;
94
- // @ts-expect-error
95
- const device = gl.device;
95
+ const contextData = getWebGLContextData(gl);
96
+ const device = contextData.device;
96
97
  spector?.startCapture(props.gl, 500); // 500 commands
97
- // @ts-expect-error
98
- gl.device = device;
98
+ contextData.device = device;
99
99
 
100
100
  new Promise(resolve => setTimeout(resolve, 2000)).then(_ => {
101
101
  log.info('Spector capture stopped after 2 seconds')();
@@ -104,6 +104,8 @@ function getDebugContext(
104
104
  // Store the debug context
105
105
  data.realContext = gl;
106
106
  data.debugContext = debugContext;
107
+ // Share the context metadata object with the debug context so lookups stay consistent.
108
+ (debugContext as {luma?: unknown}).luma = data;
107
109
  debugContext.debug = true;
108
110
 
109
111
  // Return it
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import {getWebGLContextData} from './webgl-context-data';
6
+
5
7
  /**
6
8
  * ContextProps
7
9
  * @param onContextLost
@@ -55,15 +57,11 @@ export function createBrowserContext(
55
57
  }
56
58
 
57
59
  // Creation failed with failIfMajorPerformanceCaveat - Try a Software GPU
60
+ let softwareRenderer = false;
58
61
  if (!gl && allowSoftwareRenderer) {
59
62
  webglProps.failIfMajorPerformanceCaveat = false;
60
63
  gl = canvas.getContext('webgl2', webglProps);
61
- if (gl) {
62
- // @ts-expect-error
63
- gl.luma ||= {};
64
- // @ts-expect-error
65
- gl.luma.softwareRenderer = true;
66
- }
64
+ softwareRenderer = true;
67
65
  }
68
66
 
69
67
  if (!gl) {
@@ -79,6 +77,10 @@ export function createBrowserContext(
79
77
  throw new Error(`Failed to create WebGL context: ${errorMessage}`);
80
78
  }
81
79
 
80
+ // Initialize luma.gl specific context data
81
+ const luma = getWebGLContextData(gl);
82
+ luma.softwareRenderer = softwareRenderer;
83
+
82
84
  // Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
83
85
  const {onContextLost, onContextRestored} = props;
84
86
  canvas.addEventListener('webglcontextlost', (event: Event) => onContextLost(event), false);
@@ -88,8 +90,6 @@ export function createBrowserContext(
88
90
  false
89
91
  );
90
92
 
91
- // @ts-expect-error
92
- gl.luma ||= {};
93
93
  return gl;
94
94
  } finally {
95
95
  canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
@@ -2,12 +2,17 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import type {GLExtensions} from '@luma.gl/constants';
6
+
5
7
  /**
6
8
  * Stores luma.gl specific state associated with a context
7
9
  */
8
10
  export interface WebGLContextData {
11
+ /** This type is used by lower level code that is not aware of the Device type */
12
+ device?: unknown;
9
13
  _polyfilled: boolean;
10
- _extensions: Record<string, any>;
14
+ extensions: GLExtensions;
15
+ softwareRenderer?: boolean;
11
16
  }
12
17
 
13
18
  /**
@@ -16,16 +21,17 @@ export interface WebGLContextData {
16
21
  */
17
22
  export function getWebGLContextData(gl: WebGL2RenderingContext): WebGLContextData {
18
23
  // @ts-expect-error
19
- const luma = gl.luma as WebGLContextData | null;
20
- if (!luma) {
21
- const contextState: WebGLContextData = {
22
- _polyfilled: false,
23
- _extensions: {}
24
- };
25
- // @ts-expect-error
26
- gl.luma = contextState;
27
- }
24
+ const contextData = (gl.luma as WebGLContextData | null) || {
25
+ _polyfilled: false,
26
+ extensions: {},
27
+ softwareRenderer: false
28
+ };
29
+
30
+ contextData._polyfilled ??= false;
31
+ contextData.extensions ||= {};
28
32
 
29
33
  // @ts-expect-error
30
- return gl.luma;
34
+ gl.luma = contextData;
35
+
36
+ return contextData;
31
37
  }
@@ -18,7 +18,7 @@ export type {GLParameters};
18
18
  /**
19
19
  * Sets any GL parameter regardless of function (gl.blendMode, ...)
20
20
  *
21
- * @note requires a `cache` object to be set on the context (gl.state.cache)
21
+ * @note requires a `cache` object to be set on the context (lumaState.cache)
22
22
  * This object is used to fill in any missing values for composite setter functions
23
23
  */
24
24
  export function setGLParameters(gl: WebGL2RenderingContext, parameters: GLParameters): void {
@@ -58,7 +58,7 @@ export function setGLParameters(gl: WebGL2RenderingContext, parameters: GLParame
58
58
  // But it is too inconvenient to always require a cache parameter here.
59
59
  // This is the ONLY external dependency in this module/
60
60
  // @ts-expect-error
61
- const cache = gl.state && gl.state.cache;
61
+ const cache = gl.lumaState?.cache;
62
62
  if (cache) {
63
63
  for (const key in compositeSetters) {
64
64
  // TODO - avoid calling composite setters if values have not changed.
@@ -21,7 +21,7 @@ import {
21
21
  export class WebGLStateTracker {
22
22
  static get(gl: WebGL2RenderingContext): WebGLStateTracker {
23
23
  // @ts-expect-error
24
- return gl.state as WebGLStateTracker;
24
+ return gl.lumaState as WebGLStateTracker;
25
25
  }
26
26
 
27
27
  gl: WebGL2RenderingContext;
@@ -78,7 +78,7 @@ export class WebGLStateTracker {
78
78
  this.initialized = true;
79
79
 
80
80
  // @ts-expect-error
81
- this.gl.state = this;
81
+ this.gl.lumaState = this;
82
82
 
83
83
  installProgramSpy(gl);
84
84
 
@@ -16,7 +16,7 @@ export function fillArray(options: {
16
16
  const total = count * length;
17
17
  let copied = 0;
18
18
  for (let i = start; copied < length; copied++) {
19
- target[i++] = source[copied];
19
+ target[i++] = source[copied] ?? 0;
20
20
  }
21
21
 
22
22
  while (copied < total) {