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

visgl / luma.gl / 23446532725

23 Mar 2026 03:52PM UTC coverage: 73.683% (-0.02%) from 73.701%
23446532725

push

github

web-flow
chore: Use biome for lint and prettier (#2565)

4631 of 7113 branches covered (65.11%)

Branch coverage included in aggregate %.

20 of 24 new or added lines in 10 files covered. (83.33%)

5 existing lines in 3 files now uncovered.

10586 of 13539 relevant lines covered (78.19%)

267.15 hits per line

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

55.29
/modules/webgl/src/context/debug/webgl-developer-tools.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import {log} from '@luma.gl/core';
6
// Rename constant to prevent inlining. We need the full set of constants for generating debug strings.
7
import {GL as GLEnum} from '@luma.gl/webgl/constants';
8
import {isBrowser} from '@probe.gl/env';
9
import {loadScript} from '../../utils/load-script';
10

11
const WEBGL_DEBUG_CDN_URL = 'https://unpkg.com/webgl-debug@2.0.1/index.js';
61✔
12

13
type DebugContextProps = {
14
  debugWebGL?: boolean;
15
  traceWebGL?: boolean;
16
};
17

18
type ContextData = {
19
  realContext?: WebGL2RenderingContext;
20
  debugContext?: WebGL2RenderingContext;
21
};
22

23
// Helper to get shared context data
24
function getWebGLContextData(gl: any): ContextData {
25
  gl.luma = gl.luma || {};
49!
26
  return gl.luma;
49✔
27
}
28

29
declare global {
30
  // eslint-disable-next-line no-var
31
  var WebGLDebugUtils: any;
32
}
33

34
/**
35
 * Loads Khronos WebGLDeveloperTools from CDN if not already installed
36
 * const WebGLDebugUtils = require('webgl-debug');
37
 * @see https://github.com/KhronosGroup/WebGLDeveloperTools
38
 * @see https://github.com/vorg/webgl-debug
39
 */
40
export async function loadWebGLDeveloperTools(): Promise<void> {
41
  if (isBrowser() && !globalThis.WebGLDebugUtils) {
49✔
42
    globalThis.global = globalThis.global || globalThis;
48✔
43
    // @ts-expect-error Developer tools expects global to be set
44
    globalThis.global.module = {};
48✔
45
    await loadScript(WEBGL_DEBUG_CDN_URL);
48✔
46
  }
47
}
48

49
// Returns (a potentially new) context with debug instrumentation turned off or on.
50
// Note that this actually returns a new context
51
export function makeDebugContext(
52
  gl: WebGL2RenderingContext,
53
  props: DebugContextProps = {}
49✔
54
): WebGL2RenderingContext {
55
  return props.debugWebGL || props.traceWebGL ? getDebugContext(gl, props) : getRealContext(gl);
49!
56
}
57

58
// Returns the real context from either of the real/debug contexts
59
function getRealContext(gl: WebGL2RenderingContext): WebGL2RenderingContext {
60
  const data = getWebGLContextData(gl);
×
61
  // If the context has a realContext member, it is a debug context so return the realContext
62
  return data.realContext ? data.realContext : gl;
×
63
}
64

65
// Returns the debug context from either of the real/debug contexts
66
function getDebugContext(
67
  gl: WebGL2RenderingContext,
68
  props: DebugContextProps
69
): WebGL2RenderingContext {
70
  if (!globalThis.WebGLDebugUtils) {
49!
71
    log.warn('webgl-debug not loaded')();
×
72
    return gl;
×
73
  }
74

75
  const data = getWebGLContextData(gl);
49✔
76

77
  // If this already has a debug context, return it.
78
  if (data.debugContext) {
49!
79
    return data.debugContext;
×
80
  }
81

82
  // Create a new debug context
83
  globalThis.WebGLDebugUtils.init({...GLEnum, ...gl});
49✔
84
  const glDebug = globalThis.WebGLDebugUtils.makeDebugContext(
49✔
85
    gl,
86
    onGLError.bind(null, props),
87
    onValidateGLFunc.bind(null, props)
88
  );
89

90
  // Make sure we have all WebGL2 and extension constants (todo dynamic import to circumvent minification?)
91
  for (const key in GLEnum) {
49✔
92
    if (!(key in glDebug) && typeof GLEnum[key] === 'number') {
62,671✔
93
      glDebug[key] = GLEnum[key];
5,782✔
94
    }
95
  }
96

97
  // Ensure we have a clean prototype on the instrumented object
98
  // Note: setPrototypeOf does come with perf warnings, but we already take a bigger perf reduction
99
  // by synchronizing the WebGL errors after each WebGL call.
100
  class WebGLDebugContext {}
101
  Object.setPrototypeOf(glDebug, Object.getPrototypeOf(gl));
49✔
102
  Object.setPrototypeOf(WebGLDebugContext, glDebug);
49✔
103
  const debugContext = Object.create(WebGLDebugContext);
49✔
104
  // Store the debug context
105
  data.realContext = gl;
49✔
106
  data.debugContext = debugContext;
49✔
107
  // Share the context metadata object with the debug context so lookups stay consistent.
108
  (debugContext as {luma?: unknown}).luma = data;
49✔
109
  debugContext.debug = true;
49✔
110

111
  // Return it
112
  return debugContext;
49✔
113
}
114

115
// DEBUG TRACING
116

117
function getFunctionString(functionName: string, functionArgs: unknown[]): string {
118
  // Cover bug in webgl-debug-tools
119
  functionArgs = Array.from(functionArgs).map(arg => (arg === undefined ? 'undefined' : arg));
×
120
  let args = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, functionArgs);
×
121
  args = `${args.slice(0, 100)}${args.length > 100 ? '...' : ''}`;
×
122
  return `gl.${functionName}(${args})`;
×
123
}
124

125
function onGLError(
126
  props: DebugContextProps,
127
  err: number,
128
  functionName: string,
129
  args: unknown[]
130
): void {
131
  // Cover bug in webgl-debug-tools
132
  args = Array.from(args).map(arg => (arg === undefined ? 'undefined' : arg));
×
133
  const errorMessage = globalThis.WebGLDebugUtils.glEnumToString(err);
×
134
  const functionArgs = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, args);
×
135
  const message = `${errorMessage} in gl.${functionName}(${functionArgs})`;
×
136
  // TODO - call device.reportError
137
  log.error(
×
138
    '%cWebGL',
139
    'color: white; background: red; padding: 2px 6px; border-radius: 3px;',
140
    message
141
  )();
142
  // biome-ignore lint/suspicious/noDebugger: pause immediately on WebGL debug utility errors.
NEW
143
  debugger;
×
UNCOV
144
  throw new Error(message);
×
145
}
146

147
// Don't generate function string until it is needed
148
function onValidateGLFunc(
149
  props: DebugContextProps,
150
  functionName: string,
151
  functionArgs: unknown[]
152
): void {
153
  let functionString: string = '';
19,910✔
154
  if (props.traceWebGL && log.level >= 1) {
19,910!
155
    functionString = getFunctionString(functionName, functionArgs);
×
156
    log.info(
×
157
      1,
158
      '%cWebGL',
159
      'color: white; background: blue; padding: 2px 6px; border-radius: 3px;',
160
      functionString
161
    )();
162
  }
163

164
  for (const arg of functionArgs) {
19,910✔
165
    if (arg === undefined) {
38,328!
166
      functionString = functionString || getFunctionString(functionName, functionArgs);
×
167
      // biome-ignore lint/suspicious/noDebugger: pause when validating undefined WebGL call arguments.
NEW
168
      debugger;
×
169
      // throw new Error(`Undefined argument: ${functionString}`);
170
    }
171
  }
172
}
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