Skip to content

Commit eb0e663

Browse files
authored
Merge branch 'main' into pbr-maps-2
2 parents ef2bce8 + 2924d05 commit eb0e663

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterBase.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,14 @@ export abstract class CanvasEntityAdapterBase<T extends CanvasEntityState, U ext
546546
this.renderer.syncKonvaCache();
547547
};
548548

549+
/**
550+
* Invalidates the raster cache for this entity by delegating to the renderer's
551+
* `invalidateRasterCache` method.
552+
*/
553+
invalidateRasterCache = () => {
554+
this.renderer.invalidateRasterCache();
555+
};
556+
549557
/**
550558
* Synchronizes the entity's locked state with the canvas.
551559
*/

invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
6565
*/
6666
renderers = new SyncableMap<string, AnyObjectRenderer>();
6767

68+
/**
69+
* Tracks the cache keys used when rasterizing this entity so they can be invalidated on demand.
70+
*/
71+
rasterCacheKeys = new Set<string>();
72+
6873
/**
6974
* A object containing singleton Konva nodes.
7075
*/
@@ -476,7 +481,7 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
476481
}): Promise<ImageDTO> => {
477482
const rasterizingAdapter = this.manager.stateApi.$rasterizingAdapter.get();
478483
if (rasterizingAdapter) {
479-
assert(false, `Already rasterizing an entity: ${rasterizingAdapter.id}`);
484+
await this.manager.stateApi.waitForRasterizationToFinish();
480485
}
481486

482487
const { rect, replaceObjects, attrs, bg, ignoreCache } = {
@@ -493,6 +498,7 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
493498
if (cachedImageName && !ignoreCache) {
494499
imageDTO = await getImageDTOSafe(cachedImageName);
495500
if (imageDTO) {
501+
this.rasterCacheKeys.add(hash);
496502
this.log.trace({ rect, cachedImageName, imageDTO }, 'Using cached rasterized image');
497503
return imageDTO;
498504
}
@@ -524,6 +530,7 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
524530
replaceObjects,
525531
});
526532
this.manager.cache.imageNameCache.set(hash, imageDTO.image_name);
533+
this.rasterCacheKeys.add(hash);
527534
return imageDTO;
528535
} catch (error) {
529536
this.log.error({ rasterizeArgs, error: serializeError(error as Error) }, 'Failed to rasterize entity');
@@ -533,6 +540,22 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
533540
}
534541
};
535542

543+
/**
544+
* Invalidates all cached rasterizations for this entity by removing the cached image
545+
* names from the image cache and clearing the tracked raster cache keys. This forces
546+
* future rasterizations to regenerate images instead of using potentially stale
547+
* cached versions.
548+
*/
549+
invalidateRasterCache = () => {
550+
if (this.rasterCacheKeys.size === 0) {
551+
return;
552+
}
553+
for (const key of this.rasterCacheKeys) {
554+
this.manager.cache.imageNameCache.delete(key);
555+
}
556+
this.rasterCacheKeys.clear();
557+
};
558+
536559
cloneObjectGroup = (arg: { attrs?: GroupConfig } = {}): Konva.Group => {
537560
const { attrs } = arg;
538561
const clone = this.konva.objectGroup.clone();

invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,17 @@ export class CanvasManager extends CanvasModuleBase {
199199
];
200200
};
201201

202+
/**
203+
* Invalidates the raster cache for all regional guidance adapters.
204+
* This should be called when bbox or related regional guidance settings change
205+
* to ensure that cached masks are regenerated with the new settings.
206+
*/
207+
invalidateRegionalGuidanceRasterCache = () => {
208+
for (const adapter of this.adapters.regionMasks.values()) {
209+
adapter.invalidateRasterCache();
210+
}
211+
};
212+
202213
createAdapter = (entityIdentifier: CanvasEntityIdentifier): CanvasEntityAdapter => {
203214
if (isRasterLayerEntityIdentifier(entityIdentifier)) {
204215
const adapter = new CanvasEntityAdapterRasterLayer(entityIdentifier, this);

invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,52 @@ export class CanvasStateApiModule extends CanvasModuleBase {
213213
*/
214214
setGenerationBbox = (rect: Rect) => {
215215
this.store.dispatch(bboxChangedFromCanvas(rect));
216+
this.manager.invalidateRegionalGuidanceRasterCache();
217+
};
218+
219+
/**
220+
* Waits for the current rasterization operation to complete.
221+
*
222+
* If no rasterization is in progress, this returns immediately. Use this
223+
* before starting a new rasterization to avoid multiple simultaneous
224+
* rasterization operations acting on the same canvas state.
225+
*
226+
* @returns A promise that resolves once rasterization has finished or
227+
* immediately if no rasterization is in progress.
228+
*/
229+
waitForRasterizationToFinish = async () => {
230+
if (!this.$rasterizingAdapter.get()) {
231+
return;
232+
}
233+
234+
await new Promise<void>((resolve) => {
235+
// Ensure we only resolve once, even if multiple events fire.
236+
let resolved = false;
237+
238+
// Re-check before subscribing to avoid a race where rasterization completes
239+
// between the outer check and listener registration.
240+
if (!this.$rasterizingAdapter.get()) {
241+
resolved = true;
242+
resolve();
243+
return;
244+
}
245+
246+
const unsubscribe = this.$rasterizingAdapter.listen((adapter) => {
247+
if (!adapter && !resolved) {
248+
resolved = true;
249+
unsubscribe();
250+
resolve();
251+
}
252+
});
253+
254+
// Re-check immediately after subscribing to close the race where
255+
// rasterization completes between the check above and `listen()`.
256+
if (!this.$rasterizingAdapter.get() && !resolved) {
257+
resolved = true;
258+
unsubscribe();
259+
resolve();
260+
}
261+
});
216262
};
217263

218264
/**

0 commit comments

Comments
 (0)