molstar
Version:
A comprehensive macromolecular library.
307 lines • 12.8 kB
JavaScript
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createContext = exports.readPixels = exports.checkError = exports.getErrorDescription = exports.getGLContext = void 0;
var tslib_1 = require("tslib");
var compat_1 = require("./compat");
var framebuffer_1 = require("./framebuffer");
var mol_task_1 = require("../../mol-task");
var debug_1 = require("../../mol-util/debug");
var extensions_1 = require("./extensions");
var state_1 = require("./state");
var image_1 = require("../../mol-util/image");
var resources_1 = require("./resources");
var render_target_1 = require("./render-target");
var rxjs_1 = require("rxjs");
var now_1 = require("../../mol-util/now");
function getGLContext(canvas, attribs) {
function get(id) {
try {
return canvas.getContext(id, attribs);
}
catch (e) {
return null;
}
}
var gl = ((attribs === null || attribs === void 0 ? void 0 : attribs.preferWebGl1) ? null : get('webgl2')) || get('webgl') || get('experimental-webgl');
if (debug_1.isDebugMode)
console.log("isWebgl2: " + (0, compat_1.isWebGL2)(gl));
return gl;
}
exports.getGLContext = getGLContext;
function getErrorDescription(gl, error) {
switch (error) {
case gl.NO_ERROR: return 'no error';
case gl.INVALID_ENUM: return 'invalid enum';
case gl.INVALID_VALUE: return 'invalid value';
case gl.INVALID_OPERATION: return 'invalid operation';
case gl.INVALID_FRAMEBUFFER_OPERATION: return 'invalid framebuffer operation';
case gl.OUT_OF_MEMORY: return 'out of memory';
case gl.CONTEXT_LOST_WEBGL: return 'context lost';
}
return 'unknown error';
}
exports.getErrorDescription = getErrorDescription;
function checkError(gl) {
var error = gl.getError();
if (error !== gl.NO_ERROR) {
throw new Error("WebGL error: '" + getErrorDescription(gl, error) + "'");
}
}
exports.checkError = checkError;
function unbindResources(gl) {
// bind null to all texture units
var maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
for (var i = 0; i < maxTextureImageUnits; ++i) {
gl.activeTexture(gl.TEXTURE0 + i);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
if ((0, compat_1.isWebGL2)(gl)) {
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
gl.bindTexture(gl.TEXTURE_3D, null);
}
}
// assign the smallest possible buffer to all attributes
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
var maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
for (var i = 0; i < maxVertexAttribs; ++i) {
gl.vertexAttribPointer(i, 1, gl.FLOAT, false, 0, 0);
}
// bind null to all buffers
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
unbindFramebuffer(gl);
}
function unbindFramebuffer(gl) {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
var tmpPixel = new Uint8Array(1 * 4);
function checkSync(gl, sync, resolve) {
if (gl.getSyncParameter(sync, gl.SYNC_STATUS) === gl.SIGNALED) {
gl.deleteSync(sync);
resolve();
}
else {
mol_task_1.Scheduler.setImmediate(checkSync, gl, sync, resolve);
}
}
function fence(gl, resolve) {
var sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
if (!sync) {
console.warn('Could not create a WebGLSync object');
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, tmpPixel);
resolve();
}
else {
mol_task_1.Scheduler.setImmediate(checkSync, gl, sync, resolve);
}
}
var SentWebglSyncObjectNotSupportedInWebglMessage = false;
function waitForGpuCommandsComplete(gl) {
return new Promise(function (resolve) {
if ((0, compat_1.isWebGL2)(gl)) {
// TODO seems quite slow
fence(gl, resolve);
}
else {
if (!SentWebglSyncObjectNotSupportedInWebglMessage) {
console.info('Sync object not supported in WebGL');
SentWebglSyncObjectNotSupportedInWebglMessage = true;
}
waitForGpuCommandsCompleteSync(gl);
resolve();
}
});
}
function waitForGpuCommandsCompleteSync(gl) {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, tmpPixel);
}
function readPixels(gl, x, y, width, height, buffer) {
if (debug_1.isDebugMode)
(0, framebuffer_1.checkFramebufferStatus)(gl);
if (buffer instanceof Uint8Array) {
gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
}
else if (buffer instanceof Float32Array) {
gl.readPixels(x, y, width, height, gl.RGBA, gl.FLOAT, buffer);
}
else if (buffer instanceof Int32Array && (0, compat_1.isWebGL2)(gl)) {
gl.readPixels(x, y, width, height, gl.RGBA_INTEGER, gl.INT, buffer);
}
else {
throw new Error('unsupported readPixels buffer type');
}
if (debug_1.isDebugMode)
checkError(gl);
}
exports.readPixels = readPixels;
function getDrawingBufferPixelData(gl) {
var w = gl.drawingBufferWidth;
var h = gl.drawingBufferHeight;
var buffer = new Uint8Array(w * h * 4);
unbindFramebuffer(gl);
gl.viewport(0, 0, w, h);
readPixels(gl, 0, 0, w, h, buffer);
return image_1.PixelData.flipY(image_1.PixelData.create(buffer, w, h));
}
//
function createStats() {
return {
resourceCounts: {
attribute: 0,
elements: 0,
framebuffer: 0,
program: 0,
renderbuffer: 0,
shader: 0,
texture: 0,
vertexArray: 0,
},
drawCount: 0,
instanceCount: 0,
instancedDrawCount: 0,
};
}
function createContext(gl, props) {
var _this = this;
if (props === void 0) { props = {}; }
var extensions = (0, extensions_1.createExtensions)(gl);
var state = (0, state_1.createState)(gl);
var stats = createStats();
var resources = (0, resources_1.createResources)(gl, state, stats, extensions);
var parameters = {
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
max3dTextureSize: (0, compat_1.isWebGL2)(gl) ? gl.getParameter(gl.MAX_3D_TEXTURE_SIZE) : 0,
maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
maxDrawBuffers: extensions.drawBuffers ? gl.getParameter(extensions.drawBuffers.MAX_DRAW_BUFFERS) : 0,
maxTextureImageUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),
maxVertexTextureImageUnits: gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS),
};
if (parameters.maxVertexTextureImageUnits < 8) {
throw new Error('Need "MAX_VERTEX_TEXTURE_IMAGE_UNITS" >= 8');
}
var isContextLost = false;
var contextRestored = new rxjs_1.BehaviorSubject(0);
var readPixelsAsync;
if ((0, compat_1.isWebGL2)(gl)) {
var pbo_1 = gl.createBuffer();
var _buffer_1 = void 0;
var _resolve_1 = void 0;
var _reading_1 = false;
var bindPBO_1 = function () {
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo_1);
gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, _buffer_1);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
_reading_1 = false;
_resolve_1();
_resolve_1 = void 0;
_buffer_1 = void 0;
};
readPixelsAsync = function (x, y, width, height, buffer) { return new Promise(function (resolve, reject) {
if (_reading_1) {
reject('Can not call multiple readPixelsAsync at the same time');
return;
}
_reading_1 = true;
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo_1);
gl.bufferData(gl.PIXEL_PACK_BUFFER, width * height * 4, gl.STREAM_READ);
gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
// need to unbind/bind PBO before/after async awaiting the fence
_resolve_1 = resolve;
_buffer_1 = buffer;
fence(gl, bindPBO_1);
}); };
}
else {
readPixelsAsync = function (x, y, width, height, buffer) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () {
return (0, tslib_1.__generator)(this, function (_a) {
readPixels(gl, x, y, width, height, buffer);
return [2 /*return*/];
});
}); };
}
var renderTargets = new Set();
return {
gl: gl,
isWebGL2: (0, compat_1.isWebGL2)(gl),
get pixelRatio() {
var dpr = (typeof window !== 'undefined') ? window.devicePixelRatio : 1;
return dpr * (props.pixelScale || 1);
},
extensions: extensions,
state: state,
stats: stats,
resources: resources,
get maxTextureSize() { return parameters.maxTextureSize; },
get max3dTextureSize() { return parameters.max3dTextureSize; },
get maxRenderbufferSize() { return parameters.maxRenderbufferSize; },
get maxDrawBuffers() { return parameters.maxDrawBuffers; },
get maxTextureImageUnits() { return parameters.maxTextureImageUnits; },
namedComputeRenderables: Object.create(null),
namedFramebuffers: Object.create(null),
namedTextures: Object.create(null),
get isContextLost() {
return isContextLost || gl.isContextLost();
},
contextRestored: contextRestored,
setContextLost: function () {
isContextLost = true;
},
handleContextRestored: function (extraResets) {
Object.assign(extensions, (0, extensions_1.createExtensions)(gl));
state.reset();
state.currentMaterialId = -1;
state.currentProgramId = -1;
state.currentRenderItemId = -1;
resources.reset();
renderTargets.forEach(function (rt) { return rt.reset(); });
extraResets === null || extraResets === void 0 ? void 0 : extraResets();
isContextLost = false;
contextRestored.next((0, now_1.now)());
},
createRenderTarget: function (width, height, depth, type, filter) {
var renderTarget = (0, render_target_1.createRenderTarget)(gl, resources, width, height, depth, type, filter);
renderTargets.add(renderTarget);
return (0, tslib_1.__assign)((0, tslib_1.__assign)({}, renderTarget), { destroy: function () {
renderTarget.destroy();
renderTargets.delete(renderTarget);
} });
},
unbindFramebuffer: function () { return unbindFramebuffer(gl); },
readPixels: function (x, y, width, height, buffer) {
readPixels(gl, x, y, width, height, buffer);
},
readPixelsAsync: readPixelsAsync,
waitForGpuCommandsComplete: function () { return waitForGpuCommandsComplete(gl); },
waitForGpuCommandsCompleteSync: function () { return waitForGpuCommandsCompleteSync(gl); },
getDrawingBufferPixelData: function () { return getDrawingBufferPixelData(gl); },
clear: function (red, green, blue, alpha) {
unbindFramebuffer(gl);
state.enable(gl.SCISSOR_TEST);
state.depthMask(true);
state.colorMask(true, true, true, true);
state.clearColor(red, green, blue, alpha);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.scissor(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
},
destroy: function (options) {
var _a;
resources.destroy();
unbindResources(gl);
// to aid GC
if (!(options === null || options === void 0 ? void 0 : options.doNotForceWebGLContextLoss))
(_a = gl.getExtension('WEBGL_lose_context')) === null || _a === void 0 ? void 0 : _a.loseContext();
}
};
}
exports.createContext = createContext;
//# sourceMappingURL=context.js.map
;