Skip to content

Commit 8bb6a91

Browse files
committed
Fix LTX2 multihost JAX Array fetching error and refactor VAE decode
1 parent 9616d1c commit 8bb6a91

2 files changed

Lines changed: 74 additions & 48 deletions

File tree

src/maxdiffusion/pipelines/ltx2/ltx2_pipeline.py

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,66 @@ def _denormalize_latents(
11521152
latents = latents * latents_std / scaling_factor + latents_mean
11531153
return latents
11541154

1155+
def _decode_latents_to_video(
1156+
self,
1157+
latents: jax.Array,
1158+
generator: jax.Array,
1159+
batch_size: int,
1160+
decode_timestep: Union[float, List[float]],
1161+
decode_noise_scale: Optional[Union[float, List[float]]],
1162+
output_type: str,
1163+
replicate_vae: bool,
1164+
):
1165+
if replicate_vae:
1166+
try:
1167+
mesh = latents.sharding.mesh
1168+
replicated_sharding = NamedSharding(mesh, P())
1169+
# Replicate VAE weights
1170+
graphdef, state = nnx.split(self.vae)
1171+
state = jax.tree_util.tree_map(
1172+
lambda x: jax.lax.with_sharding_constraint(x, replicated_sharding) if isinstance(x, jax.Array) else x, state
1173+
)
1174+
self.vae = nnx.merge(graphdef, state)
1175+
except Exception as e: # pylint: disable=broad-exception-caught
1176+
max_logging.log(f"[Tuning] Failed to replicate VAE weights: {e}")
1177+
1178+
t0_video_vae = time.perf_counter()
1179+
with jax.named_scope("video_vae_decode"):
1180+
if getattr(self.vae.config, "timestep_conditioning", False):
1181+
noise = jax.random.normal(generator, latents.shape, dtype=latents.dtype)
1182+
1183+
if not isinstance(decode_timestep, list):
1184+
decode_timestep = [decode_timestep] * batch_size
1185+
if decode_noise_scale is None:
1186+
decode_noise_scale = decode_timestep
1187+
elif not isinstance(decode_noise_scale, list):
1188+
decode_noise_scale = [decode_noise_scale] * batch_size
1189+
1190+
timestep = jnp.array(decode_timestep, dtype=latents.dtype)
1191+
decode_noise_scale = jnp.array(decode_noise_scale, dtype=latents.dtype)[:, None, None, None, None]
1192+
1193+
latents = (1 - decode_noise_scale) * latents + decode_noise_scale * noise
1194+
1195+
latents = latents.astype(self.vae.dtype)
1196+
video = self.vae.decode(latents, temb=timestep, return_dict=False)[0]
1197+
else:
1198+
latents = latents.astype(self.vae.dtype)
1199+
video = self.vae.decode(latents, return_dict=False)[0]
1200+
1201+
video = video.block_until_ready()
1202+
video_vae_time = time.perf_counter() - t0_video_vae
1203+
max_logging.log(f"Video VAE decode time: {video_vae_time:.2f}s")
1204+
1205+
# VAE outputs (B, T, H, W, C), but video processor expects (B, C, T, H, W)
1206+
t0_video_post = time.perf_counter()
1207+
video = jax.experimental.multihost_utils.process_allgather(video, tiled=True)
1208+
video_np = np.array(video).transpose(0, 4, 1, 2, 3)
1209+
video = self.video_processor.postprocess_video(torch.from_numpy(video_np), output_type=output_type)
1210+
video_post_time = time.perf_counter() - t0_video_post
1211+
max_logging.log(f"Video Post-processing time (numpy+PIL): {video_post_time:.2f}s")
1212+
1213+
return video, video_vae_time, video_post_time
1214+
11551215
@staticmethod
11561216
def _normalize_audio_latents(latents: jax.Array, latents_mean: jax.Array, latents_std: jax.Array):
11571217
latents_mean = latents_mean.astype(latents.dtype)
@@ -1849,55 +1909,18 @@ def convert_to_vel(lat, x0, sig):
18491909
except Exception as e: # pylint: disable=broad-exception-caught
18501910
max_logging.log(f"[Tuning] Failed to apply replicate VAE latents sharding: {e}")
18511911

1852-
if replicate_vae:
1853-
try:
1854-
mesh = latents.sharding.mesh
1855-
replicated_sharding = NamedSharding(mesh, P())
1856-
# Replicate VAE weights
1857-
graphdef, state = nnx.split(self.vae)
1858-
state = jax.tree_util.tree_map(
1859-
lambda x: jax.lax.with_sharding_constraint(x, replicated_sharding) if isinstance(x, jax.Array) else x, state
1860-
)
1861-
self.vae = nnx.merge(graphdef, state)
1862-
except Exception as e: # pylint: disable=broad-exception-caught
1863-
max_logging.log(f"[Tuning] Failed to replicate VAE weights: {e}")
1864-
18651912
latent_processing_time += time.perf_counter() - t0_latent_processing
18661913
timings["Latent Processing"] = latent_processing_time
18671914

1868-
t0_video_vae = time.perf_counter()
1869-
with jax.named_scope("video_vae_decode"):
1870-
if getattr(self.vae.config, "timestep_conditioning", False):
1871-
noise = jax.random.normal(generator, latents.shape, dtype=latents.dtype)
1872-
1873-
if not isinstance(decode_timestep, list):
1874-
decode_timestep = [decode_timestep] * batch_size
1875-
if decode_noise_scale is None:
1876-
decode_noise_scale = decode_timestep
1877-
elif not isinstance(decode_noise_scale, list):
1878-
decode_noise_scale = [decode_noise_scale] * batch_size
1879-
1880-
timestep = jnp.array(decode_timestep, dtype=latents.dtype)
1881-
decode_noise_scale = jnp.array(decode_noise_scale, dtype=latents.dtype)[:, None, None, None, None]
1882-
1883-
latents = (1 - decode_noise_scale) * latents + decode_noise_scale * noise
1884-
1885-
latents = latents.astype(self.vae.dtype)
1886-
video = self.vae.decode(latents, temb=timestep, return_dict=False)[0]
1887-
else:
1888-
latents = latents.astype(self.vae.dtype)
1889-
video = self.vae.decode(latents, return_dict=False)[0]
1890-
1891-
video = video.block_until_ready()
1892-
video_vae_time = time.perf_counter() - t0_video_vae
1893-
max_logging.log(f"Video VAE decode time: {video_vae_time:.2f}s")
1894-
1895-
# VAE outputs (B, T, H, W, C), but video processor expects (B, C, T, H, W)
1896-
t0_video_post = time.perf_counter()
1897-
video_np = np.array(video).transpose(0, 4, 1, 2, 3)
1898-
video = self.video_processor.postprocess_video(torch.from_numpy(video_np), output_type=output_type)
1899-
video_post_time = time.perf_counter() - t0_video_post
1900-
max_logging.log(f"Video Post-processing time (numpy+PIL): {video_post_time:.2f}s")
1915+
video, video_vae_time, video_post_time = self._decode_latents_to_video(
1916+
latents=latents,
1917+
generator=generator,
1918+
batch_size=batch_size,
1919+
decode_timestep=decode_timestep,
1920+
decode_noise_scale=decode_noise_scale,
1921+
output_type=output_type,
1922+
replicate_vae=replicate_vae,
1923+
)
19011924

19021925
# Decode Audio
19031926
t0_audio_vae = time.perf_counter()
@@ -1917,6 +1940,7 @@ def convert_to_vel(lat, x0, sig):
19171940
audio = self._jitted_vocoder(self.vocoder, generated_mel_spectrograms)
19181941

19191942
# Convert audio to numpy
1943+
audio = jax.experimental.multihost_utils.process_allgather(audio, tiled=True)
19201944
audio = np.array(audio)
19211945
vocoder_time = time.perf_counter() - t0_vocoder
19221946
max_logging.log(f"Vocoder & Audio numpy time: {vocoder_time:.2f}s")

src/maxdiffusion/pipelines/ltx2/pipeline_ltx2_latent_upsample.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import jax
1818
import jax.numpy as jnp
1919
import numpy as np
20+
import torch
2021
from flax import nnx
2122
from flax.core.frozen_dict import FrozenDict
2223
from typing import Dict, List, Optional, Union
@@ -250,8 +251,9 @@ def __call__(
250251
if video.dtype == jnp.bfloat16:
251252
video = video.astype(jnp.float32)
252253

253-
video = np.transpose(np.array(video), (0, 4, 1, 2, 3))
254-
video = self.video_processor.postprocess_video(video, output_type=output_type)
254+
video = jax.experimental.multihost_utils.process_allgather(video, tiled=True)
255+
video_np = np.transpose(np.array(video), (0, 4, 1, 2, 3))
256+
video = self.video_processor.postprocess_video(torch.from_numpy(video_np), output_type=output_type)
255257

256258
if not return_dict:
257259
return (video,)

0 commit comments

Comments
 (0)