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

visgl / luma.gl / 23357510199

20 Mar 2026 06:40PM UTC coverage: 58.158% (+5.9%) from 52.213%
23357510199

push

github

web-flow
chore: Run tests on src instead of dist (#2555)

3021 of 6029 branches covered (50.11%)

Branch coverage included in aggregate %.

7102 of 11377 relevant lines covered (62.42%)

243.33 hits per line

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

69.41
/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/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';
57✔
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 || {};
51!
26
  return gl.luma;
51✔
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) {
51✔
42
    globalThis.global = globalThis.global || globalThis;
45✔
43
    // @ts-expect-error Developer tools expects global to be set
44
    globalThis.global.module = {};
45✔
45
    await loadScript(WEBGL_DEBUG_CDN_URL);
45✔
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 = {}
51✔
54
): WebGL2RenderingContext {
55
  return props.debugWebGL || props.traceWebGL ? getDebugContext(gl, props) : getRealContext(gl);
51!
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) {
51!
71
    log.warn('webgl-debug not loaded')();
×
72
    return gl;
×
73
  }
74

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

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

82
  // Create a new debug context
83
  globalThis.WebGLDebugUtils.init({...GLEnum, ...gl});
51✔
84
  const glDebug = globalThis.WebGLDebugUtils.makeDebugContext(
51✔
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) {
51✔
92
    if (!(key in glDebug) && typeof GLEnum[key] === 'number') {
65,229✔
93
      glDebug[key] = GLEnum[key];
6,018✔
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));
51✔
102
  Object.setPrototypeOf(WebGLDebugContext, glDebug);
51✔
103
  const debugContext = Object.create(WebGLDebugContext);
51✔
104
  // Store the debug context
105
  data.realContext = gl;
51✔
106
  data.debugContext = debugContext;
51✔
107
  // Share the context metadata object with the debug context so lookups stay consistent.
108
  (debugContext as {luma?: unknown}).luma = data;
51✔
109
  debugContext.debug = true;
51✔
110

111
  // Return it
112
  return debugContext;
51✔
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));
4✔
120
  let args = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, functionArgs);
2✔
121
  args = `${args.slice(0, 100)}${args.length > 100 ? '...' : ''}`;
2!
122
  return `gl.${functionName}(${args})`;
2✔
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
  debugger; // eslint-disable-line
×
143
  throw new Error(message);
×
144
}
145

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

163
  for (const arg of functionArgs) {
18,586✔
164
    if (arg === undefined) {
35,874✔
165
      functionString = functionString || getFunctionString(functionName, functionArgs);
2✔
166
      debugger; // eslint-disable-line
2✔
167
      // throw new Error(`Undefined argument: ${functionString}`);
168
    }
169
  }
170
}
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