202 lines
8.6 KiB
JavaScript
202 lines
8.6 KiB
JavaScript
"use strict";
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.TextureManager = void 0;
|
|
const instrument_1 = require("../../instrument");
|
|
/**
|
|
* TextureManager is the mainly responsible for caching Textures
|
|
* Textures are cached in 2 levels:
|
|
* 1. the texures which are associated with a dataId (from Tensor)
|
|
* Caching these is crucial to performance. These are In-use Textures
|
|
* 2. textures which are not in use by any current ProgramInfo/Tensor
|
|
* These are called Free Textures
|
|
* TextureManager is also used to help creating textures. For this it
|
|
* uses WebGLContext and TextureLayoutStrategy
|
|
*/
|
|
class TextureManager {
|
|
constructor(glContext, layoutStrategy, profiler, config) {
|
|
this.glContext = glContext;
|
|
this.layoutStrategy = layoutStrategy;
|
|
this.profiler = profiler;
|
|
this.config = config;
|
|
this.pendingRead = new Map();
|
|
if (config.reuseTextures) {
|
|
this.inUseTextures = new Map();
|
|
this.idleTextures = new Map();
|
|
this.textureLookup = new Map();
|
|
}
|
|
}
|
|
createTextureFromLayout(dataType, layout, data, usage) {
|
|
const textureDataType = this.toEncoderType(dataType);
|
|
const encoder = this.glContext.getEncoder(textureDataType, layout.channels || 1, usage);
|
|
if (layout.isPacked && usage === 1 /* Encoder.Usage.UploadOnly */) {
|
|
throw new Error('not implemented');
|
|
}
|
|
const width = layout.width;
|
|
const height = layout.height;
|
|
let key;
|
|
let inUseTextures;
|
|
if (this.config.reuseTextures) {
|
|
key = `${width}x${height}_${encoder.format}_${encoder.internalFormat}_${encoder.textureType}`;
|
|
inUseTextures = this.inUseTextures.get(key);
|
|
if (!inUseTextures) {
|
|
inUseTextures = [];
|
|
this.inUseTextures.set(key, inUseTextures);
|
|
}
|
|
const idleTextures = this.idleTextures.get(key);
|
|
if (idleTextures && idleTextures.length > 0) {
|
|
const texture = idleTextures.pop();
|
|
inUseTextures.push(texture);
|
|
if (usage === 1 /* Encoder.Usage.UploadOnly */) {
|
|
this.glContext.updateTexture(texture, width, height, encoder, this.toTextureData(dataType, data));
|
|
}
|
|
return texture;
|
|
}
|
|
}
|
|
instrument_1.Logger.verbose('TextureManager', `Creating new texture of size ${layout.width}x${layout.height}`);
|
|
const texture = this.glContext.allocateTexture(width, height, encoder, this.toTextureData(dataType, data));
|
|
if (this.config.reuseTextures) {
|
|
inUseTextures.push(texture);
|
|
this.textureLookup.set(texture, key);
|
|
}
|
|
return texture;
|
|
}
|
|
readTexture(td, dataType, channels) {
|
|
if (!channels) {
|
|
channels = 1;
|
|
}
|
|
return this.profiler.event('backend', 'TextureManager.readTexture', () => {
|
|
const dataSize = td.shape.reduce((a, b) => a * b) * channels;
|
|
const data = this.glContext.readTexture(td.texture, td.width, td.height, dataSize, this.toEncoderType(dataType), channels);
|
|
return this.toTensorData(dataType, data);
|
|
});
|
|
}
|
|
async readTextureAsync(td, dataType, channels) {
|
|
const dataId = td.tensor.dataId;
|
|
if (!channels) {
|
|
channels = 1;
|
|
}
|
|
if (this.pendingRead.has(dataId)) {
|
|
const subscribers = this.pendingRead.get(dataId);
|
|
return new Promise(resolve => subscribers === null || subscribers === void 0 ? void 0 : subscribers.push(resolve));
|
|
}
|
|
return this.profiler.event('backend', 'TextureManager.readTextureAsync', async () => {
|
|
this.pendingRead.set(dataId, []);
|
|
const dataSize = td.shape.reduce((a, b) => a * b) * channels;
|
|
// add a fence waiting for the data to be ready
|
|
await this.glContext.createAndWaitForFence();
|
|
const data = this.glContext.readTexture(td.texture, td.width, td.height, dataSize, this.toEncoderType(dataType), channels);
|
|
const tensorData = this.toTensorData(dataType, data);
|
|
const subscribers = this.pendingRead.get(dataId);
|
|
this.pendingRead.delete(dataId);
|
|
subscribers === null || subscribers === void 0 ? void 0 : subscribers.forEach(resolve => resolve(tensorData));
|
|
return tensorData;
|
|
});
|
|
}
|
|
readUint8TextureAsFloat(td) {
|
|
return this.profiler.event('backend', 'TextureManager.readUint8TextureAsFloat', () => {
|
|
const dataSize = td.shape.reduce((a, b) => a * b);
|
|
const data = this.glContext.readTexture(td.texture, td.width, td.height, dataSize * 4, 'byte', 4);
|
|
return new Float32Array(data.buffer, data.byteOffset, dataSize);
|
|
});
|
|
}
|
|
releaseTexture(textureData, deleteTexture) {
|
|
let key;
|
|
if (this.config.reuseTextures) {
|
|
key = this.textureLookup.get(textureData.texture);
|
|
if (key) {
|
|
if (deleteTexture) {
|
|
this.textureLookup.delete(key);
|
|
}
|
|
const inUseTextures = this.inUseTextures.get(key);
|
|
if (inUseTextures) {
|
|
const index = inUseTextures.indexOf(textureData.texture);
|
|
if (index !== -1) {
|
|
inUseTextures.splice(index, 1);
|
|
let idleTextures = this.idleTextures.get(key);
|
|
if (!idleTextures) {
|
|
idleTextures = [];
|
|
this.idleTextures.set(key, idleTextures);
|
|
}
|
|
idleTextures.push(textureData.texture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!key || deleteTexture) {
|
|
instrument_1.Logger.verbose('TextureManager', `Deleting texture of size ${textureData.width}x${textureData.height}`);
|
|
this.glContext.deleteTexture(textureData.texture);
|
|
}
|
|
}
|
|
toTensorData(dataType, data) {
|
|
switch (dataType) {
|
|
case 'int16':
|
|
return data instanceof Int16Array ? data : Int16Array.from(data);
|
|
case 'int32':
|
|
return data instanceof Int32Array ? data : Int32Array.from(data);
|
|
case 'int8':
|
|
return data instanceof Int8Array ? data : Int8Array.from(data);
|
|
case 'uint16':
|
|
return data instanceof Uint16Array ? data : Uint16Array.from(data);
|
|
case 'uint32':
|
|
return data instanceof Uint32Array ? data : Uint32Array.from(data);
|
|
case 'uint8':
|
|
case 'bool':
|
|
return data instanceof Uint8Array ? data : Uint8Array.from(data);
|
|
case 'float32':
|
|
return data instanceof Float32Array ? data : Float32Array.from(data);
|
|
case 'float64':
|
|
return data instanceof Float64Array ? data : Float64Array.from(data);
|
|
default:
|
|
throw new Error(`TensorData type ${dataType} is not supported`);
|
|
}
|
|
}
|
|
toTextureData(dataType, data) {
|
|
if (!data) {
|
|
return undefined;
|
|
}
|
|
return (data instanceof Float32Array) ? data : new Float32Array(data);
|
|
/*
|
|
switch (dataType) {
|
|
case 'int16':
|
|
case 'int32':
|
|
case 'uint16':
|
|
case 'uint32':
|
|
return (data.constructor === Uint32Array) ? data as Uint32Array : new Uint32Array(data);
|
|
case 'int8':
|
|
case 'uint8':
|
|
case 'bool':
|
|
return (data.constructor === Uint8Array) ? data as Uint8Array : new Uint8Array(data);
|
|
case 'float32':
|
|
case 'float64':
|
|
return (data.constructor === Float32Array) ? data as Float32Array : new Float32Array(data);
|
|
default:
|
|
throw new Error(`TensorData type ${dataType} is not supported`);
|
|
}
|
|
*/
|
|
}
|
|
toEncoderType(_dataType) {
|
|
return 'float';
|
|
// switch (dataType) {
|
|
// case 'int16':
|
|
// case 'int32':
|
|
// case 'uint16':
|
|
// case 'uint32':
|
|
// return 'int';
|
|
// case 'uint8':
|
|
// case 'bool':
|
|
// return 'byte';
|
|
// case 'float32':
|
|
// case 'float64':
|
|
// return 'float';
|
|
// default:
|
|
// throw new Error(`TensorData type ${dataType} is not supported`);
|
|
// }
|
|
}
|
|
clearActiveTextures() {
|
|
this.glContext.clearActiveTextures();
|
|
}
|
|
}
|
|
exports.TextureManager = TextureManager;
|
|
//# sourceMappingURL=texture-manager.js.map
|