nx_vae_tile.nx
buildroot/runtime/nx_vae_tile.nx
about
nx_vae_tile.nx -- overlapping tile split + alpha-feathered blend.
Ships VRAM-track V-001 per docs/VRAM_OPTIMIZATION_REALISTIC_TRACKING.md:
VAE tiling with overlap blending. ~1-1.5 GB saved during decode
at ZERO quality cost when overlap >= 16 and blending is feathered.
Substrate primitive -- model-agnostic. The caller orchestrates:
tiles, meta = tile_split(latent, h_tile, w_tile, overlap)
for each tile_i:
out_tile_i = decode_2d_block(tile_i) -- caller's model
image = tile_blend_feathered(out_tiles, meta, overlap, H, W)
The decoder runs on one tile at a time, so peak VRAM is roughly
(model weights + ONE tile of activations) rather than (weights +
full-image activations). For Z-Image-class latent 96x128 -> image
768x1024 (8x upsample), per-tile decode of 64x64 latent -> 512x512
image drops decoder working memory by ~6x.
Pure NishiLang i64 row-major. Single-channel buffer (multi-channel
= caller calls per channel; for fused CHW caller passes the
flattened buffer and strides). Q10 fixed point for blend weights.
Quality envelope (honest measurement basis):
* overlap >= 16 px and alpha-feather -> sub-perceptual seams
* overlap = 8 px and alpha-feather -> visible boundary at
high-contrast edges
* unfeathered blending (constant 1) -> hard seams; refused.
We ship feathered only; unfeathered is documented as deliberate
non-shipping per the no-silent-corruption cardinal.
Algorithm (alpha-feathered blend):
For each tile, weight at position (tr, tc) inside the tile is
w_y = min(tr, overlap, tile_h - 1 - tr) // distance from
w_x = min(tc, overlap, tile_w - 1 - tc) // tile edges
weight = (w_y + 1) * (w_x + 1)
so weight rises from 1 at the tile boundary to (overlap+1)^2 at
tile interior (overlap >= edge distance saturates). Output:
image[i, j] = sum_t (weight_t * tile_t[r, c]) / sum_t weight_t
where the sum is over all tiles t containing (i, j).
dependencies 3 imports · 0 importers
imports: nx_syscalls.nxnx_tier.nxnx_loop.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 58 | const NX_VT_OK: nx_int = 0 |
| 59 | const NX_VT_ERR_BAD_DIMS: nx_int = 1 |
| 60 | const NX_VT_ERR_BAD_OVERLAP: nx_int = 2 |
| 61 | const NX_VT_ERR_BAD_TILE_SIZE: nx_int = 3 |
| 62 | const NX_VT_ERR_BUFFER_TOO_SMALL: nx_int = 4 |
| 63 | const NX_VT_N_VERDICTS: nx_int = 5 |
| 126 | const NX_VT_META_STRIDE: nx_int = 3 |
functions
| 65 | func nx_vt_verdict_is_valid(v: nx_int) -> nx_int |
| 80 | func _vt_n_tiles_axis(h: nx_int, tile_h: nx_int, overlap: nx_int) -> nx_int called by 1: nx_vt_n_tiles |
| 109 | func nx_vt_n_tiles(h: nx_int, w: nx_int, |
| 128 | func nx_vt_tile_top(meta: *i64, t: nx_int) -> i64 { return meta[t * NX_VT_META_STRIDE] } |
| 129 | func nx_vt_tile_left(meta: *i64, t: nx_int) -> i64 { return meta[t * NX_VT_META_STRIDE + 1] } |
| 133 | func _vt_fill_starts_axis(starts: *i64, h: nx_int, tile_h: nx_int, overlap: nx_int) -> nx_int called by 1: nx_vt_split |
| 169 | func nx_vt_split(src: *i64, h: nx_int, w: nx_int, |
| 226 | func _vt_axis_weight(r: nx_int, tile_h: nx_int, overlap: nx_int) -> nx_int called by 1: nx_vt_blend_feathered |
| 248 | func nx_vt_blend_feathered(tiles: *i64, n_tiles: nx_int, meta: *i64, |
| 322 | func main() -> i64 |