UNPKG

handsfree

Version:

Quickly integrate face, hand, and/or pose tracking to your frontend projects in a snap ✨👌

1,035 lines (1,021 loc) 577 kB
/** * @license * Copyright 2020 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tensorflow/tfjs-core'), require('seedrandom')) : typeof define === 'function' && define.amd ? define(['exports', '@tensorflow/tfjs-core', 'seedrandom'], factory) : (global = global || self, factory(global.tf = global.tf || {}, global.tf, global.seedrandom)); }(this, (function (exports, tf, seedrandom) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 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) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(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 }; } } /** * @license * Copyright 2018 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ var contexts = {}; var WEBGL_ATTRIBUTES = { alpha: false, antialias: false, premultipliedAlpha: false, preserveDrawingBuffer: false, depth: false, stencil: false, failIfMajorPerformanceCaveat: true }; function setWebGLContext(webGLVersion, gl) { contexts[webGLVersion] = gl; } function getWebGLContext(webGLVersion) { if (!(webGLVersion in contexts)) { contexts[webGLVersion] = getWebGLRenderingContext(webGLVersion); } var gl = contexts[webGLVersion]; if (gl.isContextLost()) { delete contexts[webGLVersion]; return getWebGLContext(webGLVersion); } 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 contexts[webGLVersion]; } function createCanvas(webGLVersion) { if (typeof OffscreenCanvas !== 'undefined' && webGLVersion === 2) { return new OffscreenCanvas(300, 150); } else if (typeof document !== 'undefined') { return document.createElement('canvas'); } else { throw new Error('Cannot create a canvas in this context'); } } function getWebGLRenderingContext(webGLVersion) { if (webGLVersion !== 1 && webGLVersion !== 2) { throw new Error('Cannot get WebGL rendering context, WebGL is disabled.'); } var canvas = createCanvas(webGLVersion); canvas.addEventListener('webglcontextlost', function (ev) { ev.preventDefault(); delete contexts[webGLVersion]; }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || canvas.getContext('experimental-webgl', WEBGL_ATTRIBUTES)); } return canvas.getContext('webgl2', WEBGL_ATTRIBUTES); } /** * @license * Copyright 2017 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ var PackingScheme; (function (PackingScheme) { /** * All values in a single texel are densely packed without any constraints. * * This is how the shader encodes a tensor with shape = [2, 3, 4] * (indices are [batch, row, col]). * * 000|001 010|011 020|021 * ------- ------- ------- * 002|003 012|013 022|023 * * 100|101 110|111 120|121 * ------- ------- ------- * 102|103 112|113 122|123 * */ PackingScheme[PackingScheme["DENSE"] = 0] = "DENSE"; /** * Single texels contain only values from the same batch, and from adjacent * rows and columns. * * This is how the shader encodes a tensor with shape = [2, 3, 5] * (indices are [batch, row, col]). * * 000|001 002|003 004|xxx 020|021 022|023 024|xxx * ------- ------- ------- ------- ------- ------- * 010|011 012|013 014|xxx xxx|xxx xxx|xxx xxx|xxx * * 100|101 102|103 104|xxx 120|121 122|123 124|xxx * ------- ------- ------- ------- ------- ------- * 110|111 112|113 114|xxx xxx|xxx xxx|xxx xxx|xxx * */ PackingScheme[PackingScheme["SHARED_BATCH"] = 1] = "SHARED_BATCH"; })(PackingScheme || (PackingScheme = {})); var TextureUsage; (function (TextureUsage) { TextureUsage[TextureUsage["RENDER"] = 0] = "RENDER"; TextureUsage[TextureUsage["UPLOAD"] = 1] = "UPLOAD"; TextureUsage[TextureUsage["PIXELS"] = 2] = "PIXELS"; TextureUsage[TextureUsage["DOWNLOAD"] = 3] = "DOWNLOAD"; })(TextureUsage || (TextureUsage = {})); var PhysicalTextureType; (function (PhysicalTextureType) { PhysicalTextureType[PhysicalTextureType["UNPACKED_FLOAT16"] = 0] = "UNPACKED_FLOAT16"; PhysicalTextureType[PhysicalTextureType["UNPACKED_FLOAT32"] = 1] = "UNPACKED_FLOAT32"; PhysicalTextureType[PhysicalTextureType["PACKED_4X1_UNSIGNED_BYTE"] = 2] = "PACKED_4X1_UNSIGNED_BYTE"; PhysicalTextureType[PhysicalTextureType["PACKED_2X2_FLOAT32"] = 3] = "PACKED_2X2_FLOAT32"; PhysicalTextureType[PhysicalTextureType["PACKED_2X2_FLOAT16"] = 4] = "PACKED_2X2_FLOAT16"; })(PhysicalTextureType || (PhysicalTextureType = {})); function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { return [columns, rows]; } function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { return matrixSize * channelsPerTexture; } /** * Get shape for densely packed RGBA texture. */ function getDenseTexShape(shape) { var size = tf.util.sizeFromShape(shape); var texelsNeeded = Math.ceil(size / 4); return tf.util.sizeToSquarishShape(texelsNeeded); } function getPackedMatrixTextureShapeWidthHeight(rows, columns) { return [ Math.max(1, Math.ceil(columns / 2)), Math.max(1, Math.ceil(rows / 2)) ]; } function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; return w * h * 4; } function getTextureConfig( // tslint:disable-next-line:no-any gl, textureHalfFloatExtension) { // tslint:disable-next-line:no-any var glany = gl; var internalFormatFloat; var internalFormatHalfFloat; var internalFormatPackedHalfFloat; var internalFormatPackedFloat; var textureFormatFloat; var downloadTextureFormat; var downloadUnpackNumChannels; var defaultNumChannels; var textureTypeHalfFloat; var textureTypeFloat; if (tf.env().getNumber('WEBGL_VERSION') === 2) { internalFormatFloat = glany.R32F; internalFormatHalfFloat = glany.R16F; internalFormatPackedHalfFloat = glany.RGBA16F; internalFormatPackedFloat = glany.RGBA32F; textureFormatFloat = glany.RED; downloadUnpackNumChannels = 4; defaultNumChannels = 1; textureTypeHalfFloat = glany.HALF_FLOAT; textureTypeFloat = glany.FLOAT; } else { internalFormatFloat = gl.RGBA; internalFormatHalfFloat = gl.RGBA; internalFormatPackedHalfFloat = gl.RGBA; internalFormatPackedFloat = glany.RGBA; textureFormatFloat = gl.RGBA; downloadUnpackNumChannels = 4; defaultNumChannels = 4; textureTypeHalfFloat = textureHalfFloatExtension != null ? textureHalfFloatExtension.HALF_FLOAT_OES : null; textureTypeFloat = gl.FLOAT; } downloadTextureFormat = gl.RGBA; return { internalFormatFloat: internalFormatFloat, internalFormatHalfFloat: internalFormatHalfFloat, internalFormatPackedHalfFloat: internalFormatPackedHalfFloat, internalFormatPackedFloat: internalFormatPackedFloat, textureFormatFloat: textureFormatFloat, downloadTextureFormat: downloadTextureFormat, downloadUnpackNumChannels: downloadUnpackNumChannels, defaultNumChannels: defaultNumChannels, textureTypeHalfFloat: textureTypeHalfFloat, textureTypeFloat: textureTypeFloat }; } /** * @license * Copyright 2017 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ function callAndCheck(gl, func) { var returnValue = func(); if (tf.env().getBool('DEBUG')) { checkWebGLError(gl); } return returnValue; } function checkWebGLError(gl) { var error = gl.getError(); if (error !== gl.NO_ERROR) { throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); } } // https://en.wikipedia.org/wiki/Half-precision_floating-point_format var MIN_FLOAT16 = 5.96e-8; var MAX_FLOAT16 = 65504; function canBeRepresented(num) { if (tf.env().getBool('WEBGL_RENDER_FLOAT32_ENABLED') || num === 0 || (MIN_FLOAT16 < Math.abs(num) && Math.abs(num) < MAX_FLOAT16)) { return true; } return false; } function getWebGLErrorMessage(gl, status) { switch (status) { 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_WEBGL'; default: return "Unknown error code " + status; } } function getExtensionOrThrow(gl, extensionName) { return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); } function createVertexShader(gl, vertexShaderSource) { var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { console.log(gl.getShaderInfoLog(vertexShader)); throw new Error('Failed to compile vertex shader.'); } return vertexShader; } function createFragmentShader(gl, fragmentShaderSource) { var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { logShaderSourceAndInfoLog(fragmentShaderSource, gl.getShaderInfoLog(fragmentShader)); throw new Error('Failed to compile fragment shader.'); } return fragmentShader; } var lineNumberRegex = /ERROR: [0-9]+:([0-9]+):/g; function logShaderSourceAndInfoLog(shaderSource, shaderInfoLog) { var lineNumberRegexResult = lineNumberRegex.exec(shaderInfoLog); if (lineNumberRegexResult == null) { console.log("Couldn't parse line number in error: " + shaderInfoLog); console.log(shaderSource); return; } var lineNumber = +lineNumberRegexResult[1]; var shaderLines = shaderSource.split('\n'); var pad = shaderLines.length.toString().length + 2; var linesWithLineNumbers = shaderLines.map(function (line, lineNumber) { return tf.util.rightPad((lineNumber + 1).toString(), pad) + line; }); var maxLineLength = 0; for (var i = 0; i < linesWithLineNumbers.length; i++) { maxLineLength = Math.max(linesWithLineNumbers[i].length, maxLineLength); } var beforeErrorLines = linesWithLineNumbers.slice(0, lineNumber - 1); var errorLine = linesWithLineNumbers.slice(lineNumber - 1, lineNumber); var afterErrorLines = linesWithLineNumbers.slice(lineNumber); console.log(beforeErrorLines.join('\n')); console.log(shaderInfoLog.split('\n')[0]); console.log("%c " + tf.util.rightPad(errorLine[0], maxLineLength), 'border:1px solid red; background-color:#e3d2d2; color:#a61717'); console.log(afterErrorLines.join('\n')); } function createProgram(gl) { return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); } function linkProgram(gl, program) { callAndCheck(gl, function () { return gl.linkProgram(program); }); if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { console.log(gl.getProgramInfoLog(program)); throw new Error('Failed to link vertex and fragment shaders.'); } } function validateProgram(gl, program) { callAndCheck(gl, function () { return gl.validateProgram(program); }); if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { console.log(gl.getProgramInfoLog(program)); throw new Error('Shader program validation failed.'); } } function createStaticVertexBuffer(gl, data) { var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); return buffer; } function createStaticIndexBuffer(gl, data) { var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); return buffer; } function getNumChannels() { if (tf.env().getNumber('WEBGL_VERSION') === 2) { return 1; } return 4; } function createTexture(gl) { return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); } function validateTextureSize(width, height) { var maxTextureSize = tf.env().getNumber('WEBGL_MAX_TEXTURE_SIZE'); if ((width <= 0) || (height <= 0)) { var requested = "[" + width + "x" + height + "]"; throw new Error('Requested texture size ' + requested + ' is invalid.'); } if ((width > maxTextureSize) || (height > maxTextureSize)) { var requested = "[" + width + "x" + height + "]"; var max = "[" + maxTextureSize + "x" + maxTextureSize + "]"; throw new Error('Requested texture size ' + requested + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); } } function createFramebuffer(gl) { return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); } function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { var loc = gl.getAttribLocation(program, attribute); if (loc === -1) { // The GPU compiler decided to strip out this attribute because it's unused, // thus no need to bind. return false; } callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); return true; } function bindTextureUnit(gl, texture, textureUnit) { validateTextureUnit(gl, textureUnit); callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); } function unbindTextureUnit(gl, textureUnit) { validateTextureUnit(gl, textureUnit); callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); } function getProgramUniformLocationOrThrow(gl, program, uniformName) { return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); } function getProgramUniformLocation(gl, program, uniformName) { return gl.getUniformLocation(program, uniformName); } function bindTextureToProgramUniformSampler(gl, texture, uniformSamplerLocation, textureUnit) { callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); callAndCheck(gl, function () { return gl.uniform1i(uniformSamplerLocation, textureUnit); }); } function bindCanvasToFramebuffer(gl) { callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); } function bindColorTextureToFramebuffer(gl, texture, framebuffer) { callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); } function unbindColorTextureFromFramebuffer(gl, framebuffer) { callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); } function validateFramebuffer(gl) { var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); if (status !== gl.FRAMEBUFFER_COMPLETE) { throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); } } function getFramebufferErrorMessage(gl, status) { switch (status) { case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; case gl.FRAMEBUFFER_UNSUPPORTED: return 'FRAMEBUFFER_UNSUPPORTED'; default: return "unknown error " + status; } } function throwIfNull(gl, returnTOrNull, failureMessage) { var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); if (tOrNull == null) { throw new Error(failureMessage); } return tOrNull; } function validateTextureUnit(gl, textureUnit) { var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; var glTextureUnit = textureUnit + gl.TEXTURE0; if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { var textureUnitRange = "[gl.TEXTURE0, gl.TEXTURE" + maxTextureUnit + "]"; throw new Error("textureUnit must be in " + textureUnitRange + "."); } } function getBatchDim(shape, dimsToSkip) { if (dimsToSkip === void 0) { dimsToSkip = 2; } return tf.util.sizeFromShape(shape.slice(0, shape.length - dimsToSkip)); } function getRowsCols(shape) { if (shape.length === 0) { throw Error('Cannot get rows and columns of an empty shape array.'); } return [ shape.length > 1 ? shape[shape.length - 2] : 1, shape[shape.length - 1] ]; } function getShapeAs3D(shape) { var shapeAs3D = [1, 1, 1]; var isScalar = shape.length === 0 || (shape.length === 1 && shape[0] === 1); if (!isScalar) { shapeAs3D = [getBatchDim(shape)].concat(getRowsCols(shape)); } return shapeAs3D; } function getTextureShapeFromLogicalShape(logShape, isPacked) { var _a; if (isPacked === void 0) { isPacked = false; } var maxTexSize = tf.env().getNumber('WEBGL_MAX_TEXTURE_SIZE'); if (isPacked) { maxTexSize = maxTexSize * 2; // This logic ensures we accurately count the number of packed texels needed // to accommodate the tensor. We can only pack values in the same texel if // they are from adjacent pairs of rows/cols within the same batch. So if a // tensor has 3 rows, we pretend it has 4 rows in order to account for the // fact that the texels containing the third row are half empty. logShape = logShape.map(function (d, i) { return i >= logShape.length - 2 ? tf.util.nearestLargerEven(logShape[i]) : logShape[i]; }); // Packed texture height is at least 2 (the channel height of a single // texel). if (logShape.length === 1) { logShape = [2, logShape[0]]; } } // If logical shape is 2, we don't squeeze, since we want to match physical. if (logShape.length !== 2) { var squeezeResult = tf.util.squeezeShape(logShape); logShape = squeezeResult.newShape; } var size = tf.util.sizeFromShape(logShape); if (logShape.length <= 1 && size <= maxTexSize) { return [1, size]; } else if (logShape.length === 2 && logShape[0] <= maxTexSize && logShape[1] <= maxTexSize) { return logShape; } else if (logShape.length === 3 && logShape[0] * logShape[1] <= maxTexSize && logShape[2] <= maxTexSize) { return [logShape[0] * logShape[1], logShape[2]]; } else if (logShape.length === 3 && logShape[0] <= maxTexSize && logShape[1] * logShape[2] <= maxTexSize) { return [logShape[0], logShape[1] * logShape[2]]; } else if (logShape.length === 4 && logShape[0] * logShape[1] * logShape[2] <= maxTexSize && logShape[3] <= maxTexSize) { return [logShape[0] * logShape[1] * logShape[2], logShape[3]]; } else if (logShape.length === 4 && logShape[0] <= maxTexSize && logShape[1] * logShape[2] * logShape[3] <= maxTexSize) { return [logShape[0], logShape[1] * logShape[2] * logShape[3]]; } else { if (isPacked) { // For packed textures size equals the number of channels required to // accommodate the texture data. However in order to squarify such that // inner dimensions stay even, we rewrite size to equal the number of // texels. Then in the return statement we rehydrate the squarified // dimensions to channel units. var batchDim = getBatchDim(logShape); var rows = 2, cols = 2; if (logShape.length) { _a = getRowsCols(logShape), rows = _a[0], cols = _a[1]; } size = batchDim * (rows / 2) * (cols / 2); return tf.util.sizeToSquarishShape(size).map(function (d) { return d * 2; }); } return tf.util.sizeToSquarishShape(size); } } function isEven(n) { return n % 2 === 0; } /** * This determines whether reshaping a packed texture requires rearranging * the data within the texture, assuming 2x2 packing. */ function isReshapeFree(shape1, shape2) { shape1 = shape1.slice(-2); shape2 = shape2.slice(-2); if (tf.util.arraysEqual(shape1, shape2)) { return true; } if (!shape1.length || !shape2.length) { // One of the shapes is a scalar. return true; } if (shape1[0] === 0 || shape1[1] === 0 || shape2[0] === 0 || shape2[1] === 0) { return true; } if (shape1.length !== shape2.length) { // One of the shapes is a vector. var shape1Cols = shape1.slice(-1)[0]; var shape2Cols = shape2.slice(-1)[0]; if (shape1Cols === shape2Cols) { return true; } if (isEven(shape1Cols) && isEven(shape2Cols) && (shape1[0] === 1 || shape2[0] === 1)) { return true; } } return shape1[1] === shape2[1] && isEven(shape1[0]) && isEven(shape2[0]); } // We cache webgl params because the environment gets reset between // unit tests and we don't want to constantly query the WebGLContext for // MAX_TEXTURE_SIZE. var MAX_TEXTURE_SIZE; var MAX_TEXTURES_IN_SHADER; function getWebGLMaxTextureSize(webGLVersion) { if (MAX_TEXTURE_SIZE == null) { var gl = getWebGLContext(webGLVersion); MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE); } return MAX_TEXTURE_SIZE; } function resetMaxTextureSize() { MAX_TEXTURE_SIZE = null; } function resetMaxTexturesInShader() { MAX_TEXTURES_IN_SHADER = null; } function getMaxTexturesInShader(webGLVersion) { if (MAX_TEXTURES_IN_SHADER == null) { var gl = getWebGLContext(webGLVersion); MAX_TEXTURES_IN_SHADER = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); } // We cap at 16 to avoid spurious runtime "memory exhausted" error. return Math.min(16, MAX_TEXTURES_IN_SHADER); } function getWebGLDisjointQueryTimerVersion(webGLVersion) { if (webGLVersion === 0) { return 0; } var queryTimerVersion; var gl = getWebGLContext(webGLVersion); if (hasExtension(gl, 'EXT_disjoint_timer_query_webgl2') && webGLVersion === 2) { queryTimerVersion = 2; } else if (hasExtension(gl, 'EXT_disjoint_timer_query')) { queryTimerVersion = 1; } else { queryTimerVersion = 0; } return queryTimerVersion; } function hasExtension(gl, extensionName) { var ext = gl.getExtension(extensionName); return ext != null; } function isWebGLVersionEnabled(webGLVersion) { try { var gl = getWebGLContext(webGLVersion); if (gl != null) { return true; } } catch (e) { return false; } return false; } function isCapableOfRenderingToFloatTexture(webGLVersion) { if (webGLVersion === 0) { return false; } var gl = getWebGLContext(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { return false; } } else { if (!hasExtension(gl, 'EXT_color_buffer_float')) { return false; } } var isFrameBufferComplete = createFloatTextureAndBindToFramebuffer(gl); return isFrameBufferComplete; } /** * Check if we can download values from a float/half-float texture. * * Note that for performance reasons we use binding a texture to a framebuffer * as a proxy for ability to download float values later using readPixels. The * texture params of this texture will not match those in readPixels exactly * but if we are unable to bind some kind of float texture to the frameBuffer * then we definitely will not be able to read float values from it. */ function isDownloadFloatTextureEnabled(webGLVersion) { if (webGLVersion === 0) { return false; } var gl = getWebGLContext(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { return false; } if (!hasExtension(gl, 'WEBGL_color_buffer_float')) { return false; } } else { if (hasExtension(gl, 'EXT_color_buffer_float')) { return createFloatTextureAndBindToFramebuffer(gl); } var COLOR_BUFFER_HALF_FLOAT = 'EXT_color_buffer_half_float'; if (hasExtension(gl, COLOR_BUFFER_HALF_FLOAT)) { var textureHalfFloatExtension = gl.getExtension(COLOR_BUFFER_HALF_FLOAT); return createHalfFloatTextureAndBindToFramebuffer(gl, textureHalfFloatExtension); } return false; } var isFrameBufferComplete = createFloatTextureAndBindToFramebuffer(gl); return isFrameBufferComplete; } function createFloatTextureAndBindToFramebuffer(gl) { var texConfig = getTextureConfig(gl); var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); var width = 1; var height = 1; gl.texImage2D(gl.TEXTURE_2D, 0, texConfig.internalFormatFloat, width, height, 0, texConfig.textureFormatFloat, texConfig.textureTypeFloat, null); var frameBuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); var isFrameBufferComplete = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE; gl.bindTexture(gl.TEXTURE_2D, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.deleteTexture(texture); gl.deleteFramebuffer(frameBuffer); return isFrameBufferComplete; } function createHalfFloatTextureAndBindToFramebuffer( // tslint:disable-next-line:no-any gl, textureHalfFloatExtension) { var texConfig = getTextureConfig(gl, textureHalfFloatExtension); var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); var width = 1; var height = 1; gl.texImage2D(gl.TEXTURE_2D, 0, texConfig.internalFormatHalfFloat, width, height, 0, texConfig.textureFormatFloat, texConfig.textureTypeHalfFloat, null); var frameBuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); var isFrameBufferComplete = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE; gl.bindTexture(gl.TEXTURE_2D, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.deleteTexture(texture); gl.deleteFramebuffer(frameBuffer); return isFrameBufferComplete; } function isWebGLFenceEnabled(webGLVersion) { if (webGLVersion !== 2) { return false; } var gl = getWebGLContext(webGLVersion); // tslint:disable-next-line:no-any var isEnabled = gl.fenceSync != null; return isEnabled; } var webgl_util = { __proto__: null, callAndCheck: callAndCheck, canBeRepresented: canBeRepresented, getWebGLErrorMessage: getWebGLErrorMessage, getExtensionOrThrow: getExtensionOrThrow, createVertexShader: createVertexShader, createFragmentShader: createFragmentShader, createProgram: createProgram, linkProgram: linkProgram, validateProgram: validateProgram, createStaticVertexBuffer: createStaticVertexBuffer, createStaticIndexBuffer: createStaticIndexBuffer, getNumChannels: getNumChannels, createTexture: createTexture, validateTextureSize: validateTextureSize, createFramebuffer: createFramebuffer, bindVertexBufferToProgramAttribute: bindVertexBufferToProgramAttribute, bindTextureUnit: bindTextureUnit, unbindTextureUnit: unbindTextureUnit, getProgramUniformLocationOrThrow: getProgramUniformLocationOrThrow, getProgramUniformLocation: getProgramUniformLocation, bindTextureToProgramUniformSampler: bindTextureToProgramUniformSampler, bindCanvasToFramebuffer: bindCanvasToFramebuffer, bindColorTextureToFramebuffer: bindColorTextureToFramebuffer, unbindColorTextureFromFramebuffer: unbindColorTextureFromFramebuffer, validateFramebuffer: validateFramebuffer, getFramebufferErrorMessage: getFramebufferErrorMessage, getBatchDim: getBatchDim, getRowsCols: getRowsCols, getShapeAs3D: getShapeAs3D, getTextureShapeFromLogicalShape: getTextureShapeFromLogicalShape, isReshapeFree: isReshapeFree, getWebGLMaxTextureSize: getWebGLMaxTextureSize, resetMaxTextureSize: resetMaxTextureSize, resetMaxTexturesInShader: resetMaxTexturesInShader, getMaxTexturesInShader: getMaxTexturesInShader, getWebGLDisjointQueryTimerVersion: getWebGLDisjointQueryTimerVersion, hasExtension: hasExtension, isWebGLVersionEnabled: isWebGLVersionEnabled, isCapableOfRenderingToFloatTexture: isCapableOfRenderingToFloatTexture, isDownloadFloatTextureEnabled: isDownloadFloatTextureEnabled, isWebGLFenceEnabled: isWebGLFenceEnabled }; /** * @license * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ var ENV = tf.env(); /** * This file contains WebGL-specific flag registrations. */ /** * True if WebGL is supported. */ ENV.registerFlag('HAS_WEBGL', function () { return ENV.getNumber('WEBGL_VERSION') > 0; }); /** 0: No WebGL, 1: WebGL 1.0, 2: WebGL 2.0. */ ENV.registerFlag('WEBGL_VERSION', function () { if (isWebGLVersionEnabled(2)) { return 2; } else if (isWebGLVersionEnabled(1)) { return 1; } return 0; }); /** Whether to check for numerical representation problems. */ ENV.registerFlag('WEBGL_CHECK_NUMERICAL_PROBLEMS', function () { return false; }); ENV.registerFlag('WEBGL_BUFFER_SUPPORTED', function () { return ENV.get('WEBGL_VERSION') === 2; }); /** Whether the WebGL backend will sometimes forward ops to the CPU. */ ENV.registerFlag('WEBGL_CPU_FORWARD', function () { return true; }); /** Whether the WebGL backend will always use f16 textures for rendering. */ ENV.registerFlag('WEBGL_FORCE_F16_TEXTURES', function () { return false; }); /** Whether to turn all packing related flags on. */ ENV.registerFlag('WEBGL_PACK', function () { return ENV.getBool('HAS_WEBGL'); }); /** Whether we will pack the batchnormalization op. */ ENV.registerFlag('WEBGL_PACK_NORMALIZATION', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will pack the clip op. */ ENV.registerFlag('WEBGL_PACK_CLIP', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will pack the depthwise conv op. */ // TODO: https://github.com/tensorflow/tfjs/issues/1679 ENV.registerFlag('WEBGL_PACK_DEPTHWISECONV', function () { return false; }); /** Whether we will pack binary ops. */ ENV.registerFlag('WEBGL_PACK_BINARY_OPERATIONS', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will pack unary ops. */ ENV.registerFlag('WEBGL_PACK_UNARY_OPERATIONS', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will pack array ops. */ ENV.registerFlag('WEBGL_PACK_ARRAY_OPERATIONS', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will pack image ops. */ ENV.registerFlag('WEBGL_PACK_IMAGE_OPERATIONS', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will pack reduce ops. */ ENV.registerFlag('WEBGL_PACK_REDUCE', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether packed WebGL kernels lazily unpack their outputs. */ ENV.registerFlag('WEBGL_LAZILY_UNPACK', function () { return ENV.getBool('WEBGL_PACK'); }); /** Whether we will use the im2col algorithm to speed up convolutions. */ ENV.registerFlag('WEBGL_CONV_IM2COL', function () { return ENV.getBool('WEBGL_PACK'); }); /** The maximum texture dimension. */ ENV.registerFlag('WEBGL_MAX_TEXTURE_SIZE', function () { return getWebGLMaxTextureSize(ENV.getNumber('WEBGL_VERSION')); }); /** The maximum texture dimension. */ ENV.registerFlag('WEBGL_MAX_TEXTURES_IN_SHADER', function () { return getMaxTexturesInShader(ENV.getNumber('WEBGL_VERSION')); }); /** * The disjoint_query_timer extension version. * 0: disabled, 1: EXT_disjoint_timer_query, 2: * EXT_disjoint_timer_query_webgl2. * In Firefox with WebGL 2.0, * EXT_disjoint_timer_query_webgl2 is not available, so we must use the * WebGL 1.0 extension. */ ENV.registerFlag('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION', function () { var webGLVersion = ENV.getNumber('WEBGL_VERSION'); if (webGLVersion === 0) { return 0; } return getWebGLDisjointQueryTimerVersion(webGLVersion); }); /** * Whether the timer object from the disjoint_query_timer extension gives * timing information that is reliable. */ ENV.registerFlag('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE', function () { return ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0 && !tf.device_util.isMobile(); }); /** * Whether the device is physically capable of rendering to float32 textures. */ ENV.registerFlag('WEBGL_RENDER_FLOAT32_CAPABLE', function () { return isCapableOfRenderingToFloatTexture(ENV.getNumber('WEBGL_VERSION')); }); /** * Whether rendering to float32 textures is enabled. If disabled, renders to * float16 textures. */ ENV.registerFlag('WEBGL_RENDER_FLOAT32_ENABLED', function () { return ENV.getBool('WEBGL_FORCE_F16_TEXTURES') ? false : ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE'); }); /** * Whether downloading float textures is enabled (16 or 32 bit). If disabled, * uses IEEE 754 encoding of the float32 values to 4 uint8 when downloading. */ ENV.registerFlag('WEBGL_DOWNLOAD_FLOAT_ENABLED', function () { return isDownloadFloatTextureEnabled(ENV.getNumber('WEBGL_VERSION')); }); /** Whether the fence API is available. */ ENV.registerFlag('WEBGL_FENCE_API_ENABLED', function () { return isWebGLFenceEnabled(ENV.getNumber('WEBGL_VERSION')); }); /** * Tensors with size <= than this will be uploaded as uniforms, not textures. */ ENV.registerFlag('WEBGL_SIZE_UPLOAD_UNIFORM', function () { // Use uniform uploads only when 32bit floats are supported. In // 16bit // environments there are problems with comparing a 16bit texture value // with a 32bit uniform value. var useUniforms = ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); return useUniforms ? 4 : 0; }); /** * If the total number of bytes allocated on the GPU is greater than this * number, we will aggressively delete textures upon disposal with * gl.deleteMatrixTexture, rather than making them available for reuse. * * Default value -1 indicates that we will never aggressively delete textures. */ ENV.registerFlag('WEBGL_DELETE_TEXTURE_THRESHOLD', function () { return -1; }, function (threshold) { if (threshold < 0 && threshold !== -1) { throw new Error("WEBGL_DELETE_TEXTURE_THRESHOLD must be -1 (indicating never " + ("delete) or at least 0, but got " + threshold + ".")); } }); /** * @license * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ var AddNProgram = /** @class */ (function () { function AddNProgram(outputShape, shapes) { this.outputShape = []; this.outputShape = outputShape;