From f779d67adf4e8900aebc5fd19399ed1caa07e779 Mon Sep 17 00:00:00 2001 From: iGavroche Date: Sat, 13 Jun 2026 11:10:56 -0400 Subject: [PATCH] v2.2.0: clean up ROCM SamplerCustomAdvanced interface - Remove optimize_for_video (auto-detects 5D video latents) - Remove precision_mode (was a no-op) - compatibility_mode moved to advanced=True toggle - Node now matches stock interface exactly (5 inputs) with only an advanced debug toggle added - Video workflow auto-detected from latent shape (5D, T > 1) - Architecture logging explicitly shows GPU family on ROCm - Benchmark node also cleaned up (no extra params) --- docs/SAMPLER_CUSTOM_ADVANCED.md | 15 +++---- rocm_nodes/core/sampler.py | 70 ++++++++++++--------------------- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/docs/SAMPLER_CUSTOM_ADVANCED.md b/docs/SAMPLER_CUSTOM_ADVANCED.md index c55ee5d..996201a 100644 --- a/docs/SAMPLER_CUSTOM_ADVANCED.md +++ b/docs/SAMPLER_CUSTOM_ADVANCED.md @@ -39,9 +39,7 @@ This is the standard pattern for LTX Video, LTX 2.3 AV, and many other advanced | `sampler` | SAMPLER | Yes | e.g. KSamplerSelect | | `sigmas` | SIGMAS | Yes | e.g. BasicScheduler | | `latent_image` | LATENT | Yes | e.g. LTXVConcatAVLatent | -| `optimize_for_video` | BOOLEAN | No (default: False) | Disables previews when latent is 5D with T > 1 | -| `precision_mode` | COMBO | No (default: auto) | `auto`, `fp32`, `bf16` — ROCm precision hint | -| `compatibility_mode` | BOOLEAN | No (default: False) | When True, skips all ROCm optimizations (pure stock behavior) | +| `compatibility_mode` | BOOLEAN | No (advanced) | Skip all ROCm optimizations, use pure stock behavior | ## Outputs @@ -57,18 +55,21 @@ This is the standard pattern for LTX Video, LTX 2.3 AV, and many other advanced 1. Delete the stock `SamplerCustomAdvanced` node 2. Add `ROCM SamplerCustomAdvanced` from `ROCm Ninodes/Sampling` 3. Wire the same connections (noise, guider, sampler, sigmas, latent_image) -4. Optionally configure `optimize_for_video`, `precision_mode`, or `compatibility_mode` -5. Run — the console will show per-step progress, model detection, and completion +4. Run — the console will show GPU architecture, model detection, and per-step progress ### LTX 2.3 / LTX Video Workflows -For LTX-based workflows (like `LTX2.3-Director-App-Mariah.json`), the `latent_image` input typically comes from `LTXVConcatAVLatent`. ROCMSamplerCustomAdvanced detects the flow-matching model type (128ch latent, memory factor 5.5x) and applies appropriate memory management before and after sampling. +For LTX-based workflows (like `LTX2.3-Director-App-Mariah.json`), the `latent_image` input typically comes from `LTXVConcatAVLatent`. ROCMSamplerCustomAdvanced detects the flow-matching model type (128ch latent, memory factor 5.5x) and applies appropriate memory management before and after sampling. Video (5D latent with T > 1) is auto-detected — previews are disabled for multi-frame latents to reduce overhead. + +### When to Use `compatibility_mode` + +Enable it via the node's advanced properties panel if you suspect the ROCm optimizations are causing issues. This returns the exact stock behavior for debugging. ## Benchmark Node `ROCMSamplerCustomAdvancedBenchmark` (`ROCm Ninodes/Sampling > ROCm SamplerCustomAdvanced Benchmark`) runs both the stock and ROCm-optimized sampler in sequence on identical inputs and outputs a comparison string. -**Inputs:** Same as SamplerCustomAdvanced. +**Inputs:** Same as SamplerCustomAdvanced (no extra params). **Outputs:** - `LATENT` — result from the ROCm-optimized run diff --git a/rocm_nodes/core/sampler.py b/rocm_nodes/core/sampler.py index 78b5b44..0ae70f9 100644 --- a/rocm_nodes/core/sampler.py +++ b/rocm_nodes/core/sampler.py @@ -449,8 +449,8 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): Key enhancements over stock SamplerCustomAdvanced: - Auto-detects AMD GPU architecture and applies optimal backend settings - Emergency memory defrag for high-memory models (128ch latents) - - Optional BF16 precision hint for supported AMD GPUs - - Enhanced callback with ETA, per-step timing, and video-workflow optimization + - Enhanced callback with ETA and per-step timing + - Video workflow auto-detection (5D latent → no previews) - Gentle post-sample memory cleanup """ @@ -458,7 +458,7 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): def define_schema(cls): return io.Schema( node_id="ROCMSamplerCustomAdvanced", - display_name="ROCM SamplerCustomAdvanced", + display_name="ROCm SamplerCustomAdvanced", category="ROCm Ninodes/Sampling", description="Drop-in replacement for SamplerCustomAdvanced with ROCm memory management, backend tuning, and detailed progress tracking. Particularly beneficial for high-memory models like LTX Video (128ch latent).", inputs=[ @@ -467,13 +467,8 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): io.Sampler.Input("sampler"), io.Sigmas.Input("sigmas"), io.Latent.Input("latent_image"), - io.Boolean.Input("optimize_for_video", optional=True, default=False, - tooltip="Disable previews for multi-frame latents (video workflows)"), - io.Combo.Input("precision_mode", optional=True, default="auto", - options=["auto", "fp32", "bf16"], - tooltip="ROCm precision hint (no-op on CUDA/CPU)"), - io.Boolean.Input("compatibility_mode", optional=True, default=False, - tooltip="Force pure stock behavior, no ROCm optimizations"), + io.Boolean.Input("compatibility_mode", optional=True, default=False, advanced=True, + tooltip="Skip all ROCm optimizations, force pure stock behavior"), ], outputs=[ io.Latent.Output(display_name="output"), @@ -483,31 +478,28 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): @classmethod def execute(cls, noise, guider, sampler, sigmas, latent_image, - optimize_for_video=False, precision_mode="auto", compatibility_mode=False) -> io.NodeOutput: - # ── Detect architecture and model type ────────────────────────── + compatibility_mode=False) -> io.NodeOutput: model = guider.model_patcher + # ── ROCm setup (skipped in compatibility_mode) ────────────────── if not compatibility_mode: arch_info = detect_architecture() model_info = detect_model_sampling_type(model) apply_rocm_backend_settings(arch_info) + is_rocm = bool(getattr(torch.version, 'hip', None)) + if is_rocm: + print(f"🚀 ROCm detected on {arch_info['family']} " + f"({arch_info.get('arch_name', 'unknown')}) " + f"— applying backend tuning", flush=True) + if model_info["has_high_memory"]: - print(f"💾 High-memory model detected (factor: {model_info['memory_usage_factor']}) — cleaning up") + print(f"💾 High-memory model (factor: {model_info['memory_usage_factor']}x, " + f"{model_info['latent_channels']}ch latent) — defragging VRAM", flush=True) emergency_memory_cleanup() if model_info["model_type"] == "flow": - print(f"🌊 Flow-matching model detected ({model_info['latent_channels']}ch latent)") - elif model_info["is_pixel_space"]: - print(f"📷 Pixel-space model detected — no VAE decode needed downstream") - - is_rocm = bool(getattr(torch.version, 'hip', None)) - if is_rocm and torch.cuda.is_available() and precision_mode == "bf16": - try: - if getattr(torch.cuda, 'is_bf16_supported', lambda: False)(): - pass - except Exception: - pass + print(f"🌊 Flow-matching model ({model_info['latent_channels']}ch)", flush=True) # ── Stock SamplerCustomAdvanced logic ────────────────────────── latent = latent_image @@ -527,14 +519,12 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): x0_output = {} total_steps = sigmas.shape[-1] - 1 - # ── Video optimization ────────────────────────────────────────── - is_video_workflow = False - if not compatibility_mode and optimize_for_video and latent_image_tensor.ndim == 5 and latent_image_tensor.shape[2] > 1: - is_video_workflow = True + # ── Auto-detect video (5D latent with T > 1) ────────────────────── + is_video = latent_image_tensor.ndim == 5 and latent_image_tensor.shape[2] > 1 - # ── Create previewer once (like stock prepare_callback) ────── + # ── Create previewer once (like stock prepare_callback) ────────── previewer = None - if not is_video_workflow: + if not is_video: try: previewer = latent_preview.get_previewer( guider.model_patcher.load_device, @@ -556,7 +546,6 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): progress_pct = ((step + 1) / total_steps) * 100 pct_key = (int(progress_pct) // 10) * 10 - # ── Update progress bar with preview (throttled) ──────── if current_time - last_update_time[0] >= 0.3 or step == total_steps - 1: preview_bytes = None if previewer and step % 5 == 0: @@ -570,11 +559,9 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): pass last_update_time[0] = current_time - # ── Console timing at 10% milestones only ────────────── if step == 0 or step == total_steps - 1 or pct_key not in reported_milestones: reported_milestones.add(pct_key) elapsed = current_time - start_time - wf_type = "Video" if is_video_workflow else "Image" if step == 0: print(f"📊 ROCm CustomAdvanced: Starting {total_steps} steps...", flush=True) else: @@ -587,11 +574,11 @@ class ROCMSamplerCustomAdvanced(io.ComfyNode): f"({avg:.2f}s/step)", flush=True) callback = enhanced_callback - disable_pbar = is_video_workflow + disable_pbar = is_video # ── Sampling ──────────────────────────────────────────────────── print(f"🚀 ROCm SamplerCustomAdvanced: {total_steps} steps" - f"{' (video mode)' if is_video_workflow else ''}", flush=True) + f"{' (video mode)' if is_video else ''}", flush=True) samples = guider.sample( noise.generate_noise(latent), latent_image_tensor, sampler, sigmas, denoise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, @@ -649,10 +636,6 @@ class ROCMSamplerCustomAdvancedBenchmark: "sampler": ("SAMPLER",), "sigmas": ("SIGMAS",), "latent_image": ("LATENT",), - }, - "optional": { - "optimize_for_video": ("BOOLEAN", {"default": False}), - "precision_mode": (["auto", "fp32", "bf16"], {"default": "auto"}), } } @@ -662,8 +645,7 @@ class ROCMSamplerCustomAdvancedBenchmark: CATEGORY = "ROCm Ninodes/Sampling" DESCRIPTION = "Compare stock vs ROCm-optimized custom sampler performance" - def run(self, noise, guider, sampler, sigmas, latent_image, - optimize_for_video=False, precision_mode="auto"): + def run(self, noise, guider, sampler, sigmas, latent_image): import time as time_module import gc @@ -713,9 +695,7 @@ class ROCMSamplerCustomAdvancedBenchmark: if model_info["has_high_memory"]: emergency_memory_cleanup() - is_video_workflow = False - if optimize_for_video and latent_image_tensor.ndim == 5 and latent_image_tensor.shape[2] > 1: - is_video_workflow = True + is_video = latent_image_tensor.ndim == 5 and latent_image_tensor.shape[2] > 1 mem_before_rocm = torch.cuda.memory_allocated(0) if torch.cuda.is_available() else 0 @@ -739,7 +719,7 @@ class ROCMSamplerCustomAdvancedBenchmark: rocm_samples = guider.sample( noise.generate_noise(latent), latent_image_tensor, sampler, sigmas, denoise_mask=noise_mask, callback=rocm_callback, - disable_pbar=is_video_workflow, seed=noise.seed + disable_pbar=is_video, seed=noise.seed ) rocm_samples = rocm_samples.to(comfy.model_management.intermediate_device()) t3 = time_module.time()