From c33d26c283ea53b8ba3e42ef3dca1f03ddf4d7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Sepp=C3=A4nen?= <40791699+kijai@users.noreply.github.com> Date: Mon, 4 May 2026 20:20:40 +0300 Subject: [PATCH 1/3] fix: Proper memory estimation for frame interpolation when not using dynamic VRAM (#13698) --- comfy_extras/frame_interpolation_models/film_net.py | 3 +++ comfy_extras/frame_interpolation_models/ifnet.py | 3 +++ comfy_extras/nodes_frame_interpolation.py | 11 ++++------- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/comfy_extras/frame_interpolation_models/film_net.py b/comfy_extras/frame_interpolation_models/film_net.py index cf4f6e1e1fb6..36bc79dc3c0d 100644 --- a/comfy_extras/frame_interpolation_models/film_net.py +++ b/comfy_extras/frame_interpolation_models/film_net.py @@ -199,6 +199,9 @@ def __init__(self, pyramid_levels=7, fusion_pyramid_levels=5, specialized_levels def get_dtype(self): return self.extract.extract_sublevels.convs[0][0].conv.weight.dtype + def memory_used_forward(self, shape, dtype): + return 1700 * shape[1] * shape[2] * dtype.itemsize + def _build_warp_grids(self, H, W, device): """Pre-compute warp grids for all pyramid levels.""" if (H, W) in self._warp_grids: diff --git a/comfy_extras/frame_interpolation_models/ifnet.py b/comfy_extras/frame_interpolation_models/ifnet.py index 03cb34c50a9c..ad6edbec91af 100644 --- a/comfy_extras/frame_interpolation_models/ifnet.py +++ b/comfy_extras/frame_interpolation_models/ifnet.py @@ -74,6 +74,9 @@ def __init__(self, head_ch=4, channels=(192, 128, 96, 64, 32), device=None, dtyp def get_dtype(self): return self.encode.cnn0.weight.dtype + def memory_used_forward(self, shape, dtype): + return 300 * shape[1] * shape[2] * dtype.itemsize + def _build_warp_grids(self, H, W, device): if (H, W) in self._warp_grids: return diff --git a/comfy_extras/nodes_frame_interpolation.py b/comfy_extras/nodes_frame_interpolation.py index a3b00d36ec58..fa49c203a56b 100644 --- a/comfy_extras/nodes_frame_interpolation.py +++ b/comfy_extras/nodes_frame_interpolation.py @@ -37,7 +37,7 @@ def execute(cls, model_name) -> io.NodeOutput: model = cls._detect_and_load(sd) dtype = torch.float16 if model_management.should_use_fp16(model_management.get_torch_device()) else torch.float32 model.eval().to(dtype) - patcher = comfy.model_patcher.ModelPatcher( + patcher = comfy.model_patcher.CoreModelPatcher( model, load_device=model_management.get_torch_device(), offload_device=model_management.unet_offload_device(), @@ -98,16 +98,13 @@ def execute(cls, interp_model, images, multiplier) -> io.NodeOutput: if num_frames < 2 or multiplier < 2: return io.NodeOutput(images) - model_management.load_model_gpu(interp_model) device = interp_model.load_device dtype = interp_model.model_dtype() inference_model = interp_model.model - - # Free VRAM for inference activations (model weights + ~20x a single frame's worth) - H, W = images.shape[1], images.shape[2] - activation_mem = H * W * 3 * images.element_size() * 20 - model_management.free_memory(activation_mem, device) + activation_mem = inference_model.memory_used_forward(images.shape, dtype) + model_management.load_models_gpu([interp_model], memory_required=activation_mem) align = getattr(inference_model, "pad_align", 1) + H, W = images.shape[1], images.shape[2] # Prepare a single padded frame on device for determining output dimensions def prepare_frame(idx): From c47633f3befbc32bf5aeece6a899c20d55a9feb1 Mon Sep 17 00:00:00 2001 From: rattus <46076784+rattus128@users.noreply.github.com> Date: Tue, 5 May 2026 05:56:05 +1000 Subject: [PATCH 2/3] prefetch: guard against no offload (#13703) cast_ will return no stream if there is no work to do. guard against this is the consume logic. --- comfy/model_prefetch.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/comfy/model_prefetch.py b/comfy/model_prefetch.py index 0ad35deb5e7c..72e11dec6a1a 100644 --- a/comfy/model_prefetch.py +++ b/comfy/model_prefetch.py @@ -37,7 +37,8 @@ def prefetch_queue_pop(queue, device, module): consumed = queue.pop(0) if consumed is not None: offload_stream, prefetch_state = consumed - offload_stream.wait_stream(comfy.model_management.current_stream(device)) + if offload_stream is not None: + offload_stream.wait_stream(comfy.model_management.current_stream(device)) _, comfy_modules = prefetch_state if comfy_modules is not None: cleanup_prefetched_modules(comfy_modules) From 1ac78180b3f797f09f5805e5a923debe77638889 Mon Sep 17 00:00:00 2001 From: rattus <46076784+rattus128@users.noreply.github.com> Date: Tue, 5 May 2026 05:58:06 +1000 Subject: [PATCH 3/3] make control-net load order deterministic (#13701) Make this deterministic so speeds dont change base of load order. Load them in reverse order so whatever the caller lists first is the top priority. --- comfy/model_management.py | 10 ++++++---- comfy/sampler_helpers.py | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/comfy/model_management.py b/comfy/model_management.py index 02ad666560ce..21738a4c7816 100644 --- a/comfy/model_management.py +++ b/comfy/model_management.py @@ -721,13 +721,15 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu else: minimum_memory_required = max(inference_memory, minimum_memory_required + extra_reserved_memory()) - models_temp = set() + # Order-preserving dedup. A plain set() would randomize iteration order across runs + models_temp = {} for m in models: - models_temp.add(m) + models_temp[m] = None for mm in m.model_patches_models(): - models_temp.add(mm) + models_temp[mm] = None - models = models_temp + models = list(models_temp) + models.reverse() models_to_load = [] diff --git a/comfy/sampler_helpers.py b/comfy/sampler_helpers.py index bbba09e26974..3782fd2d5a1b 100644 --- a/comfy/sampler_helpers.py +++ b/comfy/sampler_helpers.py @@ -89,7 +89,8 @@ def get_additional_models(conds, dtype): gligen += get_models_from_cond(conds[k], "gligen") add_models += get_models_from_cond(conds[k], "additional_models") - control_nets = set(cnets) + # Order-preserving dedup. A plain set() would randomize iteration order across runs + control_nets = list(dict.fromkeys(cnets)) inference_memory = 0 control_models = []