Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc072a12bd | ||
|
|
81e5e8e3bb |
No files matched your search
@@ -779,6 +779,26 @@ def test_structured_diffusion_config_rejects_non_boolean_compile_dynamic():
|
|||||||
omni_config_module._DiffusionConfigProjection(diffusion_compile_dynamic="false")
|
omni_config_module._DiffusionConfigProjection(diffusion_compile_dynamic="false")
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_pipeline_config_routes_ltx2_model_extra(tmp_path):
|
||||||
|
deploy_path = tmp_path / "dreamzero_ltx2_extras.yaml"
|
||||||
|
deploy_path.write_text(
|
||||||
|
"\n".join(
|
||||||
|
[
|
||||||
|
"pipeline: dreamzero",
|
||||||
|
"async_chunk: false",
|
||||||
|
"stages:",
|
||||||
|
" - stage_id: 0",
|
||||||
|
" extras:",
|
||||||
|
" ltx2_use_channels_last_3d: true",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
stage = _from_pipeline_key("dreamzero", deploy_config_path=str(deploy_path)).stage_by_id(0)
|
||||||
|
|
||||||
|
assert stage.diffusion_config.extras["ltx2_use_channels_last_3d"] is True
|
||||||
|
|
||||||
|
|
||||||
def test_structured_diffusion_config_rejects_invalid_compile_granularity():
|
def test_structured_diffusion_config_rejects_invalid_compile_granularity():
|
||||||
with pytest.raises(ValidationError, match="diffusion_compile_granularity"):
|
with pytest.raises(ValidationError, match="diffusion_compile_granularity"):
|
||||||
omni_config_module._DiffusionConfigProjection(diffusion_compile_granularity="block")
|
omni_config_module._DiffusionConfigProjection(diffusion_compile_granularity="block")
|
||||||
|
|||||||
@@ -11,6 +11,100 @@ import torch
|
|||||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||||
|
|
||||||
|
|
||||||
|
class TestLTXChannelsLast3D:
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("extras", "expected"),
|
||||||
|
[({}, False), ({"ltx2_use_channels_last_3d": True}, True)],
|
||||||
|
)
|
||||||
|
def test_channels_last_opt_in_is_ltx2_model_extra(self, extras, expected):
|
||||||
|
from vllm_omni.diffusion.models.ltx2.ltx2_components import _ltx2_use_channels_last_3d
|
||||||
|
|
||||||
|
assert _ltx2_use_channels_last_3d(SimpleNamespace(extras=extras)) is expected
|
||||||
|
|
||||||
|
def test_channels_last_opt_in_rejects_non_boolean_model_extra(self):
|
||||||
|
from vllm_omni.diffusion.models.ltx2.ltx2_components import _ltx2_use_channels_last_3d
|
||||||
|
|
||||||
|
with pytest.raises(TypeError, match="ltx2_use_channels_last_3d"):
|
||||||
|
_ltx2_use_channels_last_3d(SimpleNamespace(extras={"ltx2_use_channels_last_3d": "true"}))
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("causal", "kernel_size"),
|
||||||
|
[
|
||||||
|
(True, 3),
|
||||||
|
(False, 3),
|
||||||
|
(True, (3, 1, 1)),
|
||||||
|
(True, 1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_layout_preserving_causal_conv_matches_original(self, causal, kernel_size):
|
||||||
|
from diffusers.models.autoencoders.autoencoder_kl_ltx2 import LTX2VideoCausalConv3d
|
||||||
|
|
||||||
|
from vllm_omni.diffusion.distributed.autoencoders.autoencoder_kl_ltx2 import (
|
||||||
|
_enable_channels_last_3d,
|
||||||
|
_weight_is_channels_last_3d,
|
||||||
|
)
|
||||||
|
|
||||||
|
torch.manual_seed(0)
|
||||||
|
conv = LTX2VideoCausalConv3d(4, 5, kernel_size).to(dtype=torch.float32).eval()
|
||||||
|
hidden_states = torch.randn(1, 4, 5, 6, 7, dtype=torch.float32)
|
||||||
|
|
||||||
|
# Pointwise Conv3d storage is valid in both contiguous formats because
|
||||||
|
# all spatial/temporal kernel dimensions are singleton.
|
||||||
|
if kernel_size != 1:
|
||||||
|
assert not _weight_is_channels_last_3d(conv)
|
||||||
|
with torch.no_grad():
|
||||||
|
expected = conv(hidden_states.clone(), causal=causal)
|
||||||
|
|
||||||
|
converted, patched = _enable_channels_last_3d(conv)
|
||||||
|
|
||||||
|
assert (converted, patched) == (1, 1)
|
||||||
|
assert _weight_is_channels_last_3d(conv)
|
||||||
|
with torch.no_grad():
|
||||||
|
actual = conv(hidden_states.clone(), causal=causal)
|
||||||
|
|
||||||
|
torch.testing.assert_close(actual, expected, rtol=1e-4, atol=1e-4)
|
||||||
|
|
||||||
|
def test_temporal_pad_replicates_edges_in_channels_last_3d(self):
|
||||||
|
from vllm_omni.diffusion.distributed.autoencoders.autoencoder_kl_ltx2 import (
|
||||||
|
_temporal_pad_channels_last_3d,
|
||||||
|
)
|
||||||
|
|
||||||
|
torch.manual_seed(0)
|
||||||
|
hidden_states = torch.randn(1, 4, 3, 2, 2, dtype=torch.float32)
|
||||||
|
|
||||||
|
actual = _temporal_pad_channels_last_3d(hidden_states, left=2, right=1)
|
||||||
|
expected = torch.cat(
|
||||||
|
[
|
||||||
|
hidden_states[:, :, :1].repeat(1, 1, 2, 1, 1),
|
||||||
|
hidden_states,
|
||||||
|
hidden_states[:, :, -1:],
|
||||||
|
],
|
||||||
|
dim=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert actual.is_contiguous(memory_format=torch.channels_last_3d)
|
||||||
|
torch.testing.assert_close(actual, expected, rtol=0, atol=0)
|
||||||
|
|
||||||
|
def test_contiguous_weight_keeps_original_padding_path(self, monkeypatch):
|
||||||
|
from diffusers.models.autoencoders.autoencoder_kl_ltx2 import LTX2VideoCausalConv3d
|
||||||
|
|
||||||
|
import vllm_omni.diffusion.distributed.autoencoders.autoencoder_kl_ltx2 as ltx2_vae
|
||||||
|
|
||||||
|
conv = LTX2VideoCausalConv3d(4, 5, 3).to(dtype=torch.float32).eval()
|
||||||
|
hidden_states = torch.randn(1, 4, 3, 2, 2, dtype=torch.float32)
|
||||||
|
|
||||||
|
def fail_channels_last_pad(*_args, **_kwargs):
|
||||||
|
raise AssertionError("contiguous weights must retain the original padding path")
|
||||||
|
|
||||||
|
monkeypatch.setattr(ltx2_vae, "_temporal_pad_channels_last_3d", fail_channels_last_pad)
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
expected = conv(hidden_states, causal=True)
|
||||||
|
actual = ltx2_vae._layout_preserving_causal_conv_forward(conv, hidden_states, causal=True)
|
||||||
|
|
||||||
|
torch.testing.assert_close(actual, expected, rtol=0, atol=0)
|
||||||
|
|
||||||
|
|
||||||
class TestLTXOutputRank:
|
class TestLTXOutputRank:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("distributed_vae_state", "expected_decode_calls"),
|
("distributed_vae_state", "expected_decode_calls"),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ def test_default_stage_config_includes_cache_backend():
|
|||||||
"cache_backend": "cache_dit",
|
"cache_backend": "cache_dit",
|
||||||
"cache_config": '{"Fn_compute_blocks": 2}',
|
"cache_config": '{"Fn_compute_blocks": 2}',
|
||||||
"vae_use_slicing": True,
|
"vae_use_slicing": True,
|
||||||
|
"extras": {"ltx2_use_channels_last_3d": True},
|
||||||
"ulysses_degree": 2,
|
"ulysses_degree": 2,
|
||||||
}
|
}
|
||||||
)[0]
|
)[0]
|
||||||
@@ -29,6 +30,7 @@ def test_default_stage_config_includes_cache_backend():
|
|||||||
assert engine_args["cache_backend"] == "cache_dit"
|
assert engine_args["cache_backend"] == "cache_dit"
|
||||||
assert engine_args["cache_config"]["Fn_compute_blocks"] == 2
|
assert engine_args["cache_config"]["Fn_compute_blocks"] == 2
|
||||||
assert engine_args["vae_use_slicing"] is True
|
assert engine_args["vae_use_slicing"] is True
|
||||||
|
assert engine_args["extras"]["ltx2_use_channels_last_3d"] is True
|
||||||
assert engine_args["parallel_config"].ulysses_degree == 2
|
assert engine_args["parallel_config"].ulysses_degree == 2
|
||||||
assert engine_args["model_stage"] == "diffusion"
|
assert engine_args["model_stage"] == "diffusion"
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||||
|
|
||||||
|
from types import MethodType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from diffusers import AutoencoderKLLTX2Video
|
from diffusers import AutoencoderKLLTX2Video
|
||||||
|
from diffusers.models.autoencoders.autoencoder_kl_ltx2 import LTX2VideoCausalConv3d
|
||||||
from diffusers.models.autoencoders.vae import DecoderOutput
|
from diffusers.models.autoencoders.vae import DecoderOutput
|
||||||
|
from torch import nn
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
|
|
||||||
from vllm_omni.diffusion.distributed.autoencoders.distributed_vae_executor import (
|
from vllm_omni.diffusion.distributed.autoencoders.distributed_vae_executor import (
|
||||||
@@ -19,6 +22,89 @@ from vllm_omni.diffusion.distributed.autoencoders.distributed_vae_executor impor
|
|||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Adapted from https://github.com/sgl-project/sglang/pull/27431.
|
||||||
|
def _weight_is_channels_last_3d(module: LTX2VideoCausalConv3d) -> bool:
|
||||||
|
weight = module.conv.weight
|
||||||
|
return (
|
||||||
|
weight.dim() == 5
|
||||||
|
and hasattr(torch, "channels_last_3d")
|
||||||
|
and weight.is_contiguous(memory_format=torch.channels_last_3d)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _temporal_pad_channels_last_3d(
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
*,
|
||||||
|
left: int,
|
||||||
|
right: int,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
"""Replicate temporal edge frames directly into CL3D storage."""
|
||||||
|
batch, channels, frames, height, width = hidden_states.shape
|
||||||
|
padded = torch.empty(
|
||||||
|
(batch, channels, frames + left + right, height, width),
|
||||||
|
dtype=hidden_states.dtype,
|
||||||
|
device=hidden_states.device,
|
||||||
|
memory_format=torch.channels_last_3d,
|
||||||
|
)
|
||||||
|
padded[:, :, left : left + frames].copy_(hidden_states)
|
||||||
|
if left:
|
||||||
|
padded[:, :, :left].copy_(hidden_states[:, :, :1])
|
||||||
|
if right:
|
||||||
|
padded[:, :, left + frames :].copy_(hidden_states[:, :, -1:])
|
||||||
|
return padded
|
||||||
|
|
||||||
|
|
||||||
|
def _layout_preserving_causal_conv_forward(
|
||||||
|
module: LTX2VideoCausalConv3d,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
causal: bool = True,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
"""Diffusers LTX causal-conv math with a CL3D-preserving pad path."""
|
||||||
|
time_kernel_size = module.kernel_size[0]
|
||||||
|
use_channels_last_pad = hidden_states.dim() == 5 and _weight_is_channels_last_3d(module)
|
||||||
|
|
||||||
|
if use_channels_last_pad:
|
||||||
|
if causal:
|
||||||
|
left, right = time_kernel_size - 1, 0
|
||||||
|
else:
|
||||||
|
left = right = (time_kernel_size - 1) // 2
|
||||||
|
if left == 0 and right == 0:
|
||||||
|
hidden_states = hidden_states.contiguous(memory_format=torch.channels_last_3d)
|
||||||
|
else:
|
||||||
|
hidden_states = _temporal_pad_channels_last_3d(
|
||||||
|
hidden_states,
|
||||||
|
left=left,
|
||||||
|
right=right,
|
||||||
|
)
|
||||||
|
elif causal:
|
||||||
|
pad_left = hidden_states[:, :, :1, :, :].repeat((1, 1, time_kernel_size - 1, 1, 1))
|
||||||
|
hidden_states = torch.concatenate([pad_left, hidden_states], dim=2)
|
||||||
|
else:
|
||||||
|
pad_size = (time_kernel_size - 1) // 2
|
||||||
|
pad_left = hidden_states[:, :, :1, :, :].repeat((1, 1, pad_size, 1, 1))
|
||||||
|
pad_right = hidden_states[:, :, -1:, :, :].repeat((1, 1, pad_size, 1, 1))
|
||||||
|
hidden_states = torch.concatenate([pad_left, hidden_states, pad_right], dim=2)
|
||||||
|
|
||||||
|
return module.conv(hidden_states)
|
||||||
|
|
||||||
|
|
||||||
|
def _enable_channels_last_3d(model: nn.Module) -> tuple[int, int]:
|
||||||
|
"""Convert LTX Conv3d weights and install layout-preserving causal pads."""
|
||||||
|
if not hasattr(torch, "channels_last_3d"):
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
converted_convs = 0
|
||||||
|
patched_causal_convs = 0
|
||||||
|
for module in model.modules():
|
||||||
|
if isinstance(module, LTX2VideoCausalConv3d):
|
||||||
|
module.forward = MethodType(_layout_preserving_causal_conv_forward, module)
|
||||||
|
patched_causal_convs += 1
|
||||||
|
if isinstance(module, nn.Conv3d):
|
||||||
|
module.weight.data = module.weight.data.contiguous(memory_format=torch.channels_last_3d)
|
||||||
|
converted_convs += 1
|
||||||
|
return converted_convs, patched_causal_convs
|
||||||
|
|
||||||
|
|
||||||
class LTX2VaeExecutor(DistributedVaeExecutor):
|
class LTX2VaeExecutor(DistributedVaeExecutor):
|
||||||
def _pack_local_tiles_without_meta(self, local_results, global_padding_shape, device, dtype):
|
def _pack_local_tiles_without_meta(self, local_results, global_padding_shape, device, dtype):
|
||||||
tile_tensor = torch.zeros(global_padding_shape, device=device, dtype=dtype)
|
tile_tensor = torch.zeros(global_padding_shape, device=device, dtype=dtype)
|
||||||
@@ -80,6 +166,16 @@ class DistributedAutoencoderKLLTX2Video(AutoencoderKLLTX2Video, DistributedVaeMi
|
|||||||
model.init_distributed()
|
model.init_distributed()
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
def enable_channels_last_3d(self) -> None:
|
||||||
|
"""Opt into the layout-preserving CL3D path for this video VAE."""
|
||||||
|
converted_convs, patched_causal_convs = _enable_channels_last_3d(self)
|
||||||
|
logger.info(
|
||||||
|
"LTX2 video VAE: converted %d Conv3d weights to channels_last_3d and "
|
||||||
|
"installed %d layout-preserving causal conv paths",
|
||||||
|
converted_convs,
|
||||||
|
patched_causal_convs,
|
||||||
|
)
|
||||||
|
|
||||||
def init_distributed(self):
|
def init_distributed(self):
|
||||||
self.distributed_executor = LTX2VaeExecutor()
|
self.distributed_executor = LTX2VaeExecutor()
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,17 @@ _LTX_COMPONENT_SUBFOLDERS = (
|
|||||||
)
|
)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_LTX2_CHANNELS_LAST_3D_EXTRA = "ltx2_use_channels_last_3d"
|
||||||
|
|
||||||
|
|
||||||
|
def _ltx2_use_channels_last_3d(od_config: Any) -> bool:
|
||||||
|
"""Read the LTX2-only channels-last opt-in from model extras."""
|
||||||
|
extras = getattr(od_config, "extras", {}) or {}
|
||||||
|
enabled = extras.get(_LTX2_CHANNELS_LAST_3D_EXTRA, False)
|
||||||
|
if not isinstance(enabled, bool):
|
||||||
|
raise TypeError(f"{_LTX2_CHANNELS_LAST_3D_EXTRA} must be a bool, got {type(enabled)!r}")
|
||||||
|
return enabled
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class LTXComponentProfile:
|
class LTXComponentProfile:
|
||||||
@@ -365,6 +376,8 @@ def initialize_pipeline_components(pipeline: Any, od_config: Any) -> None:
|
|||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
dtype=dtype,
|
dtype=dtype,
|
||||||
)
|
)
|
||||||
|
if _ltx2_use_channels_last_3d(od_config):
|
||||||
|
pipeline.vae.enable_channels_last_3d()
|
||||||
pipeline.audio_vae = _load_component(
|
pipeline.audio_vae = _load_component(
|
||||||
AutoencoderKLLTX2Audio,
|
AutoencoderKLLTX2Audio,
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -1075,6 +1075,7 @@ class AsyncOmniEngine:
|
|||||||
"streaming_output": kwargs.get("diffusion_streaming_output", False),
|
"streaming_output": kwargs.get("diffusion_streaming_output", False),
|
||||||
"enable_ar_profiler": kwargs.get("enable_ar_profiler", False),
|
"enable_ar_profiler": kwargs.get("enable_ar_profiler", False),
|
||||||
"extras": {
|
"extras": {
|
||||||
|
**(kwargs.get("extras") or {}),
|
||||||
"auxiliary_text_encoder": kwargs.get("auxiliary_text_encoder", None),
|
"auxiliary_text_encoder": kwargs.get("auxiliary_text_encoder", None),
|
||||||
"default_llama_model_id": kwargs.get("default_llama_model_id", "meta-llama/Meta-Llama-3.1-8B-Instruct"),
|
"default_llama_model_id": kwargs.get("default_llama_model_id", "meta-llama/Meta-Llama-3.1-8B-Instruct"),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -668,7 +668,6 @@ class OmniServeCommand(CLISubcommand):
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Enable VAE tiling for memory optimization (useful for mitigating OOM issues).",
|
help="Enable VAE tiling for memory optimization (useful for mitigating OOM issues).",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parallel weight loading (faster diffusion startup)
|
# Parallel weight loading (faster diffusion startup)
|
||||||
omni_config_group.add_argument(
|
omni_config_group.add_argument(
|
||||||
"--disable-multithread-weight-load",
|
"--disable-multithread-weight-load",
|
||||||
|
|||||||
Reference in new issue
Block a user