• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / luma.gl / 14683349798

26 Apr 2025 05:08PM UTC coverage: 74.055% (-0.9%) from 74.913%
14683349798

push

github

web-flow
feat(core): TextureFormat generics (#2377)

2019 of 2652 branches covered (76.13%)

Branch coverage included in aggregate %.

62 of 262 new or added lines in 15 files covered. (23.66%)

196 existing lines in 9 files now uncovered.

26575 of 35960 relevant lines covered (73.9%)

47.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.06
/modules/webgl/src/adapter/webgl-device.ts
1
// luma.gl
1✔
2
// SPDX-License-Identifier: MIT
1✔
3
// Copyright (c) vis.gl contributors
1✔
4

1✔
5
import type {TypedArray} from '@math.gl/types';
1✔
6
import type {
1✔
7
  DeviceProps,
1✔
8
  DeviceInfo,
1✔
9
  DeviceTextureFormatCapabilities,
1✔
10
  CanvasContextProps,
1✔
11
  Buffer,
1✔
12
  Texture,
1✔
13
  Framebuffer,
1✔
14
  VertexArray,
1✔
15
  VertexArrayProps,
1✔
16
  BufferProps,
1✔
17
  ShaderProps,
1✔
18
  // Sampler,
1✔
19
  SamplerProps,
1✔
20
  TextureProps,
1✔
21
  ExternalTexture,
1✔
22
  ExternalTextureProps,
1✔
23
  FramebufferProps,
1✔
24
  // RenderPipeline,
1✔
25
  RenderPipelineProps,
1✔
26
  ComputePipeline,
1✔
27
  ComputePipelineProps,
1✔
28
  // CommandEncoder,
1✔
29
  CommandEncoderProps,
1✔
30
  TransformFeedbackProps,
1✔
31
  QuerySetProps,
1✔
32
  Resource,
1✔
33
  VertexFormat
1✔
34
} from '@luma.gl/core';
1✔
35
import {Device, CanvasContext, log} from '@luma.gl/core';
1✔
36
import type {GLExtensions} from '@luma.gl/constants';
1✔
37
import {WebGLStateTracker} from '../context/state-tracker/webgl-state-tracker';
1✔
38
import {createBrowserContext} from '../context/helpers/create-browser-context';
1✔
39
import {getDeviceInfo} from './device-helpers/webgl-device-info';
1✔
40
import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
1✔
41
import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
1✔
42
import {WebGLCanvasContext} from './webgl-canvas-context';
1✔
43
import type {Spector} from '../context/debug/spector-types';
1✔
44
import {initializeSpectorJS} from '../context/debug/spector';
1✔
45
import {makeDebugContext} from '../context/debug/webgl-developer-tools';
1✔
46
import {getTextureFormatCapabilitiesWebGL} from './converters/webgl-texture-table';
1✔
47
import {uid} from '../utils/uid';
1✔
48

1✔
49
import {WEBGLBuffer} from './resources/webgl-buffer';
1✔
50
import {WEBGLShader} from './resources/webgl-shader';
1✔
51
import {WEBGLSampler} from './resources/webgl-sampler';
1✔
52
import {WEBGLTexture} from './resources/webgl-texture';
1✔
53
import {WEBGLFramebuffer} from './resources/webgl-framebuffer';
1✔
54
import {WEBGLRenderPipeline} from './resources/webgl-render-pipeline';
1✔
55
import {WEBGLCommandEncoder} from './resources/webgl-command-encoder';
1✔
56
import {WEBGLCommandBuffer} from './resources/webgl-command-buffer';
1✔
57
import {WEBGLVertexArray} from './resources/webgl-vertex-array';
1✔
58
import {WEBGLTransformFeedback} from './resources/webgl-transform-feedback';
1✔
59
import {WEBGLQuerySet} from './resources/webgl-query-set';
1✔
60

1✔
61
import {readPixelsToArray, readPixelsToBuffer} from './helpers/webgl-texture-utils';
1✔
62
import {
1✔
63
  setGLParameters,
1✔
64
  getGLParameters,
1✔
65
  resetGLParameters
1✔
66
} from '../context/parameters/unified-parameter-api';
1✔
67
import {withGLParameters} from '../context/state-tracker/with-parameters';
1✔
68
import {getWebGLExtension} from '../context/helpers/webgl-extensions';
1✔
69

1✔
70
/** WebGPU style Device API for a WebGL context */
1✔
71
export class WebGLDevice extends Device {
1✔
72
  // Public `Device` API
4✔
73

4✔
74
  /** type of this device */
4✔
75
  readonly type = 'webgl';
4✔
76

4✔
77
  // Use the ! assertion to handle the case where _reuseDevices causes the constructor to return early
4✔
78
  /** The underlying WebGL context */
4✔
79
  readonly handle!: WebGL2RenderingContext;
4✔
80
  features!: WebGLDeviceFeatures;
4✔
81
  limits!: WebGLDeviceLimits;
4✔
82
  readonly info!: DeviceInfo;
4✔
83
  readonly canvasContext!: WebGLCanvasContext;
4✔
84

4✔
85
  readonly preferredColorFormat = 'rgba8unorm';
4✔
86
  readonly preferredDepthFormat = 'depth24plus';
4✔
87

4✔
88
  commandEncoder!: WEBGLCommandEncoder;
4✔
89

4✔
90
  readonly lost: Promise<{reason: 'destroyed'; message: string}>;
4✔
91

4✔
92
  private _resolveContextLost?: (value: {reason: 'destroyed'; message: string}) => void;
4✔
93

4✔
94
  /** WebGL2 context. */
4✔
95
  readonly gl!: WebGL2RenderingContext;
4✔
96

4✔
97
  /** Store constants */
4✔
98
  // @ts-ignore TODO fix
4✔
99
  _constants: (TypedArray | null)[];
4✔
100

4✔
101
  /** State used by luma.gl classes - TODO - not used? */
4✔
102
  readonly _extensions: GLExtensions = {};
4✔
103
  _polyfilled: boolean = false;
4✔
104

4✔
105
  /** Instance of Spector.js (if initialized) */
4✔
106
  spectorJS!: Spector | null;
4✔
107

4✔
108
  //
4✔
109
  // Public API
4✔
110
  //
4✔
111

4✔
112
  override get [Symbol.toStringTag](): string {
4✔
113
    return 'WebGLDevice';
4✔
114
  }
4✔
115

4✔
116
  override toString(): string {
4✔
117
    return `${this[Symbol.toStringTag]}(${this.id})`;
4✔
118
  }
4✔
119

4✔
120
  override isVertexFormatSupported(format: VertexFormat): boolean {
4✔
121
    switch (format) {
×
122
      case 'unorm8x4-bgra':
×
123
        return false;
×
124
      default:
×
125
        return true;
×
126
    }
×
127
  }
×
128

4✔
129
  constructor(props: DeviceProps) {
4✔
130
    super({...props, id: props.id || uid('webgl-device')});
4✔
131

4✔
132
    const canvasContextProps = Device._getCanvasContextProps(props)!;
4✔
133

4✔
134
    // WebGL requires a canvas to be created before creating the context
4✔
135
    if (!canvasContextProps) {
4!
136
      throw new Error('WebGLDevice requires props.createCanvasContext to be set');
×
137
    }
×
138

4✔
139
    // Check if the WebGL context is already associated with a device
4✔
140
    // Note that this can be avoided in webgl2adapter.create() if
4✔
141
    // DeviceProps._reuseDevices is set.
4✔
142
    // @ts-expect-error device is attached to context
4✔
143
    let device: WebGLDevice | undefined = canvasContextProps.canvas?.gl?.device;
4!
144
    if (device) {
4!
145
      throw new Error(`WebGL context already attached to device ${device.id}`);
×
146
    }
×
147

4✔
148
    // Create and instrument context
4✔
149
    this.canvasContext = new WebGLCanvasContext(this, canvasContextProps);
4✔
150

4✔
151
    this.lost = new Promise<{reason: 'destroyed'; message: string}>(resolve => {
4✔
152
      this._resolveContextLost = resolve;
4✔
153
    });
4✔
154

4✔
155
    const webglContextAttributes: WebGLContextAttributes = {...props.webgl};
4✔
156
    // Copy props from CanvasContextProps
4✔
157
    if (canvasContextProps.alphaMode === 'premultiplied') {
4!
158
      webglContextAttributes.premultipliedAlpha = true;
×
159
    }
×
160
    if (props.powerPreference !== undefined) {
4✔
161
      webglContextAttributes.powerPreference = props.powerPreference;
2✔
162
    }
2✔
163

4✔
164
    // Check if we should attach to an externally created context or create a new context
4✔
165
    const externalGLContext = this.props._handle as WebGL2RenderingContext | null;
4✔
166

4✔
167
    const gl =
4✔
168
      externalGLContext ||
4✔
169
      createBrowserContext(
4✔
170
        this.canvasContext.canvas,
4✔
171
        {
4✔
172
          onContextLost: (event: Event) =>
4✔
173
            this._resolveContextLost?.({
1✔
174
              reason: 'destroyed',
1✔
175
              message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
1✔
176
            }),
1✔
177
          // eslint-disable-next-line no-console
4✔
178
          onContextRestored: (event: Event) => console.log('WebGL context restored')
4✔
179
        },
4✔
180
        webglContextAttributes
4✔
181
      );
4✔
182

4✔
183
    if (!gl) {
4!
184
      throw new Error('WebGL context creation failed');
×
185
    }
×
186

4✔
187
    // Note that the browser will only create one WebGL context per canvas.
4✔
188
    // This means that a newly created gl context may already have a device attached to it.
4✔
189
    // @ts-expect-error luma.gl stores a device reference on the context.
4✔
190
    device = gl.device;
4✔
191
    if (device) {
4!
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
      }
×
201
      throw new Error(`WebGL context already attached to device ${device.id}`);
×
202
    }
×
203

4✔
204
    this.handle = gl;
4✔
205
    this.gl = gl;
4✔
206

4✔
207
    // Add spector debug instrumentation to context
4✔
208
    // We need to trust spector integration to decide if spector should be initialized
4✔
209
    // We also run spector instrumentation first, otherwise spector can clobber luma instrumentation.
4✔
210
    this.spectorJS = initializeSpectorJS({...this.props, gl: this.handle});
4✔
211

4✔
212
    // Instrument context
4✔
213
    (this.gl as any).device = this; // Update GL context: Link webgl context back to device
4✔
214
    // TODO - remove, this is only used to detect debug contexts.
4✔
215
    (this.gl as any)._version = 2; // Update GL context: Store WebGL version field on gl context (HACK to identify debug contexts)
4✔
216

4✔
217
    // initialize luma Device fields
4✔
218
    this.info = getDeviceInfo(this.gl, this._extensions);
4✔
219
    this.limits = new WebGLDeviceLimits(this.gl);
4✔
220
    this.features = new WebGLDeviceFeatures(
4✔
221
      this.gl,
4✔
222
      this._extensions,
4✔
223
      this.props._disabledFeatures
4✔
224
    );
4✔
225
    if (this.props._initializeFeatures) {
4✔
226
      this.features.initializeFeatures();
4✔
227
    }
4✔
228

4✔
229
    // Install context state tracking
4✔
230
    const glState = new WebGLStateTracker(this.gl, {
4✔
231
      log: (...args: any[]) => log.log(1, ...args)()
4✔
232
    });
4✔
233
    glState.trackState(this.gl, {copyState: false});
4✔
234

4✔
235
    // DEBUG contexts: Add luma debug instrumentation to the context, force log level to at least 1
4✔
236
    const debugWebGL = props.debugWebGL || props.debug;
4✔
237
    const traceWebGL = props.debugWebGL;
4✔
238
    if (debugWebGL) {
4✔
239
      this.gl = makeDebugContext(this.gl, {debugWebGL, traceWebGL});
1✔
240
      log.warn('WebGL debug mode activated. Performance reduced.')();
1✔
241
      if (props.debugWebGL) {
1✔
242
        log.level = Math.max(log.level, 1);
1✔
243
      }
1✔
244
    }
1✔
245

4✔
246
    this.commandEncoder = new WEBGLCommandEncoder(this, {id: `${this}-command-encoder`});
4✔
247
  }
4✔
248

4✔
249
  /**
4✔
250
   * Destroys the device
4✔
251
   *
4✔
252
   * @note "Detaches" from the WebGL context unless _reuseDevices is true.
4✔
253
   *
4✔
254
   * @note The underlying WebGL context is not immediately destroyed,
4✔
255
   * but may be destroyed later through normal JavaScript garbage collection.
4✔
256
   * This is a fundamental limitation since WebGL does not offer any
4✔
257
   * browser API for destroying WebGL contexts.
4✔
258
   */
4✔
259
  destroy(): void {
4✔
260
    // Note that deck.gl (especially in React strict mode) depends on being able
1✔
261
    // to asynchronously create a Device against the same canvas (i.e. WebGL context)
1✔
262
    // multiple times and getting the same device back. Since deck.gl is not aware
1✔
263
    // of this sharing, it might call destroy() multiple times on the same device.
1✔
264
    // Therefore we must do nothing in destroy() if props._reuseDevices is true
1✔
265
    if (!this.props._reuseDevices && !this._reused) {
1✔
266
      // Delete the reference to the device that we store on the WebGL context
1✔
267
      delete (this.gl as any).device;
1✔
268
    }
1✔
269
  }
1✔
270

4✔
271
  get isLost(): boolean {
4✔
272
    return this.gl.isContextLost();
10✔
273
  }
10✔
274

4✔
275
  // IMPLEMENTATION OF ABSTRACT DEVICE
4✔
276

4✔
277
  getTextureByteAlignment(): number {
4✔
NEW
278
    return 4;
×
NEW
279
  }
×
280

4✔
281
  createCanvasContext(props?: CanvasContextProps): CanvasContext {
4✔
282
    throw new Error('WebGL only supports a single canvas');
×
283
  }
×
284

4✔
285
  createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WEBGLBuffer {
4✔
286
    const newProps = this._normalizeBufferProps(props);
69✔
287
    return new WEBGLBuffer(this, newProps);
69✔
288
  }
69✔
289

4✔
290
  createTexture(props: TextureProps): WEBGLTexture {
4✔
291
    return new WEBGLTexture(this, props);
51✔
292
  }
51✔
293

4✔
294
  createExternalTexture(props: ExternalTextureProps): ExternalTexture {
4✔
295
    throw new Error('createExternalTexture() not implemented'); // return new Program(props);
×
296
  }
×
297

4✔
298
  createSampler(props: SamplerProps): WEBGLSampler {
4✔
299
    return new WEBGLSampler(this, props);
56✔
300
  }
56✔
301

4✔
302
  createShader(props: ShaderProps): WEBGLShader {
4✔
303
    return new WEBGLShader(this, props);
44✔
304
  }
44✔
305

4✔
306
  createFramebuffer(props: FramebufferProps): WEBGLFramebuffer {
4✔
307
    return new WEBGLFramebuffer(this, props);
38✔
308
  }
38✔
309

4✔
310
  createVertexArray(props: VertexArrayProps): VertexArray {
4✔
311
    return new WEBGLVertexArray(this, props);
22✔
312
  }
22✔
313

4✔
314
  createTransformFeedback(props: TransformFeedbackProps): WEBGLTransformFeedback {
4✔
315
    return new WEBGLTransformFeedback(this, props);
5✔
316
  }
5✔
317

4✔
318
  createQuerySet(props: QuerySetProps): WEBGLQuerySet {
4✔
319
    return new WEBGLQuerySet(this, props);
1✔
320
  }
1✔
321

4✔
322
  createRenderPipeline(props: RenderPipelineProps): WEBGLRenderPipeline {
4✔
323
    return new WEBGLRenderPipeline(this, props);
22✔
324
  }
22✔
325

4✔
326
  createComputePipeline(props?: ComputePipelineProps): ComputePipeline {
4✔
327
    throw new Error('ComputePipeline not supported in WebGL');
×
328
  }
×
329

4✔
330
  override createCommandEncoder(props: CommandEncoderProps = {}): WEBGLCommandEncoder {
4✔
331
    return new WEBGLCommandEncoder(this, props);
31✔
332
  }
31✔
333

4✔
334
  /**
4✔
335
   * Offscreen Canvas Support: Commit the frame
4✔
336
   * https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/commit
4✔
337
   * Chrome's offscreen canvas does not require gl.commit
4✔
338
   */
4✔
339
  submit(commandBuffer: WEBGLCommandBuffer): void {
4✔
340
    if (!commandBuffer) {
31✔
341
      commandBuffer = this.commandEncoder.finish();
13✔
342
      this.commandEncoder.destroy();
13✔
343
      this.commandEncoder = this.createCommandEncoder({id: `${this.id}-default-encoder`});
13✔
344
    }
13✔
345

31✔
346
    commandBuffer._executeCommands();
31✔
347
  }
31✔
348

4✔
349
  //
4✔
350
  // TEMPORARY HACKS - will be removed in v9.1
4✔
351
  //
4✔
352

4✔
353
  /** @deprecated - should use command encoder */
4✔
354
  override readPixelsToArrayWebGL(
4✔
355
    source: Framebuffer | Texture,
6✔
356
    options?: {
6✔
357
      sourceX?: number;
6✔
358
      sourceY?: number;
6✔
359
      sourceFormat?: number;
6✔
360
      sourceAttachment?: number;
6✔
361
      target?: Uint8Array | Uint16Array | Float32Array;
6✔
362
      // following parameters are auto deduced if not provided
6✔
363
      sourceWidth?: number;
6✔
364
      sourceHeight?: number;
6✔
365
      sourceType?: number;
6✔
366
    }
6✔
367
  ): Uint8Array | Uint16Array | Float32Array {
6✔
368
    return readPixelsToArray(source, options);
6✔
369
  }
6✔
370

4✔
371
  /** @deprecated - should use command encoder */
4✔
372
  override readPixelsToBufferWebGL(
4✔
373
    source: Framebuffer | Texture,
×
374
    options?: {
×
375
      sourceX?: number;
×
376
      sourceY?: number;
×
377
      sourceFormat?: number;
×
378
      target?: Buffer; // A new Buffer object is created when not provided.
×
379
      targetByteOffset?: number; // byte offset in buffer object
×
380
      // following parameters are auto deduced if not provided
×
381
      sourceWidth?: number;
×
382
      sourceHeight?: number;
×
383
      sourceType?: number;
×
384
    }
×
385
  ): Buffer {
×
386
    return readPixelsToBuffer(source, options);
×
387
  }
×
388

4✔
389
  override setParametersWebGL(parameters: any): void {
4✔
390
    setGLParameters(this.gl, parameters);
×
391
  }
×
392

4✔
393
  override getParametersWebGL(parameters: any): any {
4✔
394
    return getGLParameters(this.gl, parameters);
×
395
  }
×
396

4✔
397
  override withParametersWebGL(parameters: any, func: any): any {
4✔
398
    return withGLParameters(this.gl, parameters, func);
×
399
  }
×
400

4✔
401
  override resetWebGL(): void {
4✔
402
    log.warn('WebGLDevice.resetWebGL is deprecated, use only for debugging')();
×
403
    resetGLParameters(this.gl);
×
404
  }
×
405

4✔
406
  override _getDeviceSpecificTextureFormatCapabilities(
4✔
407
    capabilities: DeviceTextureFormatCapabilities
8✔
408
  ): DeviceTextureFormatCapabilities {
8✔
409
    return getTextureFormatCapabilitiesWebGL(this.gl, capabilities, this._extensions);
8✔
410
  }
8✔
411

4✔
412
  //
4✔
413
  // WebGL-only API (not part of `Device` API)
4✔
414
  //
4✔
415

4✔
416
  /**
4✔
417
   * Triggers device (or WebGL context) loss.
4✔
418
   * @note primarily intended for testing how application reacts to device loss
4✔
419
   */
4✔
420
  override loseDevice(): boolean {
4✔
421
    let deviceLossTriggered = false;
1✔
422
    const extensions = this.getExtension('WEBGL_lose_context');
1✔
423
    const ext = extensions.WEBGL_lose_context;
1✔
424
    if (ext) {
1✔
425
      deviceLossTriggered = true;
1✔
426
      ext.loseContext();
1✔
427
      // ext.loseContext should trigger context loss callback but the platform may not do this, so do it explicitly
1✔
428
    }
1✔
429
    this._resolveContextLost?.({
1✔
430
      reason: 'destroyed',
1✔
431
      message: 'Application triggered context loss'
1✔
432
    });
1✔
433
    return deviceLossTriggered;
1✔
434
  }
1✔
435

4✔
436
  /** Save current WebGL context state onto an internal stack */
4✔
437
  pushState(): void {
4✔
438
    const webglState = WebGLStateTracker.get(this.gl);
15✔
439
    webglState.push();
15✔
440
  }
15✔
441

4✔
442
  /** Restores previously saved context state */
4✔
443
  popState(): void {
4✔
444
    const webglState = WebGLStateTracker.get(this.gl);
14✔
445
    webglState.pop();
14✔
446
  }
14✔
447

4✔
448
  /**
4✔
449
   * Returns the GL.<KEY> constant that corresponds to a numeric value of a GL constant
4✔
450
   * Be aware that there are some duplicates especially for constants that are 0,
4✔
451
   * so this isn't guaranteed to return the right key in all cases.
4✔
452
   */
4✔
453
  getGLKey(value: unknown, options?: {emptyIfUnknown?: boolean}): string {
4✔
454
    const number = Number(value);
1,115✔
455
    for (const key in this.gl) {
1,115✔
456
      // @ts-ignore expect-error depends on settings
320,389✔
457
      if (this.gl[key] === number) {
320,389✔
458
        return `GL.${key}`;
1,063✔
459
      }
1,063✔
460
    }
320,389✔
461
    // No constant found. Stringify the value and return it.
52✔
462
    return options?.emptyIfUnknown ? '' : String(value);
1,115!
463
  }
1,115✔
464

4✔
465
  /**
4✔
466
   * Returns a map with any GL.<KEY> constants mapped to strings, both for keys and values
4✔
467
   */
4✔
468
  getGLKeys(glParameters: Record<number, unknown>): Record<string, string> {
4✔
469
    const opts = {emptyIfUnknown: true};
51✔
470
    return Object.entries(glParameters).reduce<Record<string, string>>((keys, [key, value]) => {
51✔
471
      // eslint-disable-next-line @typescript-eslint/no-base-to-string
459✔
472
      keys[`${key}:${this.getGLKey(key, opts)}`] = `${value}:${this.getGLKey(value, opts)}`;
459✔
473
      return keys;
459✔
474
    }, {});
51✔
475
  }
51✔
476

4✔
477
  /**
4✔
478
   * Set a constant value for a location. Disabled attributes at that location will read from this value
4✔
479
   * @note WebGL constants are stored globally on the WebGL context, not the VertexArray
4✔
480
   * so they need to be updated before every render
4✔
481
   * @todo - remember/cache values to avoid setting them unnecessarily?
4✔
482
   */
4✔
483
  setConstantAttributeWebGL(location: number, constant: TypedArray): void {
4✔
484
    const maxVertexAttributes = this.limits.maxVertexAttributes;
×
485
    this._constants = this._constants || new Array(maxVertexAttributes).fill(null);
×
486
    const currentConstant = this._constants[location];
×
487
    if (currentConstant && compareConstantArrayValues(currentConstant, constant)) {
×
488
      log.info(
×
489
        1,
×
490
        `setConstantAttributeWebGL(${location}) could have been skipped, value unchanged`
×
491
      )();
×
492
    }
×
493
    this._constants[location] = constant;
×
494

×
495
    switch (constant.constructor) {
×
496
      case Float32Array:
×
497
        setConstantFloatArray(this, location, constant as Float32Array);
×
498
        break;
×
499
      case Int32Array:
×
500
        setConstantIntArray(this, location, constant as Int32Array);
×
501
        break;
×
502
      case Uint32Array:
×
503
        setConstantUintArray(this, location, constant as Uint32Array);
×
504
        break;
×
505
      default:
×
506
        throw new Error('constant');
×
507
    }
×
508
  }
×
509

4✔
510
  /** Ensure extensions are only requested once */
4✔
511
  getExtension(name: keyof GLExtensions): GLExtensions {
4✔
512
    getWebGLExtension(this.gl, name, this._extensions);
1✔
513
    return this._extensions;
1✔
514
  }
1✔
515

4✔
516
  // INTERNAL SUPPORT METHODS FOR WEBGL RESOURCES
4✔
517

4✔
518
  /**
4✔
519
   * Storing data on a special field on WebGLObjects makes that data visible in SPECTOR chrome debug extension
4✔
520
   * luma.gl ids and props can be inspected
4✔
521
   */
4✔
522
  _setWebGLDebugMetadata(
4✔
523
    handle: unknown,
224✔
524
    resource: Resource<any>,
224✔
525
    options: {spector: Record<string, unknown>}
224✔
526
  ): void {
224✔
527
    // @ts-expect-error
224✔
528
    handle.luma = resource;
224✔
529

224✔
530
    const spectorMetadata = {props: options.spector, id: options.spector.id};
224✔
531
    // @ts-expect-error
224✔
532
    // eslint-disable-next-line camelcase
224✔
533
    handle.__SPECTOR_Metadata = spectorMetadata;
224✔
534
  }
224✔
535
}
4✔
536

1✔
537
/** Set constant float array attribute */
1✔
538
function setConstantFloatArray(device: WebGLDevice, location: number, array: Float32Array): void {
×
539
  switch (array.length) {
×
540
    case 1:
×
541
      device.gl.vertexAttrib1fv(location, array);
×
542
      break;
×
543
    case 2:
×
544
      device.gl.vertexAttrib2fv(location, array);
×
545
      break;
×
546
    case 3:
×
547
      device.gl.vertexAttrib3fv(location, array);
×
548
      break;
×
549
    case 4:
×
550
      device.gl.vertexAttrib4fv(location, array);
×
551
      break;
×
552
    default:
×
553
    // assert(false);
×
554
  }
×
555
}
×
556

1✔
557
/** Set constant signed int array attribute */
1✔
558
function setConstantIntArray(device: WebGLDevice, location: number, array: Int32Array): void {
×
559
  device.gl.vertexAttribI4iv(location, array);
×
560
  // TODO - not clear if we need to use the special forms, more testing needed
×
561
  // switch (array.length) {
×
562
  //   case 1:
×
563
  //     gl.vertexAttribI1iv(location, array);
×
564
  //     break;
×
565
  //   case 2:
×
566
  //     gl.vertexAttribI2iv(location, array);
×
567
  //     break;
×
568
  //   case 3:
×
569
  //     gl.vertexAttribI3iv(location, array);
×
570
  //     break;
×
571
  //   case 4:
×
572
  //     break;
×
573
  //   default:
×
574
  //     assert(false);
×
575
  // }
×
576
}
×
577

1✔
578
/** Set constant unsigned int array attribute */
1✔
579
function setConstantUintArray(device: WebGLDevice, location: number, array: Uint32Array) {
×
580
  device.gl.vertexAttribI4uiv(location, array);
×
581
  // TODO - not clear if we need to use the special forms, more testing needed
×
582
  // switch (array.length) {
×
583
  //   case 1:
×
584
  //     gl.vertexAttribI1uiv(location, array);
×
585
  //     break;
×
586
  //   case 2:
×
587
  //     gl.vertexAttribI2uiv(location, array);
×
588
  //     break;
×
589
  //   case 3:
×
590
  //     gl.vertexAttribI3uiv(location, array);
×
591
  //     break;
×
592
  //   case 4:
×
593
  //     gl.vertexAttribI4uiv(location, array);
×
594
  //     break;
×
595
  //   default:
×
596
  //     assert(false);
×
597
  // }
×
598
}
×
599

1✔
600
/**
1✔
601
 * Compares contents of two typed arrays
1✔
602
 * @todo max length?
1✔
603
 */
1✔
604
function compareConstantArrayValues(v1: TypedArray, v2: TypedArray): boolean {
×
605
  if (!v1 || !v2 || v1.length !== v2.length || v1.constructor !== v2.constructor) {
×
606
    return false;
×
607
  }
×
608
  for (let i = 0; i < v1.length; ++i) {
×
609
    if (v1[i] !== v2[i]) {
×
610
      return false;
×
611
    }
×
612
  }
×
613
  return true;
×
614
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc