Merge branch 'dev' into master

This commit is contained in:
Vladimir Mandic
2025-09-01 11:41:07 -04:00
committed by GitHub
226 changed files with 5840 additions and 763 deletions
+2 -2
View File
@@ -168,7 +168,7 @@ def atomic_civit_search_metadata(item, results):
# log.error(f'CivitAI search metadata: item={item} {e}')
return
has_meta = os.path.isfile(meta) and os.stat(meta).st_size > 0
if ('card-no-preview.png' in item['preview'] or not has_meta) and os.path.isfile(item['filename']):
if ('missing.png' in item['preview'] or not has_meta) and os.path.isfile(item['filename']):
sha = item.get('hash', None)
found = False
result = {
@@ -260,7 +260,7 @@ def civit_search_metadata(title: str = None):
if type(title) == str:
if page.title != title:
continue
if page.name == 'style':
if page.name == 'style' or page.name == 'wildcards':
continue
for item in page.list_items():
if item is None:
+1 -1
View File
@@ -200,7 +200,7 @@ def create_model_cards(all_models: list[Model]) -> str:
if image.url and len(image.url) > 0 and not image.url.lower().endswith('.mp4'):
previews.append(image.url)
if len(previews) == 0:
previews = ['/sdapi/v1/network/thumb?filename=html/card-no-preview.png']
previews = ['/sdapi/v1/network/thumb?filename=html/missing.png']
all_cards += card.format(id=model.id, name=model.name, type=model.type, preview=previews[0])
html = details + cards.format(cards=all_cards)
return html
+2 -1
View File
@@ -7,6 +7,7 @@ from modules.processing_class import StableDiffusionProcessingControl
from modules import shared, images, masking, sd_models
from modules.timer import process as process_timer
from modules.control import util
from modules.control import processors as control_processors
debug = os.environ.get('SD_CONTROL_DEBUG', None) is not None
@@ -108,7 +109,7 @@ def preprocess_image(
if processed_image is not None:
processed_images.append(processed_image)
if shared.opts.control_unload_processor and process.processor_id is not None:
processors.config[process.processor_id]['dirty'] = True # to force reload
control_processors.config[process.processor_id]['dirty'] = True # to force reload
process.model = None
# blend processed images
+56 -4
View File
@@ -102,6 +102,15 @@ predefined_sd3 = {
"Alimama Inpainting SD35": 'alimama-creative/SD3-Controlnet-Inpainting',
"Alimama SoftEdge SD35": 'alimama-creative/SD3-Controlnet-Softedge',
}
predefined_qwen = {
"InstantX Union Qwen": 'InstantX/Qwen-Image-ControlNet-Union',
}
predefined_hunyuandit = {
"HunyuanDiT Canny": 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Canny',
"HunyuanDiT Pose": 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Pose',
"HunyuanDiT Depth": 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Depth',
}
variants = {
'NoobAI Canny XL': 'fp16',
'NoobAI Lineart Anime XL': 'fp16',
@@ -116,6 +125,8 @@ all_models.update(predefined_sd15)
all_models.update(predefined_sdxl)
all_models.update(predefined_f1)
all_models.update(predefined_sd3)
all_models.update(predefined_qwen)
all_models.update(predefined_hunyuandit)
cache_dir = 'models/control/controlnet'
load_lock = threading.Lock()
@@ -150,6 +161,10 @@ def api_list_models(model_type: str = None):
model_list += list(predefined_f1)
if model_type == 'sd3' or model_type == 'all':
model_list += list(predefined_sd3)
if model_type == 'qwen' or model_type == 'all':
model_list += list(predefined_qwen)
if model_type == 'hunyuandit' or model_type == 'all':
model_list += list(predefined_hunyuandit)
model_list += sorted(find_models())
return model_list
@@ -170,6 +185,10 @@ def list_models(refresh=False):
models = ['None'] + list(predefined_f1) + sorted(find_models())
elif modules.shared.sd_model_type == 'sd3':
models = ['None'] + list(predefined_sd3) + sorted(find_models())
elif modules.shared.sd_model_type == 'qwen':
models = ['None'] + list(predefined_qwen) + sorted(find_models())
elif modules.shared.sd_model_type == 'hunyuandit':
models = ['None'] + list(predefined_hunyuandit) + sorted(find_models())
else:
log.warning(f'Control {what} model list failed: unknown model type')
models = ['None'] + sorted(predefined_sd15) + sorted(predefined_sdxl) + sorted(predefined_f1) + sorted(predefined_sd3) + sorted(find_models())
@@ -222,12 +241,18 @@ class ControlNet():
elif shared.sd_model_type == 'sd3':
from diffusers import SD3ControlNetModel as cls
config = 'InstantX/SD3-Controlnet-Canny'
elif shared.sd_model_type == 'qwen':
from diffusers import QwenImageControlNetModel as cls
config = 'InstantX/Qwen-Image-ControlNet-Union'
elif shared.sd_model_type == 'hunyuandit':
from diffusers import HunyuanDiT2DControlNetModel as cls
config = 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Canny'
else:
log.error(f'Control {what}: type={shared.sd_model_type} unsupported model')
return None, None
return cls, config
def load_safetensors(self, model_id, model_path, cls, config):
def load_safetensors(self, model_id, model_path, cls, config): # pylint: disable=unused-argument
name = os.path.splitext(model_path)[0]
config_path = None
if not os.path.exists(model_path):
@@ -302,6 +327,7 @@ class ControlNet():
errors.display(e, 'Control')
if self.model is None:
return
self.model.offload_never = True
if self.dtype is not None:
self.model.to(self.dtype)
if "Control" in opts.sdnq_quantize_weights:
@@ -422,6 +448,30 @@ class ControlNetPipeline():
controlnet=controlnets, # can be a list
)
sd_models.move_model(self.pipeline, pipeline.device)
elif detect.is_qwen(pipeline) and len(controlnets) > 0:
from diffusers import QwenImageControlNetPipeline
self.pipeline = QwenImageControlNetPipeline(
vae=pipeline.vae,
text_encoder=pipeline.text_encoder,
tokenizer=pipeline.tokenizer,
transformer=pipeline.transformer,
scheduler=pipeline.scheduler,
controlnet=controlnets[0] if isinstance(controlnets, list) else controlnets, # can be a list
)
elif detect.is_hunyuandit(pipeline) and len(controlnets) > 0:
from diffusers import HunyuanDiTControlNetPipeline
self.pipeline = HunyuanDiTControlNetPipeline(
vae=pipeline.vae,
text_encoder=pipeline.text_encoder,
tokenizer=pipeline.tokenizer,
text_encoder_2=pipeline.text_encoder_2,
tokenizer_2=pipeline.tokenizer_2,
transformer=pipeline.transformer,
scheduler=pipeline.scheduler,
safety_checker=None,
feature_extractor=None,
controlnet=controlnets[0] if isinstance(controlnets, list) else controlnets, # can be a list
)
elif len(loras) > 0:
self.pipeline = pipeline
for lora in loras:
@@ -442,17 +492,19 @@ class ControlNetPipeline():
if dtype is not None:
self.pipeline = self.pipeline.to(dtype)
controlnet = None # free up memory
controlnets = None
sd_models.copy_diffuser_options(self.pipeline, pipeline)
if opts.diffusers_offload_mode == 'none':
sd_models.move_model(self.pipeline, devices.device)
from modules.sd_models import set_diffuser_offload
set_diffuser_offload(self.pipeline, 'model')
sd_models.clear_caches()
sd_models.set_diffuser_offload(self.pipeline, 'model')
t1 = time.time()
debug_log(f'Control {what} pipeline: class={self.pipeline.__class__.__name__} time={t1-t0:.2f}')
def restore(self):
if self.pipeline is not None:
if self.pipeline is not None and hasattr(self.pipeline, 'unload_lora_weights'):
self.pipeline.unload_lora_weights()
self.pipeline = None
return self.orig_pipeline
+8
View File
@@ -20,3 +20,11 @@ def is_f1(model):
def is_sd3(model):
return is_compatible(model, pattern='StableDiffusion3Pipeline')
def is_qwen(model):
return is_compatible(model, pattern='Qwen')
def is_hunyuandit(model):
return is_compatible(model, pattern='HunyuanDiT')
+1 -1
View File
@@ -134,7 +134,7 @@ class Script(scripts_manager.Script):
app = get_app('buffalo_l')
from modules.face.faceid import face_id
processed_images = face_id(p, app=app, source_images=input_images, model=ip_model, override=ip_override, cache=ip_cache, scale=ip_strength, structure=ip_structure) # run faceid pipeline
processed = processing.Processed(p, images_list=processed_images, seed=p.seed, subseed=p.subseed, index_of_first_image=0) # manually created processed object
processed = processing.get_processed(p, images_list=processed_images, seed=p.seed, subseed=p.subseed, index_of_first_image=0) # manually created processed object
elif mode == 'PhotoMaker': # photomaker creates pipeline and triggers original process_images
from modules.face.insightface import get_app
app = get_app('buffalo_l')
+1 -1
View File
@@ -40,7 +40,7 @@ def create_ui(prompt, negative, styles, _overrides):
with gr.Accordion(label="Video", open=False):
with gr.Row():
mp4_codec = gr.Dropdown(label="FP codec", choices=['none', 'libx264'], value='libx264', type='value')
ui_common.create_refresh_button(mp4_codec, get_codecs)
ui_common.create_refresh_button(mp4_codec, get_codecs, elem_id="framepack_mp4_codec_refresh")
mp4_ext = gr.Textbox(label="FP format", value='mp4', elem_id="framepack_mp4_ext")
mp4_opt = gr.Textbox(label="FP options", value='crf:16', elem_id="framepack_mp4_ext")
with gr.Row():
+1 -1
View File
@@ -317,7 +317,7 @@ def img2img(id_task: str, state: str, mode: int,
p.is_batch = mode == 5
if p.is_batch:
process_batch(p, img2img_batch_files, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, args)
processed = processing.Processed(p, [], p.seed, "")
processed = processing.get_processed(p, [], p.seed, "")
else:
processed = scripts_manager.scripts_img2img.run(p, *args)
if processed is None:
+1 -1
View File
@@ -253,7 +253,7 @@ def create_ui():
gr.HTML('<h2>&nbspExtract currently loaded LoRA(s)<br></h2>')
with gr.Row():
loaded = gr.Textbox(placeholder="Press refresh to query loaded LoRA", label="Loaded LoRA", interactive=False)
create_refresh_button(loaded, lambda: None, lambda: {'value': loaded_lora_str()}, "testid")
create_refresh_button(loaded, lambda: None, lambda: {'value': loaded_lora_str()}, "lora_extract_refresh")
with gr.Group():
with gr.Row():
modules = gr.CheckboxGroup(label="Modules to extract", value=['unet'], choices=['te', 'unet'])
+2 -2
View File
@@ -20,7 +20,7 @@ def create_ui(prompt, negative, styles, overrides):
with gr.Row():
frames = gr.Slider(label='LTX frames', minimum=1, maximum=513, step=1, value=17, elem_id="ltx_frames")
seed = gr.Number(label='LTX seed', value=-1, elem_id="ltx_seed", container=True)
random_seed = ToolButton(ui_symbols.random, elem_id="ltx_random_seed")
random_seed = ToolButton(ui_symbols.random, elem_id="ltx_seed_random")
with gr.Accordion(open=False, label="Condition", elem_id='ltx_condition_accordion'):
condition_strength = gr.Slider(label='LTX condition strength', minimum=0.1, maximum=1.0, step=0.05, value=0.8, elem_id="ltx_condition_image_strength")
with gr.Tabs():
@@ -47,7 +47,7 @@ def create_ui(prompt, negative, styles, overrides):
mp4_interpolate = gr.Slider(label="LTX interpolation", minimum=0, maximum=10, value=0, step=1)
with gr.Row():
mp4_codec = gr.Dropdown(label="LTX codec", choices=['none', 'libx264'], value='libx264', type='value')
ui_common.create_refresh_button(mp4_codec, get_codecs)
ui_common.create_refresh_button(mp4_codec, get_codecs, elem_id="framepack_mp4_codec_refresh")
mp4_ext = gr.Textbox(label="LTX format", value='mp4', elem_id="framepack_mp4_ext")
mp4_opt = gr.Textbox(label="LTX options", value='crf:16', elem_id="framepack_mp4_ext")
with gr.Row():
+1 -1
View File
@@ -534,7 +534,7 @@ def create_segment_ui():
with gr.Row():
controls.append(gr.Checkbox(label="Live update", value=True, elem_id="control_mask_live_update"))
btn_mask = ui_components.ToolButton(value=ui_symbols.refresh, visible=True, elem_id="control_mask_refresh", )
btn_lama = ui_components.ToolButton(value=ui_symbols.image, visible=True, elem_id="control_mask_lama")
btn_lama = ui_components.ToolButton(value=ui_symbols.image, visible=True, elem_id="control_mask_remove")
with gr.Row():
controls.append(gr.Checkbox(label="Inpaint masked only", value=False, elem_id="control_mask_only", ))
controls.append(gr.Checkbox(label="Invert mask", value=False, elem_id="control_mask_invert"))
+2
View File
@@ -76,6 +76,8 @@ def get_model_type(pipe):
# hybrid models
elif 'Wan' in name:
model_type = 'wanai'
elif 'HDM-xut' in name:
model_type = 'hdm'
else:
model_type = name
return model_type
+3 -1
View File
@@ -210,7 +210,9 @@ def get_reference_opts(name: str, quiet=False):
# shared.log.error(f'Reference: model="{name}" not found')
return {}
if not quiet:
shared.log.debug(f'Reference: model="{name}" {model_opts}')
desc = model_opts.copy()
desc.pop('desc', None)
shared.log.debug(f'Reference: model="{name}" {desc}')
return model_opts
+1 -1
View File
@@ -47,7 +47,7 @@ def create_ui():
cache_state_dirname = gr.Textbox(value=None, visible=False)
with gr.Row():
model_dropdown = gr.Dropdown(label="Model", value="Please select model", choices=checkpoint_titles())
create_refresh_button(model_dropdown, refresh_checkpoints, {}, "onnx_cache_refresh_diffusers_model")
create_refresh_button(model_dropdown, refresh_checkpoints, {}, "onnx_cache_diffusers_model_refresh")
with gr.Row():
def remove_cache_onnx_converted(dirname: str):
shutil.rmtree(os.path.join(opts.onnx_cached_models_path, dirname))
+6 -6
View File
@@ -92,7 +92,7 @@ def rgb_to_ycbcr_tensor(image: torch.ByteTensor) -> torch.FloatTensor:
@devices.inference_context()
def ycbcr_tensor_to_rgb(ycbcr: torch.FloatTensor) -> torch.ByteTensor:
ycbcr_img = (ycbcr / 2)
ycbcr_img = ycbcr / 2
y = ycbcr_img[:,0,:,:].add_(0.5)
cb = ycbcr_img[:,1,:,:]
cr = ycbcr_img[:,2,:,:]
@@ -171,12 +171,12 @@ def process_image_input(images: PipelineImageInput) -> torch.ByteTensor:
img = torch.from_numpy(np.asarray(img).copy()).unsqueeze(0)
combined_images.append(img)
elif isinstance(img, np.ndarray):
if len(img.shape) == 3:
img = img.unsqueeze(0)
img = torch.from_numpy(img)
if img.ndim == 3:
img = img.unsqueeze(0)
combined_images.append(img)
elif isinstance(img, torch.Tensor):
if len(img.shape) == 3:
if img.ndim == 3:
img = img.unsqueeze(0)
combined_images.append(img)
else:
@@ -186,11 +186,11 @@ def process_image_input(images: PipelineImageInput) -> torch.ByteTensor:
combined_images = torch.from_numpy(np.asarray(images).copy()).unsqueeze(0)
elif isinstance(images, np.ndarray):
combined_images = torch.from_numpy(images)
if len(combined_images.shape) == 3:
if combined_images.ndim == 3:
combined_images = combined_images.unsqueeze(0)
elif isinstance(images, torch.Tensor):
combined_images = images
if len(combined_images.shape) == 3:
if combined_images.ndim == 3:
combined_images = combined_images.unsqueeze(0)
else:
raise RuntimeError(f"Invalid input! Given: {type(images)} should be in ('torch.Tensor', 'np.ndarray', 'PIL.Image.Image')")
+2 -2
View File
@@ -414,8 +414,8 @@ class YoloRestorer(Detailer):
with gr.Row():
detailers = gr.Dropdown(label="Detailer models", elem_id=f"{tab}_detailers", choices=list(self.list), value=shared.opts.detailer_models, multiselect=True, visible=True)
detailers_text = gr.Textbox(label="Detailer list", elem_id=f"{tab}_detailers_text", placeholder="Comma separated list of detailer models", lines=2, visible=False, interactive=True)
refresh_btn = ui_common.create_refresh_button(detailers, self.enumerate, lambda: {"choices": self.enumerate()}, 'yolo_refresh_models')
ui_mode = ui_components.ToolButton(value=ui_symbols.view)
refresh_btn = ui_common.create_refresh_button(detailers, self.enumerate, lambda: {"choices": self.enumerate()}, 'yolo_models_refresh')
ui_mode = ui_components.ToolButton(value=ui_symbols.view, elem_id=f'{tab}_yolo_models_list')
ui_mode.click(fn=self.change_mode, inputs=[detailers, detailers_text], outputs=[detailers, detailers_text, refresh_btn])
with gr.Row():
classes = gr.Textbox(label="Detailer classes", placeholder="Classes", elem_id=f"{tab}_detailer_classes")
+39 -22
View File
@@ -27,50 +27,59 @@ get_sampler_index = processing_helpers.get_sampler_index
validate_sample = processing_helpers.validate_sample
decode_first_stage = processing_helpers.decode_first_stage
images_tensor_to_samples = processing_helpers.images_tensor_to_samples
processed = None # last known processed results
class Processed:
def __init__(self, p: StableDiffusionProcessing, images_list, seed=-1, info=None, subseed=None, all_prompts=None, all_negative_prompts=None, all_seeds=None, all_subseeds=None, index_of_first_image=0, infotexts=None, comments=""):
self.images = images_list
self.sd_model_hash = getattr(shared.sd_model, 'sd_model_hash', '') if model_data.sd_model is not None else ''
self.prompt = p.prompt or ''
self.negative_prompt = p.negative_prompt or ''
self.seed = seed if seed != -1 else p.seed
self.subseed = subseed
self.subseed_strength = p.subseed_strength
self.info = info or create_infotext(p)
self.comments = comments or ''
self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0]
self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0]
self.styles = p.styles
self.images = images_list
self.width = p.width if hasattr(p, 'width') else (self.images[0].width if len(self.images) > 0 else 0)
self.height = p.height if hasattr(p, 'height') else (self.images[0].height if len(self.images) > 0 else 0)
self.sampler_name = p.sampler_name or ''
self.cfg_scale = p.cfg_scale if p.cfg_scale > 1 else None
self.cfg_end = p.cfg_end if p.cfg_end < 0 else None
self.image_cfg_scale = p.image_cfg_scale or 0
self.steps = p.steps or 0
self.batch_size = max(1, p.batch_size)
self.denoising_strength = p.denoising_strength
self.restore_faces = p.restore_faces or False
self.face_restoration_model = shared.opts.face_restoration_model if p.restore_faces else None
self.detailer = p.detailer_enabled or False
self.detailer_model = shared.opts.detailer_model if p.detailer_enabled else None
self.sd_model_hash = getattr(shared.sd_model, 'sd_model_hash', '') if model_data.sd_model is not None else ''
self.seed_resize_from_w = p.seed_resize_from_w
self.seed_resize_from_h = p.seed_resize_from_h
self.denoising_strength = p.denoising_strength
self.extra_generation_params = p.extra_generation_params
self.index_of_first_image = index_of_first_image
self.styles = p.styles
self.job_timestamp = shared.state.job_timestamp
self.clip_skip = p.clip_skip
self.eta = p.eta
self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0]
self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0]
self.seed = seed if seed != -1 else p.seed
self.subseed = subseed
self.seed = int(self.seed if type(self.seed) != list else self.seed[0]) if self.seed is not None else -1
self.subseed = int(self.subseed if type(self.subseed) != list else self.subseed[0]) if self.subseed is not None else -1
self.subseed_strength = p.subseed_strength
self.is_using_inpainting_conditioning = p.is_using_inpainting_conditioning
self.all_prompts = all_prompts or p.all_prompts or [self.prompt]
self.all_negative_prompts = all_negative_prompts or p.all_negative_prompts or [self.negative_prompt]
self.all_seeds = all_seeds or p.all_seeds or [self.seed]
self.all_subseeds = all_subseeds or p.all_subseeds or [self.subseed]
self.info = info or create_infotext(p)
self.infotexts = infotexts or [self.info]
self.comments = comments or ''
memstats.reset_stats()
def js(self):
@@ -113,6 +122,12 @@ class Processed:
return f'{self.__class__.__name__}: {self.__dict__}'
def get_processed(*args, **kwargs):
global processed # pylint: disable=global-statement
processed = Processed(*args, **kwargs)
return processed
def process_images(p: StableDiffusionProcessing) -> Processed:
timer.process.reset()
debug(f'Process images: {vars(p)}')
@@ -133,7 +148,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
p.override_settings.pop(k, None)
for k in p.override_settings.keys():
stored_opts[k] = shared.opts.data.get(k, None) or shared.opts.data_labels[k].default
processed = None
results = None
try:
# if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint
if p.override_settings.get('sd_model_checkpoint', None) is not None and sd_checkpoint.checkpoint_aliases.get(p.override_settings.get('sd_model_checkpoint')) is None:
@@ -196,11 +211,11 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
shared.log.debug(f'Torch profile: {profile_args}')
shared.profiler = torch.profiler.profile(**profile_args)
shared.profiler.start()
processed = process_images_inner(p)
results = process_images_inner(p)
errors.profile_torch(shared.profiler, 'Process')
else:
with context_hypertile_vae(p), context_hypertile_unet(p):
processed = process_images_inner(p)
results = process_images_inner(p)
finally:
script_callbacks.after_process_callback(p)
@@ -215,7 +230,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
if k == 'sd_vae':
sd_vae.reload_vae_weights()
timer.process.record('post')
return processed
return results
def process_init(p: StableDiffusionProcessing):
@@ -370,6 +385,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
p.init(p.all_prompts, p.all_seeds, p.all_subseeds)
debug(f'Processing inner: args={vars(p)}')
for n in range(p.n_iter):
if p.n_iter > 1:
shared.log.debug(f'Processing: batch={n+1} total={p.n_iter} progress={(n+1)/p.n_iter:.2f}')
shared.state.batch_no = n + 1
debug(f'Processing inner: iteration={n+1}/{p.n_iter}')
p.iteration = n
@@ -397,10 +414,10 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
samples = None
timer.process.record('init')
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner):
processed = p.scripts.process_images(p)
if processed is not None:
samples = processed.images
for script_image, script_infotext in zip(processed.images, processed.infotexts):
results = p.scripts.process_images(p)
if results is not None:
samples = results.images
for script_image, script_infotext in zip(results.images, results.infotexts):
output_images.append(script_image)
infotexts.append(script_infotext)
if samples is None:
@@ -451,7 +468,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if shared.opts.grid_save:
images.save_image(grid, p.outpath_grids, "", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=grid_info, p=p, grid=True) # main save grid
processed = Processed(
results = get_processed(
p,
images_list=output_images,
seed=p.all_seeds[0],
@@ -462,7 +479,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
infotexts=infotexts,
)
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner) and not (shared.state.interrupted or shared.state.skipped):
p.scripts.postprocess(p, processed)
p.scripts.postprocess(p, results)
timer.process.record('post')
p.ops = list(set(p.ops))
if not p.disable_extra_networks:
@@ -472,4 +489,4 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
devices.torch_gc(force=True, reason='final')
return processed
return results
+12 -5
View File
@@ -310,7 +310,7 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t
if 'Flex2' in model.__class__.__name__:
if len(getattr(p, 'init_images', [])) > 0:
args['inpaint_image'] = p.init_images[0] if isinstance(p.init_images, list) else p.init_images
args['inpaint_mask'] = Image.new('L', args['inpaint_image'].size, 1)
args['inpaint_mask'] = Image.new('L', args['inpaint_image'].size, int(p.denoising_strength * 255))
args['control_image'] = args['inpaint_image'].convert('L').convert('RGB') # will be interpreted as depth
args['control_strength'] = p.denoising_strength
args['width'] = p.width
@@ -358,8 +358,8 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t
task_kwargs = task_specific_kwargs(p, model)
pipe_args = getattr(p, 'task_args', {})
model_args = getattr(model, 'task_args', {})
task_kwargs.update(pipe_args)
task_kwargs.update(model_args)
task_kwargs.update(pipe_args or {})
task_kwargs.update(model_args or {})
if debug_enabled:
debug_log(f'Process task args: {task_kwargs}')
for k, v in task_kwargs.items():
@@ -382,8 +382,15 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t
if 'width' in possible and 'height' in possible:
vae_scale_factor = sd_vae.get_vae_scale_factor(model)
if isinstance(args['image'], torch.Tensor) or isinstance(args['image'], np.ndarray):
args['width'] = vae_scale_factor * args['image'].shape[-1]
args['height'] = vae_scale_factor * args['image'].shape[-2]
if args['image'].shape[-1] == 3: # nhwc
args['width'] = args['image'].shape[-2]
args['height'] = args['image'].shape[-3]
elif args['image'].shape[-3] == 3: # nchw
args['width'] = args['image'].shape[-1]
args['height'] = args['image'].shape[-2]
else: # assume latent
args['width'] = vae_scale_factor * args['image'].shape[-1]
args['height'] = vae_scale_factor * args['image'].shape[-2]
elif isinstance(args['image'], Image.Image):
args['width'] = args['image'].width
args['height'] = args['image'].height
+5
View File
@@ -223,6 +223,11 @@ def process_hires(p: processing.StableDiffusionProcessing, output):
shared.state.update('Upscale', 0, 1)
output.images = resize_hires(p, latents=output.images)
sd_hijack_hypertile.hypertile_set(p, hr=True)
elif torch.is_tensor(output.images) and output.images.shape[-1] == 3: # nhwc
if output.images.dim() == 3:
output.images = TF.to_pil_image(output.images.permute(2,0,1))
elif output.images.dim() == 4:
output.images = [TF.to_pil_image(output.images[i].permute(2,0,1)) for i in range(output.images.shape[0])]
strength = p.hr_denoising_strength if p.hr_denoising_strength > 0 else p.denoising_strength
if (p.hr_upscaler.lower().startswith('latent') or p.hr_force) and strength > 0:
-1
View File
@@ -191,7 +191,6 @@ else:
def set_blaslt_enabled(enabled: bool) -> None:
if enabled:
load_library_global("/opt/rocm/lib/libhipblaslt.so") # Preload hipBLASLt.
os.environ["HIPBLASLT_TENSILE_LIBPATH"] = blaslt_tensile_libpath
else:
os.environ["TORCH_BLAS_PREFER_HIPBLASLT"] = "0"
+2
View File
@@ -48,6 +48,8 @@ def guess_by_name(fn, current_guess):
return 'SegMoE'
elif 'hunyuandit' in fn.lower():
return 'HunyuanDiT'
elif 'hdm-xut' in fn.lower():
return 'hdm'
elif 'pixart-xl' in fn.lower():
return 'PixArt Alpha'
elif 'stable-diffusion-3' in fn.lower():
+7 -7
View File
@@ -6,11 +6,12 @@ from modules import shared, errors, timer, sd_models
def hijack_encode_prompt(*args, **kwargs):
shared.state.begin('TE')
t0 = time.time()
if 'max_sequence_length' in kwargs:
if 'max_sequence_length' in kwargs and kwargs['max_sequence_length'] is not None:
kwargs['max_sequence_length'] = max(kwargs['max_sequence_length'], os.environ.get('HIDREAM_MAX_SEQUENCE_LENGTH', 256))
# if hasattr(shared.sd_model, 'text_encoder') and shared.sd_model.text_encoder is not None:
# sd_models.move_model(shared.sd_model.text_encoder, devices.device)
try:
prompt = kwargs.get('prompt', None) or (args[0] if len(args) > 0 else None)
if prompt is not None:
shared.log.debug(f'Encode: prompt="{prompt}" hijack=True')
res = shared.sd_model.orig_encode_prompt(*args, **kwargs)
except Exception as e:
shared.log.error(f'Encode prompt: {e}')
@@ -18,15 +19,14 @@ def hijack_encode_prompt(*args, **kwargs):
res = None
t1 = time.time()
timer.process.add('te', t1-t0)
if hasattr(shared.sd_model, "maybe_free_model_hooks"):
shared.sd_model.maybe_free_model_hooks()
# if hasattr(shared.sd_model, "maybe_free_model_hooks"):
# shared.sd_model.maybe_free_model_hooks()
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model)
shared.state.end()
return res
def init_hijack(pipe):
if shared.opts.te_hijack and pipe is not None and not hasattr(pipe, 'orig_encode_prompt') and hasattr(pipe, 'encode_prompt'):
# shared.log.debug(f'Model: cls={pipe.__class__.__name__} hijack encode')
if pipe is not None and not hasattr(pipe, 'orig_encode_prompt') and hasattr(pipe, 'encode_prompt'):
pipe.orig_encode_prompt = pipe.encode_prompt
pipe.encode_prompt = hijack_encode_prompt
+64
View File
@@ -0,0 +1,64 @@
import os
import time
import torch
from modules import shared, sd_models, devices, timer, errors
debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
def hijack_vae_decode(*args, **kwargs):
shared.state.begin('VAE')
t0 = time.time()
res = None
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
try:
sd_models.move_model(shared.sd_model.vae, devices.device)
if torch.is_tensor(args[0]):
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
res = shared.sd_model.vae.orig_decode(latents, *args[1:], **kwargs)
t1 = time.time()
shared.log.debug(f'Decode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
else:
res = shared.sd_model.vae.orig_decode(*args, **kwargs)
except Exception as e:
shared.log.error(f'Decode: vae={shared.sd_model.vae.__class__.__name__} {e}')
errors.display(e, 'vae')
res = None
t1 = time.time()
timer.process.add('vae', t1-t0)
shared.state.end()
return res
def hijack_vae_encode(*args, **kwargs):
shared.state.begin('VAE')
t0 = time.time()
res = None
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
try:
sd_models.move_model(shared.sd_model.vae, devices.device)
if torch.is_tensor(args[0]):
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
res = shared.sd_model.vae.orig_encode(latents, *args[1:], **kwargs)
t1 = time.time()
shared.log.debug(f'Encode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
else:
res = shared.sd_model.vae.orig_encode(*args, **kwargs)
except Exception as e:
shared.log.error(f'Encode: vae={shared.sd_model.vae.__class__.__name__} {e}')
errors.display(e, 'vae')
res = None
t1 = time.time()
timer.process.add('vae', t1-t0)
shared.state.end()
return res
def init_hijack(pipe):
if pipe is not None and hasattr(pipe, 'vae') and hasattr(pipe.vae, 'decode') and not hasattr(pipe.vae, 'orig_decode'):
pipe.vae.orig_decode = pipe.vae.decode
pipe.vae.decode = hijack_vae_decode
if pipe is not None and hasattr(pipe, 'vae') and hasattr(pipe.vae, 'encode') and not hasattr(pipe.vae, 'orig_encode'):
pipe.vae.orig_encode = pipe.vae.encode
pipe.vae.encode = hijack_vae_encode
+61 -28
View File
@@ -57,22 +57,6 @@ i2i_pipes = [
]
def copy_diffuser_options(new_pipe, orig_pipe):
new_pipe.sd_checkpoint_info = getattr(orig_pipe, 'sd_checkpoint_info', None)
new_pipe.sd_model_checkpoint = getattr(orig_pipe, 'sd_model_checkpoint', None)
new_pipe.embedding_db = getattr(orig_pipe, 'embedding_db', None)
new_pipe.sd_model_hash = getattr(orig_pipe, 'sd_model_hash', None)
new_pipe.has_accelerate = getattr(orig_pipe, 'has_accelerate', False)
new_pipe.current_attn_name = getattr(orig_pipe, 'current_attn_name', None)
new_pipe.default_scheduler = getattr(orig_pipe, 'default_scheduler', None)
new_pipe.is_sdxl = getattr(orig_pipe, 'is_sdxl', False) # a1111 compatibility item
new_pipe.is_sd2 = getattr(orig_pipe, 'is_sd2', False)
new_pipe.is_sd1 = getattr(orig_pipe, 'is_sd1', True)
add_noise_pred_to_diffusers_callback(new_pipe)
if new_pipe.has_accelerate:
set_accelerate(new_pipe)
def set_huggingface_options():
if shared.opts.diffusers_to_gpu: # and model_type.startswith('Stable Diffusion'):
sd_hijack_accelerate.hijack_accelerate()
@@ -321,7 +305,7 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
elif model_type in ['AuraFlow']: # forced pipeline
from pipelines.model_auraflow import load_auraflow
sd_model = load_auraflow(checkpoint_info, diffusers_load_config)
allow_post_quant = True
allow_post_quant = False
elif model_type in ['FLUX']:
from pipelines.model_flux import load_flux
sd_model = load_flux(checkpoint_info, diffusers_load_config)
@@ -355,7 +339,7 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
sd_model = load_meissonic(checkpoint_info, diffusers_load_config)
allow_post_quant = True
elif model_type in ['OmniGen2']: # forced pipeline
from pipelines.model_omnigen2 import load_omnigen2
from pipelines.model_omnigen import load_omnigen2
sd_model = load_omnigen2(checkpoint_info, diffusers_load_config)
allow_post_quant = False
elif model_type in ['OmniGen']: # forced pipeline
@@ -397,7 +381,7 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
elif model_type in ['Kandinsky 2.2']:
from pipelines.model_kandinsky import load_kandinsky22
sd_model = load_kandinsky22(checkpoint_info, diffusers_load_config)
allow_post_quant = False
allow_post_quant = True
elif model_type in ['Kandinsky 3.0']:
from pipelines.model_kandinsky import load_kandinsky3
sd_model = load_kandinsky3(checkpoint_info, diffusers_load_config)
@@ -406,6 +390,10 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
from pipelines.model_nextstep import load_nextstep
sd_model = load_nextstep(checkpoint_info, diffusers_load_config) # pylint: disable=assignment-from-none
allow_post_quant = False
elif model_type in ['hdm']:
from pipelines.model_hdm import load_hdm
sd_model = load_hdm(checkpoint_info, diffusers_load_config)
allow_post_quant = False
except Exception as e:
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
if debug_load:
@@ -539,16 +527,34 @@ def load_diffuser_file(model_type, pipeline, checkpoint_info, diffusers_load_con
def set_overrides(sd_model, checkpoint_info):
if 'bigaspv25' in checkpoint_info.name.lower():
checkpoint_info_name = checkpoint_info.name.lower()
if 'bigaspv25' in checkpoint_info_name or 'nyaflow' in checkpoint_info_name:
scheduler_config = sd_model.scheduler.config
scheduler_config['prediction_type'] = 'flow_prediction'
scheduler_config['use_flow_sigmas'] = True
scheduler_config['beta_schedule'] = 'linear'
sd_model.scheduler = diffusers.UniPCMultistepScheduler.from_config(scheduler_config)
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="flow-prediction"')
if 'vpred' in checkpoint_info.name.lower() or 'v-pred' in checkpoint_info.name.lower():
elif 'vpred' in checkpoint_info_name or 'v-pred' in checkpoint_info_name or 'v_pred' in checkpoint_info_name:
scheduler_config = sd_model.scheduler.config
scheduler_config['prediction_type'] = 'v_prediction'
scheduler_config['rescale_betas_zero_snr'] = True
sd_model.scheduler = diffusers.EulerDiscreteScheduler.from_config(scheduler_config)
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="v-prediction"')
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="v-prediction" rescale=True')
elif checkpoint_info.path.lower().endswith('.safetensors'):
try:
from safetensors import safe_open
with safe_open(checkpoint_info.path, framework='pt') as f:
keys = f.keys()
if 'v_pred' in keys: # NoobAI VPred models added empty v_pred and ztsnr keys
scheduler_config = sd_model.scheduler.config
scheduler_config['prediction_type'] = 'v_prediction'
if 'ztsnr' in keys:
scheduler_config['rescale_betas_zero_snr'] = True
sd_model.scheduler = diffusers.EulerDiscreteScheduler.from_config(scheduler_config)
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="v-prediction" rescale={scheduler_config.get("rescale_betas_zero_snr", False)}')
except Exception as e:
shared.log.debug(f'Setting override from keys failed: {e}')
def set_defaults(sd_model, checkpoint_info):
@@ -854,6 +860,28 @@ def clean_diffuser_pipe(pipe):
pipe.register_to_config(**internal_dict)
def copy_diffuser_options(new_pipe, orig_pipe):
new_pipe.sd_checkpoint_info = getattr(orig_pipe, 'sd_checkpoint_info', None)
new_pipe.sd_model_checkpoint = getattr(orig_pipe, 'sd_model_checkpoint', None)
new_pipe.embedding_db = getattr(orig_pipe, 'embedding_db', None)
new_pipe.loaded_loras = getattr(orig_pipe, 'loaded_loras', {})
new_pipe.sd_model_hash = getattr(orig_pipe, 'sd_model_hash', None)
new_pipe.has_accelerate = getattr(orig_pipe, 'has_accelerate', False)
new_pipe.current_attn_name = getattr(orig_pipe, 'current_attn_name', None)
new_pipe.default_scheduler = getattr(orig_pipe, 'default_scheduler', None)
new_pipe.image_encoder = getattr(orig_pipe, 'image_encoder', None)
new_pipe.feature_extractor = getattr(orig_pipe, 'feature_extractor', None)
new_pipe.mask_processor = getattr(orig_pipe, 'mask_processor', None)
new_pipe.restore_pipeline = getattr(orig_pipe, 'restore_pipeline', None)
new_pipe.task_args = getattr(orig_pipe, 'task_args', None)
new_pipe.is_sdxl = getattr(orig_pipe, 'is_sdxl', False) # a1111 compatibility item
new_pipe.is_sd2 = getattr(orig_pipe, 'is_sd2', False)
new_pipe.is_sd1 = getattr(orig_pipe, 'is_sd1', True)
add_noise_pred_to_diffusers_callback(new_pipe)
if new_pipe.has_accelerate:
set_accelerate(new_pipe)
def backup_pipe_components(pipe):
if pipe is None:
return {}
@@ -1157,13 +1185,18 @@ def unload_model_weights(op='model'):
shared.log.debug(f'Unload {op}: {memory_stats()}')
def hf_auth_check(checkpoint_info):
def hf_auth_check(checkpoint_info, force:bool=False):
login = None
try:
if (checkpoint_info.path.endswith('.safetensors') and os.path.isfile(checkpoint_info.path)) or (os.path.exists(checkpoint_info.path) and os.path.isdir(checkpoint_info.path) and os.path.isfile(os.path.join(checkpoint_info.path, 'model_index.json'))): # skip check for already downloaded models
return True
except Exception:
pass
if not force:
try:
# skip check for single-file safetensors models
if (checkpoint_info.path.endswith('.safetensors') and os.path.isfile(checkpoint_info.path)):
return True
# skip check for local diffusers folders
if (os.path.exists(checkpoint_info.path) and os.path.isdir(checkpoint_info.path) and os.path.isfile(os.path.join(checkpoint_info.path, 'model_index.json'))):
return True
except Exception:
pass
try:
login = modelloader.hf_login()
repo_id = path_to_repo(checkpoint_info)
+22 -10
View File
@@ -203,18 +203,19 @@ class OffloadHook(accelerate.hooks.ModelHook):
return module
def pre_forward(self, module, *args, **kwargs):
if self.last_pre != id(module): # offload every other module first time when new module starts pre-forward
self.last_pre = id(module)
_id = id(module)
if self.last_pre != _id and not hasattr(module, "offload_never"): # offload every other module first time when new module starts pre-forward
self.last_pre = _id
if shared.opts.diffusers_offload_pre:
debug_move(f'Offload: type=balanced op=pre module={module.__class__.__name__}')
for pipe in get_pipe_variants():
for module_name in get_module_names(pipe):
module_instance = getattr(pipe, module_name, None)
module_cls = module_instance.__class__.__name__
if (module_cls != module.__class__.__name__) and (module_cls not in self.offload_never) and (not devices.same_device(module_instance.device, devices.cpu)):
if (_id != id(module_instance)) and (module_cls not in self.offload_never) and (not devices.same_device(module_instance.device, devices.cpu)):
apply_balanced_offload_to_module(module_instance, op='pre')
if not devices.same_device(module.device, devices.device):
if not devices.same_device(module.device, devices.device): # move-to-device
device_index = torch.device(devices.device).index
if device_index is None:
device_index = 0
@@ -233,6 +234,13 @@ class OffloadHook(accelerate.hooks.ModelHook):
module._hf_hook.execution_device = torch.device(devices.device) # pylint: disable=protected-access
module.balanced_offload_device_map = device_map
module.balanced_offload_max_memory = max_memory
if debug:
for pipe in get_pipe_variants():
for module_name in get_module_names(pipe):
module_instance = getattr(pipe, module_name, None)
shared.log.trace(f'Offload: type=balanced op=pre check module={module_instance.__class__.__name__} device={module_instance.device} dtype={module_instance.dtype}')
return args, kwargs
def post_forward(self, module, output):
@@ -292,7 +300,7 @@ def get_module_sizes(pipe=None, exclude=[]):
return modules
def move_module_to_cpu(module, op='unk'):
def move_module_to_cpu(module, op='unk', force:bool=False):
try:
module_name = getattr(module, "module_name", module.__class__.__name__)
module_size = offload_hook_instance.offload_map.get(module_name, offload_hook_instance.model_size())
@@ -301,7 +309,11 @@ def move_module_to_cpu(module, op='unk'):
prev_gpu = used_gpu
module_cls = module.__class__.__name__
op = f'{op}:skip'
if module_cls in offload_hook_instance.offload_never:
if force:
op = f'{op}:force'
module = module.to(devices.cpu)
used_gpu -= module_size
elif module_cls in offload_hook_instance.offload_never:
op = f'{op}:never'
elif module_cls in offload_hook_instance.offload_always:
op = f'{op}:always'
@@ -313,7 +325,7 @@ def move_module_to_cpu(module, op='unk'):
used_gpu -= module_size
if debug:
quant = getattr(module, "quantization_method", None)
debug_move(f'Offload: type=balanced op={op} gpu={prev_gpu:.3f}:{used_gpu:.3f} perc={perc_gpu:.2f} ram={used_ram:.3f} current={module.device} dtype={module.dtype} quant={quant} module={module_cls} size={module_size:.3f}')
debug_move(f'Offload: type=balanced op={op} gpu={prev_gpu:.3f}:{used_gpu:.3f} perc={perc_gpu:.2f}:{shared.opts.diffusers_offload_min_gpu_memory} ram={used_ram:.3f} current={module.device} dtype={module.dtype} quant={quant} module={module_cls} size={module_size:.3f}')
except Exception as e:
if 'out of memory' in str(e):
devices.torch_gc(fast=True, force=True, reason='oom')
@@ -325,7 +337,7 @@ def move_module_to_cpu(module, op='unk'):
errors.display(e, f'Offload: type=balanced op=apply module={getattr(module, "__name__", None)}')
def apply_balanced_offload_to_module(module, op="apply"):
def apply_balanced_offload_to_module(module, op="apply", force:bool=False):
module_name = getattr(module, "module_name", module.__class__.__name__)
network_layer_name = getattr(module, "network_layer_name", None)
device_map = getattr(module, "balanced_offload_device_map", None)
@@ -334,7 +346,7 @@ def apply_balanced_offload_to_module(module, op="apply"):
module = accelerate.hooks.remove_hook_from_module(module, recurse=True)
except Exception as e:
shared.log.warning(f'Offload remove hook: module={module_name} {e}')
move_module_to_cpu(module, op=op)
move_module_to_cpu(module, op=op, force=force)
try:
module = accelerate.hooks.add_hook_to_module(module, offload_hook_instance, append=True)
except Exception as e:
@@ -345,7 +357,7 @@ def apply_balanced_offload_to_module(module, op="apply"):
if device_map and max_memory:
module.balanced_offload_device_map = device_map
module.balanced_offload_max_memory = max_memory
module.offload_post = shared.sd_model_type in offload_post and shared.opts.te_hijack and module_name.startswith("text_encoder")
module.offload_post = shared.sd_model_type in offload_post and module_name.startswith("text_encoder")
if shared.opts.layerwise_quantization or getattr(module, 'quantization_method', None) == 'LayerWise':
model_quant.apply_layerwise(module, quiet=True) # need to reapply since hooks were removed/readded
devices.torch_gc(fast=True, force=True, reason='offload')
+1 -6
View File
@@ -48,11 +48,6 @@ def find_sampler_config(name):
return config
def visible_sampler_names():
visible_samplers = [x for x in all_samplers if x.name in shared.opts.show_samplers] if len(shared.opts.show_samplers) > 0 else all_samplers
return visible_samplers
def restore_default(model):
if model is None:
return None
@@ -131,7 +126,7 @@ def create_sampler(name, model):
def set_samplers():
global samplers # pylint: disable=global-statement
global samplers_for_img2img # pylint: disable=global-statement
samplers = visible_sampler_names()
samplers = all_samplers
# samplers_for_img2img = [x for x in samplers if x.name != "PLMS"]
samplers_for_img2img = samplers
samplers_map.clear()
+3
View File
@@ -18,6 +18,9 @@ vae_scale_override = {
def get_vae_scale_factor(model=None):
if not shared.sd_loaded:
vae_scale_factor = 8
return vae_scale_factor
patch_size = 1
if model is None:
model = shared.sd_model
+32 -13
View File
@@ -11,7 +11,7 @@ from diffusers.quantizers.quantization_config import QuantizationConfigMixin
from diffusers.utils import get_module_from_name
from modules import devices, shared
from .common import dtype_dict, use_tensorwise_fp8_matmul, quantized_matmul_dtypes, allowed_types, conv_types, conv_transpose_types
from .common import dtype_dict, use_tensorwise_fp8_matmul, allowed_types, conv_types, conv_transpose_types
from .dequantizer import dequantizer_dict
from .forward import get_forward_func
@@ -50,6 +50,7 @@ def quantize_weight(weight: torch.FloatTensor, reduction_axes: Union[int, List[i
def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_size=0, quant_conv=False, use_quantized_matmul=False, use_quantized_matmul_conv=False, dequantize_fp32=False, non_blocking=False, quantization_device=None, return_device=None, param_name=None): # pylint: disable=unused-argument
layer_class_name = layer.__class__.__name__
if layer_class_name in allowed_types:
num_of_groups = 1
is_conv_type = False
is_conv_transpose_type = False
is_linear_type = False
@@ -69,12 +70,9 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
group_channel_size = channel_size // layer.groups
use_quantized_matmul = False
if use_quantized_matmul_conv:
use_quantized_matmul = weights_dtype in quantized_matmul_dtypes and group_channel_size >= 32 and output_channel_size >= 32
use_quantized_matmul = group_channel_size >= 32 and output_channel_size >= 32
if use_quantized_matmul and not dtype_dict[weights_dtype]["is_integer"]:
use_quantized_matmul = output_channel_size % 16 == 0 and group_channel_size % 16 == 0
if use_quantized_matmul:
result_shape = layer.weight.shape
layer.weight.data = layer.weight.reshape(output_channel_size, -1)
elif layer_class_name in conv_transpose_types:
if not quant_conv:
return layer
@@ -90,9 +88,9 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
try:
output_channel_size, channel_size = layer.weight.shape
except Exception as e:
raise ValueError(f"SDNQ: layer_class_name={layer_class_name} layer_weight_shape={layer.weight.shape} weights_dtype={weights_dtype} unsupported") from e
raise ValueError(f"SDNQ: param_name={param_name} layer_class_name={layer_class_name} layer_weight_shape={layer.weight.shape} weights_dtype={weights_dtype} unsupported") from e
if use_quantized_matmul:
use_quantized_matmul = weights_dtype in quantized_matmul_dtypes and channel_size >= 32 and output_channel_size >= 32
use_quantized_matmul = channel_size >= 32 and output_channel_size >= 32
if use_quantized_matmul:
if dtype_dict[weights_dtype]["is_integer"]:
use_quantized_matmul = output_channel_size % 8 == 0 and channel_size % 8 == 0
@@ -100,14 +98,18 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
use_quantized_matmul = output_channel_size % 16 == 0 and channel_size % 16 == 0
if group_size == 0:
if is_linear_type:
if use_quantized_matmul and dtype_dict[weights_dtype]["num_bits"] >= 6:
group_size = -1
elif is_linear_type:
group_size = 2 ** (2 + dtype_dict[weights_dtype]["num_bits"])
else:
group_size = 2 ** (1 + dtype_dict[weights_dtype]["num_bits"])
elif use_quantized_matmul and dtype_dict[weights_dtype]["num_bits"] == 8:
group_size = -1 # override user value, re-quantizing 8bit into 8bit is pointless
elif group_size != -1 and not is_linear_type:
group_size = max(group_size // 2, 1)
if not use_quantized_matmul and group_size > 0:
if group_size > 0:
if group_size >= channel_size:
group_size = channel_size
num_of_groups = 1
@@ -159,12 +161,16 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
if zero_point is not None:
zero_point = zero_point.to(torch_dtype)
if use_quantized_matmul:
re_quantize_for_matmul = (num_of_groups > 1 or zero_point is not None)
if use_quantized_matmul and not re_quantize_for_matmul:
if is_conv_type:
result_shape = layer.weight.shape
layer.weight.data = layer.weight.reshape(output_channel_size, -1)
scale.transpose_(0,1)
layer.weight.transpose_(0,1)
if not dtype_dict[weights_dtype]["is_integer"]:
stride = layer.weight.stride()
if stride[0] > stride[1] and stride[1] == 1:
weight_stride = layer.weight.stride()
if not (weight_stride[0] == 1 and weight_stride[1] > 1):
layer.weight.data = layer.weight.t().contiguous().t()
if not use_tensorwise_fp8_matmul:
scale = scale.to(torch.float32)
@@ -178,6 +184,7 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
original_shape=original_shape,
weights_dtype=weights_dtype,
use_quantized_matmul=use_quantized_matmul,
re_quantize_for_matmul=re_quantize_for_matmul,
)
layer.weight.data = layer.sdnq_dequantizer.pack_weight(layer.weight).to(return_device, non_blocking=non_blocking)
layer.sdnq_dequantizer = layer.sdnq_dequantizer.to(return_device, non_blocking=non_blocking)
@@ -392,9 +399,15 @@ class SDNQQuantizer(DiffusersQuantizer):
devices.torch_gc(force=True, reason='sdnq')
return model
def get_cuda_warm_up_factor(self):
def get_accelerator_warm_up_factor(self):
return 32 // dtype_dict[self.quantization_config.weights_dtype]["num_bits"]
def get_cuda_warm_up_factor(self):
"""
needed for transformers compatibilty, returns self.get_accelerator_warm_up_factor
"""
return self.get_accelerator_warm_up_factor()
def update_tp_plan(self, config):
"""
needed for transformers compatibilty, no-op function
@@ -425,6 +438,12 @@ class SDNQQuantizer(DiffusersQuantizer):
"""
return param_name
def update_dtype(self, dtype: torch.dtype) -> torch.dtype:
"""
needed for transformers compatibilty, no-op function
"""
return dtype
@property
def is_trainable(self):
return False
+1 -5
View File
@@ -2,7 +2,7 @@
import os
import torch
from modules import devices, shared
from modules import shared
torch_version = float(torch.__version__[:3])
@@ -34,10 +34,6 @@ if hasattr(torch, "float8_e5m2fnuz"):
use_torch_compile = shared.opts.sdnq_dequantize_compile # this setting requires a full restart of the webui to apply
use_tensorwise_fp8_matmul = os.environ.get('SDNQ_USE_TENSORWISE_FP8_MATMUL', "1").lower() not in {"0", "false", "no"} # row-wise FP8 only exist on H100 hardware, sdnq will use software row-wise with tensorwise hardware with this setting
quantized_matmul_dtypes = ("int8", "int7", "int6", "int5", "int4", "int3", "int2", "float8_e4m3fn", "float8_e5m2")
if devices.backend in {"cpu", "openvino"}:
quantized_matmul_dtypes += ("float8_e4m3fnuz", "float8_e5m2fnuz")
linear_types = ("Linear",)
conv_types = ("Conv1d", "Conv2d", "Conv3d")
conv_transpose_types = ("ConvTranspose1d", "ConvTranspose2d", "ConvTranspose3d")
+64 -4
View File
@@ -1,5 +1,7 @@
# pylint: disable=redefined-builtin,no-member,protected-access
from typing import Tuple
import torch
from .common import dtype_dict, use_torch_compile
@@ -34,6 +36,34 @@ def dequantize_packed_int_symmetric(weight: torch.ByteTensor, scale: torch.Float
return dequantize_symmetric(unpack_int_symetric(weight, shape, weights_dtype, dtype=scale.dtype), scale, dtype, result_shape, skip_quantized_matmul=skip_quantized_matmul)
def quantize_int8(input: torch.FloatTensor, dim: int = -1) -> Tuple[torch.CharTensor, torch.FloatTensor]:
scale = torch.amax(input.abs(), dim=dim, keepdims=True).div_(127)
input = torch.div(input, scale).round_().clamp_(-128, 127).to(dtype=torch.int8)
return input, scale
def re_quantize_matmul_asymmetric(weight: torch.ByteTensor, scale: torch.FloatTensor, zero_point: torch.FloatTensor, result_shape: torch.Size) -> Tuple[torch.CharTensor, torch.FloatTensor]:
result = dequantize_asymmetric(weight, scale, zero_point, scale.dtype, result_shape)
if result.ndim > 2: # convs
result = result.flatten(1,-1)
return quantize_int8(result.t_(), dim=0)
def re_quantize_matmul_symmetric(weight: torch.CharTensor, scale: torch.FloatTensor, result_shape: torch.Size) -> Tuple[torch.CharTensor, torch.FloatTensor]:
result = dequantize_symmetric(weight, scale, scale.dtype, result_shape)
if result.ndim > 2: # convs
result = result.flatten(1,-1)
return quantize_int8(result.t_(), dim=0)
def re_quantize_matmul_packed_int_asymmetric(weight: torch.ByteTensor, scale: torch.FloatTensor, zero_point: torch.FloatTensor, shape: torch.Size, result_shape: torch.Size, weights_dtype: str) -> torch.FloatTensor:
return re_quantize_matmul_asymmetric(unpack_int_asymetric(weight, shape, weights_dtype), scale, zero_point, result_shape)
def re_quantize_matmul_packed_int_symmetric(weight: torch.ByteTensor, scale: torch.FloatTensor, shape: torch.Size, result_shape: torch.Size, weights_dtype: str) -> torch.FloatTensor:
return re_quantize_matmul_symmetric(unpack_int_symetric(weight, shape, weights_dtype, dtype=scale.dtype), scale, result_shape)
class AsymmetricWeightsDequantizer(torch.nn.Module):
def __init__(
self,
@@ -43,12 +73,14 @@ class AsymmetricWeightsDequantizer(torch.nn.Module):
result_shape: torch.Size,
original_shape: torch.Size,
weights_dtype: str,
use_quantized_matmul: bool = False,
**kwargs, # pylint: disable=unused-argument
):
super().__init__()
self.weights_dtype = weights_dtype
self.original_shape = original_shape
self.use_quantized_matmul = False
self.use_quantized_matmul = use_quantized_matmul
self.re_quantize_for_matmul = True
self.result_dtype = result_dtype
self.result_shape = result_shape
self.register_buffer("scale", scale)
@@ -57,6 +89,9 @@ class AsymmetricWeightsDequantizer(torch.nn.Module):
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
return weight.to(dtype=dtype_dict[self.weights_dtype]["torch_dtype"])
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
return re_quantize_matmul_asymmetric_compiled(weight, self.scale, self.zero_point, self.result_shape)
def forward(self, weight, **kwargs): # pylint: disable=unused-argument
return dequantize_asymmetric_compiled(weight, self.scale, self.zero_point, self.result_dtype, self.result_shape)
@@ -70,12 +105,14 @@ class SymmetricWeightsDequantizer(torch.nn.Module):
original_shape: torch.Size,
weights_dtype: str,
use_quantized_matmul: bool = False,
re_quantize_for_matmul: bool = False,
**kwargs, # pylint: disable=unused-argument
):
super().__init__()
self.weights_dtype = weights_dtype
self.original_shape = original_shape
self.use_quantized_matmul = use_quantized_matmul
self.re_quantize_for_matmul = re_quantize_for_matmul
self.result_dtype = result_dtype
self.result_shape = result_shape
self.register_buffer("scale", scale)
@@ -83,7 +120,11 @@ class SymmetricWeightsDequantizer(torch.nn.Module):
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
return weight.to(dtype=dtype_dict[self.weights_dtype]["torch_dtype"])
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
return re_quantize_matmul_symmetric_compiled(weight, self.scale, self.result_shape)
def forward(self, weight, skip_quantized_matmul=False, **kwargs): # pylint: disable=unused-argument
skip_quantized_matmul = skip_quantized_matmul and not self.re_quantize_for_matmul
return dequantize_symmetric_compiled(weight, self.scale, self.result_dtype, self.result_shape, skip_quantized_matmul=skip_quantized_matmul)
@@ -97,11 +138,13 @@ class PackedINTAsymmetricWeightsDequantizer(torch.nn.Module):
result_shape: torch.Size,
original_shape: torch.Size,
weights_dtype: str,
use_quantized_matmul: bool = False,
**kwargs, # pylint: disable=unused-argument
):
super().__init__()
self.weights_dtype = weights_dtype
self.use_quantized_matmul = False
self.use_quantized_matmul = use_quantized_matmul
self.re_quantize_for_matmul = True
self.original_shape = original_shape
self.quantized_weight_shape = quantized_weight_shape
self.result_dtype = result_dtype
@@ -112,6 +155,9 @@ class PackedINTAsymmetricWeightsDequantizer(torch.nn.Module):
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
return pack_int_asymetric(weight, self.weights_dtype)
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
return re_quantize_matmul_packed_int_asymmetric_compiled(weight, self.scale, self.zero_point, self.quantized_weight_shape, self.result_shape, self.weights_dtype)
def forward(self, weight, **kwargs): # pylint: disable=unused-argument
return dequantize_packed_int_asymmetric_compiled(weight, self.scale, self.zero_point, self.quantized_weight_shape, self.result_dtype, self.result_shape, self.weights_dtype)
@@ -126,12 +172,14 @@ class PackedINTSymmetricWeightsDequantizer(torch.nn.Module):
original_shape: torch.Size,
weights_dtype: str,
use_quantized_matmul: bool = False,
re_quantize_for_matmul: bool = False,
**kwargs, # pylint: disable=unused-argument
):
super().__init__()
self.weights_dtype = weights_dtype
self.original_shape = original_shape
self.use_quantized_matmul = use_quantized_matmul
self.re_quantize_for_matmul = re_quantize_for_matmul
self.quantized_weight_shape = quantized_weight_shape
self.result_dtype = result_dtype
self.result_shape = result_shape
@@ -140,7 +188,11 @@ class PackedINTSymmetricWeightsDequantizer(torch.nn.Module):
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
return pack_int_symetric(weight, self.weights_dtype)
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
return re_quantize_matmul_packed_int_symmetric_compiled(weight, self.scale, self.quantized_weight_shape, self.result_shape, self.weights_dtype)
def forward(self, weight, skip_quantized_matmul=False, **kwargs): # pylint: disable=unused-argument
skip_quantized_matmul = skip_quantized_matmul and not self.re_quantize_for_matmul
return dequantize_packed_int_symmetric_compiled(weight, self.scale, self.quantized_weight_shape, self.result_dtype, self.result_shape, self.weights_dtype, skip_quantized_matmul=skip_quantized_matmul)
@@ -159,8 +211,8 @@ dequantizer_dict = {
"uint4": PackedINTAsymmetricWeightsDequantizer,
"uint3": PackedINTAsymmetricWeightsDequantizer,
"uint2": PackedINTAsymmetricWeightsDequantizer,
"uint1": AsymmetricWeightsDequantizer,
"bool": AsymmetricWeightsDequantizer,
"uint1": PackedINTAsymmetricWeightsDequantizer,
"bool": PackedINTAsymmetricWeightsDequantizer,
"float8_e4m3fn": SymmetricWeightsDequantizer,
"float8_e4m3fnuz": SymmetricWeightsDequantizer,
"float8_e5m2": SymmetricWeightsDequantizer,
@@ -173,8 +225,16 @@ if use_torch_compile:
dequantize_symmetric_compiled = torch.compile(dequantize_symmetric, fullgraph=True, dynamic=False)
dequantize_packed_int_asymmetric_compiled = torch.compile(dequantize_packed_int_asymmetric, fullgraph=True, dynamic=False)
dequantize_packed_int_symmetric_compiled = torch.compile(dequantize_packed_int_symmetric, fullgraph=True, dynamic=False)
re_quantize_matmul_asymmetric_compiled = torch.compile(re_quantize_matmul_asymmetric, fullgraph=True, dynamic=False)
re_quantize_matmul_symmetric_compiled = torch.compile(re_quantize_matmul_symmetric, fullgraph=True, dynamic=False)
re_quantize_matmul_packed_int_asymmetric_compiled = torch.compile(re_quantize_matmul_packed_int_asymmetric, fullgraph=True, dynamic=False)
re_quantize_matmul_packed_int_symmetric_compiled = torch.compile(re_quantize_matmul_packed_int_symmetric, fullgraph=True, dynamic=False)
else:
dequantize_asymmetric_compiled = dequantize_asymmetric
dequantize_symmetric_compiled = dequantize_symmetric
dequantize_packed_int_asymmetric_compiled = dequantize_packed_int_asymmetric
dequantize_packed_int_symmetric_compiled = dequantize_packed_int_symmetric
re_quantize_matmul_asymmetric_compiled = re_quantize_matmul_asymmetric
re_quantize_matmul_symmetric_compiled = re_quantize_matmul_symmetric
re_quantize_matmul_packed_int_asymmetric_compiled = re_quantize_matmul_packed_int_asymmetric
re_quantize_matmul_packed_int_symmetric_compiled = re_quantize_matmul_packed_int_symmetric
+8 -4
View File
@@ -25,7 +25,9 @@ def conv_fp8_matmul(
input, input_scale = quantize_fp8_matmul_input(input)
if groups == 1:
result = torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=return_dtype).view(mm_output_shape)
if bias is not None and bias.dtype != torch.bfloat16:
bias = bias.to(dtype=torch.bfloat16)
result = torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=torch.bfloat16).view(mm_output_shape).to(return_dtype)
else:
scale = scale.view(groups, 1, scale.shape[1] // groups)
input_scale = input_scale.view(groups, input_scale.shape[0] // groups, 1)
@@ -34,12 +36,14 @@ def conv_fp8_matmul(
result = []
if bias is not None:
bias = bias.view(groups, bias.shape[0] // groups)
if bias.dtype != torch.bfloat16:
bias = bias.to(dtype=torch.bfloat16)
for i in range(groups):
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=bias[i], out_dtype=return_dtype))
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=bias[i], out_dtype=torch.bfloat16))
else:
for i in range(groups):
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=None, out_dtype=return_dtype))
result = torch.cat(result, dim=-1).view(mm_output_shape)
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=None, out_dtype=torch.bfloat16))
result = torch.cat(result, dim=-1).view(mm_output_shape).to(return_dtype)
if conv_type == 1:
result = result.transpose_(1,2)
+10 -4
View File
@@ -16,8 +16,8 @@ def conv_int8_matmul(
weight: torch.CharTensor,
bias: torch.FloatTensor,
scale: torch.FloatTensor,
result_shape: torch.Size,
quantized_weight_shape: torch.Size,
result_shape: torch.Size,
weights_dtype: str,
reversed_padding_repeated_twice: List[int],
padding_mode: str, conv_type: int,
@@ -57,11 +57,17 @@ def quantized_conv_forward_int8_matmul(self, input) -> torch.FloatTensor:
if torch.numel(input) / input.shape[2] < 32:
return self._conv_forward(input, self.sdnq_dequantizer(self.weight, skip_quantized_matmul=True), self.bias)
conv_type, stride, padding, dilation = get_conv_args(input.ndim, self.stride, self.padding, self.dilation)
if self.sdnq_dequantizer.re_quantize_for_matmul:
weight, scale = self.sdnq_dequantizer.re_quantize_matmul(self.weight)
quantized_weight_shape = None
else:
weight = self.weight
scale = self.sdnq_dequantizer.scale
quantized_weight_shape = getattr(self.sdnq_dequantizer, "quantized_weight_shape", None)
return conv_int8_matmul(
input, self.weight, self.bias,
self.sdnq_dequantizer.scale,
input, weight, self.bias,
scale, quantized_weight_shape,
self.sdnq_dequantizer.result_shape,
getattr(self.sdnq_dequantizer, "quantized_weight_shape", None),
self.sdnq_dequantizer.weights_dtype,
self._reversed_padding_repeated_twice,
self.padding_mode, conv_type,
+4 -2
View File
@@ -8,7 +8,7 @@ from ...common import use_torch_compile # noqa: TID252
def quantize_fp8_matmul_input(input: torch.FloatTensor) -> Tuple[torch.Tensor, torch.FloatTensor]:
input = input.flatten(0,-2).contiguous().to(dtype=torch.float32)
input = input.flatten(0,-2).to(dtype=torch.float32)
input_scale = torch.amax(input.abs(), dim=-1, keepdims=True).div_(448)
input = torch.div(input, input_scale).clamp_(-448, 448).to(dtype=torch.float8_e4m3fn)
return input, input_scale
@@ -23,7 +23,9 @@ def fp8_matmul(
return_dtype = input.dtype
output_shape = (*input.shape[:-1], weight.shape[-1])
input, input_scale = quantize_fp8_matmul_input(input)
return torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=return_dtype).view(output_shape)
if bias is not None and bias.dtype != torch.bfloat16:
bias = bias.to(dtype=torch.bfloat16)
return torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=torch.bfloat16).view(output_shape).to(return_dtype)
def quantized_linear_forward_fp8_matmul(self, input: torch.FloatTensor) -> torch.FloatTensor:
@@ -9,7 +9,7 @@ from ...dequantizer import dequantize_symmetric, dequantize_symmetric_with_bias
def quantize_fp8_matmul_input_tensorwise(input: torch.FloatTensor, scale: torch.FloatTensor) -> Tuple[torch.Tensor, torch.FloatTensor]:
input = input.flatten(0,-2).contiguous().to(dtype=scale.dtype)
input = input.flatten(0,-2).to(dtype=scale.dtype)
input_scale = torch.amax(input.abs(), dim=-1, keepdims=True).div_(448)
input = torch.div(input, input_scale).clamp_(-448, 448).to(dtype=torch.float8_e4m3fn)
scale = torch.mul(input_scale, scale)
+11 -6
View File
@@ -1,18 +1,16 @@
# pylint: disable=relative-beyond-top-level,redefined-builtin,protected-access
from typing import Tuple
import torch
from ...common import use_torch_compile # noqa: TID252
from ...packed_int import unpack_int_symetric # noqa: TID252
from ...dequantizer import dequantize_symmetric, dequantize_symmetric_with_bias # noqa: TID252
from ...dequantizer import quantize_int8, dequantize_symmetric, dequantize_symmetric_with_bias # noqa: TID252
def quantize_int8_matmul_input(input: torch.FloatTensor, scale: torch.FloatTensor) -> Tuple[torch.CharTensor, torch.FloatTensor]:
input = input.flatten(0,-2).contiguous().to(dtype=scale.dtype)
input_scale = torch.amax(input.abs(), dim=-1, keepdims=True).div_(127)
input = torch.div(input, input_scale).round_().clamp_(-128, 127).to(dtype=torch.int8)
input = input.flatten(0,-2).to(dtype=scale.dtype)
input, input_scale = quantize_int8(input, dim=-1)
scale = torch.mul(input_scale, scale)
if scale.dtype == torch.float16: # fp16 will overflow
scale = scale.to(dtype=torch.float32)
@@ -41,7 +39,14 @@ def int8_matmul(
def quantized_linear_forward_int8_matmul(self, input: torch.FloatTensor) -> torch.FloatTensor:
if torch.numel(input) / input.shape[-1] < 32:
return torch.nn.functional.linear(input, self.sdnq_dequantizer(self.weight, skip_quantized_matmul=True), self.bias)
return int8_matmul(input, self.weight, self.bias, self.sdnq_dequantizer.scale, getattr(self.sdnq_dequantizer, "quantized_weight_shape", None), self.sdnq_dequantizer.weights_dtype)
if self.sdnq_dequantizer.re_quantize_for_matmul:
weight, scale = self.sdnq_dequantizer.re_quantize_matmul(self.weight)
quantized_weight_shape = None
else:
weight = self.weight
scale = self.sdnq_dequantizer.scale
quantized_weight_shape = getattr(self.sdnq_dequantizer, "quantized_weight_shape", None)
return int8_matmul(input, weight, self.bias, scale, quantized_weight_shape, self.sdnq_dequantizer.weights_dtype)
if use_torch_compile:
+34
View File
@@ -122,6 +122,21 @@ def pack_uint2(tensor: torch.ByteTensor) -> torch.ByteTensor:
return packed_tensor
def pack_uint1(tensor: torch.Tensor) -> torch.Tensor:
packed_tensor = tensor.contiguous().reshape(-1, 8)
packed_tensor = torch.bitwise_or(
torch.bitwise_or(
torch.bitwise_or(packed_tensor[:, 0], torch.bitwise_left_shift(packed_tensor[:, 1], 1)),
torch.bitwise_or(torch.bitwise_left_shift(packed_tensor[:, 2], 2), torch.bitwise_left_shift(packed_tensor[:, 3], 3))
),
torch.bitwise_or(
torch.bitwise_or(torch.bitwise_left_shift(packed_tensor[:, 4], 4), torch.bitwise_left_shift(packed_tensor[:, 5], 5)),
torch.bitwise_or(torch.bitwise_left_shift(packed_tensor[:, 6], 6), torch.bitwise_left_shift(packed_tensor[:, 7], 7))
),
)
return packed_tensor
def unpack_uint7(packed_tensor: torch.ByteTensor, shape: torch.Size) -> torch.ByteTensor:
result = torch.stack(
(
@@ -246,6 +261,23 @@ def unpack_uint2(packed_tensor: torch.ByteTensor, shape: torch.Size) -> torch.By
return result
def unpack_uint1(packed_tensor: torch.Tensor, shape: torch.Size) -> torch.Tensor:
result = torch.stack(
(
torch.bitwise_and(packed_tensor, 1),
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 1), 1),
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 2), 1),
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 3), 1),
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 4), 1),
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 5), 1),
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 6), 1),
torch.bitwise_right_shift(packed_tensor, 7),
),
dim=-1
).reshape(shape)
return result
packed_int_function_dict = {
"int7": {"pack": pack_uint7, "unpack": unpack_uint7},
"int6": {"pack": pack_uint6, "unpack": unpack_uint6},
@@ -259,4 +291,6 @@ packed_int_function_dict = {
"uint4": {"pack": pack_uint4, "unpack": unpack_uint4},
"uint3": {"pack": pack_uint3, "unpack": unpack_uint3},
"uint2": {"pack": pack_uint2, "unpack": unpack_uint2},
"uint1": {"pack": pack_uint1, "unpack": unpack_uint1},
"bool": {"pack": pack_uint1, "unpack": unpack_uint1},
}
+7 -9
View File
@@ -160,14 +160,14 @@ options_templates.update(options_section(('model_options', "Model Options"), {
"model_h1_sep": OptionInfo("<h2>HiDream</h2>", "", gr.HTML),
"model_h1_llama_repo": OptionInfo("Default", "LLama repo", gr.Textbox),
"model_wan_sep": OptionInfo("<h2>WanAI</h2>", "", gr.HTML),
"model_wan_stage": OptionInfo("first", "Processing stage", gr.Radio, {"choices": ['high noise', 'low noise', 'combined'] }),
"model_wan_stage": OptionInfo("low noise", "Processing stage", gr.Radio, {"choices": ['high noise', 'low noise', 'combined'] }),
"model_wan_boundary": OptionInfo(0.85, "Stage boundary ratio", gr.Slider, {"minimum": 0, "maximum": 1.0, "step": 0.05 }),
}))
options_templates.update(options_section(('offload', "Model Offloading"), {
"offload_sep": OptionInfo("<h2>Model Offloading</h2>", "", gr.HTML),
"diffusers_offload_mode": OptionInfo(startup_offload_mode, "Model offload mode", gr.Radio, {"choices": ['none', 'balanced', 'group', 'model', 'sequential']}),
"diffusers_offload_pre": OptionInfo(False, "Offload during pre-forward"),
"diffusers_offload_pre": OptionInfo(True, "Offload during pre-forward"),
"diffusers_offload_nonblocking": OptionInfo(False, "Non-blocking move operations"),
"diffusers_offload_min_gpu_memory": OptionInfo(startup_offload_min_gpu, "Balanced offload GPU low watermark", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01 }),
"diffusers_offload_max_gpu_memory": OptionInfo(startup_offload_max_gpu, "Balanced offload GPU high watermark", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.01 }),
@@ -251,7 +251,6 @@ options_templates.update(options_section(('text_encoder', "Text Encoder"), {
"sd_textencoder_cache_size": OptionInfo(4, "Text encoder cache size", gr.Slider, {"minimum": 0, "maximum": 16, "step": 1}),
"sd_textencder_linebreak": OptionInfo(True, "Use line break as prompt segment marker", gr.Checkbox),
"diffusers_zeros_prompt_pad": OptionInfo(False, "Use zeros for prompt padding", gr.Checkbox),
"te_hijack": OptionInfo(True, "Offload after prompt encode", gr.Checkbox),
"te_optional_sep": OptionInfo("<h2>Optional</h2>", "", gr.HTML),
"te_shared_t5": OptionInfo(True, "T5: Use shared instance of text encoder"),
"te_pooled_embeds": OptionInfo(False, "SDXL: Use weighted pooled embeds"),
@@ -520,7 +519,7 @@ options_templates.update(options_section(('image-metadata', "Image Metadata"), {
options_templates.update(options_section(('ui', "User Interface"), {
"themes_sep_ui": OptionInfo("<h2>Theme options</h2>", "", gr.HTML),
"theme_type": OptionInfo("Standard", "Theme type", gr.Radio, {"choices": ["Modern", "Standard", "None"]}),
"theme_type": OptionInfo("Modern", "Theme type", gr.Radio, {"choices": ["Modern", "Standard", "None"]}),
"theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}),
"gradio_theme": OptionInfo("black-teal", "UI theme", gr.Dropdown, lambda: {"choices": theme.list_themes()}, refresh=theme.refresh_themes),
@@ -533,10 +532,10 @@ options_templates.update(options_section(('ui', "User Interface"), {
"subpath": OptionInfo("", "Mount URL subpath"),
"ui_request_timeout": OptionInfo(120000, "UI request timeout", gr.Slider, {"minimum": 1000, "maximum": 300000, "step": 10}),
"cards_sep_ui": OptionInfo("<h2>Card options</h2>", "", gr.HTML),
"extra_networks_card_size": OptionInfo(140, "UI card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
"extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, {"choices": ["cover", "inline", "sidebar"]}),
"extra_networks_card_square": OptionInfo(True, "UI disable variable aspect ratio"),
"cards_sep_ui": OptionInfo("<h2>Networks panel</h2>", "", gr.HTML),
"extra_networks_card_size": OptionInfo(140, "Network card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
"extra_networks_card_cover": OptionInfo("sidebar", "Network panel position", gr.Radio, {"choices": ["cover", "inline", "sidebar"]}),
"extra_networks_card_square": OptionInfo(True, "Disable variable aspect ratio"),
"other_sep_ui": OptionInfo("<h2>Other...</h2>", "", gr.HTML),
"ui_locale": OptionInfo("Auto", "UI locale", gr.Dropdown, lambda: {"choices": theme.list_locales()}),
@@ -556,7 +555,6 @@ options_templates.update(options_section(('ui', "User Interface"), {
"return_mask_composite": OptionInfo(False, "Inpainting include masked composite in results"),
"send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface", gr.Checkbox, {"visible": False}),
"send_size": OptionInfo(False, "Send size when sending prompt or image to another interface", gr.Checkbox, {"visible": False}),
}))
options_templates.update(options_section(('live-preview', "Live Previews"), {
+2
View File
@@ -20,10 +20,12 @@ def get_default_modes(cmd_opts, mem_stat):
cmd_opts.medvram = True # VAE Tiling and other stuff
default_offload_mode = "balanced"
default_diffusers_offload_min_gpu_memory = 0
default_diffusers_offload_always = ', '.join(['T5EncoderModel', 'UMT5EncoderModel'])
log.info(f"Device detect: memory={gpu_memory:.1f} default=balanced optimization=medvram")
elif gpu_memory >= 24:
default_offload_mode = "balanced"
default_diffusers_offload_max_gpu_memory = 0.8
default_diffusers_offload_always = ', '.join(['T5EncoderModel', 'UMT5EncoderModel'])
default_diffusers_offload_never = ', '.join(['CLIPTextModel', 'CLIPTextModelWithProjection', 'AutoencoderKL'])
log.info(f"Device detect: memory={gpu_memory:.1f} default=balanced optimization=highvram")
else:
+1
View File
@@ -54,6 +54,7 @@ pipelines = {
'SegMoE': getattr(diffusers, 'DiffusionPipeline', None),
'FLite': getattr(diffusers, 'DiffusionPipeline', None),
'Bria': getattr(diffusers, 'DiffusionPipeline', None),
'hdm': getattr(diffusers, 'DiffusionPipeline', None),
}
+1 -1
View File
@@ -77,7 +77,7 @@ def create_ui():
with gr.Tab("CLiP Interrogate", elem_id='tab_clip_interrogate'):
with gr.Row():
clip_model = gr.Dropdown([], value=shared.opts.interrogate_clip_model, label='CLiP model', elem_id='clip_clip_model')
ui_common.create_refresh_button(clip_model, openclip.refresh_clip_models, lambda: {"choices": openclip.refresh_clip_models()}, 'clip_refresh_models')
ui_common.create_refresh_button(clip_model, openclip.refresh_clip_models, lambda: {"choices": openclip.refresh_clip_models()}, 'clip_models_refresh')
blip_model = gr.Dropdown(list(openclip.caption_models), value=shared.opts.interrogate_blip_model, label='Caption model', elem_id='btN_clip_blip_model')
clip_mode = gr.Dropdown(openclip.caption_types, label='Mode', value='fast', elem_id='clip_clip_mode')
with gr.Accordion(label='Advanced options', open=False, visible=True):
+28 -6
View File
@@ -68,15 +68,18 @@ def delete_files(js_data, files, all_files, index):
start_index = index
deleted = []
all_files = [f.split('/file=')[1] if 'file=' in f else f for f in all_files] if isinstance(all_files, list) else []
all_files = [os.path.normpath(f) for f in all_files]
for _image_index, filedata in enumerate(files, start_index):
try:
fn = filedata['name']
fn = os.path.normpath(filedata['name'])
if os.path.exists(fn) and os.path.isfile(fn):
deleted.append(fn)
os.remove(fn)
if fn in all_files:
all_files.remove(fn)
shared.log.info(f'Delete: image="{fn}"')
shared.log.info(f'Delete: image="{fn}"')
else:
shared.log.warning(f'Delete: image="{fn}" ui mismatch')
base, _ext = os.path.splitext(fn)
desc = f'{base}.txt'
if os.path.exists(desc) and os.path.isfile(desc):
@@ -333,7 +336,7 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args = No
return gr.update(**args)
refresh_button = ui_components.ToolButton(value=ui_symbols.refresh, elem_id=elem_id, visible=visible)
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component])
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component], show_progress=False)
return refresh_button
@@ -344,7 +347,26 @@ def create_override_inputs(tab): # pylint: disable=unused-argument
return override_settings
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None):
def reuse_seed(seed_component: gr.Number, reuse_button: gr.Button, subseed:bool=False):
def reuse_click(selected_gallery_index):
selected_gallery_index = int(selected_gallery_index)
from modules import processing
if processing.processed is None:
seed = -1
elif selected_gallery_index >= len(processing.processed.all_seeds):
selected_gallery_index -= len(processing.processed.images) - len(processing.processed.all_seeds) # if we have more images than seeds it is likely the grid image
seed = processing.processed.all_seeds[selected_gallery_index] if not subseed else processing.processed.all_subseeds[selected_gallery_index]
elif len(processing.processed.all_seeds) > 0:
seed = processing.processed.all_seeds[0] if not subseed else processing.processed.all_subseeds[0]
else:
seed = -1
shared.log.debug(f'Reuse seed: index={selected_gallery_index} seed={seed} subseed={subseed}')
return seed
reuse_button.click(fn=reuse_click, _js="selected_gallery_index", inputs=[seed_component], outputs=[seed_component], show_progress=False)
def connect_reuse_seed(seed: gr.Number, reuse_seed_btn: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None):
""" Connects a 'reuse (sub)seed' button's click event so that it copies last used
(sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
@@ -372,9 +394,9 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
return [restore_seed, gr_show(False)]
dummy_component = gr.Number(visible=False, value=0)
if subseed_strength is None:
reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component])
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component])
else:
reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength])
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength])
def update_token_counter(text):
+2 -33
View File
@@ -9,9 +9,7 @@ class FormComponent:
gr.Dropdown.get_expected_parent = FormComponent.get_expected_parent
class ToolButton(FormComponent, gr.Button):
"""Small button with single emoji as text, fits inside gradio forms"""
class ToolButton(FormComponent, gr.Button): # small button with single emoji as text
def __init__(self, *args, **kwargs):
classes = kwargs.pop("elem_classes", [])
super().__init__(*args, elem_classes=["tool", *classes], **kwargs)
@@ -19,66 +17,49 @@ class ToolButton(FormComponent, gr.Button):
def get_block_name(self):
return "button"
### unused components below for compatibility with extensions ###
class FormRow(FormComponent, gr.Row): # unused
"""Same as gr.Row but fits inside gradio forms"""
def get_block_name(self):
return "row"
class FormColumn(FormComponent, gr.Column): # unused
"""Same as gr.Column but fits inside gradio forms"""
def get_block_name(self):
return "column"
class FormGroup(FormComponent, gr.Group): # unused
"""Same as gr.Row but fits inside gradio forms"""
def get_block_name(self):
return "group"
class FormHTML(FormComponent, gr.HTML): # unused
"""Same as gr.HTML but fits inside gradio forms"""
def get_block_name(self):
return "html"
class FormColorPicker(FormComponent, gr.ColorPicker): # unused
"""Same as gr.ColorPicker but fits inside gradio forms"""
def get_block_name(self):
return "colorpicker"
class DropdownMulti(FormComponent, gr.Dropdown): # unused
"""Same as gr.Dropdown but always multiselect"""
def __init__(self, **kwargs):
super().__init__(multiselect=True, **kwargs)
def get_block_name(self):
return "dropdown"
class DropdownEditable(FormComponent, gr.Dropdown): # unused
"""Same as gr.Dropdown but allows editing value"""
def __init__(self, **kwargs):
super().__init__(allow_custom_value=True, **kwargs)
def get_block_name(self):
return "dropdown"
class InputAccordion(gr.Checkbox): # unused
"""A gr.Accordion that can be used as an input - returns True if open, False if closed.
Actaully just a hidden checkbox, but creates an accordion that follows and is followed by the state of the checkbox.
"""
global_index = 0
def __init__(self, value, **kwargs):
self.accordion_id = kwargs.get('elem_id')
if self.accordion_id is None:
@@ -97,15 +78,6 @@ class InputAccordion(gr.Checkbox): # unused
self.accordion = gr.Accordion(**kwargs_accordion)
def extra(self):
"""Allows you to put something into the label of the accordion.
Use it like this:
```
with InputAccordion(False, label="Accordion") as acc:
with acc.extra():
FormHTML(value="hello", min_width=0)
...
```
"""
return gr.Column(elem_id=self.accordion_id + '-extra', elem_classes='input-accordion-extra', min_width=0)
def __enter__(self):
@@ -120,11 +92,8 @@ class InputAccordion(gr.Checkbox): # unused
class ResizeHandleRow(gr.Row): # unusued
"""Same as gr.Row but fits inside gradio forms"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.elem_classes.append("resize-handle-row")
def get_block_name(self):
return "row"
+32 -30
View File
@@ -157,7 +157,9 @@ def create_ui(_blocks: gr.Blocks=None):
batch_count, batch_size = ui_sections.create_batch_inputs('control', accordion=True)
seed, _reuse_seed, subseed, _reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control', reuse_visible=False)
seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control')
ui_common.reuse_seed(seed, reuse_seed, subseed=False)
ui_common.reuse_seed(subseed, reuse_subseed, subseed=True)
mask_controls = masking.create_segment_ui()
@@ -244,17 +246,17 @@ def create_ui(_blocks: gr.Blocks=None):
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
model_id = gr.Dropdown(label="ControlNet", choices=controlnet.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
ui_common.create_refresh_button(model_id, controlnet.list_models, lambda: {"choices": controlnet.list_models(refresh=True)}, f'refresh_controlnet_models_{i}')
ui_common.create_refresh_button(model_id, controlnet.list_models, lambda: {"choices": controlnet.list_models(refresh=True)}, f'controlnet_models_{i}_refresh')
control_mode = gr.Dropdown(label="CN Mode", choices=['default'], value='default', visible=False, elem_id=f'control_unit-{i}-mode')
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=2.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
control_start = gr.Slider(label="CN Start", minimum=0.0, maximum=1.0, step=0.05, value=0, elem_id=f'control_unit-{i}-start')
control_end = gr.Slider(label="CN End", minimum=0.0, maximum=1.0, step=0.05, value=1.0, elem_id=f'control_unit-{i}-end')
control_tile = gr.Dropdown(label="CN Tiles", choices=[x.strip() for x in shared.opts.control_tiles.split(',') if 'x' in x], value='1x1', visible=False, elem_id=f'control_unit-{i}-tile')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
image_preview = gr.Image(label="Input", type="pil", height=128, width=128, visible=False, interactive=True, show_label=False, show_download_button=False, container=False, elem_id=f'control_unit-{i}-override')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'controlnet_unit-{i}-reset')
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'controlnet_unit-{i}-upload')
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'controlnet_unit-{i}-reuse')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'controlnet_unit-{i}-preview')
image_preview = gr.Image(label="Input", type="pil", height=128, width=128, visible=False, interactive=True, show_label=False, show_download_button=False, container=False, elem_id=f'controlnet_unit-{i}-override')
controlnet_ui_units.append(unit_ui)
units.append(unit.Unit(
unit_type = 'controlnet',
@@ -297,13 +299,13 @@ def create_ui(_blocks: gr.Blocks=None):
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
model_id = gr.Dropdown(label="Adapter", choices=t2iadapter.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
ui_common.create_refresh_button(model_id, t2iadapter.list_models, lambda: {"choices": t2iadapter.list_models(refresh=True)}, f'refresh_adapter_models_{i}')
ui_common.create_refresh_button(model_id, t2iadapter.list_models, lambda: {"choices": t2iadapter.list_models(refresh=True)}, f'adapter_models_{i}_refresh')
model_strength = gr.Slider(label="T2I Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'adapter_unit-{i}-reset')
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'adapter_unit-{i}-upload')
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'adapter_unit-{i}-reuse')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'adapter_unit-{i}-preview')
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'adapter_unit-{i}-override')
adapter_ui_units.append(unit_ui)
units.append(unit.Unit(
unit_type = 't2i adapter',
@@ -342,15 +344,15 @@ def create_ui(_blocks: gr.Blocks=None):
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
model_id = gr.Dropdown(label="ControlNet-XS", choices=xs.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
ui_common.create_refresh_button(model_id, xs.list_models, lambda: {"choices": xs.list_models(refresh=True)}, f'refresh_xs_models_{i}')
ui_common.create_refresh_button(model_id, xs.list_models, lambda: {"choices": xs.list_models(refresh=True)}, f'xs_models_{i}_refresh')
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
control_start = gr.Slider(label="Start", minimum=0.0, maximum=1.0, step=0.05, value=0, elem_id=f'control_unit-{i}-start')
control_end = gr.Slider(label="End", minimum=0.0, maximum=1.0, step=0.05, value=1.0, elem_id=f'control_unit-{i}-end')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'controlnetxs_unit-{i}-reset')
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'controlnetxs_unit-{i}-upload')
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'controlnetxs_unit-{i}-reuse')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'controlnetxs_unit-{i}-preview')
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'controlnetxs_unit-{i}-override')
controlnetxs_ui_units.append(unit_ui)
units.append(unit.Unit(
unit_type = 'xs',
@@ -390,13 +392,13 @@ def create_ui(_blocks: gr.Blocks=None):
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
model_id = gr.Dropdown(label="Model", choices=lite.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
ui_common.create_refresh_button(model_id, lite.list_models, lambda: {"choices": lite.list_models(refresh=True)}, f'refresh_lite_models_{i}')
ui_common.create_refresh_button(model_id, lite.list_models, lambda: {"choices": lite.list_models(refresh=True)}, f'lite_models_{i}_refresh')
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'lite_unit-{i}-reset')
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'lite_unit-{i}-upload')
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'lite_unit-{i}-reuse')
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'lite_unit-{i}-override')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'lite_unit-{i}-preview')
lite_ui_units.append(unit_ui)
units.append(unit.Unit(
unit_type = 'lite',
@@ -436,11 +438,11 @@ def create_ui(_blocks: gr.Blocks=None):
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
model_id = gr.Dropdown(label="Reference", choices=reference.list_models(), value='Reference', visible=False, elem_id=f'control_unit-{i}-model_name')
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, visible=False, elem_id=f'control_unit-{i}-strength')
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'reference_unit-{i}-reset')
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'reference_unit-{i}-upload')
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'reference_unit-{i}-reuse')
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'reference_unit-{i}-override')
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'reference_unit-{i}-preview')
units.append(unit.Unit(
unit_type = 'reference',
index = i,
+2 -2
View File
@@ -248,7 +248,7 @@ def create_ui_logs():
def create_ui_github():
with gr.Row():
github_search = gr.Textbox(label="Search GitHub Wiki Pages", elem_id="github_search", elem_classes="docs-search")
github_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="github_search_btn")
github_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="github_btn_search")
with gr.Row():
github_result = gr.HTML(elem_id="github_result", value='', elem_classes="github-result")
with gr.Row():
@@ -262,7 +262,7 @@ def create_ui_github():
def create_ui_docs():
with gr.Row():
docs_search = gr.Textbox(label="Search Docs", elem_id="github_search", elem_classes="docs-search")
docs_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="github_search_btn")
docs_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="docs_btn_search")
with gr.Row():
docs_result = gr.HTML(elem_id="docs_result", value='', elem_classes="docs-result")
with gr.Row():
+47 -29
View File
@@ -24,7 +24,7 @@ extra_pages = shared.extra_networks
debug = shared.log.trace if os.environ.get('SD_EN_DEBUG', None) is not None else lambda *args, **kwargs: None
debug('Trace: EN')
card_full = '''
<div class='card' onclick={card_click} title='{name}' data-page='{page}' data-name='{name}' data-filename='{filename}' data-short='{short}' data-tags='{tags}' data-mtime='{mtime}' data-size='{size}' data-search='{search}' style='--data-color: {color}'>
<div class='card' onclick={card_click} title='{name}' data-page='{page}' data-name='{name}' data-filename='{filename}' data-short='{short}' data-tags='{tags}' data-mtime='{mtime}' data-size='{size}' data-search='{search}' data-version='{version}' style='--data-color: {color}'>
<div class='overlay'>
<div class='name {reference}'>{title}</div>
</div>
@@ -56,7 +56,9 @@ def init_api():
global allowed_dirs # pylint: disable=global-statement
if len(allowed_dirs) == 0:
allowed_dirs = shared.demo.allowed_paths
if not os.path.exists(filename):
if filename is None or len(filename) == 0:
return JSONResponse({ "error": "no filename" }, status_code=400)
if not os.path.exists(filename) or not os.path.isfile(filename):
return JSONResponse({ "error": f"file {filename}: not found" }, status_code=404)
if filename.startswith('html/') or filename.startswith('models/'):
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
@@ -149,23 +151,30 @@ class ExtraNetworksPage:
return text.replace('~tabname', tabname)
def create_xyz_grid(self):
"""
xyz_grid = [x for x in scripts.scripts_data if x.script_class.__module__ == "xyz_grid.py"][0].module
pass
def add_prompt(p, opt, x):
for item in [x for x in self.items if x["name"] == opt]:
try:
p.prompt = f'{p.prompt} {eval(item["prompt"])}' # pylint: disable=eval-used
except Exception as e:
shared.log.error(f'Cannot evaluate extra network prompt: {item["prompt"]} {e}')
if not any(self.title in x.label for x in xyz_grid.axis_options):
if self.title == 'Model':
return
opt = xyz_grid.AxisOption(f"[Network] {self.title}", str, add_prompt, choices=lambda: [x["name"] for x in self.items])
if opt not in xyz_grid.axis_options:
xyz_grid.axis_options.append(opt)
"""
def find_version(self, item, info):
all_versions = info.get('modelVersions', [])
if len(all_versions) == 0:
return {}
try:
if item is None:
return all_versions[0]
elif hasattr(item, 'hash') and item.hash is not None:
current_hash = item.hash[:8].upper()
elif hasattr(item, 'shorthash') and item.shorthash is not None:
current_hash = item.shorthash[:8].upper()
elif hasattr(item, 'sha256') and item.sha256 is not None:
current_hash = item.sha256[:8].upper()
else:
return all_versions[0]
for v in info.get('modelVersions', []):
for f in v.get('files', []):
if any(h.startswith(current_hash) for h in f.get('hashes', {}).values()):
return v
except Exception as e:
errors.display(e, 'Network version')
return all_versions[0]
def link_preview(self, filename):
quoted_filename = urllib.parse.quote(filename.replace('\\', '/'))
@@ -225,7 +234,6 @@ class ExtraNetworksPage:
debug(f'EN create-items: page={self.name} items={len(self.items)} time={t1-t0:.2f}')
self.list_time += t1-t0
def create_page(self, tabname, skip = False):
debug(f'EN create-page: {self.name}')
if self.page_time > refresh_time and len(self.html) > 0: # cached page
@@ -276,9 +284,19 @@ class ExtraNetworksPage:
if len(subdir) == 0:
continue
style = 'color: var(--color-accent)' if subdir in ['All', 'Local', 'Diffusers', 'Reference'] else ''
subdirs_html += f'<button class="lg secondary gradio-button custom-button" onclick="extraNetworksSearchButton(event)" style="{style}">{html.escape(subdir)}</button><br>'
if subdir in ['All', 'Local', 'Diffusers', 'Reference']:
style = 'network-reference'
else:
style = 'network-folder'
subdirs_html += f'<button class="lg secondary gradio-button custom-button {style}" onclick="extraNetworksSearchButton(event)">{html.escape(subdir)}</button><br>'
self.html = ''
self.create_items(tabname)
versions = sorted({item.get("version", "") for item in self.items if item.get("version")})
if 'ref' in versions:
versions.remove('ref')
versions_html = ''
for ver in versions:
versions_html += f'<button class="lg secondary gradio-button custom-button network-model" onclick="extraNetworksFilterVersion(event)">{html.escape(ver)}</button><br>'
self.create_xyz_grid()
htmls = []
@@ -302,7 +320,7 @@ class ExtraNetworksPage:
htmls.append(self.create_html(item, tabname))
self.html += ''.join(htmls)
self.page_time = time.time()
self.html = f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}</div><div id='~tabname_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"
self.html = f"""<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}{versions_html}</div><div id='~tabname_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"""
shared.log.debug(f'Networks: type="{self.name}" items={len(self.items)} subfolders={len(subdirs)} tab={tabname} folders={self.allowed_directories_for_previews()} list={self.list_time:.2f} thumb={self.preview_time:.2f} desc={self.desc_time:.2f} info={self.info_time:.2f} workers={shared.max_workers}')
if len(self.missing_thumbs) > 0:
threading.Thread(target=self.create_thumb).start()
@@ -331,7 +349,7 @@ class ExtraNetworksPage:
"filename": item.get('filename', ''),
"short": os.path.splitext(os.path.basename(item.get('filename', '')))[0],
"tags": '|'.join([item.get('tags')] if isinstance(item.get('tags', {}), str) else list(item.get('tags', {}).keys())),
"preview": html.escape(item.get('preview', None) or self.link_preview('html/card-no-preview.png')),
"preview": html.escape(item.get('preview', None) or self.link_preview('html/missing.png')),
"width": 'var(--card-size)',
"height": 'var(--card-size)' if shared.opts.extra_networks_card_square else 'auto',
"fit": shared.opts.extra_networks_card_fit,
@@ -357,7 +375,7 @@ class ExtraNetworksPage:
def find_preview_file(self, path):
if path is None:
return 'html/card-no-preview.png'
return 'html/missing.png'
if os.path.join('models', 'Reference') in path:
return path
exts = ["jpg", "jpeg", "png", "webp", "tiff", "jp2", "jxl"]
@@ -374,7 +392,7 @@ class ExtraNetworksPage:
if '.thumb.' not in file:
self.missing_thumbs.append(file)
return file
return 'html/card-no-preview.png'
return 'html/missing.png'
def find_preview(self, filename):
t0 = time.time()
@@ -424,7 +442,7 @@ class ExtraNetworksPage:
item['preview'] = self.link_preview(found)
debug(f'EN mapped-preview: {item["name"]}={found}')
if item.get('preview', None) is None:
item['preview'] = self.link_preview('html/card-no-preview.png')
item['preview'] = self.link_preview('html/missing.png')
debug(f'EN missing-preview: {item["name"]}')
self.preview_time += time.time() - t0
@@ -692,19 +710,19 @@ def create_ui(container, button_parent, tabname, skip_indexing = False):
def fn_save_img(image):
if ui.last_item is None or ui.last_item.local_preview is None:
return 'html/card-no-preview.png'
return 'html/missing.png'
images = []
if ui.gallery is not None:
images = list(ui.gallery.temp_files) # gallery cannot be used as input component so looking at most recently registered temp files
if len(images) < 1:
shared.log.warning(f'Network no image: item="{ui.last_item.name}"')
return 'html/card-no-preview.png'
return 'html/missing.png'
try:
images.sort(key=lambda f: os.path.getmtime(f), reverse=True)
image = Image.open(images[0])
except Exception as e:
shared.log.error(f'Network error opening image: item="{ui.last_item.name}" {e}')
return 'html/card-no-preview.png'
return 'html/missing.png'
fn_delete_img(image)
if image.width > 512 or image.height > 512:
image = image.convert('RGB')
@@ -723,7 +741,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False):
if os.path.exists(file):
os.remove(file)
shared.log.debug(f'Network delete image: item="{ui.last_item.name}" filename="{file}"')
return 'html/card-no-preview.png'
return 'html/missing.png'
def fn_save_desc(desc):
if hasattr(ui.last_item, 'type') and ui.last_item.type == 'Style':
+7 -2
View File
@@ -28,10 +28,11 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
preview = v.get('preview', v['path'])
preview_file = self.find_preview_file(os.path.join(reference_dir, preview))
_size, mtime = modelstats.stat(preview_file)
name = os.path.normpath(os.path.join(reference_dir, k)).replace('\\', '/')
yield {
"type": 'Model',
"name": os.path.join(reference_dir, k),
"title": os.path.join(reference_dir, k),
"name": name,
"title": name,
"filename": url,
"preview": self.find_preview(os.path.join(reference_dir, preview)),
"local_preview": preview_file,
@@ -42,6 +43,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
"info": {},
"metadata": {},
"description": v.get('desc', ''),
"version": "ref",
}
def create_item(self, name):
@@ -62,6 +64,9 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
}
record["info"] = self.find_info(checkpoint.filename)
record["description"] = self.find_description(checkpoint.filename, record["info"])
version = self.find_version(checkpoint, record["info"])
record["version"] = version.get("baseModel", "") if record["info"] else ""
except Exception as e:
shared.log.debug(f'Networks error: type=model file="{name}" {e}')
return record
+11 -23
View File
@@ -17,7 +17,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
lora_load.list_available_networks()
@staticmethod
def get_tags(l, info):
def get_tags(l, info, version):
tags = {}
try:
if l.metadata is not None:
@@ -37,26 +37,13 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
tag = ' '.join(words[1:]).lower()
tags[tag] = words[0]
def find_version():
found_versions = []
current_hash = l.hash[:8].upper()
all_versions = info.get('modelVersions', [])
for v in info.get('modelVersions', []):
for f in v.get('files', []):
if any(h.startswith(current_hash) for h in f.get('hashes', {}).values()):
found_versions.append(v)
if len(found_versions) == 0:
found_versions = all_versions
return found_versions
for v in find_version(): # trigger words from info json
possible_tags = v.get('trainedWords', [])
if isinstance(possible_tags, list):
for tag_str in possible_tags:
for tag in tag_str.split(','):
tag = tag.strip().lower()
if tag not in tags:
tags[tag] = 0
possible_tags = version.get('trainedWords', [])
if isinstance(possible_tags, list):
for tag_str in possible_tags:
for tag in tag_str.split(','):
tag = tag.strip().lower()
if tag not in tags:
tags[tag] = 0
possible_tags = info.get('tags', []) # tags from info json
if not isinstance(possible_tags, list):
@@ -87,6 +74,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
name = os.path.splitext(os.path.relpath(l.filename, shared.cmd_opts.lora_dir))[0]
size, mtime = modelstats.stat(l.filename)
info = self.find_info(l.filename)
version = self.find_version(l, info)
item = {
"type": 'Lora',
"name": name,
@@ -97,10 +85,10 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
"metadata": json.dumps(l.metadata, indent=4) if l.metadata else None,
"mtime": mtime,
"size": size,
"version": l.sd_version,
"version": version.get("baseModel", l.sd_version) if info else l.sd_version,
"info": info,
"description": self.find_description(l.filename, info),
"tags": self.get_tags(l, info),
"tags": self.get_tags(l, info, version),
}
return item
except Exception as e:
+2
View File
@@ -16,6 +16,7 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage):
try:
size, mtime = modelstats.stat(filename)
info = self.find_info(filename)
version = self.find_version(None, info)
record = {
"type": 'VAE',
"name": name,
@@ -31,6 +32,7 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage):
"size": size,
"info": info,
"description": self.find_description(filename, info),
"version": version.get("baseModel", "N/A") if info else "N/A",
}
yield record
except Exception as e:
+27 -13
View File
@@ -12,20 +12,34 @@ def read_media(fn):
shared.log.error(f'Gallery not found: file="{fn}"')
return [[], None, '', '', f'Media not found: {fn}']
stat_size, stat_mtime = modelstats.stat(fn)
if fn.lower().endswith('.mp4'):
frames, fps, duration, w, h, codec, _frame = video.get_video_params(fn)
# Treat common containers as video for preview; Gradio/HTML5 will handle codec support.
video_exts = ('.mp4', '.webm', '.mkv', '.avi', '.mov', '.mpg', '.mpeg', '.mjpeg')
if fn.lower().endswith(video_exts):
geninfo = ''
log = f'''
<p>Video <b>{w} x {h}</b>
| Codec <b>{codec}</b>
| Frames <b>{frames:,}</b>
| FPS <b>{fps:.2f}</b>
| Duration <b>{duration:.2f}</b>
| Size <b>{stat_size:,}</b>
| Modified <b>{stat_mtime}</b></p><br>
'''
return [gr.update(visible=False, value=[]), gr.update(visible=True, value=fn), geninfo, geninfo, log]
else:
try:
frames, fps, duration, w, h, codec, _frame = video.get_video_params(fn)
log = f'''
<p>Video <b>{w} x {h}</b>
| Codec <b>{codec}</b>
| Frames <b>{frames:,}</b>
| FPS <b>{fps:.2f}</b>
| Duration <b>{duration:.2f}</b>
| Size <b>{stat_size:,}</b>
| Modified <b>{stat_mtime}</b></p><br>
'''
except Exception as e: # keep preview even if probing fails
shared.log.warning(f'Video probe failed: file="{fn}" {e}')
log = f'''
<p>Video
| Size <b>{stat_size:,}</b>
| Modified <b>{stat_mtime}</b></p><br>
'''
return [
gr.update(visible=False, value=[]), # hide image gallery preview
gr.update(visible=True, value=fn), # show video player
geninfo, geninfo, log
]
else: # image
image = Image.open(fn)
image.already_saved_as = fn
geninfo, _items = images.read_info_from_image(image)
+2 -2
View File
@@ -159,8 +159,8 @@ def create_ui():
img2img_gallery, img2img_generation_info, img2img_html_info, _img2img_html_info_formatted, img2img_html_log = ui_common.create_output_panel("img2img", prompt=img2img_prompt)
ui_common.connect_reuse_seed(seed, reuse_seed, img2img_generation_info, is_subseed=False)
ui_common.connect_reuse_seed(subseed, reuse_subseed, img2img_generation_info, is_subseed=True, subseed_strength=subseed_strength)
ui_common.reuse_seed(seed, reuse_seed, subseed=False)
ui_common.reuse_seed(subseed, reuse_subseed, subseed=True)
img2img_prompt_img.change(fn=modules.images.image_data, inputs=[img2img_prompt_img], outputs=[img2img_prompt, img2img_prompt_img])
dummy_component1 = gr.Textbox(visible=False, value='dummy')
+7 -7
View File
@@ -169,11 +169,11 @@ def create_ui():
merge_mode_docs = gr.HTML(value=getattr(merge_methods, "weighted_sum", "").__doc__.replace("\n", "<br>"))
with gr.Row():
primary_model_name = gr.Dropdown(sd_model_choices(), label="Primary model", value="None")
create_refresh_button(primary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_A")
create_refresh_button(primary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "checkpoint_A_refresh")
secondary_model_name = gr.Dropdown(sd_model_choices(), label="Secondary model", value="None")
create_refresh_button(secondary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_B")
create_refresh_button(secondary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "checkpoint_B_refresh")
tertiary_model_name = gr.Dropdown(sd_model_choices(), label="Tertiary model", value="None", visible=False)
tertiary_refresh = create_refresh_button(tertiary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_C", visible=False)
tertiary_refresh = create_refresh_button(tertiary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "checkpoint_C_refresh", visible=False)
with gr.Row():
with gr.Tabs() as tabs:
with gr.TabItem(label="Simple Merge", id=0):
@@ -229,7 +229,7 @@ def create_ui():
bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", interactive=True, label="Replace VAE")
create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list,
lambda: {"choices": ["None"] + list(sd_vae.vae_dict)},
"modelmerger_refresh_bake_in_vae")
"modelmerger_bake_in_vae_refresh")
with gr.Row():
modelmerger_merge = gr.Button(value="Merge", variant='primary')
@@ -403,7 +403,7 @@ def create_ui():
with gr.Column(scale=5):
with gr.Row():
model_name = gr.Dropdown(sd_models.checkpoint_titles(), label="Input model")
create_refresh_button(model_name, sd_models.list_models, lambda: {"choices": sd_models.checkpoint_titles()}, "refresh_checkpoint_Z")
create_refresh_button(model_name, sd_models.list_models, lambda: {"choices": sd_models.checkpoint_titles()}, "checkpoint_Z_refresh")
with gr.Column(scale=5):
custom_name = gr.Textbox(label="Output model", placeholder="Output model path")
with gr.Row():
@@ -492,7 +492,7 @@ def create_ui():
with gr.Row(elem_id='civitai_search_row'):
civit_search_text = gr.Textbox(label='', placeholder='keyword', elem_id="civit_search_text")
civit_search_tag = gr.Textbox(label='', placeholder='tag', elem_id="civit_search_text")
civit_search_text_btn = ToolButton(value=ui_symbols.search, interactive=True)
civit_search_text_btn = ToolButton(value=ui_symbols.search, interactive=True, elem_id="civit_text_search")
with gr.Accordion(label='Advanced', open=False, elem_id="civitai_search_options"):
civit_download_btn = gr.Button(value="Download model", variant='primary', elem_id="civitai_download_btn", visible=False)
with gr.Row():
@@ -530,7 +530,7 @@ def create_ui():
gr.HTML('<h2>&nbspDownload model from huggingface<br></h2>')
with gr.Row():
hf_search_text = gr.Textbox('', label='Search models', placeholder='search huggingface models')
hf_search_btn = ToolButton(value=ui_symbols.search)
hf_search_btn = ToolButton(value=ui_symbols.search, interactive=True, elem_id="hf_text_search")
with gr.Row():
hf_selected = gr.Textbox('', label='Select model', placeholder='select model from search results or enter model name manually')
with gr.Accordion(label='Advanced', open=False, elem_id="hf_search_options"):
+8 -8
View File
@@ -87,7 +87,7 @@ def create_resolution_inputs(tab, default_width=1024, default_height=1024):
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_ar", elem_classes=["ar-dropdown"])
for c in [ar_dropdown, width, height]:
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False)
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_switch_btn")
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_btn_swap")
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
return width, height
@@ -111,12 +111,12 @@ def create_seed_inputs(tab, reuse_visible=True, accordion=True, subseed_visible=
with gr.Accordion(open=False, label="Seed", elem_id=f"{tab}_seed_group", elem_classes=["small-accordion"]) if accordion else gr.Group():
with gr.Row(elem_id=f"{tab}_seed_row", variant="compact"):
seed = gr.Number(label='Initial seed', value=-1, elem_id=f"{tab}_seed", container=True)
random_seed = ToolButton(ui_symbols.random, elem_id=f"{tab}_random_seed")
reuse_seed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_reuse_seed", visible=reuse_visible)
random_seed = ToolButton(ui_symbols.random, elem_id=f"{tab}_seed_random")
reuse_seed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_seed_reuse", visible=reuse_visible)
with gr.Row(elem_id=f"{tab}_subseed_row", variant="compact", visible=subseed_visible):
subseed = gr.Number(label='Variation', value=-1, elem_id=f"{tab}_subseed", container=True)
random_subseed = ToolButton(ui_symbols.random, elem_id=f"{tab}_random_subseed")
reuse_subseed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_reuse_subseed", visible=reuse_visible)
random_subseed = ToolButton(ui_symbols.random, elem_id=f"{tab}_subseed_random")
reuse_subseed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_subseed_reuse", visible=reuse_visible)
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=f"{tab}_subseed_strength", elem_classes=["subseed-strength"])
with gr.Row(visible=seed_resize_visible):
seed_resize_from_w = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize seed from width", value=0, elem_id=f"{tab}_seed_resize_from_w")
@@ -342,7 +342,7 @@ def create_resize_inputs(tab, images, accordion=True, latent=False, non_zero=Tru
resize_name = gr.Dropdown(label=f"Method{prefix}" if non_zero else "Resize method", elem_id=f"{tab}_resize_name", choices=available_upscalers, value=available_upscalers[0], visible=True)
resize_context_choices = ["Add with forward", "Remove with forward", "Add with backward", "Remove with backward"]
resize_context = gr.Dropdown(label=f"Context{prefix}", elem_id=f"{tab}_resize_context", choices=resize_context_choices, value=resize_context_choices[0], visible=False)
resize_refresh_btn = ui_common.create_refresh_button(resize_name, modelloader.load_upscalers, lambda: {"choices": modelloader.load_upscalers()}, 'refresh_upscalers')
resize_refresh_btn = ui_common.create_refresh_button(resize_name, modelloader.load_upscalers, lambda: {"choices": modelloader.load_upscalers()}, f'{tab}_upscalers_refresh')
def resize_mode_change(mode):
if mode is None or mode == 0:
@@ -365,9 +365,9 @@ def create_resize_inputs(tab, images, accordion=True, latent=False, non_zero=Tru
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_resize_ar", elem_classes=["ar-dropdown"])
for c in [ar_dropdown, width, height]:
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False)
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_resize_switch_size_btn")
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_resize_size_swap")
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
detect_image_size_btn = ToolButton(value=ui_symbols.detect, elem_id=f"{tab}_resize_detect_size_btn")
detect_image_size_btn = ToolButton(value=ui_symbols.detect, elem_id=f"{tab}_resize_detect_size")
el = tab.split('_')[0]
detect_image_size_btn.click(fn=lambda w, h, _: (w or gr.update(), h or gr.update()), _js=f'currentImageResolution{el}', inputs=[dummy_component, dummy_component, dummy_component], outputs=[width, height], show_progress=False)
with gr.Tab(label="Scale", id=1, elem_id=f"{tab}_scale_tab_scale") as tab_scale_by:
+5 -7
View File
@@ -77,11 +77,11 @@ def create_setting_component(key, is_quicksettings=False):
if info.refresh is not None:
if is_quicksettings:
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"settings_{key}_refresh")
else:
with gr.Row():
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"settings_{key}_refresh")
elif info.folder is not None:
with gr.Row():
res = comp(label=info.label, value=fun(), elem_id=elem_id, elem_classes="folder-selector", **args)
@@ -175,10 +175,10 @@ def create_ui():
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"):
restart_submit = gr.Button(value="Restart server", variant='primary', elem_id="restart_submit")
shutdown_submit = gr.Button(value="Shutdown server", variant='primary', elem_id="shutdown_submit")
unload_sd_model = gr.Button(value='Unload model', variant='primary', elem_id="sett_unload_sd_model")
reload_sd_model = gr.Button(value='Reload model', variant='primary', elem_id="sett_reload_sd_model")
restart_submit = gr.Button(value="Restart server", variant='primary', elem_id="restart_submit")
shutdown_submit = gr.Button(value="Shutdown server", variant='primary', elem_id="shutdown_submit")
enable_profiling = gr.Button(value='Start profiling', variant='primary', elem_id="enable_profiling")
with gr.Tabs(elem_id="system") as system_tabs:
@@ -187,7 +187,6 @@ def create_ui():
with gr.TabItem("Settings", id="system_settings", elem_id="tab_settings"):
with gr.Row(elem_id="settings_row"):
settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
preview_theme = gr.Button(value="Preview theme", variant='primary', elem_id="settings_preview_theme")
defaults_submit = gr.Button(value="Restore defaults", variant='primary', elem_id="defaults_submit")
with gr.Row():
_settings_search = gr.Textbox(label="Search", elem_id="settings_search")
@@ -288,7 +287,6 @@ def create_ui():
reload_sd_model.click(fn=reload_sd_weights, inputs=[], outputs=[])
enable_profiling.click(fn=switch_profiling, inputs=[], outputs=[enable_profiling])
request_notifications.click(fn=lambda: None, inputs=[], outputs=[], _js='function(){}')
preview_theme.click(fn=None, _js='previewTheme', inputs=[], outputs=[])
settings_submit.click(
fn=call_queue.wrap_gradio_call(run_settings, extra_outputs=[gr.update()]),
inputs=components,
@@ -323,7 +321,7 @@ def create_quicksettings(interfaces):
quicksetting_keys.append(k)
shared.settings_components[k] = component
quicksetting_keys = gr.State(value=','.join(quicksetting_keys), elem_id="quicksettings_keys")
btn_reset = ui_components.ToolButton(value=ui_symbols.clear, visible=True, elem_id="quicksettings_reset")
btn_reset = ui_components.ToolButton(value=ui_symbols.clear, visible=True, elem_id="quicksettings_clear")
btn_reset.click(fn=reset_quicksettings, inputs=[quicksetting_keys], outputs=quicksetting_components)
generation_parameters_copypaste.connect_paste_params_buttons()
-2
View File
@@ -27,8 +27,6 @@ preview = '🖼️'
image = '🖌️'
resize = ''
interrogate = ''
int_clip = ''
int_blip = ''
bullet = ''
sort_alpha_asc = '\uf15d'
sort_alpha_dsc = '\uf15e'
+2 -2
View File
@@ -45,8 +45,8 @@ def create_ui():
txt2img_script_inputs = modules.scripts_manager.scripts_txt2img.setup_ui(parent='txt2img', accordion=True)
txt2img_gallery, txt2img_generation_info, txt2img_html_info, _txt2img_html_info_formatted, txt2img_html_log = ui_common.create_output_panel("txt2img", preview=True, prompt=txt2img_prompt)
ui_common.connect_reuse_seed(seed, reuse_seed, txt2img_generation_info, is_subseed=False)
ui_common.connect_reuse_seed(subseed, reuse_subseed, txt2img_generation_info, is_subseed=True, subseed_strength=subseed_strength)
ui_common.reuse_seed(seed, reuse_seed, subseed=False)
ui_common.reuse_seed(subseed, reuse_subseed, subseed=True)
dummy_component = gr.Textbox(visible=False, value='dummy')
+21 -7
View File
@@ -152,7 +152,7 @@ models = {
Model(name='WAN 2.2 5B I2V',
url='https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B-Diffusers',
repo='Wan-AI/Wan2.2-TI2V-5B-Diffusers',
repo_cls=diffusers.WanPipeline,
repo_cls=diffusers.WanImageToVideoPipeline,
te_cls=transformers.T5EncoderModel,
dit_cls=diffusers.WanTransformer3DModel),
Model(name='WAN 2.2 A14B T2V',
@@ -164,7 +164,7 @@ models = {
Model(name='WAN 2.2 A14B I2V',
url='https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B-Diffusers',
repo='Wan-AI/Wan2.2-T2V-A14B-Diffusers',
repo_cls=diffusers.WanPipeline,
repo_cls=diffusers.WanImageToVideoPipeline,
te_cls=transformers.T5EncoderModel,
dit_cls=diffusers.WanTransformer3DModel),
Model(name='WAN 2.1 1.3B T2V',
@@ -212,35 +212,35 @@ models = {
],
'SkyReels V2': [
Model(name='None'),
Model(name='SkyReels-V2 T2I-DF 1.3B-540P',
Model(name='SkyReels-V2 T2V-DF 1.3B-540P',
url='https://huggingface.co/Skywork/SkyReels-V2-DF-1.3B-540P-Diffusers',
repo='Skywork/SkyReels-V2-DF-1.3B-540P-Diffusers',
repo_cls=diffusers.SkyReelsV2DiffusionForcingPipeline,
repo_revision='refs/pr/1',
te_cls=transformers.UMT5EncoderModel,
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
Model(name='SkyReels-V2 T2I-DF 14B-720P',
Model(name='SkyReels-V2 T2V-DF 14B-720P',
url='https://huggingface.co/Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
repo='Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
repo_cls=diffusers.SkyReelsV2DiffusionForcingPipeline,
repo_revision='refs/pr/1',
te_cls=transformers.UMT5EncoderModel,
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
Model(name='SkyReels-V2 I2I-DF 14B-720P',
Model(name='SkyReels-V2 I2V-DF 14B-720P',
url='https://huggingface.co/Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
repo='Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
repo_cls=diffusers.SkyReelsV2DiffusionForcingImageToVideoPipeline,
repo_revision='refs/pr/1',
te_cls=transformers.UMT5EncoderModel,
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
Model(name='SkyReels-V2 T2I 14B-720P',
Model(name='SkyReels-V2 T2V 14B-720P',
url='https://huggingface.co/Skywork/SkyReels-V2-T2V-14B-720P-Diffusers',
repo='Skywork/SkyReels-V2-T2V-14B-720P-Diffusers',
repo_cls=diffusers.SkyReelsV2Pipeline,
repo_revision='refs/pr/1',
te_cls=transformers.UMT5EncoderModel,
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
Model(name='SkyReels-V2 I2I 14B-720P',
Model(name='SkyReels-V2 I2V 14B-720P',
url='https://huggingface.co/Skywork/SkyReels-V2-I2V-14B-720P-Diffusers',
repo='Skywork/SkyReels-V2-I2V-14B-720P-Diffusers',
repo_cls=diffusers.SkyReelsV2ImageToVideoPipeline,
@@ -320,4 +320,18 @@ models = {
te_cls=transformers.T5EncoderModel,
dit_cls=diffusers.CogVideoXTransformer3DModel),
],
'nVidia Cosmos': [
Model(name='nvidia Cosmos Predict2 2B I2V',
url='https://huggingface.co/nvidia/Cosmos-Predict2-2B-Text2Image',
repo='nvidia/Cosmos-Predict2-2B-Video2World',
repo_cls=diffusers.Cosmos2VideoToWorldPipeline,
te_cls=transformers.T5EncoderModel,
dit_cls=diffusers.CosmosTransformer3DModel),
Model(name='nvidia Cosmos Predict2 2B I2V',
url='https://huggingface.co/nvidia/Cosmos-Predict2-2B-Text2Image',
repo='nvidia/Cosmos-Predict2-2B-Video2World',
repo_cls=diffusers.Cosmos2VideoToWorldPipeline,
te_cls=transformers.T5EncoderModel,
dit_cls=diffusers.CosmosTransformer3DModel),
],
}
+7 -12
View File
@@ -1,12 +1,10 @@
import os
import copy
import time
from modules import shared, errors, sd_models, sd_checkpoint, model_quant, devices, sd_hijack_te
from modules.video_models import models_def, video_utils, video_vae, video_overrides, video_cache
from modules import shared, errors, sd_models, sd_checkpoint, model_quant, devices, sd_hijack_te, sd_hijack_vae
from modules.video_models import models_def, video_utils, video_overrides, video_cache
loaded_model = None
debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
def load_model(selected: models_def.Model):
@@ -24,7 +22,7 @@ def load_model(selected: models_def.Model):
# text encoder
try:
quant_args = model_quant.create_config(module='TE')
debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
shared.log.debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
text_encoder = selected.te_cls.from_pretrained(
pretrained_model_name_or_path=selected.te or selected.repo,
subfolder=selected.te_folder,
@@ -41,7 +39,7 @@ def load_model(selected: models_def.Model):
# transformer
try:
quant_args = model_quant.create_config(module='Model')
debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" folder="{selected.dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
shared.log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" folder="{selected.dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
transformer = selected.dit_cls.from_pretrained(
pretrained_model_name_or_path=selected.dit or selected.repo,
subfolder=selected.dit_folder,
@@ -60,7 +58,7 @@ def load_model(selected: models_def.Model):
# model
try:
debug(f'Video load: module=pipe repo="{selected.repo}" cls={selected.repo_cls.__name__}')
shared.log.debug(f'Video load: module=pipe repo="{selected.repo}" cls={selected.repo_cls.__name__}')
shared.sd_model = selected.repo_cls.from_pretrained(
pretrained_model_name_or_path=selected.repo,
transformer=transformer,
@@ -81,13 +79,10 @@ def load_model(selected: models_def.Model):
shared.sd_model.sd_checkpoint_info = sd_checkpoint.CheckpointInfo(selected.repo)
shared.sd_model.sd_model_hash = None
sd_models.set_diffuser_options(shared.sd_model, offload=False)
if selected.vae_hijack and hasattr(shared.sd_model.vae, 'decode'):
shared.sd_model.vae.orig_decode = shared.sd_model.vae.decode
shared.sd_model.vae.decode = video_vae.hijack_vae_decode
shared.sd_model.vae.orig_encode = shared.sd_model.vae.encode
shared.sd_model.vae.encode = video_vae.hijack_vae_encode
sd_hijack_vae.init_hijack(shared.sd_model)
if selected.te_hijack and hasattr(shared.sd_model, 'encode_prompt'):
# shared.sd_model.orig_encode_prompt = shared.sd_model.encode_prompt
sd_hijack_te.init_hijack(shared.sd_model)
if selected.image_hijack and hasattr(shared.sd_model, 'encode_image'):
shared.sd_model.orig_encode_image = shared.sd_model.encode_image
+2
View File
@@ -46,3 +46,5 @@ def set_overrides(p: processing.StableDiffusionProcessingVideo, selected: Model)
if 'LTX' in cls:
p.task_args['width'] = 32 * (p.width // 32)
p.task_args['height'] = 32 * (p.height // 32)
if 'SkyReelsV2DiffusionForcing' in cls:
p.task_args['overlap_history'] = 17
+2
View File
@@ -69,6 +69,8 @@ def generate(*args, **kwargs):
elif 'T2V' in model:
if init_image is not None:
shared.log.warning('Video: op=T2V init image not supported')
else:
shared.log.warning(f'Video: unknown model type "{model}"')
# cleanup memory
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model)
+2 -2
View File
@@ -92,8 +92,8 @@ def create_ui(prompt, negative, styles, overrides):
with gr.Row():
frames = gr.Slider(label='Frames', minimum=1, maximum=1024, step=1, value=17, elem_id="video_frames")
seed = gr.Number(label='Initial seed', value=-1, elem_id="video_seed", container=True)
random_seed = ToolButton(ui_symbols.random, elem_id="video_random_seed")
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_reuse_seed")
random_seed = ToolButton(ui_symbols.random, elem_id="video_seed_random")
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_seed_reuse")
with gr.Accordion(open=False, label="Parameters", elem_id='video_parameters_accordion'):
steps, sampler_index = ui_sections.create_sampler_and_steps_selection(None, "video", default_steps=50)
with gr.Row():
+1 -57
View File
@@ -1,7 +1,5 @@
import os
import time
import torch
from modules import shared, sd_models, devices, timer, errors
from modules import shared, devices
debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
@@ -45,57 +43,3 @@ def vae_decode_tiny(latents):
images = vae.decode_video(latents, parallel=False).transpose(1, 2).mul_(2).sub_(1)
images = images.transpose(1, 2).mul_(2).sub_(1)
return (images, None)
def hijack_vae_decode(*args, **kwargs):
shared.state.begin('VAE')
t0 = time.time()
res = None
if vae_type == 'Tiny':
res = vae_decode_tiny(args[0])
if vae_type == 'Remote':
pass
if res is None:
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
try:
sd_models.move_model(shared.sd_model.vae, devices.device)
if torch.is_tensor(args[0]):
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
res = shared.sd_model.vae.orig_decode(latents, *args[1:], **kwargs)
t1 = time.time()
shared.log.debug(f'Decode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
else:
res = shared.sd_model.vae.orig_decode(*args, **kwargs)
except Exception as e:
shared.log.error(f'Decode: type={vae_type} {e}')
errors.display(e, 'vae')
res = None
t1 = time.time()
timer.process.add('vae', t1-t0)
shared.state.end()
return res
def hijack_vae_encode(*args, **kwargs):
shared.state.begin('VAE')
t0 = time.time()
res = None
if res is None:
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
try:
sd_models.move_model(shared.sd_model.vae, devices.device)
if torch.is_tensor(args[0]):
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
res = shared.sd_model.vae.orig_encode(latents, *args[1:], **kwargs)
t1 = time.time()
shared.log.debug(f'Encode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
else:
res = shared.sd_model.vae.orig_encode(*args, **kwargs)
except Exception as e:
shared.log.error(f'Encode: type={vae_type} {e}')
errors.display(e, 'vae')
res = None
t1 = time.time()
timer.process.add('vae', t1-t0)
shared.state.end()
return res