onnxruntime/js/web/lib/onnxjs/backends/webgl/webgl-context-factory.ts
Yulong Wang de476c8075
[js/web] update webgl context creating (#16436)
### Description
Modify the creating of webgl context.

Previous behavior:
STEP.1 - create canvas (document.createElement), if failed, goto step.2
else step.3
STEP.2 - create offscreenCanvas, if failed abort
STEP.3 - use the canvas created in step.1 or 2 to create webgl context.
if successful return context else abort

Now bahavior:
STEP.1 create offscreenCanvas, if failed goto step.3
STEP.2 use it to create webgl context. if successful, return context
STEP.3 create canvas  (document.createElement). if failed, abort
STEP.4 use it to create webgl context. if successful, return context
else abort

Motivation:
we found in some environment, normalCanvas.getContext() returns null but
offscreenCanvas.getContext() returns the context object. and when
offscreenCanvas is available it is good idea to always prefer to use it.
2023-06-21 17:10:26 -07:00

114 lines
3.5 KiB
TypeScript

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Logger} from '../../instrument';
import {WebGLContext} from './webgl-context';
const cache: {[contextId: string]: WebGLContext} = {};
/**
* This factory function creates proper WebGLRenderingContext based on
* the current browsers capabilities
* The order is from higher/most recent versions to most basic
*/
export function createWebGLContext(contextId?: 'webgl'|'webgl2'): WebGLContext {
let context: WebGLContext|undefined;
if ((!contextId || contextId === 'webgl2') && 'webgl2' in cache) {
context = cache.webgl2;
} else if ((!contextId || contextId === 'webgl') && 'webgl' in cache) {
context = cache.webgl;
}
if (!context) {
try {
// try to create webgl context from an offscreen canvas
const offscreenCanvas = createOffscreenCanvas();
context = createNewWebGLContext(offscreenCanvas, contextId);
} catch (e) {
// if failed, fallback to try to use a normal canvas element
const canvas = createCanvas();
context = createNewWebGLContext(canvas, contextId);
}
}
contextId = contextId || context.version === 1 ? 'webgl' : 'webgl2';
const gl = context.gl;
cache[contextId] = context;
if (gl.isContextLost()) {
delete cache[contextId];
return createWebGLContext(contextId);
}
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.STENCIL_TEST);
gl.disable(gl.BLEND);
gl.disable(gl.DITHER);
gl.disable(gl.POLYGON_OFFSET_FILL);
gl.disable(gl.SAMPLE_COVERAGE);
gl.enable(gl.SCISSOR_TEST);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
return context;
}
export function createNewWebGLContext(canvas: HTMLCanvasElement, contextId?: 'webgl'|'webgl2'): WebGLContext {
const contextAttributes: WebGLContextAttributes = {
alpha: false,
depth: false,
antialias: false,
stencil: false,
preserveDrawingBuffer: false,
premultipliedAlpha: false,
failIfMajorPerformanceCaveat: false
};
let gl: WebGLRenderingContext|null;
const ca = contextAttributes;
if (!contextId || contextId === 'webgl2') {
gl = canvas.getContext('webgl2', ca);
if (gl) {
try {
return new WebGLContext(gl, 2);
} catch (err) {
Logger.warning('GlContextFactory', `failed to create WebGLContext using contextId 'webgl2'. Error: ${err}`);
}
}
}
if (!contextId || contextId === 'webgl') {
gl = canvas.getContext('webgl', ca) || canvas.getContext('experimental-webgl', ca) as WebGLRenderingContext;
if (gl) {
try {
return new WebGLContext(gl, 1);
} catch (err) {
Logger.warning(
'GlContextFactory',
`failed to create WebGLContext using contextId 'webgl' or 'experimental-webgl'. Error: ${err}`);
}
}
}
throw new Error('WebGL is not supported');
}
// eslint-disable-next-line @typescript-eslint/naming-convention
declare let OffscreenCanvas: {new (width: number, height: number): HTMLCanvasElement};
function createCanvas(): HTMLCanvasElement {
if (typeof document === 'undefined') {
throw new TypeError('failed to create canvas: document is not supported');
}
const canvas: HTMLCanvasElement = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
return canvas;
}
function createOffscreenCanvas(): HTMLCanvasElement {
if (typeof OffscreenCanvas === 'undefined') {
throw new TypeError('failed to create offscreen canvas: OffscreenCanvas is not supported');
}
return new OffscreenCanvas(1, 1);
}