@luma.gl/engine
Version:
3D Engine Components for luma.gl
99 lines • 4.29 kB
JavaScript
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import { RenderPipeline, ComputePipeline } from '@luma.gl/core';
/**
* Efficiently creates / caches pipelines
*/
export class PipelineFactory {
static defaultProps = { ...RenderPipeline.defaultProps };
/** Get the singleton default pipeline factory for the specified device */
static getDefaultPipelineFactory(device) {
device._lumaData.defaultPipelineFactory =
device._lumaData.defaultPipelineFactory || new PipelineFactory(device);
return device._lumaData.defaultPipelineFactory;
}
device;
destroyPolicy;
_hashCounter = 0;
_hashes = {};
_renderPipelineCache = {};
_computePipelineCache = {};
constructor(device) {
this.device = device;
this.destroyPolicy = device.props._factoryDestroyPolicy;
}
/** Return a RenderPipeline matching props. Reuses a similar pipeline if already created. */
createRenderPipeline(props) {
const allProps = { ...RenderPipeline.defaultProps, ...props };
const hash = this._hashRenderPipeline(allProps);
if (!this._renderPipelineCache[hash]) {
const pipeline = this.device.createRenderPipeline({
...allProps,
id: allProps.id ? `${allProps.id}-cached` : undefined
});
pipeline.hash = hash;
this._renderPipelineCache[hash] = { pipeline, useCount: 0 };
}
this._renderPipelineCache[hash].useCount++;
return this._renderPipelineCache[hash].pipeline;
}
createComputePipeline(props) {
const allProps = { ...ComputePipeline.defaultProps, ...props };
const hash = this._hashComputePipeline(allProps);
if (!this._computePipelineCache[hash]) {
const pipeline = this.device.createComputePipeline({
...allProps,
id: allProps.id ? `${allProps.id}-cached` : undefined
});
pipeline.hash = hash;
this._computePipelineCache[hash] = { pipeline, useCount: 0 };
}
this._computePipelineCache[hash].useCount++;
return this._computePipelineCache[hash].pipeline;
}
release(pipeline) {
const hash = pipeline.hash;
const cache = pipeline instanceof ComputePipeline ? this._computePipelineCache : this._renderPipelineCache;
cache[hash].useCount--;
if (cache[hash].useCount === 0) {
if (this.destroyPolicy === 'unused') {
cache[hash].pipeline.destroy();
delete cache[hash];
}
}
}
// PRIVATE
_hashComputePipeline(props) {
const shaderHash = this._getHash(props.shader.source);
return `${shaderHash}`;
}
/** Calculate a hash based on all the inputs for a render pipeline */
_hashRenderPipeline(props) {
const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
// WebGL specific
// const {varyings = [], bufferMode = {}} = props;
// const varyingHashes = varyings.map((v) => this._getHash(v));
const varyingHash = '-'; // `${varyingHashes.join('/')}B${bufferMode}`
const bufferLayoutHash = this._getHash(JSON.stringify(props.bufferLayout));
switch (this.device.type) {
case 'webgl':
// WebGL is more dynamic
return `${vsHash}/${fsHash}V${varyingHash}BL${bufferLayoutHash}`;
default:
// On WebGPU we need to rebuild the pipeline if topology, parameters or bufferLayout change
const parameterHash = this._getHash(JSON.stringify(props.parameters));
// TODO - Can json.stringify() generate different strings for equivalent objects if order of params is different?
// create a deepHash() to deduplicate?
return `${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${parameterHash}BL${bufferLayoutHash}`;
}
}
_getHash(key) {
if (this._hashes[key] === undefined) {
this._hashes[key] = this._hashCounter++;
}
return this._hashes[key];
}
}
//# sourceMappingURL=pipeline-factory.js.map