feat(ui): do not optimize size when changing between models with same base model

There's a challenge to accomplish this due to our slice structure - the model is stored in `generationSlice`, but `canvasSlice` also needs to have awareness of it. For example, when the model changes, the canvas slice doesn't know what the previous model was, so it doesn't know whether or not to optimize the size.

This means we need to lift the "should we optimize size" information up. To do this, the `modelChanged` action creator accepts the previous model as an optional second arg.

Now the canvas has access to both the previous model and new model selection, and can decide whether or not it should optimize its size setting in the same way that the generation slice does.

Closes  #5452
This commit is contained in:
psychedelicious 2024-01-09 13:47:40 +11:00 committed by Kent Keirsey
parent 63d74b4ba6
commit 4082f25062
4 changed files with 56 additions and 24 deletions

View File

@ -37,8 +37,10 @@ export const addModelSelectedListener = () => {
const newModel = result.data;
const { base_model } = newModel;
const didBaseModelChange =
state.generation.model?.base_model !== base_model;
if (state.generation.model?.base_model !== base_model) {
if (didBaseModelChange) {
// we may need to reset some incompatible submodels
let modelsCleared = 0;
@ -81,7 +83,7 @@ export const addModelSelectedListener = () => {
}
}
dispatch(modelChanged(newModel));
dispatch(modelChanged(newModel, state.generation.model));
},
});
};

View File

@ -74,7 +74,7 @@ export const addModelsLoadedListener = () => {
return;
}
dispatch(modelChanged(result.data));
dispatch(modelChanged(result.data, currentModel));
},
});
startAppListening({
@ -149,7 +149,7 @@ export const addModelsLoadedListener = () => {
if (!firstModel) {
// No custom VAEs loaded at all; use the default
dispatch(modelChanged(null));
dispatch(vaeSelected(null));
return;
}
@ -227,7 +227,7 @@ export const addModelsLoadedListener = () => {
const log = logger('models');
log.info(
{ models: action.payload.entities },
`ControlNet models loaded (${action.payload.ids.length})`
`T2I Adapter models loaded (${action.payload.ids.length})`
);
selectAllT2IAdapters(getState().controlAdapters).forEach((ca) => {

View File

@ -682,6 +682,12 @@ export const canvasSlice = createSlice({
},
extraReducers: (builder) => {
builder.addCase(modelChanged, (state, action) => {
if (
action.meta.previousModel?.base_model === action.payload?.base_model
) {
// The base model hasn't changed, we don't need to optimize the size
return;
}
const optimalDimension = getOptimalDimension(action.payload);
const { width, height } = state.boundingBoxDimensions;
if (getIsSizeOptimal(width, height, optimalDimension)) {

View File

@ -158,27 +158,51 @@ export const generationSlice = createSlice({
const { image_name, width, height } = action.payload;
state.initialImage = { imageName: image_name, width, height };
},
modelChanged: (state, action: PayloadAction<ParameterModel | null>) => {
const newModel = action.payload;
state.model = newModel;
modelChanged: {
reducer: (
state,
action: PayloadAction<
ParameterModel | null,
string,
{ previousModel?: ParameterModel | null }
>
) => {
const newModel = action.payload;
state.model = newModel;
if (newModel === null) {
return;
}
if (newModel === null) {
return;
}
// Clamp ClipSkip Based On Selected Model
const { maxClip } = CLIP_SKIP_MAP[newModel.base_model];
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
const optimalDimension = getOptimalDimension(newModel);
if (getIsSizeOptimal(state.width, state.height, optimalDimension)) {
return;
}
const { width, height } = calculateNewSize(
state.aspectRatio.value,
optimalDimension * optimalDimension
);
state.width = width;
state.height = height;
// Clamp ClipSkip Based On Selected Model
const { maxClip } = CLIP_SKIP_MAP[newModel.base_model];
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
if (action.meta.previousModel?.base_model === newModel.base_model) {
// The base model hasn't changed, we don't need to optimize the size
return;
}
const optimalDimension = getOptimalDimension(newModel);
if (getIsSizeOptimal(state.width, state.height, optimalDimension)) {
return;
}
const { width, height } = calculateNewSize(
state.aspectRatio.value,
optimalDimension * optimalDimension
);
state.width = width;
state.height = height;
},
prepare: (
payload: ParameterModel | null,
previousModel?: ParameterModel | null
) => ({
payload,
meta: {
previousModel,
},
}),
},
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
// null is a valid VAE!