UNPKG

@tensorflow/tfjs-core

Version:

Hardware-accelerated JavaScript library for machine intelligence

484 lines 23.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var canvas_util_1 = require("../../canvas_util"); var environment_1 = require("../../environment"); var util = require("../../util"); var gpgpu_util = require("./gpgpu_util"); var tex_util = require("./tex_util"); var webgl_util = require("./webgl_util"); var GPGPUContext = (function () { function GPGPUContext(gl) { this.outputTexture = null; this.program = null; this.disposed = false; this.autoDebugValidate = false; this.vertexAttrsAreBound = false; this.itemsToPoll = []; if (gl != null) { this.gl = gl; } else { this.gl = canvas_util_1.getWebGLContext(environment_1.ENV.get('WEBGL_VERSION')); } if (environment_1.ENV.get('WEBGL_VERSION') === 1) { this.textureFloatExtension = webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); this.colorBufferFloatExtension = this.gl.getExtension('WEBGL_color_buffer_float'); if (!environment_1.ENV.get('WEBGL_RENDER_FLOAT32_ENABLED')) { this.textureHalfFloatExtension = webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_half_float'); this.colorBufferHalfFloatExtension = this.gl.getExtension('EXT_color_buffer_half_float'); } } else { this.colorBufferFloatExtension = webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); } this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); this.framebuffer = webgl_util.createFramebuffer(this.gl); this.textureConfig = gpgpu_util.getTextureConfig(this.gl, this.textureHalfFloatExtension); } GPGPUContext.prototype.dispose = function () { var _this = this; if (this.disposed) { return; } if (this.program != null) { console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + ' This is probably a resource leak, delete the program with ' + 'GPGPUContext.deleteProgram before disposing.'); } if (this.outputTexture != null) { console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + 'texture. This is probably a resource leak, delete the output ' + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + 'disposing.'); } var gl = this.gl; webgl_util.callAndCheck(gl, function () { return gl.finish(); }); webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); this.disposed = true; }; GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { this.autoDebugValidate = enabled; webgl_util.enableDebugWebGLErrorChecking(enabled); }; GPGPUContext.prototype.createFloat32MatrixTexture = function (rows, columns) { this.throwIfDisposed(); return gpgpu_util.createFloat32MatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.createFloat16MatrixTexture = function (rows, columns) { this.throwIfDisposed(); return gpgpu_util.createFloat16MatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.createUnsignedBytesMatrixTexture = function (rows, columns) { this.throwIfDisposed(); return gpgpu_util.createUnsignedBytesMatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { this.throwIfDisposed(); gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); }; GPGPUContext.prototype.createFloat16PackedMatrixTexture = function (rows, columns) { this.throwIfDisposed(); return gpgpu_util.createFloat16PackedMatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { this.throwIfDisposed(); return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.deleteMatrixTexture = function (texture) { var _this = this; this.throwIfDisposed(); if (this.outputTexture === texture) { webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); this.outputTexture = null; } webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); }; GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { this.throwIfDisposed(); var numChannels = webgl_util.getNumChannels(); return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels, this.textureConfig); }; GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, batch, rows, columns, physicalRows, physicalCols, matrix) { this.throwIfDisposed(); return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, batch, rows, columns, physicalRows, physicalCols, matrix, this.textureConfig); }; GPGPUContext.prototype.downloadFloat32MatrixFromOutputTexture = function (texture, rows, columns) { var _this = this; return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadFloat32MatrixFromOutputTexture(_this.gl, rows, columns, _this.textureConfig); }); }; GPGPUContext.prototype.downloadByteEncodedFloatMatrixFromOutputTexture = function (texture, rows, columns) { var _this = this; return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadByteEncodedFloatMatrixFromOutputTexture(_this.gl, rows, columns, _this.textureConfig); }); }; GPGPUContext.prototype.downloadPackedMatrixFromBuffer = function (buffer, batch, rows, columns, physicalRows, physicalCols) { return gpgpu_util.downloadPackedMatrixFromBuffer(this.gl, buffer, batch, rows, columns, physicalRows, physicalCols, this.textureConfig); }; GPGPUContext.prototype.downloadFloat32MatrixFromBuffer = function (buffer, rows, columns) { return gpgpu_util.downloadFloat32MatrixFromBuffer(this.gl, buffer, rows, columns, this.textureConfig); }; GPGPUContext.prototype.maybeCreateBufferFromTexture = function (texture, rows, columns) { this.bindTextureToFrameBuffer(texture); var result = gpgpu_util.maybeCreateBufferFromOutputTexture(this.gl, texture, rows, columns, this.textureConfig); this.unbindTextureToFrameBuffer(); return result; }; GPGPUContext.prototype.createAndWaitForFence = function () { var fenceContext = this.createFence(this.gl); return this.pollFence(fenceContext); }; GPGPUContext.prototype.createFence = function (gl) { var _this = this; var query; var isFencePassed; if (environment_1.ENV.get('WEBGL_FENCE_API_ENABLED')) { var gl2_1 = gl; var sync_1 = gl2_1.fenceSync(gl2_1.SYNC_GPU_COMMANDS_COMPLETE, 0); gl.flush(); isFencePassed = function () { var status = gl2_1.clientWaitSync(sync_1, 0, 0); return status === gl2_1.ALREADY_SIGNALED || status === gl2_1.CONDITION_SATISFIED; }; query = sync_1; } else if (environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0) { query = this.beginQuery(); this.endQuery(); isFencePassed = function () { return _this.isQueryAvailable(query, environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION')); }; } else { isFencePassed = function () { return true; }; } return { query: query, isFencePassed: isFencePassed }; }; GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, batch, rows, columns, physicalRows, physicalCols) { var _this = this; return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, batch, rows, columns, physicalRows, physicalCols, _this.textureConfig); }); }; GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { this.throwIfDisposed(); var gl = this.gl; var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); var vertexShader = gpgpu_util.createVertexShader(gl); var program = webgl_util.createProgram(gl); webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); webgl_util.linkProgram(gl, program); if (this.autoDebugValidate) { webgl_util.validateProgram(gl, program); } if (!this.vertexAttrsAreBound) { this.setProgram(program); this.vertexAttrsAreBound = gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); } return program; }; GPGPUContext.prototype.deleteProgram = function (program) { var _this = this; this.throwIfDisposed(); if (program === this.program) { this.program = null; } if (program != null) { webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); } }; GPGPUContext.prototype.setProgram = function (program) { var _this = this; this.throwIfDisposed(); this.program = program; if ((this.program != null) && this.autoDebugValidate) { webgl_util.validateProgram(this.gl, this.program); } webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); }; GPGPUContext.prototype.getUniformLocation = function (program, uniformName, shouldThrow) { if (shouldThrow === void 0) { shouldThrow = true; } this.throwIfDisposed(); if (shouldThrow) { return webgl_util.getProgramUniformLocationOrThrow(this.gl, program, uniformName); } else { return webgl_util.getProgramUniformLocation(this.gl, program, uniformName); } }; GPGPUContext.prototype.getAttributeLocation = function (program, attribute) { var _this = this; this.throwIfDisposed(); return webgl_util.callAndCheck(this.gl, function () { return _this.gl.getAttribLocation(program, attribute); }); }; GPGPUContext.prototype.getUniformLocationNoThrow = function (program, uniformName) { this.throwIfDisposed(); return this.gl.getUniformLocation(program, uniformName); }; GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformLocation, textureUnit) { this.throwIfDisposed(); this.throwIfNoProgram(); webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformLocation, textureUnit); }; GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); }; GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { this.throwIfDisposed(); var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); }; GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); }; GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); }; GPGPUContext.prototype.debugValidate = function () { if (this.program != null) { webgl_util.validateProgram(this.gl, this.program); } webgl_util.validateFramebuffer(this.gl); }; GPGPUContext.prototype.executeProgram = function () { this.throwIfDisposed(); this.throwIfNoProgram(); var gl = this.gl; if (this.autoDebugValidate) { this.debugValidate(); } webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); }; GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { var _this = this; this.throwIfDisposed(); webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); }; GPGPUContext.prototype.getQueryTimerExtension = function () { if (this.disjointQueryTimerExtension == null) { this.disjointQueryTimerExtension = webgl_util.getExtensionOrThrow(this.gl, environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2 ? 'EXT_disjoint_timer_query_webgl2' : 'EXT_disjoint_timer_query'); } return this.disjointQueryTimerExtension; }; GPGPUContext.prototype.getQueryTimerExtensionWebGL2 = function () { return this.getQueryTimerExtension(); }; GPGPUContext.prototype.getQueryTimerExtensionWebGL1 = function () { return this.getQueryTimerExtension(); }; GPGPUContext.prototype.beginQuery = function () { if (environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { var gl2 = this.gl; var ext_1 = this.getQueryTimerExtensionWebGL2(); var query_1 = gl2.createQuery(); gl2.beginQuery(ext_1.TIME_ELAPSED_EXT, query_1); return query_1; } var ext = this.getQueryTimerExtensionWebGL1(); var query = ext.createQueryEXT(); ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); return query; }; GPGPUContext.prototype.endQuery = function () { if (environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { var gl2 = this.gl; var ext_2 = this.getQueryTimerExtensionWebGL2(); gl2.endQuery(ext_2.TIME_ELAPSED_EXT); return; } var ext = this.getQueryTimerExtensionWebGL1(); ext.endQueryEXT(ext.TIME_ELAPSED_EXT); }; GPGPUContext.prototype.waitForQueryAndGetTime = function (query) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4, util.repeatedTry(function () { return _this.disposed || _this.isQueryAvailable(query, environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION')); })]; case 1: _a.sent(); return [2, this.getQueryTime(query, environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION'))]; } }); }); }; GPGPUContext.prototype.getQueryTime = function (query, queryTimerVersion) { if (queryTimerVersion === 0) { return null; } if (queryTimerVersion === 2) { var gl2 = this.gl; var timeElapsedNanos = gl2.getQueryParameter(query, gl2.QUERY_RESULT); return timeElapsedNanos / 1000000; } else { var ext = this.getQueryTimerExtensionWebGL1(); var timeElapsedNanos = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); return timeElapsedNanos / 1000000; } }; GPGPUContext.prototype.isQueryAvailable = function (query, queryTimerVersion) { if (queryTimerVersion === 0) { return true; } if (queryTimerVersion === 2) { var gl2 = this.gl; var ext = this.getQueryTimerExtensionWebGL2(); var available = gl2.getQueryParameter(query, gl2.QUERY_RESULT_AVAILABLE); if (this.disjoint == null) { this.disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; } else { var ext = this.getQueryTimerExtensionWebGL1(); var available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); if (this.disjoint == null) { this.disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; } }; GPGPUContext.prototype.pollFence = function (fenceContext) { var _this = this; return new Promise(function (resolve) { _this.addItemToPoll(function () { return fenceContext.isFencePassed(); }, function () { return resolve(); }); }); }; GPGPUContext.prototype.pollItems = function () { var index = binSearchLastTrue(this.itemsToPoll.map(function (x) { return x.isDoneFn; })); for (var i = 0; i <= index; ++i) { var resolveFn = this.itemsToPoll[i].resolveFn; resolveFn(); } this.itemsToPoll = this.itemsToPoll.slice(index + 1); }; GPGPUContext.prototype.addItemToPoll = function (isDoneFn, resolveFn) { var _this = this; this.itemsToPoll.push({ isDoneFn: isDoneFn, resolveFn: resolveFn }); if (this.itemsToPoll.length > 1) { return; } util.repeatedTry(function () { _this.pollItems(); return _this.itemsToPoll.length === 0; }); }; GPGPUContext.prototype.bindTextureToFrameBuffer = function (texture) { this.throwIfDisposed(); webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); if (this.autoDebugValidate) { webgl_util.validateFramebuffer(this.gl); } }; GPGPUContext.prototype.unbindTextureToFrameBuffer = function () { if (this.outputTexture != null) { webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); if (this.autoDebugValidate) { webgl_util.validateFramebuffer(this.gl); } } else { webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); } }; GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { this.bindTextureToFrameBuffer(texture); var result = downloadAndDecode(); this.unbindTextureToFrameBuffer(); return result; }; GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { this.throwIfDisposed(); var gl = this.gl; webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); if (this.autoDebugValidate) { webgl_util.validateFramebuffer(gl); } this.outputTexture = outputMatrixTextureMaybePacked; webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); }; GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { var _this = this; this.throwIfDisposed(); webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); }; GPGPUContext.prototype.throwIfDisposed = function () { if (this.disposed) { throw new Error('Attempted to use disposed GPGPUContext.'); } }; GPGPUContext.prototype.throwIfNoProgram = function () { if (this.program == null) { throw new Error('No GPU program is currently set.'); } }; return GPGPUContext; }()); exports.GPGPUContext = GPGPUContext; function binSearchLastTrue(arr) { var start = 0; var end = arr.length - 1; var best = -1; while (start <= end) { var mid = (start + end) >> 1; var isDone = arr[mid](); if (isDone) { best = mid; start = mid + 1; } else { end = mid - 1; } } return best; } exports.binSearchLastTrue = binSearchLastTrue; //# sourceMappingURL=gpgpu_context.js.map