Skip to content

Commit 47f3515

Browse files
authored
fix(nodes,ui): fix missed/canvas temp images in gallery (#5111)
## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [x] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Community Node Submission ## Description Resolves two bugs introduced in #5106: 1. Linear UI images sometimes didn't make it to the gallery. This was a race condition. The VAE decode nodes were handled by the socketInvocationComplete listener. At that moment, the image was marked as intermediate. Immediately after this node was handled, a LinearUIOutputInvocation, introduced in #5106, was handled by socketInvocationComplete. This node internally sets changed the image to not intermediate. During the handling of that socketInvocationComplete, RTK Query would sometimes use its cache instead of retrieving the image DTO again. The result is that the UI never got the message that the image was not intermediate, so it wasn't added to the gallery. This is resolved by refactoring the socketInvocationComplete listener. We now skip the gallery processing for linear UI events, except for the LinearUIOutputInvocation. Images now always make it to the gallery, and network requests to get image DTOs are substantially reduced. 2. Canvas temp images always went into the gallery The LinearUIOutputInvocation was always setting its image's is_intermediate to false. This included all canvas images and resulted in all canvas temp images going to gallery. This is resolved by making LinearUIOutputInvocation set is_intermediate based on `self.is_intermediate`. The behaviour now more or less mirroring the behaviour of is_intermediate on other image-outputting nodes, except it doesn't save the image again - only changes it. One extra minor change - LinearUIOutputInvocation only changes is_intermediate if it differs from the image's current setting. Very minor optimisation. ## Related Tickets & Documents <!-- For pull requests that relate or close an issue, please include them below. For example having the text: "closes #1234" would connect the current pull request to issue 1234. And when we merge the pull request, Github will automatically close the issue. --> - Related Issue https://discord.com/channels/1020123559063990373/1149513625321603162/1174721072826945638 ## QA Instructions, Screenshots, Recordings Try to reproduce the issues described int he discord thread: - Images should always go to the gallery from txt2img and img2img - Canvas temp images should not go to the gallery unless auto-save is enabled <!-- Please provide steps on how to test changes, any hardware or software specifications as well as any other pertinent information. -->
2 parents 5ee55cf + 950021a commit 47f3515

File tree

4 files changed

+53
-12
lines changed

4 files changed

+53
-12
lines changed

invokeai/app/invocations/image.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ def invoke(self, context: InvocationContext) -> ImageOutput:
10241024
title="Linear UI Image Output",
10251025
tags=["primitives", "image"],
10261026
category="primitives",
1027-
version="1.0.0",
1027+
version="1.0.1",
10281028
use_cache=False,
10291029
)
10301030
class LinearUIOutputInvocation(BaseInvocation, WithWorkflow, WithMetadata):
@@ -1039,7 +1039,10 @@ def invoke(self, context: InvocationContext) -> ImageOutput:
10391039
if self.board:
10401040
context.services.board_images.add_image_to_board(self.board.board_id, self.image.image_name)
10411041

1042-
context.services.images.update(self.image.image_name, changes=ImageRecordChanges(is_intermediate=False))
1042+
if image_dto.is_intermediate != self.is_intermediate:
1043+
context.services.images.update(
1044+
self.image.image_name, changes=ImageRecordChanges(is_intermediate=self.is_intermediate)
1045+
)
10431046

10441047
return ImageOutput(
10451048
image=ImageField(image_name=self.image.image_name),

invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
imageSelected,
88
} from 'features/gallery/store/gallerySlice';
99
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
10-
import { CANVAS_OUTPUT } from 'features/nodes/util/graphBuilders/constants';
10+
import {
11+
LINEAR_UI_OUTPUT,
12+
nodeIDDenyList,
13+
} from 'features/nodes/util/graphBuilders/constants';
1114
import { boardsApi } from 'services/api/endpoints/boards';
1215
import { imagesApi } from 'services/api/endpoints/images';
1316
import { isImageOutput } from 'services/api/guards';
@@ -19,7 +22,7 @@ import {
1922
import { startAppListening } from '../..';
2023

2124
// These nodes output an image, but do not actually *save* an image, so we don't want to handle the gallery logic on them
22-
const nodeDenylist = ['load_image', 'image'];
25+
const nodeTypeDenylist = ['load_image', 'image'];
2326

2427
export const addInvocationCompleteEventListener = () => {
2528
startAppListening({
@@ -32,22 +35,31 @@ export const addInvocationCompleteEventListener = () => {
3235
`Invocation complete (${action.payload.data.node.type})`
3336
);
3437

35-
const { result, node, queue_batch_id } = data;
38+
const { result, node, queue_batch_id, source_node_id } = data;
3639

3740
// This complete event has an associated image output
38-
if (isImageOutput(result) && !nodeDenylist.includes(node.type)) {
41+
if (
42+
isImageOutput(result) &&
43+
!nodeTypeDenylist.includes(node.type) &&
44+
!nodeIDDenyList.includes(source_node_id)
45+
) {
3946
const { image_name } = result.image;
4047
const { canvas, gallery } = getState();
4148

4249
// This populates the `getImageDTO` cache
43-
const imageDTO = await dispatch(
44-
imagesApi.endpoints.getImageDTO.initiate(image_name)
45-
).unwrap();
50+
const imageDTORequest = dispatch(
51+
imagesApi.endpoints.getImageDTO.initiate(image_name, {
52+
forceRefetch: true,
53+
})
54+
);
55+
56+
const imageDTO = await imageDTORequest.unwrap();
57+
imageDTORequest.unsubscribe();
4658

4759
// Add canvas images to the staging area
4860
if (
4961
canvas.batchIds.includes(queue_batch_id) &&
50-
[CANVAS_OUTPUT].includes(data.source_node_id)
62+
[LINEAR_UI_OUTPUT].includes(data.source_node_id)
5163
) {
5264
dispatch(addImageToStagingArea(imageDTO));
5365
}

invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildAdHocUpscaleGraph.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
Graph,
77
LinearUIOutputInvocation,
88
} from 'services/api/types';
9-
import { REALESRGAN as ESRGAN, LINEAR_UI_OUTPUT } from './constants';
9+
import { ESRGAN, LINEAR_UI_OUTPUT } from './constants';
1010
import { addCoreMetadataNode, upsertMetadata } from './metadata';
1111

1212
type Arg = {

invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const BATCH_PROMPT = 'batch_prompt';
6767
export const BATCH_STYLE_PROMPT = 'batch_style_prompt';
6868
export const METADATA_COLLECT = 'metadata_collect';
6969
export const MERGE_METADATA = 'merge_metadata';
70-
export const REALESRGAN = 'esrgan';
70+
export const ESRGAN = 'esrgan';
7171
export const DIVIDE = 'divide';
7272
export const SCALE = 'scale_image';
7373
export const SDXL_MODEL_LOADER = 'sdxl_model_loader';
@@ -82,6 +82,32 @@ export const SDXL_REFINER_INPAINT_CREATE_MASK = 'refiner_inpaint_create_mask';
8282
export const SEAMLESS = 'seamless';
8383
export const SDXL_REFINER_SEAMLESS = 'refiner_seamless';
8484

85+
// these image-outputting nodes are from the linear UI and we should not handle the gallery logic on them
86+
// instead, we wait for LINEAR_UI_OUTPUT node, and handle it like any other image-outputting node
87+
export const nodeIDDenyList = [
88+
CANVAS_OUTPUT,
89+
LATENTS_TO_IMAGE,
90+
LATENTS_TO_IMAGE_HRF_HR,
91+
NSFW_CHECKER,
92+
WATERMARKER,
93+
ESRGAN,
94+
ESRGAN_HRF,
95+
RESIZE_HRF,
96+
LATENTS_TO_IMAGE_HRF_LR,
97+
IMG2IMG_RESIZE,
98+
INPAINT_IMAGE,
99+
SCALED_INPAINT_IMAGE,
100+
INPAINT_IMAGE_RESIZE_UP,
101+
INPAINT_IMAGE_RESIZE_DOWN,
102+
INPAINT_INFILL,
103+
INPAINT_INFILL_RESIZE_DOWN,
104+
INPAINT_FINAL_IMAGE,
105+
INPAINT_CREATE_MASK,
106+
INPAINT_MASK,
107+
PASTE_IMAGE,
108+
SCALE,
109+
];
110+
85111
// friendly graph ids
86112
export const TEXT_TO_IMAGE_GRAPH = 'text_to_image_graph';
87113
export const IMAGE_TO_IMAGE_GRAPH = 'image_to_image_graph';

0 commit comments

Comments
 (0)