refactor(ui): merge compositing, params into canvasV2 slice

This commit is contained in:
psychedelicious 2024-06-15 23:48:21 +10:00
parent 84fde74331
commit 83a5c87f5e
5 changed files with 308 additions and 26 deletions

View File

@ -15,10 +15,8 @@ import { modelManagerV2PersistConfig, modelManagerV2Slice } from 'features/model
import { nodesPersistConfig, nodesSlice, nodesUndoableConfig } from 'features/nodes/store/nodesSlice';
import { workflowSettingsPersistConfig, workflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice';
import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workflowSlice';
import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice';
import { upscalePersistConfig, upscaleSlice } from 'features/parameters/store/upscaleSlice';
import { queueSlice } from 'features/queue/store/queueSlice';
import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice';
import { stylePresetPersistConfig, stylePresetSlice } from 'features/stylePresets/store/stylePresetSlice';
import { configSlice } from 'features/system/store/configSlice';
import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice';
@ -42,7 +40,7 @@ import { listenerMiddleware } from './middleware/listenerMiddleware';
const allReducers = {
[api.reducerPath]: api.reducer,
[gallerySlice.name]: gallerySlice.reducer,
[generationSlice.name]: generationSlice.reducer,
// [generationSlice.name]: generationSlice.reducer,
[nodesSlice.name]: undoable(nodesSlice.reducer, nodesUndoableConfig),
[systemSlice.name]: systemSlice.reducer,
[configSlice.name]: configSlice.reducer,
@ -52,7 +50,7 @@ const allReducers = {
[changeBoardModalSlice.name]: changeBoardModalSlice.reducer,
[loraSlice.name]: loraSlice.reducer,
[modelManagerV2Slice.name]: modelManagerV2Slice.reducer,
[sdxlSlice.name]: sdxlSlice.reducer,
// [sdxlSlice.name]: sdxlSlice.reducer,
[queueSlice.name]: queueSlice.reducer,
[workflowSlice.name]: workflowSlice.reducer,
[hrfSlice.name]: hrfSlice.reducer,
@ -90,13 +88,13 @@ export type PersistConfig<T = any> = {
const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
[galleryPersistConfig.name]: galleryPersistConfig,
[generationPersistConfig.name]: generationPersistConfig,
// [generationPersistConfig.name]: generationPersistConfig,
[nodesPersistConfig.name]: nodesPersistConfig,
[systemPersistConfig.name]: systemPersistConfig,
[workflowPersistConfig.name]: workflowPersistConfig,
[uiPersistConfig.name]: uiPersistConfig,
[dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig,
[sdxlPersistConfig.name]: sdxlPersistConfig,
// [sdxlPersistConfig.name]: sdxlPersistConfig,
[loraPersistConfig.name]: loraPersistConfig,
[modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig,
[hrfPersistConfig.name]: hrfPersistConfig,

View File

@ -3,15 +3,14 @@ import { createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import { deepClone } from 'common/util/deepClone';
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
import { controlAdaptersReducers } from 'features/controlLayers/store/controlAdaptersReducers';
import { ipAdaptersReducers } from 'features/controlLayers/store/ipAdaptersReducers';
import { layersReducers } from 'features/controlLayers/store/layersReducers';
import { paramsReducers } from 'features/controlLayers/store/paramsReducers';
import { regionsReducers } from 'features/controlLayers/store/regionsReducers';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
import { modelChanged } from 'features/parameters/store/generationSlice';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import type { IRect, Vector2d } from 'konva/lib/types';
import { atom } from 'nanostores';
@ -54,6 +53,46 @@ const initialState: CanvasV2State = {
regions: [],
layers: [],
maskFillOpacity: 0.3,
compositing: {
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
canvasCoherenceMinDenoise: 0,
canvasCoherenceEdgeSize: 16,
infillMethod: 'patchmatch',
infillTileSize: 32,
infillPatchmatchDownscaleSize: 1,
infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
},
params: {
cfgScale: 7.5,
cfgRescaleMultiplier: 0,
img2imgStrength: 0.75,
iterations: 1,
scheduler: 'euler',
seed: 0,
shouldRandomizeSeed: true,
steps: 50,
model: null,
vae: null,
vaePrecision: 'fp32',
seamlessXAxis: false,
seamlessYAxis: false,
clipSkip: 0,
shouldUseCpuNoise: true,
positivePrompt: '',
negativePrompt: '',
positivePrompt2: '',
negativePrompt2: '',
shouldConcatPrompts: true,
refinerModel: null,
refinerSteps: 20,
refinerCFGScale: 7.5,
refinerScheduler: 'euler',
refinerPositiveAestheticScore: 6,
refinerNegativeAestheticScore: 2.5,
refinerStart: 0.8,
},
};
export const canvasV2Slice = createSlice({
@ -64,6 +103,8 @@ export const canvasV2Slice = createSlice({
...ipAdaptersReducers,
...controlAdaptersReducers,
...regionsReducers,
...paramsReducers,
...compositingReducers,
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { width, updateAspectRatio, clamp } = action.payload;
state.document.width = clamp ? Math.max(roundDownToMultiple(width, 8), 64) : width;
@ -119,22 +160,6 @@ export const canvasV2Slice = createSlice({
state.controlAdapters = [];
},
},
extraReducers(builder) {
builder.addCase(modelChanged, (state, action) => {
const newModel = action.payload;
if (!newModel || action.meta.previousModel?.base === newModel.base) {
// Model was cleared or the base didn't change
return;
}
const optimalDimension = getOptimalDimension(newModel);
if (getIsSizeOptimal(state.document.width, state.document.height, optimalDimension)) {
return;
}
const { width, height } = calculateNewSize(state.document.aspectRatio.value, optimalDimension * optimalDimension);
state.document.width = width;
state.document.height = height;
});
},
});
export const {
@ -153,6 +178,7 @@ export const {
allEntitiesDeleted,
// layers
layerAdded,
layerRecalled,
layerDeleted,
layerReset,
layerMovedForwardOne,
@ -230,6 +256,43 @@ export const {
rgEraserLineAdded,
rgLinePointAdded,
rgRectAdded,
// Compositing
setInfillMethod,
setInfillTileSize,
setInfillPatchmatchDownscaleSize,
setInfillColorValue,
setMaskBlur,
setCanvasCoherenceMode,
setCanvasCoherenceEdgeSize,
setCanvasCoherenceMinDenoise,
// Parameters
setIterations,
setSteps,
setCfgScale,
setCfgRescaleMultiplier,
setScheduler,
setSeed,
setImg2imgStrength,
setSeamlessXAxis,
setSeamlessYAxis,
setShouldRandomizeSeed,
vaeSelected,
vaePrecisionChanged,
setClipSkip,
shouldUseCpuNoiseChanged,
positivePromptChanged,
negativePromptChanged,
positivePrompt2Changed,
negativePrompt2Changed,
shouldConcatPromptsChanged,
refinerModelChanged,
setRefinerSteps,
setRefinerCFGScale,
setRefinerScheduler,
setRefinerPositiveAestheticScore,
setRefinerNegativeAestheticScore,
setRefinerStart,
modelChanged,
} = canvasV2Slice.actions;
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;

View File

@ -0,0 +1,30 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import type { CanvasV2State, RgbaColor } from 'features/controlLayers/store/types';
import type { ParameterCanvasCoherenceMode } from 'features/parameters/types/parameterSchemas';
export const compositingReducers = {
setInfillMethod: (state, action: PayloadAction<string>) => {
state.compositing.infillMethod = action.payload;
},
setInfillTileSize: (state, action: PayloadAction<number>) => {
state.compositing.infillTileSize = action.payload;
},
setInfillPatchmatchDownscaleSize: (state, action: PayloadAction<number>) => {
state.compositing.infillPatchmatchDownscaleSize = action.payload;
},
setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => {
state.compositing.infillColorValue = action.payload;
},
setMaskBlur: (state, action: PayloadAction<number>) => {
state.compositing.maskBlur = action.payload;
},
setCanvasCoherenceMode: (state, action: PayloadAction<ParameterCanvasCoherenceMode>) => {
state.compositing.canvasCoherenceMode = action.payload;
},
setCanvasCoherenceEdgeSize: (state, action: PayloadAction<number>) => {
state.compositing.canvasCoherenceEdgeSize = action.payload;
},
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
state.compositing.canvasCoherenceMinDenoise = action.payload;
},
} satisfies SliceCaseReducers<CanvasV2State>;

View File

@ -0,0 +1,132 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundingBoxDimensions';
import type { CanvasV2State } from 'features/controlLayers/store/types';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
import type {
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterModel,
ParameterPrecision,
ParameterScheduler,
ParameterSDXLRefinerModel,
ParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { clamp } from 'lodash-es';
export const paramsReducers = {
setIterations: (state, action: PayloadAction<number>) => {
state.params.iterations = action.payload;
},
setSteps: (state, action: PayloadAction<number>) => {
state.params.steps = action.payload;
},
setCfgScale: (state, action: PayloadAction<ParameterCFGScale>) => {
state.params.cfgScale = action.payload;
},
setCfgRescaleMultiplier: (state, action: PayloadAction<ParameterCFGRescaleMultiplier>) => {
state.params.cfgRescaleMultiplier = action.payload;
},
setScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.params.scheduler = action.payload;
},
setSeed: (state, action: PayloadAction<number>) => {
state.params.seed = action.payload;
state.params.shouldRandomizeSeed = false;
},
setImg2imgStrength: (state, action: PayloadAction<number>) => {
state.params.img2imgStrength = action.payload;
},
setSeamlessXAxis: (state, action: PayloadAction<boolean>) => {
state.params.seamlessXAxis = action.payload;
},
setSeamlessYAxis: (state, action: PayloadAction<boolean>) => {
state.params.seamlessYAxis = action.payload;
},
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
state.params.shouldRandomizeSeed = action.payload;
},
modelChanged: (state, action: PayloadAction<{ model: ParameterModel | null; previousModel?: ParameterModel }>) => {
const { model, previousModel } = action.payload;
state.params.model = model;
// If the model base changes (e.g. SD1.5 -> SDXL), we need to change a few things
if (model === null || previousModel?.base === model.base) {
return;
}
// Update the bbox size to match the new model's optimal size
// TODO(psyche): Should we change the document size too?
const optimalDimension = getOptimalDimension(model);
if (!getIsSizeOptimal(state.document.width, state.document.height, optimalDimension)) {
const bboxDims = calculateNewSize(state.document.aspectRatio.value, optimalDimension * optimalDimension);
state.bbox.width = bboxDims.width;
state.bbox.height = bboxDims.height;
if (state.scaledBbox.scaleMethod === 'auto') {
const scaledBboxDims = getScaledBoundingBoxDimensions(bboxDims, optimalDimension);
state.scaledBbox.width = scaledBboxDims.width;
state.scaledBbox.height = scaledBboxDims.height;
}
}
// Clamp CLIP skip layer count to the bounds of the new model
if (model.base === 'sdxl') {
// We don't support user-defined CLIP skip for SDXL because it doesn't do anything useful
state.params.clipSkip = 0;
} else {
const { maxClip } = CLIP_SKIP_MAP[model.base];
state.params.clipSkip = clamp(state.params.clipSkip, 0, maxClip);
}
},
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
// null is a valid VAE!
state.params.vae = action.payload;
},
vaePrecisionChanged: (state, action: PayloadAction<ParameterPrecision>) => {
state.params.vaePrecision = action.payload;
},
setClipSkip: (state, action: PayloadAction<number>) => {
state.params.clipSkip = action.payload;
},
shouldUseCpuNoiseChanged: (state, action: PayloadAction<boolean>) => {
state.params.shouldUseCpuNoise = action.payload;
},
positivePromptChanged: (state, action: PayloadAction<string>) => {
state.params.positivePrompt = action.payload;
},
negativePromptChanged: (state, action: PayloadAction<string>) => {
state.params.negativePrompt = action.payload;
},
positivePrompt2Changed: (state, action: PayloadAction<string>) => {
state.params.positivePrompt2 = action.payload;
},
negativePrompt2Changed: (state, action: PayloadAction<string>) => {
state.params.negativePrompt2 = action.payload;
},
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
state.params.shouldConcatPrompts = action.payload;
},
refinerModelChanged: (state, action: PayloadAction<ParameterSDXLRefinerModel | null>) => {
state.params.refinerModel = action.payload;
},
setRefinerSteps: (state, action: PayloadAction<number>) => {
state.params.refinerSteps = action.payload;
},
setRefinerCFGScale: (state, action: PayloadAction<number>) => {
state.params.refinerCFGScale = action.payload;
},
setRefinerScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.params.refinerScheduler = action.payload;
},
setRefinerPositiveAestheticScore: (state, action: PayloadAction<number>) => {
state.params.refinerPositiveAestheticScore = action.payload;
},
setRefinerNegativeAestheticScore: (state, action: PayloadAction<number>) => {
state.params.refinerNegativeAestheticScore = action.payload;
},
setRefinerStart: (state, action: PayloadAction<number>) => {
state.params.refinerStart = action.payload;
},
} satisfies SliceCaseReducers<CanvasV2State>;

View File

@ -1,7 +1,26 @@
import { deepClone } from 'common/util/deepClone';
import { zModelIdentifierField } from 'features/nodes/types/common';
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
import type { ParameterHeight, ParameterWidth } from 'features/parameters/types/parameterSchemas';
import type {
ParameterCanvasCoherenceMode,
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterHeight,
ParameterMaskBlurMethod,
ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterPrecision,
ParameterScheduler,
ParameterSDXLRefinerModel,
ParameterSeed,
ParameterSteps,
ParameterStrength,
ParameterVAEModel,
ParameterWidth,
} from 'features/parameters/types/parameterSchemas';
import {
zAutoNegative,
zParameterNegativePrompt,
@ -785,6 +804,46 @@ export type CanvasV2State = {
ipAdapters: IPAdapterData[];
regions: RegionalGuidanceData[];
maskFillOpacity: number;
compositing: {
maskBlur: number;
maskBlurMethod: ParameterMaskBlurMethod;
canvasCoherenceMode: ParameterCanvasCoherenceMode;
canvasCoherenceMinDenoise: ParameterStrength;
canvasCoherenceEdgeSize: number;
infillMethod: string;
infillTileSize: number;
infillPatchmatchDownscaleSize: number;
infillColorValue: RgbaColor;
};
params: {
cfgScale: ParameterCFGScale;
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
img2imgStrength: ParameterStrength;
iterations: number;
scheduler: ParameterScheduler;
seed: ParameterSeed;
shouldRandomizeSeed: boolean;
steps: ParameterSteps;
model: ParameterModel | null;
vae: ParameterVAEModel | null;
vaePrecision: ParameterPrecision;
seamlessXAxis: boolean;
seamlessYAxis: boolean;
clipSkip: number;
shouldUseCpuNoise: boolean;
positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt;
positivePrompt2: ParameterPositiveStylePromptSDXL;
negativePrompt2: ParameterNegativeStylePromptSDXL;
shouldConcatPrompts: boolean;
refinerModel: ParameterSDXLRefinerModel | null;
refinerSteps: number;
refinerCFGScale: number;
refinerScheduler: ParameterScheduler;
refinerPositiveAestheticScore: number;
refinerNegativeAestheticScore: number;
refinerStart: number;
};
};
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number };