@luma.gl/webgl 9.2.0-alpha.1 → 9.2.0-alpha.2

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 (72) hide show
  1. package/dist/adapter/converters/sampler-parameters.js +13 -2
  2. package/dist/adapter/converters/sampler-parameters.js.map +1 -1
  3. package/dist/adapter/converters/webgl-shadertypes.d.ts.map +1 -1
  4. package/dist/adapter/converters/webgl-shadertypes.js +1 -2
  5. package/dist/adapter/converters/webgl-shadertypes.js.map +1 -1
  6. package/dist/adapter/converters/webgl-texture-table.d.ts.map +1 -1
  7. package/dist/adapter/converters/webgl-texture-table.js +15 -11
  8. package/dist/adapter/converters/webgl-texture-table.js.map +1 -1
  9. package/dist/adapter/converters/webgl-vertex-formats.d.ts.map +1 -1
  10. package/dist/adapter/converters/webgl-vertex-formats.js +1 -2
  11. package/dist/adapter/converters/webgl-vertex-formats.js.map +1 -1
  12. package/dist/adapter/helpers/webgl-texture-utils.d.ts +0 -217
  13. package/dist/adapter/helpers/webgl-texture-utils.d.ts.map +1 -1
  14. package/dist/adapter/helpers/webgl-texture-utils.js +3 -3
  15. package/dist/adapter/helpers/webgl-texture-utils.js.map +1 -1
  16. package/dist/adapter/resources/webgl-buffer.d.ts +7 -7
  17. package/dist/adapter/resources/webgl-buffer.d.ts.map +1 -1
  18. package/dist/adapter/resources/webgl-buffer.js +24 -15
  19. package/dist/adapter/resources/webgl-buffer.js.map +1 -1
  20. package/dist/adapter/resources/webgl-command-buffer.d.ts +2 -1
  21. package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
  22. package/dist/adapter/resources/webgl-command-buffer.js +1 -0
  23. package/dist/adapter/resources/webgl-command-buffer.js.map +1 -1
  24. package/dist/adapter/resources/webgl-framebuffer.d.ts +2 -2
  25. package/dist/adapter/resources/webgl-framebuffer.d.ts.map +1 -1
  26. package/dist/adapter/resources/webgl-framebuffer.js.map +1 -1
  27. package/dist/adapter/resources/webgl-query-set.d.ts +2 -2
  28. package/dist/adapter/resources/webgl-query-set.d.ts.map +1 -1
  29. package/dist/adapter/resources/webgl-query-set.js.map +1 -1
  30. package/dist/adapter/resources/webgl-render-pass.d.ts +1 -0
  31. package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgl-render-pass.js +1 -0
  33. package/dist/adapter/resources/webgl-render-pass.js.map +1 -1
  34. package/dist/adapter/resources/webgl-render-pipeline.d.ts +2 -2
  35. package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
  36. package/dist/adapter/resources/webgl-render-pipeline.js +2 -1
  37. package/dist/adapter/resources/webgl-render-pipeline.js.map +1 -1
  38. package/dist/adapter/resources/webgl-texture.d.ts.map +1 -1
  39. package/dist/adapter/resources/webgl-texture.js +8 -5
  40. package/dist/adapter/resources/webgl-texture.js.map +1 -1
  41. package/dist/adapter/resources/webgl-vertex-array.d.ts +1 -1
  42. package/dist/adapter/webgl-adapter.d.ts +2 -1
  43. package/dist/adapter/webgl-adapter.d.ts.map +1 -1
  44. package/dist/adapter/webgl-adapter.js +35 -25
  45. package/dist/adapter/webgl-adapter.js.map +1 -1
  46. package/dist/adapter/webgl-device.d.ts +12 -4
  47. package/dist/adapter/webgl-device.d.ts.map +1 -1
  48. package/dist/adapter/webgl-device.js +60 -17
  49. package/dist/adapter/webgl-device.js.map +1 -1
  50. package/dist/context/debug/webgl-developer-tools.js +1 -0
  51. package/dist/context/debug/webgl-developer-tools.js.map +1 -1
  52. package/dist/dist.dev.js +124 -57
  53. package/dist/dist.min.js +2 -2
  54. package/dist/index.cjs +117 -57
  55. package/dist/index.cjs.map +2 -2
  56. package/package.json +4 -4
  57. package/src/adapter/converters/sampler-parameters.ts +13 -2
  58. package/src/adapter/converters/webgl-shadertypes.ts +1 -2
  59. package/src/adapter/converters/webgl-texture-table.ts +20 -14
  60. package/src/adapter/converters/webgl-vertex-formats.ts +1 -2
  61. package/src/adapter/helpers/webgl-texture-utils.ts +3 -224
  62. package/src/adapter/resources/webgl-buffer.ts +38 -20
  63. package/src/adapter/resources/webgl-command-buffer.ts +4 -3
  64. package/src/adapter/resources/webgl-command-encoder.ts +1 -1
  65. package/src/adapter/resources/webgl-framebuffer.ts +2 -2
  66. package/src/adapter/resources/webgl-query-set.ts +2 -2
  67. package/src/adapter/resources/webgl-render-pass.ts +1 -0
  68. package/src/adapter/resources/webgl-render-pipeline.ts +7 -3
  69. package/src/adapter/resources/webgl-texture.ts +8 -5
  70. package/src/adapter/webgl-adapter.ts +36 -28
  71. package/src/adapter/webgl-device.ts +85 -31
  72. package/src/context/debug/webgl-developer-tools.ts +1 -0
@@ -45,7 +45,8 @@ export class WebGLAdapter extends Adapter {
45
45
 
46
46
  /**
47
47
  * Get a device instance from a GL context
48
- * Creates and instruments the device if not already created
48
+ * Creates a WebGLCanvasContext against the contexts canvas
49
+ * @note autoResize will be disabled, assuming that whoever created the external context will be handling resizes.
49
50
  * @param gl
50
51
  * @returns
51
52
  */
@@ -62,51 +63,58 @@ export class WebGLAdapter extends Adapter {
62
63
  if (!isWebGL(gl)) {
63
64
  throw new Error('Invalid WebGL2RenderingContext');
64
65
  }
65
- return new WebGLDevice({_handle: gl as WebGL2RenderingContext});
66
+
67
+ // We create a new device using the provided WebGL context and its canvas
68
+ // Assume that whoever created the external context will be handling resizes.
69
+ return new WebGLDevice({
70
+ _handle: gl,
71
+ createCanvasContext: {canvas: gl.canvas, autoResize: false}
72
+ });
66
73
  }
67
74
 
68
75
  async create(props: DeviceProps = {}): Promise<WebGLDevice> {
69
76
  const {WebGLDevice} = await import('./webgl-device');
70
77
 
71
78
  log.groupCollapsed(LOG_LEVEL, 'WebGLDevice created')();
79
+ try {
80
+ const promises: Promise<unknown>[] = [];
72
81
 
73
- const promises: Promise<unknown>[] = [];
74
-
75
- // Load webgl and spector debug scripts from CDN if requested
76
- if (props.debugWebGL || props.debug) {
77
- promises.push(loadWebGLDeveloperTools());
78
- }
82
+ // Load webgl and spector debug scripts from CDN if requested
83
+ if (props.debugWebGL || props.debug) {
84
+ promises.push(loadWebGLDeveloperTools());
85
+ }
79
86
 
80
- if (props.debugSpectorJS) {
81
- promises.push(loadSpectorJS(props));
82
- }
87
+ if (props.debugSpectorJS) {
88
+ promises.push(loadSpectorJS(props));
89
+ }
83
90
 
84
- // Wait for all the loads to settle before creating the context.
85
- // The Device.create() functions are async, so in contrast to the constructor, we can `await` here.
86
- const results = await Promise.allSettled(promises);
87
- for (const result of results) {
88
- if (result.status === 'rejected') {
89
- log.error(`Failed to initialize debug libraries ${result.reason}`)();
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}`)();
97
+ }
90
98
  }
91
- }
92
99
 
93
- const device = new WebGLDevice(props);
100
+ const device = new WebGLDevice(props);
94
101
 
95
- // Log some debug info about the newly created context
96
- const message = `\
97
- Created ${device.type}${device.debug ? ' debug' : ''} context: \
102
+ // Log some debug info about the newly created context
103
+ const message = `\
104
+ ${device._reused ? 'Reusing' : 'Created'} device with WebGL2 ${device.props.debug ? 'debug ' : ''}context: \
98
105
  ${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContext.id}`;
99
- log.probe(LOG_LEVEL, message)();
100
- log.table(LOG_LEVEL, device.info)();
106
+ log.probe(LOG_LEVEL, message)();
107
+ log.table(LOG_LEVEL, device.info)();
101
108
 
102
- log.groupEnd(LOG_LEVEL)();
103
-
104
- return device;
109
+ return device;
110
+ } finally {
111
+ log.groupEnd(LOG_LEVEL)();
112
+ }
105
113
  }
106
114
  }
107
115
 
108
116
  /** Check if supplied parameter is a WebGL2RenderingContext */
109
- function isWebGL(gl: any): boolean {
117
+ function isWebGL(gl: any): gl is WebGL2RenderingContext {
110
118
  if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) {
111
119
  return true;
112
120
  }
@@ -29,7 +29,8 @@ import type {
29
29
  CommandEncoderProps,
30
30
  TransformFeedbackProps,
31
31
  QuerySetProps,
32
- Resource
32
+ Resource,
33
+ VertexFormat
33
34
  } from '@luma.gl/core';
34
35
  import {Device, CanvasContext, log} from '@luma.gl/core';
35
36
  import type {GLExtensions} from '@luma.gl/constants';
@@ -73,25 +74,25 @@ export class WebGLDevice extends Device {
73
74
  /** type of this device */
74
75
  readonly type = 'webgl';
75
76
 
77
+ // Use the ! assertion to handle the case where _reuseDevices causes the constructor to return early
76
78
  /** The underlying WebGL context */
77
- readonly handle: WebGL2RenderingContext;
78
- features: WebGLDeviceFeatures;
79
- limits: WebGLDeviceLimits;
80
- readonly info: DeviceInfo;
81
- readonly canvasContext: WebGLCanvasContext;
79
+ readonly handle!: WebGL2RenderingContext;
80
+ features!: WebGLDeviceFeatures;
81
+ limits!: WebGLDeviceLimits;
82
+ readonly info!: DeviceInfo;
83
+ readonly canvasContext!: WebGLCanvasContext;
82
84
 
83
85
  readonly preferredColorFormat = 'rgba8unorm';
84
86
  readonly preferredDepthFormat = 'depth24plus';
85
87
 
86
- commandEncoder: WEBGLCommandEncoder;
88
+ commandEncoder!: WEBGLCommandEncoder;
87
89
 
88
90
  readonly lost: Promise<{reason: 'destroyed'; message: string}>;
89
91
 
90
92
  private _resolveContextLost?: (value: {reason: 'destroyed'; message: string}) => void;
91
93
 
92
94
  /** WebGL2 context. */
93
- readonly gl: WebGL2RenderingContext;
94
- readonly debug: boolean = false;
95
+ readonly gl!: WebGL2RenderingContext;
95
96
 
96
97
  /** Store constants */
97
98
  // @ts-ignore TODO fix
@@ -102,26 +103,42 @@ export class WebGLDevice extends Device {
102
103
  _polyfilled: boolean = false;
103
104
 
104
105
  /** Instance of Spector.js (if initialized) */
105
- spectorJS: Spector | null;
106
+ spectorJS!: Spector | null;
106
107
 
107
108
  //
108
109
  // Public API
109
110
  //
110
111
 
112
+ override get [Symbol.toStringTag](): string {
113
+ return 'WebGLDevice';
114
+ }
115
+
111
116
  override toString(): string {
112
117
  return `${this[Symbol.toStringTag]}(${this.id})`;
113
118
  }
114
119
 
120
+ override isVertexFormatSupported(format: VertexFormat): boolean {
121
+ switch (format) {
122
+ case 'unorm8x4-bgra':
123
+ return false;
124
+ default:
125
+ return true;
126
+ }
127
+ }
128
+
115
129
  constructor(props: DeviceProps) {
116
130
  super({...props, id: props.id || uid('webgl-device')});
117
131
 
132
+ const canvasContextProps = Device._getCanvasContextProps(props)!;
133
+
118
134
  // WebGL requires a canvas to be created before creating the context
119
- if (!props.createCanvasContext) {
135
+ if (!canvasContextProps) {
120
136
  throw new Error('WebGLDevice requires props.createCanvasContext to be set');
121
137
  }
122
- const canvasContextProps = props.createCanvasContext === true ? {} : props.createCanvasContext;
123
138
 
124
- // If attaching to an already attached context, return the attached device
139
+ // Check if the WebGL context is already associated with a device
140
+ // Note that this can be avoided in webgl2adapter.create() if
141
+ // DeviceProps._reuseDevices is set.
125
142
  // @ts-expect-error device is attached to context
126
143
  let device: WebGLDevice | undefined = canvasContextProps.canvas?.gl?.device;
127
144
  if (device) {
@@ -144,27 +161,43 @@ export class WebGLDevice extends Device {
144
161
  webglContextAttributes.powerPreference = props.powerPreference;
145
162
  }
146
163
 
147
- const gl = createBrowserContext(
148
- this.canvasContext.canvas,
149
- {
150
- onContextLost: (event: Event) =>
151
- this._resolveContextLost?.({
152
- reason: 'destroyed',
153
- message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
154
- }),
155
- // eslint-disable-next-line no-console
156
- onContextRestored: (event: Event) => console.log('WebGL context restored')
157
- },
158
- webglContextAttributes
159
- );
164
+ // Check if we should attach to an externally created context or create a new context
165
+ const externalGLContext = this.props._handle as WebGL2RenderingContext | null;
166
+
167
+ const gl =
168
+ externalGLContext ||
169
+ createBrowserContext(
170
+ this.canvasContext.canvas,
171
+ {
172
+ onContextLost: (event: Event) =>
173
+ this._resolveContextLost?.({
174
+ reason: 'destroyed',
175
+ message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
176
+ }),
177
+ // eslint-disable-next-line no-console
178
+ onContextRestored: (event: Event) => console.log('WebGL context restored')
179
+ },
180
+ webglContextAttributes
181
+ );
160
182
 
161
183
  if (!gl) {
162
184
  throw new Error('WebGL context creation failed');
163
185
  }
164
186
 
165
- // @ts-expect-error device is attached to context
187
+ // Note that the browser will only create one WebGL context per canvas.
188
+ // This means that a newly created gl context may already have a device attached to it.
189
+ // @ts-expect-error luma.gl stores a device reference on the context.
166
190
  device = gl.device;
167
191
  if (device) {
192
+ if (props._reuseDevices) {
193
+ log.log(
194
+ 1,
195
+ `Not creating a new Device, instead returning a reference to Device ${device.id} already attached to WebGL context`,
196
+ device
197
+ )();
198
+ device._reused = true;
199
+ return device;
200
+ }
168
201
  throw new Error(`WebGL context already attached to device ${device.id}`);
169
202
  }
170
203
 
@@ -178,6 +211,7 @@ export class WebGLDevice extends Device {
178
211
 
179
212
  // Instrument context
180
213
  (this.gl as any).device = this; // Update GL context: Link webgl context back to device
214
+ // TODO - remove, this is only used to detect debug contexts.
181
215
  (this.gl as any)._version = 2; // Update GL context: Store WebGL version field on gl context (HACK to identify debug contexts)
182
216
 
183
217
  // initialize luma Device fields
@@ -213,10 +247,26 @@ export class WebGLDevice extends Device {
213
247
  }
214
248
 
215
249
  /**
216
- * Destroys the context
217
- * @note Has no effect for WebGL browser contexts, there is no browser API for destroying contexts
250
+ * Destroys the device
251
+ *
252
+ * @note "Detaches" from the WebGL context unless _reuseDevices is true.
253
+ *
254
+ * @note The underlying WebGL context is not immediately destroyed,
255
+ * but may be destroyed later through normal JavaScript garbage collection.
256
+ * This is a fundamental limitation since WebGL does not offer any
257
+ * browser API for destroying WebGL contexts.
218
258
  */
219
- destroy(): void {}
259
+ destroy(): void {
260
+ // Note that deck.gl (especially in React strict mode) depends on being able
261
+ // to asynchronously create a Device against the same canvas (i.e. WebGL context)
262
+ // multiple times and getting the same device back. Since deck.gl is not aware
263
+ // of this sharing, it might call destroy() multiple times on the same device.
264
+ // Therefore we must do nothing in destroy() if props._reuseDevices is true
265
+ if (!this.props._reuseDevices && !this._reused) {
266
+ // Delete the reference to the device that we store on the WebGL context
267
+ delete (this.gl as any).device;
268
+ }
269
+ }
220
270
 
221
271
  get isLost(): boolean {
222
272
  return this.gl.isContextLost();
@@ -224,6 +274,10 @@ export class WebGLDevice extends Device {
224
274
 
225
275
  // IMPLEMENTATION OF ABSTRACT DEVICE
226
276
 
277
+ getTextureByteAlignment(): number {
278
+ return 4;
279
+ }
280
+
227
281
  createCanvasContext(props?: CanvasContextProps): CanvasContext {
228
282
  throw new Error('WebGL only supports a single canvas');
229
283
  }
@@ -473,7 +527,7 @@ export class WebGLDevice extends Device {
473
527
  // @ts-expect-error
474
528
  handle.luma = resource;
475
529
 
476
- const spectorMetadata = {props: options.spector, id: options.spector.id};
530
+ const spectorMetadata = {props: options.spector, id: options.spector['id']};
477
531
  // @ts-expect-error
478
532
  // eslint-disable-next-line camelcase
479
533
  handle.__SPECTOR_Metadata = spectorMetadata;
@@ -131,6 +131,7 @@ function onGLError(
131
131
  const errorMessage = globalThis.WebGLDebugUtils.glEnumToString(err);
132
132
  const functionArgs = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, args);
133
133
  const message = `${errorMessage} in gl.${functionName}(${functionArgs})`;
134
+ // TODO - call device.reportError
134
135
  log.error(message)();
135
136
  debugger; // eslint-disable-line
136
137
  // throw new Error(message);