Merge remote-tracking branch 'origin/master' into Compel

This commit is contained in:
Hameer Abbasi
2023-08-06 11:15:53 +00:00
27 changed files with 436 additions and 166 deletions
+13
View File
@@ -1,5 +1,18 @@
# Change Log for SD.Next
## Update for 2023-08-05
Another minor update, but it unlocks some cool new items...
- diffusers:
- vaesd live preview (sd and sd-xl)
- fix inpainting (sd and sd-xl)
- general:
- new torch 2.0 with ipex (intel arc)
- additional callbacks for extensions
enables latest comfyui extension
- update requirements
## Update for 2023-07-30
Smaller release, but IMO worth a post...
-1
View File
@@ -15,7 +15,6 @@ Stuff to be added, in no particular order...
- Add Hires
- Add Lora/Lyco mixer
- Add ControlNet
- Fix SD-XL Img2img/Inpaint
- Add SD and SD-XL Pix2Pix
- Fix DeepFloyd IF model
- Redo Prompt parser for diffusers
+1 -1
View File
@@ -328,7 +328,7 @@ def check_torch():
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.1a0 torchvision==0.15.2a0 intel_extension_for_pytorch==2.0.110+xpu -f https://developer.intel.com/ipex-whl-stable-xpu')
os.environ.setdefault('TENSORFLOW_PACKAGE', 'tensorflow==2.13.0 intel-extension-for-tensorflow[gpu]')
else:
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0a0 torchvision intel_extension_for_pytorch==2.0.110+gitba7f6c1 -f https://developer.intel.com/ipex-whl-stable-xpu')
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0a0 intel_extension_for_pytorch==2.0.110+gitba7f6c1 -f https://developer.intel.com/ipex-whl-stable-xpu')
else:
machine = platform.machine()
if sys.platform == 'darwin':
+4 -4
View File
@@ -119,9 +119,9 @@ def set_cuda_params():
shared.log.debug('Verifying Torch settings')
if cuda_ok:
try:
torch.backends.cuda.matmul.allow_tf32 = shared.opts.cuda_allow_tf32
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = shared.opts.cuda_allow_tf16_reduced
torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = shared.opts.cuda_allow_tf16_reduced
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = True
torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = True
except Exception:
pass
if torch.backends.cudnn.is_available():
@@ -130,7 +130,7 @@ def set_cuda_params():
if shared.opts.cudnn_benchmark:
shared.log.debug('Torch enable cuDNN benchmark')
torch.backends.cudnn.benchmark_limit = 0
torch.backends.cudnn.allow_tf32 = shared.opts.cuda_allow_tf32
torch.backends.cudnn.allow_tf32 = True
except Exception:
pass
global dtype, dtype_vae, dtype_unet, unet_needs_upcast # pylint: disable=global-statement
+2 -1
View File
@@ -106,7 +106,8 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
elif mode == 2: # inpaint
if init_img_with_mask is None:
return
image, mask = init_img_with_mask["image"], init_img_with_mask["mask"]
image = init_img_with_mask["image"]
mask = init_img_with_mask["mask"]
alpha_mask = ImageOps.invert(image.split()[-1]).convert('L').point(lambda x: 255 if x > 0 else 0, mode='1')
mask = ImageChops.lighter(alpha_mask, mask.convert('L')).convert('L')
image = image.convert("RGB")
+22 -4
View File
@@ -4,6 +4,7 @@ import torch
import intel_extension_for_pytorch as ipex
from modules import shared
from modules.sd_hijack_utils import CondFunc
from .diffusers import ipex_diffusers
#ControlNet depth_leres++
class DummyDataParallel(torch.nn.Module):
@@ -111,13 +112,28 @@ def ipex_init():
CondFunc('torch.nn.modules.GroupNorm.forward',
lambda orig_func, self, input: orig_func(self, input.to(self.weight.data.dtype)),
lambda orig_func, self, input: input.dtype != self.weight.data.dtype)
#FP32:
CondFunc('torch.nn.modules.Linear.forward',
lambda orig_func, self, input: orig_func(self, input.to(self.weight.data.dtype)),
lambda orig_func, self, input: input.dtype != self.weight.data.dtype)
#Diffusers bfloat16:
CondFunc('torch.nn.modules.Conv2d._conv_forward',
lambda orig_func, self, input, weight, bias=None: orig_func(self, input.to(weight.data.dtype), weight, bias=bias),
lambda orig_func, self, input, weight, bias=None: input.dtype != weight.data.dtype)
#Embedding FP32:
CondFunc('torch.bmm',
lambda orig_func, input, mat2, *args, **kwargs: orig_func(input, mat2.to(input.dtype), *args, **kwargs),
lambda orig_func, input, mat2, *args, **kwargs: input.dtype != mat2.dtype)
#BF16:
CondFunc('torch.nn.functional.layer_norm',
lambda orig_func, input, normalized_shape=None, weight=None, *args, **kwargs:
orig_func(input.to(weight.data.dtype), normalized_shape, weight, *args, **kwargs),
lambda orig_func, input, normalized_shape=None, weight=None, *args, **kwargs:
input.dtype != weight.data.dtype and weight is not None)
#Embedding BF16
CondFunc('torch.cat',
lambda orig_func, input, *args, **kwargs: orig_func([input[0].to(input[1].dtype), input[1], input[2].to(input[1].dtype)], *args, **kwargs),
lambda orig_func, input, *args, **kwargs: len(input) == 3 and (input[0].dtype != input[1].dtype or input[2].dtype != input[1].dtype))
#Diffusers BF16:
CondFunc('torch.nn.functional.conv2d',
lambda orig_func, input, weight, *args, **kwargs: orig_func(input.to(weight.data.dtype), weight, *args, **kwargs),
lambda orig_func, input, weight, *args, **kwargs: input.dtype != weight.data.dtype)
#Functions that does not work with the XPU:
#UniPC:
@@ -149,3 +165,5 @@ def ipex_init():
weight if weight is not None else torch.ones(input.size()[1], device=shared.device),
bias if bias is not None else torch.zeros(input.size()[1], device=shared.device), *args, **kwargs),
lambda orig_func, input, *args, **kwargs: input.device != torch.device("cpu"))
ipex_diffusers()
+112
View File
@@ -0,0 +1,112 @@
import torch
import intel_extension_for_pytorch as ipex
import diffusers
#ARC GPUs can't allocate more than 4GB to a single block:
class SlicedAttnProcessor:
r"""
Processor for implementing sliced attention.
Args:
slice_size (`int`, *optional*):
The number of steps to compute attention. Uses as many slices as `attention_head_dim // slice_size`, and
`attention_head_dim` must be a multiple of the `slice_size`.
"""
def __init__(self, slice_size):
self.slice_size = slice_size
def __call__(self, attn: diffusers.models.attention_processor.Attention, hidden_states, encoder_hidden_states=None, attention_mask=None):
residual = hidden_states
input_ndim = hidden_states.ndim
if input_ndim == 4:
batch_size, channel, height, width = hidden_states.shape
hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2)
batch_size, sequence_length, _ = (
hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape
)
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
if attn.group_norm is not None:
hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2)
query = attn.to_q(hidden_states)
dim = query.shape[-1]
query = attn.head_to_batch_dim(query)
if encoder_hidden_states is None:
encoder_hidden_states = hidden_states
elif attn.norm_cross:
encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states)
key = attn.to_k(encoder_hidden_states)
value = attn.to_v(encoder_hidden_states)
key = attn.head_to_batch_dim(key)
value = attn.head_to_batch_dim(value)
batch_size_attention, query_tokens, shape_three = query.shape
hidden_states = torch.zeros(
(batch_size_attention, query_tokens, dim // attn.heads), device=query.device, dtype=query.dtype
)
block_multiply = 2.4 if query.dtype == torch.float32 else 1.2
block_size = (batch_size_attention * query_tokens * shape_three) / 1024 * block_multiply #MB
split_2_slice_size = query_tokens
if block_size >= 4000:
do_split_2 = True
#Find something divisible with the query_tokens
while ((self.slice_size * split_2_slice_size * shape_three) / 1024 * block_multiply) > 4000:
split_2_slice_size = split_2_slice_size // 2
else:
do_split_2 = False
for i in range(batch_size_attention // self.slice_size):
start_idx = i * self.slice_size
end_idx = (i + 1) * self.slice_size
if do_split_2:
for i2 in range(query_tokens // split_2_slice_size):
start_idx_2 = i2 * split_2_slice_size
end_idx_2 = (i2 + 1) * split_2_slice_size
query_slice = query[start_idx:end_idx, start_idx_2:end_idx_2]
key_slice = key[start_idx:end_idx, start_idx_2:end_idx_2]
attn_mask_slice = attention_mask[start_idx:end_idx, start_idx_2:end_idx_2] if attention_mask is not None else None
attn_slice = attn.get_attention_scores(query_slice, key_slice, attn_mask_slice)
attn_slice = torch.bmm(attn_slice, value[start_idx:end_idx, start_idx_2:end_idx_2])
hidden_states[start_idx:end_idx, start_idx_2:end_idx_2] = attn_slice
else:
query_slice = query[start_idx:end_idx]
key_slice = key[start_idx:end_idx]
attn_mask_slice = attention_mask[start_idx:end_idx] if attention_mask is not None else None
attn_slice = attn.get_attention_scores(query_slice, key_slice, attn_mask_slice)
attn_slice = torch.bmm(attn_slice, value[start_idx:end_idx])
hidden_states[start_idx:end_idx] = attn_slice
hidden_states = attn.batch_to_head_dim(hidden_states)
# linear proj
hidden_states = attn.to_out[0](hidden_states)
# dropout
hidden_states = attn.to_out[1](hidden_states)
if input_ndim == 4:
hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width)
if attn.residual_connection:
hidden_states = hidden_states + residual
hidden_states = hidden_states / attn.rescale_output_factor
return hidden_states
def ipex_diffusers():
diffusers.models.attention_processor.SlicedAttnProcessor = SlicedAttnProcessor
+37 -26
View File
@@ -444,8 +444,13 @@ def fix_seed(p):
p.subseed = get_fixed_seed(p.subseed)
def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument
index = position_in_batch + iteration * p.batch_size
def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0, index=None, all_negative_prompts=None): # pylint: disable=unused-argument
if index is None:
index = position_in_batch + iteration * p.batch_size
if all_negative_prompts is None:
all_negative_prompts = p.all_negative_prompts
generation_params = {
"Steps": p.steps,
"Seed": all_seeds[index],
@@ -487,7 +492,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
generation_params['Token merging ratio hr'] = token_merging_ratio_hr if token_merging_ratio_hr != 0 else None
generation_params.update(p.extra_generation_params)
generation_params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in generation_params.items() if v is not None])
negative_prompt_text = f"\nNegative prompt: {p.all_negative_prompts[index]}" if p.all_negative_prompts[index] else ""
negative_prompt_text = f"\nNegative prompt: {all_negative_prompts[index]}" if all_negative_prompts[index] else ""
return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
@@ -599,9 +604,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
else:
p.all_subseeds = [int(subseed) + x for x in range(len(p.all_prompts))]
def infotext(iteration=0, position_in_batch=0):
return create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, comments, iteration, position_in_batch)
if os.path.exists(shared.opts.embeddings_dir) and not p.do_not_reload_embeddings:
model_hijack.embedding_db.load_textual_inversion_embeddings()
if p.scripts is not None:
@@ -646,20 +648,20 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if shared.state.interrupted:
shared.log.debug(f'Process interrupted: {n}/{p.n_iter}')
break
prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
seeds = p.all_seeds[n * p.batch_size:(n + 1) * p.batch_size]
subseeds = p.all_subseeds[n * p.batch_size:(n + 1) * p.batch_size]
p.prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
p.seeds = p.all_seeds[n * p.batch_size:(n + 1) * p.batch_size]
p.subseeds = p.all_subseeds[n * p.batch_size:(n + 1) * p.batch_size]
if p.scripts is not None:
p.scripts.before_process_batch(p, batch_number=n, prompts=prompts, seeds=seeds, subseeds=subseeds)
if len(prompts) == 0:
p.scripts.before_process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
if len(p.prompts) == 0:
break
prompts, extra_network_data = extra_networks.parse_prompts(prompts)
p.prompts, extra_network_data = extra_networks.parse_prompts(p.prompts)
if not p.disable_extra_networks:
with devices.autocast():
extra_networks.activate(p, extra_network_data)
if p.scripts is not None:
p.scripts.process_batch(p, batch_number=n, prompts=prompts, seeds=seeds, subseeds=subseeds)
p.scripts.process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
if n == 0:
with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file:
processed = Processed(p, [], p.seed, "")
@@ -671,13 +673,13 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
shared.state.job = f"Batch {n+1} out of {p.n_iter}"
if shared.backend == shared.Backend.ORIGINAL:
uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, negative_prompts, p.steps * step_multiplier, cached_uc)
c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, prompts, p.steps * step_multiplier, cached_c)
uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, p.negative_prompts, p.steps * step_multiplier, cached_uc)
c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, p.prompts, p.steps * step_multiplier, cached_c)
if len(model_hijack.comments) > 0:
for comment in model_hijack.comments:
comments[comment] = 1
with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast():
samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, prompts=prompts)
samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts)
x_samples_ddim = [decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))]
try:
for x in x_samples_ddim:
@@ -699,7 +701,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
elif shared.backend == shared.Backend.DIFFUSERS:
from modules.processing_diffusers import process_diffusers
x_samples_ddim = process_diffusers(p, seeds, prompts, negative_prompts)
x_samples_ddim = process_diffusers(p, p.seeds, p.prompts, p.negative_prompts)
else:
raise ValueError(f"Unknown backend {shared.backend}")
@@ -709,6 +711,15 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
devices.torch_gc()
if p.scripts is not None:
p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n)
if p.scripts is not None:
p.prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
batch_params = scripts.PostprocessBatchListArgs(list(x_samples_ddim))
p.scripts.postprocess_batch_list(p, batch_params, batch_number=n)
x_samples_ddim = batch_params.images
def infotext(index=0):
return create_infotext(p, p.prompts, p.seeds, p.subseeds, index=index, all_negative_prompts=p.negative_prompts)
for i, x_sample in enumerate(x_samples_ddim):
p.batch_index = i
@@ -721,9 +732,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_face_restoration:
orig = p.restore_faces
p.restore_faces = False
info=infotext(n, i)
info = infotext(i)
p.restore_faces = orig
images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restoration")
images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restoration")
p.ops.append('face')
x_sample = modules.face_restoration.restore_faces(x_sample)
image = Image.fromarray(x_sample)
@@ -735,16 +746,16 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_color_correction:
orig = p.color_corrections
p.color_corrections = None
info=infotext(n, i)
info = infotext(i)
p.color_corrections = orig
image_without_cc = apply_overlay(image, p.paste_to, i, p.overlay_images)
images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correction")
images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correction")
p.ops.append('color')
image = apply_color_correction(p.color_corrections[i], image)
image = apply_overlay(image, p.paste_to, i, p.overlay_images)
if shared.opts.samples_save and not p.do_not_save_samples:
images.save_image(image, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=infotext(n, i), p=p)
text = infotext(n, i)
images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p)
text = infotext(i)
infotexts.append(text)
image.info["parameters"] = text
output_images.append(image)
@@ -752,9 +763,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
image_mask = p.mask_for_overlay.convert('RGB')
image_mask_composite = Image.composite(image.convert('RGBA').convert('RGBa'), Image.new('RGBa', image.size), images.resize_image(3, p.mask_for_overlay, image.width, image.height).convert('L')).convert('RGBA')
if shared.opts.save_mask:
images.save_image(image_mask, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=infotext(n, i), p=p, suffix="-mask")
images.save_image(image_mask, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p, suffix="-mask")
if shared.opts.save_mask_composite:
images.save_image(image_mask_composite, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=infotext(n, i), p=p, suffix="-mask-composite")
images.save_image(image_mask_composite, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p, suffix="-mask-composite")
if shared.opts.return_mask:
output_images.append(image_mask)
if shared.opts.return_mask_composite:
+1 -3
View File
@@ -134,7 +134,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
task_specific_kwargs = {"image": p.init_images, "strength": p.denoising_strength}
elif sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.INPAINTING:
p.ops.append('inpaint')
task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength}
task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength, "height": p.height, "width": p.width}
# TODO diffusers use transformers for prompt parsing
# from modules.prompt_parser import parse_prompt_attention
@@ -229,6 +229,4 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
if p.is_hr_pass:
shared.log.warning('Diffusers not implemented: hires fix')
return results
+33
View File
@@ -18,6 +18,11 @@ class PostprocessImageArgs:
self.image = image
class PostprocessBatchListArgs:
def __init__(self, images):
self.images = images
class Script:
name = None
filename = None
@@ -108,6 +113,22 @@ class Script:
"""
pass # pylint: disable=unnecessary-pass
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, *args, **kwargs):
"""
Same as postprocess_batch(), but receives batch images as a list of 3D tensors instead of a 4D tensor.
This is useful when you want to update the entire batch instead of individual images.
You can modify the postprocessing object (pp) to update the images in the batch, remove images, add images, etc.
If the number of images is different from the batch size when returning,
then the script has the responsibility to also update the following attributes in the processing object (p):
- p.prompts
- p.negative_prompts
- p.seeds
- p.subseeds
**kwargs will have same items as process_batch, and also:
- batch_number - index of current batch, from 0 to number of batches-1
"""
pass # pylint: disable=unnecessary-pass
def postprocess(self, p, processed, *args):
"""
This function is called after processing ends for AlwaysVisible scripts.
@@ -457,6 +478,18 @@ class ScriptRunner:
errors.display(e, f'Running script before postprocess batch: {script.filename}')
log.debug(f'Script postprocess-batch: {s}')
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, **kwargs):
s = []
for script in self.alwayson_scripts:
try:
t0 = time.time()
args = p.per_script_args.get(script.title(), p.script_args[script.args_from:script.args_to])
script.postprocess_batch_list(p, pp, *args, **kwargs)
s.append(f'{script.title()}:{round(time.time()-t0, 2)}s')
except Exception as e:
errors.display(e, f'Running script before postprocess batch list: {script.filename}')
log.debug(f'Script postprocess-batch-list: {s}')
def postprocess_image(self, p, pp: PostprocessImageArgs):
s = []
for script in self.alwayson_scripts:
+5 -5
View File
@@ -534,10 +534,7 @@ def change_backend():
def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=None, op='model'): # pylint: disable=unused-argument
if op != 'model' and checkpoint_info is None and (shared.cmd_opts.ckpt is None or shared.cmd_opts.ckpt.lower() == 'none'):
return
import torch # pylint: disable=reimported,redefined-outer-name
devices.set_cuda_params()
if timer is None:
timer = Timer()
import logging
@@ -570,7 +567,6 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
if (model_data.sd_refiner is not None) and (checkpoint_info is not None) and (checkpoint_info.hash == model_data.sd_refiner.sd_checkpoint_info.hash): # trying to load the same model
return
shared.log.debug(f'Diffusers load {op} config: {diffusers_load_config}')
sd_model = None
try:
@@ -580,7 +576,9 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
if model_name is not None:
shared.log.info(f'Loading diffuser {op}: {model_name}')
model_file = modelloader.download_diffusers_model(hub_id=model_name)
devices.set_cuda_params()
try:
shared.log.debug(f'Diffusers load {op} config: {diffusers_load_config}')
sd_model = diffusers.DiffusionPipeline.from_pretrained(model_file, **diffusers_load_config)
except Exception as e:
shared.log.error(f'Diffusers failed loading model: {model_file} {e}')
@@ -592,8 +590,8 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
if checkpoint_info is None:
unload_model_weights(op=op)
return
shared.log.info(f'Loading diffuser {op}: {checkpoint_info.filename}')
devices.set_cuda_params()
vae = None
if op == 'model' or op == 'refiner':
vae_file, vae_source = sd_vae.resolve_vae(checkpoint_info.filename)
@@ -601,8 +599,10 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
if vae is not None:
diffusers_load_config["vae"] = vae
shared.log.info(f'Loading diffuser {op}: {checkpoint_info.filename}')
if not os.path.isfile(checkpoint_info.path):
try:
shared.log.debug(f'Diffusers load {op} config: {diffusers_load_config}')
sd_model = diffusers.DiffusionPipeline.from_pretrained(checkpoint_info.path, **diffusers_load_config)
except Exception as e:
shared.log.error(f'Diffusers {op} failed loading model: {checkpoint_info.path} {e}')
+1 -3
View File
@@ -23,9 +23,7 @@ def list_samplers(backend_name = shared.backend):
samplers = all_samplers
samplers_for_img2img = all_samplers
samplers_map = {}
shared.log.debug(f'Samplers enumerated: {[x.name for x in all_samplers]}')
list_samplers()
shared.log.debug(f'Available samplers: {[x.name for x in all_samplers]}')
def find_sampler_config(name):
+11 -10
View File
@@ -2,17 +2,16 @@ from collections import namedtuple
import numpy as np
import torch
from PIL import Image
from modules import devices, processing, images, sd_vae_approx, sd_samplers, sd_vae_taesd
from modules.shared import opts, state
from modules import devices, processing, images, sd_vae_approx, sd_samplers
import modules.shared as shared
import modules.taesd.sd_vae_taesd as sd_vae_taesd
SamplerData = namedtuple('SamplerData', ['name', 'constructor', 'aliases', 'options'])
approximation_indexes = {"Full VAE": 0, "Approximate NN": 1, "Approximate simple": 2, "TAESD": 3}
def setup_img2img_steps(p, steps=None):
if opts.img2img_fix_steps or steps is not None:
if shared.opts.img2img_fix_steps or steps is not None:
requested_steps = (steps or p.steps)
steps = int(requested_steps / min(p.denoising_strength, 0.999)) if p.denoising_strength > 0 else 0
t_enc = requested_steps - 1
@@ -25,7 +24,7 @@ def setup_img2img_steps(p, steps=None):
def single_sample_to_image(sample, approximation=None):
if approximation is None:
approximation = approximation_indexes.get(opts.show_progress_type, 0)
approximation = approximation_indexes.get(shared.opts.show_progress_type, 0)
if approximation == 0:
x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] * 0.5 + 0.5
elif approximation == 1:
@@ -33,8 +32,9 @@ def single_sample_to_image(sample, approximation=None):
elif approximation == 2:
x_sample = sd_vae_approx.cheap_approximation(sample) * 0.5 + 0.5
elif approximation == 3:
x_sample = sample * 1.5
x_sample = sd_vae_taesd.model()(x_sample.to(devices.device, devices.dtype).unsqueeze(0))[0].detach()
# x_sample = sample * 1.5
# x_sample = sd_vae_taesd.model()(x_sample.to(devices.device, devices.dtype).unsqueeze(0))[0].detach()
x_sample = sd_vae_taesd.decode(sample)
else:
shared.log.warning(f"Unknown image decode type: {approximation}")
return Image.new(mode="RGB", size=(512, 512))
@@ -52,10 +52,11 @@ def samples_to_image_grid(samples, approximation=None):
def store_latent(decoded):
state.current_latent = decoded
if opts.live_previews_enable and opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % opts.show_progress_every_n_steps == 0:
shared.state.current_latent = decoded
if shared.opts.live_previews_enable and shared.opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % shared.opts.show_progress_every_n_steps == 0:
if not shared.parallel_processing_allowed:
shared.state.assign_current_image(sample_to_image(decoded))
image = sample_to_image(decoded)
shared.state.assign_current_image(image)
def is_sampler_using_eta_noise_seed_delta(p):
+4 -3
View File
@@ -196,9 +196,10 @@ def load_vae_diffusers(_model, vae_file=None, vae_source="from unknown source"):
try:
import diffusers
if os.path.isfile(vae_file):
# load_config passed to from_single_file doesn't apply
# from_single_file by default downloads VAE1.5 config
shared.log.warning("Using SDXL VAE loaded from singular file will result in low contrast images.")
if shared.opts.diffusers_pipeline == "Stable Diffusion XL":
# load_config passed to from_single_file doesn't apply
# from_single_file by default downloads VAE1.5 config
shared.log.warning("Using SDXL VAE loaded from singular file will result in low contrast images.")
vae = diffusers.AutoencoderKL.from_single_file(vae_file)
vae = vae.to(devices.dtype_vae)
else:
-88
View File
@@ -1,88 +0,0 @@
"""
Tiny AutoEncoder for Stable Diffusion
(DNN for encoding / decoding SD's latent space)
https://github.com/madebyollin/taesd
"""
import os
import torch
import torch.nn as nn
from modules import devices, paths_internal
sd_vae_taesd = None
def conv(n_in, n_out, **kwargs):
return nn.Conv2d(n_in, n_out, 3, padding=1, **kwargs)
class Clamp(nn.Module):
@staticmethod
def forward(x):
return torch.tanh(x / 3) * 3
class Block(nn.Module):
def __init__(self, n_in, n_out):
super().__init__()
self.conv = nn.Sequential(conv(n_in, n_out), nn.ReLU(), conv(n_out, n_out), nn.ReLU(), conv(n_out, n_out))
self.skip = nn.Conv2d(n_in, n_out, 1, bias=False) if n_in != n_out else nn.Identity()
self.fuse = nn.ReLU()
def forward(self, x):
return self.fuse(self.conv(x) + self.skip(x))
def decoder():
return nn.Sequential(
Clamp(), conv(4, 64), nn.ReLU(),
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
Block(64, 64), conv(64, 3),
)
class TAESD(nn.Module): # pylint: disable=abstract-method
latent_magnitude = 3
latent_shift = 0.5
def __init__(self, decoder_path="taesd_decoder.pth"):
"""Initialize pretrained TAESD on the given device from the given checkpoints."""
super().__init__()
self.decoder = decoder()
self.decoder.load_state_dict(
torch.load(decoder_path, map_location='cpu' if devices.device.type != 'cuda' else None))
@staticmethod
def unscale_latents(x):
"""[0, 1] -> raw latents"""
return x.sub(TAESD.latent_shift).mul(2 * TAESD.latent_magnitude)
def download_model(model_path):
model_url = 'https://github.com/madebyollin/taesd/raw/main/taesd_decoder.pth'
if not os.path.exists(model_path):
os.makedirs(os.path.dirname(model_path), exist_ok=True)
print(f'Downloading TAESD decoder to: {model_path}')
torch.hub.download_url_to_file(model_url, model_path)
def model():
global sd_vae_taesd # pylint: disable=global-statement
if sd_vae_taesd is None:
model_path = os.path.join(paths_internal.models_path, "VAE-taesd", "taesd_decoder.pth")
download_model(model_path)
if os.path.exists(model_path):
sd_vae_taesd = TAESD(model_path)
sd_vae_taesd.eval()
sd_vae_taesd.to(devices.device, devices.dtype)
else:
raise FileNotFoundError('TAESD model not found')
return sd_vae_taesd.decoder
+25 -11
View File
@@ -171,13 +171,11 @@ class State:
return
import modules.sd_samplers # pylint: disable=W0621
try:
if opts.show_progress_grid:
self.assign_current_image(modules.sd_samplers.samples_to_image_grid(self.current_latent))
else:
self.assign_current_image(modules.sd_samplers.sample_to_image(self.current_latent))
except Exception:
pass
self.current_image_sampling_step = self.sampling_step
image = modules.sd_samplers.samples_to_image_grid(self.current_latent) if opts.show_progress_grid else modules.sd_samplers.sample_to_image(self.current_latent)
self.assign_current_image(image)
self.current_image_sampling_step = self.sampling_step
except Exception as e:
log.error(f'Error setting current image: step={self.sampling_step} {e}')
def assign_current_image(self, image):
self.current_image = image
@@ -382,8 +380,8 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
"rollback_vae": OptionInfo(False, "Attempt VAE roll back when produced NaN values (experimental)"),
"opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "),
"cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"),
"cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"),
"cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"),
# "cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"),
# "cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"),
"cuda_compile": OptionInfo(False, "Enable model compile (experimental)"),
"cuda_compile_backend": OptionInfo("none", "Model compile backend (experimental)", gr.Radio, lambda: {"choices": ['none', 'inductor', 'cudagraphs', 'aot_ts_nvfuser', 'hidet', 'ipex']}),
"cuda_compile_mode": OptionInfo("default", "Model compile mode (experimental)", gr.Radio, lambda: {"choices": ['default', 'reduce-overhead', 'max-autotune']}),
@@ -398,7 +396,6 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
options_templates.update(options_section(('diffusers', "Diffusers Settings"), {
"diffusers_allow_safetensors": OptionInfo(True, 'Diffusers allow loading from safetensors files'),
"diffusers_pipeline": OptionInfo(pipelines[0], 'Diffusers pipeline', gr.Dropdown, lambda: {"choices": pipelines}),
"diffusers_refiner_latents": OptionInfo(True, "Use latents when using refiner"),
"diffusers_move_base": OptionInfo(False, "Move base model to CPU when using refiner"),
"diffusers_move_refiner": OptionInfo(True, "Move refiner model to CPU when not in use"),
"diffusers_move_unet": OptionInfo(False, "Move UNet to CPU while VAE decoding"),
@@ -409,7 +406,7 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), {
"diffusers_vae_upcast": OptionInfo("default", "VAE upcasting", gr.Radio, lambda: {"choices": ['default', 'true', 'false']}),
"diffusers_vae_slicing": OptionInfo(True, "Enable VAE slicing"),
"diffusers_vae_tiling": OptionInfo(False, "Enable VAE tiling"),
"diffusers_attention_slicing": OptionInfo(False, "Enable attention slicing"),
"diffusers_attention_slicing": OptionInfo(True if devices.backend == "ipex" else False, "Enable attention slicing"),
"diffusers_model_load_variant": OptionInfo("default", "Diffusers model loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}),
"diffusers_vae_load_variant": OptionInfo("default", "Diffusers VAE loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}),
# "diffusers_force_zeros": OptionInfo(False, "Force zeros for prompts when empty"),
@@ -984,7 +981,24 @@ class Shared(sys.modules[__name__].__class__): # this class is here to provide s
def backend(self):
return Backend.ORIGINAL if opts.data['sd_backend'] == 'original' else Backend.DIFFUSERS
@property
def sd_model_type(self):
try:
if backend == Backend.ORIGINAL:
model_type = 'ldm'
elif "StableDiffusionXL" in self.sd_model.__class__.__name__:
model_type = 'sdxl'
elif "StableDiffusion" in self.sd_model.__class__.__name__:
model_type = 'sd'
elif "Kandinsky" in self.sd_model.__class__.__name__:
model_type = 'kandinsky'
else:
model_type = self.sd_model.__class__.__name__
except Exception:
model_type = 'unknown'
return model_type
sd_model = None
sd_refiner = None
sd_model_type = ''
sys.modules[__name__].__class__ = Shared
+61
View File
@@ -0,0 +1,61 @@
"""
Tiny AutoEncoder for Stable Diffusion
(DNN for encoding / decoding SD's latent space)
https://github.com/madebyollin/taesd
"""
import os
from PIL import Image
from modules import devices, paths_internal
from modules.taesd.taesd import TAESD
taesd_models = { 'sd-decoder': None, 'sd-encoder': None, 'sdxl-decoder': None, 'sdxl-encoder': None }
def download_model(model_path):
model_name = os.path.basename(model_path)
model_url = f'https://github.com/madebyollin/taesd/raw/main/{model_name}'
if not os.path.exists(model_path):
os.makedirs(os.path.dirname(model_path), exist_ok=True)
from modules.shared import log
log.info(f'Downloading TAESD decoder: {model_path}')
import torch
torch.hub.download_url_to_file(model_url, model_path)
def model(model_class = 'sd', model_type = 'decoder'):
vae = taesd_models[f'{model_class}-{model_type}']
if vae is None:
model_path = os.path.join(paths_internal.models_path, "TAESD", f"tae{model_class}_{model_type}.pth")
download_model(model_path)
if os.path.exists(model_path):
taesd_models[f'{model_class}-{model_type}'] = TAESD(decoder_path=model_path, encoder_path=None) if model_type == 'decoder' else TAESD(encoder_path=model_path, decoder_path=None)
vae = taesd_models[f'{model_class}-{model_type}']
vae.eval()
vae.to(devices.device, devices.dtype_vae)
else:
raise FileNotFoundError('TAESD model not found')
if vae is None:
return None
else:
return vae.decoder if model_type == 'decoder' else vae.encoder
def decode(latents):
from modules import shared
model_class = shared.sd_model_type
if model_class == 'ldm':
model_class = 'sd'
if 'sd' not in model_class:
shared.log.warning(f'TAESD unsupported model type: {model_class}')
return Image.new('RGB', (8, 8), color = (0, 0, 0))
vae = taesd_models[f'{model_class}-decoder']
if vae is None:
model_path = os.path.join(paths_internal.models_path, "TAESD", f"tae{model_class}_decoder.pth")
download_model(model_path)
if os.path.exists(model_path):
taesd_models[f'{model_class}-decoder'] = TAESD(decoder_path=model_path, encoder_path=None)
vae = taesd_models[f'{model_class}-decoder']
vae.to(devices.device, devices.dtype_vae)
enc = latents.unsqueeze(0).to(devices.device, devices.dtype_vae)
image = vae.decoder(enc).clamp(0, 1).detach()
return image[0]
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Tiny AutoEncoder for Stable Diffusion
(DNN for encoding / decoding SD's latent space)
"""
import torch
import torch.nn as nn
def conv(n_in, n_out, **kwargs):
return nn.Conv2d(n_in, n_out, 3, padding=1, **kwargs)
class Clamp(nn.Module):
def forward(self, x):
return torch.tanh(x / 3) * 3
class Block(nn.Module):
def __init__(self, n_in, n_out):
super().__init__()
self.conv = nn.Sequential(conv(n_in, n_out), nn.ReLU(), conv(n_out, n_out), nn.ReLU(), conv(n_out, n_out))
self.skip = nn.Conv2d(n_in, n_out, 1, bias=False) if n_in != n_out else nn.Identity()
self.fuse = nn.ReLU()
def forward(self, x):
return self.fuse(self.conv(x) + self.skip(x))
def Encoder():
return nn.Sequential(
conv(3, 64), Block(64, 64),
conv(64, 64, stride=2, bias=False), Block(64, 64), Block(64, 64), Block(64, 64),
conv(64, 64, stride=2, bias=False), Block(64, 64), Block(64, 64), Block(64, 64),
conv(64, 64, stride=2, bias=False), Block(64, 64), Block(64, 64), Block(64, 64),
conv(64, 4),
)
def Decoder():
return nn.Sequential(
Clamp(), conv(4, 64), nn.ReLU(),
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
Block(64, 64), conv(64, 3),
)
class TAESD(nn.Module):
latent_magnitude = 3
latent_shift = 0.5
def __init__(self, encoder_path="taesd_encoder.pth", decoder_path="taesd_decoder.pth"):
"""Initialize pretrained TAESD on the given device from the given checkpoints."""
super().__init__()
self.encoder = Encoder()
self.decoder = Decoder()
if encoder_path is not None:
self.encoder.load_state_dict(torch.load(encoder_path, map_location="cpu"))
if decoder_path is not None:
self.decoder.load_state_dict(torch.load(decoder_path, map_location="cpu"))
@staticmethod
def scale_latents(x):
"""raw latents -> [0, 1]"""
return x.div(2 * TAESD.latent_magnitude).add(TAESD.latent_shift).clamp(0, 1)
@staticmethod
def unscale_latents(x):
"""[0, 1] -> raw latents"""
return x.sub(TAESD.latent_shift).mul(2 * TAESD.latent_magnitude)
@torch.no_grad()
def main():
from PIL import Image
import sys
import torchvision.transforms.functional as TF
dev = torch.device("cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu")
print("Using device", dev)
taesd = TAESD().to(dev)
for im_path in sys.argv[1:]:
im = TF.to_tensor(Image.open(im_path).convert("RGB")).unsqueeze(0).to(dev)
# encode image, quantize, and save to file
im_enc = taesd.scale_latents(taesd.encoder(im)).mul_(255).round_().byte()
enc_path = im_path + ".encoded.png"
TF.to_pil_image(im_enc[0]).save(enc_path)
print(f"Encoded {im_path} to {enc_path}")
# load the saved file, dequantize, and decode
im_enc = taesd.unscale_latents(TF.to_tensor(Image.open(enc_path)).unsqueeze(0).to(dev))
im_dec = taesd.decoder(im_enc).clamp(0, 1)
dec_path = im_path + ".decoded.png"
print(f"Decoded {enc_path} to {dec_path}")
TF.to_pil_image(im_dec[0]).save(dec_path)
if __name__ == "__main__":
main()
+1 -1
View File
@@ -7,7 +7,7 @@ from modules.memstats import memory_stats
def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, latent_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, diffusers_guidance_rescale: float, clip_skip: int, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, refiner_start: int, refiner_prompt: str, refiner_negative: str, override_settings_texts, *args): # pylint: disable=unused-argument
shared.log.debug(f'txt2img: id_task={id_task}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|clip_skip={clip_skip}|seed={seed}|subseed={subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}||height={height}|width={width}|enable_hr={enable_hr}|denoising_strength={denoising_strength}|hr_scale={hr_scale}|hr_upscaler={hr_upscaler}|hr_second_pass_steps={hr_second_pass_steps}|hr_resize_x={hr_resize_x}|hr_resize_y={hr_resize_y}|image_cfg_scale={image_cfg_scale}|diffusers_guidance_rescale={diffusers_guidance_rescale}|refiner_start={refiner_start}||refiner_prompt={refiner_prompt}|refiner_negative={refiner_negative}|override_settings_texts={override_settings_texts}args={args}')
shared.log.debug(f'txt2img: id_task={id_task}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|clip_skip={clip_skip}|seed={seed}|subseed={subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}||height={height}|width={width}|enable_hr={enable_hr}|denoising_strength={denoising_strength}|hr_scale={hr_scale}|hr_upscaler={hr_upscaler}|hr_second_pass_steps={hr_second_pass_steps}|hr_resize_x={hr_resize_x}|hr_resize_y={hr_resize_y}|image_cfg_scale={image_cfg_scale}|diffusers_guidance_rescale={diffusers_guidance_rescale}|refiner_start={refiner_start}||refiner_prompt={refiner_prompt}|refiner_negative={refiner_negative}|override_settings_texts={override_settings_texts}|args={args}')
if shared.sd_model is None:
shared.log.warning('Model not loaded')
+1
View File
@@ -48,6 +48,7 @@ ignore = [
"C408", # Rewrite as a literal
"E402", # Module level import not at top of file
"F401", # Imported but unused
"EXE001", # Shebang present
"ISC003", # Implicit string concatenation
"RUF005", # Consider concatenation
"RUF012", # Mutable class attributes
+1
View File
@@ -52,6 +52,7 @@ opencv-python-headless==4.7.0.72
diffusers==0.19.3
einops==0.4.1
gradio==3.32.0
huggingface_hub==0.16.4
numexpr==2.8.4
numpy==1.23.5
numba==0.57.0
+3
View File
@@ -105,6 +105,9 @@ def initialize():
shared.disable_extensions()
check_rollback_vae()
modules.sd_samplers.list_samplers()
startup_timer.record("samplers")
modules.sd_vae.refresh_vae_list()
startup_timer.record("vae")
+1 -1
View File
@@ -96,7 +96,7 @@ if [[ ! -z "${ACCELERATE}" ]] && [ ${ACCELERATE}="True" ] && [ -x "$(command -v
then
echo "Launching accelerate launch.py..."
exec accelerate launch --num_cpu_threads_per_process=6 launch.py "$@"
elif [[ -z "${first_launch}" ]] && [[ $(uname -a) != *WSL2* ]] && [ -x "$(command -v ipexrun)" ] && [ -x "$(command -v numactl)" ] && [ -x "$(command -v sycl-ls)" ]
elif [[ "$@" == *"--use-ipex"* ]] && [[ -z "${first_launch}" ]] && [[ $(uname -a) != *WSL2* ]] && [ -x "$(command -v ipexrun)" ] && [ -x "$(command -v numactl)" ] && [ -x "$(command -v sycl-ls)" ]
then
echo "Launching ipexrun launch.py..."
exec ipexrun launch.py "$@"
+1 -1
Submodule wiki updated: f76cc3a9ac...35142f02ae