CHANGELOG: the branch's running notes become one 3.3.0 entry describing
the final state, with the saved-graph break first. pyproject 3.3.0.
review/ (handover + cloud test harness) leaves the repo; the harness is
kept locally under tools/.
upsampler: sparse attention falls back to dense; CPU-only upscale in float32
Sandpies
b535f16
apply_sparse_attention guarded only the import: a ComfyUI whose
apply_block_sparse_attention takes other arguments (or a model without
model_sampling) failed the whole upsample at queue time. It now runs
dense and says why in the log.
upscale_chunks_batch moved the network to float16 before choosing the
device path, so a CPU-only device fed float32 input through float16
weights (a real Conv3d raises "Input type (float) and bias type (Half)
should be the same"). The CPU path now loads it in float32, as the
out-of-memory fallback already did. CUDA is unchanged.
Upsampler: sparse attention by default; sol-VAE recommended
Sandpies
3745d16
- attention defaults to sparse on the standalone nodes, HTC Upsample
Settings and the chain (a whole 6 s run: 85 s -> 61 s, SSIM 0.988
against dense). A ComfyUI without sparse attention runs dense.
- Notes, README and the VAE tooltip recommend the sol-VAE for speed
(about 20% faster); the default VAE is unchanged. The no-decoder
error says the sol-VAE needs a ComfyUI from late October 2026.
- The upsample log only names structure lock / tile context when used.
Upsampler: stepped log splits its time into sampling, flush and setup
Sandpies
608e35b
Measures where each stepped call's time goes, to decide whether
per-call overhead is worth attacking before any batching work.
Upsampler defaults: stepped joins and redraw_start 0.6 everywhere
Sandpies
a30147f
The chain without a settings node, HTC Upsample Settings and the
standalone nodes now default to chunk_join=stepped and redraw_start 0.6
(Regen2K grid [0.6, 0.48, 0.3, 0]), both proven on a GPU. 0.8 stays one
setting away for the original recipe. Shipped workflows, notes and docs
updated.
- redraw_steps dropdown (3 recipe / 2) on the standalone nodes;
redraw_start on HTC Upsample Settings (works with its steps).
- chunk_join defaults to stepped on the standalone nodes and the
settings node.
- Removed structure_lock, lock_sigma, lock_anchor, tile_context and
off_24_audio from the nodes (always retime); the engine keeps them
at their off defaults.
- Plain-language tooltips, workflow notes and README; shipped
workflows remapped to the new widget list.
Upsampler: chunk_join=stepped on tiled chunks
Sandpies
e31d8d9
Every tile of every chunk takes each step together and is merged under
its feather times its chunk's time ramp, so the film is one grid in time
and space at one tile's VRAM. Out of VRAM, stepped runs again at a lower
tile budget instead of falling back to crossfade; tile_context=global
pass applies per chunk. Crossfade remains the fallback with the
h3upscale guide or with tiling off.
Upsampler: 3D upscale through ComfyUI core when present
Sandpies
4878472
Core (Kijai, ComfyUI PR #17412) runs the same H3 latent upscaler with
GroupNorm statistics over the whole clip, 16 latent frames at a time.
upscale_film() upscales the film once and cuts every chunk from it, so
chunks no longer start the re-draw toned differently from their
neighbours. When the whole film does not fit, it goes chunk by chunk
through core; on older ComfyUI, an unreadable file or OOM it falls back
to the bundled upscaler. Loaded via core's own loader; no core code
copied. HandTieClips gets it too (same upsample()).
Video Frames: duration in seconds, snapped to H3 lengths
Sandpies
fe6a58a
duration_seconds replaces max_frames (same slot): nobody should have to
work out frames. snap_to_h3 (on) rounds to the nearest length H3 uses
natively (17n + 5: 30 s at 24 fps = 719, 5 s = 124), so nothing is
padded; the log reports frames and seconds actually taken.
chunk_join=stepped: the chunks are re-drawn together, as spatial tiles
always were. The film starts as the upscaled chunks crossfaded into one
latent; each sigma step runs every chunk on its slice of that one state
(step 1 with its slice of one film-wide noise field) and merges the
results, each chunk weighted down linearly across the steps it shares, so
neighbours converge on one face and a chunk's off-tone last step counts
least. Every chunk is still drawn from the source: nothing accumulates.
Whole-frame chunks only; tiled or out-of-VRAM runs fall back to crossfade.
Removed chunk_join=continue: on a GPU it made a visible seam at the
hand-over (traced to a tone step at the held/free boundary) and did not
keep the face closer.
Fix: with the original (non-Regen2K) recipe and structure_lock > 0, chunk
2+ crashed -- the per-chunk lock anchor shadowed upsample()'s pin-strength
argument. Introduced on this branch; regression test added.
H3 Video Upsampler: redraw_start (Regen2K grid from less noise)
Sandpies
437f9ba
Rebuilds Regen2K's 3-step shift-6 grid from another starting noise
(regen2k_sigmas; 0.8 reproduces REGEN2K_SIGMAS exactly). A side-by-side
test put the face drift on the re-draw itself: denoise 0 matched the
source, and seed, LoRA strength and a text prompt did not move it.
Starting from less noise keeps faces closer to the source, at some cost
in sharpening. Last widget on both nodes so open graphs keep their values.
Draft: tile_context global pass -- tiles follow a whole-frame pass
Sandpies
b834474
tile_context="global pass" (default none; standalone nodes and HTC
Upsample Settings). When a chunk must be tiled, it is first re-drawn
whole-frame at the largest size that fits the budget (area-downscaled
latent, own noise, same sigmas, keyframes resized, structure_lock scaled
with it), brought back to the chunk grid, and the tiles hold their large
shapes to that result (structure_lock at full strength at lock_sigma).
A tile sees only its window, which is where invention comes from; the
layout now comes from a pass that saw all of it. Inside the OOM guard, so
an OOM retry shrinks both passes. Guided re-draws and chunks that fit
whole are untouched. Not yet measured on a GPU.
Upsampler: tile_tokens 0 learns the machine's memory, run by run
Sandpies
36dad6b
tile_tokens 0 = auto (new default on the standalone nodes; upsample_tile_
tokens 0 in HandTieClips, whose stock value stays 70000). The upsampler
starts from what this GPU learned last time (70000 the first time),
measures each chunk's activation memory (peak minus what stays resident:
ComfyUI's dynamic loading fills spare VRAM with weights, so the raw peak
says little) and stores where the next run starts in
user/htc_upsampler_memory.json: grown toward 85% of the card when there
was room (at most 2x a run), the budget that worked after an OOM and never
back near the failing value. Keyed by GPU, VRAM, attention mode and the
low-VRAM chunking. A fixed number behaves as before and teaches nothing.
The chain's pre-run chunk-count log plans with the same budget.
Upsampler: chunking/tiling switches, structure lock anchored to the source
Sandpies
676b10d
- chunking / tiling (both on by default, as before) replace split: each
ALLOWS a split when the clip does not fit tile_tokens. Time is cut before
space and never under 68 frames (Regen2K invents on short pieces); both
off = the whole clip in one whole-frame pass. Chunking off over 4x
tile_tokens chunks anyway (in HandTieClips a segment is usually the
film; the chain's chunk-count log follows). Tiling off never tiles: an
OOM is an error that says what to change.
- lock_anchor=source (default; only matters with structure_lock > 0):
structure_lock holds the large shapes to the clip's own latent enlarged
by plain interpolation instead of the 3D upscaler's output, so the
upscaler's own errors are pulled back too.
- Workflows validated against the node definitions; credit note for
DaSiWa-Nodes prompting the switches (no code copied).
New options on the standalone nodes and HTC Upsample Settings, defaults
unchanged:
- attention=sparse: ComfyUI's Model Sparse Attention (sol-attn) on every
re-draw step; dense on a ComfyUI without the node. Composes with KJ's
low-VRAM attention.
- chunk_join=continue: a chunk's shared frames are held to what the
previous chunk drew (per-frame sampler mask) and the rest is drawn after
them, instead of crossfading two independent re-draws. Whole-frame
chunks only; tiled chunks fall back to crossfade.
- split=whole clip: one pass, no chunks, no tiles; budget set to the
clip's sequence so an OOM retry goes straight to tiles.
Fixes: the 3D upscale's CPU fallback now applies to the failing chunk
only (it used to run every remaining chunk on the CPU in float32);
conditioning rows count against tile_tokens (_cond_extra_tokens was
unused).
Load Video (path): no upload, no copy, no 100 MB limit
Sandpies
961ca04
ComfyUI refuses browser uploads over 100 MB without --max-upload-size,
and Load Video only reads ComfyUI/input. The new loader reads a pasted
path in place (Copy as path quotes stripped; relative names resolve in
input/), outputs a normal VIDEO plus fps, frame_count and an info line
(resolution, fps with a variable-rate warning, length, codec, bit depth,
audio), re-runs only when the file's size or mtime changes, and reports a
missing or non-video file at queue time.
Fix: the streamed readers now skip an audio track FFmpeg cannot decode
(e.g. iPhone APAC), as core does; decoding one crashes the process.
Path-loader idea credited to silveroxides' UC_LoadVideoPath (no code
copied).
Video Frames (lean): Get Video Components at half the peak RAM
Sandpies
4ddc377
A drop-in for Get Video Components after Load Video: the same images /
audio / fps plus frame_count and the VIDEO. start_seconds, max_frames,
frame_rate (24 fps nearest-frame re-time) and resize (megapixels or long
edge, never enlarging) are applied while decoding, and the IMAGE is filled
once from 8-bit frames, each batch freed as it is copied. On a 600-frame
640x360 clip, peak RAM was 1.08x the output against 2.01x for core-style
decode-to-list-then-stack. Replaces Get Video Components in the IMAGE
upsampler workflow (same first three output slots).
H3 Video Upsampler (file to file): 24 fps on read, latent reuse, crops
Sandpies
46b0c70
- frame_rate: "24 (H3 native)" re-times the clip on read by nearest frame
(dropped above 24, repeated below), keeping the real duration so the
soundtrack syncs exactly; a 48 fps clip costs half the re-draw.
- reuse_encoded (on): the encoded clip is kept in ComfyUI's temp folder,
keyed to file content, trim, crop, rate and VAE; the next run on the
same clip skips reading and encoding (4 kept, cleared at startup).
- A cropped VIDEO is read a chunk at a time with core's crop rules
(matched against core on 20000 random crops) instead of whole.
Ideas from silveroxides' ComfyUI-UtilsCollection, written fresh with no
code copied, credited in THIRD_PARTY_NOTICES.md with their agreement.
H3 Video Upsampler (file to file): flat RAM at any clip length
Sandpies
667b03c
The IMAGE node is bounded by ComfyUI's IMAGE type: Get Video Components
decodes the whole clip to float32 (stacked from a list, so twice for a
moment) and the result comes back as another full tensor -- a minute at
48 fps is ~32 GB in and ~72 GB out at 2 MP. The re-draw only needs the
latent (~100 MB a minute).
H3 Video Upsampler (file to file) reads the VIDEO 102 frames at a time
and encodes as it goes (the H3 VAE encodes every 17 frames on their own,
so the latent is bit-identical to a whole-clip encode), re-draws exactly
as the IMAGE node, and decodes slice by slice into the save node's
streaming encoder. Frame rate and soundtrack come from the file. VIDEOs
that are not a plain file (components, list, crop) are decoded whole.
- video_upsampler: run() split into check_vae / redraw / decode;
decode_slices generator under decode_streamed (output unchanged).
- save_video: the encoder loop is now encode_stream(), fed any iterator
of frame batches; the node writes identical frames a…
H3 Video Upsampler: fps input keeps lip sync off 24 fps, lock_sigma widget
Sandpies
d3c359d
H3 places every frame 1/24 s apart, so a 48 fps clip's soundtrack sat
under the wrong frames (half speed, then silence) and the re-draw pulled
mouths off sync. New optional fps input (wired from Get Video Components
in the workflow) and off_24_audio: retime (default) stretches what the
re-draw hears by fps/24, silence conditions on silence, as_is is the old
behaviour. A warning is logged whenever fps is not 24; the output
soundtrack is untouched.
Also exposes lock_sigma on the standalone node (was only on HTC Upsample
Settings).
upsampler: structure_lock against invented detail, conditioning input on H3 Video Upsampler
Sandpies
5a0d5e8
Regen2K re-draws each chunk and tile from sigma 0.8 with zeroed-out
conditioning, so it can re-imagine faces, hands and line work, and two
chunks re-imagining one overlap get crossfaded into each other.
- structure_lock (0..1, default 0 = unchanged): a post-CFG hook moves each
step's x0 below lock_sigma latent cells (default 2) onto the 3D-upscaled
source, per tile, leaving the finer band to the re-draw. On the standalone
node and HTC Upsample Settings (structure_lock, lock_sigma).
- H3 Video Upsampler: optional conditioning input (text only; refs,
keyframes and noise-aug dropped, as regen2k_cond=hop_text).
- _highpass_detail_merge now shares _blur_hw (output unchanged).
upsampler: add REGEN2K_SIGMAS, sigmas pass-through, and R0 chunk shortening before tiling
Sandpies
21fbd92
upsampler: carry OOM-lowered budget forward across chunks (S1) and log budget to set (S2)
Sandpies
ba6c750
upsampler: rebuild standalone pack and add Unreleased changelog entry
Sandpies
aae689e
upsampler: log tile grid, OOM retries, split re-draw/join time, latent MB, and peak VRAM
Sandpies
7f94dba
upsampler: build tile weight sum (wsum) once per grid in redraw_chunk
Sandpies
90f9869
upsampler: preallocate joined film latent in upsample() (_join_into)
Sandpies
30f5031
4 days ago
hand_tie: previs-first + gate (stop before hops), chained previs inherits its framing, skill lints (plate-inventory, still-motion, end-state-window) with a suite
Sandpies
a5e40ac
Skill: establish empty when prompt_prefix carries the look
Sandpies
5268a13
hand_tie previs: drop mid-take place restatements; skill: never describe what the plate shows (framing = size + person + tag)