mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-16 00:12:33 +08:00
Merge remote-tracking branch 'origin/master' into group-nodes
This commit is contained in:
commit
19ff72db0b
@ -858,7 +858,7 @@ def predict_eps_sigma(model, input, sigma_in, **kwargs):
|
|||||||
return (input - model(input, sigma_in, **kwargs)) / sigma
|
return (input - model(input, sigma_in, **kwargs)) / sigma
|
||||||
|
|
||||||
|
|
||||||
def sample_unipc(model, noise, image, sigmas, sampling_function, max_denoise, extra_args=None, callback=None, disable=False, noise_mask=None, variant='bh1'):
|
def sample_unipc(model, noise, image, sigmas, max_denoise, extra_args=None, callback=None, disable=False, noise_mask=None, variant='bh1'):
|
||||||
timesteps = sigmas.clone()
|
timesteps = sigmas.clone()
|
||||||
if sigmas[-1] == 0:
|
if sigmas[-1] == 0:
|
||||||
timesteps = sigmas[:]
|
timesteps = sigmas[:]
|
||||||
|
|||||||
@ -624,6 +624,11 @@ class UNetModel(nn.Module):
|
|||||||
transformer_options["block"] = ("input", id)
|
transformer_options["block"] = ("input", id)
|
||||||
h = forward_timestep_embed(module, h, emb, context, transformer_options)
|
h = forward_timestep_embed(module, h, emb, context, transformer_options)
|
||||||
h = apply_control(h, control, 'input')
|
h = apply_control(h, control, 'input')
|
||||||
|
if "input_block_patch" in transformer_patches:
|
||||||
|
patch = transformer_patches["input_block_patch"]
|
||||||
|
for p in patch:
|
||||||
|
h = p(h, transformer_options)
|
||||||
|
|
||||||
hs.append(h)
|
hs.append(h)
|
||||||
|
|
||||||
transformer_options["block"] = ("middle", 0)
|
transformer_options["block"] = ("middle", 0)
|
||||||
|
|||||||
@ -157,6 +157,16 @@ class BaseModel(torch.nn.Module):
|
|||||||
def set_inpaint(self):
|
def set_inpaint(self):
|
||||||
self.inpaint_model = True
|
self.inpaint_model = True
|
||||||
|
|
||||||
|
def memory_required(self, input_shape):
|
||||||
|
area = input_shape[0] * input_shape[2] * input_shape[3]
|
||||||
|
if comfy.model_management.xformers_enabled() or comfy.model_management.pytorch_attention_flash_attention():
|
||||||
|
#TODO: this needs to be tweaked
|
||||||
|
return (area / (comfy.model_management.dtype_size(self.get_dtype()) * 10)) * (1024 * 1024)
|
||||||
|
else:
|
||||||
|
#TODO: this formula might be too aggressive since I tweaked the sub-quad and split algorithms to use less memory.
|
||||||
|
return (((area * 0.6) / 0.9) + 1024) * (1024 * 1024)
|
||||||
|
|
||||||
|
|
||||||
def unclip_adm(unclip_conditioning, device, noise_augmentor, noise_augment_merge=0.0):
|
def unclip_adm(unclip_conditioning, device, noise_augmentor, noise_augment_merge=0.0):
|
||||||
adm_inputs = []
|
adm_inputs = []
|
||||||
weights = []
|
weights = []
|
||||||
|
|||||||
@ -133,6 +133,10 @@ else:
|
|||||||
import xformers
|
import xformers
|
||||||
import xformers.ops
|
import xformers.ops
|
||||||
XFORMERS_IS_AVAILABLE = True
|
XFORMERS_IS_AVAILABLE = True
|
||||||
|
try:
|
||||||
|
XFORMERS_IS_AVAILABLE = xformers._has_cpp_library
|
||||||
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
XFORMERS_VERSION = xformers.version.__version__
|
XFORMERS_VERSION = xformers.version.__version__
|
||||||
print("xformers version:", XFORMERS_VERSION)
|
print("xformers version:", XFORMERS_VERSION)
|
||||||
@ -579,27 +583,6 @@ def get_free_memory(dev=None, torch_free_too=False):
|
|||||||
else:
|
else:
|
||||||
return mem_free_total
|
return mem_free_total
|
||||||
|
|
||||||
def batch_area_memory(area):
|
|
||||||
if xformers_enabled() or pytorch_attention_flash_attention():
|
|
||||||
#TODO: these formulas are copied from maximum_batch_area below
|
|
||||||
return (area / 20) * (1024 * 1024)
|
|
||||||
else:
|
|
||||||
return (((area * 0.6) / 0.9) + 1024) * (1024 * 1024)
|
|
||||||
|
|
||||||
def maximum_batch_area():
|
|
||||||
global vram_state
|
|
||||||
if vram_state == VRAMState.NO_VRAM:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
memory_free = get_free_memory() / (1024 * 1024)
|
|
||||||
if xformers_enabled() or pytorch_attention_flash_attention():
|
|
||||||
#TODO: this needs to be tweaked
|
|
||||||
area = 20 * memory_free
|
|
||||||
else:
|
|
||||||
#TODO: this formula is because AMD sucks and has memory management issues which might be fixed in the future
|
|
||||||
area = ((memory_free - 1024) * 0.9) / (0.6)
|
|
||||||
return int(max(area, 0))
|
|
||||||
|
|
||||||
def cpu_mode():
|
def cpu_mode():
|
||||||
global cpu_state
|
global cpu_state
|
||||||
return cpu_state == CPUState.CPU
|
return cpu_state == CPUState.CPU
|
||||||
|
|||||||
@ -52,6 +52,9 @@ class ModelPatcher:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def memory_required(self, input_shape):
|
||||||
|
return self.model.memory_required(input_shape=input_shape)
|
||||||
|
|
||||||
def set_model_sampler_cfg_function(self, sampler_cfg_function):
|
def set_model_sampler_cfg_function(self, sampler_cfg_function):
|
||||||
if len(inspect.signature(sampler_cfg_function).parameters) == 3:
|
if len(inspect.signature(sampler_cfg_function).parameters) == 3:
|
||||||
self.model_options["sampler_cfg_function"] = lambda args: sampler_cfg_function(args["cond"], args["uncond"], args["cond_scale"]) #Old way
|
self.model_options["sampler_cfg_function"] = lambda args: sampler_cfg_function(args["cond"], args["uncond"], args["cond_scale"]) #Old way
|
||||||
@ -93,6 +96,9 @@ class ModelPatcher:
|
|||||||
def set_model_attn2_output_patch(self, patch):
|
def set_model_attn2_output_patch(self, patch):
|
||||||
self.set_model_patch(patch, "attn2_output_patch")
|
self.set_model_patch(patch, "attn2_output_patch")
|
||||||
|
|
||||||
|
def set_model_input_block_patch(self, patch):
|
||||||
|
self.set_model_patch(patch, "input_block_patch")
|
||||||
|
|
||||||
def set_model_output_block_patch(self, patch):
|
def set_model_output_block_patch(self, patch):
|
||||||
self.set_model_patch(patch, "output_block_patch")
|
self.set_model_patch(patch, "output_block_patch")
|
||||||
|
|
||||||
@ -176,7 +182,7 @@ class ModelPatcher:
|
|||||||
inplace_update = self.weight_inplace_update
|
inplace_update = self.weight_inplace_update
|
||||||
|
|
||||||
if key not in self.backup:
|
if key not in self.backup:
|
||||||
self.backup[key] = weight.to(device=device_to, copy=inplace_update)
|
self.backup[key] = weight.to(device=self.offload_device, copy=inplace_update)
|
||||||
|
|
||||||
if device_to is not None:
|
if device_to is not None:
|
||||||
temp_weight = comfy.model_management.cast_to_device(weight, device_to, torch.float32, copy=True)
|
temp_weight = comfy.model_management.cast_to_device(weight, device_to, torch.float32, copy=True)
|
||||||
|
|||||||
@ -83,7 +83,7 @@ def prepare_sampling(model, noise_shape, positive, negative, noise_mask):
|
|||||||
|
|
||||||
real_model = None
|
real_model = None
|
||||||
models, inference_memory = get_additional_models(positive, negative, model.model_dtype())
|
models, inference_memory = get_additional_models(positive, negative, model.model_dtype())
|
||||||
comfy.model_management.load_models_gpu([model] + models, comfy.model_management.batch_area_memory(noise_shape[0] * noise_shape[2] * noise_shape[3]) + inference_memory)
|
comfy.model_management.load_models_gpu([model] + models, model.memory_required(noise_shape) + inference_memory)
|
||||||
real_model = model.model
|
real_model = model.model
|
||||||
|
|
||||||
return real_model, positive, negative, noise_mask, models
|
return real_model, positive, negative, noise_mask, models
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import comfy.conds
|
|||||||
|
|
||||||
#The main sampling function shared by all the samplers
|
#The main sampling function shared by all the samplers
|
||||||
#Returns denoised
|
#Returns denoised
|
||||||
def sampling_function(model_function, x, timestep, uncond, cond, cond_scale, model_options={}, seed=None):
|
def sampling_function(model, x, timestep, uncond, cond, cond_scale, model_options={}, seed=None):
|
||||||
def get_area_and_mult(conds, x_in, timestep_in):
|
def get_area_and_mult(conds, x_in, timestep_in):
|
||||||
area = (x_in.shape[2], x_in.shape[3], 0, 0)
|
area = (x_in.shape[2], x_in.shape[3], 0, 0)
|
||||||
strength = 1.0
|
strength = 1.0
|
||||||
@ -134,7 +134,7 @@ def sampling_function(model_function, x, timestep, uncond, cond, cond_scale, mod
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def calc_cond_uncond_batch(model_function, cond, uncond, x_in, timestep, max_total_area, model_options):
|
def calc_cond_uncond_batch(model, cond, uncond, x_in, timestep, model_options):
|
||||||
out_cond = torch.zeros_like(x_in)
|
out_cond = torch.zeros_like(x_in)
|
||||||
out_count = torch.ones_like(x_in) * 1e-37
|
out_count = torch.ones_like(x_in) * 1e-37
|
||||||
|
|
||||||
@ -170,9 +170,11 @@ def sampling_function(model_function, x, timestep, uncond, cond, cond_scale, mod
|
|||||||
to_batch_temp.reverse()
|
to_batch_temp.reverse()
|
||||||
to_batch = to_batch_temp[:1]
|
to_batch = to_batch_temp[:1]
|
||||||
|
|
||||||
|
free_memory = model_management.get_free_memory(x_in.device)
|
||||||
for i in range(1, len(to_batch_temp) + 1):
|
for i in range(1, len(to_batch_temp) + 1):
|
||||||
batch_amount = to_batch_temp[:len(to_batch_temp)//i]
|
batch_amount = to_batch_temp[:len(to_batch_temp)//i]
|
||||||
if (len(batch_amount) * first_shape[0] * first_shape[2] * first_shape[3] < max_total_area):
|
input_shape = [len(batch_amount) * first_shape[0]] + list(first_shape)[1:]
|
||||||
|
if model.memory_required(input_shape) < free_memory:
|
||||||
to_batch = batch_amount
|
to_batch = batch_amount
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -221,9 +223,9 @@ def sampling_function(model_function, x, timestep, uncond, cond, cond_scale, mod
|
|||||||
c['transformer_options'] = transformer_options
|
c['transformer_options'] = transformer_options
|
||||||
|
|
||||||
if 'model_function_wrapper' in model_options:
|
if 'model_function_wrapper' in model_options:
|
||||||
output = model_options['model_function_wrapper'](model_function, {"input": input_x, "timestep": timestep_, "c": c, "cond_or_uncond": cond_or_uncond}).chunk(batch_chunks)
|
output = model_options['model_function_wrapper'](model.apply_model, {"input": input_x, "timestep": timestep_, "c": c, "cond_or_uncond": cond_or_uncond}).chunk(batch_chunks)
|
||||||
else:
|
else:
|
||||||
output = model_function(input_x, timestep_, **c).chunk(batch_chunks)
|
output = model.apply_model(input_x, timestep_, **c).chunk(batch_chunks)
|
||||||
del input_x
|
del input_x
|
||||||
|
|
||||||
for o in range(batch_chunks):
|
for o in range(batch_chunks):
|
||||||
@ -242,11 +244,10 @@ def sampling_function(model_function, x, timestep, uncond, cond, cond_scale, mod
|
|||||||
return out_cond, out_uncond
|
return out_cond, out_uncond
|
||||||
|
|
||||||
|
|
||||||
max_total_area = model_management.maximum_batch_area()
|
|
||||||
if math.isclose(cond_scale, 1.0):
|
if math.isclose(cond_scale, 1.0):
|
||||||
uncond = None
|
uncond = None
|
||||||
|
|
||||||
cond, uncond = calc_cond_uncond_batch(model_function, cond, uncond, x, timestep, max_total_area, model_options)
|
cond, uncond = calc_cond_uncond_batch(model, cond, uncond, x, timestep, model_options)
|
||||||
if "sampler_cfg_function" in model_options:
|
if "sampler_cfg_function" in model_options:
|
||||||
args = {"cond": x - cond, "uncond": x - uncond, "cond_scale": cond_scale, "timestep": timestep, "input": x, "sigma": timestep}
|
args = {"cond": x - cond, "uncond": x - uncond, "cond_scale": cond_scale, "timestep": timestep, "input": x, "sigma": timestep}
|
||||||
return x - model_options["sampler_cfg_function"](args)
|
return x - model_options["sampler_cfg_function"](args)
|
||||||
@ -258,7 +259,7 @@ class CFGNoisePredictor(torch.nn.Module):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.inner_model = model
|
self.inner_model = model
|
||||||
def apply_model(self, x, timestep, cond, uncond, cond_scale, model_options={}, seed=None):
|
def apply_model(self, x, timestep, cond, uncond, cond_scale, model_options={}, seed=None):
|
||||||
out = sampling_function(self.inner_model.apply_model, x, timestep, uncond, cond, cond_scale, model_options=model_options, seed=seed)
|
out = sampling_function(self.inner_model, x, timestep, uncond, cond, cond_scale, model_options=model_options, seed=seed)
|
||||||
return out
|
return out
|
||||||
def forward(self, *args, **kwargs):
|
def forward(self, *args, **kwargs):
|
||||||
return self.apply_model(*args, **kwargs)
|
return self.apply_model(*args, **kwargs)
|
||||||
@ -511,52 +512,69 @@ class Sampler:
|
|||||||
|
|
||||||
class UNIPC(Sampler):
|
class UNIPC(Sampler):
|
||||||
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
||||||
return uni_pc.sample_unipc(model_wrap, noise, latent_image, sigmas, sampling_function=sampling_function, max_denoise=self.max_denoise(model_wrap, sigmas), extra_args=extra_args, noise_mask=denoise_mask, callback=callback, disable=disable_pbar)
|
return uni_pc.sample_unipc(model_wrap, noise, latent_image, sigmas, max_denoise=self.max_denoise(model_wrap, sigmas), extra_args=extra_args, noise_mask=denoise_mask, callback=callback, disable=disable_pbar)
|
||||||
|
|
||||||
class UNIPCBH2(Sampler):
|
class UNIPCBH2(Sampler):
|
||||||
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
||||||
return uni_pc.sample_unipc(model_wrap, noise, latent_image, sigmas, sampling_function=sampling_function, max_denoise=self.max_denoise(model_wrap, sigmas), extra_args=extra_args, noise_mask=denoise_mask, callback=callback, variant='bh2', disable=disable_pbar)
|
return uni_pc.sample_unipc(model_wrap, noise, latent_image, sigmas, max_denoise=self.max_denoise(model_wrap, sigmas), extra_args=extra_args, noise_mask=denoise_mask, callback=callback, variant='bh2', disable=disable_pbar)
|
||||||
|
|
||||||
KSAMPLER_NAMES = ["euler", "euler_ancestral", "heun", "dpm_2", "dpm_2_ancestral",
|
KSAMPLER_NAMES = ["euler", "euler_ancestral", "heun", "dpm_2", "dpm_2_ancestral",
|
||||||
"lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_sde", "dpmpp_sde_gpu",
|
"lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_sde", "dpmpp_sde_gpu",
|
||||||
"dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm"]
|
"dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm"]
|
||||||
|
|
||||||
|
class KSAMPLER(Sampler):
|
||||||
|
def __init__(self, sampler_function, extra_options={}, inpaint_options={}):
|
||||||
|
self.sampler_function = sampler_function
|
||||||
|
self.extra_options = extra_options
|
||||||
|
self.inpaint_options = inpaint_options
|
||||||
|
|
||||||
|
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
||||||
|
extra_args["denoise_mask"] = denoise_mask
|
||||||
|
model_k = KSamplerX0Inpaint(model_wrap)
|
||||||
|
model_k.latent_image = latent_image
|
||||||
|
if self.inpaint_options.get("random", False): #TODO: Should this be the default?
|
||||||
|
generator = torch.manual_seed(extra_args.get("seed", 41) + 1)
|
||||||
|
model_k.noise = torch.randn(noise.shape, generator=generator, device="cpu").to(noise.dtype).to(noise.device)
|
||||||
|
else:
|
||||||
|
model_k.noise = noise
|
||||||
|
|
||||||
|
if self.max_denoise(model_wrap, sigmas):
|
||||||
|
noise = noise * torch.sqrt(1.0 + sigmas[0] ** 2.0)
|
||||||
|
else:
|
||||||
|
noise = noise * sigmas[0]
|
||||||
|
|
||||||
|
k_callback = None
|
||||||
|
total_steps = len(sigmas) - 1
|
||||||
|
if callback is not None:
|
||||||
|
k_callback = lambda x: callback(x["i"], x["denoised"], x["x"], total_steps)
|
||||||
|
|
||||||
|
if latent_image is not None:
|
||||||
|
noise += latent_image
|
||||||
|
|
||||||
|
samples = self.sampler_function(model_k, noise, sigmas, extra_args=extra_args, callback=k_callback, disable=disable_pbar, **self.extra_options)
|
||||||
|
return samples
|
||||||
|
|
||||||
|
|
||||||
def ksampler(sampler_name, extra_options={}, inpaint_options={}):
|
def ksampler(sampler_name, extra_options={}, inpaint_options={}):
|
||||||
class KSAMPLER(Sampler):
|
if sampler_name == "dpm_fast":
|
||||||
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
def dpm_fast_function(model, noise, sigmas, extra_args, callback, disable):
|
||||||
extra_args["denoise_mask"] = denoise_mask
|
|
||||||
model_k = KSamplerX0Inpaint(model_wrap)
|
|
||||||
model_k.latent_image = latent_image
|
|
||||||
if inpaint_options.get("random", False): #TODO: Should this be the default?
|
|
||||||
generator = torch.manual_seed(extra_args.get("seed", 41) + 1)
|
|
||||||
model_k.noise = torch.randn(noise.shape, generator=generator, device="cpu").to(noise.dtype).to(noise.device)
|
|
||||||
else:
|
|
||||||
model_k.noise = noise
|
|
||||||
|
|
||||||
if self.max_denoise(model_wrap, sigmas):
|
|
||||||
noise = noise * torch.sqrt(1.0 + sigmas[0] ** 2.0)
|
|
||||||
else:
|
|
||||||
noise = noise * sigmas[0]
|
|
||||||
|
|
||||||
k_callback = None
|
|
||||||
total_steps = len(sigmas) - 1
|
|
||||||
if callback is not None:
|
|
||||||
k_callback = lambda x: callback(x["i"], x["denoised"], x["x"], total_steps)
|
|
||||||
|
|
||||||
sigma_min = sigmas[-1]
|
sigma_min = sigmas[-1]
|
||||||
if sigma_min == 0:
|
if sigma_min == 0:
|
||||||
sigma_min = sigmas[-2]
|
sigma_min = sigmas[-2]
|
||||||
|
total_steps = len(sigmas) - 1
|
||||||
|
return k_diffusion_sampling.sample_dpm_fast(model, noise, sigma_min, sigmas[0], total_steps, extra_args=extra_args, callback=callback, disable=disable)
|
||||||
|
sampler_function = dpm_fast_function
|
||||||
|
elif sampler_name == "dpm_adaptive":
|
||||||
|
def dpm_adaptive_function(model, noise, sigmas, extra_args, callback, disable):
|
||||||
|
sigma_min = sigmas[-1]
|
||||||
|
if sigma_min == 0:
|
||||||
|
sigma_min = sigmas[-2]
|
||||||
|
return k_diffusion_sampling.sample_dpm_adaptive(model, noise, sigma_min, sigmas[0], extra_args=extra_args, callback=callback, disable=disable)
|
||||||
|
sampler_function = dpm_adaptive_function
|
||||||
|
else:
|
||||||
|
sampler_function = getattr(k_diffusion_sampling, "sample_{}".format(sampler_name))
|
||||||
|
|
||||||
if latent_image is not None:
|
return KSAMPLER(sampler_function, extra_options, inpaint_options)
|
||||||
noise += latent_image
|
|
||||||
if sampler_name == "dpm_fast":
|
|
||||||
samples = k_diffusion_sampling.sample_dpm_fast(model_k, noise, sigma_min, sigmas[0], total_steps, extra_args=extra_args, callback=k_callback, disable=disable_pbar)
|
|
||||||
elif sampler_name == "dpm_adaptive":
|
|
||||||
samples = k_diffusion_sampling.sample_dpm_adaptive(model_k, noise, sigma_min, sigmas[0], extra_args=extra_args, callback=k_callback, disable=disable_pbar)
|
|
||||||
else:
|
|
||||||
samples = getattr(k_diffusion_sampling, "sample_{}".format(sampler_name))(model_k, noise, sigmas, extra_args=extra_args, callback=k_callback, disable=disable_pbar, **extra_options)
|
|
||||||
return samples
|
|
||||||
return KSAMPLER
|
|
||||||
|
|
||||||
def wrap_model(model):
|
def wrap_model(model):
|
||||||
model_denoise = CFGNoisePredictor(model)
|
model_denoise = CFGNoisePredictor(model)
|
||||||
@ -617,11 +635,11 @@ def calculate_sigmas_scheduler(model, scheduler_name, steps):
|
|||||||
print("error invalid scheduler", self.scheduler)
|
print("error invalid scheduler", self.scheduler)
|
||||||
return sigmas
|
return sigmas
|
||||||
|
|
||||||
def sampler_class(name):
|
def sampler_object(name):
|
||||||
if name == "uni_pc":
|
if name == "uni_pc":
|
||||||
sampler = UNIPC
|
sampler = UNIPC()
|
||||||
elif name == "uni_pc_bh2":
|
elif name == "uni_pc_bh2":
|
||||||
sampler = UNIPCBH2
|
sampler = UNIPCBH2()
|
||||||
elif name == "ddim":
|
elif name == "ddim":
|
||||||
sampler = ksampler("euler", inpaint_options={"random": True})
|
sampler = ksampler("euler", inpaint_options={"random": True})
|
||||||
else:
|
else:
|
||||||
@ -686,6 +704,6 @@ class KSampler:
|
|||||||
else:
|
else:
|
||||||
return torch.zeros_like(noise)
|
return torch.zeros_like(noise)
|
||||||
|
|
||||||
sampler = sampler_class(self.sampler)
|
sampler = sampler_object(self.sampler)
|
||||||
|
|
||||||
return sample(self.model, noise, positive, negative, cfg, self.device, sampler(), sigmas, self.model_options, latent_image=latent_image, denoise_mask=denoise_mask, callback=callback, disable_pbar=disable_pbar, seed=seed)
|
return sample(self.model, noise, positive, negative, cfg, self.device, sampler, sigmas, self.model_options, latent_image=latent_image, denoise_mask=denoise_mask, callback=callback, disable_pbar=disable_pbar, seed=seed)
|
||||||
|
|||||||
@ -307,13 +307,13 @@ def bislerp(samples, width, height):
|
|||||||
res[dot < 1e-5 - 1] = (b1 * (1.0-r) + b2 * r)[dot < 1e-5 - 1]
|
res[dot < 1e-5 - 1] = (b1 * (1.0-r) + b2 * r)[dot < 1e-5 - 1]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def generate_bilinear_data(length_old, length_new):
|
def generate_bilinear_data(length_old, length_new, device):
|
||||||
coords_1 = torch.arange(length_old).reshape((1,1,1,-1)).to(torch.float32)
|
coords_1 = torch.arange(length_old, dtype=torch.float32, device=device).reshape((1,1,1,-1))
|
||||||
coords_1 = torch.nn.functional.interpolate(coords_1, size=(1, length_new), mode="bilinear")
|
coords_1 = torch.nn.functional.interpolate(coords_1, size=(1, length_new), mode="bilinear")
|
||||||
ratios = coords_1 - coords_1.floor()
|
ratios = coords_1 - coords_1.floor()
|
||||||
coords_1 = coords_1.to(torch.int64)
|
coords_1 = coords_1.to(torch.int64)
|
||||||
|
|
||||||
coords_2 = torch.arange(length_old).reshape((1,1,1,-1)).to(torch.float32) + 1
|
coords_2 = torch.arange(length_old, dtype=torch.float32, device=device).reshape((1,1,1,-1)) + 1
|
||||||
coords_2[:,:,:,-1] -= 1
|
coords_2[:,:,:,-1] -= 1
|
||||||
coords_2 = torch.nn.functional.interpolate(coords_2, size=(1, length_new), mode="bilinear")
|
coords_2 = torch.nn.functional.interpolate(coords_2, size=(1, length_new), mode="bilinear")
|
||||||
coords_2 = coords_2.to(torch.int64)
|
coords_2 = coords_2.to(torch.int64)
|
||||||
@ -323,7 +323,7 @@ def bislerp(samples, width, height):
|
|||||||
h_new, w_new = (height, width)
|
h_new, w_new = (height, width)
|
||||||
|
|
||||||
#linear w
|
#linear w
|
||||||
ratios, coords_1, coords_2 = generate_bilinear_data(w, w_new)
|
ratios, coords_1, coords_2 = generate_bilinear_data(w, w_new, samples.device)
|
||||||
coords_1 = coords_1.expand((n, c, h, -1))
|
coords_1 = coords_1.expand((n, c, h, -1))
|
||||||
coords_2 = coords_2.expand((n, c, h, -1))
|
coords_2 = coords_2.expand((n, c, h, -1))
|
||||||
ratios = ratios.expand((n, 1, h, -1))
|
ratios = ratios.expand((n, 1, h, -1))
|
||||||
@ -336,7 +336,7 @@ def bislerp(samples, width, height):
|
|||||||
result = result.reshape(n, h, w_new, c).movedim(-1, 1)
|
result = result.reshape(n, h, w_new, c).movedim(-1, 1)
|
||||||
|
|
||||||
#linear h
|
#linear h
|
||||||
ratios, coords_1, coords_2 = generate_bilinear_data(h, h_new)
|
ratios, coords_1, coords_2 = generate_bilinear_data(h, h_new, samples.device)
|
||||||
coords_1 = coords_1.reshape((1,1,-1,1)).expand((n, c, -1, w_new))
|
coords_1 = coords_1.reshape((1,1,-1,1)).expand((n, c, -1, w_new))
|
||||||
coords_2 = coords_2.reshape((1,1,-1,1)).expand((n, c, -1, w_new))
|
coords_2 = coords_2.reshape((1,1,-1,1)).expand((n, c, -1, w_new))
|
||||||
ratios = ratios.reshape((1,1,-1,1)).expand((n, 1, -1, w_new))
|
ratios = ratios.reshape((1,1,-1,1)).expand((n, 1, -1, w_new))
|
||||||
|
|||||||
@ -16,7 +16,7 @@ class BasicScheduler:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SIGMAS",)
|
RETURN_TYPES = ("SIGMAS",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/schedulers"
|
||||||
|
|
||||||
FUNCTION = "get_sigmas"
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ class KarrasScheduler:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SIGMAS",)
|
RETURN_TYPES = ("SIGMAS",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/schedulers"
|
||||||
|
|
||||||
FUNCTION = "get_sigmas"
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class ExponentialScheduler:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SIGMAS",)
|
RETURN_TYPES = ("SIGMAS",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/schedulers"
|
||||||
|
|
||||||
FUNCTION = "get_sigmas"
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ class PolyexponentialScheduler:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SIGMAS",)
|
RETURN_TYPES = ("SIGMAS",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/schedulers"
|
||||||
|
|
||||||
FUNCTION = "get_sigmas"
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ class VPScheduler:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SIGMAS",)
|
RETURN_TYPES = ("SIGMAS",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/schedulers"
|
||||||
|
|
||||||
FUNCTION = "get_sigmas"
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ class SplitSigmas:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SIGMAS","SIGMAS")
|
RETURN_TYPES = ("SIGMAS","SIGMAS")
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/sigmas"
|
||||||
|
|
||||||
FUNCTION = "get_sigmas"
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
@ -118,6 +118,24 @@ class SplitSigmas:
|
|||||||
sigmas2 = sigmas[step:]
|
sigmas2 = sigmas[step:]
|
||||||
return (sigmas1, sigmas2)
|
return (sigmas1, sigmas2)
|
||||||
|
|
||||||
|
class FlipSigmas:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required":
|
||||||
|
{"sigmas": ("SIGMAS", ),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RETURN_TYPES = ("SIGMAS",)
|
||||||
|
CATEGORY = "sampling/custom_sampling/sigmas"
|
||||||
|
|
||||||
|
FUNCTION = "get_sigmas"
|
||||||
|
|
||||||
|
def get_sigmas(self, sigmas):
|
||||||
|
sigmas = sigmas.flip(0)
|
||||||
|
if sigmas[0] == 0:
|
||||||
|
sigmas[0] = 0.0001
|
||||||
|
return (sigmas,)
|
||||||
|
|
||||||
class KSamplerSelect:
|
class KSamplerSelect:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@ -126,12 +144,12 @@ class KSamplerSelect:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SAMPLER",)
|
RETURN_TYPES = ("SAMPLER",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/samplers"
|
||||||
|
|
||||||
FUNCTION = "get_sampler"
|
FUNCTION = "get_sampler"
|
||||||
|
|
||||||
def get_sampler(self, sampler_name):
|
def get_sampler(self, sampler_name):
|
||||||
sampler = comfy.samplers.sampler_class(sampler_name)()
|
sampler = comfy.samplers.sampler_object(sampler_name)
|
||||||
return (sampler, )
|
return (sampler, )
|
||||||
|
|
||||||
class SamplerDPMPP_2M_SDE:
|
class SamplerDPMPP_2M_SDE:
|
||||||
@ -145,7 +163,7 @@ class SamplerDPMPP_2M_SDE:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SAMPLER",)
|
RETURN_TYPES = ("SAMPLER",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/samplers"
|
||||||
|
|
||||||
FUNCTION = "get_sampler"
|
FUNCTION = "get_sampler"
|
||||||
|
|
||||||
@ -154,7 +172,7 @@ class SamplerDPMPP_2M_SDE:
|
|||||||
sampler_name = "dpmpp_2m_sde"
|
sampler_name = "dpmpp_2m_sde"
|
||||||
else:
|
else:
|
||||||
sampler_name = "dpmpp_2m_sde_gpu"
|
sampler_name = "dpmpp_2m_sde_gpu"
|
||||||
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "solver_type": solver_type})()
|
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "solver_type": solver_type})
|
||||||
return (sampler, )
|
return (sampler, )
|
||||||
|
|
||||||
|
|
||||||
@ -169,7 +187,7 @@ class SamplerDPMPP_SDE:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_TYPES = ("SAMPLER",)
|
RETURN_TYPES = ("SAMPLER",)
|
||||||
CATEGORY = "sampling/custom_sampling"
|
CATEGORY = "sampling/custom_sampling/samplers"
|
||||||
|
|
||||||
FUNCTION = "get_sampler"
|
FUNCTION = "get_sampler"
|
||||||
|
|
||||||
@ -178,7 +196,7 @@ class SamplerDPMPP_SDE:
|
|||||||
sampler_name = "dpmpp_sde"
|
sampler_name = "dpmpp_sde"
|
||||||
else:
|
else:
|
||||||
sampler_name = "dpmpp_sde_gpu"
|
sampler_name = "dpmpp_sde_gpu"
|
||||||
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "r": r})()
|
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "r": r})
|
||||||
return (sampler, )
|
return (sampler, )
|
||||||
|
|
||||||
class SamplerCustom:
|
class SamplerCustom:
|
||||||
@ -234,6 +252,7 @@ class SamplerCustom:
|
|||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"SamplerCustom": SamplerCustom,
|
"SamplerCustom": SamplerCustom,
|
||||||
|
"BasicScheduler": BasicScheduler,
|
||||||
"KarrasScheduler": KarrasScheduler,
|
"KarrasScheduler": KarrasScheduler,
|
||||||
"ExponentialScheduler": ExponentialScheduler,
|
"ExponentialScheduler": ExponentialScheduler,
|
||||||
"PolyexponentialScheduler": PolyexponentialScheduler,
|
"PolyexponentialScheduler": PolyexponentialScheduler,
|
||||||
@ -241,6 +260,6 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"KSamplerSelect": KSamplerSelect,
|
"KSamplerSelect": KSamplerSelect,
|
||||||
"SamplerDPMPP_2M_SDE": SamplerDPMPP_2M_SDE,
|
"SamplerDPMPP_2M_SDE": SamplerDPMPP_2M_SDE,
|
||||||
"SamplerDPMPP_SDE": SamplerDPMPP_SDE,
|
"SamplerDPMPP_SDE": SamplerDPMPP_SDE,
|
||||||
"BasicScheduler": BasicScheduler,
|
|
||||||
"SplitSigmas": SplitSigmas,
|
"SplitSigmas": SplitSigmas,
|
||||||
|
"FlipSigmas": FlipSigmas,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2533,7 +2533,7 @@
|
|||||||
var w = this.widgets[i];
|
var w = this.widgets[i];
|
||||||
if(!w)
|
if(!w)
|
||||||
continue;
|
continue;
|
||||||
if(w.options && w.options.property && this.properties[ w.options.property ])
|
if(w.options && w.options.property && (this.properties[ w.options.property ] != undefined))
|
||||||
w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) );
|
w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) );
|
||||||
}
|
}
|
||||||
if (info.widgets_values) {
|
if (info.widgets_values) {
|
||||||
@ -4928,9 +4928,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
this.title = o.title;
|
this.title = o.title;
|
||||||
this._bounding.set(o.bounding);
|
this._bounding.set(o.bounding);
|
||||||
this.color = o.color;
|
this.color = o.color;
|
||||||
if (o.font_size) {
|
this.font_size = o.font_size;
|
||||||
this.font_size = o.font_size;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LGraphGroup.prototype.serialize = function() {
|
LGraphGroup.prototype.serialize = function() {
|
||||||
@ -5714,10 +5712,10 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
* @method enableWebGL
|
* @method enableWebGL
|
||||||
**/
|
**/
|
||||||
LGraphCanvas.prototype.enableWebGL = function() {
|
LGraphCanvas.prototype.enableWebGL = function() {
|
||||||
if (typeof GL === undefined) {
|
if (typeof GL === "undefined") {
|
||||||
throw "litegl.js must be included to use a WebGL canvas";
|
throw "litegl.js must be included to use a WebGL canvas";
|
||||||
}
|
}
|
||||||
if (typeof enableWebGLCanvas === undefined) {
|
if (typeof enableWebGLCanvas === "undefined") {
|
||||||
throw "webglCanvas.js must be included to use this feature";
|
throw "webglCanvas.js must be included to use this feature";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7110,15 +7108,16 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
LGraphCanvas.prototype.copyToClipboard = function() {
|
LGraphCanvas.prototype.copyToClipboard = function(nodes) {
|
||||||
var clipboard_info = {
|
var clipboard_info = {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
links: []
|
links: []
|
||||||
};
|
};
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var selected_nodes_array = [];
|
var selected_nodes_array = [];
|
||||||
for (var i in this.selected_nodes) {
|
if (!nodes) nodes = this.selected_nodes;
|
||||||
var node = this.selected_nodes[i];
|
for (var i in nodes) {
|
||||||
|
var node = nodes[i];
|
||||||
if (node.clonable === false)
|
if (node.clonable === false)
|
||||||
continue;
|
continue;
|
||||||
node._relative_id = index;
|
node._relative_id = index;
|
||||||
@ -11702,7 +11701,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
default:
|
default:
|
||||||
iS = 0; // try with first if no name set
|
iS = 0; // try with first if no name set
|
||||||
}
|
}
|
||||||
if (typeof options.node_from.outputs[iS] !== undefined){
|
if (typeof options.node_from.outputs[iS] !== "undefined"){
|
||||||
if (iS!==false && iS>-1){
|
if (iS!==false && iS>-1){
|
||||||
options.node_from.connectByType( iS, node, options.node_from.outputs[iS].type );
|
options.node_from.connectByType( iS, node, options.node_from.outputs[iS].type );
|
||||||
}
|
}
|
||||||
@ -11730,7 +11729,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
default:
|
default:
|
||||||
iS = 0; // try with first if no name set
|
iS = 0; // try with first if no name set
|
||||||
}
|
}
|
||||||
if (typeof options.node_to.inputs[iS] !== undefined){
|
if (typeof options.node_to.inputs[iS] !== "undefined"){
|
||||||
if (iS!==false && iS>-1){
|
if (iS!==false && iS>-1){
|
||||||
// try connection
|
// try connection
|
||||||
options.node_to.connectByTypeOutput(iS,node,options.node_to.inputs[iS].type);
|
options.node_to.connectByTypeOutput(iS,node,options.node_to.inputs[iS].type);
|
||||||
|
|||||||
@ -1889,7 +1889,7 @@ export class ComfyApp {
|
|||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
const data = apiData[id];
|
const data = apiData[id];
|
||||||
const node = LiteGraph.createNode(data.class_type);
|
const node = LiteGraph.createNode(data.class_type);
|
||||||
node.id = id;
|
node.id = isNaN(+id) ? id : +id;
|
||||||
graph.add(node);
|
graph.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -331,14 +331,23 @@ export const ComfyWidgets = {
|
|||||||
return createIntWidget(node, inputName, inputData, app);
|
return createIntWidget(node, inputName, inputData, app);
|
||||||
},
|
},
|
||||||
BOOLEAN(node, inputName, inputData) {
|
BOOLEAN(node, inputName, inputData) {
|
||||||
let defaultVal = inputData[1]["default"];
|
let defaultVal = false;
|
||||||
|
let options = {};
|
||||||
|
if (inputData[1]) {
|
||||||
|
if (inputData[1].default)
|
||||||
|
defaultVal = inputData[1].default;
|
||||||
|
if (inputData[1].label_on)
|
||||||
|
options["on"] = inputData[1].label_on;
|
||||||
|
if (inputData[1].label_off)
|
||||||
|
options["off"] = inputData[1].label_off;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
widget: node.addWidget(
|
widget: node.addWidget(
|
||||||
"toggle",
|
"toggle",
|
||||||
inputName,
|
inputName,
|
||||||
defaultVal,
|
defaultVal,
|
||||||
() => {},
|
() => {},
|
||||||
{"on": inputData[1].label_on, "off": inputData[1].label_off}
|
options,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user