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

visgl / luma.gl / 16861551431

10 Aug 2025 12:43PM UTC coverage: 74.238% (+0.4%) from 73.868%
16861551431

push

github

web-flow
chore: Tests for texture reads (#2416)

2088 of 2725 branches covered (76.62%)

Branch coverage included in aggregate %.

39 of 70 new or added lines in 7 files covered. (55.71%)

12 existing lines in 4 files now uncovered.

27182 of 36702 relevant lines covered (74.06%)

57.03 hits per line

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

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

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

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

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

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

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

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

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

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

1✔
58
// Returns the real context from either of the real/debug contexts
1✔
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

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

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

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

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

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

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

1✔
109
  // Return it
1✔
110
  return debugContext;
1✔
111
}
1✔
112

1✔
113
// DEBUG TRACING
1✔
114

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

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

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

6,284✔
161
  for (const arg of functionArgs) {
6,284✔
162
    if (arg === undefined) {
12,282!
163
      functionString = functionString || getFunctionString(functionName, functionArgs);
×
164
      debugger; // eslint-disable-line
×
165
      // throw new Error(`Undefined argument: ${functionString}`);
×
166
    }
×
167
  }
12,282✔
168
}
6,284✔
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