Files
nvlabs--sana/diffusion/model/nets/sana_multi_scale_video_camctrl.py
2026-07-13 13:09:03 +08:00

1958 lines
84 KiB
Python
Executable File

# Copyright 2024 NVIDIA CORPORATION & AFFILIATES
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# This file is modified from https://github.com/PixArt-alpha/PixArt-sigma
import os
import warnings
from typing import Optional
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.nn.functional as F
from timm.models.layers import DropPath
from torch.nn.attention.flex_attention import flex_attention
from diffusion.distributed.context_parallel.config import cp_enabled, get_cp_group
from diffusion.model.builder import MODELS
from diffusion.model.nets.basic_modules import ChunkGLUMBConvTemp, GLUMBConv, GLUMBConvTemp, Mlp
from diffusion.model.nets.sana_blocks import (
CaptionEmbedder,
CausalWanRotaryPosEmbed,
ClipVisionProjection,
FlashAttention,
MultiHeadCrossAttention,
MultiHeadCrossAttentionImageEmbed,
PatchEmbedMS3D,
RopePosEmbed,
T2IFinalLayer,
WanRotaryPosEmbed,
WanRotaryTemporalPosEmbed,
WindowAttention,
t2i_modulate,
)
from diffusion.model.nets.sana_multi_scale import Sana, get_2d_sincos_pos_embed
from diffusion.model.registry import ATTENTION_BLOCKS, FFN_BLOCKS
from diffusion.model.utils import auto_grad_checkpoint, create_block_mask_cached, generate_temporal_head_mask_mod
from diffusion.model.wan.model import BlockHook
from diffusion.utils.dist_utils import get_rank
from diffusion.utils.import_utils import is_xformers_available
from .sana_camctrl_blocks import (
_maybe_drop_cam_branch,
_process_camera_conditions_ucpe,
prepare_prope_fns,
)
from .sana_gdn_blocks_triton import (
BidirectionalGDNUCPESinglePathLiteLABothTriton,
ChunkCausalGDNUCPESinglePathLiteLABothTriton,
)
from .sana_gdn_camctrl_blocks import (
BidirectionalSoftmaxUCPESinglePathLiteLA,
CachedChunkCausalGDNUCPESinglePathLiteLA,
CachedSoftmaxUCPESinglePathLiteLA,
)
# xformers is OFF by default on this stack (see diffusion/model/nets/sana_blocks.py for rationale).
# Opt in with ENABLE_XFORMERS=1.
_xformers_available = (
os.environ.get("ENABLE_XFORMERS", "0") == "1"
and os.environ.get("DISABLE_XFORMERS", "0") != "1"
and is_xformers_available()
)
class DeltaActionEmbedder(nn.Module):
def __init__(self, input_dim, hidden_size, act_layer=nn.GELU):
super().__init__()
self.mlp = nn.Sequential(
nn.Linear(input_dim, hidden_size),
act_layer(),
nn.Linear(hidden_size, hidden_size),
)
def forward(self, x):
return self.mlp(x)
class FP32LayerNorm(nn.LayerNorm):
def forward(self, x):
return super().forward(x.float()).type_as(x)
class FP32NormProxy(nn.Module):
def __init__(self, norm_module):
super().__init__()
self.norm = norm_module
def forward(self, x):
return self.norm(x.float()).type_as(x)
class SanaVideoMSCamCtrlBlock(nn.Module):
"""
A Sana block with global shared adaptive layer norm zero (adaLN-Zero) conditioning.
"""
def __init__(
self,
hidden_size,
num_heads,
mlp_ratio=4.0,
drop_path=0.0,
qk_norm=False,
attn_type="flash",
ffn_type="mlp",
mlp_acts=("silu", "silu", None),
linear_head_dim=32,
cross_norm=False,
cross_attn_image_embeds=False,
t_kernel_size=3,
additional_flash_attn=False,
flash_attn_window_count=None,
camctrl_cls=None,
patch_size=(1, 2, 2),
cam_attn_compress=2,
fp32_norm=False,
chunk_size=10,
chunk_split_strategy="uniform",
use_delta_pose_additive=False,
use_chunk_plucker_post_attn=False,
**block_kwargs,
):
super().__init__()
self.hidden_size = hidden_size
self.chunk_size = chunk_size
self.chunk_split_strategy = chunk_split_strategy
if use_delta_pose_additive:
self.delta_pose_proj = nn.Linear(hidden_size, hidden_size, bias=True)
nn.init.zeros_(self.delta_pose_proj.weight)
nn.init.zeros_(self.delta_pose_proj.bias)
if use_chunk_plucker_post_attn:
self.plucker_proj = nn.Linear(hidden_size, hidden_size, bias=True)
nn.init.zeros_(self.plucker_proj.weight)
nn.init.zeros_(self.plucker_proj.bias)
if fp32_norm:
self.norm1 = FP32LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
else:
self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
# Self-attention slot: when ``camctrl_cls`` is given, instantiate the
# UCPE camera-controlled attention; otherwise fall back to the plain
# ``attn_type`` registered in ATTENTION_BLOCKS (used for non-camctrl
# layers, e.g. when camctrl_layers_num < depth).
if camctrl_cls is not None:
self_num_heads = hidden_size // linear_head_dim
self.attn = camctrl_cls(
hidden_size,
hidden_size,
heads=self_num_heads,
cam_dim=hidden_size // cam_attn_compress,
cam_heads=max(1, self_num_heads // cam_attn_compress),
eps=1e-8,
qk_norm=qk_norm,
patch_size=patch_size,
**block_kwargs,
)
else:
attn_cls = ATTENTION_BLOCKS.get(attn_type)
if attn_cls is None:
raise ValueError(f"Unknown attn_type: {attn_type}")
self.attn = attn_cls(
hidden_size,
hidden_size,
heads=hidden_size // linear_head_dim,
eps=1e-8,
qk_norm=qk_norm,
)
if additional_flash_attn == "flash":
self.learnable_fa_scale = nn.Parameter(torch.ones(1) * 100)
self.flash_attn_additional = FlashAttention(
hidden_size,
num_heads=num_heads,
qkv_bias=True,
qk_norm=qk_norm,
**block_kwargs,
)
elif additional_flash_attn == "window_flash":
self.learnable_fa_scale = nn.Parameter(torch.ones(1) * 100)
self.flash_attn_additional = WindowAttention(
hidden_size,
num_heads=num_heads,
qkv_bias=True,
qk_norm=qk_norm,
window_count=flash_attn_window_count,
pad_if_needed=True,
**block_kwargs,
)
else:
self.flash_attn_additional = None
# Cross Attention
self.cross_attn_image_embeds = cross_attn_image_embeds
if cross_attn_image_embeds:
self.cross_attn = MultiHeadCrossAttentionImageEmbed(
hidden_size, num_heads, qk_norm=cross_norm, **block_kwargs
)
else:
self.cross_attn = MultiHeadCrossAttention(hidden_size, num_heads, qk_norm=cross_norm, **block_kwargs)
if fp32_norm:
self.norm2 = FP32LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
else:
self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
if fp32_norm and self.attn is not None:
if hasattr(self.attn, "q_norm"):
self.attn.q_norm = FP32NormProxy(self.attn.q_norm)
if hasattr(self.attn, "k_norm"):
self.attn.k_norm = FP32NormProxy(self.attn.k_norm)
if hasattr(self.attn, "norm_q"):
self.attn.norm_q = FP32NormProxy(self.attn.norm_q)
if hasattr(self.attn, "norm_k"):
self.attn.norm_k = FP32NormProxy(self.attn.norm_k)
# MLP
if ffn_type == "glumbconv":
self.mlp = GLUMBConv(
in_features=hidden_size,
hidden_features=int(hidden_size * mlp_ratio),
use_bias=(True, True, False),
norm=(None, None, None),
act=mlp_acts,
)
elif ffn_type == "GLUMBConvTemp":
self.mlp = GLUMBConvTemp(
in_features=hidden_size,
hidden_features=int(hidden_size * mlp_ratio),
use_bias=(True, True, False),
norm=(None, None, None),
act=mlp_acts,
t_kernel_size=t_kernel_size,
)
elif ffn_type == "ChunkGLUMBConvTemp":
self.mlp = ChunkGLUMBConvTemp(
in_features=hidden_size,
hidden_features=int(hidden_size * mlp_ratio),
use_bias=(True, True, False),
norm=(None, None, None),
act=mlp_acts,
t_kernel_size=t_kernel_size,
)
elif ffn_type == "mlp":
approx_gelu = lambda: nn.GELU(approximate="tanh")
self.mlp = Mlp(
in_features=hidden_size, hidden_features=int(hidden_size * mlp_ratio), act_layer=approx_gelu, drop=0
)
else:
ffn_cls = FFN_BLOCKS.get(ffn_type) if ffn_type else None
if ffn_cls is not None:
self.mlp = ffn_cls(
in_features=hidden_size,
hidden_features=int(hidden_size * mlp_ratio),
use_bias=(True, True, False),
norm=(None, None, None),
act=mlp_acts,
t_kernel_size=t_kernel_size,
)
else:
self.mlp = None
self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
self.scale_shift_table = nn.Parameter(torch.randn(6, hidden_size) / hidden_size**0.5)
self.block_hook: Optional[BlockHook] = None
@staticmethod
def _build_frame_token_mask(
frame_valid_mask: Optional[torch.Tensor],
*,
B: int,
T: int,
N: int,
device: torch.device,
dtype: torch.dtype,
) -> Optional[torch.Tensor]:
"""Convert frame-valid mask to token mask shaped ``(B, N, 1)``."""
if frame_valid_mask is None:
return None
m = frame_valid_mask
if m.ndim == 5:
m = m[:, 0, :, 0, 0]
elif m.ndim == 3 and m.shape[1] == 1:
m = m[:, 0, :]
elif m.ndim != 2:
raise ValueError(
"frame_valid_mask must be shaped (B, 1, T, 1, 1), (B, 1, T), or (B, T); "
f"got shape={list(frame_valid_mask.shape)}"
)
if m.shape[0] != B or m.shape[1] != T:
raise ValueError(f"frame_valid_mask shape mismatch: expected (B={B}, T={T}), got {list(m.shape)}")
if T <= 0 or N % T != 0:
raise ValueError(f"Invalid token/frame layout: N={N}, T={T}")
S = N // T
return m.to(device=device, dtype=dtype).view(B, T, 1).expand(B, T, S).reshape(B, N, 1)
def forward_frame_aware(
self, x, y, t, mask=None, THW=None, rotary_emb=None, block_mask=None, chunk_index=None, **kwargs
):
B, N, C = x.shape
num_frames = t.shape[2]
frame_valid_mask = kwargs.get("frame_valid_mask", None)
frame_token_mask = self._build_frame_token_mask(
frame_valid_mask,
B=B,
T=num_frames,
N=N,
device=x.device,
dtype=x.dtype,
)
if frame_token_mask is not None:
x = x * frame_token_mask
t = t.reshape(B, num_frames, 6, -1) # B,F,6,D
# scale_shift_table: 6, hidden_size -> 1,1,6,hidden_size
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
self.scale_shift_table[None, None, :, :] + t
).chunk(
6, dim=-2
) # each chunk: B,F,1,D
self_attn_kwargs = {
"HW": THW,
"rotary_emb": rotary_emb,
"block_mask": block_mask,
"camera_conditions": kwargs.get("camera_conditions", None),
"prope_fns": kwargs.get("prope_fns", None),
"camera_embedding": kwargs.get("camera_embedding", None),
"frame_valid_mask": frame_valid_mask,
}
cam_branch_drop_prob = kwargs.get("cam_branch_drop_prob", None)
if cam_branch_drop_prob is not None:
self_attn_kwargs["cam_branch_drop_prob"] = cam_branch_drop_prob
if chunk_index is not None:
self_attn_kwargs["chunk_index"] = chunk_index[:] # NOTE: important, copy the list
if kwargs.get("chunk_index_global", None) is not None:
self_attn_kwargs["chunk_index_global"] = kwargs.get("chunk_index_global")
chunk_split_strategy = kwargs.get("chunk_split_strategy", getattr(self, "chunk_split_strategy", "uniform"))
if chunk_split_strategy is not None:
self_attn_kwargs["chunk_split_strategy"] = chunk_split_strategy
chunk_size = kwargs.get("chunk_size", getattr(self, "chunk_size", 10))
if chunk_size is not None:
self_attn_kwargs["chunk_size"] = chunk_size
save_kv_cache = kwargs.get("save_kv_cache", None)
if save_kv_cache is not None:
self_attn_kwargs["save_kv_cache"] = save_kv_cache
kv_cache = kwargs.get("kv_cache", None)
if kv_cache is not None:
self_attn_kwargs["kv_cache"] = kv_cache
x_norm1 = self.norm1(x).reshape(B, num_frames, -1, C)
x_msa_in = t2i_modulate(x_norm1, shift_msa, scale_msa).reshape(B, N, C)
if frame_token_mask is not None:
x_msa_in = x_msa_in * frame_token_mask
attn_out_raw = self.attn(x_msa_in, **self_attn_kwargs)
if kv_cache is not None:
attn_out_raw, kv_cache = attn_out_raw
attn_out = attn_out_raw.reshape(B, num_frames, -1, C)
attn_out = (gate_msa * attn_out).reshape(B, N, C)
if frame_token_mask is not None:
attn_out = attn_out * frame_token_mask
x = x + self.drop_path(attn_out)
if frame_token_mask is not None:
x = x * frame_token_mask
delta_pose_emb = kwargs.get("delta_pose_emb", None)
if delta_pose_emb is not None and hasattr(self, "delta_pose_proj"):
S = N // num_frames
dpe = delta_pose_emb.unsqueeze(2).expand(-1, -1, S, -1).reshape(B, N, C)
x = x + self.delta_pose_proj(dpe)
plucker_emb = kwargs.get("plucker_emb", None)
if plucker_emb is not None and hasattr(self, "plucker_proj"):
x = x + self.plucker_proj(plucker_emb)
if self.flash_attn_additional:
x = x + self.flash_attn_additional(x, HW=THW)
if frame_token_mask is not None:
x = x * frame_token_mask
if self.cross_attn_image_embeds:
x = x + self.cross_attn(x, y, mask=mask, image_embeds=kwargs.get("image_embeds", None))
else:
x = x + self.cross_attn(x, y, mask=mask)
if frame_token_mask is not None:
x = x * frame_token_mask
mlp_kwargs = {
"HW": THW,
"frame_valid_mask": frame_valid_mask,
}
if chunk_index is not None:
mlp_kwargs["chunk_index"] = chunk_index[:] # NOTE: important, copy the list
if kwargs.get("chunk_index_global", None) is not None:
mlp_kwargs["chunk_index_global"] = kwargs.get("chunk_index_global")
if chunk_split_strategy is not None:
mlp_kwargs["chunk_split_strategy"] = chunk_split_strategy
chunk_size = kwargs.get("chunk_size", getattr(self, "chunk_size", 10))
if chunk_size is not None:
mlp_kwargs["chunk_size"] = chunk_size
if save_kv_cache is not None:
mlp_kwargs["save_kv_cache"] = save_kv_cache
if kv_cache is not None:
mlp_kwargs["kv_cache"] = kv_cache
x_norm2 = self.norm2(x).reshape(B, num_frames, -1, C)
x_mlp_in = t2i_modulate(x_norm2, shift_mlp, scale_mlp).reshape(B, N, C)
if frame_token_mask is not None:
x_mlp_in = x_mlp_in * frame_token_mask
mlp_out_raw = self.mlp(x_mlp_in, **mlp_kwargs)
if kv_cache is not None:
mlp_out_raw, kv_cache = mlp_out_raw
mlp_out = mlp_out_raw.reshape(B, num_frames, -1, C)
mlp_out = (gate_mlp * mlp_out).reshape(B, N, C)
if frame_token_mask is not None:
mlp_out = mlp_out * frame_token_mask
x = x + self.drop_path(mlp_out)
if frame_token_mask is not None:
x = x * frame_token_mask
if kv_cache is not None:
return x, kv_cache
return x
def forward(self, x, y, t, mask=None, THW=None, rotary_emb=None, block_mask=None, chunk_index=None, **kwargs):
if len(t.shape) > 2:
return self.forward_frame_aware(
x,
y,
t,
mask=mask,
THW=THW,
rotary_emb=rotary_emb,
block_mask=block_mask,
chunk_index=chunk_index,
**kwargs,
)
intermediate_feats = {
"x_in": x,
"x_self_attn": None,
"x_cross_attn": None,
"x_ffn": None,
}
B, N, C = x.shape
frame_valid_mask = kwargs.get("frame_valid_mask", None)
frame_token_mask = (
self._build_frame_token_mask(
frame_valid_mask,
B=B,
T=THW[0],
N=N,
device=x.device,
dtype=x.dtype,
)
if THW is not None
else None
)
if frame_token_mask is not None:
x = x * frame_token_mask
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
self.scale_shift_table[None] + t.reshape(B, 6, -1)
).chunk(6, dim=1)
x_sa_in = t2i_modulate(self.norm1(x), shift_msa, scale_msa)
if frame_token_mask is not None:
x_sa_in = x_sa_in * frame_token_mask
self_attn_kwargs = {
"HW": THW,
"rotary_emb": rotary_emb,
"block_mask": block_mask,
"camera_conditions": kwargs.get("camera_conditions", None),
"prope_fns": kwargs.get("prope_fns", None),
"frame_valid_mask": frame_valid_mask,
}
cam_branch_drop_prob = kwargs.get("cam_branch_drop_prob", None)
if cam_branch_drop_prob is not None:
self_attn_kwargs["cam_branch_drop_prob"] = cam_branch_drop_prob
if chunk_index is not None:
self_attn_kwargs["chunk_index"] = chunk_index[:] # NOTE: important, copy the list
if kwargs.get("chunk_index_global", None) is not None:
self_attn_kwargs["chunk_index_global"] = kwargs.get("chunk_index_global")
chunk_split_strategy = kwargs.get("chunk_split_strategy", getattr(self, "chunk_split_strategy", "uniform"))
if chunk_split_strategy is not None:
self_attn_kwargs["chunk_split_strategy"] = chunk_split_strategy
chunk_size = kwargs.get("chunk_size", getattr(self, "chunk_size", 10))
if chunk_size is not None:
self_attn_kwargs["chunk_size"] = chunk_size
save_kv_cache = kwargs.get("save_kv_cache", None)
if save_kv_cache is not None:
self_attn_kwargs["save_kv_cache"] = save_kv_cache
kv_cache = kwargs.get("kv_cache", None)
if kv_cache is not None:
self_attn_kwargs["kv_cache"] = kv_cache
x_sa = self.attn(x_sa_in, **self_attn_kwargs)
if kv_cache is not None:
x_sa, kv_cache = x_sa
if frame_token_mask is not None:
x_sa = x_sa * frame_token_mask
intermediate_feats["x_self_attn"] = x_sa
if self.flash_attn_additional:
x_sa = x_sa + self.learnable_fa_scale * self.flash_attn_additional(x_sa_in, rotary_emb=rotary_emb, HW=THW)
if frame_token_mask is not None:
x_sa = x_sa * frame_token_mask
x = x + self.drop_path(gate_msa * x_sa)
if frame_token_mask is not None:
x = x * frame_token_mask
delta_pose_emb = kwargs.get("delta_pose_emb", None)
if delta_pose_emb is not None and hasattr(self, "delta_pose_proj"):
T_dp = delta_pose_emb.shape[1]
S_dp = N // T_dp
dpe = delta_pose_emb.unsqueeze(2).expand(-1, -1, S_dp, -1).reshape(B, N, C)
x = x + self.delta_pose_proj(dpe)
plucker_emb = kwargs.get("plucker_emb", None)
if plucker_emb is not None and hasattr(self, "plucker_proj"):
x = x + self.plucker_proj(plucker_emb)
if self.cross_attn_image_embeds:
x = x + self.cross_attn(x, y, mask=mask, image_embeds=kwargs.get("image_embeds", None))
else:
x = x + self.cross_attn(x, y, mask=mask)
if frame_token_mask is not None:
x = x * frame_token_mask
intermediate_feats["x_cross_attn"] = x
mlp_kwargs = {
"HW": THW,
"frame_valid_mask": frame_valid_mask,
}
if chunk_index is not None:
mlp_kwargs["chunk_index"] = chunk_index[:] # NOTE: important, copy the list
if kwargs.get("chunk_index_global", None) is not None:
mlp_kwargs["chunk_index_global"] = kwargs.get("chunk_index_global")
if chunk_split_strategy is not None:
mlp_kwargs["chunk_split_strategy"] = chunk_split_strategy
chunk_size = kwargs.get("chunk_size", getattr(self, "chunk_size", 10))
if chunk_size is not None:
mlp_kwargs["chunk_size"] = chunk_size
if save_kv_cache is not None:
mlp_kwargs["save_kv_cache"] = save_kv_cache
if kv_cache is not None:
mlp_kwargs["kv_cache"] = kv_cache
x_mlp_in = t2i_modulate(self.norm2(x), shift_mlp, scale_mlp)
if frame_token_mask is not None:
x_mlp_in = x_mlp_in * frame_token_mask
mlp_out = self.mlp(x_mlp_in, **mlp_kwargs)
if kv_cache is not None:
mlp_out, kv_cache = mlp_out
if frame_token_mask is not None:
mlp_out = mlp_out * frame_token_mask
x = x + self.drop_path(gate_mlp * mlp_out)
if frame_token_mask is not None:
x = x * frame_token_mask
intermediate_feats["x_ffn"] = x
if self.block_hook is not None:
self.block_hook(**intermediate_feats)
if kv_cache is not None:
return x, kv_cache
return x
def _build_camctrl_cls_list(
gdn_cls: type | None,
softmax_cls: type | None,
depth: int,
camctrl_layers_num: int,
softmax_every_n: int,
) -> list[type | None]:
"""Build the per-block ``camctrl_cls`` schedule.
The first ``camctrl_layers_num`` blocks use the camera-controlled
attention. Within that range, every ``softmax_every_n``-th block
(1-indexed) swaps the GDN variant for the softmax variant when one is
provided. Remaining blocks (or trailing layers above
``camctrl_layers_num``) get ``None``, which routes the block to a plain
``attn_type`` attention.
"""
out: list[type | None] = [None] * depth
for i in range(min(depth, camctrl_layers_num)):
if softmax_every_n > 0 and softmax_cls is not None and (i + 1) % softmax_every_n == 0:
out[i] = softmax_cls
else:
out[i] = gdn_cls
return out
class SanaMSVideoCamCtrl(Sana):
"""Camera-controlled video Sana DiT.
By default, uses the fully-fused Triton UCPE GDN camera branch
(:class:`BidirectionalGDNUCPESinglePathLiteLABothTriton`), with every
``softmax_every_n``-th block swapped to the softmax-attention sibling
(:class:`BidirectionalSoftmaxUCPESinglePathLiteLA`).
For streaming chunk-causal AR inference with a per-block kv_cache, use
:class:`SanaMSVideoCamCtrlStreaming` instead.
"""
# Camera-controlled attention classes used for the GDN and softmax slots.
# Subclasses override these to swap in cached / chunk-causal variants.
_camctrl_cls_gdn: type = BidirectionalGDNUCPESinglePathLiteLABothTriton
_camctrl_cls_softmax: type = BidirectionalSoftmaxUCPESinglePathLiteLA
def __init__(
self,
input_size=32,
patch_size=(1, 2, 2),
in_channels=4,
hidden_size=1152,
depth=28,
num_heads=16,
mlp_ratio=4.0,
class_dropout_prob=0.1,
learn_sigma=True,
pred_sigma=True,
drop_path: float = 0.0,
caption_channels=2304,
pe_interpolation=1.0,
config=None,
model_max_length=300,
qk_norm=False,
y_norm=False,
norm_eps=1e-5,
attn_type="flash",
ffn_type="mlp",
use_pe=True,
y_norm_scale_factor=1.0,
patch_embed_kernel=None,
mlp_acts=("silu", "silu", None),
linear_head_dim=32,
cross_norm=False,
cross_attn_type="flash",
cross_attn_image_embeds=False,
image_embed_channels=1152,
pos_embed_type="wan_rope",
rope_fhw_dim=None,
t_kernel_size=3,
flash_attn_layer_idx=None,
flash_attn_layer_type=None,
flash_attn_window_count=None,
pack_latents=False,
camctrl_layers_num: int = None,
cam_attn_compress: int = 2,
init_cam_from_base: bool = False,
use_delta_actions: bool = False,
delta_action_dim: int = 16 * 4,
use_delta_translation: bool = False,
fp32_norm: bool = False,
chunk_size: int = 10,
chunk_split_strategy: str = "uniform",
conv_kernel_size: int = 4,
k_conv_only: bool = True,
softmax_every_n: int = 4,
use_delta_pose_additive: bool = False,
delta_pose_additive_dim: int = 64,
use_chunk_plucker_input: bool = False,
use_chunk_plucker_post_attn: bool = False,
chunk_plucker_channels: int = 48,
chunk_plucker_post_attn_blocks: int = -1,
use_autograd_kernel: bool = False,
**kwargs,
):
camctrl_type = kwargs.pop("camctrl_type", None)
super().__init__(
input_size=input_size,
patch_size=patch_size,
in_channels=in_channels,
hidden_size=hidden_size,
depth=depth,
num_heads=num_heads,
mlp_ratio=mlp_ratio,
class_dropout_prob=class_dropout_prob,
learn_sigma=learn_sigma,
pred_sigma=pred_sigma,
drop_path=drop_path,
caption_channels=caption_channels,
pe_interpolation=pe_interpolation,
config=config,
model_max_length=model_max_length,
qk_norm=qk_norm,
y_norm=y_norm,
norm_eps=norm_eps,
attn_type=attn_type,
ffn_type=ffn_type,
use_pe=use_pe,
y_norm_scale_factor=y_norm_scale_factor,
patch_embed_kernel=patch_embed_kernel,
mlp_acts=mlp_acts,
linear_head_dim=linear_head_dim,
cross_norm=cross_norm,
cross_attn_type=cross_attn_type,
pos_embed_type=pos_embed_type,
**kwargs,
)
if "ChunkCausal" in str(camctrl_type or ""):
self._camctrl_cls_gdn = ChunkCausalGDNUCPESinglePathLiteLABothTriton
self.chunk_size = chunk_size
self.chunk_split_strategy = chunk_split_strategy
self.patch_size = patch_size
self.h = self.w = 0
approx_gelu = lambda: nn.GELU(approximate="tanh")
self.t_block = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size, bias=True))
self.pos_embed_ms = None
self.pack_latents = pack_latents
self.attn_type = attn_type
self.camctrl_layers_num = camctrl_layers_num if camctrl_layers_num is not None else depth
self.cam_attn_compress = cam_attn_compress
self.init_cam_from_base = init_cam_from_base
self.use_delta_actions = use_delta_actions
self.use_delta_translation = use_delta_translation
self.use_delta_pose_additive = use_delta_pose_additive
kernel_size = patch_embed_kernel or patch_size
x_embedder_in_channels = in_channels
if self.pack_latents:
x_embedder_in_channels = x_embedder_in_channels * 2 * 2
self.out_channels = in_channels * 2 * 2
self.x_embedder = PatchEmbedMS3D(
patch_size, x_embedder_in_channels, hidden_size, kernel_size=kernel_size, bias=True
)
self.y_embedder = CaptionEmbedder(
in_channels=caption_channels,
hidden_size=hidden_size,
uncond_prob=class_dropout_prob,
act_layer=approx_gelu,
token_num=model_max_length,
)
if self.use_delta_actions:
self.delta_action_embedder = DeltaActionEmbedder(
input_dim=delta_action_dim,
hidden_size=hidden_size,
act_layer=approx_gelu,
)
nn.init.zeros_(self.delta_action_embedder.mlp[-1].weight)
nn.init.zeros_(self.delta_action_embedder.mlp[-1].bias)
if self.use_delta_translation:
self.delta_translation_embedder = DeltaActionEmbedder(
input_dim=3,
hidden_size=hidden_size,
act_layer=approx_gelu,
)
nn.init.zeros_(self.delta_translation_embedder.mlp[-1].weight)
nn.init.zeros_(self.delta_translation_embedder.mlp[-1].bias)
if self.use_delta_pose_additive:
self.delta_pose_embedder = DeltaActionEmbedder(
input_dim=delta_pose_additive_dim,
hidden_size=hidden_size,
act_layer=approx_gelu,
)
self.use_chunk_plucker_input = use_chunk_plucker_input
self.use_chunk_plucker_post_attn = use_chunk_plucker_post_attn
if self.use_chunk_plucker_input or self.use_chunk_plucker_post_attn:
self.plucker_embedder = PatchEmbedMS3D(
patch_size, chunk_plucker_channels, hidden_size, kernel_size=kernel_size, bias=True
)
nn.init.zeros_(self.plucker_embedder.proj.weight)
nn.init.zeros_(self.plucker_embedder.proj.bias)
# UCPE-style camera branch uses a 3-channel absmap (up_map + lat_map).
self.raymap_embedder = PatchEmbedMS3D(patch_size, 3, hidden_size, kernel_size=kernel_size, bias=True)
if cross_attn_image_embeds:
self.image_embedder = ClipVisionProjection(image_embed_channels, hidden_size)
else:
self.image_embedder = None
if attn_type in ["flash", "FlexLinearAttention", "flex"]:
attention_head_dim = hidden_size // num_heads
else:
attention_head_dim = linear_head_dim
if use_pe and pos_embed_type == "wan_rope":
self.rope = WanRotaryPosEmbed(
attention_head_dim=attention_head_dim, patch_size=patch_size, max_seq_len=1024, fhw_dim=rope_fhw_dim
)
elif use_pe and pos_embed_type == "casual_wan_rope":
self.rope = CausalWanRotaryPosEmbed(
attention_head_dim=attention_head_dim, patch_size=patch_size, max_seq_len=1024
)
elif use_pe and pos_embed_type == "wan_temporal_rope":
self.rope = WanRotaryTemporalPosEmbed(
attention_head_dim=attention_head_dim, patch_size=patch_size, max_seq_len=1024
)
drop_path = [x.item() for x in torch.linspace(0, drop_path, depth)] # stochastic depth decay rule
# insert flash attention layers
if flash_attn_layer_idx is not None and flash_attn_layer_type is not None:
assert int(flash_attn_layer_idx[-1]) < depth
additional_flash_attn = [
flash_attn_layer_type if i in flash_attn_layer_idx else False for i in range(depth)
]
else:
additional_flash_attn = [False] * depth
# visualize qkv
self.save_qkv = False
self.qkv_store_buffer = {}
# diagonal mask
self.diagonal_mask = None
self.softmax_every_n = softmax_every_n
attn_type_list = [attn_type] * depth
if attn_type in ["flex", "FlexLinearAttention"]:
attn_type_list[0] = "flash"
attn_type_list[1] = "flash"
camctrl_cls_list = _build_camctrl_cls_list(
gdn_cls=self._camctrl_cls_gdn,
softmax_cls=self._camctrl_cls_softmax,
depth=depth,
camctrl_layers_num=self.camctrl_layers_num,
softmax_every_n=softmax_every_n,
)
if get_rank() == 0:
cls_name_list = [c.__name__ if c is not None else None for c in camctrl_cls_list]
self.logger(
f"Hybrid attention (softmax_every_n={softmax_every_n}):\n"
f" attn_type_list = {attn_type_list}\n"
f" camctrl_cls_list = {cls_name_list}"
)
self.blocks = nn.ModuleList(
[
SanaVideoMSCamCtrlBlock(
hidden_size,
num_heads,
mlp_ratio=mlp_ratio,
drop_path=drop_path[i],
qk_norm=qk_norm,
attn_type=attn_type_list[i],
ffn_type=ffn_type,
mlp_acts=mlp_acts,
linear_head_dim=linear_head_dim,
cross_norm=cross_norm,
cross_attn_image_embeds=cross_attn_image_embeds,
t_kernel_size=t_kernel_size,
additional_flash_attn=additional_flash_attn[i],
flash_attn_window_count=flash_attn_window_count,
camctrl_cls=camctrl_cls_list[i],
patch_size=patch_size,
cam_attn_compress=self.cam_attn_compress,
fp32_norm=fp32_norm,
chunk_size=chunk_size,
chunk_split_strategy=chunk_split_strategy,
conv_kernel_size=conv_kernel_size,
k_conv_only=k_conv_only,
use_delta_pose_additive=use_delta_pose_additive,
use_chunk_plucker_post_attn=(
use_chunk_plucker_post_attn
and (chunk_plucker_post_attn_blocks < 0 or i < chunk_plucker_post_attn_blocks)
),
use_autograd_kernel=use_autograd_kernel,
)
for i in range(depth)
]
)
self.final_layer = T2IFinalLayer(hidden_size, patch_size, self.out_channels)
if get_rank() == 0:
if ffn_type == "GLUMBConvTemp":
self.logger(f"{ffn_type} Temporal kernal: {t_kernel_size}")
if flash_attn_layer_idx is not None:
self.logger(f"additional flash attn layer idx: {flash_attn_layer_idx}, type: {flash_attn_layer_type}")
if flash_attn_layer_type == "window_flash":
self.logger(f"flash attn window count: {flash_attn_window_count}")
self.initialize()
self.save_block_output = False
self.block_output_buffer = {}
@staticmethod
def _pack_latents(latents, batch_size, num_channels_latents, height, width, frame):
latents = latents.view(batch_size, num_channels_latents, frame, height // 2, 2, width // 2, 2)
latents = latents.permute(0, 1, 4, 6, 2, 3, 5)
latents = latents.reshape(batch_size, num_channels_latents * 4, frame, height // 2, width // 2)
return latents
@staticmethod
def _unpack_latents(latents, height, width, frame):
batch_size, channels, frame, H, W = latents.shape
assert height % 2 == 0 and width % 2 == 0
# latent height and width to be divisible by 2.
latents = latents.view(batch_size, channels // 4, 2, 2, frame, height // 2, width // 2)
latents = latents.permute(0, 1, 4, 5, 2, 6, 3)
latents = latents.reshape(batch_size, channels // (2 * 2), frame, height, width)
return latents
def _compute_rope_with_cp(self, device: torch.device, h: int, w: int) -> torch.Tensor:
"""Compute RoPE frequencies with correct global positions under CP."""
if not cp_enabled():
return self.rope((self.f, h, w), device)
cp_group = get_cp_group()
if cp_group is None:
return self.rope((self.f, h, w), device)
cp_rank = dist.get_rank(cp_group)
cp_world = dist.get_world_size(cp_group)
global_f = self.f * cp_world
full_rope = self.rope((global_f, h, w), device)
hw = h * w
full_rope = full_rope.view(1, 1, global_f, hw, -1)
local_rope = full_rope[:, :, cp_rank * self.f : (cp_rank + 1) * self.f, :, :]
return local_rope.reshape(1, 1, self.f * hw, -1)
def forward(self, x, timestep, y, mask=None, **kwargs):
"""
Forward pass of Sana.
x: (N, C, T, H, W) tensor of spatial inputs (images or latent representations of images)
t: (N,) tensor of diffusion timesteps or (N, 1, F) tensor of diffusion timesteps
y: (N, 1, 120, C) tensor of class labels
"""
bs = x.shape[0]
x = x.to(self.dtype)
if self.timestep_norm_scale_factor != 1.0:
timestep = (timestep.float() / self.timestep_norm_scale_factor).to(torch.float32)
else:
timestep = timestep.long().to(torch.float32)
y = y.to(self.dtype)
self.f, self.h, self.w = (
x.shape[-3] // self.patch_size[0],
x.shape[-2] // self.patch_size[1],
x.shape[-1] // self.patch_size[2],
)
data_info = kwargs.get("data_info", {})
if data_info.get("image_vae_embeds", None) is not None:
x = torch.cat([x, data_info["image_vae_embeds"].to(self.dtype)], dim=1)
if data_info.get("image_embeds", None) is not None:
image_embeds = data_info["image_embeds"].to(self.dtype)
image_embeds = self.image_embedder(image_embeds)
kwargs["image_embeds"] = image_embeds
if self.save_qkv:
self.qkv_store_buffer[int(timestep[0].item())] = {}
if self.save_block_output:
self.inference_timestep = int(timestep[0].item())
cam_embeds = kwargs.get("camera_conditions", None)
cam_branch_drop_prob = kwargs.get("cam_branch_drop_prob", 0.0)
if cam_embeds is not None and cam_branch_drop_prob:
# Keep drop-path semantics consistent: when camera branch is dropped,
# skip both camera-attention branch and camera embedding injection.
cam_embeds = _maybe_drop_cam_branch(
cam_embeds,
cam_branch_drop_prob,
self.training,
x.device,
)
if cam_embeds is None:
kwargs["camera_conditions"] = None
if self.pack_latents:
x = self._pack_latents(x, bs, self.in_channels, self.h, self.w, self.f)
if cam_embeds is not None:
cam_embeds = cam_embeds.to(self.dtype)
self.h = self.h // 2
self.w = self.w // 2
if self.x_embedder.patch_size != self.x_embedder.kernel_size and self.x_embedder.kernel_size == (1, 2, 2):
x = F.pad(x, (0, 1, 0, 1, 0, 0))
if cam_embeds is not None:
cam_embeds = F.pad(cam_embeds, (0, 1, 0, 1, 0, 0))
x = self.x_embedder(x)
if cam_embeds is not None:
# Both surviving camctrl variants are UCPE-style: build raymats + 3-channel
# absmap (up_map + lat_map) from the raw (B,F,20) camera conditions.
raw_cam_conditions = cam_embeds
cam_pos_embeds = kwargs.get("cam_pos_embeds", None)
if cam_pos_embeds is not None and "absmap" in cam_pos_embeds:
cam_embeds = cam_pos_embeds["absmap"]
if "P" in cam_pos_embeds:
kwargs["raymats"] = cam_pos_embeds["P"]
else:
raymats, cam_embeds = _process_camera_conditions_ucpe(
raw_cam_conditions, bs, (self.f, self.h, self.w), self.patch_size
)
cam_embeds = cam_embeds.permute(0, 4, 1, 2, 3).to(self.dtype)
kwargs["raymats"] = raymats
_skip_absmap = getattr(self, "use_chunk_plucker_input", False) or getattr(
self, "use_chunk_plucker_post_attn", False
)
if not _skip_absmap:
cam_embeds = self.raymap_embedder(cam_embeds)
x = x + cam_embeds
kwargs["camera_embedding"] = cam_embeds
kwargs["camera_conditions"] = raw_cam_conditions
if getattr(self, "use_chunk_plucker_input", False) and "chunk_plucker" in kwargs:
plucker_input = kwargs["chunk_plucker"].to(self.dtype)
plucker_emb = self.plucker_embedder(plucker_input)
x = x + plucker_emb
if getattr(self, "use_chunk_plucker_post_attn", False) and "chunk_plucker" in kwargs:
plucker_input = kwargs["chunk_plucker"].to(self.dtype)
kwargs["plucker_emb"] = self.plucker_embedder(plucker_input)
image_pos_embed = kwargs.get("pos_embeds", None)
if self.use_pe and image_pos_embed is None:
if self.pos_embed_type == "sincos":
if self.pos_embed_ms is None or self.pos_embed_ms.shape[1:] != x.shape[1:]:
self.pos_embed_ms = (
torch.from_numpy(
get_2d_sincos_pos_embed(
self.pos_embed.shape[-1],
(self.h, self.w),
pe_interpolation=self.pe_interpolation,
base_size=self.base_size,
)
)
.unsqueeze(0)
.to(x.device)
.to(self.dtype)
)
x += self.pos_embed_ms # (N, T, D), where T = H * W / patch_size ** 2
elif self.pos_embed_type == "flux_rope":
self.pos_embed_ms = RopePosEmbed(theta=10000, axes_dim=[12, 10, 10])
latent_image_ids = self.pos_embed_ms._prepare_latent_image_ids(
bs, self.h, self.w, x.device, x.dtype, frame=self.f
)
image_pos_embed = self.pos_embed_ms(latent_image_ids)
elif self.pos_embed_type == "wan_rope":
image_pos_embed = self._compute_rope_with_cp(x.device, self.h, self.w)
elif self.pos_embed_type == "casual_wan_rope":
image_pos_embed = self.rope((self.f, self.h, self.w), x.device)
elif self.pos_embed_type == "wan_temporal_rope":
image_pos_embed = self._compute_rope_with_cp(x.device, self.h, self.w)
else:
raise ValueError(f"Unknown pos_embed_type: {self.pos_embed_type}")
elif image_pos_embed is not None:
image_pos_embed = image_pos_embed.to(x.device)
while image_pos_embed.ndim > 4:
image_pos_embed = image_pos_embed.squeeze(1)
t = self.t_embedder(timestep.flatten()) # (N, D)
t0 = self.t_block(t)
t = t.unflatten(dim=0, sizes=timestep.shape)
t0 = t0.unflatten(dim=0, sizes=timestep.shape)
# Compute delta embeddings for final_layer (stored separately, not touching t/t0)
_delta_t_emb = None
if getattr(self, "use_delta_actions", False) and "delta_actions" in kwargs:
da = kwargs["delta_actions"].to(self.dtype)
_delta_t_emb = self.delta_action_embedder(da) # (B, T, D)
if getattr(self, "use_delta_translation", False) and kwargs.get("camera_conditions") is not None:
cam_cond = kwargs["camera_conditions"].to(self.dtype)
c2w = cam_cond[:, :, :16].view(cam_cond.shape[0], cam_cond.shape[1], 4, 4)
t_cam = c2w[:, :, :3, 3] # (B, T, 3)
delta_t = t_cam[:, 1:, :] - t_cam[:, :-1, :]
delta_t = torch.cat([torch.zeros_like(delta_t[:, :1, :]), delta_t], dim=1)
dt_emb = self.delta_translation_embedder(delta_t) # (B, T, D)
_delta_t_emb = dt_emb if _delta_t_emb is None else _delta_t_emb + dt_emb
if getattr(self, "use_delta_pose_additive", False) and "delta_actions" in kwargs:
da = kwargs["delta_actions"].to(self.dtype)
kwargs["delta_pose_emb"] = self.delta_pose_embedder(da) # (B, T, D)
y = self.y_embedder(y, self.training, mask=mask) # (N, D)
if self.y_norm:
y = self.attention_y_norm(y)
if mask is not None:
mask = mask.to(torch.int16)
mask = mask.repeat(y.shape[0] // mask.shape[0], 1) if mask.shape[0] != y.shape[0] else mask
mask = mask.squeeze(1).squeeze(1)
if _xformers_available:
y = y.squeeze(1).masked_select(mask.unsqueeze(-1) != 0).view(1, -1, x.shape[-1])
y_lens = mask.sum(dim=1).tolist()
else:
y_lens = mask
elif _xformers_available:
y_lens = [y.shape[2]] * y.shape[0]
y = y.squeeze(1).view(1, -1, x.shape[-1])
else:
raise ValueError(f"Attention type is not available due to _xformers_available={_xformers_available}.")
if self.diagonal_mask is not None:
seq_len = x.shape[1]
self.diagonal_mask = self.diagonal_mask.to(x.device)
def mask_mod(b, h, q_idx, kv_idx):
return self.diagonal_mask[q_idx, kv_idx].bool()
block_mask = create_block_mask_cached(
mask_mod, None, None, seq_len, seq_len, device=x.device, _compile=False
)
else:
block_mask = None
if kwargs.get("camera_conditions") is not None:
# Pre-compute UCPE projection functions to share across blocks
# (both surviving camctrl variants are UCPE-style).
if self.attn_type in ["flash", "FlexLinearAttention", "flex"]:
head_dim = self.hidden_size // self.num_heads
else:
head_dim = self.linear_head_dim
cam_pos_embeds = kwargs.get("cam_pos_embeds", None)
if cam_pos_embeds is not None:
for k, v in cam_pos_embeds.items():
if isinstance(v, torch.Tensor):
v = v.to(x.device)
if k == "absmap":
while v.ndim > 5:
v = v.squeeze(1)
else:
while v.ndim > 4:
v = v.squeeze(1)
cam_pos_embeds[k] = v
kwargs["prope_fns"] = prepare_prope_fns(
camctrl_type="UCPE",
head_dim=head_dim,
camera_conditions=kwargs["camera_conditions"],
HW=(self.f, self.h, self.w),
patch_size=self.patch_size,
rotary_emb=image_pos_embed,
raymats=kwargs.get("raymats"),
cam_pos_embeds=cam_pos_embeds,
)
for i, block in enumerate(self.blocks):
if self.save_qkv:
block.attn.qkv_store_buffer = {}
x = auto_grad_checkpoint(
block,
x,
y,
t0,
y_lens,
(self.f, self.h, self.w),
image_pos_embed,
block_mask=block_mask if i > 1 else None,
**kwargs,
use_reentrant=False,
)
if self.save_qkv:
self.qkv_store_buffer[int(timestep[0].item())][f"block_{i}"] = block.attn.qkv_store_buffer
block.attn.qkv_store_buffer = None
if _delta_t_emb is not None:
if t.ndim == 2:
t = t.unsqueeze(1).expand(-1, _delta_t_emb.shape[1], -1)
elif t.ndim == 4:
t = t.squeeze(1)
t = t + _delta_t_emb
t = t.unsqueeze(1)
x = self.final_layer(x, t) # (N, T, patch_size ** 2 * out_channels)
x = self.unpatchify(x) # (N, out_channels, H, W)
if self.pack_latents:
x = self._unpack_latents(x, self.h * 2, self.w * 2, self.f)
if self.save_block_output:
block_output = self.get_block_output()
self.block_output_buffer[self.inference_timestep] = block_output
return x
def forward_with_dpmsolver(self, x, timestep, y, data_info, **kwargs):
"""
dpm solver donnot need variance prediction
"""
# https://github.com/openai/glide-text2im/blob/main/notebooks/text2im.ipynb
model_out = self.forward(x, timestep, y, data_info=data_info, **kwargs)
return model_out.chunk(2, dim=1)[0] if self.pred_sigma else model_out
def unpatchify(self, x):
"""
x: (N, T, patch_size**2 * C)
imgs: (N, H, W, C)
"""
c = self.out_channels
p_f, p_h, p_w = self.x_embedder.patch_size
h, w = self.h, self.w
assert self.f * self.h * self.w == x.shape[1]
x = x.reshape(shape=(x.shape[0], self.f, h, w, p_f, p_h, p_w, c))
x = torch.einsum("nfhwopqc->ncfohpwq", x)
imgs = x.reshape(shape=(x.shape[0], c, self.f * p_f, h * p_h, w * p_w))
return imgs
def create_diagonal_mask(self, N_pad, N, num_frames, block_size=1, mask_type="nlogn"):
from tools.attn_mask.gen_nlogn_mask import (
gen_linear_mask_shrinked,
gen_log_mask_shrinked,
gen_truncated_mask_shrinked,
)
if mask_type == "nlogn":
diagonal_mask = gen_log_mask_shrinked(N, N, num_frames, block_size)
elif mask_type == "linear":
diagonal_mask = gen_linear_mask_shrinked(N, N, num_frames, block_size)
elif mask_type == "truncated":
diagonal_mask = gen_truncated_mask_shrinked(N, N, num_frames, block_size, max_frame_distance=8)
else:
raise ValueError(f'Unknown mask type: {mask_type}, only support "nlogn", "linear", "truncated"')
padded_mask = torch.zeros((N_pad, N_pad), dtype=torch.bool)
padded_mask[:N, :N] = diagonal_mask
self.diagonal_mask = padded_mask
return self.diagonal_mask
def prepare_flexattention(
self,
cfg_size,
num_head,
head_dim,
dtype,
device,
context_length,
prompt_length,
num_frame,
frame_size,
diag_width=1,
multiplier=2,
):
assert diag_width == multiplier, f"{diag_width} is not equivalent to {multiplier}"
seq_len = context_length + num_frame * frame_size
query, key, value = (
torch.zeros((cfg_size, num_head, seq_len, head_dim), dtype=dtype, device=device) for _ in range(3)
)
mask_mod = generate_temporal_head_mask_mod(context_length, prompt_length, num_frame, frame_size, mul=multiplier)
block_mask = create_block_mask_cached(mask_mod, None, None, seq_len, seq_len, device=device, _compile=True)
hidden_states = flex_attention(query, key, value, block_mask=block_mask)
return block_mask
def load_diagonal_mask(self, *args, **kwargs):
path = kwargs.get("path", None)
if path is None:
self.diagonal_mask = self.prepare_flexattention(*args, **kwargs)
else:
self.diagonal_mask = torch.load(path, map_location="cpu")
def initialize(self):
super().initialize_weights()
# Initialize transformer layers:
def _basic_init(module):
if isinstance(module, nn.Linear):
torch.nn.init.xavier_uniform_(module.weight)
if module.bias is not None:
nn.init.constant_(module.bias, 0)
self.apply(_basic_init)
# Initialize patch_embed like nn.Linear (instead of nn.Conv2d):
w = self.x_embedder.proj.weight.data
nn.init.xavier_uniform_(w.view([w.shape[0], -1]))
# Initialize timestep embedding MLP:
nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
nn.init.normal_(self.t_block[1].weight, std=0.02)
# Initialize caption embedding MLP:
nn.init.normal_(self.y_embedder.y_proj.fc1.weight, std=0.02)
nn.init.normal_(self.y_embedder.y_proj.fc2.weight, std=0.02)
# Initialize cfg embedder
if self.cfg_embedder:
nn.init.normal_(self.cfg_embedder.mlp[0].weight, std=0.02)
nn.init.zeros_(self.cfg_embedder.mlp[2].weight)
if hasattr(self.cfg_embedder.mlp[2], "bias") and self.cfg_embedder.mlp[2].bias is not None:
nn.init.zeros_(self.cfg_embedder.mlp[2].bias)
for block in self.blocks:
if hasattr(block, "flash_attn_additional") and block.flash_attn_additional is not None:
nn.init.zeros_(block.flash_attn_additional.proj.weight)
nn.init.zeros_(block.flash_attn_additional.proj.bias)
if hasattr(block, "cross_attn") and hasattr(block.cross_attn, "image_kv_linear"):
nn.init.zeros_(block.cross_attn.image_kv_linear.weight)
nn.init.zeros_(block.cross_attn.image_kv_linear.bias)
if hasattr(block, "attn") and hasattr(block.attn, "prope_proj"):
nn.init.zeros_(block.attn.prope_proj.weight)
nn.init.zeros_(block.attn.prope_proj.bias)
if hasattr(block, "attn") and hasattr(block.attn, "out_proj_cam"):
nn.init.zeros_(block.attn.out_proj_cam.weight)
nn.init.zeros_(block.attn.out_proj_cam.bias)
if hasattr(block, "attn") and hasattr(block.attn, "_init_gdn_gates_for_linear_equiv"):
block.attn._init_gdn_gates_for_linear_equiv()
if hasattr(self, "raymap_embedder") and self.raymap_embedder is not None:
nn.init.constant_(self.raymap_embedder.proj.weight, 0)
if self.raymap_embedder.proj.bias is not None:
nn.init.constant_(self.raymap_embedder.proj.bias, 0)
if self.init_cam_from_base:
self.init_cam_branch_from_base()
def load_state_dict(self, state_dict, strict=True, **kwargs):
"""when the channel in FFN is not the same as the checkpoint, load the checkpoint"""
current_state_dict = self.state_dict()
new_state_dict = {}
for key, current_param in current_state_dict.items():
checkpoint_param = state_dict.get(key)
if checkpoint_param is None:
if strict:
raise KeyError(f"Missing key in state dict: {key}")
continue
try:
new_param = torch.zeros_like(current_param)
if current_param.shape == checkpoint_param.shape:
new_param.copy_(checkpoint_param)
new_state_dict[key] = checkpoint_param
continue
else:
self.logger(
f"Loading {key} from checkpoint, shape: {checkpoint_param.shape}, current_param.shape: {current_param.shape}"
)
if "x_embedder.proj.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "x_embedder.proj.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "attn.qkv.weight" in key:
old_hidden_size = checkpoint_param.shape[1]
new_hidden_size = current_param.shape[1]
# split qkv into 3 parts
for i in range(3):
start_idx = i * old_hidden_size
new_start_idx = i * new_hidden_size
new_param[new_start_idx : new_start_idx + old_hidden_size, :old_hidden_size] = checkpoint_param[
start_idx : start_idx + old_hidden_size
]
elif "attn.qkv.bias" in key:
old_hidden_size = checkpoint_param.shape[0] // 3
new_hidden_size = current_param.shape[0] // 3
new_param[:old_hidden_size] = checkpoint_param[:old_hidden_size]
new_param[new_hidden_size : new_hidden_size + old_hidden_size] = checkpoint_param[
old_hidden_size : 2 * old_hidden_size
]
new_param[2 * new_hidden_size : 2 * new_hidden_size + old_hidden_size] = checkpoint_param[
2 * old_hidden_size :
]
elif "q_norm.weight" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "q_norm.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "k_norm.weight" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "k_norm.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "cross_attn.q_linear.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "cross_attn.q_linear.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "cross_attn.kv_linear.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "cross_attn.kv_linear.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "attn.proj.weight" in key:
old_hidden_size = checkpoint_param.shape[0]
new_param[:old_hidden_size, :old_hidden_size] = checkpoint_param
elif "attn.proj.bias" in key:
old_hidden_size = checkpoint_param.shape[0]
new_param[:old_hidden_size] = checkpoint_param
elif "scale_shift_table" in key:
# scale_shift_table shape: [6, hidden_size]
old_hidden_size = checkpoint_param.shape[1]
new_param[:, :old_hidden_size] = checkpoint_param
elif "final_layer.linear.weight" in key:
new_param[:, : checkpoint_param.shape[1]] = checkpoint_param
elif "final_layer.linear.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "t_embedder.mlp.0.weight" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "t_embedder.mlp.0.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "t_embedder.mlp.2.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "t_embedder.mlp.2.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "t_block.1.weight" in key:
# t_block.1.weight shape: [6 * hidden_size, hidden_size]
old_hidden_size = checkpoint_param.shape[1]
new_hidden_size = current_param.shape[1]
# split t_block.1.weight into 6 parts
for i in range(6):
start_idx = i * old_hidden_size
new_start_idx = i * new_hidden_size
new_param[new_start_idx : new_start_idx + old_hidden_size, :old_hidden_size] = checkpoint_param[
start_idx : start_idx + old_hidden_size
]
elif "t_block.1.bias" in key:
# t_block.1.bias shape: [6 * hidden_size]
old_hidden_size = checkpoint_param.shape[0] // 6
new_hidden_size = current_param.shape[0] // 6
# split t_block.1.bias into 6 parts
for i in range(6):
start_idx = i * old_hidden_size
new_start_idx = i * new_hidden_size
new_param[new_start_idx : new_start_idx + old_hidden_size] = checkpoint_param[
start_idx : start_idx + old_hidden_size
]
elif "t_block.2.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "y_embedder.y_proj.fc1.weight" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "y_embedder.y_proj.fc1.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "y_embedder.y_proj.fc2.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "y_embedder.y_proj.fc2.bias" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif "y_embedder.y_embedding" in key:
pass
elif "attention_y_norm.weight" in key:
new_param[: checkpoint_param.shape[0]] = checkpoint_param
elif (
"inverted_conv.conv.weight" in key
or "inverted_conv.conv.bias" in key
or "depth_conv.conv.bias" in key
):
num_old_channels = checkpoint_param.shape[0] // 2
num_new_channels = new_param.shape[0] // 2
if new_param.dim() == 1:
new_param[:num_old_channels] = checkpoint_param[:num_old_channels]
new_param[num_new_channels : num_new_channels + num_old_channels] = checkpoint_param[
num_old_channels:
]
else:
new_param[:num_old_channels, : checkpoint_param.shape[1]] = checkpoint_param[:num_old_channels]
new_param[
num_new_channels : num_new_channels + num_old_channels, : checkpoint_param.shape[1]
] = checkpoint_param[num_old_channels:]
elif "depth_conv.conv.weight" in key:
assert checkpoint_param.shape[1] == 1
num_old_channels = checkpoint_param.shape[0] // 2
new_param[:num_old_channels] = checkpoint_param[:num_old_channels]
new_param[num_new_channels : num_new_channels + num_old_channels] = checkpoint_param[
num_old_channels:
]
elif "point_conv.conv.weight" in key:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif "t_conv.weight" in key:
if new_param.shape[2] != checkpoint_param.shape[2]:
new_t_kernel_size = new_param.shape[2]
original_t_kernel_size = checkpoint_param.shape[2]
discrepancy = new_t_kernel_size - original_t_kernel_size
if discrepancy == 0:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
elif discrepancy > 0:
if discrepancy % 2 != 0:
raise ValueError(
f"Discrepancy {discrepancy} is not even, please check the t_kernel_size"
)
new_param[
: checkpoint_param.shape[0],
: checkpoint_param.shape[1],
discrepancy // 2 : -discrepancy // 2,
] = checkpoint_param
else:
if (-discrepancy) % 2 != 0:
raise ValueError(
f"Discrepancy {discrepancy} is not even, please check the t_kernel_size"
)
start = (-discrepancy) // 2
end = start + new_t_kernel_size
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param[
:, :, start:end
]
# self.logger(
# f"Loading {key} with t_kernel_size {new_t_kernel_size} from checkpoint with t_kernel_size {original_t_kernel_size}"
# )
else:
new_param[: checkpoint_param.shape[0], : checkpoint_param.shape[1]] = checkpoint_param
else:
raise KeyError(f"Unhandled key: {key}")
except Exception as e:
warnings.warn(f"Error loading {key}: {e}", stacklevel=2)
new_param = checkpoint_param
new_state_dict[key] = new_param
result = super().load_state_dict(new_state_dict, strict=strict, **kwargs)
return result
def register_block_hook(self, layers=None, device="cpu", detach=True, score_only=True):
for i, block in enumerate(self.blocks):
if layers is None or i in layers:
block.block_hook = BlockHook(device, detach, score_only)
def get_block_output(self):
block_outputs = {}
for i, block in enumerate(self.blocks):
if block.block_hook is not None:
block_outputs[i] = block.block_hook.get_output()
block.block_hook.clear()
return block_outputs
def init_cam_branch_from_base(self):
for i, block in enumerate(self.blocks):
if hasattr(block.attn, "init_cam_branch_weights"):
block.attn.init_cam_branch_weights()
#################################################################################
# Sana Multi-scale Configs #
#################################################################################
class SanaMSVideoCamCtrlStreaming(SanaMSVideoCamCtrl):
"""Streaming chunk-causal AR variant of :class:`SanaMSVideoCamCtrl`.
Uses cached chunk-causal UCPE attention
(:class:`CachedChunkCausalGDNUCPESinglePathLiteLA` + the softmax sibling
:class:`CachedSoftmaxUCPESinglePathLiteLA`) so each block accepts a
per-block ``kv_cache`` slot and updates it in place.
:meth:`forward` dispatches calls with ``start_f`` to :meth:`forward_long`;
calls without it follow the inherited behavior.
"""
_camctrl_cls_gdn: type = CachedChunkCausalGDNUCPESinglePathLiteLA
_camctrl_cls_softmax: type = CachedSoftmaxUCPESinglePathLiteLA
__call__ = nn.Module.__call__
def forward(self, x, timestep, y, mask=None, **kwargs):
if kwargs.get("start_f") is not None:
return self.forward_long(x, timestep, y, mask=mask, **kwargs)
return super().forward(x, timestep, y, mask=mask, **kwargs)
# ------------------------------------------------------------------ #
# AR KV-cache streaming forward
# ------------------------------------------------------------------ #
_SOFTMAX_OPTION_Y_TYPES: tuple = () # filled lazily; see _is_softmax_option_y_block
@staticmethod
def _is_softmax_option_y_block(block: nn.Module) -> bool:
"""Return ``True`` iff ``block.attn`` is a softmax-attention camctrl
variant (``_SoftmaxUCPESinglePathLiteLA`` or registered aliases).
Detected by class name (rather than ``isinstance``) to avoid the
circular import that would result from importing the class here.
"""
attn_cls_name = type(block.attn).__name__
return attn_cls_name in (
"_SoftmaxUCPESinglePathLiteLA",
"BidirectionalSoftmaxUCPESinglePathLiteLA",
"ChunkCausalSoftmaxUCPESinglePathLiteLA",
"SoftmaxUCPELiteLA",
)
def forward_long(self, x, timestep, y, mask=None, **kwargs):
"""Forward pass for self-forcing / chunk-causal AR KV-cache inference.
Mirrors :meth:`forward` exactly with two streaming-specific differences:
1. Rotary positional embeddings (``casual_wan_rope``) are built over the
explicit window ``[start_f, end_f)`` (or an explicit ``frame_index``
tensor for sink + window layouts) rather than the full sequence.
2. Each block receives its per-block ``kv_cache`` slot (``kv_cache[i]``)
and may write back updated state when ``save_kv_cache=True``. The
per-block returns are collected and the updated ``kv_cache`` list is
returned alongside the output tensor.
All camera-conditioning behaviour (UCPE absmap, Plucker embeddings,
``raymats``, ``camera_conditions``) is identical to :meth:`forward`.
Args:
x: ``(N, C, T, H, W)`` latent inputs for the current chunk.
timestep: ``(N,)`` or ``(N, 1, F)`` diffusion timesteps.
y: ``(N, 1, L, C)`` text caption embeddings.
mask: Optional ``(N, L)`` text mask.
**kwargs: Same as :meth:`forward`, plus the following streaming-only
keys (all popped before delegating to blocks):
* ``start_f`` / ``end_f``: latent frame range for this call,
in *unpatched* latent-frame units. Converted to patch-frame
units via ``patch_size[0]``.
* ``frame_index``: optional ``(F,)`` long tensor of explicit
per-latent-frame indices (e.g. sink + sliding window).
Takes precedence over ``(start_f, end_f)`` for RoPE.
* ``kv_cache``: ``list[Any]`` of length ``len(self.blocks)``.
Required: each entry holds the cached state for one block.
* ``save_kv_cache``: ``bool``. When ``True``, blocks update
their cache slots in-place via the returned values.
Returns:
``(x, kv_cache)``: the denoised latent tensor (same shape contract
as :meth:`forward`) and the (possibly updated) ``kv_cache`` list.
"""
# --- Extract cached-inference parameters ---
start_f = kwargs.pop("start_f", None)
end_f = kwargs.pop("end_f", None)
frame_index = kwargs.pop("frame_index", None)
kv_cache = kwargs.pop("kv_cache", None)
save_kv_cache = kwargs.pop("save_kv_cache", False)
if start_f is not None and end_f is not None:
assert self.pos_embed_type == "casual_wan_rope", (
"forward_long requires pos_embed_type='casual_wan_rope' when "
f"start_f/end_f are provided; got '{self.pos_embed_type}'"
)
start_f = start_f // self.patch_size[0]
end_f = end_f // self.patch_size[0]
if frame_index is not None:
# Sampler builds frame_index in latent-frame units. Convert to
# patch-frame units the same way start_f / end_f are converted,
# while preserving length == patch-frames (one entry per patch
# frame, not per latent frame). Assumes the sink / window ranges
# are patch-aligned.
ps_t = self.patch_size[0]
if ps_t > 1:
frame_index = frame_index[::ps_t] // ps_t
bs = x.shape[0]
x = x.to(self.dtype)
if self.timestep_norm_scale_factor != 1.0:
timestep = (timestep.float() / self.timestep_norm_scale_factor).to(torch.float32)
else:
timestep = timestep.long().to(torch.float32)
y = y.to(self.dtype)
self.f, self.h, self.w = (
x.shape[-3] // self.patch_size[0],
x.shape[-2] // self.patch_size[1],
x.shape[-1] // self.patch_size[2],
)
data_info = kwargs.get("data_info", {})
if data_info.get("image_vae_embeds", None) is not None:
x = torch.cat([x, data_info["image_vae_embeds"].to(self.dtype)], dim=1)
if data_info.get("image_embeds", None) is not None:
image_embeds = data_info["image_embeds"].to(self.dtype)
image_embeds = self.image_embedder(image_embeds)
kwargs["image_embeds"] = image_embeds
if self.save_qkv:
self.qkv_store_buffer[int(timestep[0].item())] = {}
if self.save_block_output:
self.inference_timestep = int(timestep[0].item())
cam_embeds = kwargs.get("camera_conditions", None)
cam_branch_drop_prob = kwargs.get("cam_branch_drop_prob", 0.0)
if cam_embeds is not None and cam_branch_drop_prob:
# Keep drop-path semantics consistent: when camera branch is
# dropped, skip both camera-attention branch and camera embedding
# injection. (Drop is a no-op at inference; included for parity.)
cam_embeds = _maybe_drop_cam_branch(
cam_embeds,
cam_branch_drop_prob,
self.training,
x.device,
)
if cam_embeds is None:
kwargs["camera_conditions"] = None
if self.pack_latents:
x = self._pack_latents(x, bs, self.in_channels, self.h, self.w, self.f)
if cam_embeds is not None:
cam_embeds = cam_embeds.to(self.dtype)
self.h = self.h // 2
self.w = self.w // 2
if self.x_embedder.patch_size != self.x_embedder.kernel_size and self.x_embedder.kernel_size == (1, 2, 2):
x = F.pad(x, (0, 1, 0, 1, 0, 0))
if cam_embeds is not None:
cam_embeds = F.pad(cam_embeds, (0, 1, 0, 1, 0, 0))
x = self.x_embedder(x)
if cam_embeds is not None:
# All surviving camctrl variants are UCPE-style: build raymats +
# 3-channel absmap (up_map + lat_map) from the raw (B, F, 20)
# camera conditions.
raw_cam_conditions = cam_embeds
cam_pos_embeds = kwargs.get("cam_pos_embeds", None)
if cam_pos_embeds is not None and "absmap" in cam_pos_embeds:
cam_embeds = cam_pos_embeds["absmap"]
if "P" in cam_pos_embeds:
kwargs["raymats"] = cam_pos_embeds["P"]
else:
raymats, cam_embeds = _process_camera_conditions_ucpe(
raw_cam_conditions, bs, (self.f, self.h, self.w), self.patch_size
)
cam_embeds = cam_embeds.permute(0, 4, 1, 2, 3).to(self.dtype)
kwargs["raymats"] = raymats
_skip_absmap = getattr(self, "use_chunk_plucker_input", False) or getattr(
self, "use_chunk_plucker_post_attn", False
)
if not _skip_absmap:
cam_embeds = self.raymap_embedder(cam_embeds)
x = x + cam_embeds
kwargs["camera_embedding"] = cam_embeds
kwargs["camera_conditions"] = raw_cam_conditions
if getattr(self, "use_chunk_plucker_input", False) and "chunk_plucker" in kwargs:
plucker_input = kwargs["chunk_plucker"].to(self.dtype)
plucker_emb = self.plucker_embedder(plucker_input)
x = x + plucker_emb
if getattr(self, "use_chunk_plucker_post_attn", False) and "chunk_plucker" in kwargs:
plucker_input = kwargs["chunk_plucker"].to(self.dtype)
kwargs["plucker_emb"] = self.plucker_embedder(plucker_input)
image_pos_embed = kwargs.get("pos_embeds", None)
if self.use_pe and image_pos_embed is None:
if self.pos_embed_type == "sincos":
if self.pos_embed_ms is None or self.pos_embed_ms.shape[1:] != x.shape[1:]:
self.pos_embed_ms = (
torch.from_numpy(
get_2d_sincos_pos_embed(
self.pos_embed.shape[-1],
(self.h, self.w),
pe_interpolation=self.pe_interpolation,
base_size=self.base_size,
)
)
.unsqueeze(0)
.to(x.device)
.to(self.dtype)
)
x += self.pos_embed_ms # (N, T, D), where T = H * W / patch_size ** 2
elif self.pos_embed_type == "flux_rope":
self.pos_embed_ms = RopePosEmbed(theta=10000, axes_dim=[12, 10, 10])
latent_image_ids = self.pos_embed_ms._prepare_latent_image_ids(
bs, self.h, self.w, x.device, x.dtype, frame=self.f
)
image_pos_embed = self.pos_embed_ms(latent_image_ids)
elif self.pos_embed_type == "wan_rope":
image_pos_embed = self._compute_rope_with_cp(x.device, self.h, self.w)
elif self.pos_embed_type == "casual_wan_rope":
if frame_index is not None:
# Discontinuous positions (e.g. sink + sliding window):
# rope is built from explicit per-frame indices instead of
# a contiguous range.
image_pos_embed = self.rope(
((0, int(frame_index.numel())), self.h, self.w),
x.device,
frame_index=frame_index,
)
elif start_f is not None and end_f is not None:
image_pos_embed = self.rope(((start_f, end_f), self.h, self.w), x.device)
else:
image_pos_embed = self.rope(((0, self.f), self.h, self.w), x.device)
elif self.pos_embed_type == "wan_temporal_rope":
image_pos_embed = self._compute_rope_with_cp(x.device, self.h, self.w)
else:
raise ValueError(f"Unknown pos_embed_type: {self.pos_embed_type}")
elif image_pos_embed is not None:
image_pos_embed = image_pos_embed.to(x.device)
while image_pos_embed.ndim > 4:
image_pos_embed = image_pos_embed.squeeze(1)
t = self.t_embedder(timestep.flatten()) # (N, D)
t0 = self.t_block(t)
t = t.unflatten(dim=0, sizes=timestep.shape)
t0 = t0.unflatten(dim=0, sizes=timestep.shape)
# Compute delta embeddings for final_layer (stored separately, not
# touching t / t0).
_delta_t_emb = None
if getattr(self, "use_delta_actions", False) and "delta_actions" in kwargs:
da = kwargs["delta_actions"].to(self.dtype)
_delta_t_emb = self.delta_action_embedder(da) # (B, T, D)
if getattr(self, "use_delta_translation", False) and kwargs.get("camera_conditions") is not None:
cam_cond = kwargs["camera_conditions"].to(self.dtype)
c2w = cam_cond[:, :, :16].view(cam_cond.shape[0], cam_cond.shape[1], 4, 4)
t_cam = c2w[:, :, :3, 3] # (B, T, 3)
delta_t = t_cam[:, 1:, :] - t_cam[:, :-1, :]
delta_t = torch.cat([torch.zeros_like(delta_t[:, :1, :]), delta_t], dim=1)
dt_emb = self.delta_translation_embedder(delta_t) # (B, T, D)
_delta_t_emb = dt_emb if _delta_t_emb is None else _delta_t_emb + dt_emb
if getattr(self, "use_delta_pose_additive", False) and "delta_actions" in kwargs:
da = kwargs["delta_actions"].to(self.dtype)
kwargs["delta_pose_emb"] = self.delta_pose_embedder(da) # (B, T, D)
y = self.y_embedder(y, self.training, mask=mask) # (N, D)
if self.y_norm:
y = self.attention_y_norm(y)
if mask is not None:
mask = mask.to(torch.int16)
mask = mask.repeat(y.shape[0] // mask.shape[0], 1) if mask.shape[0] != y.shape[0] else mask
mask = mask.squeeze(1).squeeze(1)
if _xformers_available:
y = y.squeeze(1).masked_select(mask.unsqueeze(-1) != 0).view(1, -1, x.shape[-1])
y_lens = mask.sum(dim=1).tolist()
else:
y_lens = mask
elif _xformers_available:
y_lens = [y.shape[2]] * y.shape[0]
y = y.squeeze(1).view(1, -1, x.shape[-1])
else:
raise ValueError(f"Attention type is not available due to _xformers_available={_xformers_available}.")
if self.diagonal_mask is not None:
seq_len = x.shape[1]
self.diagonal_mask = self.diagonal_mask.to(x.device)
def mask_mod(b, h, q_idx, kv_idx):
return self.diagonal_mask[q_idx, kv_idx].bool()
block_mask = create_block_mask_cached(
mask_mod, None, None, seq_len, seq_len, device=x.device, _compile=False
)
else:
block_mask = None
if kwargs.get("camera_conditions") is not None:
# Pre-compute UCPE projection functions to share across blocks
# (all surviving camctrl variants are UCPE-style).
if self.attn_type in ["flash", "FlexLinearAttention", "flex"]:
head_dim = self.hidden_size // self.num_heads
else:
head_dim = self.linear_head_dim
cam_pos_embeds = kwargs.get("cam_pos_embeds", None)
if cam_pos_embeds is not None:
for k, v in cam_pos_embeds.items():
if isinstance(v, torch.Tensor):
v = v.to(x.device)
if k == "absmap":
while v.ndim > 5:
v = v.squeeze(1)
else:
while v.ndim > 4:
v = v.squeeze(1)
cam_pos_embeds[k] = v
kwargs["prope_fns"] = prepare_prope_fns(
camctrl_type="UCPE",
head_dim=head_dim,
camera_conditions=kwargs["camera_conditions"],
HW=(self.f, self.h, self.w),
patch_size=self.patch_size,
rotary_emb=image_pos_embed,
raymats=kwargs.get("raymats"),
cam_pos_embeds=cam_pos_embeds,
)
assert kv_cache is not None and len(kv_cache) == len(
self.blocks
), "kv_cache must be a list of the same length as the number of blocks"
# Pad cross-attention queries to the total sequence length so that
# xformers uses the same tiling as the baseline forward() path.
# ``end_f`` is the total latent frame count (already patch-divided).
if end_f is not None and self.f > 0:
per_frame_tokens = x.shape[1] // self.f
total_tokens = end_f * per_frame_tokens
if total_tokens > x.shape[1]:
kwargs["_cross_attn_pad_to"] = total_tokens
for i, block in enumerate(self.blocks):
if self.save_qkv:
block.attn.qkv_store_buffer = {}
x, kv_cache_i = torch.utils.checkpoint.checkpoint(
block,
x,
y,
t0,
y_lens,
(self.f, self.h, self.w),
image_pos_embed,
block_mask=block_mask if i > 1 else None,
kv_cache=kv_cache[i],
save_kv_cache=save_kv_cache,
**kwargs,
use_reentrant=False,
)
kv_cache[i] = kv_cache_i
if self.save_qkv:
self.qkv_store_buffer[int(timestep[0].item())][f"block_{i}"] = block.attn.qkv_store_buffer
block.attn.qkv_store_buffer = None
if _delta_t_emb is not None:
if t.ndim == 2:
t = t.unsqueeze(1).expand(-1, _delta_t_emb.shape[1], -1)
elif t.ndim == 4:
t = t.squeeze(1)
t = t + _delta_t_emb
t = t.unsqueeze(1)
x = self.final_layer(x, t) # (N, T, patch_size ** 2 * out_channels)
x = self.unpatchify(x) # (N, out_channels, H, W)
if self.pack_latents:
x = self._unpack_latents(x, self.h * 2, self.w * 2, self.f)
if self.save_block_output:
block_output = self.get_block_output()
self.block_output_buffer[self.inference_timestep] = block_output
return x, kv_cache
@MODELS.register_module()
def SanaMSVideoCamCtrlStreaming_1600M_P1_D20(**kwargs):
# Streaming counterpart of SanaMSVideoCamCtrl_1600M_P1_D20.
return SanaMSVideoCamCtrlStreaming(depth=20, hidden_size=2240, patch_size=(1, 1, 1), num_heads=20, **kwargs)
@MODELS.register_module()
def SanaMSVideoCamCtrl_1600M_P1_D20(**kwargs):
# 20 layers, 1648.48M
return SanaMSVideoCamCtrl(depth=20, hidden_size=2240, patch_size=(1, 1, 1), num_heads=20, **kwargs)