diff --git a/.gitignore b/.gitignore index 8ac9c09..2c3d9a0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ __pycache__/ .clinerules .vscode -memory-bank/ \ No newline at end of file +memory-bank/ +.github/ \ No newline at end of file diff --git a/__init__.py b/__init__.py index 41b4f15..da7e686 100644 --- a/__init__.py +++ b/__init__.py @@ -3,6 +3,8 @@ import logging import weakref import os import copy +import json +from datetime import datetime from pathlib import Path import folder_paths import comfy.model_management as mm @@ -21,12 +23,22 @@ from .model_management_mgpu import ( ) WEB_DIRECTORY = "./web" -MGPU_MM_LOG = False +MGPU_MM_LOG = True DEBUG_LOG = False logger = logging.getLogger("MultiGPU") logger.propagate = False +FOCUS_LOG_LEVEL = logging.INFO + 5 +logging.addLevelName(FOCUS_LOG_LEVEL, "FOCUS") + +if not hasattr(logging.Logger, "focus"): + def focus(self, message, *args, **kwargs): + if self.isEnabledFor(FOCUS_LOG_LEVEL): + self._log(FOCUS_LOG_LEVEL, message, args, **kwargs) + + logging.Logger.focus = focus # type: ignore[attr-defined] + if not logger.handlers: log_level = logging.DEBUG if DEBUG_LOG else logging.INFO handler = logging.StreamHandler() @@ -35,10 +47,93 @@ if not logger.handlers: logger.addHandler(handler) logger.setLevel(log_level) + json_log_path = os.environ.get("MGPU_JSON_LOG_PATH") + json_static_fields = {} + if json_log_path: + try: + json_static_fields = json.loads(os.environ.get("MGPU_JSON_STATIC_FIELDS", "{}")) + except json.JSONDecodeError: + json_static_fields = {} + + level_aliases = { + "CRITICAL": logging.CRITICAL, + "ERROR": logging.ERROR, + "WARNING": logging.WARNING, + "FOCUS": FOCUS_LOG_LEVEL, + "INFO": logging.INFO, + "DEBUG": logging.DEBUG, + } + + json_min_level = FOCUS_LOG_LEVEL + configured_min_level = os.environ.get("MGPU_JSON_MIN_LEVEL") + if configured_min_level: + value = configured_min_level.strip() + upper_value = value.upper() + if upper_value in level_aliases: + json_min_level = level_aliases[upper_value] + else: + try: + json_min_level = int(value) + except ValueError: + json_min_level = FOCUS_LOG_LEVEL + + class JsonLineFileHandler(logging.Handler): + def __init__(self, path, static_fields, min_level, overwrite): + super().__init__() + self.path = Path(path) + self.path.parent.mkdir(parents=True, exist_ok=True) + self.static_fields = static_fields + self.setLevel(min_level) + if overwrite: + try: + with self.path.open("w", encoding="utf-8") as handle: + handle.write("") + except OSError: + pass + + def emit(self, record): + message = record.getMessage() + category = None + if message.startswith("[") and "]" in message: + bracket_split = message.split("]", 1) + category = bracket_split[0].strip("[]") + payload = { + "timestamp": datetime.utcnow().isoformat() + "Z", + "level": record.levelname, + "name": record.name, + "message": message, + } + if category: + payload["event_category"] = category + if hasattr(record, "mgpu_context") and isinstance(record.mgpu_context, dict): + payload.update(record.mgpu_context) + workflow_id = os.environ.get("MGPU_JSON_WORKFLOW") + prompt_id = os.environ.get("MGPU_JSON_PROMPT") + if workflow_id: + payload.setdefault("workflow_id", workflow_id) + if prompt_id: + payload.setdefault("prompt_id", prompt_id) + if self.static_fields: + payload.update(self.static_fields) + try: + with self.path.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(payload, ensure_ascii=True) + "\n") + except OSError: + # Fail silently for JSON logging so primary logging continues. + pass + + overwrite_value = os.environ.get("MGPU_JSON_OVERWRITE", "true").strip().lower() + overwrite_enabled = overwrite_value not in {"0", "false", "no"} + + logger.addHandler(JsonLineFileHandler(json_log_path, json_static_fields, json_min_level, overwrite_enabled)) + def mgpu_mm_log_method(self, msg): """Add MultiGPU model management logging method to logger instance.""" if MGPU_MM_LOG: - self.info(f"[MultiGPU Model Management] {msg}") + self.focus( + f"[MultiGPU Model Management] {msg}", + extra={"mgpu_context": {"component": "model_management"}}, + ) logger.mgpu_mm_log = mgpu_mm_log_method.__get__(logger, type(logger)) def check_module_exists(module_path): @@ -95,8 +190,6 @@ mm.get_torch_device = get_torch_device_patched mm.text_encoder_device = text_encoder_device_patched from .nodes import ( - DeviceSelectorMultiGPU, - HunyuanVideoEmbeddingsAdapter, UnetLoaderGGUF, UnetLoaderGGUFAdvanced, CLIPLoaderGGUF, @@ -114,21 +207,30 @@ from .nodes import ( PulidModelLoader, PulidInsightFaceLoader, PulidEvaClipLoader, - HyVideoModelLoader, - HyVideoVAELoader, - DownloadAndLoadHyVideoTextEncoder, UNetLoaderLP, ) from .wanvideo import ( - WanVideoModelLoader, - WanVideoModelLoader_2, - WanVideoVAELoader, LoadWanVideoT5TextEncoder, - LoadWanVideoClipTextEncoder, WanVideoTextEncode, + WanVideoTextEncodeCached, + WanVideoTextEncodeSingle, + WanVideoVAELoader, + WanVideoTinyVAELoader, WanVideoBlockSwap, - WanVideoSampler + WanVideoImageToVideoEncode, + WanVideoDecode, + WanVideoModelLoader, + WanVideoSampler, + WanVideoVACEEncode, + WanVideoEncode, + LoadWanVideoClipTextEncoder, + WanVideoClipVisionEncode, + WanVideoControlnetLoader, + FantasyTalkingModelLoader, + Wav2VecModelLoader, + WanVideoUni3C_ControlnetLoader, + DownloadAndLoadWav2VecModel, ) from .wrappers import ( @@ -158,8 +260,6 @@ from .checkpoint_multigpu import ( ) NODE_CLASS_MAPPINGS = { - "DeviceSelectorMultiGPU": DeviceSelectorMultiGPU, - "HunyuanVideoEmbeddingsAdapter": HunyuanVideoEmbeddingsAdapter, "CheckpointLoaderAdvancedMultiGPU": CheckpointLoaderAdvancedMultiGPU, "CheckpointLoaderAdvancedDisTorch2MultiGPU": CheckpointLoaderAdvancedDisTorch2MultiGPU, "UNetLoaderLP": UNetLoaderLP, @@ -266,22 +366,27 @@ pulid_nodes = { } register_and_count(["PuLID_ComfyUI", "pulid_comfyui"], pulid_nodes) -hunyuan_nodes = { - "HyVideoModelLoaderMultiGPU": override_class(HyVideoModelLoader), - "HyVideoVAELoaderMultiGPU": override_class(HyVideoVAELoader), - "DownloadAndLoadHyVideoTextEncoderMultiGPU": override_class(DownloadAndLoadHyVideoTextEncoder) -} -register_and_count(["ComfyUI-HunyuanVideoWrapper", "comfyui-hunyuanvideowrapper"], hunyuan_nodes) - wanvideo_nodes = { - "WanVideoModelLoaderMultiGPU": WanVideoModelLoader, - "WanVideoModelLoaderMultiGPU_2": WanVideoModelLoader_2, - "WanVideoVAELoaderMultiGPU": WanVideoVAELoader, "LoadWanVideoT5TextEncoderMultiGPU": LoadWanVideoT5TextEncoder, - "LoadWanVideoClipTextEncoderMultiGPU": LoadWanVideoClipTextEncoder, "WanVideoTextEncodeMultiGPU": WanVideoTextEncode, + "WanVideoTextEncodeCachedMultiGPU": WanVideoTextEncodeCached, + "WanVideoTextEncodeSingleMultiGPU": WanVideoTextEncodeSingle, + "WanVideoVAELoaderMultiGPU": WanVideoVAELoader, + "WanVideoTinyVAELoaderMultiGPU": WanVideoTinyVAELoader, "WanVideoBlockSwapMultiGPU": WanVideoBlockSwap, - "WanVideoSamplerMultiGPU": WanVideoSampler + "WanVideoImageToVideoEncodeMultiGPU": WanVideoImageToVideoEncode, + "WanVideoDecodeMultiGPU": WanVideoDecode, + "WanVideoModelLoaderMultiGPU": WanVideoModelLoader, + "WanVideoSamplerMultiGPU": WanVideoSampler, + "WanVideoVACEEncodeMultiGPU": WanVideoVACEEncode, + "WanVideoEncodeMultiGPU": WanVideoEncode, + "LoadWanVideoClipTextEncoderMultiGPU": LoadWanVideoClipTextEncoder, + "WanVideoClipVisionEncodeMultiGPU": WanVideoClipVisionEncode, + "WanVideoControlnetLoaderMultiGPU": WanVideoControlnetLoader, + "FantasyTalkingModelLoaderMultiGPU": FantasyTalkingModelLoader, + "Wav2VecModelLoaderMultiGPU": Wav2VecModelLoader, + "WanVideoUni3C_ControlnetLoaderMultiGPU": WanVideoUni3C_ControlnetLoader, + "DownloadAndLoadWav2VecModelMultiGPU": DownloadAndLoadWav2VecModel, } register_and_count(["ComfyUI-WanVideoWrapper", "comfyui-wanvideowrapper"], wanvideo_nodes) @@ -289,4 +394,4 @@ for item in registration_data: logger.info(fmt_reg.format(item['name'], item['found'], str(item['count']))) logger.info(dash_line) -logger.info(f"[MultiGPU] Registration complete. Final mappings: {', '.join(NODE_CLASS_MAPPINGS.keys())}") \ No newline at end of file +logger.info(f"[MultiGPU] Registration complete. Final mappings: {', '.join(NODE_CLASS_MAPPINGS.keys())}") diff --git a/assets/flux1_dev_Q8_0_benchmark.png b/assets/flux1_dev_Q8_0_benchmark.png new file mode 100755 index 0000000..aba5ddd Binary files /dev/null and b/assets/flux1_dev_Q8_0_benchmark.png differ diff --git a/assets/flux1_kontext_dev_benchmark.png b/assets/flux1_kontext_dev_benchmark.png new file mode 100755 index 0000000..fef4c1b Binary files /dev/null and b/assets/flux1_kontext_dev_benchmark.png differ diff --git a/assets/qwen_image_fp16_benchmark.png b/assets/qwen_image_fp16_benchmark.png new file mode 100755 index 0000000..2751d2c Binary files /dev/null and b/assets/qwen_image_fp16_benchmark.png differ diff --git a/assets/qwen_image_fp8_benchmark.png b/assets/qwen_image_fp8_benchmark.png new file mode 100755 index 0000000..f5a446b Binary files /dev/null and b/assets/qwen_image_fp8_benchmark.png differ diff --git a/assets/wan2_2_benchmark_v2.png b/assets/wan2_2_benchmark_v2.png new file mode 100755 index 0000000..e0f069e Binary files /dev/null and b/assets/wan2_2_benchmark_v2.png differ diff --git a/assets/wan2_2_qwen_combo_benchmark.png b/assets/wan2_2_qwen_combo_benchmark.png new file mode 100755 index 0000000..e099793 Binary files /dev/null and b/assets/wan2_2_qwen_combo_benchmark.png differ diff --git a/ci/extract_allocation.py b/ci/extract_allocation.py new file mode 100644 index 0000000..a11c05e --- /dev/null +++ b/ci/extract_allocation.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Filter MultiGPU JSON logs for allocation summaries.""" + +import argparse +import json +from pathlib import Path +from typing import Iterable, Iterator, Dict, Any + + +def load_json_lines(path: Path) -> Iterator[Dict[str, Any]]: + with path.open("r", encoding="utf-8") as handle: + for line in handle: + line = line.strip() + if not line: + continue + try: + yield json.loads(line) + except json.JSONDecodeError: + continue + + +def is_allocation_event(entry: Dict[str, Any], keywords: Iterable[str]) -> bool: + message = entry.get("message", "") + return any(keyword in message for keyword in keywords) + + +def main() -> int: + parser = argparse.ArgumentParser(description="Extract allocation-related events from MultiGPU JSON logs") + parser.add_argument("logfile", type=Path, help="Path to JSONL log produced by MGPU_JSON_LOG_PATH") + parser.add_argument( + "--keywords", + nargs="*", + default=["Final Allocation String", "Total memory", "Virtual VRAM"], + help="Keywords that mark allocation events", + ) + args = parser.parse_args() + + entries = list(load_json_lines(args.logfile)) + if not entries: + print("No entries found in log file.") + return 0 + + matched = [entry for entry in entries if is_allocation_event(entry, args.keywords)] + if not matched: + print("No allocation events matched provided keywords.") + return 0 + + for entry in matched: + timestamp = entry.get("timestamp", "unknown") + category = entry.get("event_category", "") + component = entry.get("component", "") + header_bits = [bit for bit in (timestamp, category, component) if bit] + header = " | ".join(header_bits) if header_bits else "allocation" + print(f"## {header}") + print(entry.get("message", "")) + print() + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/ci/run_workflows.py b/ci/run_workflows.py new file mode 100644 index 0000000..8ad7e3d --- /dev/null +++ b/ci/run_workflows.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +"""Minimal ComfyUI workflow runner for CI smoke tests.""" + +import argparse +import json +import os +import sys +import time +import uuid +from pathlib import Path +from typing import Iterable, Optional + +import requests +import websocket + +DEFAULT_HOST = os.environ.get("COMFYUI_HOST", "127.0.0.1") +DEFAULT_PORT = int(os.environ.get("COMFYUI_PORT", "8188")) +DEFAULT_CONNECT_TIMEOUT = int(os.environ.get("COMFYUI_CONNECT_TIMEOUT", "60")) +DEFAULT_WORKFLOW_TIMEOUT = int(os.environ.get("COMFYUI_WORKFLOW_TIMEOUT", "900")) + + +class ComfyWorkflowRunner: + def __init__(self, host: str, port: int, connect_timeout: int, workflow_timeout: int, secure: bool = False) -> None: + self.host = host + self.port = port + protocol_http = "https" if secure else "http" + protocol_ws = "wss" if secure else "ws" + self.base_http = f"{protocol_http}://{host}:{port}" + self.base_ws = f"{protocol_ws}://{host}:{port}/ws" + self.connect_timeout = connect_timeout + self.workflow_timeout = workflow_timeout + self.client_id = str(uuid.uuid4()) + self.session = requests.Session() + self.websocket: Optional[websocket.WebSocket] = None + + def wait_for_server(self) -> None: + deadline = time.monotonic() + self.connect_timeout + while time.monotonic() < deadline: + try: + response = self.session.get(f"{self.base_http}/system_stats", timeout=5) + if response.status_code == 200: + return + except requests.RequestException: + time.sleep(1) + raise TimeoutError(f"ComfyUI server not reachable at {self.base_http}") + + def open_websocket(self) -> None: + ws = websocket.WebSocket() + ws.settimeout(5) + ws.connect(f"{self.base_ws}?clientId={self.client_id}") + self.websocket = ws + + def close_websocket(self) -> None: + if self.websocket: + try: + self.websocket.close() + finally: + self.websocket = None + + def queue_prompt(self, prompt: dict) -> str: + payload = {"prompt": prompt, "client_id": self.client_id} + response = self.session.post(f"{self.base_http}/prompt", json=payload, timeout=15) + response.raise_for_status() + data = response.json() + prompt_id = data.get("prompt_id") + if not prompt_id: + raise RuntimeError("No prompt_id returned from ComfyUI") + return prompt_id + + def wait_for_completion(self, prompt_id: str) -> bool: + if not self.websocket: + raise RuntimeError("WebSocket connection not established") + deadline = time.monotonic() + self.workflow_timeout + ws = self.websocket + while time.monotonic() < deadline: + try: + message = ws.recv() + except websocket.WebSocketTimeoutException: + continue + except Exception as exc: # noqa: BLE001 + print(f"WebSocket error: {exc}", file=sys.stderr, flush=True) + return False + + if isinstance(message, bytes): + continue + + try: + payload = json.loads(message) + except json.JSONDecodeError: + continue + + message_type = payload.get("type") + data = payload.get("data", {}) + + if message_type == "execution_error": + if data.get("prompt_id") == prompt_id: + print(f"Execution error: {payload}", file=sys.stderr, flush=True) + return False + elif message_type == "status" and data.get("status") == "error": + if data.get("prompt_id") == prompt_id: + print(f"Status error: {payload}", file=sys.stderr, flush=True) + return False + elif message_type == "executing": + if data.get("prompt_id") == prompt_id and data.get("node") is None: + return True + print("Workflow timed out", file=sys.stderr, flush=True) + return False + + def run_workflow(self, workflow_path: Path) -> bool: + previous_workflow = os.environ.get("MGPU_JSON_WORKFLOW") + previous_prompt = os.environ.get("MGPU_JSON_PROMPT") + + def restore_env() -> None: + if previous_workflow is None: + os.environ.pop("MGPU_JSON_WORKFLOW", None) + else: + os.environ["MGPU_JSON_WORKFLOW"] = previous_workflow + if previous_prompt is None: + os.environ.pop("MGPU_JSON_PROMPT", None) + else: + os.environ["MGPU_JSON_PROMPT"] = previous_prompt + + if workflow_path: + os.environ["MGPU_JSON_WORKFLOW"] = workflow_path.name + try: + with workflow_path.open("r", encoding="utf-8") as handle: + workflow = json.load(handle) + except (OSError, json.JSONDecodeError) as exc: + print(f"Failed to load workflow {workflow_path}: {exc}", file=sys.stderr, flush=True) + restore_env() + return False + + print(f"Running workflow {workflow_path}", flush=True) + start = time.monotonic() + try: + prompt_id = self.queue_prompt(workflow) + os.environ["MGPU_JSON_PROMPT"] = prompt_id + except requests.HTTPError as exc: + print(f"HTTP error while queueing workflow: {exc}", file=sys.stderr, flush=True) + restore_env() + return False + except requests.RequestException as exc: + print(f"Request error while queueing workflow: {exc}", file=sys.stderr, flush=True) + restore_env() + return False + except RuntimeError as exc: + print(str(exc), file=sys.stderr, flush=True) + restore_env() + return False + + try: + if not self.wait_for_completion(prompt_id): + return False + duration = time.monotonic() - start + print(f"Workflow {workflow_path} completed in {duration:.2f}s", flush=True) + return True + finally: + restore_env() + + def run_suite(self, workflows: Iterable[Path], fail_fast: bool) -> bool: + self.wait_for_server() + self.open_websocket() + try: + overall = True + for workflow in workflows: + ok = self.run_workflow(workflow) + if not ok: + overall = False + if fail_fast: + break + return overall + finally: + self.close_websocket() + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Run ComfyUI workflows via the HTTP/WebSocket API") + parser.add_argument("workflows", nargs="+", type=Path, help="Workflow files in ComfyUI API JSON format") + parser.add_argument("--host", default=DEFAULT_HOST, help="ComfyUI HTTP host") + parser.add_argument("--port", type=int, default=DEFAULT_PORT, help="ComfyUI HTTP port") + parser.add_argument("--connect-timeout", type=int, default=DEFAULT_CONNECT_TIMEOUT, help="Seconds to wait for the server to come online") + parser.add_argument("--workflow-timeout", type=int, default=DEFAULT_WORKFLOW_TIMEOUT, help="Seconds to wait for each workflow to finish") + parser.add_argument("--fail-fast", action="store_true", help="Stop on first workflow failure") + parser.add_argument("--secure", action="store_true", help="Use secure HTTPS/WSS connections (default: insecure for localhost)") + return parser.parse_args() + + +def main() -> int: + args = parse_args() + runner = ComfyWorkflowRunner( + host=args.host, + port=args.port, + connect_timeout=args.connect_timeout, + workflow_timeout=args.workflow_timeout, + secure=args.secure, + ) + success = runner.run_suite(args.workflows, fail_fast=args.fail_fast) + return 0 if success else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/ci/smoke_test.sh b/ci/smoke_test.sh new file mode 100644 index 0000000..cfd9a79 --- /dev/null +++ b/ci/smoke_test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 1 ]]; then + echo "Usage: COMFYUI_HOME=/path/to/ComfyUI ci/smoke_test.sh [...]" >&2 + exit 1 +fi + +if [[ -z "${COMFYUI_HOME:-}" ]]; then + echo "COMFYUI_HOME environment variable must point to the ComfyUI checkout" >&2 + exit 1 +fi + +PYTHON_BIN=${PYTHON_BIN:-python3} +HOST=${COMFYUI_HOST:-127.0.0.1} +PORT=${COMFYUI_PORT:-8188} +LOG_FILE=${COMFYUI_LOG:-comfyui_ci.log} + +pushd "${COMFYUI_HOME}" >/dev/null + +${PYTHON_BIN} -m pip install --upgrade pip >/dev/null +${PYTHON_BIN} -m pip install -r requirements.txt >/dev/null + +${PYTHON_BIN} main.py --disable-auto-launch --listen "${HOST}" --port "${PORT}" >"${LOG_FILE}" 2>&1 & +SERVER_PID=$! +trap 'kill ${SERVER_PID} >/dev/null 2>&1 || true' EXIT + +popd >/dev/null + +"${PYTHON_BIN}" "$(dirname "$0")/run_workflows.py" --host "${HOST}" --port "${PORT}" "$@" diff --git a/ci/summarize_log.py b/ci/summarize_log.py new file mode 100644 index 0000000..f1d3b4b --- /dev/null +++ b/ci/summarize_log.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Convert MultiGPU JSON log into a Markdown summary.""" + +import argparse +import json +from pathlib import Path +from typing import Iterator, Dict, Any + + +def load_json_lines(path: Path) -> Iterator[Dict[str, Any]]: + with path.open("r", encoding="utf-8") as handle: + for line in handle: + line = line.strip() + if not line: + continue + try: + yield json.loads(line) + except json.JSONDecodeError: + continue + + +def main() -> int: + parser = argparse.ArgumentParser(description="Summarize MultiGPU JSON logs into Markdown") + parser.add_argument("logfile", type=Path, help="Path to JSONL log produced by MGPU_JSON_LOG_PATH") + parser.add_argument("--severity", nargs="*", help="Optional severity levels to include (e.g. INFO WARN ERROR)") + parser.add_argument( + "--component", + nargs="*", + help="Optional component names to include (matches component or event_category fields)", + ) + args = parser.parse_args() + + entries = list(load_json_lines(args.logfile)) + if not entries: + print("No entries found in log file.") + return 0 + + print("| Timestamp | Level | Component | Message |") + print("| --- | --- | --- | --- |") + for entry in entries: + level = entry.get("level", "") + if args.severity and level not in args.severity: + continue + component_values = { + entry.get("component", ""), + entry.get("event_category", ""), + } + component = next((value for value in component_values if value), "") + if args.component and component not in args.component: + continue + timestamp = entry.get("timestamp", "") + message = entry.get("message", "").replace("|", "\u2502") + print(f"| {timestamp} | {level} | {component} | {message} |") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/device_utils.py b/device_utils.py index a86cf84..18d55e1 100644 --- a/device_utils.py +++ b/device_utils.py @@ -17,7 +17,7 @@ def get_device_list(): Returns a comprehensive list of all available devices across all types: - CPU (always available) - - CUDA devices (NVIDIA GPUs) + - CUDA devices (NVIDIA GPUs + AMD w/ ROCm GPUs) - XPU devices (Intel GPUs) - NPU devices (Ascend NPUs from Huawei) - MLU devices (Cambricon MLUs) @@ -237,32 +237,28 @@ def soft_empty_cache_distorch2_patched(force=False): from .model_management_mgpu import multigpu_memory_log, check_cpu_memory_threshold, trigger_executor_cache_reset from .distorch_2 import safetensor_allocation_store, create_safetensor_model_hash - multigpu_memory_log("patched_soft_empty", f"start:force={force}") is_distorch_active = False # Detect DisTorch2-managed models - logger.mgpu_mm_log(f"[DETECT_DEBUG] Checking DisTorch2 active status - loaded models: {len(mm.current_loaded_models)}, store entries: {len(safetensor_allocation_store)}") + # logger.mgpu_mm_log(f"[DETECT_DEBUG] Checking DisTorch2 active status - loaded models: {len(mm.current_loaded_models)}, store entries: {len(safetensor_allocation_store)}") for i, lm in enumerate(mm.current_loaded_models): mp = lm.model # weakref call to ModelPatcher if mp is not None: - try: - model_hash = create_safetensor_model_hash(mp, "cache_patch_check") - in_store = model_hash in safetensor_allocation_store - alloc_value = safetensor_allocation_store.get(model_hash, "") - model_name = type(getattr(mp, 'model', mp)).__name__ - unload_distorch_model = getattr(getattr(mp, 'model', None), '_mgpu_unload_distorch_model', False) - - logger.mgpu_mm_log(f"[DETECT_DEBUG] Model {i}: {model_name}, hash={model_hash[:8]}, in_store={in_store}, alloc_value='{alloc_value}', unload_distorch_model={unload_distorch_model}") - - if in_store and alloc_value: - is_distorch_active = True - logger.mgpu_mm_log(f"[DETECT_DEBUG] DisTorch2 ACTIVE detected on model: {model_name}") - break - except Exception as e: - logger.mgpu_mm_log(f"[DETECT_DEBUG] Model {i}: Error during detection - {e}") + model_hash = create_safetensor_model_hash(mp, "cache_patch_check") + in_store = model_hash in safetensor_allocation_store + alloc_value = safetensor_allocation_store.get(model_hash, "") + model_name = type(getattr(mp, 'model', mp)).__name__ + unload_distorch_model = getattr(getattr(mp, 'model', None), '_mgpu_unload_distorch_model', False) + + #logger.mgpu_mm_log(f"[DETECT_DEBUG] Model {i}: {model_name}, hash={model_hash[:8]}, in_store={in_store}, alloc_value='{alloc_value}', unload_distorch_model={unload_distorch_model}") + + if in_store and alloc_value: + is_distorch_active = True + #logger.mgpu_mm_log(f"[DETECT_DEBUG] DisTorch2 ACTIVE detected on model: {model_name}") + break - logger.mgpu_mm_log(f"[DETECT_DEBUG] Final DisTorch2 active status: {is_distorch_active}") + #logger.mgpu_mm_log(f"[DETECT_DEBUG] Final DisTorch2 active status: {is_distorch_active}") # Phase 2: adaptive CPU memory management check_cpu_memory_threshold() @@ -272,7 +268,6 @@ def soft_empty_cache_distorch2_patched(force=False): logger.mgpu_mm_log("DisTorch2 active: clearing allocator caches on all devices (VRAM)") soft_empty_cache_multigpu() else: - logger.mgpu_mm_log("DisTorch2 not active: delegating allocator cache clear (VRAM) to original mm.soft_empty_cache") original_soft_empty_cache(force) # Optional: return CPU heap to OS (not part of Comfy Core) @@ -280,7 +275,6 @@ def soft_empty_cache_distorch2_patched(force=False): if force: logger.mgpu_mm_log("Force flag active: triggering executor cache reset (CPU)") trigger_executor_cache_reset(reason="forced_soft_empty", force=True) - multigpu_memory_log("patched_soft_empty", "end") mm.soft_empty_cache = soft_empty_cache_distorch2_patched diff --git a/examples/hunyuanvideowrapper/hunyuanvideowrapper_native_vae.json b/examples/hunyuanvideowrapper/hunyuanvideowrapper_native_vae.json deleted file mode 100644 index b6320f5..0000000 --- a/examples/hunyuanvideowrapper/hunyuanvideowrapper_native_vae.json +++ /dev/null @@ -1,717 +0,0 @@ -{ - "last_node_id": 51, - "last_link_id": 70, - "nodes": [ - { - "id": 7, - "type": "HyVideoVAELoader", - "pos": [ - -980.2922973632812, - -830.076171875 - ], - "size": [ - 379.166748046875, - 82 - ], - "flags": {}, - "order": 0, - "mode": 4, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "vae", - "type": "VAE", - "links": [], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoVAELoader" - }, - "widgets_values": [ - "hunyuan_video_vae_bf16.safetensors", - "bf16" - ], - "color": "#332922", - "bgcolor": "#593930" - }, - { - "id": 30, - "type": "HyVideoTextEncode", - "pos": [ - -194.8070831298828, - -79.95932006835938 - ], - "size": [ - 425.64068603515625, - 286.85968017578125 - ], - "flags": {}, - "order": 8, - "mode": 0, - "inputs": [ - { - "name": "text_encoders", - "type": "HYVIDTEXTENCODER", - "link": 66 - }, - { - "name": "custom_prompt_template", - "type": "PROMPT_TEMPLATE", - "link": null, - "shape": 7 - }, - { - "name": "clip_l", - "type": "CLIP", - "link": null, - "shape": 7 - }, - { - "name": "hyvid_cfg", - "type": "HYVID_CFG", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "hyvid_embeds", - "type": "HYVIDEMBEDS", - "links": [ - 36 - ] - } - ], - "properties": { - "Node name for S&R": "HyVideoTextEncode" - }, - "widgets_values": [ - "A serene Minnesota lake stretches out at sunset, the water's surface a mirror reflecting the vibrant orange and pink sky. In the foreground, a pair of loons glide gracefully across the water, their sleek black and white feathers contrasting with the warm hues of the sunset. The loons' long, slender necks curve elegantly as they dip their heads into the water, searching for fish. The camera pans slowly from left to right, capturing the tranquil scene. The shoreline is visible in the distance, lined with tall pine trees that cast long shadows across the water. The loons' haunting calls echo across the lake, adding to the peaceful atmosphere.", - false, - "video" - ] - }, - { - "id": 49, - "type": "DownloadAndLoadHyVideoTextEncoderMultiGPU", - "pos": [ - -745.2869262695312, - -80.3648452758789 - ], - "size": [ - 516.5999755859375, - 202 - ], - "flags": {}, - "order": 1, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "hyvid_text_encoder", - "type": "HYVIDTEXTENCODER", - "links": [ - 66 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "DownloadAndLoadHyVideoTextEncoderMultiGPU" - }, - "widgets_values": [ - "Kijai/llava-llama-3-8b-text-encoder-tokenizer", - "disabled", - "bf16", - false, - 2, - "disabled", - "cuda:1" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 48, - "type": "HyVideoModelLoaderMultiGPU", - "pos": [ - -338.0295715332031, - -403.1601257324219 - ], - "size": [ - 497.3603210449219, - 252.03509521484375 - ], - "flags": {}, - "order": 2, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - }, - { - "name": "block_swap_args", - "type": "BLOCKSWAPARGS", - "link": null, - "shape": 7 - }, - { - "name": "lora", - "type": "HYVIDLORA", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "model", - "type": "HYVIDEOMODEL", - "links": [ - 65 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoModelLoaderMultiGPU" - }, - "widgets_values": [ - "hunyuan_video_720_cfgdistill_fp8_e4m3fn.safetensors", - "fp32", - "fp8_e4m3fn", - "main_device", - "sdpa", - false, - "cuda:0" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 16, - "type": "DownloadAndLoadHyVideoTextEncoder", - "pos": [ - -1011.1117553710938, - -1076.6143798828125 - ], - "size": [ - 441, - 178 - ], - "flags": {}, - "order": 3, - "mode": 4, - "inputs": [], - "outputs": [ - { - "name": "hyvid_text_encoder", - "type": "HYVIDTEXTENCODER", - "links": [] - } - ], - "properties": { - "Node name for S&R": "DownloadAndLoadHyVideoTextEncoder" - }, - "widgets_values": [ - "Kijai/llava-llama-3-8b-text-encoder-tokenizer", - "openai/clip-vit-large-patch14", - "fp16", - false, - 2, - "disabled" - ], - "color": "#332922", - "bgcolor": "#593930" - }, - { - "id": 1, - "type": "HyVideoModelLoader", - "pos": [ - -557.619384765625, - -1092.4908447265625 - ], - "size": [ - 435.37628173828125, - 221.34506225585938 - ], - "flags": {}, - "order": 4, - "mode": 4, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - }, - { - "name": "block_swap_args", - "type": "BLOCKSWAPARGS", - "link": null, - "shape": 7 - }, - { - "name": "lora", - "type": "HYVIDLORA", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "model", - "type": "HYVIDEOMODEL", - "links": [], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoModelLoader" - }, - "widgets_values": [ - "hunyuan_video_720_cfgdistill_fp8_e4m3fn.safetensors", - "bf16", - "fp8_e4m3fn", - "main_device", - "sdpa", - false - ], - "color": "#332922", - "bgcolor": "#593930" - }, - { - "id": 34, - "type": "VHS_VideoCombine", - "pos": [ - 847.0758666992188, - -415.1882629394531 - ], - "size": [ - 580.7774658203125, - 698.4859008789062 - ], - "flags": {}, - "order": 12, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 69 - }, - { - "name": "audio", - "type": "AUDIO", - "link": null, - "shape": 7 - }, - { - "name": "meta_batch", - "type": "VHS_BatchManager", - "link": null, - "shape": 7 - }, - { - "name": "vae", - "type": "VAE", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "Filenames", - "type": "VHS_FILENAMES", - "links": null - } - ], - "properties": { - "Node name for S&R": "VHS_VideoCombine" - }, - "widgets_values": { - "frame_rate": 24, - "loop_count": 0, - "filename_prefix": "HunyuanVideo", - "format": "video/h264-mp4", - "pix_fmt": "yuv420p", - "crf": 19, - "save_metadata": true, - "trim_to_audio": false, - "pingpong": false, - "save_output": true, - "videopreview": { - "hidden": false, - "paused": false, - "params": { - "filename": "HunyuanVideo_00161.mp4", - "subfolder": "", - "type": "output", - "format": "video/h264-mp4", - "frame_rate": 24, - "workflow": "HunyuanVideo_00161.png", - "fullpath": "/home/johnj/ComfyUI/output/HunyuanVideo_00161.mp4" - }, - "muted": false - } - } - }, - { - "id": 45, - "type": "VAEDecodeTiled", - "pos": [ - 532.4385986328125, - -644.8389282226562 - ], - "size": [ - 315, - 150 - ], - "flags": {}, - "order": 11, - "mode": 0, - "inputs": [ - { - "name": "samples", - "type": "LATENT", - "link": 70 - }, - { - "name": "vae", - "type": "VAE", - "link": 56 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 69 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "VAEDecodeTiled" - }, - "widgets_values": [ - 256, - 64, - 64, - 8 - ] - }, - { - "id": 44, - "type": "VAELoaderMultiGPU", - "pos": [ - -230.99607849121094, - -639.45654296875 - ], - "size": [ - 315, - 82 - ], - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "VAE", - "type": "VAE", - "links": [ - 56 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "VAELoaderMultiGPU" - }, - "widgets_values": [ - "hunyuan_video_vae_bf16.safetensors", - "cuda:1" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 47, - "type": "HyVideoVAELoaderMultiGPU", - "pos": [ - -512.9594116210938, - -822.8096313476562 - ], - "size": [ - 315, - 106 - ], - "flags": {}, - "order": 6, - "mode": 4, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "vae", - "type": "VAE", - "links": [ - 64 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoVAELoaderMultiGPU" - }, - "widgets_values": [ - "hunyuan_video_vae_bf16.safetensors", - "bf16", - "cuda:1" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 5, - "type": "HyVideoDecode", - "pos": [ - -106.15780639648438, - -843.2271728515625 - ], - "size": [ - 345.4285888671875, - 150 - ], - "flags": {}, - "order": 9, - "mode": 4, - "inputs": [ - { - "name": "vae", - "type": "VAE", - "link": 64 - }, - { - "name": "samples", - "type": "LATENT", - "link": null - } - ], - "outputs": [ - { - "name": "images", - "type": "IMAGE", - "links": [], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoDecode" - }, - "widgets_values": [ - true, - 86, - 256, - true - ] - }, - { - "id": 3, - "type": "HyVideoSampler", - "pos": [ - 255.96482849121094, - -403.58502197265625 - ], - "size": [ - 315, - 630 - ], - "flags": {}, - "order": 10, - "mode": 0, - "inputs": [ - { - "name": "model", - "type": "HYVIDEOMODEL", - "link": 65 - }, - { - "name": "hyvid_embeds", - "type": "HYVIDEMBEDS", - "link": 36 - }, - { - "name": "samples", - "type": "LATENT", - "link": null, - "shape": 7 - }, - { - "name": "stg_args", - "type": "STGARGS", - "link": null, - "shape": 7 - }, - { - "name": "context_options", - "type": "COGCONTEXT", - "link": null, - "shape": 7 - }, - { - "name": "feta_args", - "type": "FETAARGS", - "link": null, - "shape": 7 - }, - { - "name": "teacache_args", - "type": "TEACACHEARGS", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "samples", - "type": "LATENT", - "links": [ - 70 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoSampler" - }, - "widgets_values": [ - 512, - 320, - 85, - 20, - 6, - 9, - 5770521, - "fixed", - false, - 1, - "FlowMatchDiscreteScheduler" - ] - }, - { - "id": 51, - "type": "Note", - "pos": [ - 449.2930908203125, - -955.3724975585938 - ], - "size": [ - 1160.6077880859375, - 211.52166748046875 - ], - "flags": {}, - "order": 7, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "kijai already does an impressive amount of memory management in these nodes, so it is important for MultiGPU to \"play nice\" and vice-versa.\n\nFor this version of the workflow:\n\n• Only two of kijai's three nodes are used - model and text. For VAE we relying on Comfy's native VAE Loader node wrapped by MultiGPU.\n\n• HunyuanVideo TextEncode: \"force_offload\" is set to \"false\". Setting this option to \"true\" would defeat the purpose of selecting a different main_device to load to.\n\n• The main model and VAE devices are set to different cuda devices (cuda0 and cuda1). This is possible because the HunyuanVideo Sampler node outputs latents compatible with Comfy's native TiledVAE Decode.\n• Consequentially, now that we no longer have any other component on cuda0, we set \"force_offload\" to \"false\" on the \"HunyuanVideo Sampler\" node. This keeps the main model in VRAM, eliminating load times for the subsequent generations.\n\n**NOTE** This the **OPTIMAL** way to use MultiGPU with kijai's awesome nodes. If you want to use all three of kijai's loader nodes, please see device_selector_lowvram_flux_controlnet.json in ./examples" - ], - "color": "#432", - "bgcolor": "#653" - } - ], - "links": [ - [ - 36, - 30, - 0, - 3, - 1, - "HYVIDEMBEDS" - ], - [ - 56, - 44, - 0, - 45, - 1, - "VAE" - ], - [ - 64, - 47, - 0, - 5, - 0, - "VAE" - ], - [ - 65, - 48, - 0, - 3, - 0, - "HYVIDEOMODEL" - ], - [ - 66, - 49, - 0, - 30, - 0, - "HYVIDTEXTENCODER" - ], - [ - 69, - 45, - 0, - 34, - 0, - "IMAGE" - ], - [ - 70, - 3, - 0, - 45, - 0, - "LATENT" - ] - ], - "groups": [], - "config": {}, - "extra": { - "ds": { - "scale": 0.8769226950001201, - "offset": [ - 895.3038892918638, - 1121.109618095784 - ] - }, - "ue_links": [], - "VHS_latentpreview": false, - "VHS_latentpreviewrate": 0 - }, - "version": 0.4 -} \ No newline at end of file diff --git a/examples/hunyuanvideowrapper/hunyuanvideowrapper_select_device.json b/examples/hunyuanvideowrapper/hunyuanvideowrapper_select_device.json deleted file mode 100644 index 9dd1f8e..0000000 --- a/examples/hunyuanvideowrapper/hunyuanvideowrapper_select_device.json +++ /dev/null @@ -1,784 +0,0 @@ -{ - "last_node_id": 51, - "last_link_id": 68, - "nodes": [ - { - "id": 7, - "type": "HyVideoVAELoader", - "pos": [ - -980.2922973632812, - -830.076171875 - ], - "size": [ - 379.166748046875, - 82 - ], - "flags": {}, - "order": 0, - "mode": 4, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "vae", - "type": "VAE", - "links": [], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoVAELoader" - }, - "widgets_values": [ - "hunyuan_video_vae_bf16.safetensors", - "bf16" - ], - "color": "#332922", - "bgcolor": "#593930" - }, - { - "id": 30, - "type": "HyVideoTextEncode", - "pos": [ - -194.8070831298828, - -79.95932006835938 - ], - "size": [ - 425.64068603515625, - 286.85968017578125 - ], - "flags": {}, - "order": 7, - "mode": 0, - "inputs": [ - { - "name": "text_encoders", - "type": "HYVIDTEXTENCODER", - "link": 66 - }, - { - "name": "custom_prompt_template", - "type": "PROMPT_TEMPLATE", - "link": null, - "shape": 7 - }, - { - "name": "clip_l", - "type": "CLIP", - "link": null, - "shape": 7 - }, - { - "name": "hyvid_cfg", - "type": "HYVID_CFG", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "hyvid_embeds", - "type": "HYVIDEMBEDS", - "links": [ - 36 - ] - } - ], - "properties": { - "Node name for S&R": "HyVideoTextEncode" - }, - "widgets_values": [ - "A serene Minnesota lake stretches out at sunset, the water's surface a mirror reflecting the vibrant orange and pink sky. In the foreground, a pair of loons glide gracefully across the water, their sleek black and white feathers contrasting with the warm hues of the sunset. The loons' long, slender necks curve elegantly as they dip their heads into the water, searching for fish. The camera pans slowly from left to right, capturing the tranquil scene. The shoreline is visible in the distance, lined with tall pine trees that cast long shadows across the water. The loons' haunting calls echo across the lake, adding to the peaceful atmosphere.", - false, - "video" - ] - }, - { - "id": 49, - "type": "DownloadAndLoadHyVideoTextEncoderMultiGPU", - "pos": [ - -745.2869262695312, - -80.3648452758789 - ], - "size": [ - 516.5999755859375, - 202 - ], - "flags": {}, - "order": 1, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "hyvid_text_encoder", - "type": "HYVIDTEXTENCODER", - "links": [ - 66 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "DownloadAndLoadHyVideoTextEncoderMultiGPU" - }, - "widgets_values": [ - "Kijai/llava-llama-3-8b-text-encoder-tokenizer", - "disabled", - "bf16", - false, - 2, - "disabled", - "cuda:1" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 48, - "type": "HyVideoModelLoaderMultiGPU", - "pos": [ - -338.0295715332031, - -403.1601257324219 - ], - "size": [ - 497.3603210449219, - 252.03509521484375 - ], - "flags": {}, - "order": 8, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - }, - { - "name": "block_swap_args", - "type": "BLOCKSWAPARGS", - "link": null, - "shape": 7 - }, - { - "name": "lora", - "type": "HYVIDLORA", - "link": null, - "shape": 7 - }, - { - "name": "device", - "type": "COMBO", - "link": 67, - "widget": { - "name": "device" - }, - "shape": 7 - } - ], - "outputs": [ - { - "name": "model", - "type": "HYVIDEOMODEL", - "links": [ - 65 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoModelLoaderMultiGPU" - }, - "widgets_values": [ - "hunyuan_video_720_cfgdistill_fp8_e4m3fn.safetensors", - "fp32", - "fp8_e4m3fn", - "main_device", - "sdpa", - false, - "cuda:0" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 16, - "type": "DownloadAndLoadHyVideoTextEncoder", - "pos": [ - -1011.1117553710938, - -1076.6143798828125 - ], - "size": [ - 441, - 178 - ], - "flags": {}, - "order": 2, - "mode": 4, - "inputs": [], - "outputs": [ - { - "name": "hyvid_text_encoder", - "type": "HYVIDTEXTENCODER", - "links": [] - } - ], - "properties": { - "Node name for S&R": "DownloadAndLoadHyVideoTextEncoder" - }, - "widgets_values": [ - "Kijai/llava-llama-3-8b-text-encoder-tokenizer", - "openai/clip-vit-large-patch14", - "fp16", - false, - 2, - "disabled" - ], - "color": "#332922", - "bgcolor": "#593930" - }, - { - "id": 1, - "type": "HyVideoModelLoader", - "pos": [ - -557.619384765625, - -1092.4908447265625 - ], - "size": [ - 435.37628173828125, - 221.34506225585938 - ], - "flags": {}, - "order": 3, - "mode": 4, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - }, - { - "name": "block_swap_args", - "type": "BLOCKSWAPARGS", - "link": null, - "shape": 7 - }, - { - "name": "lora", - "type": "HYVIDLORA", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "model", - "type": "HYVIDEOMODEL", - "links": [], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoModelLoader" - }, - "widgets_values": [ - "hunyuan_video_720_cfgdistill_fp8_e4m3fn.safetensors", - "bf16", - "fp8_e4m3fn", - "main_device", - "sdpa", - false - ], - "color": "#332922", - "bgcolor": "#593930" - }, - { - "id": 34, - "type": "VHS_VideoCombine", - "pos": [ - 847.0758666992188, - -415.1882629394531 - ], - "size": [ - 580.7774658203125, - 698.4859008789062 - ], - "flags": {}, - "order": 13, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 63 - }, - { - "name": "audio", - "type": "AUDIO", - "link": null, - "shape": 7 - }, - { - "name": "meta_batch", - "type": "VHS_BatchManager", - "link": null, - "shape": 7 - }, - { - "name": "vae", - "type": "VAE", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "Filenames", - "type": "VHS_FILENAMES", - "links": null - } - ], - "properties": { - "Node name for S&R": "VHS_VideoCombine" - }, - "widgets_values": { - "frame_rate": 24, - "loop_count": 0, - "filename_prefix": "HunyuanVideo", - "format": "video/h264-mp4", - "pix_fmt": "yuv420p", - "crf": 19, - "save_metadata": true, - "trim_to_audio": false, - "pingpong": false, - "save_output": true, - "videopreview": { - "hidden": false, - "paused": false, - "params": { - "filename": "HunyuanVideo_00161.mp4", - "subfolder": "", - "type": "output", - "format": "video/h264-mp4", - "frame_rate": 24, - "workflow": "HunyuanVideo_00161.png", - "fullpath": "/home/johnj/ComfyUI/output/HunyuanVideo_00161.mp4" - }, - "muted": false - } - } - }, - { - "id": 5, - "type": "HyVideoDecode", - "pos": [ - 538.94189453125, - -625.3562622070312 - ], - "size": [ - 345.4285888671875, - 150 - ], - "flags": {}, - "order": 12, - "mode": 0, - "inputs": [ - { - "name": "vae", - "type": "VAE", - "link": 64 - }, - { - "name": "samples", - "type": "LATENT", - "link": 4 - } - ], - "outputs": [ - { - "name": "images", - "type": "IMAGE", - "links": [ - 63 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoDecode" - }, - "widgets_values": [ - true, - 86, - 256, - true - ] - }, - { - "id": 50, - "type": "DeviceSelectorMultiGPU", - "pos": [ - -716.0182495117188, - -492.63983154296875 - ], - "size": [ - 315, - 58 - ], - "flags": {}, - "order": 4, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "device", - "type": "COMBO", - "links": [ - 67, - 68 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "DeviceSelectorMultiGPU" - }, - "widgets_values": [ - "cuda:0" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 47, - "type": "HyVideoVAELoaderMultiGPU", - "pos": [ - -313.7779846191406, - -620.58740234375 - ], - "size": [ - 315, - 106 - ], - "flags": {}, - "order": 9, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "type": "COMPILEARGS", - "link": null, - "shape": 7 - }, - { - "name": "device", - "type": "COMBO", - "link": 68, - "widget": { - "name": "device" - }, - "shape": 7 - } - ], - "outputs": [ - { - "name": "vae", - "type": "VAE", - "links": [ - 64 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoVAELoaderMultiGPU" - }, - "widgets_values": [ - "hunyuan_video_vae_bf16.safetensors", - "bf16", - "cuda:1" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 3, - "type": "HyVideoSampler", - "pos": [ - 255.96482849121094, - -403.58502197265625 - ], - "size": [ - 315, - 630 - ], - "flags": {}, - "order": 11, - "mode": 0, - "inputs": [ - { - "name": "model", - "type": "HYVIDEOMODEL", - "link": 65 - }, - { - "name": "hyvid_embeds", - "type": "HYVIDEMBEDS", - "link": 36 - }, - { - "name": "samples", - "type": "LATENT", - "link": null, - "shape": 7 - }, - { - "name": "stg_args", - "type": "STGARGS", - "link": null, - "shape": 7 - }, - { - "name": "context_options", - "type": "COGCONTEXT", - "link": null, - "shape": 7 - }, - { - "name": "feta_args", - "type": "FETAARGS", - "link": null, - "shape": 7 - }, - { - "name": "teacache_args", - "type": "TEACACHEARGS", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "samples", - "type": "LATENT", - "links": [ - 4 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "HyVideoSampler" - }, - "widgets_values": [ - 512, - 320, - 85, - 20, - 6, - 9, - 5770521, - "fixed", - true, - 1, - "FlowMatchDiscreteScheduler" - ] - }, - { - "id": 44, - "type": "VAELoaderMultiGPU", - "pos": [ - -556.3764038085938, - -809.7488403320312 - ], - "size": [ - 315, - 82 - ], - "flags": {}, - "order": 5, - "mode": 4, - "inputs": [], - "outputs": [ - { - "name": "VAE", - "type": "VAE", - "links": [ - 56 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "VAELoaderMultiGPU" - }, - "widgets_values": [ - "hunyuan_video_vae_bf16.safetensors", - "cuda:0" - ] - }, - { - "id": 45, - "type": "VAEDecodeTiled", - "pos": [ - -195.86590576171875, - -828.8155517578125 - ], - "size": [ - 315, - 150 - ], - "flags": {}, - "order": 10, - "mode": 4, - "inputs": [ - { - "name": "samples", - "type": "LATENT", - "link": null - }, - { - "name": "vae", - "type": "VAE", - "link": 56 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "VAEDecodeTiled" - }, - "widgets_values": [ - 256, - 64, - 64, - 8 - ] - }, - { - "id": 51, - "type": "Note", - "pos": [ - 217.09609985351562, - -905.6065063476562 - ], - "size": [ - 1160.6077880859375, - 211.52166748046875 - ], - "flags": {}, - "order": 6, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "kijai already does an impressive amount of memory management in these nodes, so it is important for MultiGPU to \"play nice\" and vice-versa.\n\nFor this version of the workflow:\n\n• All three of kijai's nodes are used - model, text, and VAE\n\n• HunyuanVideo TextEncode: \"force_offload\" is set to \"false\". Setting this option to \"true\" would defeat the purpose of selecting a different main_device to load to.\n\n• The main model and VAE devices are linked. This is because kijai's \"HunyuanVideo Decode\" expects both the model and the VAE to be on the same device. \n• Consequentially, to eliminate out-of-memory errors, \"force_offload\" is set to \"true\" on the \"HunyuanVideo Sampler\" node.\n\n\n**NOTE** This is not the optimial way to use MultiGPU. Please see the workflow at for an example of loading the VAE to a different cuda device using the native VAE loader and tiled decode." - ], - "color": "#432", - "bgcolor": "#653" - } - ], - "links": [ - [ - 4, - 3, - 0, - 5, - 1, - "LATENT" - ], - [ - 36, - 30, - 0, - 3, - 1, - "HYVIDEMBEDS" - ], - [ - 56, - 44, - 0, - 45, - 1, - "VAE" - ], - [ - 63, - 5, - 0, - 34, - 0, - "IMAGE" - ], - [ - 64, - 47, - 0, - 5, - 0, - "VAE" - ], - [ - 65, - 48, - 0, - 3, - 0, - "HYVIDEOMODEL" - ], - [ - 66, - 49, - 0, - 30, - 0, - "HYVIDTEXTENCODER" - ], - [ - 67, - 50, - 0, - 48, - 3, - "COMBO" - ], - [ - 68, - 50, - 0, - 47, - 1, - "COMBO" - ] - ], - "groups": [], - "config": {}, - "extra": { - "ds": { - "scale": 0.7972024500001089, - "offset": [ - 1218.3831555808085, - 1163.8844215880747 - ] - }, - "ue_links": [], - "VHS_latentpreview": false, - "VHS_latentpreviewrate": 0 - }, - "version": 0.4 -} \ No newline at end of file diff --git a/examples/wannvideowrapper/wanvideo2_2_I2V_A14B_example_WIP_Multigpu.json b/examples/wannvideowrapper/wanvideo2_2_I2V_A14B_example_WIP_Multigpu.json index 7393f45..4c3dd48 100755 --- a/examples/wannvideowrapper/wanvideo2_2_I2V_A14B_example_WIP_Multigpu.json +++ b/examples/wannvideowrapper/wanvideo2_2_I2V_A14B_example_WIP_Multigpu.json @@ -1,8 +1,8 @@ { "id": "c6e410bc-5e2c-460b-ae81-c91b6094fbb1", "revision": 0, - "last_node_id": 106, - "last_link_id": 195, + "last_node_id": 116, + "last_link_id": 215, "nodes": [ { "id": 50, @@ -36,9 +36,9 @@ } ], "properties": { - "Node name for S&R": "CLIPTextEncode", "cnr_id": "comfy-core", - "ver": "0.3.44" + "ver": "0.3.44", + "Node name for S&R": "CLIPTextEncode" }, "widgets_values": [ "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" @@ -73,9 +73,9 @@ } ], "properties": { - "Node name for S&R": "CLIPLoader", "cnr_id": "comfy-core", - "ver": "0.3.44" + "ver": "0.3.44", + "Node name for S&R": "CLIPLoader" }, "widgets_values": [ "umt5_xxl_fp16.safetensors", @@ -140,9 +140,9 @@ } ], "properties": { - "Node name for S&R": "CLIPTextEncode", "cnr_id": "comfy-core", - "ver": "0.3.44" + "ver": "0.3.44", + "Node name for S&R": "CLIPTextEncode" }, "widgets_values": [ "high quality nature video featuring a red panda balancing on a bamboo stem while a bird lands on it's head, on the background there is a waterfall" @@ -162,7 +162,7 @@ 46 ], "flags": {}, - "order": 20, + "order": 21, "mode": 2, "inputs": [ { @@ -185,9 +185,9 @@ } ], "properties": { - "Node name for S&R": "WanVideoTextEmbedBridge", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" + "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e", + "Node name for S&R": "WanVideoTextEmbedBridge" }, "widgets_values": [] }, @@ -203,19 +203,19 @@ 46 ], "flags": {}, - "order": 22, + "order": 19, "mode": 0, "inputs": [ { "name": "model", "type": "WANVIDEOMODEL", - "link": 171 + "link": null }, { "name": "block_swap_args", "shape": 7, "type": "BLOCKSWAPARGS", - "link": 177 + "link": 214 } ], "outputs": [ @@ -228,66 +228,14 @@ } ], "properties": { - "Node name for S&R": "WanVideoSetBlockSwap", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f" + "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f", + "Node name for S&R": "WanVideoSetBlockSwap" }, "widgets_values": [], "color": "#223", "bgcolor": "#335" }, - { - "id": 28, - "type": "WanVideoDecode", - "pos": [ - 2620.946533203125, - -519.3373413085938 - ], - "size": [ - 315, - 198 - ], - "flags": {}, - "order": 28, - "mode": 0, - "inputs": [ - { - "name": "vae", - "type": "WANVAE", - "link": 179 - }, - { - "name": "samples", - "type": "LATENT", - "link": 195 - } - ], - "outputs": [ - { - "name": "images", - "type": "IMAGE", - "slot_index": 0, - "links": [ - 76 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoDecode", - "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" - }, - "widgets_values": [ - false, - 272, - 272, - 144, - 128, - "default" - ], - "color": "#322", - "bgcolor": "#533" - }, { "id": 69, "type": "GetImageSizeAndCount", @@ -306,7 +254,7 @@ { "name": "image", "type": "IMAGE", - "link": 76 + "link": 211 } ], "outputs": [ @@ -324,7 +272,7 @@ "links": null }, { - "label": "704 height", + "label": "512 height", "name": "height", "type": "INT", "links": null @@ -337,83 +285,12 @@ } ], "properties": { - "Node name for S&R": "GetImageSizeAndCount", "cnr_id": "comfyui-kjnodes", - "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57" + "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57", + "Node name for S&R": "GetImageSizeAndCount" }, "widgets_values": [] }, - { - "id": 68, - "type": "ImageResizeKJv2", - "pos": [ - 696.0801391601562, - -1143.5843505859375 - ], - "size": [ - 270, - 336 - ], - "flags": {}, - "order": 17, - "mode": 0, - "inputs": [ - { - "name": "image", - "type": "IMAGE", - "link": 71 - }, - { - "name": "mask", - "shape": 7, - "type": "MASK", - "link": null - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 139 - ] - }, - { - "name": "width", - "type": "INT", - "links": [ - 141 - ] - }, - { - "name": "height", - "type": "INT", - "links": [ - 142 - ] - }, - { - "name": "mask", - "type": "MASK", - "links": null - } - ], - "properties": { - "Node name for S&R": "ImageResizeKJv2", - "cnr_id": "comfyui-kjnodes", - "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57" - }, - "widgets_values": [ - 720, - 720, - "lanczos", - "crop", - "0, 0, 0", - "center", - 32, - "cpu" - ] - }, { "id": 56, "type": "WanVideoLoraSelect", @@ -452,9 +329,9 @@ } ], "properties": { - "Node name for S&R": "WanVideoLoraSelect", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" + "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e", + "Node name for S&R": "WanVideoLoraSelect" }, "widgets_values": [ "WanVideo/Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank64_bf16.safetensors", @@ -503,9 +380,9 @@ } ], "properties": { - "Node name for S&R": "WanVideoLoraSelect", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" + "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e", + "Node name for S&R": "WanVideoLoraSelect" }, "widgets_values": [ "WanVideo/Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank64_bf16.safetensors", @@ -516,166 +393,6 @@ "color": "#223", "bgcolor": "#335" }, - { - "id": 98, - "type": "WanVideoModelLoaderMultiGPU", - "pos": [ - -42.05561065673828, - -769.6892700195312 - ], - "size": [ - 501.6299743652344, - 274 - ], - "flags": {}, - "order": 18, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "shape": 7, - "type": "WANCOMPILEARGS", - "link": 170 - }, - { - "name": "block_swap_args", - "shape": 7, - "type": "BLOCKSWAPARGS", - "link": null - }, - { - "name": "lora", - "shape": 7, - "type": "WANVIDLORA", - "link": null - }, - { - "name": "vram_management_args", - "shape": 7, - "type": "VRAM_MANAGEMENTARGS", - "link": null - }, - { - "name": "vace_model", - "shape": 7, - "type": "VACEPATH", - "link": null - }, - { - "name": "fantasytalking_model", - "shape": 7, - "type": "FANTASYTALKINGMODEL", - "link": null - }, - { - "name": "multitalk_model", - "shape": 7, - "type": "MULTITALKMODEL", - "link": null - } - ], - "outputs": [ - { - "name": "model", - "type": "WANVIDEOMODEL", - "links": [ - 171 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoModelLoaderMultiGPU" - }, - "widgets_values": [ - "WanVideo/2_2/Wan2_2-I2V-A14B-HIGH_fp8_e4m3fn_scaled_KJ.safetensors", - "fp16_fast", - "fp8_e4m3fn_scaled", - "cuda:0", - "sageattn" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 101, - "type": "WanVideoModelLoaderMultiGPU_2", - "pos": [ - -56.241695404052734, - -392.83917236328125 - ], - "size": [ - 516.6480102539062, - 307.97137451171875 - ], - "flags": {}, - "order": 19, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "shape": 7, - "type": "WANCOMPILEARGS", - "link": 175 - }, - { - "name": "block_swap_args", - "shape": 7, - "type": "BLOCKSWAPARGS", - "link": null - }, - { - "name": "lora", - "shape": 7, - "type": "WANVIDLORA", - "link": null - }, - { - "name": "vram_management_args", - "shape": 7, - "type": "VRAM_MANAGEMENTARGS", - "link": null - }, - { - "name": "vace_model", - "shape": 7, - "type": "VACEPATH", - "link": null - }, - { - "name": "fantasytalking_model", - "shape": 7, - "type": "FANTASYTALKINGMODEL", - "link": null - }, - { - "name": "multitalk_model", - "shape": 7, - "type": "MULTITALKMODEL", - "link": null - } - ], - "outputs": [ - { - "name": "model", - "type": "WANVIDEOMODEL", - "links": [ - 176 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoModelLoaderMultiGPU_2" - }, - "widgets_values": [ - "WanVideo/2_2/Wan2_2-I2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors", - "fp16_fast", - "fp8_e4m3fn_scaled", - "cuda:1", - "flash_attn_2" - ], - "color": "#233", - "bgcolor": "#355" - }, { "id": 44, "type": "Note", @@ -699,42 +416,6 @@ "color": "#432", "bgcolor": "#653" }, - { - "id": 99, - "type": "LoadWanVideoT5TextEncoderMultiGPU", - "pos": [ - 290.99420166015625, - 0.9395794868469238 - ], - "size": [ - 348.9195251464844, - 130 - ], - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "wan_t5_model", - "type": "WANTEXTENCODER", - "links": [ - 172 - ] - } - ], - "properties": { - "Node name for S&R": "LoadWanVideoT5TextEncoderMultiGPU" - }, - "widgets_values": [ - "umt5-xxl-enc-bf16.safetensors", - "bf16", - "cpu", - "disabled" - ], - "color": "#233", - "bgcolor": "#355" - }, { "id": 102, "type": "Note", @@ -747,7 +428,7 @@ 88 ], "flags": {}, - "order": 6, + "order": 5, "mode": 0, "inputs": [], "outputs": [], @@ -770,19 +451,19 @@ 46 ], "flags": {}, - "order": 23, + "order": 22, "mode": 0, "inputs": [ { "name": "model", "type": "WANVIDEOMODEL", - "link": 176 + "link": 196 }, { "name": "block_swap_args", "shape": 7, "type": "BLOCKSWAPARGS", - "link": 178 + "link": 215 } ], "outputs": [ @@ -795,97 +476,14 @@ } ], "properties": { - "Node name for S&R": "WanVideoSetBlockSwap", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f" + "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f", + "Node name for S&R": "WanVideoSetBlockSwap" }, "widgets_values": [], "color": "#223", "bgcolor": "#335" }, - { - "id": 103, - "type": "WanVideoBlockSwapMultiGPU", - "pos": [ - 524.8197021484375, - -561.302001953125 - ], - "size": [ - 292.8716735839844, - 202 - ], - "flags": {}, - "order": 7, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "block_swap_args", - "type": "BLOCKSWAPARGS", - "links": [ - 177, - 178 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoBlockSwapMultiGPU" - }, - "widgets_values": [ - 5, - "cuda:2", - "cpu", - false, - false, - false, - 0 - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 104, - "type": "WanVideoVAELoaderMultiGPU", - "pos": [ - 2039.183349609375, - -784.7181396484375 - ], - "size": [ - 294.1841735839844, - 106 - ], - "flags": {}, - "order": 8, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "shape": 7, - "type": "WANCOMPILEARGS", - "link": null - } - ], - "outputs": [ - { - "name": "vae", - "type": "WANVAE", - "links": [ - 179, - 180 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoVAELoaderMultiGPU" - }, - "widgets_values": [ - "wan_2.1_vae.safetensors", - "cuda:0", - "bf16" - ], - "color": "#233", - "bgcolor": "#355" - }, { "id": 80, "type": "WanVideoSetLoRAs", @@ -898,7 +496,7 @@ 46 ], "flags": {}, - "order": 24, + "order": 23, "mode": 0, "inputs": [ { @@ -917,15 +515,13 @@ { "name": "model", "type": "WANVIDEOMODEL", - "links": [ - 182 - ] + "links": [] } ], "properties": { - "Node name for S&R": "WanVideoSetLoRAs", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" + "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e", + "Node name for S&R": "WanVideoSetLoRAs" }, "widgets_values": [], "color": "#223", @@ -940,10 +536,10 @@ ], "size": [ 298.3199157714844, - 178 + 228 ], "flags": {}, - "order": 16, + "order": 15, "mode": 0, "inputs": [ { @@ -965,9 +561,9 @@ } ], "properties": { - "Node name for S&R": "CreateCFGScheduleFloatList", "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f" + "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f", + "Node name for S&R": "CreateCFGScheduleFloatList" }, "widgets_values": [ 30, @@ -975,19 +571,680 @@ 2, "linear", 0, - 0.01 + 0.01, + "[2.0, 1.0, 1.0, 1.0, 1.0, 1.0]" ] }, + { + "id": 79, + "type": "WanVideoSetLoRAs", + "pos": [ + 969.6483764648438, + -216.53614807128906 + ], + "size": [ + 222.27981567382812, + 46 + ], + "flags": {}, + "order": 25, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "WANVIDEOMODEL", + "link": 161 + }, + { + "name": "lora", + "shape": 7, + "type": "WANVIDLORA", + "link": 169 + } + ], + "outputs": [ + { + "name": "model", + "type": "WANVIDEOMODEL", + "links": [ + 190 + ] + } + ], + "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e", + "Node name for S&R": "WanVideoSetLoRAs" + }, + "widgets_values": [], + "color": "#223", + "bgcolor": "#335" + }, + { + "id": 94, + "type": "INTConstant", + "pos": [ + 1446.0140380859375, + -77.41889953613281 + ], + "size": [ + 210, + 58 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "value", + "type": "INT", + "links": [ + 165, + 184, + 193 + ] + } + ], + "title": "Steps", + "properties": { + "cnr_id": "comfyui-kjnodes", + "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57", + "Node name for S&R": "INTConstant" + }, + "widgets_values": [ + 6 + ], + "color": "#1b4669", + "bgcolor": "#29699c" + }, + { + "id": 91, + "type": "INTConstant", + "pos": [ + 1473.2628173828125, + 579.54443359375 + ], + "size": [ + 210, + 58 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "value", + "type": "INT", + "links": [ + 187, + 194 + ] + } + ], + "title": "Split_step", + "properties": { + "cnr_id": "comfyui-kjnodes", + "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57", + "Node name for S&R": "INTConstant" + }, + "widgets_values": [ + 3 + ], + "color": "#1b4669", + "bgcolor": "#29699c" + }, + { + "id": 60, + "type": "VHS_VideoCombine", + "pos": [ + 3150, + -390 + ], + "size": [ + 698.6392211914062, + 841.5557861328125 + ], + "flags": {}, + "order": 30, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 77 + }, + { + "name": "audio", + "shape": 7, + "type": "AUDIO", + "link": null + }, + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-videohelpersuite", + "ver": "0a75c7958fe320efcb052f1d9f8451fd20c730a8", + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 16, + "loop_count": 0, + "filename_prefix": "WanVideo2_2_I2V", + "format": "video/h264-mp4", + "pix_fmt": "yuv420p", + "crf": 19, + "save_metadata": true, + "trim_to_audio": false, + "pingpong": false, + "save_output": true, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "WanVideo2_2_I2V_00041.mp4", + "subfolder": "", + "type": "output", + "format": "video/h264-mp4", + "frame_rate": 16, + "workflow": "WanVideo2_2_I2V_00041.png", + "fullpath": "/home/johnj/ComfyUI/output/WanVideo2_2_I2V_00041.mp4" + } + } + } + }, + { + "id": 109, + "type": "WanVideoModelLoaderMultiGPU", + "pos": [ + -48.799888610839844, + -768.8370971679688 + ], + "size": [ + 450.2710876464844, + 342 + ], + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "compile_args", + "shape": 7, + "type": "WANCOMPILEARGS", + "link": 213 + }, + { + "name": "block_swap_args", + "shape": 7, + "type": "BLOCKSWAPARGS", + "link": null + }, + { + "name": "lora", + "shape": 7, + "type": "WANVIDLORA", + "link": null + }, + { + "name": "vram_management_args", + "shape": 7, + "type": "VRAM_MANAGEMENTARGS", + "link": null + }, + { + "name": "extra_model", + "shape": 7, + "type": "VACEPATH", + "link": null + }, + { + "name": "fantasytalking_model", + "shape": 7, + "type": "FANTASYTALKINGMODEL", + "link": null + }, + { + "name": "multitalk_model", + "shape": 7, + "type": "MULTITALKMODEL", + "link": null + }, + { + "name": "fantasyportrait_model", + "shape": 7, + "type": "FANTASYPORTRAITMODEL", + "link": null + } + ], + "outputs": [ + { + "name": "model", + "type": "WANVIDEOMODEL", + "links": [ + 200 + ] + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "links": [ + 201 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoModelLoaderMultiGPU" + }, + "widgets_values": [ + "WanVideo/2_2/Wan2_2-I2V-A14B-HIGH_fp8_e4m3fn_scaled_KJ.safetensors", + "fp16_fast", + "fp8_e4m3fn_scaled", + "offload_device", + "cuda:0", + "sdpa", + "default" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 112, + "type": "WanVideoImageToVideoEncodeMultiGPU", + "pos": [ + 1388.4976806640625, + -903.1487426757812 + ], + "size": [ + 360.3492126464844, + 410 + ], + "flags": {}, + "order": 24, + "mode": 0, + "inputs": [ + { + "name": "vae", + "shape": 7, + "type": "WANVAE", + "link": 202 + }, + { + "name": "load_device", + "shape": 7, + "type": "MULTIGPUDEVICE", + "link": 203 + }, + { + "name": "clip_embeds", + "shape": 7, + "type": "WANVIDIMAGE_CLIPEMBEDS", + "link": null + }, + { + "name": "start_image", + "shape": 7, + "type": "IMAGE", + "link": 204 + }, + { + "name": "end_image", + "shape": 7, + "type": "IMAGE", + "link": null + }, + { + "name": "control_embeds", + "shape": 7, + "type": "WANVIDIMAGE_EMBEDS", + "link": null + }, + { + "name": "temporal_mask", + "shape": 7, + "type": "MASK", + "link": null + }, + { + "name": "extra_latents", + "shape": 7, + "type": "LATENT", + "link": null + }, + { + "name": "add_cond_latents", + "shape": 7, + "type": "ADD_COND_LATENTS", + "link": null + }, + { + "name": "width", + "type": "INT", + "widget": { + "name": "width" + }, + "link": 205 + }, + { + "name": "height", + "type": "INT", + "widget": { + "name": "height" + }, + "link": 206 + } + ], + "outputs": [ + { + "name": "image_embeds", + "type": "WANVIDIMAGE_EMBEDS", + "links": [ + 207, + 208 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoImageToVideoEncodeMultiGPU" + }, + "widgets_values": [ + 832, + 480, + 81, + 0, + 1, + 1, + true, + false, + false + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 114, + "type": "WanVideoVAELoaderMultiGPU", + "pos": [ + 1988.8050537109375, + -840.9067993164062 + ], + "size": [ + 294.1841735839844, + 126 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "compile_args", + "shape": 7, + "type": "WANCOMPILEARGS", + "link": null + } + ], + "outputs": [ + { + "name": "vae", + "type": "WANVAE", + "links": [ + 202, + 209 + ] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": [ + 203, + 210 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoVAELoaderMultiGPU" + }, + "widgets_values": [ + "wan_2.1_vae.safetensors", + "cuda:0", + "bf16" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 115, + "type": "WanVideoDecodeMultiGPU", + "pos": [ + 2517.043212890625, + -736.8970947265625 + ], + "size": [ + 271.8716735839844, + 218 + ], + "flags": {}, + "order": 28, + "mode": 0, + "inputs": [ + { + "name": "vae", + "type": "WANVAE", + "link": 209 + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "link": 210 + }, + { + "name": "samples", + "type": "LATENT", + "link": 212 + } + ], + "outputs": [ + { + "name": "images", + "type": "IMAGE", + "links": [ + 211 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoDecodeMultiGPU" + }, + "widgets_values": [ + false, + 272, + 272, + 144, + 128, + "default" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 108, + "type": "WanVideoModelLoaderMultiGPU", + "pos": [ + -47.79293441772461, + -350.2671813964844 + ], + "size": [ + 458.3773498535156, + 342 + ], + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "compile_args", + "shape": 7, + "type": "WANCOMPILEARGS", + "link": 198 + }, + { + "name": "block_swap_args", + "shape": 7, + "type": "BLOCKSWAPARGS", + "link": null + }, + { + "name": "lora", + "shape": 7, + "type": "WANVIDLORA", + "link": null + }, + { + "name": "vram_management_args", + "shape": 7, + "type": "VRAM_MANAGEMENTARGS", + "link": null + }, + { + "name": "extra_model", + "shape": 7, + "type": "VACEPATH", + "link": null + }, + { + "name": "fantasytalking_model", + "shape": 7, + "type": "FANTASYTALKINGMODEL", + "link": null + }, + { + "name": "multitalk_model", + "shape": 7, + "type": "MULTITALKMODEL", + "link": null + }, + { + "name": "fantasyportrait_model", + "shape": 7, + "type": "FANTASYPORTRAITMODEL", + "link": null + } + ], + "outputs": [ + { + "name": "model", + "type": "WANVIDEOMODEL", + "links": [ + 196 + ] + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "links": [ + 197 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoModelLoaderMultiGPU" + }, + "widgets_values": [ + "WanVideo/2_2/Wan2_2-I2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors", + "fp16_fast", + "fp8_e4m3fn_scaled", + "offload_device", + "cuda:0", + "sdpa", + "default" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 99, + "type": "LoadWanVideoT5TextEncoderMultiGPU", + "pos": [ + 363.88275146484375, + 41.0692138671875 + ], + "size": [ + 348.9195251464844, + 150 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "wan_t5_model", + "type": "WANTEXTENCODER", + "links": [ + 172 + ] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": [ + 199 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "LoadWanVideoT5TextEncoderMultiGPU" + }, + "widgets_values": [ + "umt5-xxl-enc-bf16.safetensors", + "bf16", + "cuda:0", + "disabled" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, { "id": 105, "type": "WanVideoSamplerMultiGPU", "pos": [ - 1862.6126708984375, - -387.4342041015625 + 1822.2808837890625, + -346.01214599609375 ], "size": [ 327.80859375, - 902 + 922 ], "flags": {}, "order": 26, @@ -996,12 +1253,17 @@ { "name": "model", "type": "WANVIDEOMODEL", - "link": 182 + "link": 200 + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "link": 201 }, { "name": "image_embeds", "type": "WANVIDIMAGE_EMBEDS", - "link": 181 + "link": 207 }, { "name": "text_embeds", @@ -1134,15 +1396,17 @@ } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", "Node name for S&R": "WanVideoSamplerMultiGPU" }, "widgets_values": [ 30, 6, 8.000000000000002, - 43, - "fixed", - false, + 914434359508585, + "randomize", + true, "dpm++_sde", 0, 1, @@ -1152,298 +1416,19 @@ -1, false ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 79, - "type": "WanVideoSetLoRAs", - "pos": [ - 969.6483764648438, - -216.53614807128906 - ], - "size": [ - 222.27981567382812, - 46 - ], - "flags": {}, - "order": 25, - "mode": 0, - "inputs": [ - { - "name": "model", - "type": "WANVIDEOMODEL", - "link": 161 - }, - { - "name": "lora", - "shape": 7, - "type": "WANVIDLORA", - "link": 169 - } - ], - "outputs": [ - { - "name": "model", - "type": "WANVIDEOMODEL", - "links": [ - 190 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoSetLoRAs", - "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" - }, - "widgets_values": [], - "color": "#223", - "bgcolor": "#335" - }, - { - "id": 89, - "type": "WanVideoImageToVideoEncode", - "pos": [ - 1401.11962890625, - -698.7300415039062 - ], - "size": [ - 308.2320251464844, - 390 - ], - "flags": {}, - "order": 21, - "mode": 0, - "inputs": [ - { - "name": "vae", - "type": "WANVAE", - "link": 180 - }, - { - "name": "clip_embeds", - "shape": 7, - "type": "WANVIDIMAGE_CLIPEMBEDS", - "link": null - }, - { - "name": "start_image", - "shape": 7, - "type": "IMAGE", - "link": 139 - }, - { - "name": "end_image", - "shape": 7, - "type": "IMAGE", - "link": null - }, - { - "name": "control_embeds", - "shape": 7, - "type": "WANVIDIMAGE_EMBEDS", - "link": null - }, - { - "name": "temporal_mask", - "shape": 7, - "type": "MASK", - "link": null - }, - { - "name": "extra_latents", - "shape": 7, - "type": "LATENT", - "link": null - }, - { - "name": "add_cond_latents", - "shape": 7, - "type": "ADD_COND_LATENTS", - "link": null - }, - { - "name": "width", - "type": "INT", - "widget": { - "name": "width" - }, - "link": 141 - }, - { - "name": "height", - "type": "INT", - "widget": { - "name": "height" - }, - "link": 142 - } - ], - "outputs": [ - { - "name": "image_embeds", - "type": "WANVIDIMAGE_EMBEDS", - "links": [ - 181, - 191 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoImageToVideoEncode", - "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f" - }, - "widgets_values": [ - 832, - 480, - 81, - 0, - 1, - 1, - true, - false, - false - ], - "color": "#322", - "bgcolor": "#533" - }, - { - "id": 100, - "type": "WanVideoTextEncodeMultiGPU", - "pos": [ - 747.646728515625, - -17.347864151000977 - ], - "size": [ - 400, - 234 - ], - "flags": {}, - "order": 15, - "mode": 0, - "inputs": [ - { - "name": "t5", - "shape": 7, - "type": "WANTEXTENCODER", - "link": 172 - }, - { - "name": "model_to_offload", - "shape": 7, - "type": "WANVIDEOMODEL", - "link": null - } - ], - "outputs": [ - { - "name": "text_embeds", - "type": "WANVIDEOTEXTEMBEDS", - "links": [ - 183, - 192 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoTextEncodeMultiGPU" - }, - "widgets_values": [ - "old man gets up and jumps into the lake", - "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走", - "cpu", - false, - false - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 94, - "type": "INTConstant", - "pos": [ - 1446.0140380859375, - -77.41889953613281 - ], - "size": [ - 210, - 58 - ], - "flags": {}, - "order": 9, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "value", - "type": "INT", - "links": [ - 165, - 184, - 193 - ] - } - ], - "title": "Steps", - "properties": { - "Node name for S&R": "INTConstant", - "cnr_id": "comfyui-kjnodes", - "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57" - }, - "widgets_values": [ - 6 - ], - "color": "#1b4669", - "bgcolor": "#29699c" - }, - { - "id": 91, - "type": "INTConstant", - "pos": [ - 1473.2628173828125, - 579.54443359375 - ], - "size": [ - 210, - 58 - ], - "flags": {}, - "order": 10, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "value", - "type": "INT", - "links": [ - 187, - 194 - ] - } - ], - "title": "Split_step", - "properties": { - "Node name for S&R": "INTConstant", - "cnr_id": "comfyui-kjnodes", - "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57" - }, - "widgets_values": [ - 3 - ], - "color": "#1b4669", - "bgcolor": "#29699c" + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" }, { "id": 106, "type": "WanVideoSamplerMultiGPU", "pos": [ - 2261.699951171875, - -397.3438415527344 + 2237.71875, + -350.4714660644531 ], "size": [ 327.80859375, - 902 + 922 ], "flags": {}, "order": 27, @@ -1454,10 +1439,15 @@ "type": "WANVIDEOMODEL", "link": 190 }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "link": 197 + }, { "name": "image_embeds", "type": "WANVIDIMAGE_EMBEDS", - "link": 191 + "link": 208 }, { "name": "text_embeds", @@ -1572,7 +1562,7 @@ "name": "samples", "type": "LATENT", "links": [ - 195 + 212 ] }, { @@ -1582,15 +1572,17 @@ } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", "Node name for S&R": "WanVideoSamplerMultiGPU" }, "widgets_values": [ 30, 1.0000000000000002, 8.000000000000002, - 75, - "increment", - false, + 459358799394648, + "randomize", + true, "dpm++_sde", 0, 1, @@ -1600,8 +1592,222 @@ -1, false ], - "color": "#233", - "bgcolor": "#355" + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 35, + "type": "WanVideoTorchCompileSettings", + "pos": [ + -550, + -870 + ], + "size": [ + 390.5999755859375, + 202 + ], + "flags": {}, + "order": 10, + "mode": 4, + "inputs": [], + "outputs": [ + { + "name": "torch_compile_args", + "type": "WANCOMPILEARGS", + "slot_index": 0, + "links": [ + 198, + 213 + ] + } + ], + "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e", + "Node name for S&R": "WanVideoTorchCompileSettings" + }, + "widgets_values": [ + "inductor", + false, + "default", + false, + 64, + true, + 128 + ] + }, + { + "id": 116, + "type": "WanVideoBlockSwapMultiGPU", + "pos": [ + 515.2559814453125, + -589.2463989257812 + ], + "size": [ + 292.8716735839844, + 226 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "block_swap_args", + "type": "BLOCKSWAPARGS", + "links": [ + 214, + 215 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoBlockSwapMultiGPU" + }, + "widgets_values": [ + 40, + true, + true, + false, + 0, + 0, + false, + "cuda:1" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 100, + "type": "WanVideoTextEncodeMultiGPU", + "pos": [ + 800.8797607421875, + 19.50592613220215 + ], + "size": [ + 400, + 234 + ], + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "t5", + "shape": 7, + "type": "WANTEXTENCODER", + "link": 172 + }, + { + "name": "load_device", + "shape": 7, + "type": "MULTIGPUDEVICE", + "link": 199 + }, + { + "name": "model_to_offload", + "shape": 7, + "type": "WANVIDEOMODEL", + "link": null + } + ], + "outputs": [ + { + "name": "text_embeds", + "type": "WANVIDEOTEXTEMBEDS", + "links": [ + 183, + 192 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoTextEncodeMultiGPU" + }, + "widgets_values": [ + "Lines and text on a static Excel graph pulsate and glow", + "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走", + "cpu", + false + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 68, + "type": "ImageResizeKJv2", + "pos": [ + 696.0801391601562, + -1143.5843505859375 + ], + "size": [ + 270, + 336 + ], + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 71 + }, + { + "name": "mask", + "shape": 7, + "type": "MASK", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 204 + ] + }, + { + "name": "width", + "type": "INT", + "links": [ + 205 + ] + }, + { + "name": "height", + "type": "INT", + "links": [ + 206 + ] + }, + { + "name": "mask", + "type": "MASK", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-kjnodes", + "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57", + "Node name for S&R": "ImageResizeKJv2" + }, + "widgets_values": [ + 720, + 720, + "lanczos", + "resize", + "0, 0, 0", + "center", + 32, + "cpu", + "Output: 1 x 704 x 512 | 4.12MB" + ] }, { "id": 67, @@ -1615,7 +1821,7 @@ 314 ], "flags": {}, - "order": 11, + "order": 12, "mode": 0, "inputs": [], "outputs": [ @@ -1633,132 +1839,14 @@ } ], "properties": { - "Node name for S&R": "LoadImage", "cnr_id": "comfy-core", - "ver": "0.3.44" + "ver": "0.3.44", + "Node name for S&R": "LoadImage" }, "widgets_values": [ - "Google_AI_Studio_2025-08-05T15_03_28.397Z.png", + "wan2_2_benchmark_v2.png", "image" ] - }, - { - "id": 35, - "type": "WanVideoTorchCompileSettings", - "pos": [ - -550, - -870 - ], - "size": [ - 390.5999755859375, - 202 - ], - "flags": {}, - "order": 12, - "mode": 4, - "inputs": [], - "outputs": [ - { - "name": "torch_compile_args", - "type": "WANCOMPILEARGS", - "slot_index": 0, - "links": [ - 170, - 175 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoTorchCompileSettings", - "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" - }, - "widgets_values": [ - "inductor", - false, - "default", - false, - 64, - true, - 128 - ] - }, - { - "id": 60, - "type": "VHS_VideoCombine", - "pos": [ - 3150, - -390 - ], - "size": [ - 698.6392211914062, - 1026.63916015625 - ], - "flags": {}, - "order": 30, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 77 - }, - { - "name": "audio", - "shape": 7, - "type": "AUDIO", - "link": null - }, - { - "name": "meta_batch", - "shape": 7, - "type": "VHS_BatchManager", - "link": null - }, - { - "name": "vae", - "shape": 7, - "type": "VAE", - "link": null - } - ], - "outputs": [ - { - "name": "Filenames", - "type": "VHS_FILENAMES", - "links": null - } - ], - "properties": { - "Node name for S&R": "VHS_VideoCombine", - "cnr_id": "comfyui-videohelpersuite", - "ver": "0a75c7958fe320efcb052f1d9f8451fd20c730a8" - }, - "widgets_values": { - "frame_rate": 16, - "loop_count": 0, - "filename_prefix": "WanVideo2_2_I2V", - "format": "video/h264-mp4", - "pix_fmt": "yuv420p", - "crf": 19, - "save_metadata": true, - "trim_to_audio": false, - "pingpong": false, - "save_output": true, - "videopreview": { - "hidden": false, - "paused": false, - "params": { - "filename": "WanVideo2_2_I2V_00002.mp4", - "subfolder": "", - "type": "output", - "format": "video/h264-mp4", - "frame_rate": 16, - "workflow": "WanVideo2_2_I2V_00002.png", - "fullpath": "/home/johnj/ComfyUI/output/WanVideo2_2_I2V_00002.mp4" - } - } - } } ], "links": [ @@ -1802,14 +1890,6 @@ 0, "IMAGE" ], - [ - 76, - 28, - 0, - 69, - 0, - "IMAGE" - ], [ 77, 69, @@ -1826,30 +1906,6 @@ 1, "WANVIDLORA" ], - [ - 139, - 68, - 0, - 89, - 2, - "IMAGE" - ], - [ - 141, - 68, - 1, - 89, - 8, - "INT" - ], - [ - 142, - 68, - 2, - 89, - 9, - "INT" - ], [ 157, 92, @@ -1882,22 +1938,6 @@ 1, "WANVIDLORA" ], - [ - 170, - 35, - 0, - 98, - 0, - "WANCOMPILEARGS" - ], - [ - 171, - 98, - 0, - 92, - 0, - "WANVIDEOMODEL" - ], [ 172, 99, @@ -1906,76 +1946,12 @@ 0, "WANTEXTENCODER" ], - [ - 175, - 35, - 0, - 101, - 0, - "WANCOMPILEARGS" - ], - [ - 176, - 101, - 0, - 93, - 0, - "WANVIDEOMODEL" - ], - [ - 177, - 103, - 0, - 92, - 1, - "BLOCKSWAPARGS" - ], - [ - 178, - 103, - 0, - 93, - 1, - "BLOCKSWAPARGS" - ], - [ - 179, - 104, - 0, - 28, - 0, - "WANVAE" - ], - [ - 180, - 104, - 0, - 89, - 0, - "WANVAE" - ], - [ - 181, - 89, - 0, - 105, - 1, - "WANVIDIMAGE_EMBEDS" - ], - [ - 182, - 80, - 0, - 105, - 0, - "WANVIDEOMODEL" - ], [ 183, 100, 0, 105, - 2, + 3, "WANVIDEOTEXTEMBEDS" ], [ @@ -1983,7 +1959,7 @@ 94, 0, 105, - 17, + 18, "INT" ], [ @@ -1991,7 +1967,7 @@ 95, 0, 105, - 18, + 19, "FLOAT" ], [ @@ -1999,7 +1975,7 @@ 91, 0, 105, - 19, + 20, "INT" ], [ @@ -2007,7 +1983,7 @@ 105, 0, 106, - 3, + 4, "LATENT" ], [ @@ -2018,20 +1994,12 @@ 0, "WANVIDEOMODEL" ], - [ - 191, - 89, - 0, - 106, - 1, - "WANVIDIMAGE_EMBEDS" - ], [ 192, 100, 0, 106, - 2, + 3, "WANVIDEOTEXTEMBEDS" ], [ @@ -2039,7 +2007,7 @@ 94, 0, 106, - 17, + 18, "INT" ], [ @@ -2047,16 +2015,168 @@ 91, 0, 106, - 18, + 19, "INT" ], [ - 195, + 196, + 108, + 0, + 93, + 0, + "WANVIDEOMODEL" + ], + [ + 197, + 108, + 1, + 106, + 1, + "MULTIGPUDEVICE" + ], + [ + 198, + 35, + 0, + 108, + 0, + "WANCOMPILEARGS" + ], + [ + 199, + 99, + 1, + 100, + 1, + "MULTIGPUDEVICE" + ], + [ + 200, + 109, + 0, + 105, + 0, + "WANVIDEOMODEL" + ], + [ + 201, + 109, + 1, + 105, + 1, + "MULTIGPUDEVICE" + ], + [ + 202, + 114, + 0, + 112, + 0, + "WANVAE" + ], + [ + 203, + 114, + 1, + 112, + 1, + "MULTIGPUDEVICE" + ], + [ + 204, + 68, + 0, + 112, + 3, + "IMAGE" + ], + [ + 205, + 68, + 1, + 112, + 9, + "INT" + ], + [ + 206, + 68, + 2, + 112, + 10, + "INT" + ], + [ + 207, + 112, + 0, + 105, + 2, + "WANVIDIMAGE_EMBEDS" + ], + [ + 208, + 112, + 0, + 106, + 2, + "WANVIDIMAGE_EMBEDS" + ], + [ + 209, + 114, + 0, + 115, + 0, + "WANVAE" + ], + [ + 210, + 114, + 1, + 115, + 1, + "MULTIGPUDEVICE" + ], + [ + 211, + 115, + 0, + 69, + 0, + "IMAGE" + ], + [ + 212, 106, 0, - 28, - 1, + 115, + 2, "LATENT" + ], + [ + 213, + 35, + 0, + 109, + 0, + "WANCOMPILEARGS" + ], + [ + 214, + 116, + 0, + 92, + 1, + "BLOCKSWAPARGS" + ], + [ + 215, + 116, + 0, + 93, + 1, + "BLOCKSWAPARGS" ] ], "groups": [ @@ -2077,13 +2197,13 @@ "config": {}, "extra": { "ds": { - "scale": 0.672749994932603, + "scale": 0.505447028499326, "offset": [ - 248.26374563477046, - 1429.5189648926369 + 329.46053758144194, + 1458.1424592955568 ] }, - "frontendVersion": "1.23.4", + "frontendVersion": "1.27.10", "node_versions": { "ComfyUI-WanVideoWrapper": "5a2383621a05825d0d0437781afcb8552d9590fd", "comfy-core": "0.3.26", diff --git a/examples/wannvideowrapper/wanvideo_1_3B_control_lora_example_01_MultiGPU.json b/examples/wannvideowrapper/wanvideo_1_3B_control_lora_example_01_MultiGPU.json index 9bf08bf..32ebe53 100755 --- a/examples/wannvideowrapper/wanvideo_1_3B_control_lora_example_01_MultiGPU.json +++ b/examples/wannvideowrapper/wanvideo_1_3B_control_lora_example_01_MultiGPU.json @@ -1,8 +1,8 @@ { "id": "04f1de76-e363-4c27-bec7-eb184ac6e476", "revision": 0, - "last_node_id": 111, - "last_link_id": 161, + "last_node_id": 114, + "last_link_id": 175, "nodes": [ { "id": 36, @@ -51,6 +51,8 @@ } ], "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", "Node name for S&R": "WanVideoTorchCompileSettings" }, "widgets_values": [ @@ -111,56 +113,6 @@ "color": "#432", "bgcolor": "#653" }, - { - "id": 28, - "type": "WanVideoDecode", - "pos": [ - 1704.86572265625, - -604.0441284179688 - ], - "size": [ - 315, - 198 - ], - "flags": {}, - "order": 17, - "mode": 0, - "inputs": [ - { - "name": "vae", - "type": "WANVAE", - "link": 159 - }, - { - "name": "samples", - "type": "LATENT", - "link": 154 - } - ], - "outputs": [ - { - "name": "images", - "type": "IMAGE", - "slot_index": 0, - "links": [ - 145 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoDecode" - }, - "widgets_values": [ - false, - 272, - 272, - 144, - 128, - "default" - ], - "color": "#322", - "bgcolor": "#533" - }, { "id": 30, "type": "VHS_VideoCombine", @@ -208,6 +160,8 @@ } ], "properties": { + "cnr_id": "comfyui-videohelpersuite", + "ver": "08e8df15db24da292d4b7f943c460dc2ab442b24", "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { @@ -225,239 +179,17 @@ "hidden": false, "paused": false, "params": { - "filename": "WanVideoWrapper_I2V_00004.mp4", + "filename": "WanVideoWrapper_I2V_00002.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4", "frame_rate": 16, - "workflow": "WanVideoWrapper_I2V_00004.png", - "fullpath": "/home/johnj/ComfyUI/output/WanVideoWrapper_I2V_00004.mp4" + "workflow": "WanVideoWrapper_I2V_00002.png", + "fullpath": "/home/johnj/ComfyUI/output/WanVideoWrapper_I2V_00002.mp4" } } } }, - { - "id": 97, - "type": "VHS_LoadVideo", - "pos": [ - -257.8260498046875, - 26.503103256225586 - ], - "size": [ - 247.455078125, - 551.455078125 - ], - "flags": {}, - "order": 4, - "mode": 0, - "inputs": [ - { - "name": "meta_batch", - "shape": 7, - "type": "VHS_BatchManager", - "link": null - }, - { - "name": "vae", - "shape": 7, - "type": "VAE", - "link": null - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "slot_index": 0, - "links": [ - 147 - ] - }, - { - "name": "frame_count", - "type": "INT", - "links": null - }, - { - "name": "audio", - "type": "AUDIO", - "links": null - }, - { - "name": "video_info", - "type": "VHS_VIDEOINFO", - "links": null - } - ], - "properties": { - "Node name for S&R": "VHS_LoadVideo" - }, - "widgets_values": { - "video": "wolf_interpolated.mp4", - "force_rate": 0, - "custom_width": 0, - "custom_height": 0, - "frame_load_cap": 0, - "skip_first_frames": 0, - "select_every_nth": 1, - "format": "AnimateDiff", - "choose video to upload": "image", - "videopreview": { - "hidden": false, - "paused": false, - "params": { - "filename": "wolf_interpolated.mp4", - "type": "input", - "format": "video/mp4", - "force_rate": 0, - "custom_width": 0, - "custom_height": 0, - "frame_load_cap": 0, - "skip_first_frames": 0, - "select_every_nth": 1 - } - } - } - }, - { - "id": 95, - "type": "WanVideoEncode", - "pos": [ - 280.9074401855469, - -116.20671844482422 - ], - "size": [ - 315, - 242 - ], - "flags": {}, - "order": 14, - "mode": 0, - "inputs": [ - { - "name": "vae", - "type": "WANVAE", - "link": 158 - }, - { - "name": "image", - "type": "IMAGE", - "link": 148 - }, - { - "name": "mask", - "shape": 7, - "type": "MASK", - "link": null - } - ], - "outputs": [ - { - "name": "samples", - "type": "LATENT", - "links": [ - 132 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoEncode" - }, - "widgets_values": [ - false, - 272, - 272, - 144, - 128, - 0, - 1.0000000000000002 - ] - }, - { - "id": 104, - "type": "ImageBlur", - "pos": [ - 89.50601959228516, - 217.970947265625 - ], - "size": [ - 315, - 82 - ], - "flags": {}, - "order": 11, - "mode": 0, - "inputs": [ - { - "name": "image", - "type": "IMAGE", - "link": 147 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "slot_index": 0, - "links": [ - 148, - 149 - ] - } - ], - "properties": { - "Node name for S&R": "ImageBlur" - }, - "widgets_values": [ - 4, - 1 - ] - }, - { - "id": 103, - "type": "ImageConcatMulti", - "pos": [ - 2055.855224609375, - -543.0326538085938 - ], - "size": [ - 315, - 150 - ], - "flags": {}, - "order": 18, - "mode": 0, - "inputs": [ - { - "name": "image_1", - "type": "IMAGE", - "link": 149 - }, - { - "name": "image_2", - "shape": 7, - "type": "IMAGE", - "link": 145 - } - ], - "outputs": [ - { - "name": "images", - "type": "IMAGE", - "slot_index": 0, - "links": [ - 146 - ] - } - ], - "properties": {}, - "widgets_values": [ - 2, - "right", - false, - null - ] - }, { "id": 33, "type": "Note", @@ -470,7 +202,7 @@ 88 ], "flags": {}, - "order": 5, + "order": 4, "mode": 0, "inputs": [], "outputs": [], @@ -499,7 +231,7 @@ { "name": "latents", "type": "LATENT", - "link": 132 + "link": 164 }, { "name": "fun_ref_image", @@ -519,6 +251,8 @@ } ], "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", "Node name for S&R": "WanVideoControlEmbeds" }, "widgets_values": [ @@ -526,159 +260,6 @@ 0.7 ] }, - { - "id": 52, - "type": "WanVideoTeaCache", - "pos": [ - 1334.42626953125, - -588.2476196289062 - ], - "size": [ - 315, - 178 - ], - "flags": {}, - "order": 6, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "cache_args", - "type": "CACHEARGS", - "links": [ - 153 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoTeaCache" - }, - "widgets_values": [ - 0.1, - 1, - -1, - "offload_device", - "true", - "e" - ] - }, - { - "id": 64, - "type": "WanVideoTorchCompileSettings", - "pos": [ - -276.8500671386719, - -1050.6326904296875 - ], - "size": [ - 390.5999755859375, - 202 - ], - "flags": {}, - "order": 7, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "torch_compile_args", - "type": "WANCOMPILEARGS", - "slot_index": 0, - "links": [ - 157 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoTorchCompileSettings" - }, - "widgets_values": [ - "inductor", - false, - "default", - false, - 64, - true, - 128 - ] - }, - { - "id": 109, - "type": "WanVideoVAELoaderMultiGPU", - "pos": [ - -246.82862854003906, - -338.9017333984375 - ], - "size": [ - 368.710693359375, - 106 - ], - "flags": {}, - "order": 8, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "shape": 7, - "type": "WANCOMPILEARGS", - "link": null - } - ], - "outputs": [ - { - "name": "vae", - "type": "WANVAE", - "links": [ - 158, - 159 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoVAELoaderMultiGPU" - }, - "widgets_values": [ - "wan_2.1_vae.safetensors", - "cuda:0", - "bf16" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 111, - "type": "LoadWanVideoT5TextEncoderMultiGPU", - "pos": [ - 334.64263916015625, - -356.91937255859375 - ], - "size": [ - 348.9195251464844, - 130 - ], - "flags": {}, - "order": 9, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "wan_t5_model", - "type": "WANTEXTENCODER", - "links": [ - 161 - ] - } - ], - "properties": { - "Node name for S&R": "LoadWanVideoT5TextEncoderMultiGPU" - }, - "widgets_values": [ - "umt5-xxl-enc-bf16.safetensors", - "bf16", - "cuda:0", - "disabled" - ], - "color": "#233", - "bgcolor": "#355" - }, { "id": 110, "type": "WanVideoTextEncodeMultiGPU", @@ -691,7 +272,7 @@ 234 ], "flags": {}, - "order": 12, + "order": 11, "mode": 0, "inputs": [ { @@ -700,6 +281,12 @@ "type": "WANTEXTENCODER", "link": 161 }, + { + "name": "load_device", + "shape": 7, + "type": "MULTIGPUDEVICE", + "link": 166 + }, { "name": "model_to_offload", "shape": 7, @@ -717,18 +304,153 @@ } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", "Node name for S&R": "WanVideoTextEncodeMultiGPU" }, "widgets_values": [ "video of a wolf", "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走", "cuda:0", - false, false ], "color": "#233", "bgcolor": "#355" }, + { + "id": 111, + "type": "LoadWanVideoT5TextEncoderMultiGPU", + "pos": [ + 278.705810546875, + -281.6587829589844 + ], + "size": [ + 348.9195251464844, + 150 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "wan_t5_model", + "type": "WANTEXTENCODER", + "links": [ + 161 + ] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": [ + 166 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", + "Node name for S&R": "LoadWanVideoT5TextEncoderMultiGPU" + }, + "widgets_values": [ + "umt5-xxl-enc-bf16.safetensors", + "bf16", + "cuda:0", + "disabled" + ], + "color": "#233", + "bgcolor": "#355" + }, + { + "id": 64, + "type": "WanVideoTorchCompileSettings", + "pos": [ + -276.8500671386719, + -1050.6326904296875 + ], + "size": [ + 390.5999755859375, + 202 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "torch_compile_args", + "type": "WANCOMPILEARGS", + "slot_index": 0, + "links": [ + 168 + ] + } + ], + "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", + "Node name for S&R": "WanVideoTorchCompileSettings" + }, + "widgets_values": [ + "inductor", + false, + "default", + false, + 64, + true, + 128 + ] + }, + { + "id": 98, + "type": "WanVideoLoraSelect", + "pos": [ + -161.7538604736328, + -680.8223876953125 + ], + "size": [ + 315, + 150 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "prev_lora", + "shape": 7, + "type": "WANVIDLORA", + "link": null + }, + { + "name": "blocks", + "shape": 7, + "type": "SELECTEDBLOCKS", + "link": null + } + ], + "outputs": [ + { + "name": "lora", + "type": "WANVIDLORA", + "links": [ + 169 + ] + } + ], + "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", + "Node name for S&R": "WanVideoLoraSelect" + }, + "widgets_values": [ + "wan2.1-1.3b-control-lora-tile-v1.1_comfy.safetensors", + 1, + false, + true + ] + }, { "id": 108, "type": "WanVideoSamplerMultiGPU", @@ -738,7 +460,7 @@ ], "size": [ 327.80859375, - 902 + 922 ], "flags": {}, "order": 16, @@ -747,7 +469,12 @@ { "name": "model", "type": "WANVIDEOMODEL", - "link": 155 + "link": 170 + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "link": 171 }, { "name": "image_embeds", @@ -850,7 +577,7 @@ "name": "samples", "type": "LATENT", "links": [ - 154 + 174 ] }, { @@ -860,6 +587,8 @@ } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", "Node name for S&R": "WanVideoSamplerMultiGPU" }, "widgets_values": [ @@ -882,25 +611,410 @@ "bgcolor": "#355" }, { - "id": 107, - "type": "WanVideoModelLoaderMultiGPU", + "id": 109, + "type": "WanVideoVAELoaderMultiGPU", "pos": [ - 295.29010009765625, - -723.5640258789062 + 689.3143310546875, + -934.9156494140625 ], "size": [ - 400.63916015625, - 274 + 368.710693359375, + 126 ], "flags": {}, - "order": 13, + "order": 8, "mode": 0, "inputs": [ { "name": "compile_args", "shape": 7, "type": "WANCOMPILEARGS", - "link": 157 + "link": null + } + ], + "outputs": [ + { + "name": "vae", + "type": "WANVAE", + "links": [ + 162, + 172 + ] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": [ + 163, + 173 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", + "Node name for S&R": "WanVideoVAELoaderMultiGPU" + }, + "widgets_values": [ + "wan_2.1_vae.safetensors", + "cuda:0", + "bf16" + ], + "color": "#233", + "bgcolor": "#355" + }, + { + "id": 97, + "type": "VHS_LoadVideo", + "pos": [ + -854.7728271484375, + -166.42774963378906 + ], + "size": [ + 247.455078125, + 551.455078125 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "slot_index": 0, + "links": [ + 147 + ] + }, + { + "name": "frame_count", + "type": "INT", + "links": null + }, + { + "name": "audio", + "type": "AUDIO", + "links": null + }, + { + "name": "video_info", + "type": "VHS_VIDEOINFO", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-videohelpersuite", + "ver": "08e8df15db24da292d4b7f943c460dc2ab442b24", + "Node name for S&R": "VHS_LoadVideo" + }, + "widgets_values": { + "video": "wolf_interpolated.mp4", + "force_rate": 0, + "custom_width": 0, + "custom_height": 0, + "frame_load_cap": 0, + "skip_first_frames": 0, + "select_every_nth": 1, + "format": "AnimateDiff", + "choose video to upload": "image", + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "wolf_interpolated.mp4", + "type": "input", + "format": "video/mp4", + "force_rate": 0, + "custom_width": 0, + "custom_height": 0, + "frame_load_cap": 0, + "skip_first_frames": 0, + "select_every_nth": 1 + } + } + } + }, + { + "id": 103, + "type": "ImageConcatMulti", + "pos": [ + 1796.8583984375, + -624.5692138671875 + ], + "size": [ + 315, + 150 + ], + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "image_1", + "type": "IMAGE", + "link": 149 + }, + { + "name": "image_2", + "shape": 7, + "type": "IMAGE", + "link": 175 + } + ], + "outputs": [ + { + "name": "images", + "type": "IMAGE", + "slot_index": 0, + "links": [ + 146 + ] + } + ], + "properties": { + "cnr_id": "comfyui-kjnodes", + "ver": "3fcd22f2fe2be69c3229f192362b91888277cbcb" + }, + "widgets_values": [ + 2, + "right", + false, + null + ] + }, + { + "id": 52, + "type": "WanVideoTeaCache", + "pos": [ + 784.0582885742188, + 198.33489990234375 + ], + "size": [ + 315, + 178 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "cache_args", + "type": "CACHEARGS", + "links": [ + 153 + ] + } + ], + "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", + "Node name for S&R": "WanVideoTeaCache" + }, + "widgets_values": [ + 0.1, + 1, + -1, + "offload_device", + "true", + "e" + ] + }, + { + "id": 104, + "type": "ImageBlur", + "pos": [ + -336.56787109375, + -98.77837371826172 + ], + "size": [ + 315, + 82 + ], + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 147 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "slot_index": 0, + "links": [ + 149, + 165 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", + "Node name for S&R": "ImageBlur" + }, + "widgets_values": [ + 4, + 1 + ] + }, + { + "id": 112, + "type": "WanVideoEncodeMultiGPU", + "pos": [ + 169.56695556640625, + 108.46249389648438 + ], + "size": [ + 271.0992126464844, + 262 + ], + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "vae", + "type": "WANVAE", + "link": 162 + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "link": 163 + }, + { + "name": "image", + "type": "IMAGE", + "link": 165 + }, + { + "name": "mask", + "shape": 7, + "type": "MASK", + "link": null + } + ], + "outputs": [ + { + "name": "samples", + "type": "LATENT", + "links": [ + 164 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", + "Node name for S&R": "WanVideoEncodeMultiGPU" + }, + "widgets_values": [ + false, + 272, + 272, + 144, + 128, + 0, + 1 + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 114, + "type": "WanVideoDecodeMultiGPU", + "pos": [ + 1679.09765625, + -978.121826171875 + ], + "size": [ + 271.8716735839844, + 218 + ], + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "vae", + "type": "WANVAE", + "link": 172 + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "link": 173 + }, + { + "name": "samples", + "type": "LATENT", + "link": 174 + } + ], + "outputs": [ + { + "name": "images", + "type": "IMAGE", + "links": [ + 175 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", + "Node name for S&R": "WanVideoDecodeMultiGPU" + }, + "widgets_values": [ + false, + 272, + 272, + 144, + 128, + "default" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 113, + "type": "WanVideoModelLoaderMultiGPU", + "pos": [ + 304.0582275390625, + -755.562255859375 + ], + "size": [ + 325.787109375, + 342 + ], + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "compile_args", + "shape": 7, + "type": "WANCOMPILEARGS", + "link": 168 }, { "name": "block_swap_args", @@ -912,7 +1026,7 @@ "name": "lora", "shape": 7, "type": "WANVIDLORA", - "link": 156 + "link": 169 }, { "name": "vram_management_args", @@ -921,7 +1035,7 @@ "link": null }, { - "name": "vace_model", + "name": "extra_model", "shape": 7, "type": "VACEPATH", "link": null @@ -937,6 +1051,12 @@ "shape": 7, "type": "MULTITALKMODEL", "link": null + }, + { + "name": "fantasyportrait_model", + "shape": 7, + "type": "FANTASYPORTRAITMODEL", + "link": null } ], "outputs": [ @@ -944,88 +1064,36 @@ "name": "model", "type": "WANVIDEOMODEL", "links": [ - 155 + 170 + ] + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "links": [ + 171 ] } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "ba0a3a6e21b0a7f54bd24c51419a8e85b9217c3f", "Node name for S&R": "WanVideoModelLoaderMultiGPU" }, "widgets_values": [ - "wan2.1_t2v_1.3B_bf16.safetensors", + "Wan2_1-T2V-1_3B_bf16.safetensors", "bf16", "disabled", + "offload_device", "cuda:1", - "sdpa" + "sdpa", + "default" ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 98, - "type": "WanVideoLoraSelect", - "pos": [ - -161.7538604736328, - -680.8223876953125 - ], - "size": [ - 315, - 150 - ], - "flags": {}, - "order": 10, - "mode": 0, - "inputs": [ - { - "name": "prev_lora", - "shape": 7, - "type": "WANVIDLORA", - "link": null - }, - { - "name": "blocks", - "shape": 7, - "type": "SELECTEDBLOCKS", - "link": null - } - ], - "outputs": [ - { - "name": "lora", - "type": "WANVIDLORA", - "links": [ - 156 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoLoraSelect" - }, - "widgets_values": [ - "wan2.1-1.3b-control-lora-tile-v1.1_comfy.safetensors", - 1, - false, - true - ] + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" } ], "links": [ - [ - 132, - 95, - 0, - 96, - 0, - "LATENT" - ], - [ - 145, - 28, - 0, - 103, - 1, - "IMAGE" - ], [ 146, 103, @@ -1042,14 +1110,6 @@ 0, "IMAGE" ], - [ - 148, - 104, - 0, - 95, - 1, - "IMAGE" - ], [ 149, 104, @@ -1063,7 +1123,7 @@ 96, 0, 108, - 1, + 2, "WANVIDIMAGE_EMBEDS" ], [ @@ -1071,63 +1131,15 @@ 52, 0, 108, - 6, + 7, "CACHEARGS" ], - [ - 154, - 108, - 0, - 28, - 1, - "LATENT" - ], - [ - 155, - 107, - 0, - 108, - 0, - "WANVIDEOMODEL" - ], - [ - 156, - 98, - 0, - 107, - 2, - "WANVIDLORA" - ], - [ - 157, - 64, - 0, - 107, - 0, - "WANCOMPILEARGS" - ], - [ - 158, - 109, - 0, - 95, - 0, - "WANVAE" - ], - [ - 159, - 109, - 0, - 28, - 0, - "WANVAE" - ], [ 160, 110, 0, 108, - 2, + 3, "WANVIDEOTEXTEMBEDS" ], [ @@ -1137,18 +1149,123 @@ 110, 0, "WANTEXTENCODER" + ], + [ + 162, + 109, + 0, + 112, + 0, + "WANVAE" + ], + [ + 163, + 109, + 1, + 112, + 1, + "MULTIGPUDEVICE" + ], + [ + 164, + 112, + 0, + 96, + 0, + "LATENT" + ], + [ + 165, + 104, + 0, + 112, + 2, + "IMAGE" + ], + [ + 166, + 111, + 1, + 110, + 1, + "MULTIGPUDEVICE" + ], + [ + 168, + 64, + 0, + 113, + 0, + "WANCOMPILEARGS" + ], + [ + 169, + 98, + 0, + 113, + 2, + "WANVIDLORA" + ], + [ + 170, + 113, + 0, + 108, + 0, + "WANVIDEOMODEL" + ], + [ + 171, + 113, + 1, + 108, + 1, + "MULTIGPUDEVICE" + ], + [ + 172, + 109, + 0, + 114, + 0, + "WANVAE" + ], + [ + 173, + 109, + 1, + 114, + 1, + "MULTIGPUDEVICE" + ], + [ + 174, + 108, + 0, + 114, + 2, + "LATENT" + ], + [ + 175, + 114, + 0, + 103, + 1, + "IMAGE" ] ], "groups": [], "config": {}, "extra": { "ds": { - "scale": 0.814027493868451, + "scale": 0.5559917313492605, "offset": [ - 714.8717021081054, - 1253.694882650272 + 1542.2773971567624, + 1325.1448841491515 ] }, + "frontendVersion": "1.27.10", "node_versions": { "ComfyUI-WanVideoWrapper": "5a2383621a05825d0d0437781afcb8552d9590fd", "ComfyUI-VideoHelperSuite": "0a75c7958fe320efcb052f1d9f8451fd20c730a8", @@ -1158,8 +1275,7 @@ "VHS_latentpreview": false, "VHS_latentpreviewrate": 0, "VHS_MetadataImage": true, - "VHS_KeepIntermediate": true, - "frontendVersion": "1.25.11" + "VHS_KeepIntermediate": true }, "version": 0.4 } \ No newline at end of file diff --git a/examples/wannvideowrapper/wanvideo_T2V_example_MultiGPU.json b/examples/wannvideowrapper/wanvideo_T2V_example_MultiGPU.json index adb8ca4..0be682f 100755 --- a/examples/wannvideowrapper/wanvideo_T2V_example_MultiGPU.json +++ b/examples/wannvideowrapper/wanvideo_T2V_example_MultiGPU.json @@ -1,8 +1,8 @@ { "id": "c6e410bc-5e2c-460b-ae81-c91b6094fbb1", "revision": 0, - "last_node_id": 70, - "last_link_id": 80, + "last_node_id": 75, + "last_link_id": 88, "nodes": [ { "id": 50, @@ -16,7 +16,7 @@ 200 ], "flags": {}, - "order": 16, + "order": 12, "mode": 2, "inputs": [ { @@ -36,6 +36,8 @@ } ], "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", "Node name for S&R": "CLIPTextEncode" }, "widgets_values": [ @@ -71,6 +73,8 @@ } ], "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", "Node name for S&R": "CLIPLoader" }, "widgets_values": [ @@ -116,7 +120,7 @@ 200 ], "flags": {}, - "order": 15, + "order": 11, "mode": 2, "inputs": [ { @@ -136,6 +140,8 @@ } ], "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", "Node name for S&R": "CLIPTextEncode" }, "widgets_values": [ @@ -144,111 +150,6 @@ "color": "#432", "bgcolor": "#653" }, - { - "id": 53, - "type": "Note", - "pos": [ - 531.5562133789062, - -1014.3677978515625 - ], - "size": [ - 324.64129638671875, - 159.47401428222656 - ], - "flags": {}, - "order": 2, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "TeaCache could be considered to be sort of an automated step skipper \n\nThe relative l1 threshold -value determines how aggressive this is, higher values are faster but quality suffers more. Very first steps should NEVER be skipped with this model or it kills the motion. When using the pre-calculated coefficients, the treshold value should be much higher than with the default coefficients." - ], - "color": "#432", - "bgcolor": "#653" - }, - { - "id": 54, - "type": "Note", - "pos": [ - 1278.7947998046875, - -1137.541748046875 - ], - "size": [ - 327.61932373046875, - 88 - ], - "flags": {}, - "order": 3, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "Enhance-a-video can increase the fidelity of the results, too high values lead to noisy results." - ], - "color": "#432", - "bgcolor": "#653" - }, - { - "id": 35, - "type": "WanVideoTorchCompileSettings", - "pos": [ - 222.5817413330078, - -677.6240844726562 - ], - "size": [ - 390.5999755859375, - 202 - ], - "flags": {}, - "order": 4, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "torch_compile_args", - "type": "WANCOMPILEARGS", - "slot_index": 0, - "links": [] - } - ], - "properties": { - "Node name for S&R": "WanVideoTorchCompileSettings" - }, - "widgets_values": [ - "inductor", - false, - "default", - false, - 64, - true, - 128 - ] - }, - { - "id": 44, - "type": "Note", - "pos": [ - -98.58364868164062, - -675.3411254882812 - ], - "size": [ - 303.0501403808594, - 88 - ], - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "If you have Triton installed, connect this for ~30% speed increase" - ], - "color": "#432", - "bgcolor": "#653" - }, { "id": 46, "type": "WanVideoTextEmbedBridge", @@ -261,7 +162,7 @@ 46 ], "flags": {}, - "order": 19, + "order": 15, "mode": 2, "inputs": [ { @@ -284,46 +185,12 @@ } ], "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", "Node name for S&R": "WanVideoTextEmbedBridge" }, "widgets_values": [] }, - { - "id": 60, - "type": "LoadWanVideoT5TextEncoderMultiGPU", - "pos": [ - 272.1676025390625, - -32.73524856567383 - ], - "size": [ - 344.07952880859375, - 130 - ], - "flags": {}, - "order": 6, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "wan_t5_model", - "type": "WANTEXTENCODER", - "links": [ - 63 - ] - } - ], - "properties": { - "Node name for S&R": "LoadWanVideoT5TextEncoderMultiGPU" - }, - "widgets_values": [ - "umt5-xxl-enc-bf16.safetensors", - "bf16", - "cpu", - "disabled" - ], - "color": "#233", - "bgcolor": "#355" - }, { "id": 52, "type": "WanVideoTeaCache", @@ -336,7 +203,7 @@ 178 ], "flags": {}, - "order": 7, + "order": 2, "mode": 0, "inputs": [], "outputs": [ @@ -349,6 +216,8 @@ } ], "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", "Node name for S&R": "WanVideoTeaCache" }, "widgets_values": [ @@ -372,7 +241,7 @@ 126 ], "flags": {}, - "order": 8, + "order": 3, "mode": 0, "inputs": [ { @@ -398,6 +267,8 @@ } ], "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", "Node name for S&R": "WanVideoEmptyEmbeds" }, "widgets_values": [ @@ -406,56 +277,6 @@ 81 ] }, - { - "id": 61, - "type": "WanVideoTextEncodeMultiGPU", - "pos": [ - 712.607177734375, - -10.148613929748535 - ], - "size": [ - 400, - 234 - ], - "flags": {}, - "order": 17, - "mode": 0, - "inputs": [ - { - "name": "t5", - "shape": 7, - "type": "WANTEXTENCODER", - "link": 63 - }, - { - "name": "model_to_offload", - "shape": 7, - "type": "WANVIDEOMODEL", - "link": null - } - ], - "outputs": [ - { - "name": "text_embeds", - "type": "WANVIDEOTEXTEMBEDS", - "links": [ - 69 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoTextEncodeMultiGPU" - }, - "widgets_values": [ - "high quality nature video featuring a red panda balancing on a bamboo stem while a bird lands on it's head, on the background there is a waterfall", - "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走", - "cpu", - false, - false - ], - "color": "#233", - "bgcolor": "#355" - }, { "id": 55, "type": "WanVideoEnhanceAVideo", @@ -468,7 +289,7 @@ 106 ], "flags": {}, - "order": 9, + "order": 4, "mode": 0, "inputs": [], "outputs": [ @@ -481,6 +302,8 @@ } ], "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", "Node name for S&R": "WanVideoEnhanceAVideo" }, "widgets_values": [ @@ -490,18 +313,240 @@ ] }, { - "id": 64, - "type": "WanVideoModelLoaderMultiGPU", + "id": 66, + "type": "WanVideoSetBlockSwap", "pos": [ - 327.9969177246094, - -373.866455078125 + 824.3810424804688, + -377.044189453125 ], "size": [ - 351.6673889160156, - 294.30389404296875 + 201.76815795898438, + 46 ], "flags": {}, - "order": 10, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "WANVIDEOMODEL", + "link": 84 + }, + { + "name": "block_swap_args", + "shape": 7, + "type": "BLOCKSWAPARGS", + "link": 72 + } + ], + "outputs": [ + { + "name": "model", + "type": "WANVIDEOMODEL", + "links": [] + } + ], + "properties": { + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f", + "Node name for S&R": "WanVideoSetBlockSwap" + }, + "widgets_values": [], + "color": "#223", + "bgcolor": "#335" + }, + { + "id": 69, + "type": "GetImageSizeAndCount", + "pos": [ + 1655.0137939453125, + -260.44696044921875 + ], + "size": [ + 240.41265869140625, + 86 + ], + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 88 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 78 + ] + }, + { + "label": "width", + "name": "width", + "type": "INT", + "links": null + }, + { + "label": "height", + "name": "height", + "type": "INT", + "links": null + }, + { + "label": "count", + "name": "count", + "type": "INT", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-kjnodes", + "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57", + "Node name for S&R": "GetImageSizeAndCount" + }, + "widgets_values": [] + }, + { + "id": 70, + "type": "VHS_VideoCombine", + "pos": [ + 1994.4910888671875, + -545.2236328125 + ], + "size": [ + 698.6392211914062, + 334 + ], + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 78 + }, + { + "name": "audio", + "shape": 7, + "type": "AUDIO", + "link": null + }, + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-videohelpersuite", + "ver": "0a75c7958fe320efcb052f1d9f8451fd20c730a8", + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 16, + "loop_count": 0, + "filename_prefix": "WanVideo2_1_T2V", + "format": "video/h264-mp4", + "pix_fmt": "yuv420p", + "crf": 19, + "save_metadata": true, + "trim_to_audio": false, + "pingpong": false, + "save_output": true, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "WanVideo2_1_T2V_00020.mp4", + "subfolder": "", + "type": "output", + "format": "video/h264-mp4", + "frame_rate": 16, + "workflow": "WanVideo2_1_T2V_00020.png", + "fullpath": "/home/johnj/ComfyUI/output/WanVideo2_1_T2V_00020.mp4" + } + } + } + }, + { + "id": 62, + "type": "WanVideoVAELoaderMultiGPU", + "pos": [ + 1324.779052734375, + -796.8985595703125 + ], + "size": [ + 294.1841735839844, + 126 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "compile_args", + "shape": 7, + "type": "WANCOMPILEARGS", + "link": null + } + ], + "outputs": [ + { + "name": "vae", + "type": "WANVAE", + "links": [] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "15ff29c63b816a5d4139557730da6b4dae2840cf", + "Node name for S&R": "WanVideoVAELoaderMultiGPU" + }, + "widgets_values": [ + "wan_2.1_vae.safetensors", + "cuda:0", + "bf16" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 72, + "type": "WanVideoModelLoaderMultiGPU", + "pos": [ + 313.96173095703125, + -443.1802978515625 + ], + "size": [ + 325.787109375, + 342 + ], + "flags": {}, + "order": 6, "mode": 0, "inputs": [ { @@ -529,7 +574,7 @@ "link": null }, { - "name": "vace_model", + "name": "extra_model", "shape": 7, "type": "VACEPATH", "link": null @@ -545,6 +590,12 @@ "shape": 7, "type": "MULTITALKMODEL", "link": null + }, + { + "name": "fantasyportrait_model", + "shape": 7, + "type": "FANTASYPORTRAITMODEL", + "link": null } ], "outputs": [ @@ -552,66 +603,227 @@ "name": "model", "type": "WANVIDEOMODEL", "links": [ - 75 + 82, + 84 + ] + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "links": [ + 83 ] } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", "Node name for S&R": "WanVideoModelLoaderMultiGPU" }, "widgets_values": [ "wan2.1_t2v_1.3B_bf16.safetensors", "bf16", "disabled", + "offload_device", "cuda:0", - "sageattn" + "sdpa", + "default" ], - "color": "#233", - "bgcolor": "#355" + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" }, { - "id": 66, - "type": "WanVideoSetBlockSwap", + "id": 60, + "type": "LoadWanVideoT5TextEncoderMultiGPU", "pos": [ - 824.3810424804688, - -377.044189453125 + 272.1676025390625, + -32.73524856567383 ], "size": [ - 201.76815795898438, - 46 + 344.07952880859375, + 150 ], "flags": {}, - "order": 18, + "order": 7, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "wan_t5_model", + "type": "WANTEXTENCODER", + "links": [ + 63 + ] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": null + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "15ff29c63b816a5d4139557730da6b4dae2840cf", + "Node name for S&R": "LoadWanVideoT5TextEncoderMultiGPU" + }, + "widgets_values": [ + "umt5-xxl-enc-bf16.safetensors", + "bf16", + "cuda:0", + "disabled" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 61, + "type": "WanVideoTextEncodeMultiGPU", + "pos": [ + 712.607177734375, + -10.148613929748535 + ], + "size": [ + 400, + 234 + ], + "flags": {}, + "order": 13, "mode": 0, "inputs": [ { - "name": "model", - "type": "WANVIDEOMODEL", - "link": 75 + "name": "t5", + "shape": 7, + "type": "WANTEXTENCODER", + "link": 63 }, { - "name": "block_swap_args", + "name": "load_device", "shape": 7, - "type": "BLOCKSWAPARGS", - "link": 72 + "type": "MULTIGPUDEVICE", + "link": null + }, + { + "name": "model_to_offload", + "shape": 7, + "type": "WANVIDEOMODEL", + "link": null } ], "outputs": [ { - "name": "model", - "type": "WANVIDEOMODEL", + "name": "text_embeds", + "type": "WANVIDEOTEXTEMBEDS", "links": [ - 76 + 69 ] } ], "properties": { - "Node name for S&R": "WanVideoSetBlockSwap", - "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "7e290c67bff1f906cdab84523018573f6c9d4d7f" + "cnr_id": "comfyui-multigpu", + "ver": "15ff29c63b816a5d4139557730da6b4dae2840cf", + "Node name for S&R": "WanVideoTextEncodeMultiGPU" }, - "color": "#223", - "bgcolor": "#335" + "widgets_values": [ + "A towering technological monolith in a cyberpunk cityscape at night, with \"DisTorch 2\" emblazoned across its surface in massive neon blue-green mixed with purple letters that illuminate the surrounding buildings. The text occupies the central third of the frame, crafted from glowing plasma tubes and crackling energy. The model file suffix \"now with .safetensors!\" in same stying but smaller font directly underneath. Rain-slicked streets below reflect the brilliant signage, while holographic advertisements and flying vehicles populate the background. Moody atmospheric lighting, heavy contrast, photorealistic textures, cinematic color grading. ", + "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走", + "cpu", + false + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 65, + "type": "WanVideoBlockSwapMultiGPU", + "pos": [ + -41.53060531616211, + -427.4794616699219 + ], + "size": [ + 292.8716735839844, + 226 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "block_swap_args", + "type": "BLOCKSWAPARGS", + "links": [ + 72 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "15ff29c63b816a5d4139557730da6b4dae2840cf", + "Node name for S&R": "WanVideoBlockSwapMultiGPU" + }, + "widgets_values": [ + 5, + "cuda:2", + "cpu", + false, + false, + false, + 0, + "cuda:1" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" + }, + { + "id": 74, + "type": "WanVideoVAELoaderMultiGPU", + "pos": [ + 1695.74169921875, + -860.8320922851562 + ], + "size": [ + 294.1841735839844, + 126 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "compile_args", + "shape": 7, + "type": "WANCOMPILEARGS", + "link": null + } + ], + "outputs": [ + { + "name": "vae", + "type": "WANVAE", + "links": [ + 85 + ] + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "links": [ + 86 + ] + } + ], + "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoVAELoaderMultiGPU" + }, + "widgets_values": [ + "wan_2.1_vae.safetensors", + "cuda:0", + "bf16" + ], + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" }, { "id": 63, @@ -622,16 +834,21 @@ ], "size": [ 327.80859375, - 902 + 922 ], "flags": {}, - "order": 20, + "order": 16, "mode": 0, "inputs": [ { "name": "model", "type": "WANVIDEOMODEL", - "link": 76 + "link": 82 + }, + { + "name": "compute_device", + "type": "MULTIGPUDEVICE", + "link": 83 }, { "name": "image_embeds", @@ -734,7 +951,7 @@ "name": "samples", "type": "LATENT", "links": [ - 80 + 87 ] }, { @@ -744,13 +961,15 @@ } ], "properties": { + "cnr_id": "comfyui-multigpu", + "ver": "15ff29c63b816a5d4139557730da6b4dae2840cf", "Node name for S&R": "WanVideoSamplerMultiGPU" }, "widgets_values": [ 25, 6, 5, - 43, + 47, "increment", false, "unipc", @@ -762,176 +981,53 @@ -1, false ], - "color": "#233", - "bgcolor": "#355" + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" }, { - "id": 65, - "type": "WanVideoBlockSwapMultiGPU", + "id": 75, + "type": "WanVideoDecodeMultiGPU", "pos": [ - -41.53060531616211, - -427.4794616699219 + 1667.2755126953125, + -590.3536376953125 ], "size": [ - 292.8716735839844, - 202 + 271.8716735839844, + 218 ], "flags": {}, - "order": 11, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "block_swap_args", - "type": "BLOCKSWAPARGS", - "links": [ - 72 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoBlockSwapMultiGPU" - }, - "widgets_values": [ - 5, - "cuda:2", - "cpu", - false, - false, - false, - 0 - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 42, - "type": "Note", - "pos": [ - -391.3414001464844, - -423.716064453125 - ], - "size": [ - 314.96246337890625, - 152.77333068847656 - ], - "flags": {}, - "order": 12, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "Adjust the blocks to swap based on your VRAM, this is a tradeoff between speed and memory usage.\n" - ], - "color": "#432", - "bgcolor": "#653" - }, - { - "id": 36, - "type": "Note", - "pos": [ - 723.7317504882812, - -597.3093872070312 - ], - "size": [ - 374.3061828613281, - 171.9547576904297 - ], - "flags": {}, - "order": 13, - "mode": 0, - "inputs": [], - "outputs": [], - "properties": {}, - "widgets_values": [ - "\nSageattn if you have it installed can be used for almost double inference speed" - ], - "color": "#432", - "bgcolor": "#653" - }, - { - "id": 62, - "type": "WanVideoVAELoaderMultiGPU", - "pos": [ - 1324.779052734375, - -796.8985595703125 - ], - "size": [ - 294.1841735839844, - 106 - ], - "flags": {}, - "order": 14, - "mode": 0, - "inputs": [ - { - "name": "compile_args", - "shape": 7, - "type": "WANCOMPILEARGS", - "link": null - } - ], - "outputs": [ - { - "name": "vae", - "type": "WANVAE", - "links": [ - 79 - ] - } - ], - "properties": { - "Node name for S&R": "WanVideoVAELoaderMultiGPU" - }, - "widgets_values": [ - "wan_2.1_vae.safetensors", - "cuda:0", - "bf16" - ], - "color": "#233", - "bgcolor": "#355" - }, - { - "id": 68, - "type": "WanVideoDecode", - "pos": [ - 1645.92236328125, - -533.2974853515625 - ], - "size": [ - 315, - 198 - ], - "flags": {}, - "order": 21, + "order": 17, "mode": 0, "inputs": [ { "name": "vae", "type": "WANVAE", - "link": 79 + "link": 85 + }, + { + "name": "load_device", + "type": "MULTIGPUDEVICE", + "link": 86 }, { "name": "samples", "type": "LATENT", - "link": 80 + "link": 87 } ], "outputs": [ { "name": "images", "type": "IMAGE", - "slot_index": 0, "links": [ - 77 + 88 ] } ], "properties": { - "Node name for S&R": "WanVideoDecode", - "cnr_id": "ComfyUI-WanVideoWrapper", - "ver": "998a69cc0acbec503001b8b0ce0a5d5404420e1e" + "cnr_id": "comfyui-multigpu", + "ver": "db931d66c31acbcfce26f29babfb552509e586aa", + "Node name for S&R": "WanVideoDecodeMultiGPU" }, "widgets_values": [ false, @@ -941,139 +1037,46 @@ 128, "default" ], - "color": "#322", - "bgcolor": "#533" + "color": "#008181", + "bgcolor": "rgba(24,24,27,.9)" }, { - "id": 69, - "type": "GetImageSizeAndCount", + "id": 35, + "type": "WanVideoTorchCompileSettings", "pos": [ - 1655.0137939453125, - -260.44696044921875 + 352.3750915527344, + -817.9258422851562 ], "size": [ - 240.41265869140625, - 86 + 390.5999755859375, + 202 ], "flags": {}, - "order": 22, + "order": 10, "mode": 0, - "inputs": [ - { - "name": "image", - "type": "IMAGE", - "link": 77 - } - ], + "inputs": [], "outputs": [ { - "name": "image", - "type": "IMAGE", - "links": [ - 78 - ] - }, - { - "label": "832 width", - "name": "width", - "type": "INT", - "links": null - }, - { - "label": "480 height", - "name": "height", - "type": "INT", - "links": null - }, - { - "label": "81 count", - "name": "count", - "type": "INT", - "links": null + "name": "torch_compile_args", + "type": "WANCOMPILEARGS", + "slot_index": 0, + "links": [] } ], "properties": { - "Node name for S&R": "GetImageSizeAndCount", - "cnr_id": "comfyui-kjnodes", - "ver": "a6b867b63a29ca48ddb15c589e17a9f2d8530d57" - } - }, - { - "id": 70, - "type": "VHS_VideoCombine", - "pos": [ - 1994.4910888671875, - -545.2236328125 - ], - "size": [ - 698.6392211914062, - 739.5226440429688 - ], - "flags": {}, - "order": 23, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 78 - }, - { - "name": "audio", - "shape": 7, - "type": "AUDIO", - "link": null - }, - { - "name": "meta_batch", - "shape": 7, - "type": "VHS_BatchManager", - "link": null - }, - { - "name": "vae", - "shape": 7, - "type": "VAE", - "link": null - } - ], - "outputs": [ - { - "name": "Filenames", - "type": "VHS_FILENAMES", - "links": null - } - ], - "properties": { - "Node name for S&R": "VHS_VideoCombine", - "cnr_id": "comfyui-videohelpersuite", - "ver": "0a75c7958fe320efcb052f1d9f8451fd20c730a8" + "cnr_id": "ComfyUI-WanVideoWrapper", + "ver": "76ea2aa6a664edbd553799f78d6bafdd23e34dc6", + "Node name for S&R": "WanVideoTorchCompileSettings" }, - "widgets_values": { - "frame_rate": 16, - "loop_count": 0, - "filename_prefix": "WanVideo2_1_T2V", - "format": "video/h264-mp4", - "pix_fmt": "yuv420p", - "crf": 19, - "save_metadata": true, - "trim_to_audio": false, - "pingpong": false, - "save_output": true, - "videopreview": { - "hidden": false, - "paused": false, - "params": { - "filename": "WanVideo2_1_T2V_00020.mp4", - "subfolder": "", - "type": "output", - "format": "video/h264-mp4", - "frame_rate": 16, - "workflow": "WanVideo2_1_T2V_00020.png", - "fullpath": "/home/johnj/ComfyUI/output/WanVideo2_1_T2V_00020.mp4" - } - } - } + "widgets_values": [ + "inductor", + false, + "default", + false, + 64, + true, + 128 + ] } ], "links": [ @@ -1122,7 +1125,7 @@ 52, 0, 63, - 6, + 7, "CACHEARGS" ], [ @@ -1130,7 +1133,7 @@ 37, 0, 63, - 1, + 2, "WANVIDIMAGE_EMBEDS" ], [ @@ -1138,7 +1141,7 @@ 61, 0, 63, - 2, + 3, "WANVIDEOTEXTEMBEDS" ], [ @@ -1146,7 +1149,7 @@ 55, 0, 63, - 4, + 5, "FETAARGS" ], [ @@ -1157,30 +1160,6 @@ 1, "BLOCKSWAPARGS" ], - [ - 75, - 64, - 0, - 66, - 0, - "WANVIDEOMODEL" - ], - [ - 76, - 66, - 0, - 63, - 0, - "WANVIDEOMODEL" - ], - [ - 77, - 68, - 0, - 69, - 0, - "IMAGE" - ], [ 78, 69, @@ -1190,20 +1169,60 @@ "IMAGE" ], [ - 79, - 62, + 82, + 72, 0, - 68, + 63, + 0, + "WANVIDEOMODEL" + ], + [ + 83, + 72, + 1, + 63, + 1, + "MULTIGPUDEVICE" + ], + [ + 84, + 72, + 0, + 66, + 0, + "WANVIDEOMODEL" + ], + [ + 85, + 74, + 0, + 75, 0, "WANVAE" ], [ - 80, + 86, + 74, + 1, + 75, + 1, + "MULTIGPUDEVICE" + ], + [ + 87, 63, 0, - 68, - 1, + 75, + 2, "LATENT" + ], + [ + 88, + 75, + 0, + 69, + 0, + "IMAGE" ] ], "groups": [ @@ -1224,12 +1243,13 @@ "config": {}, "extra": { "ds": { - "scale": 0.683013455365071, + "scale": 0.620921323059155, "offset": [ - 229.36183362138223, - 1440.040160715665 + 949.2262021137265, + 1175.3792206745793 ] }, + "frontendVersion": "1.27.10", "node_versions": { "ComfyUI-WanVideoWrapper": "5a2383621a05825d0d0437781afcb8552d9590fd", "comfy-core": "0.3.26", @@ -1238,8 +1258,7 @@ "VHS_latentpreview": false, "VHS_latentpreviewrate": 0, "VHS_MetadataImage": true, - "VHS_KeepIntermediate": true, - "frontendVersion": "1.23.4" + "VHS_KeepIntermediate": true }, "version": 0.4 } \ No newline at end of file diff --git a/nodes.py b/nodes.py index a5ad9b4..822d059 100644 --- a/nodes.py +++ b/nodes.py @@ -5,60 +5,6 @@ from nodes import NODE_CLASS_MAPPINGS from .device_utils import get_device_list from .model_management_mgpu import force_full_system_cleanup -class DeviceSelectorMultiGPU: - @classmethod - def INPUT_TYPES(s): - devices = get_device_list() - return { - "required": { - "device": (devices, {"default": devices[1] if len(devices) > 1 else devices[0]}) - } - } - - RETURN_TYPES = (get_device_list(),) - RETURN_NAMES = ("device",) - FUNCTION = "select_device" - CATEGORY = "multigpu" - - def select_device(self, device): - """Select target device from available device list.""" - return (device,) - - -class HunyuanVideoEmbeddingsAdapter: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "hyvid_embeds": ("HYVIDEMBEDS",), - } - } - - RETURN_TYPES = ("CONDITIONING",) - FUNCTION = "adapt_embeddings" - CATEGORY = "multigpu" - - def adapt_embeddings(self, hyvid_embeds): - """Adapt HunyuanVideo embeddings to standard ComfyUI conditioning format.""" - cond = hyvid_embeds["prompt_embeds"] - - pooled_dict = { - "pooled_output": hyvid_embeds["prompt_embeds_2"], - "cross_attn": hyvid_embeds["prompt_embeds"], - "attention_mask": hyvid_embeds["attention_mask"], - } - - if hyvid_embeds["attention_mask_2"] is not None: - pooled_dict["attention_mask_controlnet"] = hyvid_embeds["attention_mask_2"] - - if hyvid_embeds["cfg"] is not None: - pooled_dict["guidance"] = float(hyvid_embeds["cfg"]) - pooled_dict["start_percent"] = float(hyvid_embeds["start_percent"]) if hyvid_embeds["start_percent"] is not None else 0.0 - pooled_dict["end_percent"] = float(hyvid_embeds["end_percent"]) if hyvid_embeds["end_percent"] is not None else 1.0 - - return ([[cond, pooled_dict]],) - - class UnetLoaderGGUF: @classmethod def INPUT_TYPES(s): @@ -465,97 +411,6 @@ class PulidEvaClipLoader: original_loader = NODE_CLASS_MAPPINGS["PulidEvaClipLoader"]() return original_loader.load_eva_clip() - -class HyVideoModelLoader: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": (folder_paths.get_filename_list("diffusion_models"), {"tooltip": "These models are loaded from the 'ComfyUI/models/diffusion_models' -folder",}), - "base_precision": (["fp32", "bf16"], {"default": "bf16"}), - "quantization": (['disabled', 'fp8_e4m3fn', 'fp8_e4m3fn_fast', 'fp8_scaled', 'torchao_fp8dq', "torchao_fp8dqrow", "torchao_int8dq", "torchao_fp6", "torchao_int4", "torchao_int8"], {"default": 'disabled', "tooltip": "optional quantization method"}), - "load_device": (["main_device"], {"default": "main_device"}), - }, - "optional": { - "attention_mode": ([ - "sdpa", - "flash_attn_varlen", - "sageattn_varlen", - "comfy", - ], {"default": "flash_attn"}), - "compile_args": ("COMPILEARGS", ), - "block_swap_args": ("BLOCKSWAPARGS", ), - "lora": ("HYVIDLORA", {"default": None}), - "auto_cpu_offload": ("BOOLEAN", {"default": False, "tooltip": "Enable auto offloading for reduced VRAM usage, implementation from DiffSynth-Studio, slightly different from block swapping and uses even less VRAM, but can be slower as you can't define how much VRAM to use"}), - } - } - - RETURN_TYPES = ("HYVIDEOMODEL",) - RETURN_NAMES = ("model", ) - FUNCTION = "loadmodel" - CATEGORY = "HunyuanVideoWrapper" - - def loadmodel(self, model, base_precision, load_device, quantization, compile_args=None, attention_mode="sdpa", block_swap_args=None, lora=None, auto_cpu_offload=False): - """Load HunyuanVideo model with specified precision and quantization.""" - original_loader = NODE_CLASS_MAPPINGS["HyVideoModelLoader"]() - return original_loader.loadmodel(model, base_precision, load_device, quantization, compile_args, attention_mode, block_swap_args, lora, auto_cpu_offload) - -class HyVideoVAELoader: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_name": (folder_paths.get_filename_list("vae"), {"tooltip": "These models are loaded from 'ComfyUI/models/vae'"}), - }, - "optional": { - "precision": (["fp16", "fp32", "bf16"], - {"default": "bf16"} - ), - "compile_args":("COMPILEARGS", ), - } - } - - RETURN_TYPES = ("VAE",) - RETURN_NAMES = ("vae", ) - FUNCTION = "loadmodel" - CATEGORY = "HunyuanVideoWrapper" - DESCRIPTION = "Loads Hunyuan VAE model from 'ComfyUI/models/vae'" - - def loadmodel(self, model_name, precision, compile_args=None): - """Load HunyuanVideo VAE model.""" - original_loader = NODE_CLASS_MAPPINGS["HyVideoVAELoader"]() - return original_loader.loadmodel(model_name, precision, compile_args) - -class DownloadAndLoadHyVideoTextEncoder: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "llm_model": (["Kijai/llava-llama-3-8b-text-encoder-tokenizer","xtuner/llava-llama-3-8b-v1_1-transformers"],), - "clip_model": (["disabled","openai/clip-vit-large-patch14",],), - "precision": (["fp16", "fp32", "bf16"], - {"default": "bf16"} - ), - }, - "optional": { - "apply_final_norm": ("BOOLEAN", {"default": False}), - "hidden_state_skip_layer": ("INT", {"default": 2}), - "quantization": (['disabled', 'bnb_nf4', "fp8_e4m3fn"], {"default": 'disabled'}), - } - } - - RETURN_TYPES = ("HYVIDTEXTENCODER",) - RETURN_NAMES = ("hyvid_text_encoder", ) - FUNCTION = "loadmodel" - CATEGORY = "HunyuanVideoWrapper" - DESCRIPTION = "Loads Hunyuan text_encoder model from 'ComfyUI/models/LLM'" - - def loadmodel(self, llm_model, clip_model, precision, apply_final_norm=False, hidden_state_skip_layer=2, quantization="disabled"): - """Download and load HunyuanVideo text encoder from HuggingFace.""" - original_loader = NODE_CLASS_MAPPINGS["DownloadAndLoadHyVideoTextEncoder"]() - return original_loader.loadmodel(llm_model, clip_model, precision, apply_final_norm, hidden_state_skip_layer, quantization) - - class UNetLoaderLP: """UNet Loader (Low Precision) - sets LoRA precision to False for CPU storage optimization""" @classmethod diff --git a/pyproject.toml b/pyproject.toml index eca2803..fa61829 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-multigpu" description = "Provides a suite of custom nodes to manage multiple GPUs for ComfyUI, including advanced model offloading for both GGUF and Safetensor formats with DisTorch, and bespoke MultiGPU support for WanVideoWrapper and other custom nodes." -version = "2.5.1" +version = "2.5.2" license = {file = "LICENSE"} [project.urls] diff --git a/wanvideo.py b/wanvideo.py index 40a8045..4ebecf9 100644 --- a/wanvideo.py +++ b/wanvideo.py @@ -2,25 +2,53 @@ import logging import torch import sys import inspect +import copy import folder_paths import comfy.model_management as mm -from .device_utils import get_device_list, comfyui_memory_load +from nodes import NODE_CLASS_MAPPINGS +from .device_utils import get_device_list +from .model_management_mgpu import multigpu_memory_log +from comfy.utils import load_torch_file, ProgressBar +import gc +import numpy as np +from accelerate import init_empty_weights +import os +import importlib.util + +logger = logging.getLogger("MultiGPU") + + +scheduler_list = [ + "unipc", "unipc/beta", + "dpm++", "dpm++/beta", + "dpm++_sde", "dpm++_sde/beta", + "euler", "euler/beta", + "deis", + "lcm", "lcm/beta", + "res_multistep", + "flowmatch_causvid", + "flowmatch_distill", + "flowmatch_pusa", + "multitalk", + "sa_ode_stable" +] + +rope_functions = ["default", "comfy", "comfy_chunked"] class WanVideoModelLoader: @classmethod def INPUT_TYPES(s): devices = get_device_list() - + default_device = devices[1] if len(devices) > 1 else devices[0] return { "required": { - "model": (folder_paths.get_filename_list("unet_gguf") + folder_paths.get_filename_list("diffusion_models"), - {"tooltip": "These models are loaded from the 'ComfyUI/models/diffusion_models' folder",}), - "base_precision": (["fp32", "bf16", "fp16", "fp16_fast"], {"default": "bf16"}), - "quantization": ( - ["disabled", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e5m2", "fp8_e4m3fn_fast_no_ffn", "fp8_e4m3fn_scaled", "fp8_e5m2_scaled"], - {"default": "disabled", "tooltip": "optional quantization method"} - ), - "device": (devices, {"default": devices[1] if len(devices) > 1 else devices[0], "tooltip": "Device to load the model to"}), + "model": (folder_paths.get_filename_list("unet_gguf") + folder_paths.get_filename_list("diffusion_models"), {"tooltip": "These models are loaded from the 'ComfyUI/models/diffusion_models' -folder",}), + + "base_precision": (["fp32", "bf16", "fp16", "fp16_fast"], {"default": "bf16"}), + "quantization": (["disabled", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e4m3fn_scaled", "fp8_e4m3fn_scaled_fast", "fp8_e5m2", "fp8_e5m2_fast", "fp8_e5m2_scaled", "fp8_e5m2_scaled_fast"], {"default": "disabled", + "tooltip": "Optional quantization method, 'disabled' acts as autoselect based by weights. Scaled modes only work with matching weights, _fast modes (fp8 matmul) require CUDA compute capability >= 8.9 (NVIDIA 4000 series and up), e4m3fn generally can not be torch.compiled on compute capability < 8.9 (3000 series and under)"}), + "load_device": (["main_device", "offload_device"], {"default": "offload_device", "tooltip": "Initial device to load the model to, NOT recommended with the larger models unless you have 48GB+ VRAM"}), + "compute_device": (devices, {"default": default_device}), }, "optional": { "attention_mode": ([ @@ -29,485 +57,810 @@ class WanVideoModelLoader: "flash_attn_3", "sageattn", "sageattn_3", - "flex_attention", "radial_sage_attention", - ], {"default": "sdpa"}), + ], {"default": "sdpa"}), "compile_args": ("WANCOMPILEARGS", ), "block_swap_args": ("BLOCKSWAPARGS", ), "lora": ("WANVIDLORA", {"default": None}), "vram_management_args": ("VRAM_MANAGEMENTARGS", {"default": None, "tooltip": "Alternative offloading method from DiffSynth-Studio, more aggressive in reducing memory use than block swapping, but can be slower"}), "extra_model": ("VACEPATH", {"default": None, "tooltip": "Extra model to add to the main model, ie. VACE or MTV Crafter"}), - "fantasytalking_model": ("FANTASYTALKMODEL", {"default": None, "tooltip": "FantasyTalking model https://github.com/Fantasy-AMAP"}), + "fantasytalking_model": ("FANTASYTALKINGMODEL", {"default": None, "tooltip": "FantasyTalking model https://github.com/Fantasy-AMAP"}), "multitalk_model": ("MULTITALKMODEL", {"default": None, "tooltip": "Multitalk model"}), "fantasyportrait_model": ("FANTASYPORTRAITMODEL", {"default": None, "tooltip": "FantasyPortrait model"}), + "rms_norm_function": (["default", "pytorch"], {"default": "default", "tooltip": "RMSNorm function to use, 'pytorch' is the new native torch RMSNorm, which is faster (when not using torch.compile mostly) but changes results slightly. 'default' is the original WanRMSNorm"}), } } - RETURN_TYPES = ("WANVIDEOMODEL",) - RETURN_NAMES = ("model", ) + RETURN_TYPES = ("WANVIDEOMODEL", "MULTIGPUDEVICE",) + RETURN_NAMES = ("model", "compute_device",) FUNCTION = "loadmodel" - CATEGORY = "WanVideoWrapper" + CATEGORY = "multigpu/WanVideoWrapper" + + def loadmodel(self, model, base_precision, compute_device, quantization, load_device, **kwargs): + from . import set_current_device - def loadmodel(self, model, base_precision, device, quantization, - compile_args=None, attention_mode="sdpa", block_swap_args=None, lora=None, vram_management_args=None, extra_model=None, fantasytalking_model=None, multitalk_model=None, fantasyportrait_model=None): - logging.debug(f"[MultiGPU] WanVideoModelLoader: User selected device: {device}") - - selected_device = torch.device(device) - - load_device = "offload_device" if device == "cpu" else "main_device" - - from nodes import NODE_CLASS_MAPPINGS original_loader = NODE_CLASS_MAPPINGS["WanVideoModelLoader"]() - loader_module = inspect.getmodule(original_loader) - - if loader_module: - logging.debug(f"[MultiGPU] Patching WanVideo modules to use {selected_device}") - - original_device = getattr(loader_module, 'device', None) - original_offload = getattr(loader_module, 'offload_device', None) - - model_offload_override = getattr(loader_module, '_model_offload_device_override', None) - - setattr(loader_module, 'device', selected_device) - if model_offload_override: - setattr(loader_module, 'offload_device', model_offload_override) - logging.debug(f"[MultiGPU] Using model offload override: {model_offload_override}") - elif device == "cpu": - setattr(loader_module, 'offload_device', selected_device) - - nodes_module_name = loader_module.__name__.replace('.nodes_model_loading', '.nodes') - if nodes_module_name in sys.modules: - nodes_module = sys.modules[nodes_module_name] - setattr(nodes_module, 'device', selected_device) - - nodes_model_offload_override = getattr(nodes_module, '_model_offload_device_override', None) - if nodes_model_offload_override: - setattr(nodes_module, 'offload_device', nodes_model_offload_override) - elif device == "cpu": - setattr(nodes_module, 'offload_device', selected_device) - logging.debug(f"[MultiGPU] Both WanVideo modules patched successfully") - - logging.debug(f"[MultiGPU] Calling original WanVideo loader") - try: - logging.info(comfyui_memory_load(f"pre-model-load:wan-model:{model}")) - except Exception: - pass - result = original_loader.loadmodel(model, base_precision, load_device, quantization, - compile_args, attention_mode, block_swap_args, lora, vram_management_args, extra_model=extra_model, fantasytalking_model=fantasytalking_model, multitalk_model=multitalk_model, fantasyportrait_model=fantasyportrait_model) - try: - logging.info(comfyui_memory_load(f"post-model-load:wan-model:{model}")) - except Exception: - pass - - if result and len(result) > 0 and hasattr(result[0], 'model'): - model_obj = result[0] - if hasattr(model_obj.model, 'diffusion_model'): - transformer = model_obj.model.diffusion_model - - block_swap_override = getattr(loader_module, '_block_swap_device_override', None) - if block_swap_override: - transformer.offload_device = block_swap_override - logging.debug(f"[MultiGPU] Patched WanVideo transformer for block swap to use: {block_swap_override}") - - logging.info(f"[MultiGPU] WanVideo model loaded on {selected_device}") - - return result - else: - logging.error(f"[MultiGPU] Could not patch WanVideo modules, falling back") - return original_loader.loadmodel(model, base_precision, load_device, quantization, - compile_args, attention_mode, block_swap_args, lora, vram_management_args, extra_model=extra_model, fantasytalking_model=fantasytalking_model, multitalk_model=multitalk_model, fantasyportrait_model=fantasyportrait_model) + original_module_device = loader_module.device + set_current_device(compute_device) + compute_device_to_be_patched = mm.get_torch_device() -class WanVideoVAELoader: + loader_module.device = compute_device_to_be_patched + + result = original_loader.loadmodel(model, base_precision, load_device, quantization, **kwargs,) + + patcher = result[0] + + try: + return (patcher, compute_device) + + finally: + loader_module.device = original_module_device + +class WanVideoSampler: @classmethod def INPUT_TYPES(s): - devices = get_device_list() - return { "required": { - "model_name": (folder_paths.get_filename_list("vae"), - {"tooltip": "These models are loaded from 'ComfyUI/models/vae'"}), - "device": (devices, {"default": devices[1] if len(devices) > 1 else devices[0], - "tooltip": "Device to load the VAE to"}), + "model": ("WANVIDEOMODEL",), + "compute_device": ("MULTIGPUDEVICE",), + "image_embeds": ("WANVIDIMAGE_EMBEDS", ), + "steps": ("INT", {"default": 30, "min": 1}), + "cfg": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 30.0, "step": 0.01}), + "shift": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 1000.0, "step": 0.01}), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "force_offload": ("BOOLEAN", {"default": True, "tooltip": "Moves the model to the offload device after sampling"}), + "scheduler": (scheduler_list, {"default": "unipc",}), + "riflex_freq_index": ("INT", {"default": 0, "min": 0, "max": 1000, "step": 1, "tooltip": "Frequency index for RIFLEX, disabled when 0, default 6. Allows for new frames to be generated after without looping"}), }, "optional": { - "precision": (["fp16", "fp32", "bf16"], {"default": "bf16"}), - "compile_args": ("WANCOMPILEARGS", ), + "text_embeds": ("WANVIDEOTEXTEMBEDS", ), + "samples": ("LATENT", {"tooltip": "init Latents to use for video2video process"} ), + "denoise_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + "feta_args": ("FETAARGS", ), + "context_options": ("WANVIDCONTEXT", ), + "cache_args": ("CACHEARGS", ), + "flowedit_args": ("FLOWEDITARGS", ), + "batched_cfg": ("BOOLEAN", {"default": False, "tooltip": "Batch cond and uncond for faster sampling, possibly faster on some hardware, uses more memory"}), + "slg_args": ("SLGARGS", ), + "rope_function": (rope_functions, {"default": "comfy", "tooltip": "Comfy's RoPE implementation doesn't use complex numbers and can thus be compiled, that should be a lot faster when using torch.compile. Chunked version has reduced peak VRAM usage when not using torch.compile"}), + "loop_args": ("LOOPARGS", ), + "experimental_args": ("EXPERIMENTALARGS", ), + "sigmas": ("SIGMAS", ), + "unianimate_poses": ("UNIANIMATE_POSE", ), + "fantasytalking_embeds": ("FANTASYTALKING_EMBEDS", ), + "uni3c_embeds": ("UNI3C_EMBEDS", ), + "multitalk_embeds": ("MULTITALK_EMBEDS", ), + "freeinit_args": ("FREEINITARGS", ), + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1, "tooltip": "Start step for the sampling, 0 means full sampling, otherwise samples only from this step"}), + "end_step": ("INT", {"default": -1, "min": -1, "max": 10000, "step": 1, "tooltip": "End step for the sampling, -1 means full sampling, otherwise samples only until this step"}), + "add_noise_to_samples": ("BOOLEAN", {"default": False, "tooltip": "Add noise to the samples before sampling, needed for video2video sampling when starting from clean video"}), } } + + RETURN_TYPES = ("LATENT", "LATENT",) + RETURN_NAMES = ("samples", "denoised_samples",) + FUNCTION = "process" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "MultiGPU-aware sampler that ensures correct device for each model" + + def process(self, model, compute_device, **kwargs): + from . import set_current_device + + original_sampler = NODE_CLASS_MAPPINGS["WanVideoSampler"]() + sampler_module = inspect.getmodule(original_sampler) - RETURN_TYPES = ("WANVAE",) - RETURN_NAMES = ("vae", ) - FUNCTION = "loadmodel" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "Loads Wan VAE model with explicit device selection" + original_module_device = sampler_module.device + original_module_offload_device = sampler_module.offload_device - def loadmodel(self, model_name, device, precision="bf16", compile_args=None): - logging.debug(f"[MultiGPU] WanVideoVAELoader: User selected device: {device}") - - from nodes import NODE_CLASS_MAPPINGS - original_loader = NODE_CLASS_MAPPINGS["WanVideoVAELoader"]() - - loader_module = inspect.getmodule(original_loader) - - if loader_module: - selected_device = torch.device(device) - logging.debug(f"[MultiGPU] Patching WanVideo VAE modules to use {selected_device}") - - setattr(loader_module, 'offload_device', selected_device) - setattr(loader_module, 'device', selected_device) - - nodes_module_name = loader_module.__name__.replace('.nodes_model_loading', '.nodes') - if nodes_module_name in sys.modules: - nodes_module = sys.modules[nodes_module_name] - setattr(nodes_module, 'device', selected_device) - setattr(nodes_module, 'offload_device', selected_device) - - try: - logging.info(comfyui_memory_load(f"pre-model-load:wan-vae:{model_name}")) - except Exception: - pass - result = original_loader.loadmodel(model_name, precision, compile_args) - try: - logging.info(comfyui_memory_load(f"post-model-load:wan-vae:{model_name}")) - except Exception: - pass - - # Attach device info to VAE object for downstream nodes - if result and len(result) > 0: - result[0].load_device = selected_device - - logging.info(f"[MultiGPU] WanVideo VAE loaded on {selected_device}") - return result - else: - logging.error(f"[MultiGPU] Could not patch WanVideo VAE modules") - return original_loader.loadmodel(model_name, precision, compile_args) + set_current_device(compute_device) + compute_device_to_be_patched = mm.get_torch_device() + sampler_module.device = compute_device_to_be_patched + transformer = model.model.diffusion_model + transformer_options = model.model_options.get("transformer_options", {}) + block_swap_args = transformer_options.get("block_swap_args") -class LoadWanVideoT5TextEncoder: - @classmethod - def INPUT_TYPES(s): - devices = get_device_list() - - return { - "required": { - "model_name": (folder_paths.get_filename_list("text_encoders"), - {"tooltip": "These models are loaded from 'ComfyUI/models/text_encoders'"}), - "precision": (["fp32", "bf16"], {"default": "bf16"}), - "device": (devices, {"default": devices[1] if len(devices) > 1 else devices[0], - "tooltip": "Device to load the text encoder to"}), - }, - "optional": { - "quantization": (['disabled', 'fp8_e4m3fn'], - {"default": 'disabled', "tooltip": "optional quantization method"}), - } - } + multi_gpu_block_swap = block_swap_args is not None and "swap_device" in block_swap_args + offload_device_to_be_patched = None + if multi_gpu_block_swap: + swap_label = block_swap_args.get("swap_device") + logger.info(f"[MultiGPU WanVideoWrapper][WanVideoSamplerMultiGPU] block swap enabled, swap device: {swap_label}") + offload_device_to_be_patched = torch.device(str(swap_label)) + sampler_module.offload_device = offload_device_to_be_patched - RETURN_TYPES = ("WANTEXTENCODER",) - RETURN_NAMES = ("wan_t5_model", ) - FUNCTION = "loadmodel" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "Loads Wan text_encoder model from 'ComfyUI/models/text_encoders'" + if transformer is not None and offload_device_to_be_patched is not None: + transformer.offload_device = offload_device_to_be_patched + transformer.cache_device = offload_device_to_be_patched - def loadmodel(self, model_name, precision, device, quantization="disabled"): - logging.debug(f"[MultiGPU] LoadWanVideoT5TextEncoder: User selected device: {device}") - - selected_device = torch.device(device) - load_device = "offload_device" if device == "cpu" else "main_device" - - from nodes import NODE_CLASS_MAPPINGS - original_loader = NODE_CLASS_MAPPINGS["LoadWanVideoT5TextEncoder"]() - - loader_module = inspect.getmodule(original_loader) - - if loader_module: - logging.debug(f"[MultiGPU] Patching WanVideo T5 modules to use {selected_device}") - - setattr(loader_module, 'device', selected_device) - if device == "cpu": - setattr(loader_module, 'offload_device', selected_device) - - nodes_module_name = loader_module.__name__.replace('.nodes_model_loading', '.nodes') - if nodes_module_name in sys.modules: - nodes_module = sys.modules[nodes_module_name] - setattr(nodes_module, 'device', selected_device) - if device == "cpu": - setattr(nodes_module, 'offload_device', selected_device) - - try: - logging.info(comfyui_memory_load(f"pre-model-load:wan-textenc:{model_name}")) - except Exception: - pass - result = original_loader.loadmodel(model_name, precision, load_device, quantization) - try: - logging.info(comfyui_memory_load(f"post-model-load:wan-textenc:{model_name}")) - except Exception: - pass - - logging.info(f"[MultiGPU] WanVideo T5 Text encoder loaded on {selected_device}") - - return result - else: - logging.error(f"[MultiGPU] Could not patch WanVideo T5 modules, falling back") - return original_loader.loadmodel(model_name, precision, load_device, quantization) + try: + return original_sampler.process(model, **kwargs) + finally: + sampler_module.device = original_module_device + sampler_module.offload_device = original_module_offload_device class WanVideoTextEncode: @classmethod def INPUT_TYPES(s): - devices = get_device_list() - return {"required": { "positive_prompt": ("STRING", {"default": "", "multiline": True} ), "negative_prompt": ("STRING", {"default": "", "multiline": True} ), - "device": (devices, {"default": devices[1] if len(devices) > 1 else devices[0], - "tooltip": "Device to run the text encoding on"}), }, "optional": { "t5": ("WANTEXTENCODER",), + "load_device": ("MULTIGPUDEVICE",), "force_offload": ("BOOLEAN", {"default": True}), "model_to_offload": ("WANVIDEOMODEL", {"tooltip": "Model to move to offload_device before encoding"}), - "use_disk_cache": ("BOOLEAN", {"default": False, "tooltip": "Cache the text embeddings to disk for faster re-use"}), + "use_disk_cache": ("BOOLEAN", {"default": False, "tooltip": "Cache the text embeddings to disk for faster re-use, under the custom_nodes/ComfyUI-WanVideoWrapper/text_embed_cache directory"}), } } RETURN_TYPES = ("WANVIDEOTEXTEMBEDS", ) RETURN_NAMES = ("text_embeds",) FUNCTION = "process" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "Encodes text prompts with explicit device selection" - - def process(self, positive_prompt, negative_prompt, device, t5=None, force_offload=True, - model_to_offload=None, use_disk_cache=False): - logging.debug(f"[MultiGPU] WanVideoTextEncode: User selected device: {device}") - - original_device = "gpu" if device != "cpu" else "cpu" - - from nodes import NODE_CLASS_MAPPINGS - original_encoder = NODE_CLASS_MAPPINGS["WanVideoTextEncode"]() - - encoder_module = inspect.getmodule(original_encoder) - - if encoder_module: - selected_device = torch.device(device) - logging.debug(f"[MultiGPU] Patching WanVideo TextEncode module to use {selected_device}") - setattr(encoder_module, 'device', selected_device) - - model_loading_name = encoder_module.__name__.replace('.nodes', '.nodes_model_loading') - if model_loading_name in sys.modules: - model_loading_module = sys.modules[model_loading_name] - setattr(model_loading_module, 'device', selected_device) - - result = original_encoder.process(positive_prompt, negative_prompt, t5=t5, - force_offload=force_offload, model_to_offload=model_to_offload, - use_disk_cache=use_disk_cache, device=original_device) - - logging.info(f"[MultiGPU] WanVideo TextEncode completed on {selected_device}") - return result + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Encodes text prompts into text embeddings. For rudimentary prompt travel you can input multiple prompts separated by '|', they will be equally spread over the video length" + + def process(self, positive_prompt, negative_prompt, t5=None, load_device=None,force_offload=True, model_to_offload=None, use_disk_cache=False): + from . import set_current_device + + set_current_device(load_device) + + if load_device == "cpu": + device = "cpu" else: - return original_encoder.process(positive_prompt, negative_prompt, t5=t5, - force_offload=force_offload, model_to_offload=model_to_offload, - use_disk_cache=use_disk_cache, device=original_device) + device = "gpu" + + text_encoder = t5[0] + + original_encoder = NODE_CLASS_MAPPINGS["WanVideoTextEncode"]() + prompt_embeds_dict = original_encoder.process(positive_prompt, negative_prompt, text_encoder, force_offload, model_to_offload, use_disk_cache, device) + return (prompt_embeds_dict) + + def parse_prompt_weights(self, prompt): + original_parser = NODE_CLASS_MAPPINGS["WanVideoTextEncode"]() + return original_parser.parse_prompt_weights(prompt) + +class LoadWanVideoT5TextEncoder: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model_name": (folder_paths.get_filename_list("text_encoders"), {"tooltip": "These models are loaded from 'ComfyUI/models/text_encoders'"}), + "precision": (["fp32", "bf16"], + {"default": "bf16"} + ), + }, + "optional": { + "device": (devices, {"default": default_device}), + "quantization": (['disabled', 'fp8_e4m3fn'], {"default": 'disabled', "tooltip": "optional quantization method"}), + } + } + + RETURN_TYPES = ("WANTEXTENCODER", "MULTIGPUDEVICE") + RETURN_NAMES = ("wan_t5_model", "load_device") + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Loads Wan text_encoder model from 'ComfyUI/models/LLM'" + + def loadmodel(self, model_name, precision, device=None, quantization="disabled"): + from . import set_current_device + + set_current_device(device) + + if device == "cpu": + load_device = "offload_device" + else: + load_device = "main_device" + + original_loader = NODE_CLASS_MAPPINGS["LoadWanVideoT5TextEncoder"]() + text_encoder = original_loader.loadmodel(model_name, precision, load_device, quantization) + + return text_encoder, device class LoadWanVideoClipTextEncoder: @classmethod def INPUT_TYPES(s): devices = get_device_list() - + default_device = devices[1] if len(devices) > 1 else devices[0] return { "required": { - "model_name": (folder_paths.get_filename_list("clip_vision") + folder_paths.get_filename_list("text_encoders"), - {"tooltip": "These models are loaded from 'ComfyUI/models/clip_vision'"}), - "precision": (["fp16", "fp32", "bf16"], {"default": "fp16"}), - "device": (devices, {"default": devices[1] if len(devices) > 1 else devices[0], - "tooltip": "Device to load the CLIP encoder to"}), + "model_name": (folder_paths.get_filename_list("clip_vision") + folder_paths.get_filename_list("text_encoders"), {"tooltip": "These models are loaded from 'ComfyUI/models/clip_vision'"}), + "precision": (["fp16", "fp32", "bf16"], + {"default": "fp16"} + ), + }, + "optional": { + "device": (devices, {"default": default_device}), } } - RETURN_TYPES = ("CLIP_VISION",) - RETURN_NAMES = ("clip_vision", ) + RETURN_TYPES = ("CLIP_VISION", "MULTIGPUDEVICE") + RETURN_NAMES = ("wan_clip_vision", "load_device") FUNCTION = "loadmodel" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "Loads Wan CLIP text encoder model from 'ComfyUI/models/clip_vision'" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Loads Wan clip_vision model from 'ComfyUI/models/clip_vision'" - def loadmodel(self, model_name, precision, device): - logging.debug(f"[MultiGPU] LoadWanVideoClipTextEncoder: User selected device: {device}") + def loadmodel(self, model_name, precision, device=None): + from . import set_current_device + + set_current_device(device) - selected_device = torch.device(device) - load_device = "offload_device" if device == "cpu" else "main_device" - - from nodes import NODE_CLASS_MAPPINGS - original_loader = NODE_CLASS_MAPPINGS["LoadWanVideoClipTextEncoder"]() - - loader_module = inspect.getmodule(original_loader) - - if loader_module: - logging.debug(f"[MultiGPU] Patching WanVideo CLIP modules to use {selected_device}") - - setattr(loader_module, 'device', selected_device) - if device == "cpu": - setattr(loader_module, 'offload_device', selected_device) - - nodes_module_name = loader_module.__name__.replace('.nodes_model_loading', '.nodes') - if nodes_module_name in sys.modules: - nodes_module = sys.modules[nodes_module_name] - setattr(nodes_module, 'device', selected_device) - if device == "cpu": - setattr(nodes_module, 'offload_device', selected_device) - - try: - logging.info(comfyui_memory_load(f"pre-model-load:wan-clip:{model_name}")) - except Exception: - pass - result = original_loader.loadmodel(model_name, precision, load_device) - try: - logging.info(comfyui_memory_load(f"post-model-load:wan-clip:{model_name}")) - except Exception: - pass - - logging.info(f"[MultiGPU] WanVideo CLIP encoder loaded on {selected_device}") - - return result + if device == "cpu": + load_device = "offload_device" else: - logging.error(f"[MultiGPU] Could not patch WanVideo CLIP modules, falling back") - return original_loader.loadmodel(model_name, precision, load_device) + load_device = "main_device" -class WanVideoModelLoader_2: + original_loader = NODE_CLASS_MAPPINGS["LoadWanVideoClipTextEncoder"]() + clip_model = original_loader.loadmodel(model_name, precision, load_device) + + return clip_model, device + +class WanVideoTextEncodeCached: @classmethod def INPUT_TYPES(s): - return WanVideoModelLoader.INPUT_TYPES() - - RETURN_TYPES = WanVideoModelLoader.RETURN_TYPES - RETURN_NAMES = WanVideoModelLoader.RETURN_NAMES + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model_name": (folder_paths.get_filename_list("text_encoders"), {"tooltip": "These models are loaded from 'ComfyUI/models/text_encoders'"}), + "precision": (["fp32", "bf16"], {"default": "bf16"}), + "positive_prompt": ("STRING", {"default": "", "multiline": True} ), + "negative_prompt": ("STRING", {"default": "", "multiline": True} ), + "quantization": (['disabled', 'fp8_e4m3fn'], {"default": 'disabled', "tooltip": "optional quantization method"}), + "use_disk_cache": ("BOOLEAN", {"default": True, "tooltip": "Cache the text embeddings to disk for faster re-use, under the custom_nodes/ComfyUI-WanVideoWrapper/text_embed_cache directory"}), + "load_device": (devices, {"default": default_device} + ), + }, + "optional": { + "extender_args": ("WANVIDEOPROMPTEXTENDER_ARGS", {"tooltip": "Use this node to extend the prompt with additional text."}), + } + } + + RETURN_TYPES = ("WANVIDEOTEXTEMBEDS", "WANVIDEOTEXTEMBEDS", "STRING") + RETURN_NAMES = ("text_embeds", "negative_text_embeds", "positive_prompt") + OUTPUT_TOOLTIPS = ("The text embeddings for both prompts", "The text embeddings for the negative prompt only (for NAG)", "Positive prompt to display prompt extender results") + FUNCTION = "process" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = """Encodes text prompts into text embeddings. This node loads and completely unloads the T5 after done, leaving no VRAM or RAM imprint.""" + + + def process(self, model_name, precision, positive_prompt, negative_prompt, quantization='disabled', use_disk_cache=True, load_device=None, extender_args=None): + from . import set_current_device + + set_current_device(load_device) + + if load_device == "cpu": + device = "cpu" + else: + device = "gpu" + + original_encoder = NODE_CLASS_MAPPINGS["WanVideoTextEncodeCached"]() + prompt_embeds_dict, negative_text_embeds, positive_prompt_out = original_encoder.process(model_name, precision, positive_prompt, negative_prompt, quantization, use_disk_cache, device, extender_args) + + return prompt_embeds_dict, negative_text_embeds, positive_prompt_out + +class WanVideoTextEncodeSingle: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "prompt": ("STRING", {"default": "", "multiline": True} ), + }, + "optional": { + "t5": ("WANTEXTENCODER",), + "load_device": ("MULTIGPUDEVICE",), + "force_offload": ("BOOLEAN", {"default": True}), + "model_to_offload": ("WANVIDEOMODEL", {"tooltip": "Model to move to offload_device before encoding"}), + "use_disk_cache": ("BOOLEAN", {"default": False, "tooltip": "Cache the text embeddings to disk for faster re-use, under the custom_nodes/ComfyUI-WanVideoWrapper/text_embed_cache directory"}), + } + } + + RETURN_TYPES = ("WANVIDEOTEXTEMBEDS", ) + RETURN_NAMES = ("text_embeds",) + FUNCTION = "process" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Encodes text prompt into text embedding." + + def process(self, prompt, t5=None, load_device=None, force_offload=True, model_to_offload=None, use_disk_cache=False): + from . import set_current_device + + set_current_device(load_device) + + if load_device == "cpu": + device = "cpu" + else: + device = "gpu" + + text_encoder = t5[0] + + original_encoder = NODE_CLASS_MAPPINGS["WanVideoTextEncodeSingle"]() + prompt_embeds_dict = original_encoder.process(prompt, text_encoder, force_offload, model_to_offload, use_disk_cache, device) + return (prompt_embeds_dict) + +class WanVideoVAELoader: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model_name": (folder_paths.get_filename_list("vae"), {"tooltip": "These models are loaded from 'ComfyUI/models/vae'"}), + }, + "optional": { + "load_device": (devices, {"default": default_device}), + "precision": (["fp16", "fp32", "bf16"], + {"default": "bf16"} + ), + "compile_args": ("WANCOMPILEARGS", ), + } + } + + RETURN_TYPES = ("WANVAE", "MULTIGPUDEVICE",) + RETURN_NAMES = ("vae", "load_device",) FUNCTION = "loadmodel" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "Second model loader instance for workflows using multiple models on different devices" - - def loadmodel(self, model, base_precision, device, quantization, - compile_args=None, attention_mode="sdpa", block_swap_args=None, lora=None, - vram_management_args=None, vace_model=None, fantasytalking_model=None, multitalk_model=None, fantasyportrait_model=None): - loader = WanVideoModelLoader() - return loader.loadmodel(model, base_precision, device, quantization, - compile_args, attention_mode, block_swap_args, lora, - vram_management_args, vace_model, fantasytalking_model, multitalk_model, fantasyportrait_model) + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Loads Wan VAE model from 'ComfyUI/models/vae'" -class WanVideoSampler: + def loadmodel(self, model_name, load_device=None, precision="fp16", compile_args=None): + from . import set_current_device + + set_current_device(load_device) + + original_loader = NODE_CLASS_MAPPINGS["WanVideoVAELoader"]() + vae_model = original_loader.loadmodel(model_name, precision, compile_args) + + return vae_model, load_device + +class WanVideoTinyVAELoader: @classmethod def INPUT_TYPES(s): - from nodes import NODE_CLASS_MAPPINGS - original_types = NODE_CLASS_MAPPINGS["WanVideoSampler"].INPUT_TYPES() - return original_types - - RETURN_TYPES = ("LATENT", "LATENT",) - RETURN_NAMES = ("samples", "denoised_samples",) - FUNCTION = "process" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "MultiGPU-aware sampler that ensures correct device for each model" - - def process(self, model, **kwargs): - model_device = model.load_device - logging.info(f"[MultiGPU] WanVideoSampler: Processing on device: {model_device}") - - for module_name in sys.modules.keys(): - if 'WanVideoWrapper' in module_name and hasattr(sys.modules[module_name], 'device'): - sys.modules[module_name].device = model_device - - from nodes import NODE_CLASS_MAPPINGS - original_sampler = NODE_CLASS_MAPPINGS["WanVideoSampler"]() - return original_sampler.process(model, **kwargs) + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model_name": (folder_paths.get_filename_list("vae_approx"), {"tooltip": "These models are loaded from 'ComfyUI/models/vae_approx'"}), + }, + "optional": { + "load_device": (devices, {"default": default_device}), + "precision": (["fp16", "fp32", "bf16"], {"default": "fp16"}), + "parallel": ("BOOLEAN", {"default": False, "tooltip": "uses more memory but is faster"}), + } + } -class WanVideoVACEEncode: - @classmethod - def INPUT_TYPES(s): - from nodes import NODE_CLASS_MAPPINGS - original_types = NODE_CLASS_MAPPINGS["WanVideoVACEEncode"].INPUT_TYPES() - return original_types - - RETURN_TYPES = ("LATENT",) - RETURN_NAMES = ("latent",) - FUNCTION = "process" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "MultiGPU-aware VACE encoder that uses device from input VAE" - - def process(self, vae, **kwargs): - # Get device from VAE object - vae_device = vae.load_device - logging.info(f"[MultiGPU] WanVideoVACEEncode: Processing on device: {vae_device}") - - # Patch all WanVideo modules to use the VAE's device - for module_name in sys.modules.keys(): - if 'WanVideoWrapper' in module_name and hasattr(sys.modules[module_name], 'device'): - sys.modules[module_name].device = vae_device - - from nodes import NODE_CLASS_MAPPINGS - original_encoder = NODE_CLASS_MAPPINGS["WanVideoVACEEncode"]() - return original_encoder.process(vae, **kwargs) + RETURN_TYPES = ("WANVAE","MULTIGPUDEVICE") + RETURN_NAMES = ("vae", "load_device") + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Loads Wan VAE model from 'ComfyUI/models/vae_approx'" + + def loadmodel(self, model_name, load_device=None, precision="fp16", parallel=False): + from . import set_current_device + + set_current_device(load_device) + + original_loader = NODE_CLASS_MAPPINGS["WanVideoTinyVAELoader"]() + vae_model = original_loader.loadmodel(model_name, precision, parallel) + + return vae_model, load_device class WanVideoBlockSwap: @classmethod def INPUT_TYPES(s): + base_inputs = copy.deepcopy(NODE_CLASS_MAPPINGS["WanVideoBlockSwap"].INPUT_TYPES()) devices = get_device_list() - - return { - "required": { - "blocks_to_swap": ("INT", {"default": 20, "min": 0, "max": 40, "step": 1, - "tooltip": "Number of transformer blocks to swap, the 14B model has 40, while the 1.3B model has 30 blocks"}), - "swap_device": (devices, {"default": "cpu", - "tooltip": "Device to swap blocks to during sampling (default: cpu for standard behavior)"}), - "model_offload_device": (devices, {"default": "cpu", - "tooltip": "Device to offload entire model to when done (default: cpu)"}), - "offload_img_emb": ("BOOLEAN", {"default": False, "tooltip": "Offload img_emb to swap_device"}), - "offload_txt_emb": ("BOOLEAN", {"default": False, "tooltip": "Offload time_emb to swap_device"}), + default_device = "cpu" if "cpu" in devices else devices[0] + base_inputs.setdefault("optional", {}) + base_inputs["optional"]["swap_device"] = ( + devices, + { + "default": default_device, + "tooltip": "Device that receives swapped transformer blocks", }, - "optional": { - "use_non_blocking": ("BOOLEAN", {"default": False, - "tooltip": "Use non-blocking memory transfer for offloading, reserves more RAM but is faster"}), - "vace_blocks_to_swap": ("INT", {"default": 0, "min": 0, "max": 15, "step": 1, - "tooltip": "Number of VACE blocks to swap, the VACE model has 15 blocks"}), - "prefetch_blocks": ("INT", {"default": 0, "min": 0, "max": 40, "step": 1, "tooltip": "Number of blocks to prefetch ahead, can speed up processing but increases memory usage. 1 is usually enough to offset speed loss from block swapping, use the debug option to confirm it for your system"}), - "block_swap_debug": ("BOOLEAN", {"default": False, "tooltip": "Enable debug logging for block swapping"}), - }, - } - + ) + return base_inputs + RETURN_TYPES = ("BLOCKSWAPARGS",) RETURN_NAMES = ("block_swap_args",) FUNCTION = "setargs" - CATEGORY = "WanVideoWrapper" - DESCRIPTION = "Block swap settings with explicit device selection for memory management across GPUs" - - def setargs(self, blocks_to_swap, swap_device, model_offload_device, offload_img_emb, offload_txt_emb, - use_non_blocking=False, vace_blocks_to_swap=0, prefetch_blocks=0, block_swap_debug=False): - logging.debug(f"[MultiGPU] WanVideoBlockSwap: swap_device={swap_device}, model_offload_device={model_offload_device}, blocks_to_swap={blocks_to_swap}") - - selected_swap_device = torch.device(swap_device) - selected_offload_device = torch.device(model_offload_device) - - for module_name in sys.modules.keys(): - if 'WanVideoWrapper' in module_name and 'nodes_model_loading' in module_name: - module = sys.modules[module_name] - setattr(module, 'offload_device', selected_offload_device) - setattr(module, '_block_swap_device_override', selected_swap_device) - setattr(module, '_model_offload_device_override', selected_offload_device) - logging.debug(f"[MultiGPU] Patched {module_name} for offload to {selected_offload_device} and swap to {selected_swap_device}") + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "Extends Wan block swap with explicit device selection" - if 'WanVideoWrapper' in module_name and module_name.endswith('.nodes'): - module = sys.modules[module_name] - setattr(module, 'offload_device', selected_offload_device) - setattr(module, '_block_swap_device_override', selected_swap_device) - setattr(module, '_model_offload_device_override', selected_offload_device) + def setargs(self, swap_device=None, **kwargs): + block_swap_config = dict(kwargs) + block_swap_config["swap_device"] = str(swap_device) + return (block_swap_config,) - block_swap_args = { - "blocks_to_swap": blocks_to_swap, - "offload_img_emb": offload_img_emb, - "offload_txt_emb": offload_txt_emb, - "use_non_blocking": use_non_blocking, - "vace_blocks_to_swap": vace_blocks_to_swap, - "prefetch_blocks": prefetch_blocks, - "block_swap_debug": block_swap_debug, - "swap_device": swap_device, - "model_offload_device": model_offload_device, +class WanVideoImageToVideoEncode: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "width": ("INT", {"default": 832, "min": 64, "max": 8096, "step": 8, "tooltip": "Width of the image to encode"}), + "height": ("INT", {"default": 480, "min": 64, "max": 8096, "step": 8, "tooltip": "Height of the image to encode"}), + "num_frames": ("INT", {"default": 81, "min": 1, "max": 10000, "step": 4, "tooltip": "Number of frames to encode"}), + "noise_aug_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Strength of noise augmentation, helpful for I2V where some noise can add motion and give sharper results"}), + "start_latent_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Additional latent multiplier, helpful for I2V where lower values allow for more motion"}), + "end_latent_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Additional latent multiplier, helpful for I2V where lower values allow for more motion"}), + "force_offload": ("BOOLEAN", {"default": True}), + }, + "optional": { + "vae": ("WANVAE",), + "load_device": ("MULTIGPUDEVICE",), + "clip_embeds": ("WANVIDIMAGE_CLIPEMBEDS", {"tooltip": "Clip vision encoded image"}), + "start_image": ("IMAGE", {"tooltip": "Image to encode"}), + "end_image": ("IMAGE", {"tooltip": "end frame"}), + "control_embeds": ("WANVIDIMAGE_EMBEDS", {"tooltip": "Control signal for the Fun -model"}), + "fun_or_fl2v_model": ("BOOLEAN", {"default": True, "tooltip": "Enable when using official FLF2V or Fun model"}), + "temporal_mask": ("MASK", {"tooltip": "mask"}), + "extra_latents": ("LATENT", {"tooltip": "Extra latents to add to the input front, used for Skyreels A2 reference images"}), + "tiled_vae": ("BOOLEAN", {"default": False, "tooltip": "Use tiled VAE encoding for reduced memory use"}), + "add_cond_latents": ("ADD_COND_LATENTS", {"advanced": True, "tooltip": "Additional cond latents WIP"}), + } } + + RETURN_TYPES = ("WANVIDIMAGE_EMBEDS",) + RETURN_NAMES = ("image_embeds",) + FUNCTION = "process" + CATEGORY = "multigpu/WanVideoWrapper" + + def process(self, width, height, num_frames, force_offload, noise_aug_strength, + start_latent_strength, end_latent_strength, start_image=None, end_image=None, control_embeds=None, fun_or_fl2v_model=False, + temporal_mask=None, extra_latents=None, clip_embeds=None, tiled_vae=False, add_cond_latents=None, vae=None, load_device=None): + from . import set_current_device + + original_encoder = NODE_CLASS_MAPPINGS["WanVideoImageToVideoEncode"]() + encoder_module = inspect.getmodule(original_encoder) + + original_module_device = encoder_module.device + original_module_offload = encoder_module.offload_device + + set_current_device(load_device) + + compute_device_to_be_patched = mm.get_torch_device() + encoder_module.device = compute_device_to_be_patched + + encoder_module.offload_device = mm.unet_offload_device() + + inner_vae = vae[0] + + try: + return original_encoder.process(width, height, num_frames, force_offload, noise_aug_strength, start_latent_strength, end_latent_strength, start_image, + end_image, control_embeds, fun_or_fl2v_model, temporal_mask, extra_latents, clip_embeds, tiled_vae, add_cond_latents, inner_vae,) + finally: + encoder_module.device = original_module_device + encoder_module.offload_device = original_module_offload + +class WanVideoDecode: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "vae": ("WANVAE",), + "load_device": ("MULTIGPUDEVICE",), + "samples": ("LATENT",), + "enable_vae_tiling": ("BOOLEAN", {"default": False, "tooltip": ( + "Drastically reduces memory use but will introduce seams at tile stride boundaries. " + "The location and number of seams is dictated by the tile stride size. " + "The visibility of seams can be controlled by increasing the tile size. " + "Seams become less obvious at 1.5x stride and are barely noticeable at 2x stride size. " + "Which is to say if you use a stride width of 160, the seams are barely noticeable with a tile width of 320." + )}), + "tile_x": ("INT", {"default": 272, "min": 40, "max": 2048, "step": 8, "tooltip": "Tile width in pixels. Smaller values use less VRAM but will make seams more obvious."}), + "tile_y": ("INT", {"default": 272, "min": 40, "max": 2048, "step": 8, "tooltip": "Tile height in pixels. Smaller values use less VRAM but will make seams more obvious."}), + "tile_stride_x": ("INT", {"default": 144, "min": 32, "max": 2040, "step": 8, "tooltip": "Tile stride width in pixels. Smaller values use less VRAM but will introduce more seams."}), + "tile_stride_y": ("INT", {"default": 128, "min": 32, "max": 2040, "step": 8, "tooltip": "Tile stride height in pixels. Smaller values use less VRAM but will introduce more seams."}), + }, + "optional": { + "normalization": (["default", "minmax"], {"advanced": True}), + } + } + + @classmethod + def VALIDATE_INPUTS(s, tile_x, tile_y, tile_stride_x, tile_stride_y): + if tile_x <= tile_stride_x: + return "Tile width must be larger than the tile stride width." + if tile_y <= tile_stride_y: + return "Tile height must be larger than the tile stride height." + return True + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + FUNCTION = "decode" + CATEGORY = "multigpu/WanVideoWrapper" + + def decode(self, vae, load_device, samples, enable_vae_tiling, tile_x, tile_y, tile_stride_x, tile_stride_y, normalization="default"): + from . import set_current_device + + original_decode = NODE_CLASS_MAPPINGS["WanVideoDecode"]() + decode_module = inspect.getmodule(original_decode) + original_module_device = decode_module.device + + set_current_device(load_device) + compute_device_to_be_patched = mm.get_torch_device() + decode_module.device = compute_device_to_be_patched + + try: + return original_decode.decode(vae[0], samples, enable_vae_tiling, tile_x, tile_y, tile_stride_x, tile_stride_y, normalization) + finally: + decode_module.device = original_module_device + + +class WanVideoVACEEncode: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "vae": ("WANVAE",), + "load_device": ("MULTIGPUDEVICE",), + "width": ("INT", {"default": 832, "min": 64, "max": 8096, "step": 8, "tooltip": "Width of the image to encode"}), + "height": ("INT", {"default": 480, "min": 64, "max": 8096, "step": 8, "tooltip": "Height of the image to encode"}), + "num_frames": ("INT", {"default": 81, "min": 1, "max": 10000, "step": 4, "tooltip": "Number of frames to encode"}), + "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}), + "vace_start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "Start percent of the steps to apply VACE"}), + "vace_end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "End percent of the steps to apply VACE"}), + }, + "optional": { + "input_frames": ("IMAGE",), + "ref_images": ("IMAGE",), + "input_masks": ("MASK",), + "prev_vace_embeds": ("WANVIDIMAGE_EMBEDS",), + "tiled_vae": ("BOOLEAN", {"default": False, "tooltip": "Use tiled VAE encoding for reduced memory use"}), + }, + } + + RETURN_TYPES = ("WANVIDIMAGE_EMBEDS", ) + RETURN_NAMES = ("vace_embeds",) + FUNCTION = "process" + CATEGORY = "multigpu/WanVideoWrapper" + + def process(self, vae, load_device, width, height, num_frames, strength, vace_start_percent, vace_end_percent, input_frames=None, ref_images=None, input_masks=None, prev_vace_embeds=None, tiled_vae=False): + from . import set_current_device + + original_encode = NODE_CLASS_MAPPINGS["WanVideoVACEEncode"]() + encode_module = inspect.getmodule(original_encode) + original_module_device = encode_module.device + + set_current_device(load_device) + compute_device_to_be_patched = mm.get_torch_device() + encode_module.device = compute_device_to_be_patched + + try: + return original_encode.process(vae[0], width, height, num_frames, strength, vace_start_percent, vace_end_percent, input_frames, ref_images, input_masks, prev_vace_embeds, tiled_vae) + finally: + encode_module.device = original_module_device + + +class WanVideoEncode: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "vae": ("WANVAE",), + "load_device": ("MULTIGPUDEVICE",), + "image": ("IMAGE",), + "enable_vae_tiling": ("BOOLEAN", {"default": False, "tooltip": "Drastically reduces memory use but may introduce seams"}), + "tile_x": ("INT", {"default": 272, "min": 64, "max": 2048, "step": 1, "tooltip": "Tile size in pixels, smaller values use less VRAM, may introduce more seams"}), + "tile_y": ("INT", {"default": 272, "min": 64, "max": 2048, "step": 1, "tooltip": "Tile size in pixels, smaller values use less VRAM, may introduce more seams"}), + "tile_stride_x": ("INT", {"default": 144, "min": 32, "max": 2048, "step": 32, "tooltip": "Tile stride in pixels, smaller values use less VRAM, may introduce more seams"}), + "tile_stride_y": ("INT", {"default": 128, "min": 32, "max": 2048, "step": 32, "tooltip": "Tile stride height in pixels, smaller values use less VRAM, may introduce more seams"}), + }, + "optional": { + "noise_aug_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Strength of noise augmentation, helpful for leapfusion I2V where some noise can add motion and give sharper results"}), + "latent_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Additional latent multiplier, helpful for leapfusion I2V where lower values allow for more motion"}), + "mask": ("MASK", ), + } + } + + RETURN_TYPES = ("LATENT",) + RETURN_NAMES = ("samples",) + FUNCTION = "encode" + CATEGORY = "multigpu/WanVideoWrapper" + + def encode(self, vae, load_device, image, enable_vae_tiling, tile_x, tile_y, tile_stride_x, tile_stride_y, noise_aug_strength=0.0, latent_strength=1.0, mask=None): + from . import set_current_device + + original_encode = NODE_CLASS_MAPPINGS["WanVideoEncode"]() + encode_module = inspect.getmodule(original_encode) + original_module_device = encode_module.device + + set_current_device(load_device) + compute_device_to_be_patched = mm.get_torch_device() + encode_module.device = compute_device_to_be_patched + + try: + return original_encode.encode(vae[0], image, enable_vae_tiling, tile_x, tile_y, tile_stride_x, tile_stride_y, noise_aug_strength, latent_strength, mask) + finally: + encode_module.device = original_module_device + +class WanVideoClipVisionEncode: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "clip_vision": ("CLIP_VISION",), + "load_device": ("MULTIGPUDEVICE",), + "image_1": ("IMAGE", {"tooltip": "Image to encode"}), + "strength_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Additional clip embed multiplier"}), + "strength_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Additional clip embed multiplier"}), + "crop": (["center", "disabled"], {"default": "center", "tooltip": "Crop image to 224x224 before encoding"}), + "combine_embeds": (["average", "sum", "concat", "batch"], {"default": "average", "tooltip": "Method to combine multiple clip embeds"}), + "force_offload": ("BOOLEAN", {"default": True}), + }, + "optional": { + "image_2": ("IMAGE", ), + "negative_image": ("IMAGE", {"tooltip": "image to use for uncond"}), + "tiles": ("INT", {"default": 0, "min": 0, "max": 16, "step": 2, "tooltip": "Use matteo's tiled image encoding for improved accuracy"}), + "ratio": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "Ratio of the tile average"}), + } + } + + RETURN_TYPES = ("WANVIDIMAGE_CLIPEMBEDS",) + RETURN_NAMES = ("image_embeds",) + FUNCTION = "process" + CATEGORY = "multigpu/WanVideoWrapper" + + def process(self, clip_vision, load_device, image_1, strength_1, strength_2, force_offload, crop, combine_embeds, image_2=None, negative_image=None, tiles=0, ratio=1.0): + from . import set_current_device + + original_encode = NODE_CLASS_MAPPINGS["WanVideoClipVisionEncode"]() + encode_module = inspect.getmodule(original_encode) + original_module_device = encode_module.device + + set_current_device(load_device) + compute_device_to_be_patched = mm.get_torch_device() + encode_module.device = compute_device_to_be_patched + + try: + return original_encode.process(clip_vision[0], image_1, strength_1, strength_2, force_offload, crop, combine_embeds, image_2, negative_image, tiles, ratio) + finally: + encode_module.device = original_module_device + +class WanVideoControlnetLoader: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model": (folder_paths.get_filename_list("controlnet"), {"tooltip": "These models are loaded from the 'ComfyUI/models/controlnet' -folder",}), + "base_precision": (["fp32", "bf16", "fp16"], {"default": "bf16"}), + "quantization": (['disabled', 'fp8_e4m3fn', 'fp8_e4m3fn_fast', 'fp8_e5m2', 'fp8_e4m3fn_fast_no_ffn'], {"default": 'disabled', "tooltip": "optional quantization method"}), + "load_device": (["main_device", "offload_device"], {"default": "main_device", "tooltip": "Initial device to load the model to, NOT recommended with the larger models unless you have 48GB+ VRAM"}), + "device": (devices, {"default": default_device}), + }, + } + + RETURN_TYPES = ("WANVIDEOCONTROLNET",) + RETURN_NAMES = ("controlnet", ) + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "MultiGPU-aware ControlNet loader for WanVideo models" + + def loadmodel(self, model, base_precision, load_device, quantization, device): + from . import set_current_device + + set_current_device(device) - logging.info(f"[MultiGPU] WanVideoBlockSwap configuration complete") + original_loader = NODE_CLASS_MAPPINGS["WanVideoControlnetLoader"]() + return original_loader.loadmodel(model, base_precision, load_device, quantization) + +class FantasyTalkingModelLoader: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model": (folder_paths.get_filename_list("diffusion_models"), {"tooltip": "These models are loaded from the 'ComfyUI/models/diffusion_models' -folder",}), + "base_precision": (["fp32", "bf16", "fp16"], {"default": "fp16"}), + "device": (devices, {"default": default_device}), + }, + } + + RETURN_TYPES = ("FANTASYTALKINGMODEL",) + RETURN_NAMES = ("model", ) + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "MultiGPU-aware FantasyTalking model loader" + + def loadmodel(self, model, base_precision, device): + from . import set_current_device + + set_current_device(device) - return (block_swap_args,) + original_loader = NODE_CLASS_MAPPINGS["FantasyTalkingModelLoader"]() + return original_loader.loadmodel(model, base_precision) + +class Wav2VecModelLoader: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model": (folder_paths.get_filename_list("wav2vec2"), {"tooltip": "These models are loaded from the 'ComfyUI/models/wav2vec2' -folder",}), + "base_precision": (["fp32", "bf16", "fp16"], {"default": "fp16"}), + "load_device": (["main_device", "offload_device"], {"default": "main_device", "tooltip": "Initial device to load the model to, NOT recommended with the larger models unless you have 48GB+ VRAM"}), + "device": (devices, {"default": default_device}), + }, + } + + RETURN_TYPES = ("WAV2VECMODEL",) + RETURN_NAMES = ("wav2vec_model", ) + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "MultiGPU-aware Wav2Vec model loader" + + def loadmodel(self, model, base_precision, load_device, device): + from . import set_current_device + + set_current_device(device) + + original_loader = NODE_CLASS_MAPPINGS["Wav2VecModelLoader"]() + return original_loader.loadmodel(model, base_precision, load_device) + +class DownloadAndLoadWav2VecModel: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model": ( + [ + "TencentGameMate/chinese-wav2vec2-base", + "facebook/wav2vec2-base-960h" + ], + ), + "base_precision": (["fp32", "bf16", "fp16"], {"default": "fp16"}), + "load_device": (["main_device", "offload_device"], {"default": "main_device", "tooltip": "Initial device to load the model to, NOT recommended with the larger models unless you have 48GB+ VRAM"}), + "device": (devices, {"default": default_device}), + }, + } + + RETURN_TYPES = ("WAV2VECMODEL",) + RETURN_NAMES = ("wav2vec_model", ) + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + DESCRIPTION = "MultiGPU-aware downloadable Wav2Vec model loader" + + def loadmodel(self, model, base_precision, load_device, device): + from . import set_current_device + + set_current_device(device) + + original_loader = NODE_CLASS_MAPPINGS["DownloadAndLoadWav2VecModel"]() + return original_loader.loadmodel(model, base_precision, load_device) + +class WanVideoUni3C_ControlnetLoader: + @classmethod + def INPUT_TYPES(s): + devices = get_device_list() + default_device = devices[1] if len(devices) > 1 else devices[0] + return { + "required": { + "model": (folder_paths.get_filename_list("controlnet"), {"tooltip": "These models are loaded from the 'ComfyUI/models/controlnet' -folder",}), + "base_precision": (["fp32", "bf16", "fp16"], {"default": "fp16"}), + "quantization": (['disabled', 'fp8_e4m3fn', 'fp8_e5m2'], {"default": 'disabled', "tooltip": "optional quantization method"}), + "load_device": (["main_device", "offload_device"], {"default": "main_device", "tooltip": "Initial device to load the model to, NOT recommended with the larger models unless you have 48GB+ VRAM"}), + "device": (devices, {"default": default_device}), + "attention_mode": ([ + "sdpa", + "sageattn", + ], {"default": "sdpa"}), + }, + "optional": { + "compile_args": ("WANCOMPILEARGS", ), + } + } + + RETURN_TYPES = ("WANVIDEOCONTROLNET",) + RETURN_NAMES = ("controlnet", ) + FUNCTION = "loadmodel" + CATEGORY = "multigpu/WanVideoWrapper" + + def loadmodel(self, model, base_precision, load_device, device, quantization, attention_mode, compile_args=None): + from . import set_current_device + + set_current_device(device) + + original_loader = NODE_CLASS_MAPPINGS["WanVideoUni3C_ControlnetLoader"]() + return original_loader.loadmodel(model, base_precision, load_device, quantization, attention_mode, compile_args) \ No newline at end of file