UNPKG

@aislamov/onnxruntime-web64

Version:

A Javascript library for running ONNX models on browsers

1,248 lines (1,200 loc) 5.14 MB
/*! * ONNX Runtime Web v1.16.0 * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["ort"] = factory(); else root["ort"] = factory(); })(self, () => { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ "./lib/backend-onnxjs.ts": /*!*******************************!*\ !*** ./lib/backend-onnxjs.ts ***! \*******************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.onnxjsBackend = void 0; const session_1 = __webpack_require__(/*! ./onnxjs/session */ "./lib/onnxjs/session.ts"); const session_handler_1 = __webpack_require__(/*! ./onnxjs/session-handler */ "./lib/onnxjs/session-handler.ts"); class OnnxjsBackend { // eslint-disable-next-line @typescript-eslint/no-empty-function async init() { } async createSessionHandler(pathOrBuffer, options) { // NOTE: Session.Config(from onnx.js) is not compatible with InferenceSession.SessionOptions(from // onnxruntime-common). // In future we should remove Session.Config and use InferenceSession.SessionOptions. // Currently we allow this to happen to make test runner work. const session = new session_1.Session(options); // typescript cannot merge method override correctly (so far in 4.2.3). need if-else to call the method. if (typeof pathOrBuffer === 'string') { await session.loadModel(pathOrBuffer); } else { await session.loadModel(pathOrBuffer); } return new session_handler_1.OnnxjsSessionHandler(session); } } exports.onnxjsBackend = new OnnxjsBackend(); /***/ }), /***/ "./lib/backend-wasm.ts": /*!*****************************!*\ !*** ./lib/backend-wasm.ts ***! \*****************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.wasmBackend = exports.initializeFlags = void 0; const onnxruntime_common_1 = __webpack_require__(/*! onnxruntime-common */ "../common/dist/cjs/index.js"); const os_1 = __webpack_require__(/*! os */ "?0757"); const proxy_wrapper_1 = __webpack_require__(/*! ./wasm/proxy-wrapper */ "./lib/wasm/proxy-wrapper.ts"); const session_handler_1 = __webpack_require__(/*! ./wasm/session-handler */ "./lib/wasm/session-handler.ts"); /** * This function initializes all flags for WebAssembly. * * Those flags are accessible from `ort.env.wasm`. Users are allow to set those flags before the first inference session * being created, to override default value. */ const initializeFlags = () => { if (typeof onnxruntime_common_1.env.wasm.initTimeout !== 'number' || onnxruntime_common_1.env.wasm.initTimeout < 0) { onnxruntime_common_1.env.wasm.initTimeout = 0; } if (typeof onnxruntime_common_1.env.wasm.simd !== 'boolean') { onnxruntime_common_1.env.wasm.simd = true; } if (typeof onnxruntime_common_1.env.wasm.proxy !== 'boolean') { onnxruntime_common_1.env.wasm.proxy = false; } if (typeof onnxruntime_common_1.env.wasm.numThreads !== 'number' || !Number.isInteger(onnxruntime_common_1.env.wasm.numThreads) || onnxruntime_common_1.env.wasm.numThreads <= 0) { const numCpuLogicalCores = typeof navigator === 'undefined' ? (0, os_1.cpus)().length : navigator.hardwareConcurrency; onnxruntime_common_1.env.wasm.numThreads = Math.min(4, Math.ceil((numCpuLogicalCores || 1) / 2)); } }; exports.initializeFlags = initializeFlags; class OnnxruntimeWebAssemblyBackend { async init() { // populate wasm flags (0, exports.initializeFlags)(); // init wasm await (0, proxy_wrapper_1.initializeWebAssemblyInstance)(); } async createSessionHandler(urisOrBuffers, options) { const handler = new session_handler_1.OnnxruntimeWebAssemblySessionHandler(); await handler.loadModel(urisOrBuffers, options); return Promise.resolve(handler); } } exports.wasmBackend = new OnnxruntimeWebAssemblyBackend(); /***/ }), /***/ "./lib/index.ts": /*!**********************!*\ !*** ./lib/index.ts ***! \**********************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", ({ value: true })); /* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports */ // We use "require" instead of "import" here because import statement must be put in top level. Our current code does // not allow terser to tree-shaking code as expected because some codes are treated as having side effects. // So we import code inside the if-clause to allow terser remove the code safely. __exportStar(__webpack_require__(/*! onnxruntime-common */ "../common/dist/cjs/index.js"), exports); const onnxruntime_common_1 = __webpack_require__(/*! onnxruntime-common */ "../common/dist/cjs/index.js"); // import {version} from './version'; if (true) { const onnxjsBackend = (__webpack_require__(/*! ./backend-onnxjs */ "./lib/backend-onnxjs.ts").onnxjsBackend); (0, onnxruntime_common_1.registerBackend)('webgl', onnxjsBackend, -10); } if (true) { const wasmBackend = (__webpack_require__(/*! ./backend-wasm */ "./lib/backend-wasm.ts").wasmBackend); // if (!BUILD_DEFS.DISABLE_WEBGPU && typeof navigator !== 'undefined' && navigator.gpu) { (0, onnxruntime_common_1.registerBackend)('webgpu', wasmBackend, 1); // } // registerBackend('cpu', wasmBackend, 1000); (0, onnxruntime_common_1.registerBackend)('wasm', wasmBackend, 1); (0, onnxruntime_common_1.registerBackend)('xnnpack', wasmBackend, 9); (0, onnxruntime_common_1.registerBackend)('webnn', wasmBackend, 9); } // Object.defineProperty(env.versions, 'web', {value: version, enumerable: true}); /***/ }), /***/ "./lib/onnxjs/attribute-with-cache-key.ts": /*!************************************************!*\ !*** ./lib/onnxjs/attribute-with-cache-key.ts ***! \************************************************/ /***/ ((__unused_webpack_module, exports) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createAttributeWithCacheKey = void 0; class AttributeWithCacheKeyImpl { constructor(attribute) { Object.assign(this, attribute); } get cacheKey() { if (!this._cacheKey) { this._cacheKey = Object.getOwnPropertyNames(this).sort().map(name => `${this[name]}`).join(';'); } return this._cacheKey; } } const createAttributeWithCacheKey = (attribute) => new AttributeWithCacheKeyImpl(attribute); exports.createAttributeWithCacheKey = createAttributeWithCacheKey; /***/ }), /***/ "./lib/onnxjs/attribute.ts": /*!*********************************!*\ !*** ./lib/onnxjs/attribute.ts ***! \*********************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Attribute = void 0; const ort_generated_1 = __webpack_require__(/*! ./ort-schema/flatbuffers/ort-generated */ "./lib/onnxjs/ort-schema/flatbuffers/ort-generated.ts"); const onnx_1 = __webpack_require__(/*! ./ort-schema/protobuf/onnx */ "./lib/onnxjs/ort-schema/protobuf/onnx.js"); const tensor_1 = __webpack_require__(/*! ./tensor */ "./lib/onnxjs/tensor.ts"); const util_1 = __webpack_require__(/*! ./util */ "./lib/onnxjs/util.ts"); var ortFbs = ort_generated_1.onnxruntime.experimental.fbs; class Attribute { constructor(attributes) { this._attributes = new Map(); if (attributes !== null && attributes !== undefined) { for (const attr of attributes) { if (attr instanceof onnx_1.onnx.AttributeProto) { this._attributes.set(attr.name, [Attribute.getValue(attr), Attribute.getType(attr)]); } else if (attr instanceof ortFbs.Attribute) { this._attributes.set(attr.name(), [Attribute.getValue(attr), Attribute.getType(attr)]); } } if (this._attributes.size < attributes.length) { throw new Error('duplicated attribute names'); } } } set(key, type, value) { this._attributes.set(key, [value, type]); } delete(key) { this._attributes.delete(key); } getFloat(key, defaultValue) { return this.get(key, 'float', defaultValue); } getInt(key, defaultValue) { return this.get(key, 'int', defaultValue); } getString(key, defaultValue) { return this.get(key, 'string', defaultValue); } getTensor(key, defaultValue) { return this.get(key, 'tensor', defaultValue); } getFloats(key, defaultValue) { return this.get(key, 'floats', defaultValue); } getInts(key, defaultValue) { return this.get(key, 'ints', defaultValue); } getStrings(key, defaultValue) { return this.get(key, 'strings', defaultValue); } getTensors(key, defaultValue) { return this.get(key, 'tensors', defaultValue); } get(key, type, defaultValue) { const valueAndType = this._attributes.get(key); if (valueAndType === undefined) { if (defaultValue !== undefined) { return defaultValue; } throw new Error(`required attribute not found: ${key}`); } if (valueAndType[1] !== type) { throw new Error(`type mismatch: expected ${type} but got ${valueAndType[1]}`); } return valueAndType[0]; } static getType(attr) { const type = attr instanceof onnx_1.onnx.AttributeProto ? (attr).type : attr.type(); switch (type) { case onnx_1.onnx.AttributeProto.AttributeType.FLOAT: return 'float'; case onnx_1.onnx.AttributeProto.AttributeType.INT: return 'int'; case onnx_1.onnx.AttributeProto.AttributeType.STRING: return 'string'; case onnx_1.onnx.AttributeProto.AttributeType.TENSOR: return 'tensor'; case onnx_1.onnx.AttributeProto.AttributeType.FLOATS: return 'floats'; case onnx_1.onnx.AttributeProto.AttributeType.INTS: return 'ints'; case onnx_1.onnx.AttributeProto.AttributeType.STRINGS: return 'strings'; case onnx_1.onnx.AttributeProto.AttributeType.TENSORS: return 'tensors'; default: throw new Error(`attribute type is not supported yet: ${onnx_1.onnx.AttributeProto.AttributeType[type]}`); } } static getValue(attr) { const attrType = attr instanceof onnx_1.onnx.AttributeProto ? attr.type : attr.type(); if (attrType === onnx_1.onnx.AttributeProto.AttributeType.GRAPH || attrType === onnx_1.onnx.AttributeProto.AttributeType.GRAPHS) { throw new Error('graph attribute is not supported yet'); } const value = this.getValueNoCheck(attr); // cast LONG to number if (attrType === onnx_1.onnx.AttributeProto.AttributeType.INT && util_1.LongUtil.isLong(value)) { return util_1.LongUtil.longToNumber(value); } // cast LONG[] to number[] if (attrType === onnx_1.onnx.AttributeProto.AttributeType.INTS) { const arr = value; const numberValue = new Array(arr.length); for (let i = 0; i < arr.length; i++) { const maybeLong = arr[i]; numberValue[i] = util_1.LongUtil.longToNumber(maybeLong); } return numberValue; } // cast onnx.TensorProto to onnxjs.Tensor if (attrType === onnx_1.onnx.AttributeProto.AttributeType.TENSOR) { return attr instanceof onnx_1.onnx.AttributeProto ? tensor_1.Tensor.fromProto(value) : tensor_1.Tensor.fromOrtTensor(value); } // cast onnx.TensorProto[] to onnxjs.Tensor[] if (attrType === onnx_1.onnx.AttributeProto.AttributeType.TENSORS) { if (attr instanceof onnx_1.onnx.AttributeProto) { const tensorProtos = value; return tensorProtos.map(value => tensor_1.Tensor.fromProto(value)); } else if (attr instanceof ortFbs.Attribute) { const tensorProtos = value; return tensorProtos.map(value => tensor_1.Tensor.fromOrtTensor(value)); } } // cast Uint8Array to string if (attrType === onnx_1.onnx.AttributeProto.AttributeType.STRING) { // string in onnx attribute is of uint8array type, so we need to convert it to string below. While in ort format, // string attributes are returned as string, so no conversion is needed. if (attr instanceof onnx_1.onnx.AttributeProto) { const utf8String = value; return (0, util_1.decodeUtf8String)(utf8String); } } // cast Uint8Array[] to string[] if (attrType === onnx_1.onnx.AttributeProto.AttributeType.STRINGS) { // strings in onnx attribute is returned as uint8array[], so we need to convert it to string[] below. While in ort // format strings attributes are returned as string[], so no conversion is needed. if (attr instanceof onnx_1.onnx.AttributeProto) { const utf8Strings = value; return utf8Strings.map(util_1.decodeUtf8String); } } return value; } static getValueNoCheck(attr) { return attr instanceof (onnx_1.onnx.AttributeProto) ? this.getValueNoCheckFromOnnxFormat(attr) : this.getValueNoCheckFromOrtFormat(attr); } static getValueNoCheckFromOnnxFormat(attr) { switch (attr.type) { case onnx_1.onnx.AttributeProto.AttributeType.FLOAT: return attr.f; case onnx_1.onnx.AttributeProto.AttributeType.INT: return attr.i; case onnx_1.onnx.AttributeProto.AttributeType.STRING: return attr.s; case onnx_1.onnx.AttributeProto.AttributeType.TENSOR: return attr.t; case onnx_1.onnx.AttributeProto.AttributeType.GRAPH: return attr.g; case onnx_1.onnx.AttributeProto.AttributeType.FLOATS: return attr.floats; case onnx_1.onnx.AttributeProto.AttributeType.INTS: return attr.ints; case onnx_1.onnx.AttributeProto.AttributeType.STRINGS: return attr.strings; case onnx_1.onnx.AttributeProto.AttributeType.TENSORS: return attr.tensors; case onnx_1.onnx.AttributeProto.AttributeType.GRAPHS: return attr.graphs; default: throw new Error(`unsupported attribute type: ${onnx_1.onnx.AttributeProto.AttributeType[attr.type]}`); } } static getValueNoCheckFromOrtFormat(attr) { switch (attr.type()) { case ortFbs.AttributeType.FLOAT: return attr.f(); case ortFbs.AttributeType.INT: return attr.i(); case ortFbs.AttributeType.STRING: return attr.s(); case ortFbs.AttributeType.TENSOR: return attr.t(); case ortFbs.AttributeType.GRAPH: return attr.g(); case ortFbs.AttributeType.FLOATS: return attr.floatsArray(); case ortFbs.AttributeType.INTS: { const ints = []; for (let i = 0; i < attr.intsLength(); i++) { ints.push(attr.ints(i)); } return ints; } case ortFbs.AttributeType.STRINGS: { const strings = []; for (let i = 0; i < attr.stringsLength(); i++) { strings.push(attr.strings(i)); } return strings; } case ortFbs.AttributeType.TENSORS: { const tensors = []; for (let i = 0; i < attr.tensorsLength(); i++) { tensors.push(attr.tensors(i)); } return tensors; } // case ortFbs.AttributeType.GRAPHS: // TODO: Subgraph not supported yet. // const graphs = []; // for (let i = 0; i < attr.graphsLength(); i++) { // graphs.push(attr.graphs(i)!); // } // return graphs; default: throw new Error(`unsupported attribute type: ${ortFbs.AttributeType[attr.type()]}`); } } } exports.Attribute = Attribute; /***/ }), /***/ "./lib/onnxjs/backend.ts": /*!*******************************!*\ !*** ./lib/onnxjs/backend.ts ***! \*******************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveBackend = exports.backend = void 0; const backend_webgl_1 = __webpack_require__(/*! ./backends/backend-webgl */ "./lib/onnxjs/backends/backend-webgl.ts"); // caches all initialized backend instances const backendsCache = new Map(); exports.backend = { webgl: new backend_webgl_1.WebGLBackend() }; /** * Resolve a reference to the backend. If a hint is specified, the corresponding * backend will be used. */ async function resolveBackend(hint) { if (!hint) { return resolveBackend(['webgl']); } else { const hints = typeof hint === 'string' ? [hint] : hint; for (const backendHint of hints) { const cache = backendsCache.get(backendHint); if (cache) { return cache; } const backend = await tryLoadBackend(backendHint); if (backend) { return backend; } } } throw new Error('no available backend to use'); } exports.resolveBackend = resolveBackend; async function tryLoadBackend(backendHint) { const backendObj = exports.backend; if (typeof backendObj[backendHint] !== 'undefined' && isBackend(backendObj[backendHint])) { const backend = backendObj[backendHint]; let init = backend.initialize(); if (typeof init === 'object' && 'then' in init) { init = await init; } if (init) { backendsCache.set(backendHint, backend); return backend; } } return undefined; } function isBackend(obj) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const o = obj; // check if an object is a Backend instance if ('initialize' in o && typeof o.initialize === 'function' && // initialize() 'createSessionHandler' in o && typeof o.createSessionHandler === 'function' && // createSessionHandler() 'dispose' in o && typeof o.dispose === 'function' // dispose() ) { return true; } return false; } /***/ }), /***/ "./lib/onnxjs/backends/backend-webgl.ts": /*!**********************************************!*\ !*** ./lib/onnxjs/backends/backend-webgl.ts ***! \**********************************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.WebGLBackend = void 0; const onnxruntime_common_1 = __webpack_require__(/*! onnxruntime-common */ "../common/dist/cjs/index.js"); const instrument_1 = __webpack_require__(/*! ../instrument */ "./lib/onnxjs/instrument.ts"); const session_handler_1 = __webpack_require__(/*! ./webgl/session-handler */ "./lib/onnxjs/backends/webgl/session-handler.ts"); const webgl_context_factory_1 = __webpack_require__(/*! ./webgl/webgl-context-factory */ "./lib/onnxjs/backends/webgl/webgl-context-factory.ts"); /** * WebGLBackend is the entry point for all WebGL opeartions * When it starts it created the WebGLRenderingContext * and other main framework components such as Program and Texture Managers */ class WebGLBackend { get contextId() { return onnxruntime_common_1.env.webgl.contextId; } set contextId(value) { onnxruntime_common_1.env.webgl.contextId = value; } get matmulMaxBatchSize() { return onnxruntime_common_1.env.webgl.matmulMaxBatchSize; } set matmulMaxBatchSize(value) { onnxruntime_common_1.env.webgl.matmulMaxBatchSize = value; } get textureCacheMode() { return onnxruntime_common_1.env.webgl.textureCacheMode; } set textureCacheMode(value) { onnxruntime_common_1.env.webgl.textureCacheMode = value; } get pack() { return onnxruntime_common_1.env.webgl.pack; } set pack(value) { onnxruntime_common_1.env.webgl.pack = value; } get async() { return onnxruntime_common_1.env.webgl.async; } set async(value) { onnxruntime_common_1.env.webgl.async = value; } initialize() { try { this.glContext = (0, webgl_context_factory_1.createWebGLContext)(this.contextId); if (typeof this.matmulMaxBatchSize !== 'number') { this.matmulMaxBatchSize = 16; } if (typeof this.textureCacheMode !== 'string') { this.textureCacheMode = 'full'; } if (typeof this.pack !== 'boolean') { this.pack = false; } if (typeof this.async !== 'boolean') { this.async = false; } instrument_1.Logger.setWithEnv(onnxruntime_common_1.env); Object.defineProperty(onnxruntime_common_1.env.webgl, 'context', { value: this.glContext.gl }); instrument_1.Logger.verbose('WebGLBackend', `Created WebGLContext: ${typeof this.glContext} with matmulMaxBatchSize: ${this.matmulMaxBatchSize}; textureCacheMode: ${this.textureCacheMode}; pack: ${this.pack}; async: ${this.async}.`); return true; } catch (e) { instrument_1.Logger.warning('WebGLBackend', `Unable to initialize WebGLBackend. ${e}`); return false; } } createSessionHandler(context) { return new session_handler_1.WebGLSessionHandler(this, context); } dispose() { this.glContext.dispose(); } } exports.WebGLBackend = WebGLBackend; /***/ }), /***/ "./lib/onnxjs/backends/webgl/glsl-coordinate-lib.ts": /*!**********************************************************!*\ !*** ./lib/onnxjs/backends/webgl/glsl-coordinate-lib.ts ***! \**********************************************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CoordsGlslLib = void 0; const util_1 = __webpack_require__(/*! ../../util */ "./lib/onnxjs/util.ts"); const glsl_definitions_1 = __webpack_require__(/*! ./glsl-definitions */ "./lib/onnxjs/backends/webgl/glsl-definitions.ts"); const glsl_source_1 = __webpack_require__(/*! ./glsl-source */ "./lib/onnxjs/backends/webgl/glsl-source.ts"); const texture_layout_strategy_1 = __webpack_require__(/*! ./texture-layout-strategy */ "./lib/onnxjs/backends/webgl/texture-layout-strategy.ts"); const utils_1 = __webpack_require__(/*! ./utils */ "./lib/onnxjs/backends/webgl/utils.ts"); /** * GLSL Library responsible for data types and routines for manipulating * coordinates and mapping to/from tensor indices */ class CoordsGlslLib extends glsl_definitions_1.GlslLib { constructor(context) { super(context); } getFunctions() { return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, this.offsetToCoords()), this.coordsToOffset()), this.toVec()), this.valueFrom()), this.getCommonUtilFuncs()), this.getInputsSamplingSnippets()), this.getOutputSamplingSnippet()); } getCustomTypes() { return {}; } /** * Produces a function that can map from * 2D normalzied coordinates (s,t) to a flat offset */ offsetToCoords() { const funcName = 'offsetToCoords'; return { offsetToCoords: new glsl_definitions_1.GlslLibRoutine(` vec2 ${funcName}(int offset, int width, int height) { int t = offset / width; int s = offset - t*width; vec2 coords = (vec2(s,t) + vec2(0.5,0.5)) / vec2(width, height); return coords; } `) }; } /** * Produces a function that can map from * 2D normalzied coordinates (s,t) to a flat offset */ coordsToOffset() { const funcName = 'coordsToOffset'; return { coordsToOffset: new glsl_definitions_1.GlslLibRoutine(` int ${funcName}(vec2 coords, int width, int height) { float s = coords.s * float(width); float t = coords.t * float(height); int offset = int(t) * width + int(s); return offset; } `) }; } /** * Generates code for output sampler. */ getOutputSamplingSnippet() { const outputLayout = this.context.outputTextureLayout; if (outputLayout.isPacked) { return this.getPackedOutputSamplingSnippet(outputLayout); } else { return this.getUnpackedOutputSamplingSnippet(outputLayout); } } /** * Generates code for packed output sampler. */ getPackedOutputSamplingSnippet(outputLayout) { const outShape = outputLayout.unpackedShape; const outTexShape = [outputLayout.width, outputLayout.height]; const result = {}; const funcName = 'getOutputCoords'; switch (outShape.length) { case 0: result[funcName] = this.getOutputScalarCoords(); break; case 1: result[funcName] = this.getOutputPacked1DCoords(outShape, outTexShape); break; case 2: result[funcName] = this.getOutputPacked2DCoords(outShape, outTexShape); break; case 3: result[funcName] = this.getOutputPacked3DCoords(outShape, outTexShape); break; default: result[funcName] = this.getOutputPackedNDCoords(outShape, outTexShape); } const glsl = (0, glsl_source_1.getGlsl)(this.context.glContext.version); // TODO we need this to properly return a packed vec4 from kernels. // Replace all '{glsl.output} = result' with 'setOutput(result)' in all kernels. const floatTextureSetRGBASource = ` void setOutput(vec4 val) { ${glsl.output} = val; } `; const floatTextureSetRGBAFuncName = 'floatTextureSetRGBA'; result[floatTextureSetRGBAFuncName] = new glsl_definitions_1.GlslLibRoutine(floatTextureSetRGBASource); return result; } /** * Generates code for unpacked output sampler. */ getUnpackedOutputSamplingSnippet(outputLayout) { const outShape = outputLayout.unpackedShape; const outTexShape = [outputLayout.width, outputLayout.height]; const result = {}; const funcName = 'getOutputCoords'; switch (outShape.length) { case 0: result[funcName] = this.getOutputScalarCoords(); break; case 1: result[funcName] = this.getOutputUnpacked1DCoords(outShape, outTexShape); break; case 2: result[funcName] = this.getOutputUnpacked2DCoords(outShape, outTexShape); break; case 3: result[funcName] = this.getOutputUnpacked3DCoords(outShape, outTexShape); break; case 4: result[funcName] = this.getOutputUnpacked4DCoords(outShape, outTexShape); break; case 5: result[funcName] = this.getOutputUnpacked5DCoords(outShape, outTexShape); break; case 6: result[funcName] = this.getOutputUnpacked6DCoords(outShape, outTexShape); break; default: throw new Error(`Unsupported output dimensionality: ${outShape.length}`); } const glsl = (0, glsl_source_1.getGlsl)(this.context.glContext.version); // TODO we need this to properly return a packed vec4 from kernels. // Replace all '{glsl.output} = result' with 'setOutput(result)' in all kernels. const floatTextureSetRSource = ` void setOutput(float val) { ${glsl.output} = vec4(val, 0, 0, 0); } `; const floatTextureSetRFuncName = 'floatTextureSetR'; result[floatTextureSetRFuncName] = new glsl_definitions_1.GlslLibRoutine(floatTextureSetRSource); return result; } /** * Scalar output coordinates. */ getOutputScalarCoords() { return new glsl_definitions_1.GlslLibRoutine(` int getOutputCoords() { return 0; } `); } /** * 1D packed output coordinates. */ getOutputPacked1DCoords(shape, texShape) { const packedTexShape = texShape; let source = ''; if (packedTexShape[0] === 1) { source = ` int getOutputCoords() { return 2 * int(TexCoords.y * ${packedTexShape[1]}.0); } `; return new glsl_definitions_1.GlslLibRoutine(source); } if (packedTexShape[1] === 1) { source = ` int getOutputCoords() { return 2 * int(TexCoords.x * ${packedTexShape[0]}.0); } `; return new glsl_definitions_1.GlslLibRoutine(source); } source = ` int getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${packedTexShape[0]}, ${packedTexShape[1]})); return 2 * (resTexRC.y * ${packedTexShape[0]} + resTexRC.x); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * 2D packed output coordinates. */ getOutputPacked2DCoords(shape, texShape) { let source = ''; if (util_1.ArrayUtil.arraysEqual(shape, texShape)) { source = ` ivec2 getOutputCoords() { return 2 * ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); } `; return new glsl_definitions_1.GlslLibRoutine(source); } const packedTexShape = texShape; // texels needed to accommodate a logical row const texelsInLogicalRow = Math.ceil(shape[1] / 2); /** * getOutputCoords * * resTexRC: The rows and columns of the texels. If you move over one * texel to the right in the packed texture, you are moving over one column * (not two). * * index: The texel index */ source = ` ivec2 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${packedTexShape[0]}, ${packedTexShape[1]})); int index = resTexRC.y * ${packedTexShape[0]} + resTexRC.x; // reverse r and c order for packed texture int r = imod(index, ${texelsInLogicalRow}) * 2; int c = 2 * (index / ${texelsInLogicalRow}); return ivec2(r, c); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * 3D packed output coordinates. */ getOutputPacked3DCoords(shape, texShape) { const packedTexShape = [texShape[0], texShape[1]]; const texelsInLogicalRow = Math.ceil(shape[2] / 2); const texelsInBatch = texelsInLogicalRow * Math.ceil(shape[1] / 2); const source = ` ivec3 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${packedTexShape[0]}, ${packedTexShape[1]})); int index = resTexRC.y * ${packedTexShape[0]} + resTexRC.x; int b = index / ${texelsInBatch}; index -= b * ${texelsInBatch}; // reverse r and c order for packed texture int r = imod(index, ${texelsInLogicalRow}) * 2; int c = 2 * (index / ${texelsInLogicalRow}); return ivec3(b, r, c); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * ND packed output coordinates. */ getOutputPackedNDCoords(shape, texShape) { const packedTexShape = [texShape[0], texShape[1]]; const texelsInLogicalRow = Math.ceil(shape[shape.length - 1] / 2); const texelsInBatch = texelsInLogicalRow * Math.ceil(shape[shape.length - 2] / 2); let texelsInBatchN = texelsInBatch; let batches = ''; let coords = 'b, r, c'; for (let b = 2; b < shape.length - 1; b++) { texelsInBatchN *= shape[shape.length - b - 1]; batches = ` int b${b} = index / ${texelsInBatchN}; index -= b${b} * ${texelsInBatchN}; ` + batches; coords = `b${b}, ` + coords; } const source = ` ivec${shape.length} getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${packedTexShape[0]}, ${packedTexShape[1]})); int index = resTexRC.y * ${packedTexShape[0]} + resTexRC.x; ${batches} int b = index / ${texelsInBatch}; index -= b * ${texelsInBatch}; // reverse r and c order for packed texture int r = imod(index, ${texelsInLogicalRow}) * 2; int c = 2 * (index / ${texelsInLogicalRow}); return ivec${shape.length}(${coords}); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Unpacked 1D output coordinates. */ getOutputUnpacked1DCoords(shape, texShape) { const source = ` int getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); return resTexRC.y * ${texShape[0]} + resTexRC.x; } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Unpacked 2D output coordinates. */ getOutputUnpacked2DCoords(shape, texShape) { const source = ` ivec2 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); int index = resTexRC.y * ${texShape[0]} + resTexRC.x; int r = index / ${shape[1]}; int c = index - r * ${shape[1]}; return ivec2(r, c); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Unpacked 3D output coordinates. */ getOutputUnpacked3DCoords(shape, texShape) { let source = ''; const rank = shape.length; let strides = null; if (rank < 2) { strides = []; } strides = new Array(rank - 1); strides[rank - 2] = shape[rank - 1]; for (let i = rank - 3; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } const coordsToCompute = ['r', 'c', 'd']; const coordsFromIndexSnippet = strides .map((stride, i) => { const line1 = `int ${coordsToCompute[i]} = index / ${stride}`; const line2 = i === strides.length - 1 ? `int ${coordsToCompute[i + 1]} = index - ${coordsToCompute[i]} * ${stride}` : `index -= ${coordsToCompute[i]} * ${stride}`; return `${line1}; ${line2};`; }) .join(''); source = ` ivec3 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); int index = resTexRC.y * ${texShape[0]} + resTexRC.x; ${coordsFromIndexSnippet} return ivec3(r, c, d); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Unpacked 4D output coordinates. */ getOutputUnpacked4DCoords(shape, texShape) { let source = ''; const rank = shape.length; let strides = null; if (rank < 2) { strides = []; } strides = new Array(rank - 1); strides[rank - 2] = shape[rank - 1]; for (let i = rank - 3; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } const coordsToCompute = ['r', 'c', 'd', 'd2']; const coordsFromIndexSnippet = strides .map((stride, i) => { const line1 = `int ${coordsToCompute[i]} = index / ${stride}`; const line2 = i === strides.length - 1 ? `int ${coordsToCompute[i + 1]} = index - ${coordsToCompute[i]} * ${stride}` : `index -= ${coordsToCompute[i]} * ${stride}`; return `${line1}; ${line2};`; }) .join(''); source = ` ivec4 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); int index = resTexRC.y * ${texShape[0]} + resTexRC.x; ${coordsFromIndexSnippet} return ivec4(r, c, d, d2); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Unpacked 5D output coordinates. */ getOutputUnpacked5DCoords(shape, texShape) { let source = ''; const rank = shape.length; let strides = null; if (rank < 2) { strides = []; } strides = new Array(rank - 1); strides[rank - 2] = shape[rank - 1]; for (let i = rank - 3; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } const coordsToCompute = ['r', 'c', 'd', 'd2', 'd3']; const coordsFromIndexSnippet = strides .map((stride, i) => { const line1 = `int ${coordsToCompute[i]} = index / ${stride}`; const line2 = i === strides.length - 1 ? `int ${coordsToCompute[i + 1]} = index - ${coordsToCompute[i]} * ${stride}` : `index -= ${coordsToCompute[i]} * ${stride}`; return `${line1}; ${line2};`; }) .join(''); source = ` ivec5 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); int index = resTexRC.y * ${texShape[0]} + resTexRC.x; ${coordsFromIndexSnippet} return ivec5(r, c, d, d2, d3); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Unpacked 6D output coordinates. */ getOutputUnpacked6DCoords(shape, texShape) { let source = ''; const rank = shape.length; let strides = null; if (rank < 2) { strides = []; } strides = new Array(rank - 1); strides[rank - 2] = shape[rank - 1]; for (let i = rank - 3; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } const coordsToCompute = ['r', 'c', 'd', 'd2', 'd3', 'd4']; const coordsFromIndexSnippet = strides .map((stride, i) => { const line1 = `int ${coordsToCompute[i]} = index / ${stride}`; const line2 = i === strides.length - 1 ? `int ${coordsToCompute[i + 1]} = index - ${coordsToCompute[i]} * ${stride}` : `index -= ${coordsToCompute[i]} * ${stride}`; return `${line1}; ${line2};`; }) .join(''); source = ` ivec6 getOutputCoords() { ivec2 resTexRC = ivec2(TexCoords.xy * vec2(${texShape[0]}, ${texShape[1]})); int index = resTexRC.y * ${texShape[0]} + resTexRC.x; ${coordsFromIndexSnippet} return ivec6(r, c, d, d2, d3, d4); } `; return new glsl_definitions_1.GlslLibRoutine(source); } /** * Generates code for common UV coords computation utility functions. */ getCommonUtilFuncs() { const result = {}; let funcName = 'uvFromFlat'; result[funcName] = new glsl_definitions_1.GlslLibRoutine(` vec2 uvFromFlat(int texNumR, int texNumC, int index) { int texC = index / texNumR; int texR = index - texC * texNumR; // TODO: swap texR, texC order in following function so row is corresponding to u and column is corresponding to // v. return (vec2(texR, texC) + halfCR) / vec2(texNumR, texNumC); } `); funcName = 'packedUVfrom1D'; result[funcName] = new glsl_definitions_1.GlslLibRoutine(` vec2 packedUVfrom1D(int texNumR, int texNumC, int index) { int texelIndex = index / 2; int texR = texelIndex / texNumC; int texC = texelIndex - texR * texNumC; return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR); } `); funcName = 'packedUVfrom2D'; result[funcName] = new glsl_definitions_1.GlslLibRoutine(` vec2 packedUVfrom2D(int texNumR, int texNumC, int texelsInLogicalRow, int row, int col) { int texelIndex = (row / 2) * texelsInLogicalRow + (col / 2); int texR = texelIndex / texNumC; int texC = texelIndex - texR * texNumC; return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR); } `); funcName = 'packedUVfrom3D'; result[funcName] = new glsl_definitions_1.GlslLibRoutine(` vec2 packedUVfrom3D(int texNumR, int texNumC, int texelsInBatch, int texelsInLogicalRow, int b, int row, int col) { int index = b * texelsInBatch + (row / 2) * texelsInLogicalRow + (col / 2); int texR = index / texNumC; int texC = index - texR * texNumC; return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR); } `); funcName = 'sampleTexture'; const glsl = (0, glsl_source_1.getGlsl)(this.context.glContext.version); result[funcName] = new glsl_definitions_1.GlslLibRoutine(` float sampleTexture(sampler2D textureSampler, vec2 uv) { return ${glsl.texture2D}(textureSampler, uv).r; }`); return result; } /** * Constructing snippets for inputs */ getInputsSamplingSnippets() { const result = {}; const outputLayout = this.context.outputTextureLayout; this.context.programInfo.inputNames.forEach((samplerName, i) => { const inputLayout = this.context.inputTextureLayouts[i]; const funcName = (0, utils_1.generateShaderFuncNameFromInputSamplerName)(samplerName); if (inputLayout.isPacked) { result[funcName] = this.getPackedSamplerFromInput(funcName, samplerName, inputLayout); } else { result[funcName] = this.getUnpackedSamplerFromInput(funcName, samplerName, inputLayout); } const outCoordFuncName = (0, utils_1.generateShaderFuncNameFromInputSamplerNameAtOutCoords)(samplerName); if (inputLayout.unpackedShape.length <= outputLayout.unpackedShape.length) { if (inputLayout.isPacked) { result[outCoordFuncName] = this.getPackedSamplerAtOutputCoords(outCoordFuncName, inputLayout, outputLayout, samplerName); } else { result[outCoordFuncName] = this.getUnpackedSamplerAtOutputCoords(outCoordFuncName, inputLayout, outputLayout, samplerName); } } }); return result; } /** * Constructing snippets for output coordinates of samplers */ getPackedSamplerAtOutputCoords(funcName, inputLayout, outputLayout, name) { const inShape = inputLayout.unpackedShape; const outShape = outputLayout.unpackedShape; const texName = name; const texFuncSnippet = (0, utils_1.generateShaderFuncNameFromInputSamplerName)(texName); const inRank = inShape.length; const outRank = outShape.length; const broadcastDims = util_1.BroadcastUtil.getBroadcastDims(inShape, outShape); const type = (0, utils_1.getCoordsDataType)(outRank); const rankDiff = outRank - inRank; let coordsSnippet; const fields = (0, utils_1.getGlChannels)(); if (inRank === 0) { coordsSnippet = ''; } else if (outRank < 2 && broadcastDims.length >= 1) { coordsSnippet = 'coords = 0;'; } else { coordsSnippet = broadcastDims.map(d => `coords.${fields[d + rankDiff]} = 0;`).join('\n'); } let unpackedCoordsSnippet = ''; if (outRank < 2 && inRank > 0) { unpackedCoordsSnippet = 'coords'; } else { unpackedCoordsSnippet = inShape.map((s, i) => `coords.${fields[i + rankDiff]}`).join(', '); } let output = 'return outputValue;'; const inSize = util_1.ShapeUtil.size(inShape); const isInputScalar = inSize === 1; const outSize = util_1.ShapeUtil.size(outShape); const isOutputScalar = outSize === 1; if (inRank === 1 && !isInputScalar && !isOutputScalar) { output = ` return vec4(outputValue.xy, outputValue.xy); `; } else if (isInputScalar && !isOutputScalar) { if (outRank === 1) { output = ` return vec4(outputValue.x, outputValue.x, 0., 0.); `; } else { output = ` return vec4(outputValue.x); `; } } else if (broadcastDims.length) { const rows = inRank - 2; const cols = inRank - 1; if (broadcastDims.indexOf(rows) > -1 && broadcastDims.indexOf(cols) > -1) { output = 'return vec4(outputValue.x);'; } else if (broadcastDims.indexOf(rows) > -1) { output = 'return vec4(outputValue.x, outputValue.y, ' + 'outputValue.x, outputValue.y);'; } else if (broadcastDims.indexOf(cols) > -1) { output = 'return vec4(outputValue.xx, outputValue.zz);'; } } const swapLastDimsSnippet = ` int lastDim = coords.${fields[outRank - 1]}; coords.${fields[outRank - 1]} = coords.${fields[outRank - 2]}; coords.${fields[outRank - 2]} = lastDim; `; const source = ` vec4 ${funcName}() { ${type} coords = getOutputCoords(); ${swapLastDimsSnippet} ${coordsSnippet} vec4 outputValue = ${texFuncSnippet}(${unpackedCoordsSnippet}); ${output} } `; return new glsl_definitions_1.GlslLibRoutine(source, ['coordinates.getOutputCoords']); } /** * Constructing snippets for unpacked output coordinates of samplers */ getUnpackedSamplerAtOutputCoords(funcName, inputLayout, outputLayout, name) { const outTexShape = [outputLayout.width, outputLayout.height]; const inTexShape = [inputLayout.width, inputLayout.height]; const inRank = inputLayout.unpackedShape.length; const outRank = outputLayout.unpackedShape.length; const inShape = inputLayout.unpackedShape; const outShape = outputLayout.unpackedShape; const texFuncSnippet = (0, utils_1.generateShaderFuncNameFromInputSamplerName)(name); if (inRank === outRank && util_1.ArrayUtil.arraysEqual(inTexShape, outT