mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): error handling for all rasterization calls
This commit is contained in:
committed by
Kent Keirsey
parent
07ef96ee6e
commit
7ddbdd56b5
@@ -1,5 +1,6 @@
|
||||
import { $authToken } from 'app/store/nanostores/authToken';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { withResult } from 'common/util/result';
|
||||
import { SyncableMap } from 'common/util/SyncableMap/SyncableMap';
|
||||
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
@@ -356,14 +357,25 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
/**
|
||||
* Rasterizes the parent entity. If the entity has a rasterization cache for the given rect, the cached image is
|
||||
* returned. Otherwise, the entity is rasterized and the image is uploaded to the server.
|
||||
* Rasterizes the parent entity, returning a promise that resolves to the image DTO.
|
||||
*
|
||||
* If the entity has a rasterization cache for the given rect, the cached image is returned. Otherwise, the entity is
|
||||
* rasterized and the image is uploaded to the server.
|
||||
*
|
||||
* The rasterization cache is reset when the entity's state changes. The buffer object is not considered part of the
|
||||
* entity state for this purpose as it is a temporary object.
|
||||
*
|
||||
* @param rect The rect to rasterize. If omitted, the entity's full rect will be used.
|
||||
* @returns A promise that resolves to the rasterized image DTO.
|
||||
* If rasterization fails for any reason, the promise will reject.
|
||||
*
|
||||
* @param options The rasterization options.
|
||||
* @param options.rect The region of the entity to rasterize.
|
||||
* @param options.replaceObjects Whether to replace the entity's objects with the rasterized image. If you just want
|
||||
* the entity's image, omit or set this to false.
|
||||
* @param options.attrs The Konva node attributes to apply to the rasterized image group. For example, you might want
|
||||
* to disable filters or set the opacity to the rasterized image.
|
||||
* @param options.bg Draws the entity on a canvas with the given background color. If omitted, the entity is drawn on
|
||||
* a transparent canvas.
|
||||
* @returns A promise that resolves to the rasterized image DTO or rejects if rasterization fails.
|
||||
*/
|
||||
rasterize = async (options: {
|
||||
rect: Rect;
|
||||
@@ -423,26 +435,38 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
if (this.parent.transformer.$isPendingRectCalculation.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pixelRect = this.parent.transformer.$pixelRect.get();
|
||||
if (pixelRect.width === 0 || pixelRect.height === 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// TODO(psyche): This is an internal Konva method, so it may break in the future. Can we make this API public?
|
||||
const canvas = this.konva.objectGroup._getCachedSceneCanvas()._canvas as HTMLCanvasElement | undefined | null;
|
||||
if (canvas) {
|
||||
const nodeRect = this.parent.transformer.$nodeRect.get();
|
||||
const rect = {
|
||||
x: pixelRect.x - nodeRect.x,
|
||||
y: pixelRect.y - nodeRect.y,
|
||||
width: pixelRect.width,
|
||||
height: pixelRect.height,
|
||||
};
|
||||
this.$canvasCache.set({ rect, canvas });
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
/**
|
||||
* TODO(psyche): This is an internal Konva method, so it may break in the future. Can we make this API public?
|
||||
*
|
||||
* This method's API is unknown. It has been experimentally determined that it may throw, so we need to handle
|
||||
* errors.
|
||||
*/
|
||||
const getCacheCanvasResult = withResult(
|
||||
() => this.konva.objectGroup._getCachedSceneCanvas()._canvas as HTMLCanvasElement | undefined | null
|
||||
);
|
||||
if (getCacheCanvasResult.isErr()) {
|
||||
// We are using an internal Konva method, so we need to catch any errors that may occur.
|
||||
this.log.warn({ error: serializeError(error) }, 'Failed to update preview canvas');
|
||||
this.log.warn({ error: serializeError(getCacheCanvasResult.error) }, 'Failed to update preview canvas');
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = getCacheCanvasResult.value;
|
||||
|
||||
if (canvas) {
|
||||
const nodeRect = this.parent.transformer.$nodeRect.get();
|
||||
const rect = {
|
||||
x: pixelRect.x - nodeRect.x,
|
||||
y: pixelRect.y - nodeRect.y,
|
||||
width: pixelRect.width,
|
||||
height: pixelRect.height,
|
||||
};
|
||||
this.$canvasCache.set({ rect, canvas });
|
||||
}
|
||||
}, 300);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { withResultAsync } from 'common/util/result';
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
@@ -15,6 +16,7 @@ import type { GroupConfig } from 'konva/lib/Group';
|
||||
import { debounce, get } from 'lodash-es';
|
||||
import { atom } from 'nanostores';
|
||||
import type { Logger } from 'roarr';
|
||||
import { serializeError } from 'serialize-error';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
type CanvasEntityTransformerConfig = {
|
||||
@@ -575,7 +577,12 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
this.log.debug('Applying transform');
|
||||
this.$isProcessing.set(true);
|
||||
const rect = this.getRelativeRect();
|
||||
await this.parent.renderer.rasterize({ rect, replaceObjects: true, attrs: { opacity: 1, filters: [] } });
|
||||
const rasterizeResult = await withResultAsync(() =>
|
||||
this.parent.renderer.rasterize({ rect, replaceObjects: true, attrs: { opacity: 1, filters: [] } })
|
||||
);
|
||||
if (rasterizeResult.isErr()) {
|
||||
this.log.error({ error: serializeError(rasterizeResult.error) }, 'Failed to rasterize entity');
|
||||
}
|
||||
this.requestRectCalculation();
|
||||
this.stopTransform();
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { withResultAsync } from 'common/util/result';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
@@ -6,9 +8,12 @@ import type {
|
||||
T2IAdapterConfig,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { serializeError } from 'serialize-error';
|
||||
import type { BaseModelType, ImageDTO, Invocation } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
type AddControlNetsResult = {
|
||||
addedControlNets: number;
|
||||
};
|
||||
@@ -33,9 +38,17 @@ export const addControlNets = async (
|
||||
for (const layer of validControlLayers) {
|
||||
result.addedControlNets++;
|
||||
|
||||
const adapter = manager.adapters.controlLayers.get(layer.id);
|
||||
assert(adapter, 'Adapter not found');
|
||||
const imageDTO = await adapter.renderer.rasterize({ rect, attrs: { opacity: 1, filters: [] }, bg: 'black' });
|
||||
const getImageDTOResult = await withResultAsync(() => {
|
||||
const adapter = manager.adapters.controlLayers.get(layer.id);
|
||||
assert(adapter, 'Adapter not found');
|
||||
return adapter.renderer.rasterize({ rect, attrs: { opacity: 1, filters: [] }, bg: 'black' });
|
||||
});
|
||||
if (getImageDTOResult.isErr()) {
|
||||
log.warn({ error: serializeError(getImageDTOResult.error) }, 'Error rasterizing control layer');
|
||||
continue;
|
||||
}
|
||||
|
||||
const imageDTO = getImageDTOResult.value;
|
||||
addControlNetToGraph(g, layer, imageDTO, collector);
|
||||
}
|
||||
|
||||
@@ -66,9 +79,17 @@ export const addT2IAdapters = async (
|
||||
for (const layer of validControlLayers) {
|
||||
result.addedT2IAdapters++;
|
||||
|
||||
const adapter = manager.adapters.controlLayers.get(layer.id);
|
||||
assert(adapter, 'Adapter not found');
|
||||
const imageDTO = await adapter.renderer.rasterize({ rect, attrs: { opacity: 1, filters: [], bg: 'black' } });
|
||||
const getImageDTOResult = await withResultAsync(() => {
|
||||
const adapter = manager.adapters.controlLayers.get(layer.id);
|
||||
assert(adapter, 'Adapter not found');
|
||||
return adapter.renderer.rasterize({ rect, attrs: { opacity: 1, filters: [] }, bg: 'black' });
|
||||
});
|
||||
if (getImageDTOResult.isErr()) {
|
||||
log.warn({ error: serializeError(getImageDTOResult.error) }, 'Error rasterizing control layer');
|
||||
continue;
|
||||
}
|
||||
|
||||
const imageDTO = getImageDTOResult.value;
|
||||
addT2IAdapterToGraph(g, layer, imageDTO, collector);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { withResultAsync } from 'common/util/result';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
@@ -8,9 +10,12 @@ import type {
|
||||
RegionalGuidanceReferenceImageState,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { serializeError } from 'serialize-error';
|
||||
import type { BaseModelType, Invocation } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
type AddedRegionResult = {
|
||||
addedPositivePrompt: boolean;
|
||||
addedNegativePrompt: boolean;
|
||||
@@ -64,9 +69,18 @@ export const addRegions = async (
|
||||
addedAutoNegativePositivePrompt: false,
|
||||
addedIPAdapters: 0,
|
||||
};
|
||||
const adapter = manager.adapters.regionMasks.get(region.id);
|
||||
assert(adapter, 'Adapter not found');
|
||||
const imageDTO = await adapter.renderer.rasterize({ rect: bbox });
|
||||
|
||||
const getImageDTOResult = await withResultAsync(() => {
|
||||
const adapter = manager.adapters.regionMasks.get(region.id);
|
||||
assert(adapter, 'Adapter not found');
|
||||
return adapter.renderer.rasterize({ rect: bbox, attrs: { opacity: 1, filters: [] } });
|
||||
});
|
||||
if (getImageDTOResult.isErr()) {
|
||||
log.warn({ error: serializeError(getImageDTOResult.error) }, 'Error rasterizing region mask');
|
||||
continue;
|
||||
}
|
||||
|
||||
const imageDTO = getImageDTOResult.value;
|
||||
|
||||
// The main mask-to-tensor node
|
||||
const maskToTensor = g.addNode({
|
||||
|
||||
@@ -612,7 +612,7 @@ export type UploadOptions = {
|
||||
board_id?: BoardId;
|
||||
metadata?: SerializableObject;
|
||||
};
|
||||
export const uploadImage = async (arg: UploadOptions): Promise<ImageDTO> => {
|
||||
export const uploadImage = (arg: UploadOptions): Promise<ImageDTO> => {
|
||||
const { blob, fileName, image_category, is_intermediate, crop_visible = false, board_id, metadata } = arg;
|
||||
|
||||
const { dispatch } = getStore();
|
||||
@@ -628,5 +628,5 @@ export const uploadImage = async (arg: UploadOptions): Promise<ImageDTO> => {
|
||||
})
|
||||
);
|
||||
req.reset();
|
||||
return await req.unwrap();
|
||||
return req.unwrap();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user