MMH3 Spatial Split Params
The VRAM dial for H3 upscaling
- spatial_split_param
- tile_width
- tile_height
This is the node that decides whether your upscale runs on your GPU or dies in a fireball of "out of memory". MMH3 Spatial SplitParams bundles the tile settings for MMH3 Ultimate Upscale: how each video frame gets chopped into overlapping tiles, how the seams between tiles get stitched, and - the part that matters most to you - how big a single tile is. Because peak VRAM is bounded to one tile, tile size is your VRAM budget. This is tiled diffusion for H3, and this node is the dial.
The mechanism in plain terms
Each chunk (from the temporal split) is cut into a grid of overlapping tiles. One tile is sampled at a time. Here's the trick that keeps seams invisible: the overlap strip a tile shares with an already-stitched neighbor is split into two segments. The part nearest the seam is frozen - pre-filled from the accumulated result and locked with a noise mask, so the re-sample literally cannot change it. The part nearest the tile's interior is the fade segment, a smooth mask transition. After sampling, the frozen seam is written back with torch.where, guaranteeing the already-consistent content is never overwritten. That's how you get big upscales without the blocky checkerboard that naive tiling produces.
The inputs that matter
Two of them do 90% of the work, and the rest are seam polish:
tile_width/tile_height(default 512) - tile size in pixels at the upscaled chunk resolution, multiples of 32. Smaller = less VRAM, more tiles, slower. 512 is a good starting point; drop to 256 or 384 on a small card.spatial_w_overlap/spatial_h_overlap(default 128) - overlap in pixels between neighbors, also multiples of 32 and must be smaller than the tile size. More overlap = better consistency, more wasted work. 128 is the sensible default.
Then the seam controls: fade_width / fade_height (default 32) set the length of the fade segment; set to 0 to freeze the entire overlap strip. min_tile_size (default 256) pulls the last edge tile back so it never degenerates into a sliver. And overlap_mode (earlier or later - who wins the shared band) plus overlap_blend (linear / smoothstep / overwrite / midpoint) control exactly how adjacent tiles transition. The defaults - linear, earlier - are the safe ones; the others exist for when a seam shows.
The single output, spatial_split_param, plugs into the spatial_split_param input on MMH3 Ultimate Upscale. Don't connect it and the node samples each chunk whole, which is fine for short clips at native-ish size.
Install and troubleshooting
Same pack, same install: Comfyui-MMH3-UltimateUpscale in ComfyUI Manager, or clone the repo into custom_nodes and restart. No dependencies, no models - this node is pure geometry.
Real failure modes to expect, all with the author's own validation baked in:
- "must be multiples of 32" - the node checks this and throws a ValueError. Tile dims, overlaps, and fades all have to land on the 32px grid.
- "overlap must be smaller than tile" - obvious in hindsight, but easy to hit while dialing in a small tile.
- "Upscale width must be >= tile_width" - if you're tiling and upscaling, the upscale target has to be at least as big as the tile, or the grid makes no sense.
If tiles are too small you'll get more seams to manage and slower runs for marginal VRAM savings; too big and you've recreated the OOM you were trying to dodge. The overlap/fade defaults are well chosen - tweak tile size first, leave the seam math alone until you can see a problem.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| upscale_width | INT | 102432–100000 | [rows_cols mode] Overall upscaled frame WIDTH in PIXELS that gets split into grid_cols equal-size tile columns. Must be a multiple of 32 and must match the width set in 'MMH3 Latent Upscale Params'. Ignored in specific_size mode. |
| upscale_height | INT | 102432–100000 | [rows_cols mode] Overall upscaled frame HEIGHT in PIXELS that gets split into grid_rows equal-size tile rows. Must be a multiple of 32 and must match the height set in 'MMH3 Latent Upscale Params'. Ignored in specific_size mode. |
| tile_size_mode | COMBO | specific_size | How the tile size is determined. 'specific_size' (default): use tile_width/tile_height below. 'rows_cols': split the frame given by upscale_width/upscale_height into grid_rows x grid_cols EQUAL-SIZE tiles (edge tiles included) - the per-axis overlap is auto-solved so every tile ends up exactly the same size; errors out if the solved tiles would be smaller than min_tile_size. |
| tile_width | INT | 51232–100000 | [specific_size mode] Tile width in PIXELS at the (upscaled) chunk resolution. Must be a multiple of 32. |
| tile_height | INT | 51232–100000 | [specific_size mode] Tile height in PIXELS at the (upscaled) chunk resolution. Must be a multiple of 32. |
| grid_rows | INT | 21–9 | [rows_cols mode] Number of tile ROWS along the height axis (1-9). |
| grid_cols | INT | 21–9 | [rows_cols mode] Number of tile COLUMNS along the width axis (1-9). |
| spatial_w_overlap | INT | 1280–100000 | Horizontal overlap in PIXELS between neighbouring tiles. Must be a multiple of 32 and smaller than the tile width. In rows_cols mode this is the DESIRED overlap; the node auto-solves the actual value (multiple of 16px, the H3 latent token) so all tiles stay equal. If the solved tile size would not be a multiple of 32 (the model's 2x2 latent patch grid, which eliminates seams), the overlap is automatically increased by 32px and the tiles re-solved at the next valid 32px grid alignment. |
| spatial_h_overlap | INT | 1280–100000 | Vertical overlap in PIXELS between neighbouring tiles. Must be a multiple of 32 and smaller than the tile height. In rows_cols mode this is the DESIRED overlap; the node auto-solves the actual value (multiple of 16px, the H3 latent token) so all tiles stay equal. If the solved tile size would not be a multiple of 32 (the model's 2x2 latent patch grid, which eliminates seams), the overlap is automatically increased by 32px and the tiles re-solved at the next valid 32px grid alignment. |
| fade_width | INT | 320–100000 | Width in PIXELS of the FADE segment (mask 0->1) at the interior edge of the overlap band. The overlap band splits into a FROZEN segment (seam side, mask=0, keeps the neighbour's content) + this FADE segment (interior side). fade_width sets the fade length; the frozen segment takes the rest (overlap - fade). Default 32. Set to 0 to freeze the entire overlap strip. Clamped to the solved overlap in rows_cols mode. |
| fade_height | INT | 320–100000 | Height in PIXELS of the FADE segment (mask 0->1) at the interior edge of the overlap band. The overlap band splits into a FROZEN segment (seam side, mask=0, keeps the neighbour's content) + this FADE segment (interior side). fade_height sets the fade length; the frozen segment takes the rest (overlap - fade). Default 32. Set to 0 to freeze the entire overlap strip. Clamped to the solved overlap in rows_cols mode. |
| min_tile_size | INT | 2560–100000 | Minimum PIXEL size of edge tiles. If a leftover edge tile would be smaller, the last tile is pulled back until it reaches at least this size; the seam overlap then grows and is blended over its full width. 256 (default) keeps small leftover tiles as-is. Must not exceed the tile size. In rows_cols mode an error is raised if the solved tile size falls below this. |
| overlap_mode | COMBO | earlier | Who wins each shared overlap band when stitching. 'earlier' (default): the already-stitched content wins. 'later': the re-sampled tile wins. Does NOT affect the noise mask. |
| overlap_blend | COMBO | linear | How the overlap band transitions when stitching: linear cross-fade (default), smoothstep (eased), overwrite (whole band from the overlap_mode side), midpoint (hard switch at the band's middle). |
| masked_area_noise | FLOAT | 0.000–1 | How much noise is allowed into the masked (frozen/fade) overlap band during sampling. 0 (default): current behaviour, the masked area gets no noise injection and stays fixed. 1.0: the mask has no effect and every tile is sampled freely. Small values like 0.01 let a little noise through to probe how much the scene moves under the mask. |
| brightness_match | BOOLEAN | false | Per-frame, per-channel median brightness match: after sampling, shift each tile's brightness baseline to the source region at the same (tile, frame). Reduces tile-to-tile and frame-to-frame luminance drift. Experimental - may be removed. |
| dynamic_fade | COMBO | off | Temporal fade schedule over each tile's sampling. 'off' (default): the FADE segment keeps its fixed width for the whole tile (current behaviour). 'narrowing': the fade width starts at fade_width/fade_height and shrinks linearly to dynamic_fade_min by the end of the tile. 'widening': it starts at dynamic_fade_min and grows linearly to fade_width/fade_height. Only takes effect when the tile is sampled in more than one step; if a per-axis fade is not larger than dynamic_fade_min it behaves like 'off'. |
| dynamic_fade_min | INT | 320–100000 | Minimum width (PIXELS) the FADE segment can reach when dynamic_fade is 'narrowing' or 'widening'. Only effective when a per-axis fade is larger than this; otherwise the fade stays at its static width. Default 32. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| spatial_split_param | H3_SPATIAL_PARAM | Spatial split settings consumed by 'MMH3 Ultimate Upscale'. |
| tile_width | INT | Resolved tile width in PIXELS: the validated input in specific_size mode, or the equal-tile solution computed from upscale_width/grid_cols in rows_cols mode. |
| tile_height | INT | Resolved tile height in PIXELS: the validated input in specific_size mode, or the equal-tile solution computed from upscale_height/grid_rows in rows_cols mode. |