mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-13 20:15:04 -05:00
perf(ui): debounce invoke readiness calculations
This commit is contained in:
@@ -1,22 +1,15 @@
|
||||
import type { TooltipProps } from '@invoke-ai/ui-library';
|
||||
import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $true } from 'app/store/nanostores/util';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectSendToCanvas } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { selectIterations } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectDynamicPromptsIsLoading } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { selectAutoAddBoardId } from 'features/gallery/store/gallerySelectors';
|
||||
import { $templates } from 'features/nodes/store/nodesSlice';
|
||||
import type { Reason } from 'features/queue/store/readiness';
|
||||
import {
|
||||
buildSelectIsReadyToEnqueueCanvasTab,
|
||||
buildSelectIsReadyToEnqueueUpscaleTab,
|
||||
buildSelectIsReadyToEnqueueWorkflowsTab,
|
||||
buildSelectReasonsWhyCannotEnqueueCanvasTab,
|
||||
buildSelectReasonsWhyCannotEnqueueUpscaleTab,
|
||||
buildSelectReasonsWhyCannotEnqueueWorkflowsTab,
|
||||
$isReadyToEnqueue,
|
||||
$reasonsWhyCannotEnqueue,
|
||||
selectPromptsCount,
|
||||
selectWorkflowsBatchSize,
|
||||
} from 'features/queue/store/readiness';
|
||||
@@ -26,7 +19,6 @@ import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { enqueueMutationFixedCacheKeyOptions, useEnqueueBatchMutation } from 'services/api/endpoints/queue';
|
||||
import { useBoardName } from 'services/api/hooks/useBoardName';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
type Props = TooltipProps & {
|
||||
prepend?: boolean;
|
||||
@@ -60,56 +52,8 @@ const TooltipContent = memo(({ prepend = false }: { prepend?: boolean }) => {
|
||||
TooltipContent.displayName = 'TooltipContent';
|
||||
|
||||
const CanvasTabTooltipContent = memo(({ prepend = false }: { prepend?: boolean }) => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const canvasManager = useCanvasManagerSafe();
|
||||
const canvasIsFiltering = useStore(canvasManager?.stateApi.$isFiltering ?? $true);
|
||||
const canvasIsTransforming = useStore(canvasManager?.stateApi.$isTransforming ?? $true);
|
||||
const canvasIsRasterizing = useStore(canvasManager?.stateApi.$isRasterizing ?? $true);
|
||||
const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $true);
|
||||
const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $true);
|
||||
|
||||
const selectIsReady = useMemo(
|
||||
() =>
|
||||
buildSelectIsReadyToEnqueueCanvasTab({
|
||||
isConnected,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsCompositing,
|
||||
}),
|
||||
[
|
||||
isConnected,
|
||||
canvasIsCompositing,
|
||||
canvasIsFiltering,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsTransforming,
|
||||
]
|
||||
);
|
||||
|
||||
const selectReasons = useMemo(
|
||||
() =>
|
||||
buildSelectReasonsWhyCannotEnqueueCanvasTab({
|
||||
isConnected,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsCompositing,
|
||||
}),
|
||||
[
|
||||
isConnected,
|
||||
canvasIsCompositing,
|
||||
canvasIsFiltering,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsTransforming,
|
||||
]
|
||||
);
|
||||
|
||||
const isReady = useAppSelector(selectIsReady);
|
||||
const reasons = useAppSelector(selectReasons);
|
||||
const isReady = useStore($isReadyToEnqueue);
|
||||
const reasons = useStore($reasonsWhyCannotEnqueue);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={1}>
|
||||
@@ -129,13 +73,8 @@ const CanvasTabTooltipContent = memo(({ prepend = false }: { prepend?: boolean }
|
||||
CanvasTabTooltipContent.displayName = 'CanvasTabTooltipContent';
|
||||
|
||||
const UpscaleTabTooltipContent = memo(({ prepend = false }: { prepend?: boolean }) => {
|
||||
const isConnected = useStore($isConnected);
|
||||
|
||||
const selectIsReady = useMemo(() => buildSelectIsReadyToEnqueueUpscaleTab({ isConnected }), [isConnected]);
|
||||
const selectReasons = useMemo(() => buildSelectReasonsWhyCannotEnqueueUpscaleTab({ isConnected }), [isConnected]);
|
||||
|
||||
const isReady = useAppSelector(selectIsReady);
|
||||
const reasons = useAppSelector(selectReasons);
|
||||
const isReady = useStore($isReadyToEnqueue);
|
||||
const reasons = useStore($reasonsWhyCannotEnqueue);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={1}>
|
||||
@@ -153,20 +92,8 @@ const UpscaleTabTooltipContent = memo(({ prepend = false }: { prepend?: boolean
|
||||
UpscaleTabTooltipContent.displayName = 'UpscaleTabTooltipContent';
|
||||
|
||||
const WorkflowsTabTooltipContent = memo(({ prepend = false }: { prepend?: boolean }) => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const templates = useStore($templates);
|
||||
|
||||
const selectIsReady = useMemo(
|
||||
() => buildSelectIsReadyToEnqueueWorkflowsTab({ isConnected, templates }),
|
||||
[isConnected, templates]
|
||||
);
|
||||
const selectReasons = useMemo(
|
||||
() => buildSelectReasonsWhyCannotEnqueueWorkflowsTab({ isConnected, templates }),
|
||||
[isConnected, templates]
|
||||
);
|
||||
|
||||
const isReady = useAppSelector(selectIsReady);
|
||||
const reasons = useAppSelector(selectReasons);
|
||||
const isReady = useStore($isReadyToEnqueue);
|
||||
const reasons = useStore($reasonsWhyCannotEnqueue);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={1}>
|
||||
|
||||
@@ -1,61 +1,15 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { enqueueRequested } from 'app/store/actions';
|
||||
import { $true } from 'app/store/nanostores/util';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { $templates } from 'features/nodes/store/nodesSlice';
|
||||
import {
|
||||
buildSelectIsReadyToEnqueueCanvasTab,
|
||||
buildSelectIsReadyToEnqueueUpscaleTab,
|
||||
buildSelectIsReadyToEnqueueWorkflowsTab,
|
||||
} from 'features/queue/store/readiness';
|
||||
import { $isReadyToEnqueue } from 'features/queue/store/readiness';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { enqueueMutationFixedCacheKeyOptions, useEnqueueBatchMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useInvoke = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const tabName = useAppSelector(selectActiveTab);
|
||||
const isConnected = useStore($isConnected);
|
||||
const canvasManager = useCanvasManagerSafe();
|
||||
const canvasIsFiltering = useStore(canvasManager?.stateApi.$isFiltering ?? $true);
|
||||
const canvasIsTransforming = useStore(canvasManager?.stateApi.$isTransforming ?? $true);
|
||||
const canvasIsRasterizing = useStore(canvasManager?.stateApi.$isRasterizing ?? $true);
|
||||
const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $true);
|
||||
const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $true);
|
||||
const templates = useStore($templates);
|
||||
|
||||
const selectIsReady = useMemo(() => {
|
||||
if (tabName === 'canvas') {
|
||||
return buildSelectIsReadyToEnqueueCanvasTab({
|
||||
isConnected,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsCompositing,
|
||||
});
|
||||
}
|
||||
if (tabName === 'upscaling') {
|
||||
return buildSelectIsReadyToEnqueueUpscaleTab({ isConnected });
|
||||
}
|
||||
if (tabName === 'workflows') {
|
||||
return buildSelectIsReadyToEnqueueWorkflowsTab({ isConnected, templates });
|
||||
}
|
||||
return () => false;
|
||||
}, [
|
||||
tabName,
|
||||
isConnected,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsCompositing,
|
||||
templates,
|
||||
]);
|
||||
|
||||
const isReady = useAppSelector(selectIsReady);
|
||||
const isReady = useStore($isReadyToEnqueue);
|
||||
|
||||
const [_, { isLoading }] = useEnqueueBatchMutation(enqueueMutationFixedCacheKeyOptions);
|
||||
const queueBack = useCallback(() => {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { getConnectedEdges } from '@xyflow/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { $true } from 'app/store/nanostores/util';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { AppConfig } from 'app/types/invokeai';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import type { ParamsState } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
@@ -16,6 +21,7 @@ import {
|
||||
import type { DynamicPromptsState } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import { $templates } from 'features/nodes/store/nodesSlice';
|
||||
import { selectNodesSlice } from 'features/nodes/store/selectors';
|
||||
import type { NodesState, Templates } from 'features/nodes/store/types';
|
||||
import type { WorkflowSettingsState } from 'features/nodes/store/workflowSettingsSlice';
|
||||
@@ -46,8 +52,13 @@ import { isBatchNode, isExecutableNode, isInvocationNode } from 'features/nodes/
|
||||
import type { UpscaleState } from 'features/parameters/store/upscaleSlice';
|
||||
import { selectUpscaleSlice } from 'features/parameters/store/upscaleSlice';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import i18n from 'i18next';
|
||||
import { forEach, groupBy, upperFirst } from 'lodash-es';
|
||||
import { debounce, forEach, groupBy, upperFirst } from 'lodash-es';
|
||||
import { atom, computed } from 'nanostores';
|
||||
import { useEffect } from 'react';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
/**
|
||||
@@ -68,6 +79,122 @@ const LAYER_TYPE_TO_TKEY = {
|
||||
|
||||
export type Reason = { prefix?: string; content: string };
|
||||
|
||||
export const $reasonsWhyCannotEnqueue = atom<Reason[]>([]);
|
||||
export const $isReadyToEnqueue = computed($reasonsWhyCannotEnqueue, (reasons) => reasons.length === 0);
|
||||
|
||||
const debouncedUpdateReasons = debounce(
|
||||
(
|
||||
tab: TabName,
|
||||
isConnected: boolean,
|
||||
canvas: CanvasState,
|
||||
params: ParamsState,
|
||||
dynamicPrompts: DynamicPromptsState,
|
||||
canvasIsFiltering: boolean,
|
||||
canvasIsTransforming: boolean,
|
||||
canvasIsRasterizing: boolean,
|
||||
canvasIsCompositing: boolean,
|
||||
canvasIsSelectingObject: boolean,
|
||||
nodes: NodesState,
|
||||
workflowSettings: WorkflowSettingsState,
|
||||
templates: Templates,
|
||||
upscale: UpscaleState,
|
||||
config: AppConfig
|
||||
) => {
|
||||
if (tab === 'canvas') {
|
||||
$reasonsWhyCannotEnqueue.set(
|
||||
getReasonsWhyCannotEnqueueCanvasTab({
|
||||
isConnected,
|
||||
canvas,
|
||||
params,
|
||||
dynamicPrompts,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsCompositing,
|
||||
canvasIsSelectingObject,
|
||||
})
|
||||
);
|
||||
} else if (tab === 'workflows') {
|
||||
$reasonsWhyCannotEnqueue.set(
|
||||
getReasonsWhyCannotEnqueueWorkflowsTab({
|
||||
isConnected,
|
||||
nodes,
|
||||
workflowSettings,
|
||||
templates,
|
||||
})
|
||||
);
|
||||
} else if (tab === 'upscaling') {
|
||||
$reasonsWhyCannotEnqueue.set(
|
||||
getReasonsWhyCannotEnqueueUpscaleTab({
|
||||
isConnected,
|
||||
upscale,
|
||||
config,
|
||||
params,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
$reasonsWhyCannotEnqueue.set([]);
|
||||
}
|
||||
},
|
||||
300
|
||||
);
|
||||
|
||||
export const useReadinessWatcher = () => {
|
||||
useAssertSingleton('useReadinessWatcher');
|
||||
const canvasManager = useCanvasManagerSafe();
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const canvas = useAppSelector(selectCanvasSlice);
|
||||
const params = useAppSelector(selectParamsSlice);
|
||||
const dynamicPrompts = useAppSelector(selectDynamicPromptsSlice);
|
||||
const nodes = useAppSelector(selectNodesSlice);
|
||||
const workflowSettings = useAppSelector(selectWorkflowSettingsSlice);
|
||||
const upscale = useAppSelector(selectUpscaleSlice);
|
||||
const config = useAppSelector(selectConfigSlice);
|
||||
const templates = useStore($templates);
|
||||
const isConnected = useStore($isConnected);
|
||||
const canvasIsFiltering = useStore(canvasManager?.stateApi.$isFiltering ?? $true);
|
||||
const canvasIsTransforming = useStore(canvasManager?.stateApi.$isTransforming ?? $true);
|
||||
const canvasIsRasterizing = useStore(canvasManager?.stateApi.$isRasterizing ?? $true);
|
||||
const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $true);
|
||||
const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $true);
|
||||
|
||||
useEffect(() => {
|
||||
debouncedUpdateReasons(
|
||||
tab,
|
||||
isConnected,
|
||||
canvas,
|
||||
params,
|
||||
dynamicPrompts,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsCompositing,
|
||||
canvasIsSelectingObject,
|
||||
nodes,
|
||||
workflowSettings,
|
||||
templates,
|
||||
upscale,
|
||||
config
|
||||
);
|
||||
}, [
|
||||
canvas,
|
||||
canvasIsCompositing,
|
||||
canvasIsFiltering,
|
||||
canvasIsRasterizing,
|
||||
canvasIsSelectingObject,
|
||||
canvasIsTransforming,
|
||||
config,
|
||||
dynamicPrompts,
|
||||
isConnected,
|
||||
nodes,
|
||||
params,
|
||||
tab,
|
||||
templates,
|
||||
upscale,
|
||||
workflowSettings,
|
||||
]);
|
||||
};
|
||||
|
||||
const disconnectedReason = (t: typeof i18n.t) => ({ content: t('parameters.invoke.systemDisconnected') });
|
||||
|
||||
export const resolveBatchValue = (batchNode: InvocationNode, nodes: InvocationNode[], edges: AnyEdge[]) => {
|
||||
@@ -485,126 +612,6 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
|
||||
return reasons;
|
||||
};
|
||||
|
||||
export const buildSelectReasonsWhyCannotEnqueueCanvasTab = (arg: {
|
||||
isConnected: boolean;
|
||||
canvasIsFiltering: boolean;
|
||||
canvasIsTransforming: boolean;
|
||||
canvasIsRasterizing: boolean;
|
||||
canvasIsCompositing: boolean;
|
||||
canvasIsSelectingObject: boolean;
|
||||
}) => {
|
||||
const {
|
||||
isConnected,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsCompositing,
|
||||
canvasIsSelectingObject,
|
||||
} = arg;
|
||||
|
||||
return createSelector(
|
||||
selectCanvasSlice,
|
||||
selectParamsSlice,
|
||||
selectDynamicPromptsSlice,
|
||||
(canvas, params, dynamicPrompts) =>
|
||||
getReasonsWhyCannotEnqueueCanvasTab({
|
||||
isConnected,
|
||||
canvas,
|
||||
params,
|
||||
dynamicPrompts,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsCompositing,
|
||||
canvasIsSelectingObject,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const buildSelectIsReadyToEnqueueCanvasTab = (arg: {
|
||||
isConnected: boolean;
|
||||
canvasIsFiltering: boolean;
|
||||
canvasIsTransforming: boolean;
|
||||
canvasIsRasterizing: boolean;
|
||||
canvasIsCompositing: boolean;
|
||||
canvasIsSelectingObject: boolean;
|
||||
}) => {
|
||||
const {
|
||||
isConnected,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsCompositing,
|
||||
canvasIsSelectingObject,
|
||||
} = arg;
|
||||
|
||||
return createSelector(
|
||||
selectCanvasSlice,
|
||||
selectParamsSlice,
|
||||
selectDynamicPromptsSlice,
|
||||
(canvas, params, dynamicPrompts) =>
|
||||
getReasonsWhyCannotEnqueueCanvasTab({
|
||||
isConnected,
|
||||
canvas,
|
||||
params,
|
||||
dynamicPrompts,
|
||||
canvasIsFiltering,
|
||||
canvasIsTransforming,
|
||||
canvasIsRasterizing,
|
||||
canvasIsCompositing,
|
||||
canvasIsSelectingObject,
|
||||
}).length === 0
|
||||
);
|
||||
};
|
||||
|
||||
export const buildSelectReasonsWhyCannotEnqueueUpscaleTab = (arg: { isConnected: boolean }) => {
|
||||
const { isConnected } = arg;
|
||||
return createSelector(selectUpscaleSlice, selectConfigSlice, selectParamsSlice, (upscale, config, params) =>
|
||||
getReasonsWhyCannotEnqueueUpscaleTab({ isConnected, upscale, config, params })
|
||||
);
|
||||
};
|
||||
|
||||
export const buildSelectIsReadyToEnqueueUpscaleTab = (arg: { isConnected: boolean }) => {
|
||||
const { isConnected } = arg;
|
||||
|
||||
return createSelector(
|
||||
selectUpscaleSlice,
|
||||
selectConfigSlice,
|
||||
selectParamsSlice,
|
||||
(upscale, config, params) =>
|
||||
getReasonsWhyCannotEnqueueUpscaleTab({ isConnected, upscale, config, params }).length === 0
|
||||
);
|
||||
};
|
||||
|
||||
export const buildSelectReasonsWhyCannotEnqueueWorkflowsTab = (arg: { isConnected: boolean; templates: Templates }) => {
|
||||
const { isConnected, templates } = arg;
|
||||
|
||||
return createSelector(selectNodesSlice, selectWorkflowSettingsSlice, (nodes, workflowSettings) =>
|
||||
getReasonsWhyCannotEnqueueWorkflowsTab({
|
||||
isConnected,
|
||||
nodes,
|
||||
workflowSettings,
|
||||
templates,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const buildSelectIsReadyToEnqueueWorkflowsTab = (arg: { isConnected: boolean; templates: Templates }) => {
|
||||
const { isConnected, templates } = arg;
|
||||
|
||||
return createSelector(
|
||||
selectNodesSlice,
|
||||
selectWorkflowSettingsSlice,
|
||||
(nodes, workflowSettings) =>
|
||||
getReasonsWhyCannotEnqueueWorkflowsTab({
|
||||
isConnected,
|
||||
nodes,
|
||||
workflowSettings,
|
||||
templates,
|
||||
}).length === 0
|
||||
);
|
||||
};
|
||||
|
||||
export const selectPromptsCount = createSelector(
|
||||
selectParamsSlice,
|
||||
selectDynamicPromptsSlice,
|
||||
|
||||
Reference in New Issue
Block a user