From 25d6c9f1d2544e1e6324f845e7297f19758f4108 Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Thu, 25 Sep 2025 00:08:26 +1200 Subject: [PATCH 01/17] Make the node auto download/build models and update readme.md accordingly --- __init__.py | 165 +++++++++++++++++++++++++++++++++++++----- load_rife_config.json | 16 ++++ readme.md | 37 +++++++--- requirements.txt | 6 +- utilities.py | 101 ++++++++++++++++++++++++++ 5 files changed, 291 insertions(+), 34 deletions(-) create mode 100644 load_rife_config.json create mode 100644 utilities.py diff --git a/__init__.py b/__init__.py index c13155b..34bd62c 100644 --- a/__init__.py +++ b/__init__.py @@ -3,23 +3,149 @@ import os from comfy.model_management import get_torch_device from .vfi_utilities import preprocess_frames, postprocess_frames, generate_frames_rife, logger from .trt_utilities import Engine +from .utilities import download_file, ColoredLogger import folder_paths import time from polygraphy import cuda +import comfy.model_management as mm +import tensorrt +import json ENGINE_DIR = os.path.join(folder_paths.models_dir, "tensorrt", "rife") +# Image dimensions for TensorRT engine building +IMAGE_DIM_MIN = 256 +IMAGE_DIM_OPT = 512 +IMAGE_DIM_MAX = 3840 + +# Logger for this module +rife_logger = ColoredLogger("ComfyUI-Rife-Tensorrt") + +# Function to load configuration +def load_node_config(config_filename="load_rife_config.json"): + """Loads node configuration from a JSON file.""" + current_dir = os.path.dirname(__file__) + config_path = os.path.join(current_dir, config_filename) + + default_config = { + "model": { + "options": ["rife49_ensemble_True_scale_1_sim"], + "default": "rife49_ensemble_True_scale_1_sim", + "tooltip": "Default model (fallback from code)" + }, + "precision": { + "options": ["fp16", "fp32"], + "default": "fp16", + "tooltip": "Default precision (fallback from code)" + } + } + + try: + with open(config_path, 'r') as f: + config = json.load(f) + rife_logger.info(f"Successfully loaded configuration from {config_filename}") + return config + except FileNotFoundError: + rife_logger.warning(f"Configuration file '{config_path}' not found. Using default fallback configuration.") + return default_config + except json.JSONDecodeError: + rife_logger.error(f"Error decoding JSON from '{config_path}'. Using default fallback configuration.") + return default_config + except Exception as e: + rife_logger.error(f"An unexpected error occurred while loading '{config_path}': {e}. Using default fallback.") + return default_config + +# Load the configuration once when the module is imported +LOAD_RIFE_NODE_CONFIG = load_node_config() + +class LoadRifeTensorrtModel: + @classmethod + def INPUT_TYPES(cls): + # Use the pre-loaded configuration + model_config = LOAD_RIFE_NODE_CONFIG.get("model", {}) + precision_config = LOAD_RIFE_NODE_CONFIG.get("precision", {}) + + # Provide sensible defaults if keys are missing in the config + model_options = model_config.get("options", ["rife49_ensemble_True_scale_1_sim"]) + model_default = model_config.get("default", "rife49_ensemble_True_scale_1_sim") + model_tooltip = model_config.get("tooltip", "Select a RIFE model.") + + precision_options = precision_config.get("options", ["fp16", "fp32"]) + precision_default = precision_config.get("default", "fp16") + precision_tooltip = precision_config.get("tooltip", "Select precision.") + + return { + "required": { + "model": (model_options, {"default": model_default, "tooltip": model_tooltip}), + "precision": (precision_options, {"default": precision_default, "tooltip": precision_tooltip}), + } + } + + RETURN_NAMES = ("rife_trt_model",) + RETURN_TYPES = ("RIFE_TRT_MODEL",) + CATEGORY = "tensorrt" + DESCRIPTION = "Load RIFE tensorrt models, they will be built automatically if not found." + FUNCTION = "load_rife_tensorrt_model" + + def load_rife_tensorrt_model(self, model, precision): + tensorrt_models_dir = os.path.join(folder_paths.models_dir, "tensorrt", "rife") + onnx_models_dir = os.path.join(folder_paths.models_dir, "onnx") + + os.makedirs(tensorrt_models_dir, exist_ok=True) + os.makedirs(onnx_models_dir, exist_ok=True) + + onnx_model_path = os.path.join(onnx_models_dir, f"{model}.onnx") + + # Build tensorrt model path with detailed naming + engine_channel = 3 + engine_min_batch, engine_opt_batch, engine_max_batch = 1, 1, 1 + engine_min_h, engine_opt_h, engine_max_h = IMAGE_DIM_MIN, IMAGE_DIM_OPT, IMAGE_DIM_MAX + engine_min_w, engine_opt_w, engine_max_w = IMAGE_DIM_MIN, IMAGE_DIM_OPT, IMAGE_DIM_MAX + tensorrt_model_path = os.path.join(tensorrt_models_dir, f"{model}_{precision}_{engine_min_batch}x{engine_channel}x{engine_min_h}x{engine_min_w}_{engine_opt_batch}x{engine_channel}x{engine_opt_h}x{engine_opt_w}_{engine_max_batch}x{engine_channel}x{engine_max_h}x{engine_max_w}_{tensorrt.__version__}.trt") + + if not os.path.exists(tensorrt_model_path): + if not os.path.exists(onnx_model_path): + onnx_model_download_url = f"https://huggingface.co/yuvraj108c/rife-onnx/resolve/main/{model}.onnx" + rife_logger.info(f"Downloading {onnx_model_download_url}") + download_file(url=onnx_model_download_url, save_path=onnx_model_path) + else: + rife_logger.info(f"ONNX model found at: {onnx_model_path}") + + rife_logger.info(f"Building TensorRT engine for {onnx_model_path}: {tensorrt_model_path}") + mm.soft_empty_cache() + s = time.time() + engine = Engine(tensorrt_model_path) + engine.build( + onnx_path=onnx_model_path, + fp16=True if precision == "fp16" else False, + input_profile=[ + { + "img0": [(engine_min_batch, engine_channel, engine_min_h, engine_min_w), (engine_opt_batch, engine_channel, engine_opt_h, engine_opt_w), (engine_max_batch, engine_channel, engine_max_h, engine_max_w)], + "img1": [(engine_min_batch, engine_channel, engine_min_h, engine_min_w), (engine_opt_batch, engine_channel, engine_opt_h, engine_opt_w), (engine_max_batch, engine_channel, engine_max_h, engine_max_w)], + } + ], + ) + e = time.time() + rife_logger.info(f"Time taken to build: {(e-s)} seconds") + + rife_logger.info(f"Loading TensorRT engine: {tensorrt_model_path}") + mm.soft_empty_cache() + engine = Engine(tensorrt_model_path) + engine.load() + + return (engine,) + class RifeTensorrt: @classmethod def INPUT_TYPES(s): return { "required": { - "frames": ("IMAGE", ), - "engine": (os.listdir(ENGINE_DIR),), - "clear_cache_after_n_frames": ("INT", {"default": 100, "min": 1, "max": 1000}), - "multiplier": ("INT", {"default": 2, "min": 1}), - "use_cuda_graph": ("BOOLEAN", {"default": True}), - "keep_model_loaded": ("BOOLEAN", {"default": False}), + "frames": ("IMAGE", {"tooltip": "Input frames for video frame interpolation"}), + "rife_trt_model": ("RIFE_TRT_MODEL", {"tooltip": "Tensorrt model built and loaded"}), + "clear_cache_after_n_frames": ("INT", {"default": 100, "min": 1, "max": 1000, "tooltip": "Clear CUDA cache after processing this many frames"}), + "multiplier": ("INT", {"default": 2, "min": 1, "tooltip": "Frame interpolation multiplier"}), + "use_cuda_graph": ("BOOLEAN", {"default": True, "tooltip": "Use CUDA graph for better performance"}), + "keep_model_loaded": ("BOOLEAN", {"default": False, "tooltip": "Keep model loaded in memory after processing"}), }, } @@ -31,7 +157,7 @@ class RifeTensorrt: def vfi( self, frames, - engine, + rife_trt_model, clear_cache_after_n_frames=100, multiplier=2, use_cuda_graph=True, @@ -45,24 +171,21 @@ class RifeTensorrt: } cudaStream = cuda.Stream() - engine_path = os.path.join(ENGINE_DIR, engine) - if (not hasattr(self, 'engine') or self.engine_label != engine): - self.engine = Engine(engine_path) - logger(f"Loading TensorRT engine: {engine_path}") - self.engine.load() - self.engine.activate() - self.engine_label = engine - else: - logger(f"Using cached TensorRT engine: {engine_path}") - self.engine.allocate_buffers(shape_dict=shape_dict) + # Use the provided model directly + engine = rife_trt_model + logger(f"Using loaded TensorRT engine") + + # Activate and allocate buffers for the engine + engine.activate() + engine.allocate_buffers(shape_dict=shape_dict) frames = preprocess_frames(frames) def return_middle_frame(frame_0, frame_1, timestep): timestep_t = torch.tensor([timestep], dtype=torch.float32).to(get_torch_device()) # s = time.time() - output = self.engine.infer({"img0": frame_0, "img1": frame_1, "timestep": timestep_t}, cudaStream, use_cuda_graph) + output = engine.infer({"img0": frame_0, "img1": frame_1, "timestep": timestep_t}, cudaStream, use_cuda_graph) # e = time.time() # print(f"Time taken to infer: {(e-s)*1000} ms") @@ -71,19 +194,21 @@ class RifeTensorrt: result = generate_frames_rife(frames, clear_cache_after_n_frames, multiplier, return_middle_frame) out = postprocess_frames(result) - + if not keep_model_loaded: - del self.engine, self.engine_label + engine.reset() return (out,) NODE_CLASS_MAPPINGS = { "RifeTensorrt": RifeTensorrt, + "LoadRifeTensorrtModel": LoadRifeTensorrtModel, } NODE_DISPLAY_NAME_MAPPINGS = { "RifeTensorrt": "⚡ Rife Tensorrt", + "LoadRifeTensorrtModel": "Load Rife Tensorrt Model", } __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/load_rife_config.json b/load_rife_config.json new file mode 100644 index 0000000..3c01bd0 --- /dev/null +++ b/load_rife_config.json @@ -0,0 +1,16 @@ +{ + "model": { + "options": [ + "rife47_ensemble_True_scale_1_sim", + "rife48_ensemble_True_scale_1_sim", + "rife49_ensemble_True_scale_1_sim" + ], + "default": "rife49_ensemble_True_scale_1_sim", + "tooltip": "RIFE models for video frame interpolation. These models have been tested with tensorrt. Loaded from config." + }, + "precision": { + "options": ["fp16", "fp32"], + "default": "fp16", + "tooltip": "Precision to build the tensorrt engines. Loaded from config." + } +} \ No newline at end of file diff --git a/readme.md b/readme.md index 6374357..7fc2314 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ [![python](https://img.shields.io/badge/python-3.10.12-green)](https://www.python.org/downloads/release/python-31012/) [![cuda](https://img.shields.io/badge/cuda-12.4-green)](https://developer.nvidia.com/cuda-downloads) -[![trt](https://img.shields.io/badge/TRT-10.4.0-green)](https://developer.nvidia.com/tensorrt) +[![trt](https://img.shields.io/badge/TRT-10.13.3.9-green)](https://developer.nvidia.com/tensorrt) [![by-nc-sa/4.0](https://img.shields.io/badge/license-CC--BY--NC--SA--4.0-lightgrey)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) ![node](https://github.com/user-attachments/assets/5fd6d529-300c-42a5-b9cf-46e031f0bcb5) @@ -40,27 +40,40 @@ cd ./ComfyUI-Rife-Tensorrt pip install -r requirements.txt ``` -## 🛠️ Building Tensorrt Engine +## 🛠️ Supported Models -1. Download one of the following onnx models: - - [rife49_ensemble_True_scale_1_sim.onnx](https://huggingface.co/yuvraj108c/rife-onnx/resolve/main/rife49_ensemble_True_scale_1_sim.onnx) - - [rife48_ensemble_True_scale_1_sim.onnx](https://huggingface.co/yuvraj108c/rife-onnx/resolve/main/rife48_ensemble_True_scale_1_sim.onnx) - - [rife47_ensemble_True_scale_1_sim.onnx](https://huggingface.co/yuvraj108c/rife-onnx/resolve/main/rife47_ensemble_True_scale_1_sim.onnx) -2. Edit onnx/trt paths inside [export_trt.py](./export_trt.py) and build tensorrt engine by running: - - `python export_trt.py` +The following RIFE models are supported and will be automatically downloaded and built: + - **rife49_ensemble_True_scale_1_sim** (default) - Latest and most accurate + - **rife48_ensemble_True_scale_1_sim** - Good balance of speed and quality + - **rife47_ensemble_True_scale_1_sim** - Fastest option -3. Place the exported engine inside ComfyUI `/models/tensorrt/rife` directory +Models are automatically downloaded from [HuggingFace](https://huggingface.co/yuvraj108c/rife-onnx) and TensorRT engines are built on first use. ## ☀️ Usage -- Insert node by `Right Click -> tensorrt -> Rife Tensorrt` -- Image resolutions between `256x256` and `3840x3840` will work with the tensorrt engines +1. **Load Model**: Insert `Right Click -> tensorrt -> Load Rife Tensorrt Model` + - Choose your preferred RIFE model (rife47, rife48, or rife49) + - Select precision (fp16 recommended for speed, fp32 for maximum accuracy) + - The model will be automatically downloaded and TensorRT engine built on first use + +2. **Process Frames**: Insert `Right Click -> tensorrt -> Rife Tensorrt` + - Connect the loaded model from step 1 + - Input your video frames + - Configure interpolation settings (multiplier, CUDA graph, etc.) + - Image resolutions between `256x256` and `3840x3840` are supported ## 🤖 Environment tested -- Ubuntu 22.04 LTS, Cuda 12.4, Tensorrt 10.4.0, Python 3.10, RTX 3070 GPU +- Ubuntu 22.04 LTS, Cuda 12.4, Tensorrt 10.13.3.9, Python 3.10, RTX 3070 GPU - Windows (Not tested, but should work) +## 🚨 Updates + +### December 2025 +- **Automatic Model Management**: No more manual downloads! Models are automatically downloaded from HuggingFace and TensorRT engines are built on demand +- **Improved Workflow**: New two-node system with `Load Rife Tensorrt Model` + `Rife Tensorrt` for better organization +- **Updated Dependencies**: TensorRT updated to 10.13.3.9 for better performance and compatibility + ## 👏 Credits - https://github.com/styler00dollar/VSGAN-tensorrt-docker diff --git a/requirements.txt b/requirements.txt index b479d99..f24af34 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ einops colored polygraphy -tensorrt==10.4.0 -cuda-python \ No newline at end of file +tensorrt==10.13.3.9 +cuda-python +requests +tqdm \ No newline at end of file diff --git a/utilities.py b/utilities.py new file mode 100644 index 0000000..0362ed6 --- /dev/null +++ b/utilities.py @@ -0,0 +1,101 @@ +import requests +from tqdm import tqdm +import logging +import sys + +class ColoredLogger: + COLORS = { + 'RED': '\033[91m', + 'GREEN': '\033[92m', + 'YELLOW': '\033[93m', + 'BLUE': '\033[94m', + 'MAGENTA': '\033[95m', + 'RESET': '\033[0m' + } + + LEVEL_COLORS = { + 'DEBUG': COLORS['BLUE'], + 'INFO': COLORS['GREEN'], + 'WARNING': COLORS['YELLOW'], + 'ERROR': COLORS['RED'], + 'CRITICAL': COLORS['MAGENTA'] + } + + def __init__(self, name="MY-APP"): + self.logger = logging.getLogger(name) + self.logger.setLevel(logging.DEBUG) + self.app_name = name + + # Prevent message propagation to parent loggers + self.logger.propagate = False + + # Clear existing handlers + self.logger.handlers = [] + + # Create console handler + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(logging.DEBUG) + + # Custom formatter class to handle colored components + class ColoredFormatter(logging.Formatter): + def format(self, record): + # Color the level name according to severity + level_color = ColoredLogger.LEVEL_COLORS.get(record.levelname, '') + colored_levelname = f"{level_color}{record.levelname}{ColoredLogger.COLORS['RESET']}" + + # Color the logger name in blue + colored_name = f"{ColoredLogger.COLORS['BLUE']}{record.name}{ColoredLogger.COLORS['RESET']}" + + # Set the colored components + record.levelname = colored_levelname + record.name = colored_name + + return super().format(record) + + # Create formatter with the new format + formatter = ColoredFormatter('[%(name)s|%(levelname)s] - %(message)s') + handler.setFormatter(formatter) + + self.logger.addHandler(handler) + + + def debug(self, message): + self.logger.debug(f"{self.COLORS['BLUE']}{message}{self.COLORS['RESET']}") + + def info(self, message): + self.logger.info(f"{self.COLORS['GREEN']}{message}{self.COLORS['RESET']}") + + def warning(self, message): + self.logger.warning(f"{self.COLORS['YELLOW']}{message}{self.COLORS['RESET']}") + + def error(self, message): + self.logger.error(f"{self.COLORS['RED']}{message}{self.COLORS['RESET']}") + + def critical(self, message): + self.logger.critical(f"{self.COLORS['MAGENTA']}{message}{self.COLORS['RESET']}") + +def download_file(url, save_path): + """ + Download a file from URL with progress bar + + Args: + url (str): URL of the file to download + save_path (str): Path to save the file as + """ + GREEN = '\033[92m' + RESET = '\033[0m' + response = requests.get(url, stream=True) + total_size = int(response.headers.get('content-length', 0)) + + with open(save_path, 'wb') as file, tqdm( + desc=save_path, + total=total_size, + unit='iB', + unit_scale=True, + unit_divisor=1024, + colour='green', + bar_format=f'{GREEN}{{l_bar}}{{bar}}{RESET}{GREEN}{{r_bar}}{RESET}' + ) as progress_bar: + for data in response.iter_content(chunk_size=1024): + size = file.write(data) + progress_bar.update(size) \ No newline at end of file From 8a2a1ae405bf28278703a1fdae31277d6e114d1a Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Thu, 25 Sep 2025 00:36:16 +1200 Subject: [PATCH 02/17] Update readme.md Update the Python and Cuda version images as it works with these later versions that I'm using --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 7fc2314..ad34ee7 100644 --- a/readme.md +++ b/readme.md @@ -2,8 +2,8 @@ # ComfyUI Rife TensorRT ⚡ -[![python](https://img.shields.io/badge/python-3.10.12-green)](https://www.python.org/downloads/release/python-31012/) -[![cuda](https://img.shields.io/badge/cuda-12.4-green)](https://developer.nvidia.com/cuda-downloads) +[![python](https://img.shields.io/badge/python-3.12.11-green)](https://www.python.org/downloads/release/python-31211/) +[![cuda](https://img.shields.io/badge/cuda-12.9-green)](https://developer.nvidia.com/cuda-downloads) [![trt](https://img.shields.io/badge/TRT-10.13.3.9-green)](https://developer.nvidia.com/tensorrt) [![by-nc-sa/4.0](https://img.shields.io/badge/license-CC--BY--NC--SA--4.0-lightgrey)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) From 16994ac3cba618bfc824d7a4a4cad06a255ded34 Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Thu, 25 Sep 2025 00:42:28 +1200 Subject: [PATCH 03/17] Update readme.md more readme updates --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index ad34ee7..d165869 100644 --- a/readme.md +++ b/readme.md @@ -51,12 +51,12 @@ Models are automatically downloaded from [HuggingFace](https://huggingface.co/yu ## ☀️ Usage -1. **Load Model**: Insert `Right Click -> tensorrt -> Load Rife Tensorrt Model` +1. **Load Model**: Insert `Right Click -> Add Node -> tensorrt -> Load Rife Tensorrt Model` - Choose your preferred RIFE model (rife47, rife48, or rife49) - Select precision (fp16 recommended for speed, fp32 for maximum accuracy) - The model will be automatically downloaded and TensorRT engine built on first use -2. **Process Frames**: Insert `Right Click -> tensorrt -> Rife Tensorrt` +2. **Process Frames**: Insert `Right Click -> Add Node -> tensorrt -> Rife Tensorrt` - Connect the loaded model from step 1 - Input your video frames - Configure interpolation settings (multiplier, CUDA graph, etc.) @@ -64,7 +64,7 @@ Models are automatically downloaded from [HuggingFace](https://huggingface.co/yu ## 🤖 Environment tested -- Ubuntu 22.04 LTS, Cuda 12.4, Tensorrt 10.13.3.9, Python 3.10, RTX 3070 GPU +- WSL Ubuntu 24.04.03 LTS, Cuda 12.9, Tensorrt 10.13.3.9, Python 3.12.11, RTX 5080 GPU - Windows (Not tested, but should work) ## 🚨 Updates From 3e898b65f707b65ed3b7248853697839eb94f1db Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Thu, 25 Sep 2025 07:54:13 +1200 Subject: [PATCH 04/17] fix engine reload on batch jobs when keep_model_loaded = false --- trt_utilities.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/trt_utilities.py b/trt_utilities.py index eaa680b..0a8fb68 100644 --- a/trt_utilities.py +++ b/trt_utilities.py @@ -144,11 +144,18 @@ class Engine: del self.tensors def reset(self, engine_path=None): - del self.engine - del self.context - del self.buffers - del self.tensors - self.engine_path = engine_path + if hasattr(self, 'engine') and self.engine is not None: + del self.engine + if hasattr(self, 'context') and self.context is not None: + del self.context + if hasattr(self, 'buffers'): + del self.buffers + if hasattr(self, 'tensors'): + del self.tensors + + self.engine = None + self.context = None + self.engine_path = engine_path if engine_path else self.engine_path self.buffers = OrderedDict() self.tensors = OrderedDict() @@ -220,6 +227,10 @@ class Engine: self.engine = engine_from_bytes(bytes_from_path(self.engine_path)) def activate(self, reuse_device_memory=None): + # If engine was reset, reload it + if self.engine is None: + self.load() + if reuse_device_memory: self.context = self.engine.create_execution_context_without_device_memory() # self.context.device_memory = reuse_device_memory From 6cd119d9e558228c5bbeb7d07bfa096dc5bbbabc Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Thu, 25 Sep 2025 08:16:02 +1200 Subject: [PATCH 05/17] fix memory issues with graphs when keep_model_loaded = true --- trt_utilities.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/trt_utilities.py b/trt_utilities.py index 0a8fb68..a41d338 100644 --- a/trt_utilities.py +++ b/trt_utilities.py @@ -136,14 +136,41 @@ class Engine: self.buffers = OrderedDict() self.tensors = OrderedDict() self.cuda_graph_instance = None # cuda graph + self.graph = None def __del__(self): + # Clean up CUDA graph resources + if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: + try: + cudart.cudaGraphDestroy(self.cuda_graph_instance) + except: + pass + if hasattr(self, 'graph') and self.graph is not None: + try: + cudart.cudaGraphDestroy(self.graph) + except: + pass + del self.engine del self.context del self.buffers del self.tensors def reset(self, engine_path=None): + # Clean up CUDA graph resources first + if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: + try: + cudart.cudaGraphDestroy(self.cuda_graph_instance) + except: + pass + self.cuda_graph_instance = None + if hasattr(self, 'graph') and self.graph is not None: + try: + cudart.cudaGraphDestroy(self.graph) + except: + pass + self.graph = None + if hasattr(self, 'engine') and self.engine is not None: del self.engine if hasattr(self, 'context') and self.context is not None: @@ -238,6 +265,20 @@ class Engine: self.context = self.engine.create_execution_context() def allocate_buffers(self, shape_dict=None, device="cuda"): + # Clean up CUDA graph resources since tensors will be recreated + if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: + try: + cudart.cudaGraphDestroy(self.cuda_graph_instance) + except: + pass + self.cuda_graph_instance = None + if hasattr(self, 'graph') and self.graph is not None: + try: + cudart.cudaGraphDestroy(self.graph) + except: + pass + self.graph = None + nvtx.range_push("allocate_buffers") for idx in range(self.engine.num_io_tensors): name = self.engine.get_tensor_name(idx) From f0a75cb1b1125ba8262cdf32205fe6d325a4550f Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:34:43 +1300 Subject: [PATCH 06/17] Update requirements.txt for dependency management Reordered and uncommented dependencies in requirements.txt --- requirements.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f24af34..1a8e86e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,9 @@ einops colored polygraphy -tensorrt==10.13.3.9 +#tensorrt==10.13.3.9 +tensorrt cuda-python requests -tqdm \ No newline at end of file + +tqdm From 03b2a8e9d6ed790407925d55ae823bd6221d3673 Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 14:37:45 +1200 Subject: [PATCH 07/17] Fix torch.load to load directly to GPU device Without map_location, torch.load defaults to CPU then the model is moved to GPU with .to(TORCH_DEVICE). This is inefficient and can cause issues on systems where CPU tensors aren't properly set up. Co-Authored-By: Claude Opus 4.7 --- export_onnx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/export_onnx.py b/export_onnx.py index 97f2948..62b7d54 100644 --- a/export_onnx.py +++ b/export_onnx.py @@ -87,7 +87,7 @@ def export_onnx(ckpt_name, ensemble, scale_factor): model_path = load_file_from_github_release(MODEL_TYPE, ckpt_name) arch_ver = CKPT_NAME_VER_DICT[ckpt_name] interpolation_model = IFNet(arch_ver=arch_ver) - interpolation_model.load_state_dict(torch.load(model_path)) + interpolation_model.load_state_dict(torch.load(model_path, map_location=TORCH_DEVICE)) interpolation_model.eval().to(TORCH_DEVICE) # # dummy data From 65abe2691ab637973f95d57a22e0fff0450ef97b Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 14:44:37 +1200 Subject: [PATCH 08/17] minimax trial 1 --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 236f609..9881d64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ models __pycache__ -.vscode \ No newline at end of file +.vscode +CLAUDE.md From 3c3bac0dea99267ef7de6096ac39effaa13ad6ca Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 15:17:31 +1200 Subject: [PATCH 09/17] Fix output frame count message to show actual total Previously reported only new interpolated frames using a formula, not the actual count. Now shows out_len which reflects true total frames output. Co-Authored-By: Claude Opus 4.7 --- vfi_utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vfi_utilities.py b/vfi_utilities.py index f9ec616..34637ea 100644 --- a/vfi_utilities.py +++ b/vfi_utilities.py @@ -74,7 +74,7 @@ def generate_frames_rife( # Append final frame output_frames[out_len] = frames[-1:] - logger(f"done! - {(len(frames) -1) * (multiplier-1)} new frames generated at resolution: {output_frames[0].shape}") + logger(f"done! - {out_len} total frames output at resolution: {output_frames[0].shape}") out_len += 1 # clear cache for courtesy From 203afb5f2aa8e56f4a794f83a4d051e421d2516f Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 15:40:57 +1200 Subject: [PATCH 10/17] Improve CUDA graph memory cleanup in Engine class - __del__: Add hasattr checks before del, iterate tensors dict to delete each tensor individually, also clean up inputs/outputs - reset: Same improvements plus reset cuda_graph_instance and graph to None to prevent stale state on reuse Co-Authored-By: Claude Opus 4.7 --- trt_utilities.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/trt_utilities.py b/trt_utilities.py index a41d338..4a12561 100644 --- a/trt_utilities.py +++ b/trt_utilities.py @@ -151,10 +151,20 @@ class Engine: except: pass - del self.engine - del self.context - del self.buffers - del self.tensors + if hasattr(self, 'engine'): + del self.engine + if hasattr(self, 'context'): + del self.context + if hasattr(self, 'tensors'): + for key in list(self.tensors.keys()): + del self.tensors[key] + del self.tensors + if hasattr(self, 'buffers'): + del self.buffers + if hasattr(self, 'inputs'): + del self.inputs + if hasattr(self, 'outputs'): + del self.outputs def reset(self, engine_path=None): # Clean up CUDA graph resources first @@ -175,10 +185,12 @@ class Engine: del self.engine if hasattr(self, 'context') and self.context is not None: del self.context + if hasattr(self, 'tensors'): + for key in list(self.tensors.keys()): + del self.tensors[key] + del self.tensors if hasattr(self, 'buffers'): del self.buffers - if hasattr(self, 'tensors'): - del self.tensors self.engine = None self.context = None @@ -188,6 +200,8 @@ class Engine: self.tensors = OrderedDict() self.inputs = {} self.outputs = {} + self.cuda_graph_instance = None + self.graph = None def build( self, From 7cf8989cd3a6f1c4bb1184a13a930da28b89924d Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 18:39:17 +1200 Subject: [PATCH 11/17] Improve cuda graph tooltip with RAM/resolution caveats Advises users to disable cuda graph if experiencing high RAM usage or errors with variable input resolutions. Co-Authored-By: Claude Opus 4.7 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 34bd62c..0d04dc9 100644 --- a/__init__.py +++ b/__init__.py @@ -144,7 +144,7 @@ class RifeTensorrt: "rife_trt_model": ("RIFE_TRT_MODEL", {"tooltip": "Tensorrt model built and loaded"}), "clear_cache_after_n_frames": ("INT", {"default": 100, "min": 1, "max": 1000, "tooltip": "Clear CUDA cache after processing this many frames"}), "multiplier": ("INT", {"default": 2, "min": 1, "tooltip": "Frame interpolation multiplier"}), - "use_cuda_graph": ("BOOLEAN", {"default": True, "tooltip": "Use CUDA graph for better performance"}), + "use_cuda_graph": ("BOOLEAN", {"default": True, "tooltip": "Use CUDA graph for better performance. Disable if experiencing high RAM usage or errors with variable input resolutions."}), "keep_model_loaded": ("BOOLEAN", {"default": False, "tooltip": "Keep model loaded in memory after processing"}), }, } From eedc0938f9336fb574c0e9d3c8a5167dc0e56dcc Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 19:04:41 +1200 Subject: [PATCH 12/17] Fix bare exceptions, silent HTTP failures, and resolution display - Replace bare except: with except Exception: in __del__ and reset() to avoid catching KeyboardInterrupt/SystemExit - Add response.raise_for_status() to fail clearly on HTTP errors - Fix resolution log output to show HxW instead of CHW shape Co-Authored-By: Claude Opus 4.7 --- trt_utilities.py | 12 ++++++------ utilities.py | 1 + vfi_utilities.py | 5 ++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/trt_utilities.py b/trt_utilities.py index 4a12561..23a66fe 100644 --- a/trt_utilities.py +++ b/trt_utilities.py @@ -143,12 +143,12 @@ class Engine: if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: try: cudart.cudaGraphDestroy(self.cuda_graph_instance) - except: + except Exception: pass if hasattr(self, 'graph') and self.graph is not None: try: cudart.cudaGraphDestroy(self.graph) - except: + except Exception: pass if hasattr(self, 'engine'): @@ -171,13 +171,13 @@ class Engine: if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: try: cudart.cudaGraphDestroy(self.cuda_graph_instance) - except: + except Exception: pass self.cuda_graph_instance = None if hasattr(self, 'graph') and self.graph is not None: try: cudart.cudaGraphDestroy(self.graph) - except: + except Exception: pass self.graph = None @@ -283,13 +283,13 @@ class Engine: if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: try: cudart.cudaGraphDestroy(self.cuda_graph_instance) - except: + except Exception: pass self.cuda_graph_instance = None if hasattr(self, 'graph') and self.graph is not None: try: cudart.cudaGraphDestroy(self.graph) - except: + except Exception: pass self.graph = None diff --git a/utilities.py b/utilities.py index 0362ed6..f95e841 100644 --- a/utilities.py +++ b/utilities.py @@ -85,6 +85,7 @@ def download_file(url, save_path): GREEN = '\033[92m' RESET = '\033[0m' response = requests.get(url, stream=True) + response.raise_for_status() total_size = int(response.headers.get('content-length', 0)) with open(save_path, 'wb') as file, tqdm( diff --git a/vfi_utilities.py b/vfi_utilities.py index 34637ea..4cd3d33 100644 --- a/vfi_utilities.py +++ b/vfi_utilities.py @@ -74,7 +74,10 @@ def generate_frames_rife( # Append final frame output_frames[out_len] = frames[-1:] - logger(f"done! - {out_len} total frames output at resolution: {output_frames[0].shape}") + # Get actual frame shape from first interpolated frame (CHW format) + actual_frame = output_frames[0] + h, w = actual_frame.shape[1], actual_frame.shape[2] + logger(f"done! - {out_len} total frames output at resolution: {h}x{w}") out_len += 1 # clear cache for courtesy From 154310e3e99b91de63b502dff4fc732538763693 Mon Sep 17 00:00:00 2001 From: reaperhammer <87021961+reaperhammer@users.noreply.github.com> Date: Sat, 9 May 2026 20:09:54 +1200 Subject: [PATCH 13/17] Relax requirements to use >= pinning and add missing deps - Remove dead #tensorrt==10.13.3.9 comment - Add onnx and onnxsim for export scripts - Pin minimum versions to ensure compatibility Co-Authored-By: Claude Opus 4.7 --- requirements.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1a8e86e..a8ac367 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,10 @@ -einops -colored -polygraphy -#tensorrt==10.13.3.9 -tensorrt -cuda-python -requests - -tqdm +einops>=0.8.0 +colored>=1.1.0 +polygraphy>=0.49.0 +tensorrt>=10.12.0 +cuda-python>=12.0.0 +requests>=2.31.0 +tqdm>=4.66.0 +onnx>=1.20.0 +onnxsim>=0.5.0 +torch>=2.9.0 \ No newline at end of file From 38cd1fe2fda982a1281596a6627068591ec3f813 Mon Sep 17 00:00:00 2001 From: yuvraj108c Date: Mon, 8 Jun 2026 05:04:45 +0000 Subject: [PATCH 14/17] refactor + auto engine building + remove cuda --- __init__.py | 204 +---------------------- nodes/load_rife_tensorrt.py | 91 ++++++++++ nodes/rife_tensorrt.py | 56 +++++++ requirements.txt | 14 +- export_onnx.py => scripts/export_onnx.py | 0 export_trt.py => scripts/export_trt.py | 0 trt_utilities.py | 154 +++++------------ utilities.py | 41 ++++- 8 files changed, 237 insertions(+), 323 deletions(-) create mode 100644 nodes/load_rife_tensorrt.py create mode 100644 nodes/rife_tensorrt.py rename export_onnx.py => scripts/export_onnx.py (100%) rename export_trt.py => scripts/export_trt.py (100%) diff --git a/__init__.py b/__init__.py index 0d04dc9..0ead840 100644 --- a/__init__.py +++ b/__init__.py @@ -1,205 +1,5 @@ -import torch -import os -from comfy.model_management import get_torch_device -from .vfi_utilities import preprocess_frames, postprocess_frames, generate_frames_rife, logger -from .trt_utilities import Engine -from .utilities import download_file, ColoredLogger -import folder_paths -import time -from polygraphy import cuda -import comfy.model_management as mm -import tensorrt -import json - -ENGINE_DIR = os.path.join(folder_paths.models_dir, "tensorrt", "rife") - -# Image dimensions for TensorRT engine building -IMAGE_DIM_MIN = 256 -IMAGE_DIM_OPT = 512 -IMAGE_DIM_MAX = 3840 - -# Logger for this module -rife_logger = ColoredLogger("ComfyUI-Rife-Tensorrt") - -# Function to load configuration -def load_node_config(config_filename="load_rife_config.json"): - """Loads node configuration from a JSON file.""" - current_dir = os.path.dirname(__file__) - config_path = os.path.join(current_dir, config_filename) - - default_config = { - "model": { - "options": ["rife49_ensemble_True_scale_1_sim"], - "default": "rife49_ensemble_True_scale_1_sim", - "tooltip": "Default model (fallback from code)" - }, - "precision": { - "options": ["fp16", "fp32"], - "default": "fp16", - "tooltip": "Default precision (fallback from code)" - } - } - - try: - with open(config_path, 'r') as f: - config = json.load(f) - rife_logger.info(f"Successfully loaded configuration from {config_filename}") - return config - except FileNotFoundError: - rife_logger.warning(f"Configuration file '{config_path}' not found. Using default fallback configuration.") - return default_config - except json.JSONDecodeError: - rife_logger.error(f"Error decoding JSON from '{config_path}'. Using default fallback configuration.") - return default_config - except Exception as e: - rife_logger.error(f"An unexpected error occurred while loading '{config_path}': {e}. Using default fallback.") - return default_config - -# Load the configuration once when the module is imported -LOAD_RIFE_NODE_CONFIG = load_node_config() - -class LoadRifeTensorrtModel: - @classmethod - def INPUT_TYPES(cls): - # Use the pre-loaded configuration - model_config = LOAD_RIFE_NODE_CONFIG.get("model", {}) - precision_config = LOAD_RIFE_NODE_CONFIG.get("precision", {}) - - # Provide sensible defaults if keys are missing in the config - model_options = model_config.get("options", ["rife49_ensemble_True_scale_1_sim"]) - model_default = model_config.get("default", "rife49_ensemble_True_scale_1_sim") - model_tooltip = model_config.get("tooltip", "Select a RIFE model.") - - precision_options = precision_config.get("options", ["fp16", "fp32"]) - precision_default = precision_config.get("default", "fp16") - precision_tooltip = precision_config.get("tooltip", "Select precision.") - - return { - "required": { - "model": (model_options, {"default": model_default, "tooltip": model_tooltip}), - "precision": (precision_options, {"default": precision_default, "tooltip": precision_tooltip}), - } - } - - RETURN_NAMES = ("rife_trt_model",) - RETURN_TYPES = ("RIFE_TRT_MODEL",) - CATEGORY = "tensorrt" - DESCRIPTION = "Load RIFE tensorrt models, they will be built automatically if not found." - FUNCTION = "load_rife_tensorrt_model" - - def load_rife_tensorrt_model(self, model, precision): - tensorrt_models_dir = os.path.join(folder_paths.models_dir, "tensorrt", "rife") - onnx_models_dir = os.path.join(folder_paths.models_dir, "onnx") - - os.makedirs(tensorrt_models_dir, exist_ok=True) - os.makedirs(onnx_models_dir, exist_ok=True) - - onnx_model_path = os.path.join(onnx_models_dir, f"{model}.onnx") - - # Build tensorrt model path with detailed naming - engine_channel = 3 - engine_min_batch, engine_opt_batch, engine_max_batch = 1, 1, 1 - engine_min_h, engine_opt_h, engine_max_h = IMAGE_DIM_MIN, IMAGE_DIM_OPT, IMAGE_DIM_MAX - engine_min_w, engine_opt_w, engine_max_w = IMAGE_DIM_MIN, IMAGE_DIM_OPT, IMAGE_DIM_MAX - tensorrt_model_path = os.path.join(tensorrt_models_dir, f"{model}_{precision}_{engine_min_batch}x{engine_channel}x{engine_min_h}x{engine_min_w}_{engine_opt_batch}x{engine_channel}x{engine_opt_h}x{engine_opt_w}_{engine_max_batch}x{engine_channel}x{engine_max_h}x{engine_max_w}_{tensorrt.__version__}.trt") - - if not os.path.exists(tensorrt_model_path): - if not os.path.exists(onnx_model_path): - onnx_model_download_url = f"https://huggingface.co/yuvraj108c/rife-onnx/resolve/main/{model}.onnx" - rife_logger.info(f"Downloading {onnx_model_download_url}") - download_file(url=onnx_model_download_url, save_path=onnx_model_path) - else: - rife_logger.info(f"ONNX model found at: {onnx_model_path}") - - rife_logger.info(f"Building TensorRT engine for {onnx_model_path}: {tensorrt_model_path}") - mm.soft_empty_cache() - s = time.time() - engine = Engine(tensorrt_model_path) - engine.build( - onnx_path=onnx_model_path, - fp16=True if precision == "fp16" else False, - input_profile=[ - { - "img0": [(engine_min_batch, engine_channel, engine_min_h, engine_min_w), (engine_opt_batch, engine_channel, engine_opt_h, engine_opt_w), (engine_max_batch, engine_channel, engine_max_h, engine_max_w)], - "img1": [(engine_min_batch, engine_channel, engine_min_h, engine_min_w), (engine_opt_batch, engine_channel, engine_opt_h, engine_opt_w), (engine_max_batch, engine_channel, engine_max_h, engine_max_w)], - } - ], - ) - e = time.time() - rife_logger.info(f"Time taken to build: {(e-s)} seconds") - - rife_logger.info(f"Loading TensorRT engine: {tensorrt_model_path}") - mm.soft_empty_cache() - engine = Engine(tensorrt_model_path) - engine.load() - - return (engine,) - -class RifeTensorrt: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "frames": ("IMAGE", {"tooltip": "Input frames for video frame interpolation"}), - "rife_trt_model": ("RIFE_TRT_MODEL", {"tooltip": "Tensorrt model built and loaded"}), - "clear_cache_after_n_frames": ("INT", {"default": 100, "min": 1, "max": 1000, "tooltip": "Clear CUDA cache after processing this many frames"}), - "multiplier": ("INT", {"default": 2, "min": 1, "tooltip": "Frame interpolation multiplier"}), - "use_cuda_graph": ("BOOLEAN", {"default": True, "tooltip": "Use CUDA graph for better performance. Disable if experiencing high RAM usage or errors with variable input resolutions."}), - "keep_model_loaded": ("BOOLEAN", {"default": False, "tooltip": "Keep model loaded in memory after processing"}), - }, - } - - RETURN_TYPES = ("IMAGE", ) - FUNCTION = "vfi" - CATEGORY = "tensorrt" - OUTPUT_NODE=True - - def vfi( - self, - frames, - rife_trt_model, - clear_cache_after_n_frames=100, - multiplier=2, - use_cuda_graph=True, - keep_model_loaded=False, - ): - B, H, W, C = frames.shape - shape_dict = { - "img0": {"shape": (1, 3, H, W)}, - "img1": {"shape": (1, 3, H, W)}, - "output": {"shape": (1, 3, H, W)}, - } - - cudaStream = cuda.Stream() - - # Use the provided model directly - engine = rife_trt_model - logger(f"Using loaded TensorRT engine") - - # Activate and allocate buffers for the engine - engine.activate() - engine.allocate_buffers(shape_dict=shape_dict) - - frames = preprocess_frames(frames) - - def return_middle_frame(frame_0, frame_1, timestep): - timestep_t = torch.tensor([timestep], dtype=torch.float32).to(get_torch_device()) - # s = time.time() - output = engine.infer({"img0": frame_0, "img1": frame_1, "timestep": timestep_t}, cudaStream, use_cuda_graph) - # e = time.time() - # print(f"Time taken to infer: {(e-s)*1000} ms") - - result = output['output'] - return result - - result = generate_frames_rife(frames, clear_cache_after_n_frames, multiplier, return_middle_frame) - out = postprocess_frames(result) - - if not keep_model_loaded: - engine.reset() - - return (out,) - +from .nodes.load_rife_tensorrt import LoadRifeTensorrtModel +from .nodes.rife_tensorrt import RifeTensorrt NODE_CLASS_MAPPINGS = { "RifeTensorrt": RifeTensorrt, diff --git a/nodes/load_rife_tensorrt.py b/nodes/load_rife_tensorrt.py new file mode 100644 index 0000000..3be2d8b --- /dev/null +++ b/nodes/load_rife_tensorrt.py @@ -0,0 +1,91 @@ +from ..trt_utilities import Engine +from ..utilities import download_file, load_node_config, rife_logger +import folder_paths +import time +import comfy.model_management as mm +import tensorrt +import os + +# Image dimensions for TensorRT engine building +IMAGE_DIM_MIN = 256 +IMAGE_DIM_OPT = 512 +IMAGE_DIM_MAX = 3840 + +LOAD_RIFE_NODE_CONFIG = load_node_config() + +class LoadRifeTensorrtModel: + @classmethod + def INPUT_TYPES(cls): + # Use the pre-loaded configuration + model_config = LOAD_RIFE_NODE_CONFIG.get("model", {}) + precision_config = LOAD_RIFE_NODE_CONFIG.get("precision", {}) + + # Provide sensible defaults if keys are missing in the config + model_options = model_config.get("options", ["rife49_ensemble_True_scale_1_sim"]) + model_default = model_config.get("default", "rife49_ensemble_True_scale_1_sim") + model_tooltip = model_config.get("tooltip", "Select a RIFE model.") + + precision_options = precision_config.get("options", ["fp16", "fp32"]) + precision_default = precision_config.get("default", "fp16") + precision_tooltip = precision_config.get("tooltip", "Select precision.") + + return { + "required": { + "model": (model_options, {"default": model_default, "tooltip": model_tooltip}), + "precision": (precision_options, {"default": precision_default, "tooltip": precision_tooltip}), + } + } + + RETURN_NAMES = ("rife_trt_model",) + RETURN_TYPES = ("RIFE_TRT_MODEL",) + CATEGORY = "tensorrt" + DESCRIPTION = "Load RIFE tensorrt models, they will be built automatically if not found." + FUNCTION = "load_rife_tensorrt_model" + + def load_rife_tensorrt_model(self, model, precision): + tensorrt_models_dir = os.path.join(folder_paths.models_dir, "tensorrt", "rife") + onnx_models_dir = os.path.join(folder_paths.models_dir, "onnx") + + os.makedirs(tensorrt_models_dir, exist_ok=True) + os.makedirs(onnx_models_dir, exist_ok=True) + + onnx_model_path = os.path.join(onnx_models_dir, f"{model}.onnx") + + # Build tensorrt model path with detailed naming + engine_channel = 3 + engine_min_batch, engine_opt_batch, engine_max_batch = 1, 1, 1 + engine_min_h, engine_opt_h, engine_max_h = IMAGE_DIM_MIN, IMAGE_DIM_OPT, IMAGE_DIM_MAX + engine_min_w, engine_opt_w, engine_max_w = IMAGE_DIM_MIN, IMAGE_DIM_OPT, IMAGE_DIM_MAX + tensorrt_model_path = os.path.join(tensorrt_models_dir, f"{model}_{precision}_{engine_min_batch}x{engine_channel}x{engine_min_h}x{engine_min_w}_{engine_opt_batch}x{engine_channel}x{engine_opt_h}x{engine_opt_w}_{engine_max_batch}x{engine_channel}x{engine_max_h}x{engine_max_w}_{tensorrt.__version__}.trt") + + if not os.path.exists(tensorrt_model_path): + if not os.path.exists(onnx_model_path): + onnx_model_download_url = f"https://huggingface.co/yuvraj108c/rife-onnx/resolve/main/{model}.onnx" + rife_logger.info(f"Downloading {onnx_model_download_url}") + download_file(url=onnx_model_download_url, save_path=onnx_model_path) + else: + rife_logger.info(f"ONNX model found at: {onnx_model_path}") + + rife_logger.info(f"Building TensorRT engine for {onnx_model_path}: {tensorrt_model_path}") + mm.soft_empty_cache() + s = time.time() + engine = Engine(tensorrt_model_path) + engine.build( + onnx_path=onnx_model_path, + fp16=True if precision == "fp16" else False, + input_profile=[ + { + "img0": [(engine_min_batch, engine_channel, engine_min_h, engine_min_w), (engine_opt_batch, engine_channel, engine_opt_h, engine_opt_w), (engine_max_batch, engine_channel, engine_max_h, engine_max_w)], + "img1": [(engine_min_batch, engine_channel, engine_min_h, engine_min_w), (engine_opt_batch, engine_channel, engine_opt_h, engine_opt_w), (engine_max_batch, engine_channel, engine_max_h, engine_max_w)], + } + ], + ) + e = time.time() + rife_logger.info(f"Time taken to build: {(e-s)} seconds") + + rife_logger.info(f"Loading TensorRT engine: {tensorrt_model_path}") + mm.soft_empty_cache() + engine = Engine(tensorrt_model_path) + engine.load() + + return (engine,) diff --git a/nodes/rife_tensorrt.py b/nodes/rife_tensorrt.py new file mode 100644 index 0000000..81f2397 --- /dev/null +++ b/nodes/rife_tensorrt.py @@ -0,0 +1,56 @@ +import torch +import os +from comfy.model_management import get_torch_device +from ..vfi_utilities import preprocess_frames, postprocess_frames, generate_frames_rife +from ..trt_utilities import Engine +import folder_paths +import time +import comfy.model_management as mm + +class RifeTensorrt: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "frames": ("IMAGE", {"tooltip": "Input frames for video frame interpolation"}), + "rife_trt_model": ("RIFE_TRT_MODEL", {"tooltip": "Tensorrt model built and loaded"}), + "clear_cache_after_n_frames": ("INT", {"default": 100, "min": 1, "max": 1000, "tooltip": "Clear CUDA cache after processing this many frames"}), + "multiplier": ("INT", {"default": 2, "min": 1, "tooltip": "Frame interpolation multiplier"}), + }, + } + + RETURN_TYPES = ("IMAGE", ) + FUNCTION = "vfi" + CATEGORY = "tensorrt" + + def vfi( + self, + frames, + rife_trt_model, + clear_cache_after_n_frames=100, + multiplier=2, + ): + B, H, W, C = frames.shape + shape_dict = { + "img0": {"shape": (1, 3, H, W)}, + "img1": {"shape": (1, 3, H, W)}, + "output": {"shape": (1, 3, H, W)}, + } + + cudaStream = torch.cuda.current_stream().cuda_stream + engine = rife_trt_model + engine.activate() + engine.allocate_buffers(shape_dict=shape_dict) + + frames = preprocess_frames(frames) + + def return_middle_frame(frame_0, frame_1, timestep): + timestep_t = torch.tensor([timestep], dtype=torch.float32).to(get_torch_device()) + output = engine.infer({"img0": frame_0, "img1": frame_1, "timestep": timestep_t}, cudaStream, use_cuda_graph) + result = output['output'] + return result + + result = generate_frames_rife(frames, clear_cache_after_n_frames, multiplier, return_middle_frame) + out = postprocess_frames(result) + + return (out,) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a8ac367..c222890 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,4 @@ -einops>=0.8.0 -colored>=1.1.0 -polygraphy>=0.49.0 -tensorrt>=10.12.0 -cuda-python>=12.0.0 -requests>=2.31.0 -tqdm>=4.66.0 -onnx>=1.20.0 -onnxsim>=0.5.0 -torch>=2.9.0 \ No newline at end of file +einops +colored +polygraphy +tensorrt \ No newline at end of file diff --git a/export_onnx.py b/scripts/export_onnx.py similarity index 100% rename from export_onnx.py rename to scripts/export_onnx.py diff --git a/export_trt.py b/scripts/export_trt.py similarity index 100% rename from export_trt.py rename to scripts/export_trt.py diff --git a/trt_utilities.py b/trt_utilities.py index 23a66fe..3ea5567 100644 --- a/trt_utilities.py +++ b/trt_utilities.py @@ -1,3 +1,20 @@ +# +# Copyright 2022 The HuggingFace Inc. team. +# SPDX-FileCopyrightText: Copyright (c) 1993-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# import torch from torch.cuda import nvtx from collections import OrderedDict @@ -16,7 +33,6 @@ import tensorrt as trt from logging import error, warning from tqdm import tqdm import copy -import cuda.bindings.runtime as cudart TRT_LOGGER = trt.Logger(trt.Logger.ERROR) G_LOGGER.module_severity = G_LOGGER.ERROR @@ -44,17 +60,6 @@ torch_to_numpy_dtype_dict = { value: key for (key, value) in numpy_to_torch_dtype_dict.items() } -# https://github.com/Jeff-LiangF/streamv2v/blob/18c1a3bd56ff348d54a3300605936980bb13b03c/src/streamv2v/acceleration/tensorrt/utilities.py -def CUASSERT(cuda_ret): - err = cuda_ret[0] - if err != cudart.cudaError_t.cudaSuccess: - raise RuntimeError( - f"CUDA ERROR: {err}, error code reference: https://nvidia.github.io/cuda-python/module/cudart.html#cuda.cudart.cudaError_t" - ) - if len(cuda_ret) > 1: - return cuda_ret[1] - return None - class TQDMProgressMonitor(trt.IProgressMonitor): def __init__(self): trt.IProgressMonitor.__init__(self) @@ -125,6 +130,7 @@ class TQDMProgressMonitor(trt.IProgressMonitor): # There is no need to propagate this exception to TensorRT. We can simply cancel the build. return False + class Engine: def __init__( self, @@ -136,72 +142,24 @@ class Engine: self.buffers = OrderedDict() self.tensors = OrderedDict() self.cuda_graph_instance = None # cuda graph - self.graph = None def __del__(self): - # Clean up CUDA graph resources - if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: - try: - cudart.cudaGraphDestroy(self.cuda_graph_instance) - except Exception: - pass - if hasattr(self, 'graph') and self.graph is not None: - try: - cudart.cudaGraphDestroy(self.graph) - except Exception: - pass - - if hasattr(self, 'engine'): - del self.engine - if hasattr(self, 'context'): - del self.context - if hasattr(self, 'tensors'): - for key in list(self.tensors.keys()): - del self.tensors[key] - del self.tensors - if hasattr(self, 'buffers'): - del self.buffers - if hasattr(self, 'inputs'): - del self.inputs - if hasattr(self, 'outputs'): - del self.outputs + del self.engine + del self.context + del self.buffers + del self.tensors def reset(self, engine_path=None): - # Clean up CUDA graph resources first - if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: - try: - cudart.cudaGraphDestroy(self.cuda_graph_instance) - except Exception: - pass - self.cuda_graph_instance = None - if hasattr(self, 'graph') and self.graph is not None: - try: - cudart.cudaGraphDestroy(self.graph) - except Exception: - pass - self.graph = None - - if hasattr(self, 'engine') and self.engine is not None: - del self.engine - if hasattr(self, 'context') and self.context is not None: - del self.context - if hasattr(self, 'tensors'): - for key in list(self.tensors.keys()): - del self.tensors[key] - del self.tensors - if hasattr(self, 'buffers'): - del self.buffers - - self.engine = None - self.context = None - self.engine_path = engine_path if engine_path else self.engine_path + del self.engine + del self.context + del self.buffers + del self.tensors + self.engine_path = engine_path self.buffers = OrderedDict() self.tensors = OrderedDict() self.inputs = {} self.outputs = {} - self.cuda_graph_instance = None - self.graph = None def build( self, @@ -214,7 +172,7 @@ class Engine: timing_cache=None, update_output_names=None, ): - print(f"Building TensorRT engine for {onnx_path}: {self.engine_path}") + # print(f"Building TensorRT engine for {onnx_path}: {self.engine_path}") p = [Profile()] if input_profile: p = [Profile() for i in range(len(input_profile))] @@ -265,13 +223,10 @@ class Engine: return 0 def load(self): + # print(f"Loading TensorRT engine: {self.engine_path}") self.engine = engine_from_bytes(bytes_from_path(self.engine_path)) def activate(self, reuse_device_memory=None): - # If engine was reset, reload it - if self.engine is None: - self.load() - if reuse_device_memory: self.context = self.engine.create_execution_context_without_device_memory() # self.context.device_memory = reuse_device_memory @@ -279,20 +234,6 @@ class Engine: self.context = self.engine.create_execution_context() def allocate_buffers(self, shape_dict=None, device="cuda"): - # Clean up CUDA graph resources since tensors will be recreated - if hasattr(self, 'cuda_graph_instance') and self.cuda_graph_instance is not None: - try: - cudart.cudaGraphDestroy(self.cuda_graph_instance) - except Exception: - pass - self.cuda_graph_instance = None - if hasattr(self, 'graph') and self.graph is not None: - try: - cudart.cudaGraphDestroy(self.graph) - except Exception: - pass - self.graph = None - nvtx.range_push("allocate_buffers") for idx in range(self.engine.num_io_tensors): name = self.engine.get_tensor_name(idx) @@ -312,32 +253,25 @@ class Engine: nvtx.range_pop() def infer(self, feed_dict, stream, use_cuda_graph=False): + nvtx.range_push("set_tensors") for name, buf in feed_dict.items(): self.tensors[name].copy_(buf) for name, tensor in self.tensors.items(): self.context.set_tensor_address(name, tensor.data_ptr()) - - if use_cuda_graph: - if self.cuda_graph_instance is not None: - CUASSERT(cudart.cudaGraphLaunch(self.cuda_graph_instance, stream.ptr)) - CUASSERT(cudart.cudaStreamSynchronize(stream.ptr)) - else: - # do inference before CUDA graph capture - noerror = self.context.execute_async_v3(stream.ptr) - if not noerror: - raise ValueError("ERROR: inference failed.") - # capture cuda graph - CUASSERT( - cudart.cudaStreamBeginCapture(stream.ptr, cudart.cudaStreamCaptureMode.cudaStreamCaptureModeGlobal) - ) - self.context.execute_async_v3(stream.ptr) - self.graph = CUASSERT(cudart.cudaStreamEndCapture(stream.ptr)) - self.cuda_graph_instance = CUASSERT(cudart.cudaGraphInstantiate(self.graph, 0)) - else: - noerror = self.context.execute_async_v3(stream.ptr) - if not noerror: - raise ValueError("ERROR: inference failed.") - + nvtx.range_pop() + nvtx.range_push("execute") + noerror = self.context.execute_async_v3(stream) + if not noerror: + raise ValueError("ERROR: inference failed.") + nvtx.range_pop() return self.tensors + def __str__(self): + out = "" + for opt_profile in range(self.engine.num_optimization_profiles): + for binding_idx in range(self.engine.num_bindings): + name = self.engine.get_binding_name(binding_idx) + shape = self.engine.get_profile_shape(opt_profile, name) + out += f"\t{name} = {shape}\n" + return out \ No newline at end of file diff --git a/utilities.py b/utilities.py index f95e841..4d805d0 100644 --- a/utilities.py +++ b/utilities.py @@ -2,6 +2,8 @@ import requests from tqdm import tqdm import logging import sys +import json +import os class ColoredLogger: COLORS = { @@ -74,6 +76,8 @@ class ColoredLogger: def critical(self, message): self.logger.critical(f"{self.COLORS['MAGENTA']}{message}{self.COLORS['RESET']}") +rife_logger = ColoredLogger("ComfyUI-Rife-Tensorrt") + def download_file(url, save_path): """ Download a file from URL with progress bar @@ -99,4 +103,39 @@ def download_file(url, save_path): ) as progress_bar: for data in response.iter_content(chunk_size=1024): size = file.write(data) - progress_bar.update(size) \ No newline at end of file + progress_bar.update(size) + + +# Function to load configuration +def load_node_config(config_filename="load_rife_config.json"): + """Loads node configuration from a JSON file.""" + current_dir = os.path.dirname(__file__) + config_path = os.path.join(current_dir, config_filename) + + default_config = { + "model": { + "options": ["rife49_ensemble_True_scale_1_sim"], + "default": "rife49_ensemble_True_scale_1_sim", + "tooltip": "Default model (fallback from code)" + }, + "precision": { + "options": ["fp16", "fp32"], + "default": "fp16", + "tooltip": "Default precision (fallback from code)" + } + } + + try: + with open(config_path, 'r') as f: + config = json.load(f) + rife_logger.info(f"Successfully loaded configuration from {config_filename}") + return config + except FileNotFoundError: + rife_logger.warning(f"Configuration file '{config_path}' not found. Using default fallback configuration.") + return default_config + except json.JSONDecodeError: + rife_logger.error(f"Error decoding JSON from '{config_path}'. Using default fallback configuration.") + return default_config + except Exception as e: + rife_logger.error(f"An unexpected error occurred while loading '{config_path}': {e}. Using default fallback.") + return default_config \ No newline at end of file From ad6d4f8fe3c06e22610a3a86e6bbaeedad957e38 Mon Sep 17 00:00:00 2001 From: yuvraj108c Date: Mon, 8 Jun 2026 05:19:17 +0000 Subject: [PATCH 15/17] update readme + logging --- nodes/rife_tensorrt.py | 2 +- readme.md | 33 ++++++++++++++++++++++----------- vfi_utilities.py | 28 +++++++++++++++++++--------- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/nodes/rife_tensorrt.py b/nodes/rife_tensorrt.py index 81f2397..f85d529 100644 --- a/nodes/rife_tensorrt.py +++ b/nodes/rife_tensorrt.py @@ -46,7 +46,7 @@ class RifeTensorrt: def return_middle_frame(frame_0, frame_1, timestep): timestep_t = torch.tensor([timestep], dtype=torch.float32).to(get_torch_device()) - output = engine.infer({"img0": frame_0, "img1": frame_1, "timestep": timestep_t}, cudaStream, use_cuda_graph) + output = engine.infer({"img0": frame_0, "img1": frame_1, "timestep": timestep_t}, cudaStream) result = output['output'] return result diff --git a/readme.md b/readme.md index d165869..6c419db 100644 --- a/readme.md +++ b/readme.md @@ -2,9 +2,9 @@ # ComfyUI Rife TensorRT ⚡ -[![python](https://img.shields.io/badge/python-3.12.11-green)](https://www.python.org/downloads/release/python-31211/) -[![cuda](https://img.shields.io/badge/cuda-12.9-green)](https://developer.nvidia.com/cuda-downloads) -[![trt](https://img.shields.io/badge/TRT-10.13.3.9-green)](https://developer.nvidia.com/tensorrt) +[![python](https://img.shields.io/badge/python-3.12.3-green)](https://www.python.org/downloads/release/python-3123//) +[![cuda](https://img.shields.io/badge/cuda-13.0-green)](https://developer.nvidia.com/cuda-downloads) +[![trt](https://img.shields.io/badge/TRT-10.14.1.48-green)](https://developer.nvidia.com/tensorrt) [![by-nc-sa/4.0](https://img.shields.io/badge/license-CC--BY--NC--SA--4.0-lightgrey)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) ![node](https://github.com/user-attachments/assets/5fd6d529-300c-42a5-b9cf-46e031f0bcb5) @@ -12,11 +12,22 @@ -This project provides a [TensorRT](https://github.com/NVIDIA/TensorRT) implementation of [RIFE](https://github.com/hzwer/ECCV2022-RIFE) for ultra fast frame interpolation inside ComfyUI +## ⭐ Support +If you like my projects and wish to see updates and new features, please consider supporting me. It helps a lot! -This project is licensed under [CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/), everyone is FREE to access, use, modify and redistribute with the same license. +[![ComfyUI-Depth-Anything-Tensorrt](https://img.shields.io/badge/ComfyUI--Depth--Anything--Tensorrt-blue?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Depth-Anything-Tensorrt) +[![ComfyUI-Upscaler-Tensorrt](https://img.shields.io/badge/ComfyUI--Upscaler--Tensorrt-blue?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Upscaler-Tensorrt) +[![ComfyUI-Dwpose-Tensorrt](https://img.shields.io/badge/ComfyUI--Dwpose--Tensorrt-blue?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Dwpose-Tensorrt) +[![ComfyUI-Rife-Tensorrt](https://img.shields.io/badge/ComfyUI--Rife--Tensorrt-blue?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Rife-Tensorrt) -If you like the project, please give me a star! ⭐ +[![ComfyUI-Whisper](https://img.shields.io/badge/ComfyUI--Whisper-gray?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Whisper) +[![ComfyUI_InvSR](https://img.shields.io/badge/ComfyUI__InvSR-gray?style=flat-square)](https://github.com/yuvraj108c/ComfyUI_InvSR) +[![ComfyUI-Thera](https://img.shields.io/badge/ComfyUI--Thera-gray?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Thera) +[![ComfyUI-Video-Depth-Anything](https://img.shields.io/badge/ComfyUI--Video--Depth--Anything-gray?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-Video-Depth-Anything) +[![ComfyUI-PiperTTS](https://img.shields.io/badge/ComfyUI--PiperTTS-gray?style=flat-square)](https://github.com/yuvraj108c/ComfyUI-PiperTTS) + +[![buy-me-coffees](https://i.imgur.com/3MDbAtw.png)](https://www.buymeacoffee.com/yuvraj108cZ) +[![paypal-donation](https://i.imgur.com/w5jjubk.png)](https://paypal.me/yuvraj108c) --- @@ -59,7 +70,7 @@ Models are automatically downloaded from [HuggingFace](https://huggingface.co/yu 2. **Process Frames**: Insert `Right Click -> Add Node -> tensorrt -> Rife Tensorrt` - Connect the loaded model from step 1 - Input your video frames - - Configure interpolation settings (multiplier, CUDA graph, etc.) + - Configure interpolation settings (multiplier, etc.) - Image resolutions between `256x256` and `3840x3840` are supported ## 🤖 Environment tested @@ -69,10 +80,10 @@ Models are automatically downloaded from [HuggingFace](https://huggingface.co/yu ## 🚨 Updates -### December 2025 -- **Automatic Model Management**: No more manual downloads! Models are automatically downloaded from HuggingFace and TensorRT engines are built on demand -- **Improved Workflow**: New two-node system with `Load Rife Tensorrt Model` + `Rife Tensorrt` for better organization -- **Updated Dependencies**: TensorRT updated to 10.13.3.9 for better performance and compatibility +### 08 June 2026 +- **Automatic Model Management**: No more manual downloads! Models are automatically downloaded from HuggingFace and TensorRT engines are built on demand. [PR#14](https://github.com/yuvraj108c/ComfyUI-Rife-Tensorrt/pull/14) by [@reaperhammer](https://github.com/reaperhammer) +- **Improved Workflow + Codebase**: New two-node system with `Load Rife Tensorrt Model` + `Rife Tensorrt` for better organization +- **Remove cuda-python**: No more cuda installation issues on windows ## 👏 Credits diff --git a/vfi_utilities.py b/vfi_utilities.py index 4cd3d33..88d7b16 100644 --- a/vfi_utilities.py +++ b/vfi_utilities.py @@ -7,7 +7,9 @@ import einops from comfy.model_management import soft_empty_cache, get_torch_device import numpy as np from comfy.utils import ProgressBar -from colored import Fore, Back, Style +from colored import Fore, Back, Style +from .utilities import rife_logger +from tqdm import tqdm DEVICE = get_torch_device() @@ -25,9 +27,6 @@ def load_file_from_github_release(model_type, ckpt_name): error_str = '\n\n'.join(error_strs) raise Exception(f"Tried all GitHub base urls to download {ckpt_name} but no suceess. Below is the error log:\n\n{error_str}") -def logger(msg): - print(f'{Style.reset}{Fore.cyan}⚡ [Rife Tensorrt] - {msg}{Style.reset}') - def preprocess_frames(frames): return einops.rearrange(frames[..., :3], "n h w c -> n c h w") @@ -45,7 +44,15 @@ def generate_frames_rife( out_len = 0 number_of_frames_processed_since_last_cleared_cuda_cache = 0 - pbar = ProgressBar(len(frames)) + pbar = ProgressBar(len(frames)-1) + + bar_format = "[\033[94mComfyUI-Rife-Tensorrt\033[0m|\033[92mINFO\033[0m] - \033[92m{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]" + progress_bar = tqdm( + total=len(frames)-1, + desc="Interpolating", + bar_format=bar_format, + disable=((len(frames)-1) == 1) + ) for frame_itr in range(len(frames) - 1): # Skip the final frame since there are no frames after it @@ -67,22 +74,25 @@ def generate_frames_rife( if number_of_frames_processed_since_last_cleared_cuda_cache >= clear_cache_after_n_frames: soft_empty_cache() number_of_frames_processed_since_last_cleared_cuda_cache = 0 - logger("Clearing cache...") + rife_logger.info("Clearing cache...") pbar.update(1) + progress_bar.update(1) - + progress_bar.refresh() + progress_bar.close() + # Append final frame output_frames[out_len] = frames[-1:] # Get actual frame shape from first interpolated frame (CHW format) actual_frame = output_frames[0] h, w = actual_frame.shape[1], actual_frame.shape[2] - logger(f"done! - {out_len} total frames output at resolution: {h}x{w}") + rife_logger.info(f"done! - {out_len} total frames output at resolution: {h}x{w}") out_len += 1 # clear cache for courtesy soft_empty_cache() - logger("Final clearing cache done ...") + rife_logger.info("Final clearing cache done ...") # res = output_frames[:out_len] return res \ No newline at end of file From 11db2d2f7f71bb694af180915a79f0ce4dc532eb Mon Sep 17 00:00:00 2001 From: Yuvraj Seegolam Date: Mon, 8 Jun 2026 09:22:42 +0400 Subject: [PATCH 16/17] add node image --- readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 6c419db..aaf3152 100644 --- a/readme.md +++ b/readme.md @@ -7,8 +7,7 @@ [![trt](https://img.shields.io/badge/TRT-10.14.1.48-green)](https://developer.nvidia.com/tensorrt) [![by-nc-sa/4.0](https://img.shields.io/badge/license-CC--BY--NC--SA--4.0-lightgrey)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) -![node](https://github.com/user-attachments/assets/5fd6d529-300c-42a5-b9cf-46e031f0bcb5) - +Screenshot 2026-06-08 at 09 21 51 From 9307964f57b235ab6b75547f4fc11468b835952e Mon Sep 17 00:00:00 2001 From: Yuvraj Seegolam Date: Mon, 8 Jun 2026 09:26:34 +0400 Subject: [PATCH 17/17] update readme --- readme.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index aaf3152..48323a0 100644 --- a/readme.md +++ b/readme.md @@ -7,6 +7,11 @@ [![trt](https://img.shields.io/badge/TRT-10.14.1.48-green)](https://developer.nvidia.com/tensorrt) [![by-nc-sa/4.0](https://img.shields.io/badge/license-CC--BY--NC--SA--4.0-lightgrey)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) + +This project provides a [TensorRT](https://github.com/NVIDIA/TensorRT) implementation of [RIFE](https://github.com/hzwer/ECCV2022-RIFE) for ultra fast frame interpolation inside ComfyUI + +**Last tested**: 08 June 2026 (ComfyUI v0.23.0 | Torch 2.12.0 | Python 3.12.3 | L40S | CUDA 13.0 | Ubuntu 24.04) + Screenshot 2026-06-08 at 09 21 51 @@ -72,10 +77,6 @@ Models are automatically downloaded from [HuggingFace](https://huggingface.co/yu - Configure interpolation settings (multiplier, etc.) - Image resolutions between `256x256` and `3840x3840` are supported -## 🤖 Environment tested - -- WSL Ubuntu 24.04.03 LTS, Cuda 12.9, Tensorrt 10.13.3.9, Python 3.12.11, RTX 5080 GPU -- Windows (Not tested, but should work) ## 🚨 Updates