Skip to content

Commit

Permalink
fix(webgl): Fix spectorjs integration
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Apr 29, 2024
1 parent 32d9fff commit cf32213
Show file tree
Hide file tree
Showing 8 changed files with 1,256 additions and 70 deletions.
10 changes: 8 additions & 2 deletions modules/core/src/adapter/device.ts
Expand Up @@ -227,8 +227,11 @@ export type DeviceProps = {
debug?: boolean;
/** Break on WebGL functions matching these strings */
break?: string[];

/** WebGL: Initialize the SpectorJS WebGL debugger */
spector?: boolean;
debugWithSpectorJS?: boolean;
/** SpectorJS URL. Override if CDN is down or different SpectorJS version is desired */
spectorUrl?: string;

// EXPERIMENTAL SETTINGS
/** Set to false to disable WebGL state management instrumentation: TODO- Unclear if still supported / useful */
Expand Down Expand Up @@ -279,9 +282,12 @@ export abstract class Device {
// failIfMajorPerformanceCaveat: undefined

debug: Boolean(log.get('debug')), // Instrument context (at the expense of performance)
spector: Boolean(log.get('spector')), // Initialize the SpectorJS WebGL debugger
break: (log.get('break') as string[]) || [],

// WebGL specific debugging
debugWithSpectorJS: undefined!,
spectorUrl: undefined!,

// TODO - Change these after confirming things work as expected
initalizeFeatures: true,
disabledFeatures: {
Expand Down
2 changes: 1 addition & 1 deletion modules/engine/src/animation-loop/make-animation-loop.ts
Expand Up @@ -19,7 +19,7 @@ export function makeAnimationLoop(
): AnimationLoop {
let renderLoop: AnimationLoopTemplate | null = null;

const device = props?.device || luma.createDevice();
const device = props?.device || luma.createDevice({id: 'animation-loop'});

// Create an animation loop;
const animationLoop = new AnimationLoop({
Expand Down
68 changes: 38 additions & 30 deletions modules/webgl/src/adapter/webgl-device.ts
Expand Up @@ -13,13 +13,16 @@ import {getDeviceInfo} from './device-helpers/webgl-device-info';
import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
import {WebGLCanvasContext} from './webgl-canvas-context';
import {loadSpectorJS, initializeSpectorJS} from '../context/debug/spector';

import type {Spector} from '../context/debug/spector-types';
import {loadSpectorJS, initializeSpectorJS, DEFAULT_SPECTOR_PROPS} from '../context/debug/spector';
import {loadWebGLDeveloperTools, makeDebugContext} from '../context/debug/webgl-developer-tools';
import {
isTextureFormatSupported,
isTextureFormatRenderable,
isTextureFormatFilterable
} from './converters/texture-formats';
import {uid} from '../utils/uid';

// WebGL classes
import type {
Expand Down Expand Up @@ -93,6 +96,20 @@ export class WebGLDevice extends Device {

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

/** WebGL2 context. */
readonly gl: WebGL2RenderingContext;
readonly debug: boolean = false;

/** State used by luma.gl classes: TODO - move to canvasContext*/
readonly _canvasSizeInfo = {clientWidth: 0, clientHeight: 0, devicePixelRatio: 1};

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

/** Instance of Spector.js (if initialized) */
spectorJS: Spector;

//
// Static methods, expected to be present by `luma.createDevice()`
//
Expand Down Expand Up @@ -124,6 +141,11 @@ export class WebGLDevice extends Device {
}

static async create(props: DeviceProps = {}): Promise<WebGLDevice> {
for (const [key, value] of Object.entries(DEFAULT_SPECTOR_PROPS)) {
if (props[key] === undefined) {
props[key] = value;
}
}
log.groupCollapsed(LOG_LEVEL, 'WebGLDevice created')();

const promises: Promise<unknown>[] = [];
Expand All @@ -133,9 +155,7 @@ export class WebGLDevice extends Device {
promises.push(loadWebGLDeveloperTools());
}

if (props.spector) {
promises.push(loadSpectorJS());
}
promises.push(loadSpectorJS(props));

// Wait for page to load: if canvas is a string we need to query the DOM for the canvas element.
// We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary.
Expand Down Expand Up @@ -179,7 +199,10 @@ ${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContex
//

constructor(props: DeviceProps) {
super({...props, id: props.id || 'webgl-device'});
// Add spector default props to device default props
Device.defaultProps = {...Device.defaultProps, ...DEFAULT_SPECTOR_PROPS};

super({...props, id: props.id || uid('webgl-device')});

// If attaching to an already attached context, return the attached device
// @ts-expect-error device is attached to context
Expand All @@ -196,27 +219,30 @@ ${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContex
this._resolveContextLost = resolve;
});

let gl: WebGL2RenderingContext | null = props.gl || null;
gl ||= createBrowserContext(this.canvasContext.canvas, {
this.handle = createBrowserContext(this.canvasContext.canvas, {
...props,
onContextLost: (event: Event) =>
this._resolveContextLost?.({
reason: 'destroyed',
message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
})
});
this.gl = this.handle;

if (!gl) {
if (!this.handle) {
throw new Error('WebGL context creation failed');
}

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

// Instrument context
(this.gl as any).device = this; // Update GL context: Link webgl context back to device
(this.gl as any)._version = 2; // Update GL context: Store WebGL version field on gl context (HACK to identify debug contexts)

// luma Device fields
// initialize luma Device fields
this.info = getDeviceInfo(this.gl, this._extensions);
this.limits = new WebGLDeviceLimits(this.gl);
this.features = new WebGLDeviceFeatures(this.gl, this._extensions, this.props.disabledFeatures);
Expand All @@ -232,17 +258,13 @@ ${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContex
});
glState.trackState(this.gl, {copyState: false});

// DEBUG contexts: Add debug instrumentation to the context, force log level to at least 1
// DEBUG contexts: Add luma debug instrumentation to the context, force log level to at least 1
if (props.debug) {
this.gl = makeDebugContext(this.gl, {...props, throwOnError: true});
this.debug = true;
log.level = Math.max(log.level, 1);
log.warn('WebGL debug mode activated. Performance reduced.')();
}

if (props.spector) {
this.spectorJS = initializeSpectorJS({...this.props, canvas: this.handle.canvas});
}
}

/**
Expand Down Expand Up @@ -418,20 +440,6 @@ ${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContex
// WebGL-only API (not part of `Device` API)
//

/** WebGL2 context. */
readonly gl: WebGL2RenderingContext;
readonly debug: boolean = false;

/** State used by luma.gl classes: TODO - move to canvasContext*/
readonly _canvasSizeInfo = {clientWidth: 0, clientHeight: 0, devicePixelRatio: 1};

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

/** Instance of Spector.js (if initialized) */
spectorJS: unknown;

/**
* Triggers device (or WebGL context) loss.
* @note primarily intended for testing how application reacts to device loss
Expand Down

0 comments on commit cf32213

Please sign in to comment.