@luma.gl/webgl 9.1.0-alpha.15 → 9.1.0-alpha.17

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 (69) hide show
  1. package/dist/adapter/converters/device-parameters.d.ts +3 -3
  2. package/dist/adapter/converters/device-parameters.d.ts.map +1 -1
  3. package/dist/adapter/converters/sampler-parameters.js +6 -4
  4. package/dist/adapter/converters/texture-formats.d.ts +49 -11
  5. package/dist/adapter/converters/texture-formats.d.ts.map +1 -1
  6. package/dist/adapter/converters/texture-formats.js +150 -160
  7. package/dist/adapter/helpers/format-utils.d.ts.map +1 -1
  8. package/dist/adapter/helpers/format-utils.js +6 -0
  9. package/dist/adapter/helpers/webgl-texture-utils.d.ts +34 -30
  10. package/dist/adapter/helpers/webgl-texture-utils.d.ts.map +1 -1
  11. package/dist/adapter/helpers/webgl-texture-utils.js +52 -256
  12. package/dist/adapter/resources/webgl-command-buffer.d.ts +59 -2
  13. package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
  14. package/dist/adapter/resources/webgl-command-buffer.js +87 -31
  15. package/dist/adapter/resources/webgl-command-encoder.d.ts.map +1 -1
  16. package/dist/adapter/resources/webgl-command-encoder.js +3 -0
  17. package/dist/adapter/resources/webgl-external-texture.js +14 -0
  18. package/dist/adapter/resources/webgl-framebuffer.d.ts.map +1 -1
  19. package/dist/adapter/resources/webgl-framebuffer.js +1 -2
  20. package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
  21. package/dist/adapter/resources/webgl-render-pass.js +38 -20
  22. package/dist/adapter/resources/webgl-render-pipeline.d.ts +1 -1
  23. package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
  24. package/dist/adapter/resources/webgl-render-pipeline.js +30 -16
  25. package/dist/adapter/resources/webgl-shader.d.ts +1 -0
  26. package/dist/adapter/resources/webgl-shader.d.ts.map +1 -1
  27. package/dist/adapter/resources/webgl-shader.js +7 -5
  28. package/dist/adapter/resources/webgl-texture.d.ts +8 -14
  29. package/dist/adapter/resources/webgl-texture.d.ts.map +1 -1
  30. package/dist/adapter/resources/webgl-texture.js +119 -208
  31. package/dist/adapter/webgl-adapter.d.ts.map +1 -1
  32. package/dist/adapter/webgl-adapter.js +4 -10
  33. package/dist/adapter/webgl-device.d.ts +8 -3
  34. package/dist/adapter/webgl-device.d.ts.map +1 -1
  35. package/dist/adapter/webgl-device.js +53 -22
  36. package/dist/context/debug/spector-types.js +1 -1
  37. package/dist/context/debug/spector.d.ts +5 -5
  38. package/dist/context/debug/spector.d.ts.map +1 -1
  39. package/dist/context/debug/spector.js +6 -6
  40. package/dist/context/debug/webgl-developer-tools.d.ts +2 -3
  41. package/dist/context/debug/webgl-developer-tools.d.ts.map +1 -1
  42. package/dist/context/debug/webgl-developer-tools.js +6 -19
  43. package/dist/context/helpers/create-browser-context.d.ts +6 -22
  44. package/dist/context/helpers/create-browser-context.d.ts.map +1 -1
  45. package/dist/context/helpers/create-browser-context.js +40 -32
  46. package/dist/dist.dev.js +366 -400
  47. package/dist/dist.min.js +2 -2
  48. package/dist/index.cjs +341 -384
  49. package/dist/index.cjs.map +3 -3
  50. package/package.json +4 -4
  51. package/src/adapter/converters/device-parameters.ts +3 -3
  52. package/src/adapter/converters/sampler-parameters.ts +6 -4
  53. package/src/adapter/converters/texture-formats.ts +171 -177
  54. package/src/adapter/helpers/format-utils.ts +6 -0
  55. package/src/adapter/helpers/webgl-texture-utils.ts +99 -75
  56. package/src/adapter/resources/webgl-command-buffer.ts +124 -40
  57. package/src/adapter/resources/webgl-command-encoder.ts +6 -0
  58. package/src/adapter/resources/webgl-external-texture.ts +14 -0
  59. package/src/adapter/resources/webgl-framebuffer.ts +1 -2
  60. package/src/adapter/resources/webgl-render-pass.ts +44 -23
  61. package/src/adapter/resources/webgl-render-pipeline.ts +32 -16
  62. package/src/adapter/resources/webgl-shader.ts +8 -6
  63. package/src/adapter/resources/webgl-texture.ts +126 -235
  64. package/src/adapter/webgl-adapter.ts +4 -12
  65. package/src/adapter/webgl-device.ts +88 -48
  66. package/src/context/debug/spector-types.ts +1 -1
  67. package/src/context/debug/spector.ts +11 -11
  68. package/src/context/debug/webgl-developer-tools.ts +8 -31
  69. package/src/context/helpers/create-browser-context.ts +53 -63
@@ -12,28 +12,7 @@ import type {
12
12
  Texture,
13
13
  Framebuffer,
14
14
  VertexArray,
15
- VertexArrayProps
16
- } from '@luma.gl/core';
17
- import {Device, CanvasContext, log} from '@luma.gl/core';
18
- import type {GLExtensions} from '@luma.gl/constants';
19
- import {WebGLStateTracker} from '../context/state-tracker/webgl-state-tracker';
20
- import {createBrowserContext} from '../context/helpers/create-browser-context';
21
- import {getDeviceInfo} from './device-helpers/webgl-device-info';
22
- import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
23
- import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
24
- import {WebGLCanvasContext} from './webgl-canvas-context';
25
- import type {Spector} from '../context/debug/spector-types';
26
- import {initializeSpectorJS} from '../context/debug/spector';
27
- import {makeDebugContext} from '../context/debug/webgl-developer-tools';
28
- import {
29
- isTextureFormatSupported,
30
- isTextureFormatRenderable,
31
- isTextureFormatFilterable
32
- } from './converters/texture-formats';
33
- import {uid} from '../utils/uid';
34
-
35
- // WebGL classes
36
- import type {
15
+ VertexArrayProps,
37
16
  BufferProps,
38
17
  ShaderProps,
39
18
  // Sampler,
@@ -55,6 +34,23 @@ import type {
55
34
  TransformFeedbackProps,
56
35
  QuerySetProps
57
36
  } from '@luma.gl/core';
37
+ import {Device, CanvasContext, log} from '@luma.gl/core';
38
+ import type {GLExtensions} from '@luma.gl/constants';
39
+ import {WebGLStateTracker} from '../context/state-tracker/webgl-state-tracker';
40
+ import {createBrowserContext} from '../context/helpers/create-browser-context';
41
+ import {getDeviceInfo} from './device-helpers/webgl-device-info';
42
+ import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
43
+ import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
44
+ import {WebGLCanvasContext} from './webgl-canvas-context';
45
+ import type {Spector} from '../context/debug/spector-types';
46
+ import {initializeSpectorJS} from '../context/debug/spector';
47
+ import {makeDebugContext} from '../context/debug/webgl-developer-tools';
48
+ import {
49
+ isTextureFormatSupported,
50
+ isTextureFormatRenderable,
51
+ isTextureFormatFilterable
52
+ } from './converters/texture-formats';
53
+ import {uid} from '../utils/uid';
58
54
 
59
55
  import {WEBGLBuffer} from './resources/webgl-buffer';
60
56
  import {WEBGLShader} from './resources/webgl-shader';
@@ -120,35 +116,62 @@ export class WebGLDevice extends Device {
120
116
  constructor(props: DeviceProps) {
121
117
  super({...props, id: props.id || uid('webgl-device')});
122
118
 
119
+ // WebGL requires a canvas to be created before creating the context
120
+ if (!props.createCanvasContext) {
121
+ throw new Error('WebGLDevice requires props.createCanvasContext to be set');
122
+ }
123
+ const canvasContextProps = props.createCanvasContext === true ? {} : props.createCanvasContext;
124
+
123
125
  // If attaching to an already attached context, return the attached device
124
126
  // @ts-expect-error device is attached to context
125
- const device: WebGLDevice | undefined = props.gl?.device;
127
+ let device: WebGLDevice | undefined = canvasContextProps.canvas?.gl?.device;
126
128
  if (device) {
127
129
  throw new Error(`WebGL context already attached to device ${device.id}`);
128
130
  }
129
131
 
130
132
  // Create and instrument context
131
- const canvas = props.gl?.canvas || props.canvas;
132
- this.canvasContext = new WebGLCanvasContext(this, {...props, canvas});
133
+ this.canvasContext = new WebGLCanvasContext(this, canvasContextProps);
133
134
 
134
135
  this.lost = new Promise<{reason: 'destroyed'; message: string}>(resolve => {
135
136
  this._resolveContextLost = resolve;
136
137
  });
137
138
 
138
- this.handle = createBrowserContext(this.canvasContext.canvas, {
139
- ...props,
140
- onContextLost: (event: Event) =>
141
- this._resolveContextLost?.({
142
- reason: 'destroyed',
143
- message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
144
- })
145
- });
146
- this.gl = this.handle;
139
+ const webglContextAttributes: WebGLContextAttributes = {...props.webgl};
140
+ // Copy props from CanvasContextProps
141
+ if (canvasContextProps.alphaMode === 'premultiplied') {
142
+ webglContextAttributes.premultipliedAlpha = true;
143
+ }
144
+ if (props.powerPreference !== undefined) {
145
+ webglContextAttributes.powerPreference = props.powerPreference;
146
+ }
147
147
 
148
- if (!this.handle) {
148
+ const gl = createBrowserContext(
149
+ this.canvasContext.canvas,
150
+ {
151
+ onContextLost: (event: Event) =>
152
+ this._resolveContextLost?.({
153
+ reason: 'destroyed',
154
+ message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
155
+ }),
156
+ // eslint-disable-next-line no-console
157
+ onContextRestored: (event: Event) => console.log('WebGL context restored')
158
+ },
159
+ webglContextAttributes
160
+ );
161
+
162
+ if (!gl) {
149
163
  throw new Error('WebGL context creation failed');
150
164
  }
151
165
 
166
+ // @ts-expect-error device is attached to context
167
+ device = gl.device;
168
+ if (device) {
169
+ throw new Error(`WebGL context already attached to device ${device.id}`);
170
+ }
171
+
172
+ this.handle = gl;
173
+ this.gl = gl;
174
+
152
175
  // Add spector debug instrumentation to context
153
176
  // We need to trust spector integration to decide if spector should be initialized
154
177
  // We also run spector instrumentation first, otherwise spector can clobber luma instrumentation.
@@ -161,8 +184,12 @@ export class WebGLDevice extends Device {
161
184
  // initialize luma Device fields
162
185
  this.info = getDeviceInfo(this.gl, this._extensions);
163
186
  this.limits = new WebGLDeviceLimits(this.gl);
164
- this.features = new WebGLDeviceFeatures(this.gl, this._extensions, this.props.disabledFeatures);
165
- if (this.props.initalizeFeatures) {
187
+ this.features = new WebGLDeviceFeatures(
188
+ this.gl,
189
+ this._extensions,
190
+ this.props._disabledFeatures
191
+ );
192
+ if (this.props._initializeFeatures) {
166
193
  this.features.initializeFeatures();
167
194
  }
168
195
 
@@ -175,11 +202,14 @@ export class WebGLDevice extends Device {
175
202
  glState.trackState(this.gl, {copyState: false});
176
203
 
177
204
  // DEBUG contexts: Add luma debug instrumentation to the context, force log level to at least 1
178
- if (props.debug) {
179
- this.gl = makeDebugContext(this.gl, {...props, throwOnError: true});
180
- this.debug = true;
181
- log.level = Math.max(log.level, 1);
205
+ const debugWebGL = props.debugWebGL || props.debug;
206
+ const traceWebGL = props.debugWebGL;
207
+ if (debugWebGL) {
208
+ this.gl = makeDebugContext(this.gl, {debugWebGL, traceWebGL});
182
209
  log.warn('WebGL debug mode activated. Performance reduced.')();
210
+ if (props.debugWebGL) {
211
+ log.level = Math.max(log.level, 1);
212
+ }
183
213
  }
184
214
  }
185
215
 
@@ -212,7 +242,7 @@ export class WebGLDevice extends Device {
212
242
  }
213
243
 
214
244
  createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WEBGLBuffer {
215
- const newProps = this._getBufferProps(props);
245
+ const newProps = this._normalizeBufferProps(props);
216
246
  return new WEBGLBuffer(this, newProps);
217
247
  }
218
248
 
@@ -398,18 +428,28 @@ export class WebGLDevice extends Device {
398
428
  * Be aware that there are some duplicates especially for constants that are 0,
399
429
  * so this isn't guaranteed to return the right key in all cases.
400
430
  */
401
- getGLKey(value: unknown, gl?: WebGL2RenderingContext): string {
402
- // @ts-ignore expect-error depends on settings
403
- gl = gl || this.gl2 || this.gl;
431
+ getGLKey(value: unknown, options?: {emptyIfUnknown?: boolean}): string {
404
432
  const number = Number(value);
405
- for (const key in gl) {
433
+ for (const key in this.gl) {
406
434
  // @ts-ignore expect-error depends on settings
407
- if (gl[key] === number) {
435
+ if (this.gl[key] === number) {
408
436
  return `GL.${key}`;
409
437
  }
410
438
  }
411
439
  // No constant found. Stringify the value and return it.
412
- return String(value);
440
+ return options?.emptyIfUnknown ? '' : String(value);
441
+ }
442
+
443
+ /**
444
+ * Returns a map with any GL.<KEY> constants mapped to strings, both for keys and values
445
+ */
446
+ getGLKeys(glParameters: Record<number, unknown>): Record<string, string> {
447
+ const opts = {emptyIfUnknown: true};
448
+ return Object.entries(glParameters).reduce<Record<string, string>>((keys, [key, value]) => {
449
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
450
+ keys[`${key}:${this.getGLKey(key, opts)}`] = `${value}:${this.getGLKey(value, opts)}`;
451
+ return keys;
452
+ }, {});
413
453
  }
414
454
 
415
455
  /** Store constants */
@@ -1,5 +1,5 @@
1
1
  // Forked from https://github.com/BabylonJS/Spector.js/blob/master/dist/spector.d.ts
2
- /* eslint-disable camelcase */
2
+ /* eslint-disable camelcase, no-shadow */
3
3
 
4
4
  interface IEvent<T> {
5
5
  add(callback: (element: T) => void, context?: any): number;
@@ -5,14 +5,14 @@
5
5
  import {log} from '@luma.gl/core';
6
6
  import {loadScript} from '../../utils/load-script';
7
7
 
8
- import {Spector} from './spector-types';
8
+ import type {Spector} from './spector-types';
9
9
 
10
10
  /** Spector debug initialization options */
11
11
  type SpectorProps = {
12
- /** Whether spector is enabled */
13
- debugWithSpectorJS?: boolean;
12
+ /** Whether spector.js is enabled */
13
+ debugSpectorJS?: boolean;
14
14
  /** URL to load spector script from. Typically a CDN URL */
15
- spectorUrl?: string;
15
+ debugSpectorJSUrl?: string;
16
16
  /** Canvas to monitor */
17
17
  gl?: WebGL2RenderingContext;
18
18
  };
@@ -29,19 +29,19 @@ declare global {
29
29
  }
30
30
 
31
31
  export const DEFAULT_SPECTOR_PROPS: Required<SpectorProps> = {
32
- debugWithSpectorJS: log.get('spector') || log.get('spectorjs'),
32
+ debugSpectorJS: log.get('debug-spectorjs'),
33
33
  // https://github.com/BabylonJS/Spector.js#basic-usage
34
34
  // https://forum.babylonjs.com/t/spectorcdn-is-temporarily-off/48241
35
35
  // spectorUrl: 'https://spectorcdn.babylonjs.com/spector.bundle.js';
36
- spectorUrl: 'https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js',
36
+ debugSpectorJSUrl: 'https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js',
37
37
  gl: undefined!
38
38
  };
39
39
 
40
40
  /** Loads spector from CDN if not already installed */
41
- export async function loadSpectorJS(props: {spectorUrl?: string}): Promise<void> {
41
+ export async function loadSpectorJS(props: {debugSpectorJSUrl?: string}): Promise<void> {
42
42
  if (!globalThis.SPECTOR) {
43
43
  try {
44
- await loadScript(props.spectorUrl || DEFAULT_SPECTOR_PROPS.spectorUrl);
44
+ await loadScript(props.debugSpectorJSUrl || DEFAULT_SPECTOR_PROPS.debugSpectorJSUrl);
45
45
  } catch (error) {
46
46
  log.warn(String(error));
47
47
  }
@@ -50,14 +50,14 @@ export async function loadSpectorJS(props: {spectorUrl?: string}): Promise<void>
50
50
 
51
51
  export function initializeSpectorJS(props: SpectorProps): Spector | null {
52
52
  props = {...DEFAULT_SPECTOR_PROPS, ...props};
53
- if (!props.debugWithSpectorJS) {
53
+ if (!props.debugSpectorJS) {
54
54
  return null;
55
55
  }
56
56
 
57
57
  if (!spector && globalThis.SPECTOR && !globalThis.luma?.spector) {
58
58
  log.probe(LOG_LEVEL, 'SPECTOR found and initialized. Start with `luma.spector.displayUI()`')();
59
- const {Spector} = globalThis.SPECTOR as any;
60
- spector = new Spector();
59
+ const {Spector: SpectorJS} = globalThis.SPECTOR as any;
60
+ spector = new SpectorJS();
61
61
  if (globalThis.luma) {
62
62
  (globalThis.luma as any).spector = spector;
63
63
  }
@@ -11,18 +11,10 @@ import {loadScript} from '../../utils/load-script';
11
11
  const WEBGL_DEBUG_CDN_URL = 'https://unpkg.com/webgl-debug@2.0.1/index.js';
12
12
 
13
13
  type DebugContextProps = {
14
- debug?: boolean;
15
- throwOnError?: boolean;
16
- break?: string[];
14
+ debugWebGL?: boolean;
15
+ traceWebGL?: boolean;
17
16
  };
18
17
 
19
- // const DEFAULT_DEBUG_CONTEXT_PROPS: Required<DebugContextProps> = {
20
- // debug: true,
21
- // throwOnError: false,
22
- // break: [],
23
- // webgl2: false,
24
- // }
25
-
26
18
  type ContextData = {
27
19
  realContext?: WebGL2RenderingContext;
28
20
  debugContext?: WebGL2RenderingContext;
@@ -60,7 +52,7 @@ export function makeDebugContext(
60
52
  gl: WebGL2RenderingContext,
61
53
  props: DebugContextProps = {}
62
54
  ): WebGL2RenderingContext {
63
- return props.debug ? getDebugContext(gl, props) : getRealContext(gl);
55
+ return props.debugWebGL || props.traceWebGL ? getDebugContext(gl, props) : getRealContext(gl);
64
56
  }
65
57
 
66
58
  // Returns the real context from either of the real/debug contexts
@@ -136,9 +128,7 @@ function onGLError(props: DebugContextProps, err, functionName: string, args: an
136
128
  const message = `${errorMessage} in gl.${functionName}(${functionArgs})`;
137
129
  log.error(message)();
138
130
  debugger; // eslint-disable-line
139
- if (props.throwOnError) {
140
- throw new Error(message);
141
- }
131
+ // throw new Error(message);
142
132
  }
143
133
 
144
134
  // Don't generate function string until it is needed
@@ -150,29 +140,16 @@ function onValidateGLFunc(
150
140
  let functionString: string = '';
151
141
  if (log.level >= 1) {
152
142
  functionString = getFunctionString(functionName, functionArgs);
153
- log.log(1, functionString)();
154
- }
155
-
156
- // If array of breakpoint strings supplied, check if any of them is contained in current GLEnum function
157
- if (props.break && props.break.length > 0) {
158
- functionString = functionString || getFunctionString(functionName, functionArgs);
159
- const isBreakpoint = props.break.every(
160
- (breakOn: string) => functionString.indexOf(breakOn) !== -1
161
- );
162
- if (isBreakpoint) {
163
- debugger; // eslint-disable-line
143
+ if (props.traceWebGL) {
144
+ log.log(1, functionString)();
164
145
  }
165
146
  }
166
147
 
167
148
  for (const arg of functionArgs) {
168
149
  if (arg === undefined) {
169
150
  functionString = functionString || getFunctionString(functionName, functionArgs);
170
- if (props.throwOnError) {
171
- throw new Error(`Undefined argument: ${functionString}`);
172
- } else {
173
- log.error(`Undefined argument: ${functionString}`)();
174
- debugger; // eslint-disable-line
175
- }
151
+ debugger; // eslint-disable-line
152
+ // throw new Error(`Undefined argument: ${functionString}`);
176
153
  }
177
154
  }
178
155
  }
@@ -5,37 +5,13 @@
5
5
  /**
6
6
  * ContextProps
7
7
  * @param onContextLost
8
- * @param onContextRestored
9
- *
10
- * BROWSER CONTEXT PARAMETERS
11
- * @param debug Instrument context (at the expense of performance).
12
- * @param alpha Default render target has an alpha buffer.
13
- * @param depth Default render target has a depth buffer of at least 16 bits.
14
- * @param stencil Default render target has a stencil buffer of at least 8 bits.
15
- * @param antialias Boolean that indicates whether or not to perform anti-aliasing.
16
- * @param premultipliedAlpha Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
17
- * @param preserveDrawingBuffer Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
18
- * @param failIfMajorPerformanceCaveat Do not create if the system performance is low.
8
+ * @param onContextRestored *
19
9
  */
20
10
  type ContextProps = {
21
- onContextLost?: (event: Event) => void;
22
- onContextRestored?: (event: Event) => void;
23
- alpha?: boolean; // indicates if the canvas contains an alpha buffer.
24
- desynchronized?: boolean; // hints the user agent to reduce the latency by desynchronizing the canvas paint cycle from the event loop
25
- antialias?: boolean; // indicates whether or not to perform anti-aliasing.
26
- depth?: boolean; // indicates that the drawing buffer has a depth buffer of at least 16 bits.
27
- failIfMajorPerformanceCaveat?: boolean; // indicates if a context will be created if the system performance is low or if no hardware GPU is available.
28
- powerPreference?: 'default' | 'high-performance' | 'low-power';
29
- premultipliedAlpha?: boolean; // page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
30
- preserveDrawingBuffer?: boolean; // buffers will not be cleared and will preserve their values until cleared or overwritten by the author.
31
- };
32
-
33
- const DEFAULT_CONTEXT_PROPS: ContextProps = {
34
- powerPreference: 'high-performance', // After all, most apps are using WebGL for performance reasons
35
- // eslint-disable-next-line no-console
36
- onContextLost: () => console.error('WebGL context lost'),
37
- // eslint-disable-next-line no-console
38
- onContextRestored: () => console.info('WebGL context restored')
11
+ /** Called when a context is lost */
12
+ onContextLost: (event: Event) => void;
13
+ /** Called when a context is restored */
14
+ onContextRestored: (event: Event) => void;
39
15
  };
40
16
 
41
17
  /**
@@ -45,53 +21,67 @@ const DEFAULT_CONTEXT_PROPS: ContextProps = {
45
21
  */
46
22
  export function createBrowserContext(
47
23
  canvas: HTMLCanvasElement | OffscreenCanvas,
48
- props: ContextProps
24
+ props: ContextProps,
25
+ webglContextAttributes: WebGLContextAttributes
49
26
  ): WebGL2RenderingContext {
50
- props = {...DEFAULT_CONTEXT_PROPS, ...props};
51
-
52
27
  // Try to extract any extra information about why context creation failed
53
- let errorMessage = null;
54
- const onCreateError = error => (errorMessage = error.statusMessage || errorMessage);
55
- canvas.addEventListener('webglcontextcreationerror', onCreateError, false);
56
-
57
- // Create the desired context
58
- let gl: WebGL2RenderingContext | null = null;
28
+ let errorMessage = '';
29
+ // const onCreateError = error => (errorMessage = error.statusMessage || errorMessage);
59
30
 
60
- // props.failIfMajorPerformanceCaveat = true;
31
+ // Avoid multiple listeners?
32
+ // canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
33
+ // canvas.addEventListener('webglcontextcreationerror', onCreateError, false);
61
34
 
62
- // We require webgl2 context
63
- gl ||= canvas.getContext('webgl2', props) as WebGL2RenderingContext;
35
+ const webglProps: WebGLContextAttributes = {
36
+ preserveDrawingBuffer: true,
37
+ // failIfMajorPerformanceCaveat: true,
38
+ ...webglContextAttributes
39
+ };
64
40
 
65
- // Software GPU
66
-
67
- // props.failIfMajorPerformanceCaveat = false;
41
+ // Create the desired context
42
+ let gl: WebGL2RenderingContext | null = null;
68
43
 
69
- // if (!gl && props.webgl1) {
70
- // gl = canvas.getContext('webgl', props);
71
- // }
44
+ // Create a webgl2 context
45
+ gl ||= canvas.getContext('webgl2', webglProps);
46
+ if (webglProps.failIfMajorPerformanceCaveat) {
47
+ errorMessage ||=
48
+ 'Only software GPU is available. Set `failIfMajorPerformanceCaveat: false` to allow.';
49
+ }
72
50
 
73
- // TODO are we removing this listener before giving it a chance to fire?
74
- canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
51
+ // Creation failed with failIfMajorPerformanceCaveat - Try a Software GPU
52
+ if (!gl && !webglContextAttributes.failIfMajorPerformanceCaveat) {
53
+ webglProps.failIfMajorPerformanceCaveat = false;
54
+ gl = canvas.getContext('webgl2', webglProps);
55
+ // @ts-expect-error
56
+ gl.luma ||= {};
57
+ // @ts-expect-error
58
+ gl.luma.softwareRenderer = true;
59
+ }
75
60
 
76
61
  if (!gl) {
77
- throw new Error(`Failed to create WebGL context: ${errorMessage || 'Unknown error'}`);
62
+ gl = canvas.getContext('webgl', {}) as WebGL2RenderingContext;
63
+ if (gl) {
64
+ gl = null;
65
+ errorMessage ||= 'Your browser only supports WebGL1';
66
+ }
78
67
  }
79
68
 
80
- if (props.onContextLost) {
81
- // Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
82
- const {onContextLost} = props;
83
- canvas.addEventListener('webglcontextlost', (event: Event) => onContextLost(event), false);
84
- }
85
- if (props.onContextRestored) {
86
- // Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
87
- const {onContextRestored} = props;
88
- canvas.addEventListener(
89
- 'webglcontextrestored',
90
- (event: Event) => onContextRestored(event),
91
- false
92
- );
69
+ if (!gl) {
70
+ errorMessage ||= 'Your browser does not support WebGL';
71
+ throw new Error(`Failed to create WebGL context: ${errorMessage}`);
93
72
  }
94
73
 
74
+ // Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
75
+ const {onContextLost, onContextRestored} = props;
76
+ canvas.addEventListener('webglcontextlost', (event: Event) => onContextLost(event), false);
77
+ canvas.addEventListener(
78
+ 'webglcontextrestored',
79
+ (event: Event) => onContextRestored(event),
80
+ false
81
+ );
82
+
83
+ // @ts-expect-error
84
+ gl.luma ||= {};
95
85
  return gl;
96
86
  }
97
87