diff --git a/CHANGELOG.md b/CHANGELOG.md
index 622d64498..569e5c194 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
- add timers to measure onload/offload times during generate
- experimental offloading using `torch.streams`
enable in settings -> model offloading
+ - new feature to specify which models types not to offload
+ in *settings -> model offloading -> model types not to offload*
- **Extensions**
- **agent-scheduler** was a high-value built-in extension, but it has not been maintained for 1.5 years
it also does not work with control and video tabs which are the core of sdnext nowadays
diff --git a/TODO.md b/TODO.md
index 26f5fa202..e796a3740 100644
--- a/TODO.md
+++ b/TODO.md
@@ -39,6 +39,7 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma
- [HunyuanImage](https://huggingface.co/tencent/HunyuanImage-2.1)
- [Phantom HuMo](https://github.com/Phantom-video/Phantom)
- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO)
+- [Wan2.2-Animate-14B](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B)
- [Magi](https://github.com/SandAI-org/MAGI-1)(https://github.com/huggingface/diffusers/pull/11713)
- [SEVA](https://github.com/huggingface/diffusers/pull/11440)
- [Ming](https://github.com/inclusionAI/Ming)
diff --git a/modules/sd_offload.py b/modules/sd_offload.py
index be0379f66..cf7a92a36 100644
--- a/modules/sd_offload.py
+++ b/modules/sd_offload.py
@@ -207,6 +207,8 @@ class OffloadHook(accelerate.hooks.ModelHook):
return False
if hasattr(module, 'nets') and any(hasattr(n, "offload_never") for n in module.nets):
return False
+ if shared.sd_model_type.lower() in [m.lower().strip() for m in re.split(r'[ ,]+', shared.opts.models_not_to_offload)]:
+ return False
return True
def pre_forward(self, module, *args, **kwargs):
diff --git a/modules/shared.py b/modules/shared.py
index 25db15490..9577535c0 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -174,6 +174,7 @@ options_templates.update(options_section(('offload', "Model Offloading"), {
"diffusers_offload_min_gpu_memory": OptionInfo(startup_offload_min_gpu, "Offload low watermark", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01 }),
"diffusers_offload_max_gpu_memory": OptionInfo(startup_offload_max_gpu, "Offload GPU high watermark", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.01 }),
"diffusers_offload_max_cpu_memory": OptionInfo(0.90, "Offload CPU high watermark", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01, "visible": False }),
+ "models_not_to_offload": OptionInfo("", "Model types not to offload"),
"diffusers_offload_always": OptionInfo(startup_offload_always, "Modules to always offload"),
"diffusers_offload_never": OptionInfo(startup_offload_never, "Modules to never offload"),
"offload_group_sep": OptionInfo("
Group Offload
", "", gr.HTML),
@@ -184,7 +185,7 @@ options_templates.update(options_section(('offload', "Model Offloading"), {
}))
options_templates.update(options_section(("quantization", "Model Quantization"), {
- "models_not_to_quant": OptionInfo("", "List of model types not to quantize"),
+ "models_not_to_quant": OptionInfo("", "Model types not to quantize"),
"sdnq_quantize_sep": OptionInfo("SDNQ: SD.Next Quantization
", "", gr.HTML),
"sdnq_quantize_weights": OptionInfo([], "Quantization enabled", gr.CheckboxGroup, {"choices": ["Model", "TE", "LLM", "Control", "VAE"]}),
diff --git a/modules/ui.py b/modules/ui.py
index d87f02c70..f9daddb35 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -28,6 +28,7 @@ plaintext_to_html = ui_common.plaintext_to_html # compatibility item
infotext_to_html = ui_common.infotext_to_html # compatibility item
create_sampler_and_steps_selection = ui_sections.create_sampler_and_steps_selection # compatibility item
ui_system_tabs = None # required for system-info
+interfaces = []
if not shared.cmd_opts.share and not shared.cmd_opts.listen:
@@ -132,7 +133,8 @@ def create_ui(startup_timer = None):
ui_extensions.create_ui()
timer.startup.record("ui-extensions")
- interfaces = []
+ global interfaces # pylint: disable=global-statement
+ interfaces.clear()
interfaces += [(txt2img_interface, "Text", "txt2img")]
interfaces += [(img2img_interface, "Image", "img2img")]
if control_interface is not None:
diff --git a/modules/ui_caption.py b/modules/ui_caption.py
index 85d3ca0bc..290ec74f8 100644
--- a/modules/ui_caption.py
+++ b/modules/ui_caption.py
@@ -28,6 +28,7 @@ def update_clip_params(*args):
def create_ui():
+ shared.log.debug('UI initialize: tab=caption')
with gr.Row(equal_height=False, variant='compact', elem_classes="caption", elem_id="caption_tab"):
with gr.Column(variant='compact', elem_id='interrogate_input'):
with gr.Row():
diff --git a/modules/ui_control_helpers.py b/modules/ui_control_helpers.py
index a04512863..c5757c56f 100644
--- a/modules/ui_control_helpers.py
+++ b/modules/ui_control_helpers.py
@@ -24,7 +24,7 @@ def initialize():
from modules.control.units import xs # vislearn ControlNet-XS
from modules.control.units import lite # vislearn ControlNet-XS
from modules.control.units import t2iadapter # TencentARC T2I-Adapter
- shared.log.debug(f'UI initialize: control models="{shared.opts.control_dir}"')
+ shared.log.debug(f'UI initialize: tab=control models="{shared.opts.control_dir}"')
controlnet.cache_dir = os.path.join(shared.opts.control_dir, 'controlnet')
xs.cache_dir = os.path.join(shared.opts.control_dir, 'xs')
lite.cache_dir = os.path.join(shared.opts.control_dir, 'lite')
diff --git a/modules/ui_docs.py b/modules/ui_docs.py
index d8b4e3dce..8b1381ce8 100644
--- a/modules/ui_docs.py
+++ b/modules/ui_docs.py
@@ -275,6 +275,7 @@ def create_ui_docs():
def create_ui():
+ log.debug('UI initialize: tab=info')
with gr.Tabs(elem_id="tabs_info"):
with gr.TabItem("Docs", id="docs", elem_id="system_tab_docs"):
create_ui_docs()
diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py
index 28d792393..43158211a 100644
--- a/modules/ui_extensions.py
+++ b/modules/ui_extensions.py
@@ -430,6 +430,7 @@ def create_html(search_text, sort_column):
def create_ui():
+ shared.log.debug('UI initialize: tab=extensions')
extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "user", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all", visible=False)
extensions_disabled_list = gr.Textbox(elem_id="extensions_disabled_list", visible=False, container=False)
extensions_update_list = gr.Textbox(elem_id="extensions_update_list", visible=False, container=False)
diff --git a/modules/ui_gallery.py b/modules/ui_gallery.py
index e6cb3f297..283b720a8 100644
--- a/modules/ui_gallery.py
+++ b/modules/ui_gallery.py
@@ -54,6 +54,7 @@ def read_media(fn):
def create_ui():
+ shared.log.debug('UI initialize: tab=gallery')
with gr.Blocks() as tab:
with gr.Row(elem_id='tab-gallery-sort-buttons'):
sort_buttons = []
diff --git a/modules/ui_img2img.py b/modules/ui_img2img.py
index c1597321d..d08999c8c 100644
--- a/modules/ui_img2img.py
+++ b/modules/ui_img2img.py
@@ -33,7 +33,7 @@ def process_interrogate(mode, ii_input_files, ii_input_dir, ii_output_dir, *ii_s
def create_ui():
- shared.log.debug('UI initialize: img2img')
+ shared.log.debug('UI initialize: tab=img2img')
import modules.img2img # pylint: disable=redefined-outer-name
modules.scripts_manager.scripts_current = modules.scripts_manager.scripts_img2img
modules.scripts_manager.scripts_img2img.initialize_scripts(is_img2img=True, is_control=False)
diff --git a/modules/ui_loadsave.py b/modules/ui_loadsave.py
index a8e3dce9b..3b6484e06 100644
--- a/modules/ui_loadsave.py
+++ b/modules/ui_loadsave.py
@@ -35,7 +35,6 @@ class UiLoadsave:
saved_value = self.ui_settings.get(key, None)
self.ui_defaults[key] = getattr(obj, field)
if saved_value is None:
- # self.ui_settings[key] = getattr(obj, field)
pass
elif condition and not condition(saved_value):
pass
@@ -126,15 +125,37 @@ class UiLoadsave:
writefile(current_ui_settings, self.filename)
def dump_defaults(self):
- """saves default values to a file unless the file is present and there was an error loading default values at start"""
if os.path.exists(self.filename):
return
self.write_to_file(self.ui_settings)
+ def iter_all(self, values):
+ updates = []
+ for i, name in enumerate(self.component_mapping):
+ component = self.component_mapping[name]
+ choices = getattr(component, 'choices', None)
+ if type(choices) is list and len(choices) > 0: # fix gradio radio button choices being tuples
+ if type(choices[0]) is tuple:
+ choices = [c[0] for c in choices]
+ new_value = values[i]
+ if isinstance(new_value, int) and choices:
+ if new_value >= len(choices):
+ updates.append(None)
+ new_value = choices[new_value]
+ old_value = self.ui_settings.get(name, None)
+ default_value = self.ui_defaults.get(name, '')
+ if old_value == new_value:
+ updates.append(None)
+ elif old_value is None and (new_value == '' or new_value == []):
+ updates.append(None)
+ elif (new_value == default_value) and (old_value is None):
+ updates.append(None)
+ else:
+ updates.append((name, old_value, new_value, default_value))
+ return updates
+
def iter_changes(self, values):
for i, name in enumerate(self.component_mapping):
- # if '__init__' in name:
- # continue
component = self.component_mapping[name]
choices = getattr(component, 'choices', None)
if type(choices) is list and len(choices) > 0: # fix gradio radio button choices being tuples
@@ -201,16 +222,23 @@ class UiLoadsave:
def ui_apply(self, *values):
num_changed = 0
+ num_unchanged = 0
current_ui_settings = self.read_from_file()
- for name, old_value, new_value, default_value in self.iter_changes(values):
- component = self.component_mapping[name]
- errors.log.debug(f'Settings: name={name} component={component} old={old_value} default={default_value} new={new_value}')
- num_changed += 1
- current_ui_settings[name] = new_value
+ for x in self.iter_all(values):
+ if x is None:
+ num_unchanged += 1
+ else:
+ name, old_value, new_value, default_value = x
+ component = self.component_mapping[name]
+ errors.log.debug(f'Settings: name={name} component={component} old={old_value} default={default_value} new={new_value}')
+ num_changed += 1
+ current_ui_settings[name] = new_value
+ # what = name.split('/')[-1]
+ # setattr(component, what, new_value)
if num_changed == 0:
return "No changes"
self.write_to_file(current_ui_settings)
- errors.log.info(f'UI defaults saved: {self.filename}')
+ errors.log.info(f'UI defaults saved: {self.filename} changes={num_changed} unchanged={num_unchanged}')
return f"Wrote {num_changed} changes"
def ui_submenu_apply(self, items):
diff --git a/modules/ui_models.py b/modules/ui_models.py
index bfb876372..34563e891 100644
--- a/modules/ui_models.py
+++ b/modules/ui_models.py
@@ -12,6 +12,7 @@ extra_ui = []
def create_ui():
+ log.debug('UI initialize: tab=models')
dummy_component = gr.Label(visible=False)
with gr.Row(elem_id="models_tab"):
with gr.Column(elem_id='models_output_container', scale=1):
diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py
index b575fa67a..5ed90446a 100644
--- a/modules/ui_postprocessing.py
+++ b/modules/ui_postprocessing.py
@@ -16,6 +16,7 @@ def submit_process(tab_index, extras_image, image_batch, extras_batch_input_dir,
def create_ui():
+ shared.log.debug('UI initialize: tab=process')
tab_index = gr.State(value=0) # pylint: disable=abstract-class-instantiated
with gr.Row(equal_height=False, variant='compact', elem_classes="extras", elem_id="extras_tab"):
with gr.Column(variant='compact'):
diff --git a/modules/ui_settings.py b/modules/ui_settings.py
index 9b24464cc..4cee32959 100644
--- a/modules/ui_settings.py
+++ b/modules/ui_settings.py
@@ -172,6 +172,7 @@ def run_settings_single(value, key, progress=False):
def create_ui():
+ shared.log.debug('UI initialize: tab=settings')
global text_settings # pylint: disable=global-statement
text_settings = gr.Textbox(elem_id="settings_json", elem_classes=["settings_json"], value=lambda: shared.opts.dumpjson(), visible=False)
with gr.Row(elem_id="system_row"):
diff --git a/modules/ui_txt2img.py b/modules/ui_txt2img.py
index fe9a19c50..a7efadba1 100644
--- a/modules/ui_txt2img.py
+++ b/modules/ui_txt2img.py
@@ -5,7 +5,7 @@ from modules.ui_components import ToolButton # pylint: disable=unused-import
def create_ui():
- shared.log.debug('UI initialize: txt2img')
+ shared.log.debug('UI initialize: tab=txt2img')
import modules.txt2img # pylint: disable=redefined-outer-name
modules.scripts_manager.scripts_current = modules.scripts_manager.scripts_txt2img
modules.scripts_manager.scripts_txt2img.initialize_scripts(is_img2img=False, is_control=False)
diff --git a/modules/ui_video.py b/modules/ui_video.py
index bb9cace94..02c787c6e 100644
--- a/modules/ui_video.py
+++ b/modules/ui_video.py
@@ -7,7 +7,7 @@ debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None e
def create_ui():
- shared.log.debug('UI initialize: video')
+ shared.log.debug('UI initialize: tab=video')
with gr.Blocks(analytics_enabled=False) as _video_interface:
prompt, styles, negative, generate_btn, _reprocess, paste, networks_button, _token_counter, _token_button, _token_counter_negative, _token_button_negative = ui_sections.create_toprow(
is_img2img=False,