diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5332f97..69f31cb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Change Log for SD.Next -## Update for 2024-09-05 +## Update for 2024-09-06 -### Highlights for 2024-09-05 +### Highlights for 2024-09-06 Major refactor of [FLUX.1](https://blackforestlabs.ai/announcing-black-forest-labs/) support: - Full **ControlNet** support, better **LoRA** support, full **prompt attention** support, @@ -84,6 +84,7 @@ Plus tons of minor items and fixes - see [changelog](https://github.com/vladmand set via *settings -> live preview -> taesd decode layers* - **xhinker** prompt parser handle offloaded models - **control** better handle offloading +- **control** upscale will use resize-to if set to non-zero values over resize-by - speed up some garbage collection ops - sampler settings add **dynamic shift** used by flow-matching samplers to adjust between structure and details @@ -91,6 +92,7 @@ Plus tons of minor items and fixes - see [changelog](https://github.com/vladmand improves quality of the flow-matching samplers - **t5** support manually downloaded models applies to all models that use t5 transformer +- **modern-ui** add override field - use `diffusers` from main branch, no longer tied to release **Fixes:** diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui index c84d677e0..e79a28d90 160000 --- a/extensions-builtin/sdnext-modernui +++ b/extensions-builtin/sdnext-modernui @@ -1 +1 @@ -Subproject commit c84d677e0c2df4aabe556dc3b40d5fed024e4cc1 +Subproject commit e79a28d90dd04014817ad991abd3d4fc68cde32f diff --git a/modules/images.py b/modules/images.py index 550b08b25..cca84b0ea 100644 --- a/modules/images.py +++ b/modules/images.py @@ -215,8 +215,6 @@ def draw_prompt_matrix(im, width, height, all_prompts, margin=0): def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type='image'): - if im.width == width and im.height == height: - shared.log.debug(f'Image resize: input={im} target={width}x{height} mode={shared.resize_modes[resize_mode]} upscaler="{upscaler_name}" fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access upscaler_name = upscaler_name or shared.opts.upscaler_for_img2img def latent(im, w, h, upscaler): @@ -305,9 +303,8 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type else: res = im.copy() shared.log.error(f'Invalid resize mode: {resize_mode}') - if output_type == 'np': - return np.array(res) - return res + shared.log.debug(f'Image resize: input={im} width={width} height={height} mode={shared.resize_modes[resize_mode]} upscaler="{upscaler_name}" type={output_type} fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access + return np.array(res) if output_type == 'np' else res re_nonletters = re.compile(r'[\s' + string.punctuation + ']+') diff --git a/modules/loader.py b/modules/loader.py index e7cb03339..66286b572 100644 --- a/modules/loader.py +++ b/modules/loader.py @@ -27,6 +27,10 @@ logging.getLogger("pytorch_lightning").disabled = True warnings.filterwarnings(action="ignore", category=DeprecationWarning) warnings.filterwarnings(action="ignore", category=FutureWarning) warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvision") +try: + torch._logging.set_logs(all=logging.ERROR, bytecode=False, aot_graphs=False, aot_joint_graph=False, ddp_graphs=False, graph=False, graph_code=False, graph_breaks=False, graph_sizes=False, guards=False, recompiles=False, recompiles_verbose=False, trace_source=False, trace_call=False, trace_bytecode=False, output_code=False, kernel_code=False, schedule=False, perf_hints=False, post_grad_graphs=False, onnx_diagnostics=False, fusion=False, overlap=False, export=None, modules=None, cudagraphs=False, sym_node=False, compiled_autograd_verbose=False) # pylint: disable=protected-access +except Exception: + pass if ".dev" in torch.__version__ or "+git" in torch.__version__: torch.__long_version__ = torch.__version__ torch.__version__ = re.search(r'[\d.]+[\d]', torch.__version__).group(0) diff --git a/modules/model_flux.py b/modules/model_flux.py index 62c41d379..b85aa220a 100644 --- a/modules/model_flux.py +++ b/modules/model_flux.py @@ -5,7 +5,7 @@ import diffusers import transformers from safetensors.torch import load_file from huggingface_hub import hf_hub_download -from modules import shared, devices, modelloader +from modules import shared, devices, modelloader, sd_models debug = shared.log.trace if os.environ.get('SD_LOAD_DEBUG', None) is not None else lambda *args, **kwargs: None @@ -46,7 +46,7 @@ def load_flux_quanto(checkpoint_info, diffusers_load_config): quantization_map = os.path.join(repo_path, "transformer", "quantization_map.json") debug(f'Loading FLUX: quantization map="{quantization_map}" repo="{checkpoint_info.name}" component="transformer"') if not os.path.exists(quantization_map): - repo_id = checkpoint_info.name.replace('Diffusers/', '').replace('Diffusers\\', '').replace('models--', '').replace('--', '/') + repo_id = sd_models.path_to_repo(checkpoint_info.name) quantization_map = hf_hub_download(repo_id, subfolder='transformer', filename='quantization_map.json', cache_dir=shared.opts.diffusers_dir) with open(quantization_map, "r", encoding='utf8') as f: quantization_map = json.load(f) @@ -71,7 +71,7 @@ def load_flux_quanto(checkpoint_info, diffusers_load_config): quantization_map = os.path.join(repo_path, "text_encoder_2", "quantization_map.json") debug(f'Loading FLUX: quantization map="{quantization_map}" repo="{checkpoint_info.name}" component="text_encoder_2"') if not os.path.exists(quantization_map): - repo_id = checkpoint_info.name.replace('Diffusers/', '').replace('Diffusers\\', '').replace('models--', '').replace('--', '/') + repo_id = sd_models.path_to_repo(checkpoint_info.name) quantization_map = hf_hub_download(repo_id, subfolder='text_encoder_2', filename='quantization_map.json', cache_dir=shared.opts.diffusers_dir) with open(quantization_map, "r", encoding='utf8') as f: quantization_map = json.load(f) @@ -153,7 +153,7 @@ def load_transformer(file_path): # triggered by opts.sd_unet change def load_flux(checkpoint_info, diffusers_load_config): # triggered by opts.sd_checkpoint change quant = get_quant(checkpoint_info.path) - repo_id = checkpoint_info.name.replace('Diffusers/', '').replace('Diffusers\\', '').replace('models--', '').replace('--', '/') + repo_id = sd_models.path_to_repo(checkpoint_info.name) shared.log.debug(f'Loading FLUX: model="{checkpoint_info.name}" repo="{repo_id}" unet="{shared.opts.sd_unet}" t5="{shared.opts.sd_text_encoder}" vae="{shared.opts.sd_vae}" quant={quant} offload={shared.opts.diffusers_offload_mode} dtype={devices.dtype}') debug(f'Loading FLUX: config={diffusers_load_config}') modelloader.hf_login() diff --git a/modules/processing_class.py b/modules/processing_class.py index 830cdc618..0b8b80d1a 100644 --- a/modules/processing_class.py +++ b/modules/processing_class.py @@ -496,15 +496,20 @@ class StableDiffusionProcessingControl(StableDiffusionProcessingImg2Img): def init_hr(self, scale = None, upscaler = None, force = False): scale = scale or self.scale_by upscaler = upscaler or self.resize_name - if upscaler == 'None' or scale == 1.0: + use_scale = self.hr_resize_x == 0 or self.hr_resize_y == 0 + if upscaler == 'None' or (use_scale and scale == 1.0): return self.is_hr_pass = True self.hr_force = force self.hr_upscaler = upscaler - self.hr_upscale_to_x, self.hr_upscale_to_y = 8 * int(self.width * scale / 8), 8 * int(self.height * scale / 8) + use_scale = self.hr_resize_x == 0 or self.hr_resize_y == 0 + if use_scale: + self.hr_upscale_to_x, self.hr_upscale_to_y = 8 * int(self.width * scale / 8), 8 * int(self.height * scale / 8) + else: + self.hr_upscale_to_x, self.hr_upscale_to_y = self.hr_resize_x, self.hr_resize_y # hypertile_set(self, hr=True) shared.state.job_count = 2 * self.n_iter - shared.log.debug(f'Control hires: upscaler="{self.hr_upscaler}" upscale={scale} size={self.hr_upscale_to_x}x{self.hr_upscale_to_y}') + shared.log.debug(f'Control hires: upscaler="{self.hr_upscaler}" scale={scale} fixed={not use_scale} size={self.hr_upscale_to_x}x{self.hr_upscale_to_y}') def switch_class(p: StableDiffusionProcessing, new_class: type, dct: dict = None): diff --git a/modules/sd_models.py b/modules/sd_models.py index 8ea02af68..906cf6b13 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -907,7 +907,7 @@ def move_model(model, device=None, force=False): if hasattr(model, "prior_pipe"): model.prior_pipe.to(device) except Exception as e0: - if 'Cannot copy out of meta tensor' in str(e0): + if 'Cannot copy out of meta tensor' in str(e0) or 'must be Tensor, not NoneType': if hasattr(model, "components"): for _name, component in model.components.items(): if hasattr(component, 'modules'): @@ -1926,3 +1926,11 @@ def remove_token_merging(sd_model): sd_model.applied_todo = 0 except Exception: pass + + +def path_to_repo(fn: str = ''): + repo_id = fn + repo_id = repo_id.replace('Diffusers/', '').replace('Diffusers\\', '') + repo_id = repo_id.replace('diffusers/', '').replace('diffusers\\', '') + repo_id = repo_id.replace('models--', '').replace('--', '/') + return repo_id diff --git a/modules/sd_models_compile.py b/modules/sd_models_compile.py index 2ec8b26a8..6cd11fb18 100644 --- a/modules/sd_models_compile.py +++ b/modules/sd_models_compile.py @@ -3,7 +3,7 @@ import time import logging import torch from modules import shared, devices, sd_models -from installer import setup_logging +from installer import install, setup_logging #Used by OpenVINO, can be used with TensorRT or Olive @@ -165,7 +165,6 @@ def nncf_compress_weights(sd_model): t0 = time.time() shared.log.info(f"NNCF Compress Weights: {shared.opts.nncf_compress_weights}") global quant_last_model_name, quant_last_model_device # pylint: disable=global-statement - from installer import install install('nncf==2.7.0', quiet=True) sd_model = apply_compile_to_model(sd_model, nncf_compress_model, shared.opts.nncf_compress_weights, op="nncf") @@ -233,7 +232,6 @@ def optimum_quanto_weights(sd_model): t0 = time.time() shared.log.info(f"Optimum Quanto Weights: {shared.opts.optimum_quanto_weights}") global quant_last_model_name, quant_last_model_device # pylint: disable=global-statement - from installer import install install('optimum-quanto', quiet=True) from optimum import quanto # pylint: disable=no-name-in-module quanto.tensor.qbits.QBitsTensor.create = lambda *args, **kwargs: quanto.tensor.qbits.QBitsTensor(*args, **kwargs) @@ -484,11 +482,14 @@ def compile_diffusers(sd_model): def dynamic_quantization(sd_model): try: - from torchao.quantization import quant_api + install('torchao', quiet=True) + from torchao.quantization import autoquant except Exception as e: shared.log.error(f"Model dynamic quantization not supported: {e}") return sd_model + """ + from torchao.quantization import quant_api def dynamic_quant_filter_fn(mod, *args): # pylint: disable=unused-argument return (isinstance(mod, torch.nn.Linear) and mod.in_features > 16 and (mod.in_features, mod.out_features) not in [(1280, 640), (1920, 1280), (1920, 640), (2048, 1280), (2048, 2560), (2560, 1280), (256, 128), (2816, 1280), (320, 640), (512, 1536), (512, 256), (512, 512), (640, 1280), (640, 1920), (640, 320), (640, 5120), (640, 640), (960, 320), (960, 640)]) @@ -496,14 +497,23 @@ def dynamic_quantization(sd_model): def conv_filter_fn(mod, *args): # pylint: disable=unused-argument return (isinstance(mod, torch.nn.Conv2d) and mod.kernel_size == (1, 1) and 128 in [mod.in_channels, mod.out_channels]) + quant_api.swap_conv2d_1x1_to_linear(sd_model.unet, conv_filter_fn) + quant_api.swap_conv2d_1x1_to_linear(sd_model.vae, conv_filter_fn) + quant_api.apply_dynamic_quant(sd_model.unet, dynamic_quant_filter_fn) + quant_api.apply_dynamic_quant(sd_model.vae, dynamic_quant_filter_fn) + """ + shared.log.info(f"Model dynamic quantization: pipeline={sd_model.__class__.__name__}") try: - quant_api.swap_conv2d_1x1_to_linear(sd_model.unet, conv_filter_fn) - quant_api.swap_conv2d_1x1_to_linear(sd_model.vae, conv_filter_fn) - quant_api.apply_dynamic_quant(sd_model.unet, dynamic_quant_filter_fn) - quant_api.apply_dynamic_quant(sd_model.vae, dynamic_quant_filter_fn) + if shared.sd_model_type == 'sd' or shared.sd_model_type == 'sdxl': + sd_model.unet = sd_model.unet.to(devices.device) + sd_model.unet = autoquant(sd_model.unet, error_on_unseen=False) + elif shared.sd_model_type == 'f1': + sd_model.transformer = autoquant(sd_model.transformer, error_on_unseen=False) + else: + shared.log.error(f"Model dynamic quantization not supported: {shared.sd_model_type}") except Exception as e: - shared.log.error(f"Model dynamic quantization error: {e}") + shared.log.error(f"Model dynamic quantization: {e}") return sd_model diff --git a/wiki b/wiki index 3cfd8da8d..87bd5adaa 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 3cfd8da8d2a1426df54e31967f835c15082c2c33 +Subproject commit 87bd5adaae34ebe5c880f64e56322ff47d0a315a