|
from typing import Callable, Union |
|
|
|
import comfy.sample |
|
from comfy.model_patcher import ModelPatcher |
|
from comfy.controlnet import ControlBase |
|
from comfy.ldm.modules.attention import BasicTransformerBlock |
|
|
|
|
|
from .control import convert_all_to_advanced, restore_all_controlnet_conns |
|
from .control_reference import (ReferenceAdvanced, ReferenceInjections, |
|
RefBasicTransformerBlock, RefTimestepEmbedSequential, |
|
InjectionBasicTransformerBlockHolder, InjectionTimestepEmbedSequentialHolder, |
|
_forward_inject_BasicTransformerBlock, factory_forward_inject_UNetModel, |
|
handle_context_ref_setup, |
|
REF_CONTROL_LIST_ALL, CONTEXTREF_CLEAN_FUNC) |
|
from .control_lllite import (ControlLLLiteAdvanced) |
|
from .utils import torch_dfs |
|
|
|
|
|
def support_sliding_context_windows(model, positive, negative) -> tuple[bool, dict, dict]: |
|
|
|
modified, new_conds = convert_all_to_advanced([positive, negative]) |
|
positive, negative = new_conds |
|
return modified, positive, negative |
|
|
|
|
|
def has_sliding_context_windows(model): |
|
motion_injection_params = getattr(model, "motion_injection_params", None) |
|
if motion_injection_params is None: |
|
return False |
|
context_options = getattr(motion_injection_params, "context_options") |
|
return context_options.context_length is not None |
|
|
|
|
|
def get_contextref_obj(model): |
|
motion_injection_params = getattr(model, "motion_injection_params", None) |
|
if motion_injection_params is None: |
|
return None |
|
context_options = getattr(motion_injection_params, "context_options") |
|
extras = getattr(context_options, "extras", None) |
|
if extras is None: |
|
return None |
|
return getattr(extras, "context_ref", None) |
|
|
|
|
|
def acn_sample_factory(orig_comfy_sample: Callable, is_custom=False) -> Callable: |
|
def get_refcn(control: ControlBase, order: int=-1): |
|
ref_set: set[ReferenceAdvanced] = set() |
|
if control is None: |
|
return ref_set |
|
if type(control) == ReferenceAdvanced and not control.is_context_ref: |
|
control.order = order |
|
order -= 1 |
|
ref_set.add(control) |
|
ref_set.update(get_refcn(control.previous_controlnet, order=order)) |
|
return ref_set |
|
|
|
def get_lllitecn(control: ControlBase): |
|
cn_dict: dict[ControlLLLiteAdvanced,None] = {} |
|
if control is None: |
|
return cn_dict |
|
if type(control) == ControlLLLiteAdvanced: |
|
cn_dict[control] = None |
|
cn_dict.update(get_lllitecn(control.previous_controlnet)) |
|
return cn_dict |
|
|
|
def acn_sample(model: ModelPatcher, *args, **kwargs): |
|
controlnets_modified = False |
|
orig_positive = args[-3] |
|
orig_negative = args[-2] |
|
try: |
|
orig_model_options = model.model_options |
|
|
|
positive = args[-3] |
|
negative = args[-2] |
|
|
|
context_refs = [] |
|
if has_sliding_context_windows(model): |
|
model.model_options = model.model_options.copy() |
|
model.model_options["transformer_options"] = model.model_options["transformer_options"].copy() |
|
|
|
controlnets_modified, positive, negative = support_sliding_context_windows(model, positive, negative) |
|
if controlnets_modified: |
|
args = list(args) |
|
args[-3] = positive |
|
args[-2] = negative |
|
args = tuple(args) |
|
|
|
existing_contextref_obj = get_contextref_obj(model) |
|
if existing_contextref_obj is not None: |
|
context_refs = handle_context_ref_setup(existing_contextref_obj, model.model_options["transformer_options"], positive, negative) |
|
controlnets_modified = True |
|
|
|
ref_set = set() |
|
lllite_dict: dict[ControlLLLiteAdvanced, None] = {} |
|
if positive is not None: |
|
for cond in positive: |
|
if "control" in cond[1]: |
|
ref_set.update(get_refcn(cond[1]["control"])) |
|
lllite_dict.update(get_lllitecn(cond[1]["control"])) |
|
if negative is not None: |
|
for cond in negative: |
|
if "control" in cond[1]: |
|
ref_set.update(get_refcn(cond[1]["control"])) |
|
lllite_dict.update(get_lllitecn(cond[1]["control"])) |
|
|
|
if len(lllite_dict) > 0: |
|
lllite_list = list(lllite_dict.keys()) |
|
model.model_options = model.model_options.copy() |
|
model.model_options["transformer_options"] = model.model_options["transformer_options"].copy() |
|
lllite_list.reverse() |
|
for lll in lllite_list: |
|
lll.live_model_patches(model.model_options) |
|
|
|
if len(ref_set) == 0 and len(context_refs) == 0: |
|
return orig_comfy_sample(model, *args, **kwargs) |
|
|
|
try: |
|
|
|
|
|
reference_injections = ReferenceInjections() |
|
|
|
|
|
all_modules = torch_dfs(model.model) |
|
attn_modules: list[RefBasicTransformerBlock] = [] |
|
for module in all_modules: |
|
if isinstance(module, BasicTransformerBlock): |
|
attn_modules.append(module) |
|
attn_modules = [module for module in all_modules if isinstance(module, BasicTransformerBlock)] |
|
attn_modules = sorted(attn_modules, key=lambda x: -x.norm1.normalized_shape[0]) |
|
for i, module in enumerate(attn_modules): |
|
injection_holder = InjectionBasicTransformerBlockHolder(block=module, idx=i) |
|
injection_holder.attn_weight = float(i) / float(len(attn_modules)) |
|
if hasattr(module, "_forward"): |
|
module._forward = _forward_inject_BasicTransformerBlock.__get__(module, type(module)) |
|
else: |
|
module.forward = _forward_inject_BasicTransformerBlock.__get__(module, type(module)) |
|
module.injection_holder = injection_holder |
|
reference_injections.attn_modules.append(module) |
|
|
|
if hasattr(model.model.diffusion_model, "middle_block"): |
|
mid_modules = torch_dfs(model.model.diffusion_model.middle_block) |
|
mid_attn_modules: list[RefBasicTransformerBlock] = [module for module in mid_modules if isinstance(module, BasicTransformerBlock)] |
|
for module in mid_attn_modules: |
|
module.injection_holder.is_middle = True |
|
|
|
|
|
|
|
if type(model.model).__name__ == "SDXL": |
|
input_block_indices = [4, 5, 7, 8] |
|
output_block_indices = [0, 1, 2, 3, 4, 5] |
|
else: |
|
input_block_indices = [4, 5, 7, 8, 10, 11] |
|
output_block_indices = [0, 1, 2, 3, 4, 5, 6, 7] |
|
if hasattr(model.model.diffusion_model, "middle_block"): |
|
module = model.model.diffusion_model.middle_block |
|
injection_holder = InjectionTimestepEmbedSequentialHolder(block=module, idx=0, is_middle=True) |
|
injection_holder.gn_weight = 0.0 |
|
module.injection_holder = injection_holder |
|
reference_injections.gn_modules.append(module) |
|
for w, i in enumerate(input_block_indices): |
|
module = model.model.diffusion_model.input_blocks[i] |
|
injection_holder = InjectionTimestepEmbedSequentialHolder(block=module, idx=i, is_input=True) |
|
injection_holder.gn_weight = 1.0 - float(w) / float(len(input_block_indices)) |
|
module.injection_holder = injection_holder |
|
reference_injections.gn_modules.append(module) |
|
for w, i in enumerate(output_block_indices): |
|
module = model.model.diffusion_model.output_blocks[i] |
|
injection_holder = InjectionTimestepEmbedSequentialHolder(block=module, idx=i, is_output=True) |
|
injection_holder.gn_weight = float(w) / float(len(output_block_indices)) |
|
module.injection_holder = injection_holder |
|
reference_injections.gn_modules.append(module) |
|
|
|
for i, module in enumerate(reference_injections.gn_modules): |
|
module.injection_holder.gn_weight *= 2 |
|
|
|
|
|
reference_injections.diffusion_model_orig_forward = model.model.diffusion_model.forward |
|
model.model.diffusion_model.forward = factory_forward_inject_UNetModel(reference_injections).__get__(model.model.diffusion_model, type(model.model.diffusion_model)) |
|
|
|
new_model_options = model.model_options.copy() |
|
new_model_options["transformer_options"] = model.model_options["transformer_options"].copy() |
|
ref_list: list[ReferenceAdvanced] = list(ref_set) |
|
new_model_options["transformer_options"][REF_CONTROL_LIST_ALL] = sorted(ref_list, key=lambda x: x.order) |
|
new_model_options["transformer_options"][CONTEXTREF_CLEAN_FUNC] = reference_injections.clean_contextref_module_mem |
|
model.model_options = new_model_options |
|
|
|
return orig_comfy_sample(model, *args, **kwargs) |
|
finally: |
|
|
|
|
|
attn_modules: list[RefBasicTransformerBlock] = reference_injections.attn_modules |
|
for module in attn_modules: |
|
module.injection_holder.restore(module) |
|
module.injection_holder.clean_all() |
|
del module.injection_holder |
|
del attn_modules |
|
|
|
gn_modules: list[RefTimestepEmbedSequential] = reference_injections.gn_modules |
|
for module in gn_modules: |
|
module.injection_holder.restore(module) |
|
module.injection_holder.clean_all() |
|
del module.injection_holder |
|
del gn_modules |
|
|
|
model.model.diffusion_model.forward = reference_injections.diffusion_model_orig_forward.__get__(model.model.diffusion_model, type(model.model.diffusion_model)) |
|
|
|
reference_injections.cleanup() |
|
finally: |
|
|
|
model.model_options = orig_model_options |
|
|
|
if controlnets_modified: |
|
restore_all_controlnet_conns([orig_positive, orig_negative]) |
|
|
|
return acn_sample |
|
|