@aislamov/onnxruntime-web64
Version:
A Javascript library for running ONNX models on browsers
1,248 lines (1,200 loc) • 5.14 MB
JavaScript
/*!
* 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