@luma.gl/engine
Version:
3D Engine Components for luma.gl
65 lines (53 loc) • 2 kB
text/typescript
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import {Device, Shader, ShaderProps} from '@luma.gl/core';
/** Manages a cached pool of Shaders for reuse. */
export class ShaderFactory {
static readonly defaultProps: Required<ShaderProps> = {...Shader.defaultProps};
/** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
static getDefaultShaderFactory(device: Device): ShaderFactory {
device._lumaData.defaultShaderFactory ||= new ShaderFactory(device);
return device._lumaData.defaultShaderFactory as ShaderFactory;
}
public readonly device: Device;
readonly destroyPolicy: 'unused' | 'never';
private readonly _cache: Record<string, {shader: Shader; useCount: number}> = {};
/** @internal */
constructor(device: Device) {
this.device = device;
this.destroyPolicy = device.props._factoryDestroyPolicy;
}
/** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
createShader(props: ShaderProps): Shader {
const key = this._hashShader(props);
let cacheEntry = this._cache[key];
if (!cacheEntry) {
const shader = this.device.createShader({
...props,
id: props.id ? `${props.id}-cached` : undefined
});
this._cache[key] = cacheEntry = {shader, useCount: 0};
}
cacheEntry.useCount++;
return cacheEntry.shader;
}
/** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
release(shader: Shader): void {
const key = this._hashShader(shader);
const cacheEntry = this._cache[key];
if (cacheEntry) {
cacheEntry.useCount--;
if (cacheEntry.useCount === 0) {
if (this.destroyPolicy === 'unused') {
delete this._cache[key];
cacheEntry.shader.destroy();
}
}
}
}
// PRIVATE
private _hashShader(value: Shader | ShaderProps): string {
return `${value.stage}:${value.source}`;
}
}