UNPKG

@lightningtv/renderer

Version:
189 lines (169 loc) 5.43 kB
/* * If not stated otherwise in this file or this component's LICENSE file the * following copyright and licenses apply: * * Copyright 2023 Comcast Cable Communications Management, LLC. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { deepClone } from '../utils.js'; import { CoreShaderNode, resolveShaderProps, type CoreShaderType, } from './renderers/CoreShaderNode.js'; import type { CoreShaderProgram } from './renderers/CoreShaderProgram.js'; import type { Stage } from './Stage.js'; export type ShaderMap = Record<string, CoreShaderType<any>>; export type ExtractProps<Props> = { [K in keyof Props]: Props[K] extends { default: infer D } ? D : Props[K]; }; export type PartialShaderProps<Props> = Partial<ExtractProps<Props>>; export type ExtractShaderProps<T extends keyof ShaderMap> = ExtractProps< ShaderMap[T]['props'] >; export type OptionalShaderProps<T extends keyof ShaderMap> = PartialShaderProps< ShaderMap[T]['props'] >; export class CoreShaderManager { protected shTypes: Record<string, CoreShaderType<any>> = {}; protected shCache: Map<string, CoreShaderProgram> = new Map(); /** * valuesCache is used to store calculations that can be shared between shader nodes. */ protected valuesCache: Map<string, Record<string, unknown>> = new Map(); protected valuesCacheUsage: Map<string, number> = new Map(); protected attachedShader: CoreShaderProgram | null = null; constructor(readonly stage: Stage) {} registerShaderType(name: string, shType: CoreShaderType<any>): void { /** * block name duplicates */ if (this.shTypes[name] !== undefined) { console.warn( `ShaderType already exists with the name: ${name}. Breaking off registration.`, ); return; } /** * Check renderer if shader type is supported. */ if (this.stage.renderer.supportsShaderType(shType) === false) { console.warn( `The renderer being used does not support this shader type. Breaking off registration.`, ); return; } this.shTypes[name] = deepClone(shType); } /** * Loads a shader (if not already loaded) and returns a controller for it. * * @param shType * @param props * @returns */ createShader<Name extends keyof ShaderMap>( name: Name, props?: Record<string, unknown>, ): CoreShaderNode | null { const shType = this.shTypes[name as string] as ShaderMap[Name]; if (shType === undefined) { console.warn( `ShaderType not found falling back on renderer default shader`, ); return this.stage.defShaderNode; } let shaderKey = name as string; if (shType.props !== undefined) { /** * if props is undefined create empty obj to fill */ props = props || {}; /** * resolve shader values */ resolveShaderProps(props, shType.props); if (shType.getCacheMarkers !== undefined) { shaderKey += `-${shType.getCacheMarkers(props)}`; } } if (this.stage.renderer.mode === 'canvas') { return this.stage.renderer.createShaderNode(shaderKey, shType, props); } /** * get shaderProgram by cacheKey */ let shProgram = this.shCache.get(shaderKey); /** * if shaderProgram was not found create a new one */ if (shProgram === undefined) { shProgram = this.stage.renderer.createShaderProgram(shType, props)!; this.shCache.set(shaderKey, shProgram); } return this.stage.renderer.createShaderNode( shaderKey, shType, props, shProgram, ); } mutateShaderValueUsage(key: string, mutation: number) { let usage = this.valuesCacheUsage.get(key) || 0; this.valuesCacheUsage.set(key, usage + mutation); } getShaderValues(key: string) { const values = this.valuesCache.get(key); if (values === undefined) { return undefined; } this.mutateShaderValueUsage(key, 1); return values; } setShaderValues(key: string, values: Record<string, unknown>) { this.valuesCache.set(key, values); this.mutateShaderValueUsage(key, 1); } cleanup() { const values = [...this.valuesCacheUsage.entries()].sort( (entryA, entryB) => { if (entryA[1] < entryB[1]) { return -1; } else if (entryA[1] > entryB[1]) { return 1; } return 0; }, ); for (let i = 0; i < values.length; i++) { if (values[i]![1] > 0) { break; } this.valuesCacheUsage.delete(values[i]![0]); this.valuesCache.delete(values[i]![0]); } } useShader(shader: CoreShaderProgram): void { if (this.attachedShader === shader) { return; } if (this.attachedShader && this.attachedShader.detach) { this.attachedShader.detach(); } if (shader.attach) { shader.attach(); } this.attachedShader = shader; } }