From c254c8c1ec95849fe5e3b9550532f96325db2375 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 25 Jun 2025 19:14:44 -0400 Subject: [PATCH 1/8] fix hypertile for img2img and inpaint operations Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 1 + modules/processing_diffusers.py | 4 ++-- modules/sd_hijack_hypertile.py | 33 +++++++++++++++++++++++---------- wiki | 2 +- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd2156a3e..23b8feb2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - Add `SD_SAVE_DEBUG` env variable to report all params and metadata save operations as they happen - Fix TAESD model type detection - Fix LoRA loader incorrectly reporting errors + - Fix hypertile for img2img and inpaint operations ## Update for 2025-06-16 diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index c3eb66d77..4b10b4835 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -200,8 +200,8 @@ def process_hires(p: processing.StableDiffusionProcessing, output): if 'Upscale' in shared.sd_model.__class__.__name__ or 'Flux' in shared.sd_model.__class__.__name__ or 'Kandinsky' in shared.sd_model.__class__.__name__: output.images = processing_vae.vae_decode(latents=output.images, model=shared.sd_model, vae_type=p.vae_type, output_type='pil', width=p.width, height=p.height) if p.is_control and hasattr(p, 'task_args') and p.task_args.get('image', None) is not None: - if hasattr(shared.sd_model, "vae") and output.images is not None and len(output.images) > 0: - output.images = processing_vae.vae_decode(latents=output.images, model=shared.sd_model, vae_type=p.vae_type, output_type='pil', width=p.hr_upscale_to_x, height=p.hr_upscale_to_y) # controlnet cannnot deal with latent input + if hasattr(shared.sd_model, "vae") and output.images is not None and len(output.images) > 0: + output.images = processing_vae.vae_decode(latents=output.images, model=shared.sd_model, vae_type=p.vae_type, output_type='pil', width=p.hr_upscale_to_x, height=p.hr_upscale_to_y) # controlnet cannnot deal with latent input update_sampler(p, shared.sd_model, second_pass=True) orig_denoise = p.denoising_strength p.denoising_strength = strength diff --git a/modules/sd_hijack_hypertile.py b/modules/sd_hijack_hypertile.py index c27bf8f39..ebe86948a 100644 --- a/modules/sd_hijack_hypertile.py +++ b/modules/sd_hijack_hypertile.py @@ -181,17 +181,19 @@ def context_hypertile_vae(p): if shared.opts.cross_attention_optimization == 'Sub-quadratic': shared.log.warning('Hypertile UNet is not compatible with Sub-quadratic cross-attention optimization') return nullcontext() - global height, width, max_h, max_w, error_reported # pylint: disable=global-statement + global max_h, max_w, error_reported # pylint: disable=global-statement error_reported = False error_reported = False - height, width = p.height, p.width + set_resolution(p) max_h, max_w = 0, 0 vae = getattr(p.sd_model, "vae", None) if shared.native else getattr(p.sd_model, "first_stage_model", None) + if height == 0 or width == 0: + log.warning('Hypertile VAE disabled: resolution unknown') + return nullcontext() if height % 8 != 0 or width % 8 != 0: log.warning(f'Hypertile VAE disabled: width={width} height={height} are not divisible by 8') return nullcontext() if vae is None: - # shared.log.warning('Hypertile VAE is enabled but no VAE model was found') return nullcontext() else: tile_size = shared.opts.hypertile_vae_tile if shared.opts.hypertile_vae_tile > 0 else max(128, 64 * min(p.width // 128, p.height // 128)) @@ -208,11 +210,14 @@ def context_hypertile_unet(p): if shared.opts.cross_attention_optimization == 'Sub-quadratic' and not shared.cmd_opts.experimental: shared.log.warning('Hypertile UNet is not compatible with Sub-quadratic cross-attention optimization') return nullcontext() - global height, width, max_h, max_w, error_reported # pylint: disable=global-statement + global max_h, max_w, error_reported # pylint: disable=global-statement error_reported = False - height, width = p.height, p.width + set_resolution(p) max_h, max_w = 0, 0 unet = getattr(p.sd_model, "unet", None) if shared.native else getattr(p.sd_model.model, "diffusion_model", None) + if height == 0 or width == 0: + log.warning('Hypertile VAE disabled: resolution unknown') + return nullcontext() if height % 8 != 0 or width % 8 != 0: log.warning(f'Hypertile UNet disabled: width={width} height={height} are not divisible by 8') return nullcontext() @@ -229,17 +234,25 @@ def context_hypertile_unet(p): def hypertile_set(p, hr=False): from modules import shared - global height, width, error_reported, reset_needed, skip_hypertile # pylint: disable=global-statement + global error_reported, reset_needed, skip_hypertile # pylint: disable=global-statement if not shared.opts.hypertile_unet_enabled: return error_reported = False + set_resolution(p, hr=hr) + skip_hypertile = shared.opts.hypertile_hires_only and not getattr(p, 'is_hr_pass', False) + reset_needed = True + + +def set_resolution(p, hr=False): + global height, width # pylint: disable=global-statement if hr: x = getattr(p, 'hr_upscale_to_x', 0) y = getattr(p, 'hr_upscale_to_y', 0) width = y if y > 0 else p.width height = x if x > 0 else p.height else: - width=p.width - height=p.height - skip_hypertile = shared.opts.hypertile_hires_only and not getattr(p, 'is_hr_pass', False) - reset_needed = True + width = p.width + height = p.height + if height == 0 or width == 0: + if hasattr(p, 'init_images') and isinstance(p.init_images, list) and len(p.init_images) > 0: + height, width = p.init_images[0].size diff --git a/wiki b/wiki index 5e97702f2..f2814574e 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 5e97702f219b879c035057204303ae649e1edcf7 +Subproject commit f2814574e02fd313348cc68c31573606617240cd From dc8fd006b2b7e026dc0cd9914e6e760bc154ca62 Mon Sep 17 00:00:00 2001 From: Disty0 Date: Thu, 26 Jun 2025 02:47:10 +0300 Subject: [PATCH 2/8] Add modules_to_not_convert to pre-mode quants --- modules/model_quant.py | 73 +++++++++++++++++++--------------------- modules/sdnq/__init__.py | 5 ++- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/modules/model_quant.py b/modules/model_quant.py index 307707708..9f2bd6a0b 100644 --- a/modules/model_quant.py +++ b/modules/model_quant.py @@ -37,7 +37,7 @@ def get_quant(name): return 'none' -def create_bnb_config(kwargs = None, allow_bnb: bool = True, module: str = 'Model'): +def create_bnb_config(kwargs = None, allow_bnb: bool = True, module: str = 'Model', modules_to_not_convert: list = []): from modules import shared, devices if len(shared.opts.bnb_quantization) > 0 and allow_bnb: if 'Model' in shared.opts.bnb_quantization or (module is not None and module in shared.opts.bnb_quantization) or module == 'any': @@ -49,7 +49,8 @@ def create_bnb_config(kwargs = None, allow_bnb: bool = True, module: str = 'Mode load_in_4bit=shared.opts.bnb_quantization_type in ['nf4', 'fp4'], bnb_4bit_quant_storage=shared.opts.bnb_quantization_storage, bnb_4bit_quant_type=shared.opts.bnb_quantization_type, - bnb_4bit_compute_dtype=devices.dtype + bnb_4bit_compute_dtype=devices.dtype, + #modules_to_not_convert=modules_to_not_convert, # ignored by bnb ) log.debug(f'Quantization: module={module} type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}') if kwargs is None: @@ -60,7 +61,7 @@ def create_bnb_config(kwargs = None, allow_bnb: bool = True, module: str = 'Mode return kwargs -def create_ao_config(kwargs = None, allow_ao: bool = True, module: str = 'Model'): +def create_ao_config(kwargs = None, allow_ao: bool = True, module: str = 'Model', modules_to_not_convert: list = []): from modules import shared if len(shared.opts.torchao_quantization) > 0 and (shared.opts.torchao_quantization_mode == 'pre') and allow_ao: if 'Model' in shared.opts.torchao_quantization or (module is not None and module in shared.opts.torchao_quantization) or module == 'any': @@ -68,9 +69,9 @@ def create_ao_config(kwargs = None, allow_ao: bool = True, module: str = 'Model' if torchao is None: return kwargs if module in {'TE', 'LLM'}: - ao_config = transformers.TorchAoConfig(quant_type=shared.opts.torchao_quantization_type) + ao_config = transformers.TorchAoConfig(quant_type=shared.opts.torchao_quantization_type, modules_to_not_convert=modules_to_not_convert) else: - ao_config = diffusers.TorchAoConfig(shared.opts.torchao_quantization_type) + ao_config = diffusers.TorchAoConfig(shared.opts.torchao_quantization_type, modules_to_not_convert=modules_to_not_convert) log.debug(f'Quantization: module={module} type=torchao dtype={shared.opts.torchao_quantization_type}') if kwargs is None: return ao_config @@ -80,7 +81,7 @@ def create_ao_config(kwargs = None, allow_ao: bool = True, module: str = 'Model' return kwargs -def create_quanto_config(kwargs = None, allow_quanto: bool = True, module: str = 'Model'): +def create_quanto_config(kwargs = None, allow_quanto: bool = True, module: str = 'Model', modules_to_not_convert: list = []): from modules import shared if len(shared.opts.quanto_quantization) > 0 and allow_quanto: if 'Model' in shared.opts.quanto_quantization or (module is not None and module in shared.opts.quanto_quantization) or module == 'any': @@ -88,10 +89,10 @@ def create_quanto_config(kwargs = None, allow_quanto: bool = True, module: str = if optimum_quanto is None: return kwargs if module in {'TE', 'LLM'}: - quanto_config = transformers.QuantoConfig(weights=shared.opts.quanto_quantization_type) + quanto_config = transformers.QuantoConfig(weights=shared.opts.quanto_quantization_type, modules_to_not_convert=modules_to_not_convert) quanto_config.weights_dtype = quanto_config.weights else: - quanto_config = diffusers.QuantoConfig(weights_dtype=shared.opts.quanto_quantization_type) + quanto_config = diffusers.QuantoConfig(weights_dtype=shared.opts.quanto_quantization_type, modules_to_not_convert=modules_to_not_convert) quanto_config.activations = None # patch so it works with transformers quanto_config.weights = quanto_config.weights_dtype log.debug(f'Quantization: module={module} type=quanto dtype={shared.opts.quanto_quantization_type}') @@ -103,7 +104,7 @@ def create_quanto_config(kwargs = None, allow_quanto: bool = True, module: str = return kwargs -def create_sdnq_config(kwargs = None, allow_sdnq: bool = True, module: str = 'Model', weights_dtype: str = None): +def create_sdnq_config(kwargs = None, allow_sdnq: bool = True, module: str = 'Model', weights_dtype: str = None, modules_to_not_convert: list = []): from modules import devices, shared if len(shared.opts.sdnq_quantize_weights) > 0 and (shared.opts.sdnq_quantize_mode == 'pre') and allow_sdnq: if 'Model' in shared.opts.sdnq_quantize_weights or (module is not None and module in shared.opts.sdnq_quantize_weights) or module == 'any': @@ -114,15 +115,8 @@ def create_sdnq_config(kwargs = None, allow_sdnq: bool = True, module: str = 'Mo transformers.quantizers.auto.AUTO_QUANTIZATION_CONFIG_MAPPING["sdnq"] = SDNQConfig if weights_dtype is None: - if module in {"TE", "LLM"}: - if shared.opts.sdnq_quantize_weights_mode_te == "none": - return kwargs - elif shared.opts.sdnq_quantize_weights_mode_te in {"same as model", "default"}: - weights_dtype = shared.opts.sdnq_quantize_weights_mode - else: - weights_dtype = shared.opts.sdnq_quantize_weights_mode_te - elif shared.opts.sdnq_quantize_weights_mode == "none": - return kwargs + if module in {"TE", "LLM"} and shared.opts.sdnq_quantize_weights_mode_te not in {"same as model", "default"}: + weights_dtype = shared.opts.sdnq_quantize_weights_mode_te else: weights_dtype = shared.opts.sdnq_quantize_weights_mode if weights_dtype is None or weights_dtype == 'none': @@ -150,6 +144,7 @@ def create_sdnq_config(kwargs = None, allow_sdnq: bool = True, module: str = 'Mo dequantize_fp32=shared.opts.sdnq_dequantize_fp32, quantization_device=quantization_device, return_device=return_device, + modules_to_not_convert=modules_to_not_convert, ) log.debug(f'Quantization: module="{module}" type=sdnq dtype={weights_dtype} matmul={shared.opts.sdnq_use_quantized_matmul} group_size={shared.opts.sdnq_quantize_weights_group_size} quant_conv={shared.opts.sdnq_quantize_conv_layers} matmul_conv={shared.opts.sdnq_use_quantized_matmul_conv} dequantize_fp32={shared.opts.sdnq_dequantize_fp32} quantize_with_gpu={shared.opts.sdnq_quantize_with_gpu} quantization_device={quantization_device} return_device={return_device}') if kwargs is None: @@ -180,25 +175,25 @@ def check_nunchaku(module: str = ''): return True -def create_config(kwargs = None, allow: bool = True, module: str = 'Model'): +def create_config(kwargs = None, allow: bool = True, module: str = 'Model', modules_to_not_convert = []): if kwargs is None: kwargs = {} - kwargs = create_sdnq_config(kwargs, allow_sdnq=allow, module=module) + kwargs = create_sdnq_config(kwargs, allow_sdnq=allow, module=module, modules_to_not_convert=modules_to_not_convert) if kwargs is not None and 'quantization_config' in kwargs: if debug: log.trace(f'Quantization: type=sdnq config={kwargs.get("quantization_config", None)}') return kwargs - kwargs = create_bnb_config(kwargs, allow_bnb=allow, module=module) + kwargs = create_bnb_config(kwargs, allow_bnb=allow, module=module, modules_to_not_convert=modules_to_not_convert) if kwargs is not None and 'quantization_config' in kwargs: if debug: log.trace(f'Quantization: type=bnb config={kwargs.get("quantization_config", None)}') return kwargs - kwargs = create_quanto_config(kwargs, allow_quanto=allow, module=module) + kwargs = create_quanto_config(kwargs, allow_quanto=allow, module=module, modules_to_not_convert=modules_to_not_convert) if kwargs is not None and 'quantization_config' in kwargs: if debug: log.trace(f'Quantization: type=quanto config={kwargs.get("quantization_config", None)}') return kwargs - kwargs = create_ao_config(kwargs, allow_ao=allow, module=module) + kwargs = create_ao_config(kwargs, allow_ao=allow, module=module, modules_to_not_convert=modules_to_not_convert) if kwargs is not None and 'quantization_config' in kwargs: if debug: log.trace(f'Quantization: type=torchao config={kwargs.get("quantization_config", None)}') @@ -331,20 +326,16 @@ def apply_layerwise(sd_model, quiet:bool=False): log.error(f'Quantization: type=layerwise {e}') -def sdnq_quantize_model(model, op=None, sd_model=None, do_gc=True): +def sdnq_quantize_model(model, op=None, sd_model=None, do_gc: bool = True, weights_dtype: str = None, modules_to_not_convert: list = []): global quant_last_model_name, quant_last_model_device # pylint: disable=global-statement from modules import devices, shared from modules.sdnq import apply_sdnq_to_module - model.eval() - backup_embeddings = None - if hasattr(model, "get_input_embeddings"): - backup_embeddings = copy.deepcopy(model.get_input_embeddings()) - - if shared.opts.sdnq_quantize_weights_mode_te != "default" and op is not None and "text_encoder" in op: - weights_dtype = shared.opts.sdnq_quantize_weights_mode_te - else: - weights_dtype = shared.opts.sdnq_quantize_weights_mode + if weights_dtype is None: + if op is not None and ("text_encoder" in op or op in {"TE", "LLM"}) and shared.opts.sdnq_quantize_weights_mode_te not in {"same as model", "default"}: + weights_dtype = shared.opts.sdnq_quantize_weights_mode_te + else: + weights_dtype = shared.opts.sdnq_quantize_weights_mode if weights_dtype is None or weights_dtype == 'none': return model @@ -361,9 +352,15 @@ def sdnq_quantize_model(model, op=None, sd_model=None, do_gc=True): quantization_device = None return_device = None - modules_to_not_convert = getattr(model, "_keep_in_fp32_modules", []) - if modules_to_not_convert is None: - modules_to_not_convert = [] + if getattr(model, "_keep_in_fp32_modules", None) is not None: + modules_to_not_convert.extend(model._keep_in_fp32_modules) + if model.__class__.__name__ == "ChromaTransformer2DModel": + modules_to_not_convert.append("distilled_guidance_layer") + + model.eval() + backup_embeddings = None + if hasattr(model, "get_input_embeddings"): + backup_embeddings = copy.deepcopy(model.get_input_embeddings()) model = apply_sdnq_to_module( model, @@ -558,7 +555,7 @@ def torchao_quantization(sd_model): return sd_model -def get_dit_args(load_config:dict={}, module:str=None, device_map:bool=False, allow_quant:bool=True): +def get_dit_args(load_config:dict={}, module:str=None, device_map:bool=False, allow_quant:bool=True, modules_to_not_convert: list = []): from modules import shared, devices config = load_config.copy() if 'torch_dtype' not in config: @@ -581,7 +578,7 @@ def get_dit_args(load_config:dict={}, module:str=None, device_map:bool=False, al elif shared.opts.device_map == 'gpu': config['device_map'] = devices.device if allow_quant: - quant_args = create_config(module=module) + quant_args = create_config(module=module, modules_to_not_convert=modules_to_not_convert) else: quant_args = {} return config, quant_args diff --git a/modules/sdnq/__init__.py b/modules/sdnq/__init__.py index c76d1e9f9..b4e251601 100644 --- a/modules/sdnq/__init__.py +++ b/modules/sdnq/__init__.py @@ -441,5 +441,8 @@ class SDNQConfig(QuantizationConfigMixin): accepted_weights = ["int8", "int7", "int6", "int5", "int4", "int3", "int2", "uint8", "uint7", "uint6", "uint5", "uint4", "uint3", "uint2", "uint1", "bool", "float8_e4m3fn", "float8_e4m3fnuz", "float8_e5m2", "float8_e5m2fnuz"] if self.weights_dtype not in accepted_weights: raise ValueError(f"Only support weights in {accepted_weights} but found {self.weights_dtype}") - if not isinstance(self.modules_to_not_convert, list): + + if self.modules_to_not_convert is None: + self.modules_to_not_convert = [] + elif not isinstance(self.modules_to_not_convert, list): self.modules_to_not_convert = [self.modules_to_not_convert] From 0f6eb624c9787c2d4c8fe2365df21a354fc55c75 Mon Sep 17 00:00:00 2001 From: Disty0 Date: Thu, 26 Jun 2025 03:10:26 +0300 Subject: [PATCH 3/8] Use llm_int8_skip_modules with bnb --- modules/model_quant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/model_quant.py b/modules/model_quant.py index 9f2bd6a0b..b46e25715 100644 --- a/modules/model_quant.py +++ b/modules/model_quant.py @@ -50,7 +50,7 @@ def create_bnb_config(kwargs = None, allow_bnb: bool = True, module: str = 'Mode bnb_4bit_quant_storage=shared.opts.bnb_quantization_storage, bnb_4bit_quant_type=shared.opts.bnb_quantization_type, bnb_4bit_compute_dtype=devices.dtype, - #modules_to_not_convert=modules_to_not_convert, # ignored by bnb + llm_int8_skip_modules=modules_to_not_convert, ) log.debug(f'Quantization: module={module} type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}') if kwargs is None: From 7380c08f8ea101f5a356678b227bf4072569c102 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Jun 2025 06:50:50 -0400 Subject: [PATCH 4/8] lint fix Signed-off-by: Vladimir Mandic --- modules/lora/lora_load.py | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/lora/lora_load.py b/modules/lora/lora_load.py index dbeb57630..c5f6bc952 100644 --- a/modules/lora/lora_load.py +++ b/modules/lora/lora_load.py @@ -150,7 +150,6 @@ def load_safetensors(name, network_on_disk) -> Union[network.Network, None]: break if net_module is None: module_errors += 1 - if l.debug: shared.log.error(f'LoRA unhandled: name={name} key={key} weights={weights.w.keys()}') else: From 4de160608ed3aa09a3064aa0072e27105d58805d Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Jun 2025 07:47:03 -0400 Subject: [PATCH 5/8] fix prompt parser with batch size Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 1 + modules/processing_helpers.py | 9 +++++++++ modules/prompt_parser_diffusers.py | 13 +++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23b8feb2d..651847e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - Fix TAESD model type detection - Fix LoRA loader incorrectly reporting errors - Fix hypertile for img2img and inpaint operations + - Fix prompt parser batch size ## Update for 2025-06-16 diff --git a/modules/processing_helpers.py b/modules/processing_helpers.py index 461e0e9ed..b41127e69 100644 --- a/modules/processing_helpers.py +++ b/modules/processing_helpers.py @@ -424,19 +424,28 @@ def resize_hires(p, latents): # input=latents output=pil if not latent_upscaler def fix_prompts(p, prompts, negative_prompts, prompts_2, negative_prompts_2): if hasattr(p, 'keep_prompts'): return prompts, negative_prompts, prompts_2, negative_prompts_2 + if type(prompts) is str: prompts = [prompts] if type(negative_prompts) is str: negative_prompts = [negative_prompts] + if hasattr(p, '[init_images]') and p.init_images is not None and len(p.init_images) > 1: while len(prompts) < len(p.init_images): prompts.append(prompts[-1]) while len(negative_prompts) < len(p.init_images): negative_prompts.append(negative_prompts[-1]) + + while len(prompts) < p.batch_size: + prompts.append(prompts[-1]) + while len(negative_prompts) < p.batch_size: + negative_prompts.append(negative_prompts[-1]) + while len(negative_prompts) < len(prompts): negative_prompts.append(negative_prompts[-1]) while len(prompts) < len(negative_prompts): prompts.append(prompts[-1]) + if type(prompts_2) is str: prompts_2 = [prompts_2] if type(prompts_2) is list: diff --git a/modules/prompt_parser_diffusers.py b/modules/prompt_parser_diffusers.py index ad80e3af0..731dc6608 100644 --- a/modules/prompt_parser_diffusers.py +++ b/modules/prompt_parser_diffusers.py @@ -53,7 +53,8 @@ class PromptEmbedder: self.negative_prompts = negative_prompts self.batchsize = len(self.prompts) self.attention = last_attention - self.allsame = self.compare_prompts() # collapses batched prompts to single prompt if possible + self.allsame = False # dont collapse prompts + # self.allsame = self.compare_prompts() # collapses batched prompts to single prompt if possible self.steps = steps self.clip_skip = clip_skip # All embeds are nested lists, outer list batch length, inner schedule length @@ -83,7 +84,7 @@ class PromptEmbedder: self.checkcache(p) debug(f"Prompt encode: time={(time.time() - t0):.3f}") - def checkcache(self, p): + def checkcache(self, p) -> bool: if shared.opts.sd_textencoder_cache_size == 0: return False if self.scheduled_prompt: @@ -176,13 +177,13 @@ class PromptEmbedder: else: prompt_embed, positive_pooled, negative_embed, negative_pooled = get_weighted_text_embeddings(pipe, positive_prompt, negative_prompt, self.clip_skip) if prompt_embed is not None: - self.prompt_embeds[batchidx].append(prompt_embed) + self.prompt_embeds[batchidx] = [prompt_embed] if negative_embed is not None: - self.negative_prompt_embeds[batchidx].append(negative_embed) + self.negative_prompt_embeds[batchidx] = [negative_embed] if positive_pooled is not None: - self.positive_pooleds[batchidx].append(positive_pooled) + self.positive_pooleds[batchidx] = [positive_pooled] if negative_pooled is not None: - self.negative_pooleds[batchidx].append(negative_pooled) + self.negative_pooleds[batchidx] = [negative_pooled] if debug_enabled: get_tokens(pipe, 'positive', positive_prompt) From 82aaff9d707ab14d898e98db8a2092a292b6e58c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Jun 2025 07:59:51 -0400 Subject: [PATCH 6/8] gallery trace file fetch Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 2 +- modules/api/gallery.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 651847e83..ec8698873 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log for SD.Next -## Update for 2025-06-25 +## Update for 2025-06-26 - **Changes** - Add [JoyCaption Beta](https://huggingface.co/fancyfeast/llama-joycaption-beta-one-hf-llava) support (in addition to existing JoyCaption Alpha) diff --git a/modules/api/gallery.py b/modules/api/gallery.py index e1add81af..6510cd673 100644 --- a/modules/api/gallery.py +++ b/modules/api/gallery.py @@ -6,7 +6,7 @@ from typing import List, Union from urllib.parse import quote, unquote from fastapi import FastAPI from fastapi.responses import JSONResponse -from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect +from starlette.websockets import WebSocket, WebSocketState from pydantic import BaseModel, Field # pylint: disable=no-name-in-module from PIL import Image from modules import shared, images, files_cache @@ -196,6 +196,6 @@ def register_api(app: FastAPI): # register api await manager.send(ws, '#END#') t1 = time.time() shared.log.debug(f'Gallery: type=ws folder="{folder}" files={numFiles} time={t1-t0:.3f}') - except WebSocketDisconnect: - debug('Browser WS unexpected disconnect') + except Exception as e: + debug(f'Browser WS error: {e}') manager.disconnect(ws) From 931650c43f992e795a6e0a35fca5fc45fe42e4c6 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Jun 2025 09:17:10 -0400 Subject: [PATCH 7/8] fix process batch Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 2 ++ modules/img2img.py | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8698873..96d8fc9a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ - Fix LoRA loader incorrectly reporting errors - Fix hypertile for img2img and inpaint operations - Fix prompt parser batch size + - Fix process batch with batch count + - Fix process batch double image save ## Update for 2025-06-16 diff --git a/modules/img2img.py b/modules/img2img.py index ca71ff0e7..888bf3626 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -32,21 +32,23 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) inpaint_masks = [f for f in inpaint_masks if filetype.is_image(f)] is_inpaint_batch = len(inpaint_masks) > 0 shared.log.info(f'Process batch: mask folder="{input_dir}" images={len(inpaint_masks)}') - save_normally = output_dir == '' p.do_not_save_grid = True - p.do_not_save_samples = not save_normally + p.do_not_save_samples = True p.default_prompt = p.prompt + if p.n_iter > 1: + p.n_iter = 1 + shared.log.warning(f'Process batch: batch_count={p.n_iter} forced to 1') shared.state.job_count = len(image_files) * p.n_iter if shared.opts.batch_frame_mode: # SBM Frame mode is on, process each image in batch with same seed window_size = p.batch_size btcrept = 1 p.seed = [p.seed] * window_size # SBM MONKEYPATCH: Need to change processing to support a fixed seed value. p.subseed = [p.subseed] * window_size # SBM MONKEYPATCH - shared.log.info(f"Process batch: inputs={len(image_files)} parallel={window_size} outputs={p.n_iter} per input ") + shared.log.info(f"Process batch: inputs={len(image_files)} outputs={p.n_iter}x{len(image_files)} parallel={window_size}") else: # SBM Frame mode is off, standard operation of repeating same images with sequential seed. window_size = 1 btcrept = p.batch_size - shared.log.info(f"Process batch: inputs={len(image_files)} outputs={p.n_iter * p.batch_size} per input") + shared.log.info(f"Process batch: inputs={len(image_files)} outputs={p.n_iter*p.batch_size}x{len(image_files)}") for i in range(0, len(image_files), window_size): if shared.state.skipped: shared.state.skipped = False @@ -117,8 +119,7 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) basename = '' if output_dir == '': output_dir = shared.opts.outdir_img2img_samples - if not save_normally: - os.makedirs(output_dir, exist_ok=True) + os.makedirs(output_dir, exist_ok=True) geninfo, items = images.read_info_from_image(image) for k, v in items.items(): image.info[k] = v From 2714d29993a06c3a542eadbf577a77a6cf5d77dc Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Jun 2025 09:30:15 -0400 Subject: [PATCH 8/8] fix unapply texture tiling Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 1 + modules/processing_helpers.py | 3 ++- modules/sd_models.py | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96d8fc9a2..cccb4ede7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Fix prompt parser batch size - Fix process batch with batch count - Fix process batch double image save + - Fix unapply texture tiling ## Update for 2025-06-16 diff --git a/modules/processing_helpers.py b/modules/processing_helpers.py index b41127e69..013b32109 100644 --- a/modules/processing_helpers.py +++ b/modules/processing_helpers.py @@ -551,7 +551,8 @@ def set_latents(p): def apply_circular(enable: bool, model): if not hasattr(model, 'unet') or not hasattr(model, 'vae'): return - if getattr(model, 'texture_tiling', False) == enable: + current = getattr(model, 'texture_tiling', 0) + if isinstance(current, bool) and current == enable: return try: i = 0 diff --git a/modules/sd_models.py b/modules/sd_models.py index f6a7e53db..0530f5a5b 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -427,7 +427,6 @@ def load_diffuser_folder(model_type, pipeline, checkpoint_info, diffusers_load_c def load_diffuser_file(model_type, pipeline, checkpoint_info, diffusers_load_config, op='model'): sd_model = None - diffusers_load_config["local_files_only"] = diffusers_version < 28 # must be true for old diffusers, otherwise false but we override config for sd15/sdxl diffusers_load_config["extract_ema"] = shared.opts.diffusers_extract_ema if pipeline is None: shared.log.error(f'Load {op}: pipeline={shared.opts.diffusers_pipeline} not initialized')