Skip to content

Commit 49cb0ce

Browse files
committed
Fall back to sharding the height in the WAN VAE.
The WAN VAE always sharded the width of its activations over the vae_spatial mesh axis. This works for the default 1280x720 landscape size, but users may also want to run inference on portrait videos 720x1280. The VAE downsamples their width to 720, 360, 180 and 90, which isn't always divisible by vae_spatial (e.g., 90 for 4). This change adds a _spatial_sharding(mesh, shape) helper that shards height if width **is not divisible** by vae_spatial, but height is divisible. Videos with a divisible width keep the current behavior.
1 parent bdb6359 commit 49cb0ce

1 file changed

Lines changed: 22 additions & 14 deletions

File tree

‎src/maxdiffusion/models/wan/autoencoder_kl_wan.py‎

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ def _update_cache(cache, idx, value):
4848
return cache[:idx] + (value,) + cache[idx + 1 :]
4949

5050

51+
def _spatial_sharding(mesh: Optional[jax.sharding.Mesh], shape: Sequence[int]) -> Optional[NamedSharding]:
52+
"""Returns the sharding of a (N, D, H, W, C) array over the vae_spatial axis.
53+
54+
Shards the width, or the height if only the height is divisible by
55+
vae_spatial. If neither is, the width is still sharded and XLA pads it.
56+
"""
57+
if mesh is None or "vae_spatial" not in mesh.shape:
58+
return None
59+
_, _, height, width, _ = shape
60+
num_shards = mesh.shape["vae_spatial"]
61+
if width % num_shards != 0 and height % num_shards == 0:
62+
return NamedSharding(mesh, P("redundant", None, "vae_spatial", None, None))
63+
return NamedSharding(mesh, P("redundant", None, None, "vae_spatial", None))
64+
65+
5166
# Helper to ensure kernel_size, stride, padding are tuples of 3 integers
5267
def _canonicalize_tuple(x: Union[int, Sequence[int]], rank: int, name: str) -> Tuple[int, ...]:
5368
"""Canonicalizes a value to a tuple of integers."""
@@ -121,7 +136,7 @@ def __init__(
121136

122137
def __call__(self, x: jax.Array, cache_x: Optional[jax.Array] = None, idx=-1) -> jax.Array:
123138
if self.mesh is not None and "vae_spatial" in self.mesh.shape:
124-
spatial_sharding = NamedSharding(self.mesh, P("redundant", None, None, "vae_spatial", None))
139+
spatial_sharding = _spatial_sharding(self.mesh, x.shape)
125140
if spatial_sharding is not None:
126141
x = jax.lax.with_sharding_constraint(x, spatial_sharding)
127142

@@ -1169,12 +1184,6 @@ def _encode(self, x: jax.Array, feat_cache: AutoencoderKLWanCache):
11691184
iter_ = 1 + ((t - 1 + CHUNK_SIZE - 1) // CHUNK_SIZE) if t > 1 else 1
11701185
enc_feat_map = feat_cache._enc_feat_map
11711186

1172-
spatial_sharding = (
1173-
NamedSharding(self.mesh, P("redundant", None, None, "vae_spatial", None))
1174-
if self.mesh is not None and "vae_spatial" in self.mesh.shape
1175-
else None
1176-
)
1177-
11781187
def finalize(out, enc_feat_map):
11791188
feat_cache._enc_feat_map = enc_feat_map
11801189
enc = self.quant_conv(out)
@@ -1184,6 +1193,7 @@ def finalize(out, enc_feat_map):
11841193
with jax.named_scope("AutoencoderKLWan_encode_chunk_0"):
11851194
chunk_0 = x[:, :1, ...]
11861195
out_0, enc_feat_map, _ = self.encoder(chunk_0, feat_cache=enc_feat_map, feat_idx=0)
1196+
spatial_sharding = _spatial_sharding(self.mesh, out_0.shape)
11871197
if spatial_sharding is not None:
11881198
out_0 = jax.lax.with_sharding_constraint(out_0, spatial_sharding)
11891199

@@ -1280,25 +1290,23 @@ def _decode(
12801290
x = self.post_quant_conv(z)
12811291

12821292
dec_feat_map = feat_cache._feat_map
1283-
spatial_sharding = (
1284-
NamedSharding(self.mesh, P("redundant", None, None, "vae_spatial", None))
1285-
if self.mesh is not None and "vae_spatial" in self.mesh.shape
1286-
else None
1287-
)
1293+
spatial_sharding = _spatial_sharding(self.mesh, x.shape)
12881294

12891295
# First chunk (i=0)
12901296
with jax.named_scope("AutoencoderKLWan_decode_chunk_0"):
1297+
chunk_in_0 = x[:, 0:1, ...]
12911298
if spatial_sharding is not None:
1292-
chunk_in_0 = jax.lax.with_sharding_constraint(x[:, 0:1, ...], spatial_sharding)
1299+
chunk_in_0 = jax.lax.with_sharding_constraint(chunk_in_0, spatial_sharding)
12931300
out_0, dec_feat_map, _ = self.decoder(chunk_in_0, feat_cache=dec_feat_map, feat_idx=0)
12941301
if spatial_sharding is not None:
12951302
out_0 = jax.lax.with_sharding_constraint(out_0, spatial_sharding)
12961303

12971304
if iter_ > 1:
12981305
# Run chunk 1 outside scan to properly form the cache shape
12991306
with jax.named_scope("AutoencoderKLWan_decode_chunk_1"):
1307+
chunk_in_1 = x[:, 1:2, ...]
13001308
if spatial_sharding is not None:
1301-
chunk_in_1 = jax.lax.with_sharding_constraint(x[:, 1:2, ...], spatial_sharding)
1309+
chunk_in_1 = jax.lax.with_sharding_constraint(chunk_in_1, spatial_sharding)
13021310
out_chunk_1, dec_feat_map, _ = self.decoder(chunk_in_1, feat_cache=dec_feat_map, feat_idx=0)
13031311
if spatial_sharding is not None:
13041312
out_chunk_1 = jax.lax.with_sharding_constraint(out_chunk_1, spatial_sharding)

0 commit comments

Comments
 (0)