UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

307 lines (304 loc) 10.6 kB
import { Filter, Shader } from 'pixi.js'; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); const _ShaderResourceManager = class _ShaderResourceManager { /** * Creates a new ShaderResourceManager instance * * @param options - Configuration options */ constructor(options = {}) { /** Pool of shader programs indexed by hash */ __publicField(this, "shaderPool", /* @__PURE__ */ new Map()); /** Map of filters to their associated shader hashes */ __publicField(this, "filterShaderMap", /* @__PURE__ */ new Map()); /** Manager configuration */ __publicField(this, "options"); /** Is debug logging enabled */ __publicField(this, "debug", false); this.options = { enableMetrics: options.enableMetrics ?? true, debug: options.debug ?? false, maxPoolSize: options.maxPoolSize ?? 100 }; this.debug = this.options.debug ?? false; this.log("ShaderResourceManager initialized"); } /** * Get the singleton instance of the ShaderResourceManager * * @param options - Optional configuration options * @returns The singleton instance */ static getInstance(options = {}) { if (!_ShaderResourceManager.instance) { _ShaderResourceManager.instance = new _ShaderResourceManager(options); } return _ShaderResourceManager.instance; } /** * Log a message if debug is enabled * * @param message - Message to log */ log(message) { if (this.debug) { console.log(`[ShaderResourceManager] ${message}`); } } /** * Generate a hash for a shader program based on its source code * * @param vertexSrc - Vertex shader source code * @param fragmentSrc - Fragment shader source code * @returns A hash string uniquely identifying the shader program */ generateShaderHash(vertexSrc, fragmentSrc) { const combinedSrc = `${vertexSrc}:${fragmentSrc}`; let hash = 0; for (let i = 0; i < combinedSrc.length; i++) { const char = combinedSrc.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return `shader_${Math.abs(hash).toString(16)}`; } getShaderProgram(keyOrFilter, vertexSrcOrFilter, fragmentSrc, filterType) { if (typeof keyOrFilter === "string" && vertexSrcOrFilter instanceof Filter) { const key = keyOrFilter; const filter = vertexSrcOrFilter; if (this.filterShaderMap.has(filter)) { const hash = this.filterShaderMap.get(filter); if (hash) { const entry = this.shaderPool.get(hash); if (entry) { entry.stats.usageCount++; entry.stats.lastUsed = Date.now(); this.log(`Using already mapped shader for ${key}`); return entry.program; } } } try { let program = null; let vertexSrc = ""; let fragmentSrc2 = ""; const extractedFilterType = filter.constructor.name; if ("shader" in filter && filter.shader) { if (filter.shader instanceof Shader && filter.shader.glProgram) { program = filter.shader.glProgram; const hash = `key_${key}_${Date.now()}`; const entry = { program, filterType: extractedFilterType, vertexSrc: "extracted", // Placeholder fragmentSrc: "extracted", // Placeholder stats: { compilationCount: 0, // Not compiled by us usageCount: 1, instanceCount: 1, compilationTime: 0, lastUsed: Date.now() } }; this.shaderPool.set(hash, entry); this.filterShaderMap.set(filter, hash); this.log(`Registered existing shader for ${key} (${extractedFilterType})`); return program; } } const fallbackHash = `key_${key}_${Date.now()}`; this.log(`Could not extract shader source for ${key}, using basic tracking`); this.filterShaderMap.set(filter, fallbackHash); if ("program" in filter && typeof filter.program === "object" && filter.program !== null) { return filter.program; } if ("shader" in filter && filter.shader && typeof filter.shader === "object" && "glProgram" in filter.shader && filter.shader.glProgram) { return filter.shader.glProgram; } this.log(`Unable to access shader program for ${key}`); return void 0; } catch (error) { this.log(`Error extracting shader from filter: ${error}`); return void 0; } } if (typeof keyOrFilter === "object" && typeof vertexSrcOrFilter === "string" && typeof fragmentSrc === "string" && typeof filterType === "string") { return this.getShaderProgramInternal( keyOrFilter, vertexSrcOrFilter, fragmentSrc, filterType ); } this.log("Invalid parameters for getShaderProgram"); return void 0; } /** * Internal implementation of getShaderProgram with all parameters */ getShaderProgramInternal(filter, vertexSrc, fragmentSrc, filterType) { performance.now(); const hash = this.generateShaderHash(vertexSrc, fragmentSrc); let entry = this.shaderPool.get(hash); if (entry) { entry.stats.usageCount++; entry.stats.instanceCount++; entry.stats.lastUsed = Date.now(); this.filterShaderMap.set(filter, hash); this.log(`Reusing shader program ${hash} for ${filterType} filter`); return entry.program; } this.log(`Cannot directly create shader program in this PixiJS version`); const fallbackHash = `key_manual_${Date.now()}`; this.filterShaderMap.set(filter, fallbackHash); return void 0; } releaseShader(filter) { if (typeof filter === "string") { const filtersToRelease = []; for (const [filterInstance, hash] of this.filterShaderMap.entries()) { if (hash.includes(`key_${filter}_`)) { filtersToRelease.push(filterInstance); } } for (const filterToRelease of filtersToRelease) { this.releaseShaderByFilter(filterToRelease); } return; } this.releaseShaderByFilter(filter); } /** * Release a shader program associated with a filter * * @param filter - The filter instance */ releaseShaderByFilter(filter) { const hash = this.filterShaderMap.get(filter); if (!hash) { return; } const entry = this.shaderPool.get(hash); if (entry) { entry.stats.instanceCount--; if (entry.stats.instanceCount <= 0) { this.log(`Removing unused shader program ${hash}`); this.shaderPool.delete(hash); } } this.filterShaderMap.delete(filter); } /** * Prune the shader pool by removing the least recently used shaders * when the pool exceeds the maximum size */ pruneShaderPool() { if (this.shaderPool.size <= (this.options.maxPoolSize || 100)) { return; } const entries = Array.from(this.shaderPool.entries()).sort(([, a], [, b]) => a.stats.lastUsed - b.stats.lastUsed); const removeCount = Math.ceil(this.shaderPool.size * 0.2); for (let i = 0; i < removeCount && i < entries.length; i++) { const [hash, entry] = entries[i]; if (entry.stats.instanceCount <= 0) { this.log(`Pruning shader ${hash} (last used: ${new Date(entry.stats.lastUsed).toISOString()})`); this.shaderPool.delete(hash); } } } /** * Get statistics about shader usage and the shader pool * * @returns Statistics object */ getStats() { const stats = { totalShaders: this.shaderPool.size, activeShaders: 0, totalCompilationTime: 0, avgCompilationTime: 0, totalUsage: 0, oldestShader: 0, newestShader: 0, shaderTypes: {} }; if (this.shaderPool.size > 0) { for (const [, entry] of this.shaderPool.entries()) { if (entry.stats.instanceCount > 0) { stats.activeShaders++; } stats.totalCompilationTime += entry.stats.compilationTime; stats.totalUsage += entry.stats.usageCount; if (!stats.shaderTypes[entry.filterType]) { stats.shaderTypes[entry.filterType] = 0; } stats.shaderTypes[entry.filterType]++; if (stats.oldestShader === 0 || entry.stats.lastUsed < stats.oldestShader) { stats.oldestShader = entry.stats.lastUsed; } if (stats.newestShader === 0 || entry.stats.lastUsed > stats.newestShader) { stats.newestShader = entry.stats.lastUsed; } } stats.avgCompilationTime = stats.totalCompilationTime / this.shaderPool.size; } return stats; } /** * Registers a filter with the shader manager for tracking and optimization * * @param filter - The filter to register * @param key - Optional unique key to identify this filter's shader * @returns True if registration was successful */ registerFilter(filter, key) { if (!filter) { this.log("Cannot register null or undefined filter"); return false; } try { if (key) { this.filterShaderMap.set(filter, key); this.log(`Filter registered with key: ${key}`); } else { this.log("Filter registered without key (will be auto-generated)"); } return true; } catch (error) { this.log(`Error registering filter: ${error}`); return false; } } /** * Releases a filter from the shader manager * * @param filter - The filter to release * @param key - Optional key that was used to register the filter */ releaseFilter(filter, key) { if (!filter) { this.log("Cannot release null or undefined filter"); return; } try { if (key) { this.filterShaderMap.delete(filter); this.log(`Filter with key ${key} released`); } else { this.releaseShaderByFilter(filter); } } catch (error) { this.log(`Error releasing filter: ${error}`); } } }; /** Singleton instance of the shader manager */ __publicField(_ShaderResourceManager, "instance"); let ShaderResourceManager = _ShaderResourceManager; export { ShaderResourceManager }; //# sourceMappingURL=ShaderResourceManager.js.map