kinetic-slider
Version:
A WebGL-powered kinetic slider component using PIXI.js
307 lines (304 loc) • 10.6 kB
JavaScript
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