沐阳 H3 · 条件显存屏障(内部)
Why a 16GB card dies during conditioning, and the barrier node that stops it
- conditioning
- keep_model
- conditioning
Here's a failure that looks like a bug and isn't. You queue a long H3 job. It doesn't die in the sampler - it dies before sampling, while the reference images and prompts are being encoded, with an out-of-memory error that mentions the VAE. Nothing is wrong with your VAE. The DiT is still sitting on the card from a previous stage, and now a 33B video model and a reference VAE are sharing VRAM to do a job that only needed one of them at a time.
H3ConditionMemoryBarrier is the pack's fix for exactly that moment.
The mechanism
The node is a stage barrier, not a compute node. It takes the conditioning (and, optionally, the model about to be used) and calls ComfyUI's model-management layer to offload registered patchers and return unused CUDA blocks to the pool before passing the conditioning straight through unchanged.
Under the hood it does more than unload_all_models. It first drains prefetch queues and resets cast buffers, then unloads - because a model can be missing from ComfyUI's loaded-model list while still owning prefetched weight pages, CUDA graph streams or reusable cast buffers. Skip that step and you get several gigabytes of unexplained "other" memory sitting in the pool. That was the actual bug being fixed here, and it's the kind of thing you'd never guess from the symptom.
The optional keep_model input is the clever bit. Name the DiT that this segment is about to sample with and the barrier keeps its loaded-model entry - freeing the CLIP, VAE and upscaler residency while leaving the model you're going to need in about four seconds alone. Skip it and the model gets evicted and re-staged from disk. On a 33B model that's the difference between a pause and a coffee break.
Inputs and outputs
conditioning(CONDITIONING, required) - the built conditioning. Comes back out untouched.stage(STRING, defaultconditioning -> sampling) - a label. It's used for the log line and for identifying which barrier fired when you're reading a long run's output.keep_model(MODEL, optional) - the DiT for the upcoming sampling pass. Its tooltip ("本段马上要用的 DiT;保留它的宿主缓存,只清 CLIP/VAE") is the whole story: keep its host cache, clear only CLIP/VAE.
One output: conditioning. Wire it into your guider or sampler.
The node also forces itself to re-run on every queue regardless of caching. That's deliberate - the conditioning object is often cached across seed changes (same prompt, same references), but memory cleanup is a side effect, and a cached node doesn't execute. So the barrier declares itself always-changed and fires every run. If you're building something similar, that's the idiom to copy; the node plumbing layer explains why IS_CHANGED returning NaN defeats the cache.
Do you need to wire it?
No, and the Chinese display name says so - 内部 means internal. In the Director and long-video-expansion graphs, this node is already in the subgraph the pack builds for you. You'll see it in the graph, greyed out in spirit if not in fact, and it'll do its job.
The reason to care is diagnostic. When a long job OOMs, you now know there are four barriers in the chain and what each one guards: H3PreConditionMemoryBarrier before conditioning (evict stale DiT so reference VAE can fit), this one after conditioning (evict CLIP/VAE before pass 1), H3RefineMemoryBarrier before pass 2, and H3OutputMemoryRelease when the finished video exists. If your OOM happens between two stages rather than inside a sampler, one of these is the thing to check.
Install
It's part of the pack:
cd ComfyUI/custom_nodes
git clone https://github.com/civilcoco/ComfyUI-MiniMaxH3-Myang
Restart ComfyUI and hard-refresh the browser. Category 沐阳 H3/内部. No dependencies - the pack declares none, and this node only reaches into ComfyUI's own model_management and model_prefetch modules.
Where it gets weird
If you add it to a hand-built graph, put it on the wire between your conditioning node and the sampler. Wiring it into a branch that never reaches the sampler still frees the memory, but it will also run before the conditioning it's supposed to be guarding is built - same node, wrong place, confusing result.
And if you're on a CPU-only or non-CUDA backend, memory reporting is skipped rather than fatal. That's intentional; the pack's regression suite runs without a GPU.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| conditioning | CONDITIONING | — | |
| stage | STRING | conditioning -> sampling | — |
| keep_modelopt | MODEL | 本段马上要用的 DiT;保留它的宿主缓存,只清 CLIP/VAE |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| conditioning | CONDITIONING | — |