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,