UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

370 lines (324 loc) 12.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _DataMap = _interopRequireDefault(require("./DataMap.js")); var _RenderPipeline = _interopRequireDefault(require("./RenderPipeline.js")); var _ComputePipeline = _interopRequireDefault(require("./ComputePipeline.js")); var _ProgrammableStage = _interopRequireDefault(require("./ProgrammableStage.js")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * This renderer module manages the pipelines of the renderer. * * @private * @augments DataMap */ class Pipelines extends _DataMap.default { /** * Constructs a new pipeline management component. * * @param {Backend} backend - The renderer's backend. * @param {Nodes} nodes - Renderer component for managing nodes related logic. */ constructor(backend, nodes) { super(); /** * The renderer's backend. * * @type {Backend} */ this.backend = backend; /** * Renderer component for managing nodes related logic. * * @type {Nodes} */ this.nodes = nodes; /** * A references to the bindings management component. * This reference will be set inside the `Bindings` * constructor. * * @type {?Bindings} * @default null */ this.bindings = null; /** * Internal cache for maintaining pipelines. * The key of the map is a cache key, the value the pipeline. * * @type {Map<string,Pipeline>} */ this.caches = new Map(); /** * This dictionary maintains for each shader stage type (vertex, * fragment and compute) the programmable stage objects which * represent the actual shader code. * * @type {Object<string,Map>} */ this.programs = { vertex: new Map(), fragment: new Map(), compute: new Map() }; } /** * Returns a compute pipeline for the given compute node. * * @param {Node} computeNode - The compute node. * @param {Array<BindGroup>} bindings - The bindings. * @return {ComputePipeline} The compute pipeline. */ getForCompute(computeNode, bindings) { const { backend } = this; const data = this.get(computeNode); if (this._needsComputeUpdate(computeNode)) { const previousPipeline = data.pipeline; if (previousPipeline) { previousPipeline.usedTimes--; previousPipeline.computeProgram.usedTimes--; } // get shader const nodeBuilderState = this.nodes.getForCompute(computeNode); // programmable stage let stageCompute = this.programs.compute.get(nodeBuilderState.computeShader); if (stageCompute === undefined) { if (previousPipeline && previousPipeline.computeProgram.usedTimes === 0) this._releaseProgram(previousPipeline.computeProgram); stageCompute = new _ProgrammableStage.default(nodeBuilderState.computeShader, 'compute', computeNode.name, nodeBuilderState.transforms, nodeBuilderState.nodeAttributes); this.programs.compute.set(nodeBuilderState.computeShader, stageCompute); backend.createProgram(stageCompute); } // determine compute pipeline const cacheKey = this._getComputeCacheKey(computeNode, stageCompute); let pipeline = this.caches.get(cacheKey); if (pipeline === undefined) { if (previousPipeline && previousPipeline.usedTimes === 0) this._releasePipeline(previousPipeline); pipeline = this._getComputePipeline(computeNode, stageCompute, cacheKey, bindings); } // keep track of all used times pipeline.usedTimes++; stageCompute.usedTimes++; // data.version = computeNode.version; data.pipeline = pipeline; } return data.pipeline; } /** * Returns a render pipeline for the given render object. * * @param {RenderObject} renderObject - The render object. * @param {?Array<Promise>} [promises=null] - An array of compilation promises which is only relevant in context of `Renderer.compileAsync()`. * @return {RenderPipeline} The render pipeline. */ getForRender(renderObject, promises = null) { const { backend } = this; const data = this.get(renderObject); if (this._needsRenderUpdate(renderObject)) { const previousPipeline = data.pipeline; if (previousPipeline) { previousPipeline.usedTimes--; previousPipeline.vertexProgram.usedTimes--; previousPipeline.fragmentProgram.usedTimes--; } // get shader const nodeBuilderState = renderObject.getNodeBuilderState(); const name = renderObject.material ? renderObject.material.name : ''; // programmable stages let stageVertex = this.programs.vertex.get(nodeBuilderState.vertexShader); if (stageVertex === undefined) { if (previousPipeline && previousPipeline.vertexProgram.usedTimes === 0) this._releaseProgram(previousPipeline.vertexProgram); stageVertex = new _ProgrammableStage.default(nodeBuilderState.vertexShader, 'vertex', name); this.programs.vertex.set(nodeBuilderState.vertexShader, stageVertex); backend.createProgram(stageVertex); } let stageFragment = this.programs.fragment.get(nodeBuilderState.fragmentShader); if (stageFragment === undefined) { if (previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0) this._releaseProgram(previousPipeline.fragmentProgram); stageFragment = new _ProgrammableStage.default(nodeBuilderState.fragmentShader, 'fragment', name); this.programs.fragment.set(nodeBuilderState.fragmentShader, stageFragment); backend.createProgram(stageFragment); } // determine render pipeline const cacheKey = this._getRenderCacheKey(renderObject, stageVertex, stageFragment); let pipeline = this.caches.get(cacheKey); if (pipeline === undefined) { if (previousPipeline && previousPipeline.usedTimes === 0) this._releasePipeline(previousPipeline); pipeline = this._getRenderPipeline(renderObject, stageVertex, stageFragment, cacheKey, promises); } else { renderObject.pipeline = pipeline; } // keep track of all used times pipeline.usedTimes++; stageVertex.usedTimes++; stageFragment.usedTimes++; // data.pipeline = pipeline; } return data.pipeline; } /** * Deletes the pipeline for the given render object. * * @param {RenderObject} object - The render object. * @return {?Object} The deleted dictionary. */ delete(object) { const pipeline = this.get(object).pipeline; if (pipeline) { // pipeline pipeline.usedTimes--; if (pipeline.usedTimes === 0) this._releasePipeline(pipeline); // programs if (pipeline.isComputePipeline) { pipeline.computeProgram.usedTimes--; if (pipeline.computeProgram.usedTimes === 0) this._releaseProgram(pipeline.computeProgram); } else { pipeline.fragmentProgram.usedTimes--; pipeline.vertexProgram.usedTimes--; if (pipeline.vertexProgram.usedTimes === 0) this._releaseProgram(pipeline.vertexProgram); if (pipeline.fragmentProgram.usedTimes === 0) this._releaseProgram(pipeline.fragmentProgram); } } return super.delete(object); } /** * Frees internal resources. */ dispose() { super.dispose(); this.caches = new Map(); this.programs = { vertex: new Map(), fragment: new Map(), compute: new Map() }; } /** * Updates the pipeline for the given render object. * * @param {RenderObject} renderObject - The render object. */ updateForRender(renderObject) { this.getForRender(renderObject); } /** * Returns a compute pipeline for the given parameters. * * @private * @param {Node} computeNode - The compute node. * @param {ProgrammableStage} stageCompute - The programmable stage representing the compute shader. * @param {string} cacheKey - The cache key. * @param {Array<BindGroup>} bindings - The bindings. * @return {ComputePipeline} The compute pipeline. */ _getComputePipeline(computeNode, stageCompute, cacheKey, bindings) { // check for existing pipeline cacheKey = cacheKey || this._getComputeCacheKey(computeNode, stageCompute); let pipeline = this.caches.get(cacheKey); if (pipeline === undefined) { pipeline = new _ComputePipeline.default(cacheKey, stageCompute); this.caches.set(cacheKey, pipeline); this.backend.createComputePipeline(pipeline, bindings); } return pipeline; } /** * Returns a render pipeline for the given parameters. * * @private * @param {RenderObject} renderObject - The render object. * @param {ProgrammableStage} stageVertex - The programmable stage representing the vertex shader. * @param {ProgrammableStage} stageFragment - The programmable stage representing the fragment shader. * @param {string} cacheKey - The cache key. * @param {?Array<Promise>} promises - An array of compilation promises which is only relevant in context of `Renderer.compileAsync()`. * @return {ComputePipeline} The compute pipeline. */ _getRenderPipeline(renderObject, stageVertex, stageFragment, cacheKey, promises) { // check for existing pipeline cacheKey = cacheKey || this._getRenderCacheKey(renderObject, stageVertex, stageFragment); let pipeline = this.caches.get(cacheKey); if (pipeline === undefined) { pipeline = new _RenderPipeline.default(cacheKey, stageVertex, stageFragment); this.caches.set(cacheKey, pipeline); renderObject.pipeline = pipeline; // The `promises` array is `null` by default and only set to an empty array when // `Renderer.compileAsync()` is used. The next call actually fills the array with // pending promises that resolve when the render pipelines are ready for rendering. this.backend.createRenderPipeline(renderObject, promises); } return pipeline; } /** * Computes a cache key representing a compute pipeline. * * @private * @param {Node} computeNode - The compute node. * @param {ProgrammableStage} stageCompute - The programmable stage representing the compute shader. * @return {string} The cache key. */ _getComputeCacheKey(computeNode, stageCompute) { return computeNode.id + ',' + stageCompute.id; } /** * Computes a cache key representing a render pipeline. * * @private * @param {RenderObject} renderObject - The render object. * @param {ProgrammableStage} stageVertex - The programmable stage representing the vertex shader. * @param {ProgrammableStage} stageFragment - The programmable stage representing the fragment shader. * @return {string} The cache key. */ _getRenderCacheKey(renderObject, stageVertex, stageFragment) { return stageVertex.id + ',' + stageFragment.id + ',' + this.backend.getRenderCacheKey(renderObject); } /** * Releases the given pipeline. * * @private * @param {Pipeline} pipeline - The pipeline to release. */ _releasePipeline(pipeline) { this.caches.delete(pipeline.cacheKey); } /** * Releases the shader program. * * @private * @param {Object} program - The shader program to release. */ _releaseProgram(program) { const code = program.code; const stage = program.stage; this.programs[stage].delete(code); } /** * Returns `true` if the compute pipeline for the given compute node requires an update. * * @private * @param {Node} computeNode - The compute node. * @return {boolean} Whether the compute pipeline for the given compute node requires an update or not. */ _needsComputeUpdate(computeNode) { const data = this.get(computeNode); return data.pipeline === undefined || data.version !== computeNode.version; } /** * Returns `true` if the render pipeline for the given render object requires an update. * * @private * @param {RenderObject} renderObject - The render object. * @return {boolean} Whether the render object for the given render object requires an update or not. */ _needsRenderUpdate(renderObject) { const data = this.get(renderObject); return data.pipeline === undefined || this.backend.needsRenderUpdate(renderObject); } } var _default = exports.default = Pipelines;