File size: 4,709 Bytes
8a37e0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { Transparency } from 'features/controlLayers/konva/util';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import type { GenerationMode } from 'features/controlLayers/store/types';
import { LRUCache } from 'lru-cache';
import type { Logger } from 'roarr';
type GetCacheEntryWithFallbackArg<T extends NonNullable<unknown>> = {
cache: LRUCache<string, T>;
key: string;
getValue: () => Promise<T>;
onHit?: (value: T) => void;
onMiss?: () => void;
};
type CanvasCacheModuleConfig = {
/**
* The maximum size of the image name cache.
*/
imageNameCacheSize: number;
/**
* The maximum size of the image data cache.
*/
imageDataCacheSize: number;
/**
* The maximum size of the transparency calculation cache.
*/
transparencyCalculationCacheSize: number;
/**
* The maximum size of the canvas element cache.
*/
canvasElementCacheSize: number;
/**
* The maximum size of the generation mode cache.
*/
generationModeCacheSize: number;
};
const DEFAULT_CONFIG: CanvasCacheModuleConfig = {
imageNameCacheSize: 1000,
imageDataCacheSize: 32,
transparencyCalculationCacheSize: 1000,
canvasElementCacheSize: 32,
generationModeCacheSize: 100,
};
/**
* A cache module for storing the results of expensive calculations. For example, when we rasterize a layer and upload
* it to the server, we store the resultant image name in this cache for future use.
*/
export class CanvasCacheModule extends CanvasModuleBase {
readonly type = 'cache';
readonly id: string;
readonly path: string[];
readonly log: Logger;
readonly parent: CanvasManager;
readonly manager: CanvasManager;
config: CanvasCacheModuleConfig = DEFAULT_CONFIG;
/**
* A cache for storing image names.
*
* For example, the key might be a hash of a composite of entities with the uploaded image name as the value.
*/
imageNameCache = new LRUCache<string, string>({ max: this.config.imageNameCacheSize });
/**
* A cache for storing canvas elements.
*
* For example, the key might be a hash of a composite of entities with the canvas element as the value.
*/
canvasElementCache = new LRUCache<string, HTMLCanvasElement>({ max: this.config.canvasElementCacheSize });
/**
* A cache for image data objects.
*
* For example, the key might be a hash of a composite of entities with the image data as the value.
*/
imageDataCache = new LRUCache<string, ImageData>({ max: this.config.imageDataCacheSize });
/**
* A cache for transparency calculation results.
*
* For example, the key might be a hash of a composite of entities with the transparency as the value.
*/
transparencyCalculationCache = new LRUCache<string, Transparency>({ max: this.config.imageDataCacheSize });
/**
* A cache for generation mode calculation results.
*
* For example, the key might be a hash of a composite of raster and inpaint mask entities with the generation mode
* as the value.
*/
generationModeCache = new LRUCache<string, GenerationMode>({ max: this.config.generationModeCacheSize });
constructor(manager: CanvasManager) {
super();
this.id = getPrefixedId('cache');
this.manager = manager;
this.parent = manager;
this.path = this.manager.buildPath(this);
this.log = this.manager.buildLogger(this);
this.log.debug('Creating cache module');
}
/**
* A helper function for getting a cache entry with a fallback.
* @param param0.cache The LRUCache to get the entry from.
* @param param0.key The key to use to retrieve the entry.
* @param param0.getValue An async function to generate the value if the entry is not in the cache.
* @param param0.onHit An optional function to call when the entry is in the cache.
* @param param0.onMiss An optional function to call when the entry is not in the cache.
* @returns
*/
static getWithFallback = async <T extends NonNullable<unknown>>({
cache,
getValue,
key,
onHit,
onMiss,
}: GetCacheEntryWithFallbackArg<T>): Promise<T> => {
let value = cache.get(key);
if (value === undefined) {
onMiss?.();
value = await getValue();
cache.set(key, value);
} else {
onHit?.(value);
}
return value;
};
/**
* Clears all caches.
*/
clearAll = () => {
this.canvasElementCache.clear();
this.imageNameCache.clear();
this.generationModeCache.clear();
};
destroy = () => {
this.log.debug('Destroying cache module');
this.clearAll();
};
}
|