/
context.js
192 lines (162 loc) · 5.79 KB
/
context.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/* eslint-disable quotes */
// WebGLRenderingContext related methods
import {trackContextState} from '@luma.gl/webgl-state-tracker';
import {createHeadlessContext} from './create-headless-context';
import {getCanvas} from './create-canvas';
import {createBrowserContext} from './create-browser-context';
import {getContextDebugInfo} from '../debug/get-context-debug-info';
import {WebGLRenderingContext, WebGL2RenderingContext} from '../utils';
import {log, isBrowser, assert} from '../../utils';
import {global} from '../../utils/globals';
// Heuristic testing of contexts (to indentify debug wrappers around gl contexts)
const GL_ARRAY_BUFFER = 0x8892;
const GL_TEXTURE_BINDING_3D = 0x806a;
export const ERR_CONTEXT = 'Invalid WebGLRenderingContext';
export const ERR_WEBGL = ERR_CONTEXT;
export const ERR_WEBGL2 = 'Requires WebGL2';
// HACK: avoid use of `gl` parameter name to defeat GL constant inliner, which invalidates check
export function isWebGL(glContext) {
return Boolean(
glContext &&
(glContext instanceof WebGLRenderingContext || glContext.ARRAY_BUFFER === GL_ARRAY_BUFFER)
);
}
// HACK: avoid use of `gl` parameter name to defeat GL constant inliner, which invalidates check
export function isWebGL2(glContext) {
return Boolean(
glContext &&
(glContext instanceof WebGL2RenderingContext ||
glContext.TEXTURE_BINDING_3D === GL_TEXTURE_BINDING_3D)
);
}
export function assertWebGLContext(gl) {
// Need to handle debug context
assert(isWebGL(gl), ERR_CONTEXT);
}
export function assertWebGL2Context(gl) {
// Need to handle debug context
assert(isWebGL2(gl), ERR_WEBGL2);
}
const contextDefaults = {
// COMMON CONTEXT PARAMETERS
// Attempt to allocate WebGL2 context
webgl2: true, // Attempt to create a WebGL2 context (false to force webgl1)
webgl1: true, // Attempt to create a WebGL1 context (false to fail if webgl2 not available)
throwOnFailure: true,
manageState: true,
// BROWSER CONTEXT PARAMETERS
canvas: null, // A canvas element or a canvas string id
debug: false, // Instrument context (at the expense of performance)
// HEADLESS CONTEXT PARAMETERS
width: 800, // width are height are only used by headless gl
height: 600
// WEBGL/HEADLESS CONTEXT PARAMETERS
// Remaining options are passed through to context creator
};
/*
* Change default context creation parameters.
* Main use case is regression test suite.
*/
export function setContextDefaults(opts = {}) {
Object.assign(contextDefaults, {width: 1, height: 1}, opts);
}
/*
* Creates a context giving access to the WebGL API
*/
/* eslint-disable complexity, max-statements */
export function createGLContext(opts = {}) {
opts = Object.assign({}, contextDefaults, opts);
const {canvas, width, height, throwOnError, manageState, debug} = opts;
// Error reporting function, enables exceptions to be disabled
function onError(message) {
if (throwOnError) {
throw new Error(message);
}
return null;
}
let gl = opts.gl;
if (!gl) {
if (isBrowser) {
// Get or create a canvas
const targetCanvas = getCanvas({canvas, width, height, onError});
// Create a WebGL context in the canvas
gl = createBrowserContext({canvas: targetCanvas, opts});
} else {
// Create a headless-gl context under Node.js
gl = createHeadlessContext({width, height, opts, onError});
}
}
if (!gl) {
return null;
}
// Install context state tracking
if (manageState) {
trackContextState(gl, {
copyState: false,
log: (...args) => log.log(1, ...args)()
});
}
// Add debug instrumentation to the context
if (isBrowser && debug) {
if (!global.makeDebugContext) {
log.warn('WebGL debug mode not activated. import "@luma.gl/debug" to enable.')();
} else {
gl = global.makeDebugContext(gl, {debug});
// Debug forces log level to at least 1
log.priority = Math.max(log.priority, 1);
}
}
// Log some debug info about the newly created context
logInfo(gl);
// Add to seer integration
return gl;
}
export function destroyGLContext(gl) {
// TODO - Remove from seer integration
// TODO - Unregister any tracking/polyfills
// There is no way to delete browser based context
// Destroy headless gl context
const ext = gl.getExtension('STACKGL_destroy_context');
if (ext) {
ext.destroy();
}
}
/**
* Resize the canvas' drawing buffer.
*
* Can match the canvas CSS size, and optionally also consider devicePixelRatio
* Can be called every frame
*
* Regardless of size, the drawing buffer will always be scaled to the viewport, but
* for best visual results, usually set to either:
* canvas CSS width x canvas CSS height
* canvas CSS width * devicePixelRatio x canvas CSS height * devicePixelRatio
* See http://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
*
* resizeGLContext(gl, {width, height, useDevicePixels})
*/
export function resizeGLContext(gl, opts = {}) {
// Resize browser context
if (gl.canvas) {
/* global window */
const devicePixelRatio = opts.useDevicePixels ? window.devicePixelRatio || 1 : 1;
const width = `width` in opts ? opts.width : gl.canvas.clientWidth;
const height = `height` in opts ? opts.height : gl.canvas.clientHeight;
gl.canvas.width = width * devicePixelRatio;
gl.canvas.height = height * devicePixelRatio;
return;
}
// Resize headless gl context
const ext = gl.getExtension('STACKGL_resize_drawingbuffer');
if (ext && `width` in opts && `height` in opts) {
ext.resize(opts.width, opts.height);
}
}
// HELPER METHODS
function logInfo(gl) {
const webGL = isWebGL2(gl) ? 'WebGL2' : 'WebGL1';
const info = getContextDebugInfo(gl);
const driver = info ? `(${info.vendor},${info.renderer})` : '';
const debug = gl.debug ? ' debug' : '';
log.once(1, `${webGL}${debug} context ${driver}`)();
}