Nodes/Distilled ResShift SR/ResShift SR Upscale (1-step)
ComfyUI Node

ResShift SR Upscale (1-step)

One stochastic step from 512px to 2048px — no sampler, no CFG

By sorryhyun·Created 2 months ago·Updated about a month ago· 2
ResShift SR Upscale (1-step)
  • rsd_model
  • image
  • IMAGE
seed0
chop512
overlap64
tile_batch4
shared_noisetrue

This is where the Distilled ResShift pack does its party trick: it upscales any RGB image ×4 or ×2 in one diffusion step. Not fifteen, not eight - one. There's no sampler dropdown, no CFG, no scheduler anywhere in the node. You feed it a RESSHIFT_MODEL (from ResShiftLoader) and an IMAGE, and it hands back the upscaled IMAGE. That's the whole interface.

What it actually is

The student is distilled from a 15-step ResShift teacher into a single stochastic step. That's the trade you should know going in: distillation collapses the whole denoising trajectory into one jump, and distilled models always give up some refinement for the speed. So this won't rebuild a face the way a heavy restoration model will - which, for most upscaling jobs, is a feature, not a bug. In the upscaling taxonomy (more pixels vs. more detail vs. video), this is a detail model that's fast enough to feel like an ESRGAN-class upscaler: seconds, not the multi-minute grind of a SeedVR2 pass.

It's also genuinely model-agnostic. Because it's a pixel-space IMAGE → IMAGE node, it doesn't care what generated the image. Drop it after VAEDecode and before SaveImage, and it'll happily upscale anime, photos, or a random PNG you loaded from disk.

How the one step works

The student runs at the same spatial resolution - the upscale lives in the residual-shift, not in a spatial resize. The image gets bicubic-upsampled ×scale, encoded to the vq-f4 latent, pushed through one student step (z_T = z_y + κ·randn, plus injected noise ε), and decoded. Large images are split into tiles with overlap-averaged blending, and each tile is reflect-padded to a multiple of the Swin alignment (256 px at ×4, 128 px at ×2).

The inputs that matter

Most of the defaults are fine to leave alone. The ones you'll actually touch:

  • seed (default 0) - the step is stochastic, so the same seed plus the same input gives the same output. Change it if you want a different rendering of detail; keep it if you want reproducibility.
  • chop (default 512) - tile size for large images, snapped to the 256 alignment. An image at or under chop runs as a single tile. This is your VRAM lever - if you're OOM, lower it. Bigger tiles mean more context per tile and weaker seams.
  • overlap (default 64) - the seam overlap between tiles (tile stride = chop - overlap), and also the feather ramp the tile weights blend across. Larger = fewer seams but more redundant compute; 0 gives hard-abutted tiles.
  • tile_batch (default 4) - how many tiles go into one forward pass. Small tiles underfill the GPU one at a time, so batching amortizes launch overhead. VRAM scales roughly linearly with it; tune to your card.

The one optional input, shared_noise (default true), draws a single full-image noise field and shares it across overlapping tiles so the seam-average is discontinuity-free - it kills the faint tile-block seams you'd otherwise see in flat regions. Leave it on; it only matters when the image is actually tiled.

Output is IMAGE at ×scale, ready for SaveImage or further processing.

Installing it

Same pack, same install as the loader:

# ComfyUI Manager: search "Distilled ResShift SR"
# ...or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/sorryhyun/ComfyUI-Distilled-ResShift

Restart, and the first load auto-downloads the student (~478 MB) and the vq-f4 VQGAN (~211 MB) into ComfyUI/models/resshift/. Dependencies (einops, timm, omegaconf, huggingface_hub) auto-install from pyproject.toml, and there's no xformers requirement. Keep the loader's dtype on bf16 - it's ~2x faster and half the VRAM, and it matches how the student was trained.

Common issues

  • Out of VRAM on big images - lower chop, then tile_batch, before touching anything else. That's the whole story.
  • Faint seams in flat skies/gradients - make sure shared_noise is on and raise overlap a bit.
  • Output keeps changing - that's the stochastic step. Same seed + same input = same output; different seed = different detail.
  • Wrong scale factor - that's a loader-side scale/student mismatch, not this node. Check ResShiftLoader.

One step, no sampler, no CFG, no key. It's the rare distilled model that just works out of the box.

CategoryResShift

Inputs (7)

NameTypeDefaultDescription
rsd_modelRESSHIFT_MODEL
imageIMAGE
seedINT00–18446744073709550000Seeds the stochastic step (z_T = z_y + κ·randn and the injected ε). Same seed + input = reproducible output.
chopINT512256–4096Tile size in px for large images (snapped to a multiple of the Swin align stride, 256). Lower it if VRAM-bound; a whole image ≤ chop runs single-tile. Bigger tiles = more context per tile = weaker seams.
overlapINT640–512Tile seam overlap in px. Tile stride = chop - overlap (must be >0). Also the feather ramp width (tile weights blend with a raised-cosine across the overlap); 0 = hard-abutted box tiles. Larger = fewer seams, more redundant compute.
tile_batchINT41–32Tiles stacked into one forward — small tiles underfill the GPU one-at-a-time, so batching amortizes launch overhead. VRAM scales ~linearly; tune to the card.
shared_noiseoptBOOLEANtrueDraw one full-image noise field and share it across overlapping tiles so the seam-average is discontinuity-free (removes faint tile-block seams in flat regions). A few MB, quality-neutral; only matters when tiled (image > chop). Off = independent per-tile noise (old).

Outputs (1)

NameTypeDescription
IMAGEIMAGE