commit 916fc0b1bcfd37b6bd9ece0daeb5b3cbaa53d0a9 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Mon Dec 15 17:30:37 2025 +0200 Update nodes.py commit 63818324f5dbb0b300064bea0402c4cd1bd57b2b Author: kijai <40791699+kijai@users.noreply.github.com> Date: Mon Dec 15 17:30:26 2025 +0200 Refactor RoPE caching commit bb0c55da4d8f8bca4968704e877fd057a90a1eeb Author: kijai <40791699+kijai@users.noreply.github.com> Date: Mon Dec 15 01:59:16 2025 +0200 Update nodes_sampler.py commit a0447d55534857051606ee4201bc7f4e25aa73ae Author: kijai <40791699+kijai@users.noreply.github.com> Date: Mon Dec 15 01:28:09 2025 +0200 Fix non scale wfs commit fa761cc2f2a426faa9c391aeede62cf6f0fd7266 Merge: ea1677b3aae54fAuthor: kijai <40791699+kijai@users.noreply.github.com> Date: Mon Dec 15 01:26:23 2025 +0200 Merge branch 'main' into SCAIL commit ea1677bd4ad42f19e369551590a9d4f17a36fa29 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 19:41:43 2025 +0200 Handle torchscript issue better Some other custom nodes globally set torch._C._jit_set_profiling_executor(False) which breaks the NLF model commit e3cfa64bd3712ac153ce84a75215842c884a8ba4 Merge: ad7a0b93611341Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 16:49:04 2025 +0200 Merge branch 'main' into SCAIL commit ad7a0b925de61ff705b928cd802e752e46089b42 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 16:10:34 2025 +0200 Fix possible uni3c issue commit 74d97fa4bb7c58a0edf8516cc9fad4468da5c57e Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 15:58:42 2025 +0200 Match Uni3C temporal dim commit 056d8ad96ffa5a223a8cd88c900a573a8d450e22 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 14:47:58 2025 +0200 Add warning for potential other overrides on torch.jit.script commit f6dff002ffdcd880451955db298872ea90a4e3f8 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 14:19:33 2025 +0200 Add option to warmup the NLF model on load and fix it's offloading commit a19107501dff23804e7db984d7da304a9955adc9 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sun Dec 14 13:45:20 2025 +0200 Add error to indicate ComfyUI-RMBG currently breaks the NLF model commit e2cfa486e48ead50195884167d9794c7caf0a69f Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 23:29:49 2025 +0200 Cleanup unnecessary code commit 462b61855fb96b0cb18cbccd48593256992808d7 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 18:05:10 2025 +0200 context windows commit e57d4baeebf12c43e851c6c2467d698d7dbb4d03 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 16:55:23 2025 +0200 Start/end percentages and strength commit 3e507ae32256ed3e41cea69d9e26c30b5272968e Merge: 1e5c7cb0fa5383Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 16:09:16 2025 +0200 Merge branch 'main' into SCAIL commit 1e5c7cb2113138bdeae562d266f911c1e3edee91 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 15:45:39 2025 +0200 Update nodes.py commit 98f8e56bcacfc07e12cbb4b26555b2b28d9db92f Merge: 965214678e3e18Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 15:42:44 2025 +0200 Merge branch 'main' into SCAIL commit 9652146763fb27e916a6853a8125efd0a67cd601 Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 02:41:06 2025 +0200 Add imitation of SCAIL pose drawing to the existing NLF node This only draws the pose with same colors, it's not meant as final solution, just for testing. commit 1f86cebdaa97570ed88da0c9986b85c6664d62dc Author: kijai <40791699+kijai@users.noreply.github.com> Date: Sat Dec 13 01:11:56 2025 +0200 test pose inputs commit b348b21dbef0dcb92c0961df85959648e78da6aa Author: kijai <40791699+kijai@users.noreply.github.com> Date: Fri Dec 12 20:10:48 2025 +0200 Init
3231 lines
151 KiB
Python
3231 lines
151 KiB
Python
# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
|
|
import math
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.nn.functional as F
|
|
from einops import repeat, rearrange
|
|
from ...enhance_a_video.enhance import get_feta_scores
|
|
import time
|
|
from contextlib import nullcontext
|
|
|
|
try:
|
|
from ..radial_attention.attn_mask import RadialSpargeSageAttn, RadialSpargeSageAttnDense, MaskMap
|
|
except:
|
|
pass
|
|
|
|
from .attention import attention
|
|
import numpy as np
|
|
from tqdm import tqdm
|
|
import gc
|
|
|
|
from ...utils import log, get_module_memory_mb
|
|
from ...cache_methods.cache_methods import TeaCacheState, MagCacheState, EasyCacheState, relative_l1_distance
|
|
from ...multitalk.multitalk import get_attn_map_with_target
|
|
from ...echoshot.echoshot import rope_apply_z, rope_apply_c, rope_apply_echoshot
|
|
from ...custom_linear import update_lora_step
|
|
|
|
from ...MTV.mtv import apply_rotary_emb
|
|
from comfy.ldm.flux.math import apply_rope1 as apply_rope_comfy1
|
|
from comfy.ldm.flux.math import apply_rope as apply_rope_comfy
|
|
from comfy import model_management as mm
|
|
|
|
__all__ = ['WanModel']
|
|
|
|
def apply_rotary_emb_split(hidden_states, freqs_cis, t_dim):
|
|
"""Apply rotary embedding only to the spatial (H/W) dimensions, leaving temporal (T) unchanged."""
|
|
t_part, hw_part = torch.split(hidden_states, [t_dim, hidden_states.shape[-1] - t_dim], dim=-1)
|
|
hw_freqs = freqs_cis[..., t_dim//2:, :, :]
|
|
|
|
x_ = hw_part.to(dtype=hw_freqs.dtype).reshape(*hw_part.shape[:-1], -1, 1, 2)
|
|
x_out = hw_freqs[..., 0] * x_[..., 0]
|
|
x_out.addcmul_(hw_freqs[..., 1], x_[..., 1])
|
|
out_hw = x_out.reshape(*hw_part.shape).type_as(hidden_states)
|
|
|
|
return torch.cat([t_part, out_hw], dim=-1)
|
|
|
|
class AdaLayerNorm(nn.Module):
|
|
def __init__(self, embedding_dim, output_dim=None, norm_elementwise_affine=False, norm_eps=1e-5):
|
|
super().__init__()
|
|
|
|
output_dim = output_dim or embedding_dim * 2
|
|
|
|
self.silu = nn.SiLU()
|
|
self.linear = nn.Linear(embedding_dim, output_dim)
|
|
self.norm = nn.LayerNorm(output_dim // 2, norm_eps, norm_elementwise_affine)
|
|
|
|
def forward(self, x, temb):
|
|
temb = self.linear(self.silu(temb))
|
|
shift, scale = temb.chunk(2, dim=1)
|
|
shift = shift[:, None, :]
|
|
scale = scale[:, None, :]
|
|
x = self.norm(x) * (1 + scale) + shift
|
|
return x
|
|
|
|
class FramePackMotioner(nn.Module):#from comfy.ldm.wan.model
|
|
def __init__(
|
|
self,
|
|
inner_dim=1024,
|
|
num_heads=16, # Used to indicate the number of heads in the backbone network; unrelated to this module's design
|
|
zip_frame_buckets=[1, 2, 16], # Three numbers representing the number of frames sampled for patch operations from the nearest to the farthest frames
|
|
drop_mode="drop", # If not "drop", it will use "padd", meaning padding instead of deletion
|
|
):
|
|
super().__init__()
|
|
self.proj = nn.Conv3d(16, inner_dim, kernel_size=(1, 2, 2), stride=(1, 2, 2))
|
|
self.proj_2x = nn.Conv3d(16, inner_dim, kernel_size=(2, 4, 4), stride=(2, 4, 4))
|
|
self.proj_4x = nn.Conv3d(16, inner_dim, kernel_size=(4, 8, 8), stride=(4, 8, 8))
|
|
self.zip_frame_buckets = zip_frame_buckets
|
|
|
|
self.inner_dim = inner_dim
|
|
self.num_heads = num_heads
|
|
self.drop_mode = drop_mode
|
|
|
|
def forward(self, motion_latents, rope_embedder, add_last_motion=2):
|
|
lat_height, lat_width = motion_latents.shape[3], motion_latents.shape[4]
|
|
padd_lat = torch.zeros(motion_latents.shape[0], 16, sum(self.zip_frame_buckets), lat_height, lat_width).to(device=motion_latents.device, dtype=motion_latents.dtype)
|
|
overlap_frame = min(padd_lat.shape[2], motion_latents.shape[2])
|
|
if overlap_frame > 0:
|
|
padd_lat[:, :, -overlap_frame:] = motion_latents[:, :, -overlap_frame:]
|
|
|
|
if add_last_motion < 2 and self.drop_mode != "drop":
|
|
zero_end_frame = sum(self.zip_frame_buckets[:len(self.zip_frame_buckets) - add_last_motion - 1])
|
|
padd_lat[:, :, -zero_end_frame:] = 0
|
|
|
|
clean_latents_4x, clean_latents_2x, clean_latents_post = padd_lat[:, :, -sum(self.zip_frame_buckets):, :, :].split(self.zip_frame_buckets[::-1], dim=2) # 16, 2 ,1
|
|
|
|
# patchfy
|
|
clean_latents_post = self.proj(clean_latents_post).flatten(2).transpose(1, 2)
|
|
clean_latents_2x = self.proj_2x(clean_latents_2x)
|
|
l_2x_shape = clean_latents_2x.shape
|
|
clean_latents_2x = clean_latents_2x.flatten(2).transpose(1, 2)
|
|
clean_latents_4x = self.proj_4x(clean_latents_4x)
|
|
l_4x_shape = clean_latents_4x.shape
|
|
clean_latents_4x = clean_latents_4x.flatten(2).transpose(1, 2)
|
|
|
|
if add_last_motion < 2 and self.drop_mode == "drop":
|
|
clean_latents_post = clean_latents_post[:, :0] if add_last_motion < 2 else clean_latents_post
|
|
clean_latents_2x = clean_latents_2x[:, :0] if add_last_motion < 1 else clean_latents_2x
|
|
|
|
motion_lat = torch.cat([clean_latents_post, clean_latents_2x, clean_latents_4x], dim=1)
|
|
|
|
rope_post = rope_embedder.rope_encode_comfy(1, lat_height, lat_width, t_start=-1, device=motion_latents.device, dtype=motion_latents.dtype)
|
|
rope_2x = rope_embedder.rope_encode_comfy(1, lat_height, lat_width, t_start=-3, steps_h=l_2x_shape[-2], steps_w=l_2x_shape[-1], device=motion_latents.device, dtype=motion_latents.dtype)
|
|
rope_4x = rope_embedder.rope_encode_comfy(4, lat_height, lat_width, t_start=-19, steps_h=l_4x_shape[-2], steps_w=l_4x_shape[-1], device=motion_latents.device, dtype=motion_latents.dtype)
|
|
|
|
rope = torch.cat([rope_post, rope_2x, rope_4x], dim=1)
|
|
return motion_lat, rope
|
|
|
|
def torch_dfs(model: nn.Module, parent_name='root'):
|
|
module_names, modules = [], []
|
|
current_name = parent_name if parent_name else 'root'
|
|
module_names.append(current_name)
|
|
modules.append(model)
|
|
|
|
for name, child in model.named_children():
|
|
if parent_name:
|
|
child_name = f'{parent_name}.{name}'
|
|
else:
|
|
child_name = name
|
|
child_modules, child_names = torch_dfs(child, child_name)
|
|
module_names += child_names
|
|
modules += child_modules
|
|
return modules, module_names
|
|
|
|
def rope_riflex(pos, dim, i, theta, L_test, k, ntk_factor=1.0):
|
|
assert dim % 2 == 0
|
|
if mm.is_device_mps(pos.device) or mm.is_intel_xpu() or mm.is_directml_enabled():
|
|
device = torch.device("cpu")
|
|
else:
|
|
device = pos.device
|
|
|
|
if ntk_factor != 1.0:
|
|
theta *= ntk_factor
|
|
|
|
scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device)
|
|
omega = 1.0 / (theta**scale)
|
|
|
|
# RIFLEX modification - adjust last frequency component if L_test and k are provided
|
|
if i==0 and k > 0 and L_test:
|
|
omega[k-1] = 0.9 * 2 * torch.pi / L_test
|
|
|
|
out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega)
|
|
out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1)
|
|
out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2)
|
|
return out.to(dtype=torch.float32, device=pos.device)
|
|
|
|
class EmbedND_RifleX(nn.Module):
|
|
def __init__(self, dim, theta, axes_dim, num_frames, k):
|
|
super().__init__()
|
|
self.dim = dim
|
|
self.theta = theta
|
|
self.axes_dim = axes_dim
|
|
self.num_frames = num_frames
|
|
self.k = k
|
|
|
|
def forward(self, ids, ntk_factor=[1.0,1.0,1.0]):
|
|
n_axes = ids.shape[-1]
|
|
emb = torch.cat(
|
|
[rope_riflex(
|
|
ids[..., i],
|
|
self.axes_dim[i],
|
|
i, #f h w
|
|
self.theta,
|
|
self.num_frames,
|
|
self.k,
|
|
ntk_factor[i])
|
|
for i in range(n_axes)],
|
|
dim=-3,
|
|
)
|
|
return emb.unsqueeze(1)
|
|
|
|
def poly1d(coefficients, x):
|
|
result = torch.zeros_like(x)
|
|
for i, coeff in enumerate(coefficients):
|
|
result += coeff * (x ** (len(coefficients) - 1 - i))
|
|
return result.abs()
|
|
|
|
def sinusoidal_embedding_1d(dim, position):
|
|
# preprocess
|
|
assert dim % 2 == 0
|
|
half = dim // 2
|
|
position = position.type(torch.float32)
|
|
|
|
# calculation
|
|
sinusoid = torch.outer(
|
|
position, torch.pow(10000, -torch.arange(half).to(position).div(half)))
|
|
x = torch.cat([torch.cos(sinusoid), torch.sin(sinusoid)], dim=1)
|
|
return x
|
|
|
|
def rope_params(max_seq_len, dim, theta=10000, L_test=25, k=0, freqs_scaling=1.0):
|
|
assert dim % 2 == 0
|
|
exponents = torch.arange(0, dim, 2, dtype=torch.float64).div(dim)
|
|
inv_theta_pow = 1.0 / torch.pow(theta, exponents)
|
|
|
|
if k > 0:
|
|
print(f"RifleX: Using {k}th freq")
|
|
inv_theta_pow[k-1] = 0.9 * 2 * torch.pi / L_test
|
|
|
|
inv_theta_pow *= freqs_scaling
|
|
|
|
freqs = torch.outer(torch.arange(max_seq_len), inv_theta_pow)
|
|
freqs = torch.polar(torch.ones_like(freqs), freqs)
|
|
return freqs
|
|
|
|
@torch.autocast(device_type=mm.get_autocast_device(mm.get_torch_device()), enabled=False)
|
|
@torch.compiler.disable()
|
|
def rope_apply(x, grid_sizes, freqs, reverse_time=False):
|
|
x_ndim = grid_sizes.shape[-1]
|
|
if x_ndim == 3:
|
|
return rope_apply_3d(x, grid_sizes, freqs, reverse_time=reverse_time)
|
|
else:
|
|
return rope_apply_1d(x, grid_sizes, freqs)
|
|
|
|
def rope_apply_3d(x, grid_sizes, freqs, reverse_time=False):
|
|
n, c = x.size(2), x.size(3) // 2
|
|
|
|
# split freqs
|
|
freqs = freqs.split([c - 2 * (c // 3), c // 3, c // 3], dim=1)
|
|
|
|
# loop over samples
|
|
output = []
|
|
for i, (f, h, w) in enumerate(grid_sizes.tolist()):
|
|
seq_len = f * h * w
|
|
|
|
# precompute multipliers
|
|
x_i = torch.view_as_complex(x[i, :seq_len].to(torch.float64).reshape(
|
|
seq_len, n, -1, 2))
|
|
if reverse_time:
|
|
time_freqs = freqs[0][:f].view(f, 1, 1, -1)
|
|
time_freqs = torch.flip(time_freqs, dims=[0])
|
|
time_freqs = time_freqs.expand(f, h, w, -1)
|
|
|
|
spatial_freqs = torch.cat([
|
|
freqs[1][:h].view(1, h, 1, -1).expand(f, h, w, -1),
|
|
freqs[2][:w].view(1, 1, w, -1).expand(f, h, w, -1)
|
|
], dim=-1)
|
|
|
|
freqs_i = torch.cat([time_freqs, spatial_freqs], dim=-1).reshape(seq_len, 1, -1)
|
|
else:
|
|
freqs_i = torch.cat([
|
|
freqs[0][:f].view(f, 1, 1, -1).expand(f, h, w, -1),
|
|
freqs[1][:h].view(1, h, 1, -1).expand(f, h, w, -1),
|
|
freqs[2][:w].view(1, 1, w, -1).expand(f, h, w, -1)
|
|
],
|
|
dim=-1).reshape(seq_len, 1, -1)
|
|
|
|
# apply rotary embedding
|
|
x_i = torch.view_as_real(x_i * freqs_i).flatten(2)
|
|
x_i = torch.cat([x_i, x[i, seq_len:]])
|
|
|
|
# append to collection
|
|
output.append(x_i)
|
|
return torch.stack(output).to(x.dtype)
|
|
|
|
|
|
def rope_apply_1d(x, grid_sizes, freqs):
|
|
n, c = x.size(2), x.size(3) // 2 ## b l h d
|
|
c_rope = freqs.shape[1] # number of complex dims to rotate
|
|
assert c_rope <= c, "RoPE dimensions cannot exceed half of hidden size"
|
|
|
|
# loop over samples
|
|
output = []
|
|
for i, (l, ) in enumerate(grid_sizes.tolist()):
|
|
seq_len = l
|
|
# precompute multipliers
|
|
x_i = torch.view_as_complex(x[i, :seq_len].to(torch.float64).reshape(
|
|
seq_len, n, -1, 2)) # [l n d//2]
|
|
x_i_rope = x_i[:, :, :c_rope] * freqs[:seq_len, None, :] # [L, N, c_rope]
|
|
x_i_passthrough = x_i[:, :, c_rope:] # untouched dims
|
|
x_i = torch.cat([x_i_rope, x_i_passthrough], dim=2)
|
|
|
|
# apply rotary embedding
|
|
x_i = torch.view_as_real(x_i).flatten(2)
|
|
x_i = torch.cat([x_i, x[i, seq_len:]])
|
|
|
|
# append to collection
|
|
output.append(x_i)
|
|
return torch.stack(output).to(x.dtype)
|
|
|
|
class WanRMSNorm(nn.Module):
|
|
|
|
def __init__(self, dim, eps=1e-5):
|
|
super().__init__()
|
|
self.dim = dim
|
|
self.eps = eps
|
|
self.weight = nn.Parameter(torch.ones(dim))
|
|
|
|
def forward(self, x, num_chunks=1):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L, C]
|
|
"""
|
|
use_chunked = num_chunks > 1
|
|
if use_chunked:
|
|
return self.forward_chunked(x, num_chunks)
|
|
else:
|
|
return self._norm(x.to(self.weight.dtype)) * self.weight
|
|
|
|
def _norm(self, x):
|
|
return x * (torch.rsqrt(x.pow(2).mean(dim=-1, keepdim=True) + self.eps)).to(x.dtype)
|
|
|
|
def forward_chunked(self, x, num_chunks=4):
|
|
output = torch.empty_like(x)
|
|
|
|
chunk_sizes = [x.shape[1] // num_chunks + (1 if i < x.shape[1] % num_chunks else 0)
|
|
for i in range(num_chunks)]
|
|
|
|
start_idx = 0
|
|
for size in chunk_sizes:
|
|
end_idx = start_idx + size
|
|
|
|
chunk = x[:, start_idx:end_idx, :]
|
|
|
|
norm_factor = torch.rsqrt(chunk.pow(2).mean(dim=-1, keepdim=True) + self.eps)
|
|
output[:, start_idx:end_idx, :] = chunk * norm_factor.to(chunk.dtype) * self.weight
|
|
|
|
start_idx = end_idx
|
|
|
|
return output
|
|
|
|
class WanFusedRMSNorm(nn.RMSNorm):
|
|
def forward(self, x, num_chunks=1):
|
|
use_chunked = num_chunks > 1
|
|
if use_chunked:
|
|
return self.forward_chunked(x, num_chunks)
|
|
else:
|
|
return super().forward(x)
|
|
|
|
def forward_chunked(self, x, num_chunks=4):
|
|
output = torch.empty_like(x)
|
|
|
|
chunk_sizes = [x.shape[1] // num_chunks + (1 if i < x.shape[1] % num_chunks else 0)
|
|
for i in range(num_chunks)]
|
|
|
|
start_idx = 0
|
|
for size in chunk_sizes:
|
|
end_idx = start_idx + size
|
|
chunk = x[:, start_idx:end_idx, :]
|
|
output[:, start_idx:end_idx, :] = super().forward(chunk)
|
|
start_idx = end_idx
|
|
|
|
return output
|
|
|
|
class WanLayerNorm(nn.LayerNorm):
|
|
|
|
def __init__(self, dim, eps=1e-6, elementwise_affine=False):
|
|
super().__init__(dim, elementwise_affine=elementwise_affine, eps=eps)
|
|
|
|
def forward(self, x):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L, C]
|
|
"""
|
|
return super().forward(x)
|
|
|
|
#region selfattn
|
|
class WanSelfAttention(nn.Module):
|
|
|
|
def __init__(self,
|
|
in_features,
|
|
out_features,
|
|
num_heads,
|
|
qk_norm=True,
|
|
eps=1e-6,
|
|
attention_mode="sdpa",
|
|
rms_norm_function="default",
|
|
kv_dim=None,
|
|
head_norm=False):
|
|
assert out_features % num_heads == 0
|
|
super().__init__()
|
|
self.dim = min(in_features, out_features)
|
|
self.num_heads = num_heads
|
|
self.head_dim = out_features // num_heads
|
|
self.qk_norm = qk_norm
|
|
self.eps = eps
|
|
self.attention_mode = attention_mode
|
|
|
|
#radial attention
|
|
self.mask_map = None
|
|
self.decay_factor = 0.2
|
|
self.cond_size = None
|
|
self.ref_adapter = None
|
|
|
|
# layers
|
|
self.q = nn.Linear(in_features, out_features)
|
|
if kv_dim is not None:
|
|
self.k = nn.Linear(kv_dim, out_features)
|
|
self.v = nn.Linear(kv_dim, out_features)
|
|
else:
|
|
self.k = nn.Linear(in_features, out_features)
|
|
self.v = nn.Linear(in_features, out_features)
|
|
self.o = nn.Linear(in_features, out_features)
|
|
|
|
norm_dim = self.head_dim if head_norm else self.dim
|
|
|
|
if rms_norm_function=="pytorch":
|
|
self.norm_q = WanFusedRMSNorm(norm_dim, eps=eps) if qk_norm else nn.Identity()
|
|
self.norm_k = WanFusedRMSNorm(norm_dim, eps=eps) if qk_norm else nn.Identity()
|
|
else:
|
|
self.norm_q = WanRMSNorm(norm_dim, eps=eps) if qk_norm else nn.Identity()
|
|
self.norm_k = WanRMSNorm(norm_dim, eps=eps) if qk_norm else nn.Identity()
|
|
|
|
def qkv_fn(self, x, is_longcat=False):
|
|
b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim
|
|
if is_longcat:
|
|
q = self.q(x).view(b, s, n, d)
|
|
q = self.norm_q(q.float()).to(x.dtype)
|
|
k = self.k(x).view(b, s, n, d)
|
|
k = self.norm_k(k.float()).to(x.dtype)
|
|
else:
|
|
q = self.norm_q(self.q(x).to(self.norm_q.weight.dtype)).to(x.dtype).view(b, s, n, d)
|
|
k = self.norm_k(self.k(x).to(self.norm_k.weight.dtype)).to(x.dtype).view(b, s, n, d)
|
|
v = self.v(x).view(b, s, n, d)
|
|
return q, k, v
|
|
|
|
def _qkv_fn_with_rope(self, x, linear_layer, norm_layer, freqs, num_chunks=1, is_longcat=False):
|
|
b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim
|
|
|
|
use_chunked = num_chunks > 1
|
|
if use_chunked:
|
|
out = torch.empty(b, s, n, d, dtype=x.dtype, device=x.device)
|
|
|
|
for i, x_chunk in enumerate(x.chunk(num_chunks, dim=1)):
|
|
chunk_size = x_chunk.size(1)
|
|
start_idx = i * (s // num_chunks + (1 if i < s % num_chunks else 0))
|
|
|
|
if is_longcat:
|
|
chunk = linear_layer(x_chunk).view(b, chunk_size, n, d)
|
|
chunk = norm_layer(chunk.float()).to(x.dtype)
|
|
else:
|
|
chunk = norm_layer(linear_layer(x_chunk).to(norm_layer.weight.dtype)).to(x.dtype).view(b, chunk_size, n, d)
|
|
|
|
freqs_chunk = freqs[:, start_idx:start_idx + chunk_size] if freqs.shape[1] > 1 else freqs
|
|
out[:, start_idx:start_idx + chunk_size] = apply_rope_comfy1(chunk, freqs_chunk)
|
|
|
|
return out
|
|
else:
|
|
if is_longcat:
|
|
result = linear_layer(x).view(b, s, n, d)
|
|
result = norm_layer(result.float()).to(x.dtype)
|
|
else:
|
|
result = norm_layer(linear_layer(x).to(norm_layer.weight.dtype)).to(x.dtype).view(b, s, n, d)
|
|
return apply_rope_comfy1(result, freqs)
|
|
|
|
def qkv_fn_q_with_rope(self, x, freqs, num_chunks=1, is_longcat=False):
|
|
return self._qkv_fn_with_rope(x, self.q, self.norm_q, freqs, num_chunks, is_longcat)
|
|
|
|
def qkv_fn_k_with_rope(self, x, freqs, num_chunks=1, is_longcat=False):
|
|
return self._qkv_fn_with_rope(x, self.k, self.norm_k, freqs, num_chunks, is_longcat)
|
|
|
|
def qkv_fn_v(self, x):
|
|
b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim
|
|
return self.v(x).view(b, s, n, d)
|
|
|
|
def qkv_fn_ip(self, x):
|
|
b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim
|
|
q = self.norm_q(self.q(x) + self.q_loras(x).to(self.norm_q.weight.dtype)).to(x.dtype).view(b, s, n, d)
|
|
k = self.norm_k(self.k(x) + self.k_loras(x).to(self.norm_k.weight.dtype)).to(x.dtype).view(b, s, n, d)
|
|
v = (self.v(x) + self.v_loras(x)).view(b, s, n, d)
|
|
return q, k, v
|
|
|
|
def forward(self, q, k, v, seq_lens, lynx_ref_feature=None, lynx_ref_scale=1.0, attention_mode_override=None, onetoall_ref=None, onetoall_ref_scale=1.0):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L, num_heads, C / num_heads]
|
|
seq_lens(Tensor): Shape [B]
|
|
grid_sizes(Tensor): Shape [B, 3], the second dimension contains (F, H, W)
|
|
freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2]
|
|
"""
|
|
attention_mode = self.attention_mode
|
|
if attention_mode_override is not None:
|
|
attention_mode = attention_mode_override
|
|
|
|
if self.ref_adapter is not None and lynx_ref_feature is not None:
|
|
ref_x = self.ref_adapter(self, q, lynx_ref_feature)
|
|
|
|
x = attention(q, k, v, k_lens=seq_lens, attention_mode=attention_mode, heads=self.num_heads)
|
|
|
|
if self.ref_adapter is not None and lynx_ref_feature is not None:
|
|
x = x.add(ref_x, alpha=lynx_ref_scale)
|
|
|
|
if onetoall_ref is not None:
|
|
x = x.add(onetoall_ref, alpha=onetoall_ref_scale)
|
|
|
|
# output
|
|
return self.o(x.flatten(2))
|
|
|
|
def forward_ip(self, q, k, v, q_ip, k_ip, v_ip, seq_lens, attention_mode_override=None):
|
|
attention_mode = self.attention_mode
|
|
if attention_mode_override is not None:
|
|
attention_mode = attention_mode_override
|
|
|
|
# Concatenate main and IP keys/values for main attention
|
|
full_k = torch.cat([k, k_ip], dim=1)
|
|
full_v = torch.cat([v, v_ip], dim=1)
|
|
main_out = attention(q, full_k, full_v, k_lens=seq_lens, attention_mode=attention_mode, heads=self.num_heads)
|
|
|
|
cond_out = attention(q_ip, k_ip, v_ip, k_lens=seq_lens, attention_mode=attention_mode, heads=self.num_heads)
|
|
x = torch.cat([main_out, cond_out], dim=1)
|
|
|
|
return self.o(x.flatten(2))
|
|
|
|
|
|
def forward_radial(self, q, k, v, dense_step=False):
|
|
if dense_step:
|
|
x = RadialSpargeSageAttnDense(q, k, v, self.mask_map)
|
|
else:
|
|
x = RadialSpargeSageAttn(q, k, v, self.mask_map, decay_factor=self.decay_factor)
|
|
return self.o(x.flatten(2))
|
|
|
|
|
|
def forward_multitalk(self, q, k, v, seq_lens, grid_sizes, ref_target_masks):
|
|
x = attention(q, k, v, k_lens=seq_lens, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
x = self.o(x.flatten(2))
|
|
x_ref_attn_map = get_attn_map_with_target(q.type_as(x), k.type_as(x), grid_sizes[0], ref_target_masks=ref_target_masks)
|
|
return x, x_ref_attn_map
|
|
|
|
|
|
def forward_split(self, q, k, v, seq_lens, grid_sizes, seq_chunks):
|
|
# Split by frames if multiple prompts are provided
|
|
frames, height, width = grid_sizes[0]
|
|
tokens_per_frame = height * width
|
|
|
|
seq_chunks_tensor = torch.tensor(seq_chunks, device=q.device, dtype=frames.dtype)
|
|
actual_chunks = torch.minimum(seq_chunks_tensor, frames)
|
|
base_frames_per_chunk = frames // actual_chunks
|
|
extra_frames = frames % actual_chunks
|
|
|
|
chunk_indices = torch.arange(actual_chunks, device=q.device)
|
|
chunk_sizes = base_frames_per_chunk + (chunk_indices < extra_frames)
|
|
chunk_starts = torch.cumsum(torch.cat([torch.zeros(1, device=q.device, dtype=torch.long), chunk_sizes[:-1]]), dim=0)
|
|
chunk_ends = chunk_starts + chunk_sizes
|
|
|
|
outputs = []
|
|
for i in chunk_indices:
|
|
start_idx = chunk_starts[i] * tokens_per_frame
|
|
end_idx = chunk_ends[i] * tokens_per_frame
|
|
|
|
chunk_out = attention(
|
|
q[:, start_idx:end_idx, :, :],
|
|
k[:, start_idx:end_idx, :, :],
|
|
v[:, start_idx:end_idx, :, :],
|
|
k_lens=seq_lens,
|
|
attention_mode=self.attention_mode,
|
|
heads=self.num_heads
|
|
)
|
|
outputs.append(chunk_out)
|
|
x = torch.cat(outputs, dim=1)
|
|
|
|
# output
|
|
return self.o(x.flatten(2))
|
|
|
|
def normalized_attention_guidance(self, b, n, d, q, context, nag_context=None, nag_params={}):
|
|
# NAG text attention
|
|
context_positive = context
|
|
context_negative = nag_context
|
|
nag_scale = nag_params['nag_scale']
|
|
nag_alpha = nag_params['nag_alpha']
|
|
nag_tau = nag_params['nag_tau']
|
|
|
|
k_positive = self.norm_k(self.k(context_positive).to(self.norm_k.weight.dtype)).view(b, -1, n, d).to(q.dtype)
|
|
v_positive = self.v(context_positive).view(b, -1, n, d)
|
|
k_negative = self.norm_k(self.k(context_negative).to(self.norm_k.weight.dtype)).view(b, -1, n, d).to(q.dtype)
|
|
v_negative = self.v(context_negative).view(b, -1, n, d)
|
|
|
|
x_positive = attention(q, k_positive, v_positive, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
x_positive = x_positive.flatten(2)
|
|
|
|
x_negative = attention(q, k_negative, v_negative, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
x_negative = x_negative.flatten(2)
|
|
|
|
nag_guidance = x_positive * nag_scale - x_negative * (nag_scale - 1)
|
|
|
|
norm_positive = torch.norm(x_positive, p=1, dim=-1, keepdim=True)
|
|
norm_guidance = torch.norm(nag_guidance, p=1, dim=-1, keepdim=True)
|
|
|
|
scale = norm_guidance / norm_positive
|
|
scale = torch.nan_to_num(scale, nan=10.0)
|
|
|
|
mask = scale > nag_tau
|
|
adjustment = (norm_positive * nag_tau) / (norm_guidance + 1e-7)
|
|
nag_guidance = torch.where(mask, nag_guidance * adjustment, nag_guidance)
|
|
del mask, adjustment
|
|
|
|
return nag_guidance * nag_alpha + x_positive * (1 - nag_alpha)
|
|
|
|
class LoRALinearLayer(nn.Module):
|
|
def __init__(
|
|
self,
|
|
in_features: int,
|
|
out_features: int,
|
|
rank: int = 128,
|
|
device=torch.device("cuda"),
|
|
dtype=torch.float32,
|
|
strength: float = 1.0
|
|
):
|
|
super().__init__()
|
|
self.down = nn.Linear(in_features, rank, bias=False, device=device, dtype=dtype)
|
|
self.up = nn.Linear(rank, out_features, bias=False, device=device, dtype=dtype)
|
|
self.rank = rank
|
|
self.out_features = out_features
|
|
self.in_features = in_features
|
|
self.strength = strength
|
|
|
|
nn.init.normal_(self.down.weight, std=1 / rank)
|
|
nn.init.zeros_(self.up.weight)
|
|
|
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
|
orig_dtype = hidden_states.dtype
|
|
dtype = self.down.weight.dtype
|
|
|
|
down_hidden_states = self.down(hidden_states.to(dtype))
|
|
up_hidden_states = self.up(down_hidden_states) * self.strength
|
|
return up_hidden_states.to(orig_dtype)
|
|
|
|
#region crossattn
|
|
class WanT2VCrossAttention(WanSelfAttention):
|
|
|
|
def __init__(self, in_features, out_features, num_heads, kv_dim=None, qk_norm=True, eps=1e-6, attention_mode='sdpa', rms_norm_function="default", head_norm=False):
|
|
super().__init__(in_features, out_features, num_heads, qk_norm, eps, kv_dim=kv_dim, rms_norm_function=rms_norm_function, head_norm=head_norm)
|
|
self.attention_mode = attention_mode
|
|
self.ip_adapter = None
|
|
self.k_fusion = None
|
|
|
|
def forward(self, x, context, grid_sizes=None, clip_embed=None, audio_proj=None, audio_scale=1.0,
|
|
num_latent_frames=21, nag_params={}, nag_context=None, rope_func="comfy",
|
|
inner_t=None, inner_c=None, cross_freqs=None,
|
|
adapter_proj=None, adapter_attn_mask=None, ip_scale=1.0, orig_seq_len=None, lynx_x_ip=None, lynx_ip_scale=1.0, num_cond_latents=None, **kwargs):
|
|
b, n, d = x.size(0), self.num_heads, self.head_dim
|
|
s = x.size(1)
|
|
# compute query
|
|
is_longcat = x.shape[-1] == 4096
|
|
|
|
if is_longcat:
|
|
if num_cond_latents is not None and num_cond_latents > 0:
|
|
num_cond_latents_thw = num_cond_latents * (s // num_latent_frames)
|
|
x = x[:, num_cond_latents_thw:]
|
|
q = self.norm_q(self.q(x).view(b, -1, n, d))
|
|
else:
|
|
q = self.norm_q(self.q(x).to(self.norm_q.weight.dtype),num_chunks=2 if rope_func == "comfy_chunked" else 1).to(x.dtype).view(b, -1, n, d)
|
|
|
|
if nag_context is not None:
|
|
x = self.normalized_attention_guidance(b, n, d, q, context, nag_context, nag_params)
|
|
else:
|
|
if is_longcat:
|
|
k = self.norm_k(self.k(context).to(self.norm_k.weight.dtype).view(b, -1, n, d)).to(x.dtype)
|
|
else:
|
|
k = self.norm_k(self.k(context).to(self.norm_k.weight.dtype)).to(x.dtype).view(b, -1, n, d)
|
|
|
|
v = self.v(context).view(b, -1, n, d)
|
|
|
|
#EchoShot rope
|
|
if inner_t is not None and cross_freqs is not None:
|
|
q = rope_apply_z(q, grid_sizes, cross_freqs, inner_t).to(q)
|
|
k = rope_apply_c(k, cross_freqs, inner_c).to(q)
|
|
|
|
x = attention(q, k, v, attention_mode=self.attention_mode, heads=self.num_heads).flatten(2)
|
|
|
|
if lynx_x_ip is not None and self.ip_adapter is not None and ip_scale !=0:
|
|
lynx_x_ip = self.ip_adapter(self, q, lynx_x_ip)
|
|
x = x.add(lynx_x_ip, alpha=lynx_ip_scale)
|
|
|
|
# FantasyTalking audio attention
|
|
if audio_proj is not None:
|
|
if len(audio_proj.shape) == 4:
|
|
audio_q = q.view(b * num_latent_frames, -1, n, d)
|
|
ip_key = self.k_proj(audio_proj).view(b * num_latent_frames, -1, n, d)
|
|
ip_value = self.v_proj(audio_proj).view(b * num_latent_frames, -1, n, d)
|
|
audio_x = attention(audio_q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
audio_x = audio_x.view(b, q.size(1), n, d).flatten(2)
|
|
elif len(audio_proj.shape) == 3:
|
|
ip_key = self.k_proj(audio_proj).view(b, -1, n, d)
|
|
ip_value = self.v_proj(audio_proj).view(b, -1, n, d)
|
|
audio_x = attention(q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads).flatten(2)
|
|
x = x + audio_x * audio_scale
|
|
|
|
# FantasyPortrait adapter attention
|
|
if adapter_proj is not None:
|
|
if len(adapter_proj.shape) == 4:
|
|
q_in = q[:, :orig_seq_len]
|
|
adapter_q = q_in.view(b * num_latent_frames, -1, n, d)
|
|
ip_key = self.ip_adapter_single_stream_k_proj(adapter_proj).view(b * num_latent_frames, -1, n, d)
|
|
ip_value = self.ip_adapter_single_stream_v_proj(adapter_proj).view(b * num_latent_frames, -1, n, d)
|
|
|
|
adapter_x = attention(adapter_q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
adapter_x = adapter_x.view(b, q_in.size(1), n, d)
|
|
adapter_x = adapter_x.flatten(2)
|
|
elif len(adapter_proj.shape) == 3:
|
|
ip_key = self.ip_adapter_single_stream_k_proj(adapter_proj).view(b, -1, n, d)
|
|
ip_value = self.ip_adapter_single_stream_v_proj(adapter_proj).view(b, -1, n, d)
|
|
adapter_x = attention(q_in, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
adapter_x = adapter_x.flatten(2)
|
|
x[:, :orig_seq_len] = x[:, :orig_seq_len] + adapter_x * ip_scale
|
|
|
|
if self.k_fusion is not None:
|
|
# compute target attention
|
|
target_seq = self.pre_attn_norm_fusion(kwargs["target_seq"])
|
|
k_target = self.norm_k_fusion(self.k_fusion(target_seq)).view(b, -1, n, d)
|
|
v_target = self.v_fusion(target_seq).view(b, -1, n, d)
|
|
|
|
q = rope_apply(q, grid_sizes, kwargs["src_freqs"])
|
|
k_target = rope_apply(k_target, kwargs["target_grid_sizes"], kwargs["target_freqs"])
|
|
target_x = attention(q, k_target, v_target, k_lens=kwargs["target_seq_lens"], heads=self.num_heads).flatten(2)
|
|
|
|
x = x.add(target_x)
|
|
|
|
if is_longcat and num_cond_latents is not None and num_cond_latents > 0:
|
|
return torch.cat([torch.zeros((b, num_cond_latents_thw, x.shape[-1]), dtype=x.dtype, device=x.device), self.o(x)], dim=1).contiguous()
|
|
|
|
return self.o(x)
|
|
|
|
class WanI2VCrossAttention(WanSelfAttention):
|
|
|
|
def __init__(self, in_features, out_features, num_heads, qk_norm=True, eps=1e-6, attention_mode='sdpa', rms_norm_function="default", **kwargs):
|
|
super().__init__(in_features, out_features, num_heads, qk_norm, eps, rms_norm_function=rms_norm_function)
|
|
self.k_img = nn.Linear(in_features, out_features)
|
|
self.v_img = nn.Linear(in_features, out_features)
|
|
self.norm_k_img = WanRMSNorm(out_features, eps=eps) if qk_norm else nn.Identity()
|
|
self.attention_mode = attention_mode
|
|
|
|
def forward(self, x, context, grid_sizes=None, clip_embed=None, audio_proj=None,
|
|
audio_scale=1.0, num_latent_frames=21, nag_params={}, nag_context=None, rope_func="comfy",
|
|
adapter_proj=None, adapter_attn_mask=None, ip_scale=1.0, orig_seq_len=None, **kwargs):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L1, C]
|
|
context(Tensor): Shape [B, L2, C]
|
|
"""
|
|
b, n, d = x.size(0), self.num_heads, self.head_dim
|
|
|
|
# compute query
|
|
q = self.norm_q(self.q(x).to(self.norm_q.weight.dtype),num_chunks=2 if rope_func == "comfy_chunked" else 1).view(b, -1, n, d).to(x.dtype)
|
|
|
|
if nag_context is not None:
|
|
x_text = self.normalized_attention_guidance(b, n, d, q, context, nag_context, nag_params)
|
|
else:
|
|
# text attention
|
|
k = self.norm_k(self.k(context).to(self.norm_k.weight.dtype)).view(b, -1, n, d).to(x.dtype)
|
|
v = self.v(context).view(b, -1, n, d)
|
|
x_text = attention(q, k, v, attention_mode=self.attention_mode, heads=self.num_heads).flatten(2)
|
|
|
|
#img attention
|
|
if clip_embed is not None:
|
|
k_img = self.norm_k_img(self.k_img(clip_embed).to(self.norm_k_img.weight.dtype)).view(b, -1, n, d).to(x.dtype)
|
|
v_img = self.v_img(clip_embed).view(b, -1, n, d)
|
|
img_x = attention(q, k_img, v_img, attention_mode=self.attention_mode, heads=self.num_heads).flatten(2)
|
|
x = x_text + img_x
|
|
else:
|
|
x = x_text
|
|
|
|
# FantasyTalking audio attention
|
|
if audio_proj is not None:
|
|
if len(audio_proj.shape) == 4:
|
|
audio_q = q.view(b * num_latent_frames, -1, n, d)
|
|
ip_key = self.k_proj(audio_proj).view(b * num_latent_frames, -1, n, d)
|
|
ip_value = self.v_proj(audio_proj).view(b * num_latent_frames, -1, n, d)
|
|
|
|
audio_x = attention(audio_q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
audio_x = audio_x.view(b, q.size(1), n, d).flatten(2)
|
|
elif len(audio_proj.shape) == 3:
|
|
ip_key = self.k_proj(audio_proj).view(b, -1, n, d)
|
|
ip_value = self.v_proj(audio_proj).view(b, -1, n, d)
|
|
audio_x = attention(q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads).flatten(2)
|
|
x = x + audio_x * audio_scale
|
|
|
|
# FantasyPortrait adapter attention
|
|
if adapter_proj is not None:
|
|
if len(adapter_proj.shape) == 4:
|
|
adapter_q = q.view(b * num_latent_frames, -1, n, d)
|
|
ip_key = self.ip_adapter_single_stream_k_proj(adapter_proj).view(b * num_latent_frames, -1, n, d)
|
|
ip_value = self.ip_adapter_single_stream_v_proj(adapter_proj).view(b * num_latent_frames, -1, n, d)
|
|
|
|
adapter_x = attention(adapter_q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
adapter_x = adapter_x.view(b, q.size(1), n, d)
|
|
adapter_x = adapter_x.flatten(2)
|
|
elif len(adapter_proj.shape) == 3:
|
|
ip_key = self.ip_adapter_single_stream_k_proj(adapter_proj).view(b, -1, n, d)
|
|
ip_value = self.ip_adapter_single_stream_v_proj(adapter_proj).view(b, -1, n, d)
|
|
adapter_x = attention(q, ip_key, ip_value, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
adapter_x = adapter_x.flatten(2)
|
|
x = x + adapter_x * ip_scale
|
|
|
|
return self.o(x)
|
|
|
|
class WanHuMoCrossAttention(WanSelfAttention):
|
|
|
|
def __init__(self, in_features, out_features, num_heads, kv_dim=None, qk_norm=True, eps=1e-6, attention_mode='sdpa', rms_norm_function="default"):
|
|
super().__init__(in_features, out_features, num_heads, qk_norm, eps, kv_dim=kv_dim, rms_norm_function=rms_norm_function)
|
|
self.attention_mode = attention_mode
|
|
|
|
def forward(self, x, context, grid_sizes, **kwargs):
|
|
|
|
b, n, d = x.size(0), self.num_heads, self.head_dim
|
|
q = self.norm_q(self.q(x).to(self.norm_q.weight.dtype).to(x.dtype)).view(b, -1, n, d)
|
|
k = self.norm_k(self.k(context).to(self.norm_k.weight.dtype).to(context.dtype)).view(b, -1, n, d)
|
|
v = self.v(context).view(b, -1, n, d)
|
|
|
|
# Handle video spatial structure
|
|
hlen_wlen = grid_sizes[0][1] * grid_sizes[0][2]
|
|
q = q.reshape(-1, hlen_wlen, n, d)
|
|
|
|
# Handle audio temporal structure (16 tokens per frame)
|
|
k = k.reshape(-1, 16, n, d)
|
|
v = v.reshape(-1, 16, n, d)
|
|
|
|
x_text = attention(q, k, v, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
x_text = x_text.view(b, -1, n, d).flatten(2)
|
|
|
|
x = x_text
|
|
|
|
return self.o(x)
|
|
|
|
class AudioCrossAttentionWrapper(nn.Module):
|
|
def __init__(self, in_features, out_features, num_heads, qk_norm=True, eps=1e-6, kv_dim=None):
|
|
super().__init__()
|
|
|
|
self.audio_cross_attn = WanHuMoCrossAttention(in_features, out_features, num_heads, kv_dim=kv_dim)
|
|
self.norm1_audio = WanLayerNorm(out_features, eps, elementwise_affine=True)
|
|
|
|
def forward(self, x, audio, grid_sizes, humo_audio_scale=1.0):
|
|
x = x.to(self.norm1_audio.weight.dtype)
|
|
x = x + self.audio_cross_attn(self.norm1_audio(x), audio, grid_sizes) * humo_audio_scale
|
|
return x
|
|
|
|
class MTVCrafterMotionAttention(WanSelfAttention):
|
|
|
|
def forward(self, x, mo, pe, grid_sizes, freqs):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L1, C]
|
|
mo: Motion tokens
|
|
pe: 4D RoPE
|
|
"""
|
|
b, n, d = x.size(0), self.num_heads, self.head_dim
|
|
|
|
# compute query, key, value
|
|
q = self.norm_q(self.q(x)).view(b, -1, n, d)
|
|
k = self.norm_k(self.k(mo)).view(b, n, -1, d)
|
|
v = self.v(mo).view(b, -1, n, d)
|
|
|
|
# compute attention
|
|
x = attention(
|
|
q=rope_apply(q, grid_sizes, freqs),
|
|
k=apply_rotary_emb(k, pe).transpose(1, 2),
|
|
v=v,
|
|
heads=self.num_heads,
|
|
)
|
|
|
|
return self.o(x.flatten(2))
|
|
|
|
|
|
WAN_CROSSATTENTION_CLASSES = {
|
|
't2v_cross_attn': WanT2VCrossAttention,
|
|
'i2v_cross_attn': WanI2VCrossAttention,
|
|
}
|
|
|
|
|
|
class WanAttentionBlock(nn.Module):
|
|
|
|
def __init__(self,
|
|
cross_attn_type, in_features, out_features, ffn_dim, ffn2_dim, num_heads,
|
|
qk_norm=True, cross_attn_norm=False, eps=1e-6, attention_mode="sdpa", rope_func="comfy", rms_norm_function="default",
|
|
use_motion_attn=False, use_humo_audio_attn=False, face_fuser_block=False, lynx_ip_layers=None, lynx_ref_layers=None,
|
|
block_idx=0, is_longcat=False):
|
|
super().__init__()
|
|
self.dim = out_features
|
|
self.ffn_dim = ffn_dim
|
|
self.num_heads = num_heads
|
|
self.head_dim = out_features // num_heads
|
|
self.qk_norm = qk_norm
|
|
self.cross_attn_norm = cross_attn_norm
|
|
self.eps = eps
|
|
self.attention_mode = attention_mode
|
|
self.rope_func = rope_func
|
|
#radial attn
|
|
self.dense_timesteps = 10
|
|
self.dense_block = False
|
|
self.dense_attention_mode = "sageattn"
|
|
self.block_idx = block_idx
|
|
|
|
self.kv_cache = None
|
|
self.use_motion_attn = use_motion_attn
|
|
self.has_face_fuser_block = face_fuser_block
|
|
self.ref_attn_k_img = None
|
|
self.ref_attn_v_img = None
|
|
|
|
# layers
|
|
self.norm1 = WanLayerNorm(self.dim, eps)
|
|
self.self_attn = WanSelfAttention(in_features, out_features, num_heads, qk_norm, eps, self.attention_mode, rms_norm_function=rms_norm_function,
|
|
head_norm=is_longcat)
|
|
|
|
# MTV Crafter motion attn
|
|
if self.use_motion_attn:
|
|
self.norm4 = WanLayerNorm(out_features, eps, elementwise_affine=True) if cross_attn_norm else nn.Identity()
|
|
self.motion_attn = MTVCrafterMotionAttention(in_features, out_features, num_heads, qk_norm, eps, self.attention_mode)
|
|
|
|
if cross_attn_type != "no_cross_attn":
|
|
self.norm3 = WanLayerNorm(out_features, eps, elementwise_affine=True) if cross_attn_norm else nn.Identity()
|
|
self.cross_attn = WAN_CROSSATTENTION_CLASSES[cross_attn_type](in_features, out_features, num_heads, qk_norm, eps, rms_norm_function=rms_norm_function,
|
|
head_norm=is_longcat)
|
|
self.norm2 = WanLayerNorm(self.dim, eps)
|
|
|
|
if not is_longcat:
|
|
self.ffn = nn.Sequential(nn.Linear(in_features, ffn_dim), nn.GELU(approximate='tanh'), nn.Linear(ffn2_dim, out_features))
|
|
else:
|
|
from ...LongCat.layers import FeedForwardSwiGLU
|
|
mlp_ratio = 4
|
|
self.ffn = FeedForwardSwiGLU(dim=self.dim, hidden_dim=int(self.dim * mlp_ratio))
|
|
|
|
# modulation
|
|
if not is_longcat:
|
|
self.modulation = nn.Parameter(torch.randn(1, 6, out_features) / in_features**0.5)
|
|
else:
|
|
adaln_tembed_dim = 512
|
|
self.modulation = nn.Sequential(nn.SiLU(), nn.Linear(adaln_tembed_dim, 6 * self.dim, bias=True))
|
|
|
|
self.seg_idx = None
|
|
|
|
# HuMo audio cross-attn
|
|
if use_humo_audio_attn:
|
|
self.audio_cross_attn_wrapper = AudioCrossAttentionWrapper(in_features, out_features, num_heads, qk_norm, eps, kv_dim=1536)
|
|
|
|
if face_fuser_block:
|
|
from .wananimate.face_blocks import FaceBlock
|
|
self.fuser_block = FaceBlock(self.dim, num_heads)
|
|
|
|
# Lynx
|
|
self.ref_adapter = None
|
|
if lynx_ref_layers == "full":
|
|
from ...lynx.modules import WanLynxRefAttention
|
|
self.self_attn.ref_adapter = WanLynxRefAttention(dim=self.dim)
|
|
if lynx_ip_layers == "full":
|
|
from ...lynx.modules import WanLynxIPCrossAttention
|
|
self.cross_attn.ip_adapter = WanLynxIPCrossAttention(cross_attention_dim=self.dim, dim=self.dim, n_registers=16)
|
|
elif lynx_ip_layers == "lite":
|
|
from ...lynx.modules import WanLynxIPCrossAttention
|
|
if self.block_idx % 2 == 0:
|
|
self.cross_attn.ip_adapter = WanLynxIPCrossAttention(cross_attention_dim=2048, dim=self.dim, n_registers=0, bias=False)
|
|
|
|
def get_mod(self, e, modulation):
|
|
if e.dim() == 3:
|
|
if e.shape[-1] == 512:
|
|
e = self.modulation(e)
|
|
return e.unsqueeze(2).chunk(6, dim=-1)
|
|
return (modulation + e).chunk(6, dim=1) # 1, 6, dim
|
|
elif e.dim() == 4:
|
|
e_mod = modulation.unsqueeze(2) + e
|
|
return [ei.squeeze(1) for ei in e_mod.unbind(dim=1)]
|
|
|
|
|
|
def modulate(self, norm_x, shift_msa, scale_msa, seg_idx=None):
|
|
"""
|
|
Modulate x with shift and scale. If seg_idx is provided, apply segmented modulation.
|
|
"""
|
|
if seg_idx is not None:
|
|
parts = []
|
|
for i in range(2):
|
|
part = torch.addcmul(
|
|
shift_msa[:, i:i + 1],
|
|
norm_x[:, seg_idx[i]:seg_idx[i + 1]],
|
|
1 + scale_msa[:, i:i + 1]
|
|
)
|
|
parts.append(part)
|
|
norm_x = torch.cat(parts, dim=1)
|
|
return norm_x
|
|
else:
|
|
return torch.addcmul(shift_msa, norm_x, 1 + scale_msa)
|
|
|
|
def ffn_chunked(self, mod_x, num_chunks=4):
|
|
seq_len = mod_x.shape[1]
|
|
if seq_len <= 8192 or num_chunks <= 1:
|
|
return self.ffn(mod_x)
|
|
return torch.cat([self.ffn(chunk.contiguous()) for chunk in mod_x.chunk(num_chunks, dim=1)], dim=1)
|
|
|
|
#region attention forward
|
|
def forward(
|
|
self, x, e, seq_lens, grid_sizes, freqs, context, current_step,
|
|
last_step=False,
|
|
clip_embed=None,
|
|
seq_chunks=0, #comfy chunked cross-attn
|
|
chunked_self_attention=False,
|
|
camera_embed=None, #ReCamMaster
|
|
audio_proj=None, audio_scale=1.0, #fantasytalking
|
|
num_latent_frames=21,
|
|
original_seq_len=None,
|
|
enhance_enabled=False, #feta
|
|
nag_params={}, nag_context=None, #normalized attention guidance
|
|
multitalk_audio_embedding=None, ref_target_masks=None, human_num=0, #multitalk
|
|
inner_t=None, inner_c=None, cross_freqs=None, #echoshot
|
|
x_ip=None, e_ip=None, freqs_ip=None, ip_scale=1.0, #stand-in
|
|
adapter_proj=None, #fantasyportrait
|
|
reverse_time=False,
|
|
zero_timestep=False, #s2v zero timestep
|
|
mtv_motion_tokens=None, mtv_motion_rotary_emb=None, mtv_strength=1.0, mtv_freqs=None, #mtv crafter
|
|
humo_audio_input=None, humo_audio_scale=1.0, #humo audio
|
|
lynx_x_ip=None, lynx_ref_feature=None, lynx_ip_scale=1.0, lynx_ref_scale=1.0, #lynx
|
|
x_ovi=None, e_ovi=None, freqs_ovi=None, context_ovi=None, seq_lens_ovi=None, grid_sizes_ovi=None,
|
|
num_cond_latents=None, #longcat image cond amount
|
|
x_onetoall_ref=None, onetoall_freqs=None, onetoall_ref=None, onetoall_ref_scale=1.0, #one-to-all
|
|
e_tr=None, tr_num=0, tr_start=0, #token replacement
|
|
):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L, C]
|
|
e(Tensor): Shape [B, 6, C]
|
|
seq_lens(Tensor): Shape [B], length of each sequence in batch
|
|
grid_sizes(Tensor): Shape [B, 3], the second dimension contains (F, H, W)
|
|
freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2]
|
|
"""
|
|
zero_timestep = len(e) == 2
|
|
if zero_timestep: #s2v zero timestep
|
|
self.seg_idx = e[1]
|
|
self.seg_idx = min(max(0, self.seg_idx), x.size(1))
|
|
self.seg_idx = [0, self.seg_idx, x.size(1)]
|
|
e = e[0]
|
|
|
|
use_token_replace = False
|
|
if e_tr is not None and tr_num > 0:
|
|
tr_shift_msa, tr_scale_msa, tr_gate_msa, tr_shift_mlp, tr_scale_mlp, tr_gate_mlp = self.get_mod(e_tr.to(x.device), self.modulation)
|
|
use_token_replace = True
|
|
tr_start = tr_start or 0
|
|
tr_end = tr_start + (tr_num or 0)
|
|
|
|
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.get_mod(e.to(x.device), self.modulation)
|
|
del e
|
|
input_dtype = x.dtype
|
|
B, N, C = x.shape
|
|
T = num_latent_frames
|
|
is_longcat = C == 4096
|
|
if is_longcat:
|
|
input_x = self.modulate(self.norm1(x.view(B, T, -1, C).to(shift_msa.dtype)), shift_msa, scale_msa, seg_idx=self.seg_idx).to(input_dtype).view(B, N, C)
|
|
elif use_token_replace:
|
|
norm_x = self.norm1(x.to(shift_msa.dtype))
|
|
input_x = torch.cat([
|
|
torch.addcmul(shift_msa, norm_x[:, :tr_start], 1 + scale_msa), # before replace → T
|
|
torch.addcmul(tr_shift_msa, norm_x[:, tr_start:tr_end], 1 + tr_scale_msa), # replace segment → t=0
|
|
torch.addcmul(shift_msa, norm_x[:, tr_end:], 1 + scale_msa) # after replace → T
|
|
], dim=1).to(input_dtype)
|
|
else:
|
|
input_x = self.modulate(self.norm1(x.to(shift_msa.dtype)), shift_msa, scale_msa, seg_idx=self.seg_idx).to(input_dtype)
|
|
|
|
del shift_msa, scale_msa
|
|
|
|
if x_ip is not None:
|
|
shift_msa_ip, scale_msa_ip, gate_msa_ip, shift_mlp_ip, scale_mlp_ip, gate_mlp_ip = self.get_mod(e_ip.to(x.device), self.modulation)
|
|
input_x_ip = self.modulate(self.norm1(x_ip), shift_msa_ip, scale_msa_ip)
|
|
self.cond_size = input_x_ip.shape[1]
|
|
input_x = torch.concat([input_x, input_x_ip], dim=1)
|
|
self.kv_cache = None
|
|
|
|
if x_ovi is not None:
|
|
shift_msa_ovi, scale_msa_ovi, gate_msa_ovi, shift_mlp_ovi, scale_mlp_ovi, gate_mlp_ovi = self.get_mod(e_ovi.to(x.device), self.audio_block.modulation)
|
|
input_x_ovi = self.modulate(self.audio_block.norm1(x_ovi), shift_msa_ovi, scale_msa_ovi)
|
|
|
|
if camera_embed is not None:
|
|
# encode ReCamMaster camera
|
|
camera_embed = self.cam_encoder(camera_embed.to(x))
|
|
camera_embed = camera_embed.repeat(1, 2, 1)
|
|
camera_embed = camera_embed.unsqueeze(2).unsqueeze(3).repeat(1, 1, grid_sizes[0][1], grid_sizes[0][2], 1)
|
|
camera_embed = rearrange(camera_embed, 'b f h w d -> b (f h w) d')
|
|
input_x += camera_embed
|
|
|
|
# self-attention
|
|
x_ref_attn_map = None
|
|
|
|
# self-attention variables
|
|
q_ip = k_ip = v_ip = None
|
|
|
|
if lynx_ref_feature is None and self.self_attn.ref_adapter is not None:
|
|
lynx_ref_feature = input_x
|
|
|
|
onetoall_ref = None
|
|
if x_onetoall_ref is not None:
|
|
b, s, n, d = *x_onetoall_ref.shape[:2], self.self_attn.num_heads, self.self_attn.head_dim
|
|
h_dim = w_dim = 2 * (self.head_dim // 6)
|
|
t_dim = self.head_dim - h_dim - w_dim
|
|
|
|
q_ref = self.self_attn.norm_q(self.self_attn.q(input_x)).to(input_x.dtype).view(b, N, n, d)
|
|
q_ref = apply_rotary_emb_split(q_ref, freqs, t_dim) # Apply split rotary embedding (only to H/W dimensions, leaving T unchanged)
|
|
|
|
k_ref = self.ref_attn_norm_k_img(self.ref_attn_k_img(x_onetoall_ref).to(self.ref_attn_norm_k_img.weight.dtype)).to(x_onetoall_ref.dtype).view(b, s, n, d)
|
|
k_ref = apply_rotary_emb_split(k_ref, onetoall_freqs, t_dim)
|
|
|
|
v_ref = self.ref_attn_v_img(x_onetoall_ref).view(b, s, n, d)
|
|
|
|
onetoall_ref = attention(q_ref, k_ref, v_ref, k_lens=seq_lens, attention_mode=self.attention_mode, heads=self.num_heads)
|
|
del q_ref, k_ref, v_ref
|
|
|
|
#RoPE and QKV computation
|
|
if inner_t is not None:
|
|
#query, key, value
|
|
q, k, v = self.self_attn.qkv_fn(input_x)
|
|
q=rope_apply_echoshot(q, grid_sizes, freqs, inner_t).to(q)
|
|
k=rope_apply_echoshot(k, grid_sizes, freqs, inner_t).to(k)
|
|
elif x_ip is not None and self.kv_cache is None:
|
|
# First pass - separate main and IP components
|
|
x_main, x_ip_input = input_x[:, : -self.cond_size], input_x[:, -self.cond_size :]
|
|
# Compute QKV for main content
|
|
if self.rope_func == "comfy":
|
|
q = self.self_attn.qkv_fn_q_with_rope(x_main, freqs)
|
|
k = self.self_attn.qkv_fn_k_with_rope(x_main, freqs)
|
|
v = self.self_attn.qkv_fn_v(x_main)
|
|
elif self.rope_func == "comfy_chunked":
|
|
q = self.self_attn.qkv_fn_q_with_rope(x_main, freqs, num_chunks=2)
|
|
k = self.self_attn.qkv_fn_k_with_rope(x_main, freqs, num_chunks=2)
|
|
v = self.self_attn.qkv_fn_v(x_main)
|
|
# Compute QKV for IP content
|
|
if "comfy" in self.rope_func:
|
|
q_ip, k_ip, v_ip = self.self_attn.qkv_fn_ip(x_ip_input)
|
|
q_ip, k_ip = apply_rope_comfy(q_ip, k_ip, freqs_ip)
|
|
else:
|
|
if "comfy" in self.rope_func:
|
|
num_chunks = 2 if self.rope_func == "comfy_chunked" else 1
|
|
q = self.self_attn.qkv_fn_q_with_rope(input_x, freqs, num_chunks=num_chunks, is_longcat=is_longcat)
|
|
k = self.self_attn.qkv_fn_k_with_rope(input_x, freqs, num_chunks=num_chunks, is_longcat=is_longcat)
|
|
v = self.self_attn.qkv_fn_v(input_x)
|
|
else:
|
|
q, k, v = self.self_attn.qkv_fn(input_x)
|
|
if self.rope_func == "mocha":
|
|
from ...mocha.nodes import rope_apply_mocha
|
|
q = rope_apply_mocha(q, grid_sizes, freqs)
|
|
k = rope_apply_mocha(k, grid_sizes, freqs)
|
|
else:
|
|
q = rope_apply(q, grid_sizes, freqs, reverse_time=reverse_time)
|
|
k = rope_apply(k, grid_sizes, freqs, reverse_time=reverse_time)
|
|
|
|
del input_x
|
|
|
|
if x_ovi is not None:
|
|
q_ovi, k_ovi, v_ovi = self.audio_block.self_attn.qkv_fn(input_x_ovi)
|
|
q_ovi = rope_apply(q_ovi, grid_sizes_ovi, freqs_ovi)
|
|
k_ovi = rope_apply(k_ovi, grid_sizes_ovi, freqs_ovi)
|
|
y_ovi = self.audio_block.self_attn.forward(q_ovi, k_ovi, v_ovi, seq_lens_ovi)
|
|
x_ovi = x_ovi.addcmul(y_ovi, gate_msa_ovi)
|
|
del input_x_ovi, y_ovi, gate_msa_ovi
|
|
|
|
# FETA
|
|
if enhance_enabled:
|
|
feta_scores = get_feta_scores(q, k)
|
|
|
|
#self-attention
|
|
split_attn = (context is not None
|
|
and (context.shape[0] > 1 or (clip_embed is not None and clip_embed.shape[0] > 1))
|
|
and x.shape[0] == 1
|
|
and inner_t is None
|
|
and x_ip is None # Don't split when using IP-Adapter
|
|
)
|
|
if split_attn and chunked_self_attention:
|
|
y = self.self_attn.forward_split(q, k, v, seq_lens, grid_sizes, seq_chunks)
|
|
elif ref_target_masks is not None: #multi/infinite talk
|
|
y, x_ref_attn_map = self.self_attn.forward_multitalk(q, k, v, seq_lens, grid_sizes, ref_target_masks)
|
|
elif self.attention_mode == "radial_sage_attention":
|
|
if self.dense_block or self.dense_timesteps is not None and current_step < self.dense_timesteps:
|
|
if self.dense_attention_mode == "sparse_sage_attn":
|
|
y = self.self_attn.forward_radial(q, k, v, dense_step=True)
|
|
else:
|
|
y = self.self_attn.forward(q, k, v, seq_lens)
|
|
else:
|
|
y = self.self_attn.forward_radial(q, k, v, dense_step=False)
|
|
elif self.attention_mode == "sageattn_3":
|
|
if current_step != 0 and not last_step:
|
|
y = self.self_attn.forward(q, k, v, seq_lens, attention_mode_override="sageattn_3")
|
|
else:
|
|
y = self.self_attn.forward(q, k, v, seq_lens, attention_mode_override="sageattn")
|
|
elif x_ip is not None and self.kv_cache is None: #stand-in
|
|
# First pass: cache IP keys/values and compute attention
|
|
self.kv_cache = {"k_ip": k_ip.detach(), "v_ip": v_ip.detach()}
|
|
y = self.self_attn.forward_ip(q, k, v, q_ip, k_ip, v_ip, seq_lens)
|
|
elif self.kv_cache is not None:
|
|
# Subsequent passes: use cached IP keys/values
|
|
k_ip = self.kv_cache["k_ip"]
|
|
v_ip = self.kv_cache["v_ip"]
|
|
full_k = torch.cat([k, k_ip], dim=1)
|
|
full_v = torch.cat([v, v_ip], dim=1)
|
|
y = self.self_attn.forward(q, full_k, full_v, seq_lens)
|
|
elif is_longcat and num_cond_latents is not None and num_cond_latents > 0:
|
|
num_cond_latents_thw = num_cond_latents * (N // num_latent_frames)
|
|
# process the condition tokens
|
|
x_cond = self.self_attn.forward(
|
|
q[:, :num_cond_latents_thw].contiguous(),
|
|
k[:, :num_cond_latents_thw].contiguous(),
|
|
v[:, :num_cond_latents_thw].contiguous(),
|
|
seq_lens)
|
|
# process the noise tokens
|
|
x_noise = self.self_attn.forward(q[:, num_cond_latents_thw:].contiguous(), k, v, seq_lens)
|
|
# merge x_cond and x_noise
|
|
y = torch.cat([x_cond, x_noise], dim=1).contiguous()
|
|
else:
|
|
y = self.self_attn.forward(q, k, v, seq_lens, lynx_ref_feature=lynx_ref_feature, lynx_ref_scale=lynx_ref_scale, onetoall_ref=onetoall_ref, onetoall_ref_scale=onetoall_ref_scale)
|
|
|
|
del q, k, v,
|
|
|
|
# FETA
|
|
if enhance_enabled:
|
|
y.mul_(feta_scores)
|
|
|
|
# ReCamMaster
|
|
if camera_embed is not None:
|
|
y = self.projector(y)
|
|
|
|
# Stand-in
|
|
if x_ip is not None:
|
|
y, y_ip = (
|
|
y[:, : -self.cond_size],
|
|
y[:, -self.cond_size :],
|
|
)
|
|
|
|
# S2V
|
|
if zero_timestep:
|
|
z = []
|
|
for i in range(2):
|
|
z.append(y[:, self.seg_idx[i]:self.seg_idx[i + 1]] * gate_msa[:, i:i + 1])
|
|
y = torch.cat(z, dim=1)
|
|
x = x.add(y)
|
|
else:
|
|
if is_longcat:
|
|
x = x + (y.view(B, -1, N//T, C).float() * gate_msa).to(input_dtype).view(B, -1, C)
|
|
elif use_token_replace:
|
|
x = x + torch.cat([
|
|
y[:, :tr_start] * gate_msa,
|
|
y[:, tr_start:tr_end] * tr_gate_msa,
|
|
y[:, tr_end:] * gate_msa
|
|
], dim=1).to(input_dtype)
|
|
else:
|
|
x = x.addcmul(y, gate_msa)
|
|
del y, gate_msa
|
|
|
|
# cross-attention & ffn function
|
|
if context is not None:
|
|
if x_ovi is not None:
|
|
#audio
|
|
og_ovi_x = x_ovi
|
|
x_ovi = x_ovi + self.audio_block.cross_attn(self.audio_block.norm3(x_ovi), context_ovi, grid_sizes_ovi,
|
|
src_freqs=freqs_ovi,
|
|
target_seq=x,
|
|
target_seq_lens=seq_lens,
|
|
target_grid_sizes=grid_sizes,
|
|
target_freqs=freqs)
|
|
y = self.audio_block.ffn(torch.addcmul(shift_mlp_ovi, self.audio_block.norm2(x_ovi), 1 + scale_mlp_ovi))
|
|
x_ovi = x_ovi.addcmul(y, gate_mlp_ovi)
|
|
|
|
# video
|
|
x = x + self.cross_attn(self.norm3(x), context, grid_sizes,
|
|
src_freqs=freqs,
|
|
target_seq=og_ovi_x,
|
|
target_seq_lens=seq_lens_ovi,
|
|
target_grid_sizes=grid_sizes_ovi,
|
|
target_freqs=freqs_ovi)
|
|
elif split_attn:
|
|
if nag_context is not None:
|
|
raise NotImplementedError("nag_context is not supported in split_cross_attn_ffn")
|
|
x = self.split_cross_attn_ffn(x, context, shift_mlp, scale_mlp, gate_mlp, clip_embed, grid_sizes)
|
|
return x, x_ip, lynx_ref_feature, x_ovi
|
|
else:
|
|
x = x + self.cross_attn(self.norm3(x.to(self.norm3.weight.dtype)).to(input_dtype), context, grid_sizes, clip_embed=clip_embed, audio_proj=audio_proj, audio_scale=audio_scale,
|
|
num_latent_frames=num_latent_frames, nag_params=nag_params, nag_context=nag_context,
|
|
rope_func=self.rope_func, inner_t=inner_t, inner_c=inner_c, cross_freqs=cross_freqs,
|
|
adapter_proj=adapter_proj, ip_scale=ip_scale, orig_seq_len=original_seq_len, lynx_x_ip=lynx_x_ip, lynx_ip_scale=lynx_ip_scale, num_cond_latents=num_cond_latents)
|
|
x = x.to(input_dtype)
|
|
# MultiTalk
|
|
if multitalk_audio_embedding is not None and not isinstance(self, VaceWanAttentionBlock):
|
|
x_audio = self.audio_cross_attn(self.norm_x(x.to(self.norm_x.weight.dtype)).to(input_dtype), encoder_hidden_states=multitalk_audio_embedding,
|
|
shape=grid_sizes[0], x_ref_attn_map=x_ref_attn_map, human_num=human_num)
|
|
x = x.add(x_audio, alpha=audio_scale)
|
|
|
|
# MTV-Crafter Motion Attention
|
|
if self.use_motion_attn and mtv_motion_tokens is not None and mtv_motion_rotary_emb is not None:
|
|
x_motion = self.motion_attn(self.norm4(x), mtv_motion_tokens, mtv_motion_rotary_emb, grid_sizes, mtv_freqs)
|
|
x = x.add(x_motion, alpha=mtv_strength)
|
|
|
|
# HuMo Audio Cross-Attention
|
|
if humo_audio_input is not None:
|
|
x = self.audio_cross_attn_wrapper(x, humo_audio_input, grid_sizes, humo_audio_scale)
|
|
|
|
|
|
# ffn
|
|
if self.rope_func == "comfy_chunked":
|
|
mod_x = torch.addcmul(shift_mlp, self.norm2(x.to(shift_mlp.dtype)), 1 + scale_mlp)
|
|
x_ffn = self.ffn_chunked(mod_x)
|
|
else:
|
|
if zero_timestep:
|
|
norm2_x = self.norm2(x)
|
|
parts = []
|
|
for i in range(2):
|
|
parts.append(norm2_x[:, self.seg_idx[i]:self.seg_idx[i + 1]] *
|
|
(1 + scale_mlp[:, i:i + 1]) + shift_mlp[:, i:i + 1])
|
|
norm2_x = torch.cat(parts, dim=1)
|
|
x_ffn = self.ffn(norm2_x)
|
|
else:
|
|
if is_longcat:
|
|
mod_x = torch.addcmul(shift_mlp, self.norm2(x.view(B, -1, N//T, C).float()), 1 + scale_mlp).view(B, -1, C)
|
|
elif use_token_replace:
|
|
norm2_x = self.norm2(x.to(shift_mlp.dtype))
|
|
mod_x = torch.cat([
|
|
torch.addcmul(shift_mlp, norm2_x[:, :tr_start], 1 + scale_mlp),
|
|
torch.addcmul(tr_shift_mlp, norm2_x[:, tr_start:tr_end], 1 + tr_scale_mlp),
|
|
torch.addcmul(shift_mlp, norm2_x[:, tr_end:], 1 + scale_mlp)
|
|
], dim=1)
|
|
else:
|
|
mod_x = torch.addcmul(shift_mlp, self.norm2(x.to(shift_mlp.dtype)), 1 + scale_mlp)
|
|
|
|
del shift_mlp, scale_mlp
|
|
x_ffn = self.ffn_chunked(mod_x.to(input_dtype), num_chunks=1)
|
|
del mod_x
|
|
|
|
# gate_mlp
|
|
if zero_timestep:
|
|
z = []
|
|
for i in range(2):
|
|
z.append(x_ffn[:, self.seg_idx[i]:self.seg_idx[i + 1]] * gate_mlp[:, i:i + 1])
|
|
x_ffn = torch.cat(z, dim=1)
|
|
x = x.add(x_ffn)
|
|
else:
|
|
if is_longcat:
|
|
x = x + (gate_mlp * x_ffn.view(B, -1, N//T, C).float()).to(input_dtype).view(B, -1, C)
|
|
elif use_token_replace:
|
|
x = x + torch.cat([
|
|
x_ffn[:, :tr_start] * gate_mlp,
|
|
x_ffn[:, tr_start:tr_end] * tr_gate_mlp,
|
|
x_ffn[:, tr_end:] * gate_mlp
|
|
], dim=1).to(input_dtype)
|
|
else:
|
|
x = x.addcmul(x_ffn.to(gate_mlp.dtype), gate_mlp).to(input_dtype)
|
|
del gate_mlp
|
|
|
|
if x_ip is not None: #stand-in
|
|
x_ip = x_ip.addcmul(y_ip, gate_msa_ip)
|
|
y_ip = self.ffn(torch.addcmul(shift_mlp_ip, self.norm2(x_ip), 1 + scale_mlp_ip))
|
|
x_ip = x_ip.addcmul(y_ip, gate_mlp_ip)
|
|
return x, x_ip, lynx_ref_feature, x_ovi
|
|
|
|
|
|
def split_cross_attn_ffn(self, x, context, shift_mlp, scale_mlp, gate_mlp, clip_embed=None, grid_sizes=None):
|
|
# Get number of prompts
|
|
num_prompts = context.shape[0]
|
|
num_clip_embeds = 0 if clip_embed is None else clip_embed.shape[0]
|
|
num_segments = max(num_prompts, num_clip_embeds)
|
|
|
|
# Extract spatial dimensions
|
|
frames, height, width = grid_sizes[0] # Assuming batch size 1
|
|
tokens_per_frame = height * width
|
|
|
|
# Distribute frames across prompts
|
|
frames_per_segment = max(1, frames // num_segments)
|
|
|
|
# Process each prompt segment
|
|
x_combined = torch.zeros_like(x)
|
|
|
|
for i in range(num_segments):
|
|
# Calculate frame boundaries for this segment
|
|
start_frame = i * frames_per_segment
|
|
end_frame = min((i+1) * frames_per_segment, frames) if i < num_segments-1 else frames
|
|
|
|
# Convert frame indices to token indices
|
|
start_idx = start_frame * tokens_per_frame
|
|
end_idx = end_frame * tokens_per_frame
|
|
segment_indices = torch.arange(start_idx, end_idx, device=x.device, dtype=torch.long)
|
|
|
|
# Get prompt segment (cycle through available prompts if needed)
|
|
prompt_idx = i % num_prompts
|
|
segment_context = context[prompt_idx:prompt_idx+1]
|
|
|
|
# Handle clip_embed for this segment (cycle through available embeddings)
|
|
segment_clip_embed = None
|
|
if clip_embed is not None:
|
|
clip_idx = i % num_clip_embeds
|
|
segment_clip_embed = clip_embed[clip_idx:clip_idx+1]
|
|
|
|
# Get tensor segment
|
|
x_segment = x[:, segment_indices, :].to(self.norm3.weight.dtype)
|
|
|
|
# Process segment with its prompt and clip embedding
|
|
processed_segment = self.cross_attn(self.norm3(x_segment), segment_context, clip_embed=segment_clip_embed)
|
|
processed_segment = processed_segment.to(x.dtype)
|
|
|
|
# Add to combined result
|
|
x_combined[:, segment_indices, :] = processed_segment
|
|
|
|
# Continue with FFN
|
|
x = x + x_combined
|
|
mod_x = torch.addcmul(shift_mlp, self.norm2(x.to(shift_mlp.dtype)), 1 + scale_mlp)
|
|
y = self.ffn_chunked(mod_x, num_chunks=1)
|
|
return x.addcmul(y, gate_mlp)
|
|
|
|
class VaceWanAttentionBlock(WanAttentionBlock):
|
|
def __init__(
|
|
self,
|
|
cross_attn_type,
|
|
in_features,
|
|
out_features,
|
|
ffn_dim,
|
|
ffn2_dim,
|
|
num_heads,
|
|
qk_norm=True,
|
|
cross_attn_norm=False,
|
|
eps=1e-6,
|
|
block_id=0,
|
|
attention_mode='sdpa',
|
|
rope_func="comfy",
|
|
rms_norm_function="default"
|
|
):
|
|
super().__init__(cross_attn_type, in_features, out_features, ffn_dim, ffn2_dim, num_heads, qk_norm, cross_attn_norm, eps, attention_mode, rope_func, rms_norm_function=rms_norm_function)
|
|
|
|
self.register_buffer('block_id', torch.tensor(block_id, dtype=torch.long))
|
|
|
|
if torch.equal(self.block_id, torch.tensor(0)):
|
|
self.before_proj = nn.Linear(in_features, out_features)
|
|
self.after_proj = nn.Linear(in_features, out_features)
|
|
|
|
def forward(self, c, **kwargs):
|
|
return super().forward(c, **kwargs)
|
|
|
|
class BaseWanAttentionBlock(WanAttentionBlock):
|
|
def __init__(
|
|
self,
|
|
cross_attn_type,
|
|
in_features,
|
|
out_features,
|
|
ffn_dim,
|
|
ffn2_dim,
|
|
num_heads,
|
|
qk_norm=True,
|
|
cross_attn_norm=False,
|
|
eps=1e-6,
|
|
block_id=None,
|
|
block_idx=0,
|
|
attention_mode='sdpa',
|
|
rope_func="comfy",
|
|
rms_norm_function="default",
|
|
lynx_ip_layers=None,
|
|
lynx_ref_layers=None,
|
|
):
|
|
super().__init__(cross_attn_type, in_features, out_features, ffn_dim, ffn2_dim, num_heads, qk_norm,
|
|
cross_attn_norm, eps, attention_mode, rope_func, rms_norm_function=rms_norm_function,
|
|
block_idx=block_idx, lynx_ip_layers=lynx_ip_layers, lynx_ref_layers=lynx_ref_layers)
|
|
if block_id is not None:
|
|
self.register_buffer('block_id', torch.tensor(block_id, dtype=torch.long))
|
|
else:
|
|
self.block_id = None
|
|
|
|
def forward(self, x, vace_hints=None, vace_context_scale=[1.0], **kwargs):
|
|
x, x_ip, lynx_ref_feature, x_ovi = super().forward(x, **kwargs)
|
|
if vace_hints is None:
|
|
return x, x_ip, lynx_ref_feature, x_ovi
|
|
|
|
if self.block_id is not None:
|
|
for i in range(len(vace_hints)):
|
|
x.add_(vace_hints[i][self.block_id].to(x.device), alpha=vace_context_scale[i])
|
|
return x, x_ip, lynx_ref_feature, x_ovi
|
|
|
|
class Head(nn.Module):
|
|
|
|
def __init__(self, dim, out_dim, patch_size, eps=1e-6):
|
|
super().__init__()
|
|
self.dim = dim
|
|
self.out_dim = out_dim
|
|
self.patch_size = patch_size
|
|
self.eps = eps
|
|
|
|
# layers
|
|
out_dim = math.prod(patch_size) * out_dim
|
|
self.norm = WanLayerNorm(dim, eps)
|
|
self.head = nn.Linear(dim, out_dim)
|
|
|
|
# modulation
|
|
self.modulation = nn.Parameter(torch.randn(1, 2, dim) / dim**0.5)
|
|
|
|
def get_mod(self, e):
|
|
if e.dim() == 2:
|
|
return (self.modulation + e.unsqueeze(1)).chunk(2, dim=1)
|
|
elif e.dim() == 3:
|
|
e = (self.modulation.unsqueeze(2) + e.unsqueeze(1)).chunk(2, dim=1)
|
|
return [ei.squeeze(1) for ei in e]
|
|
|
|
def forward(self, x, e, e_tr=None, tr_start=0, tr_num=0, **kwargs):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L1, C]
|
|
e(Tensor): Shape [B, C]
|
|
"""
|
|
e = self.get_mod(e.to(x.device))
|
|
if tr_num > 0 and e_tr is not None:
|
|
e_tr = self.get_mod(e_tr.to(x.device))
|
|
tr_end = tr_start + tr_num
|
|
norm_x = self.norm(x.float()).to(x.dtype)
|
|
x = self.head(torch.cat([
|
|
norm_x[:, :tr_start].mul(1 + e[1]).add(e[0]),
|
|
norm_x[:, tr_start:tr_end].mul(1 + e_tr[1]).add(e_tr[0]),
|
|
norm_x[:, tr_end:].mul(1 + e[1]).add(e[0])
|
|
], dim=1))
|
|
else:
|
|
x = self.head(self.norm(x.float()).to(x.dtype).mul_(1 + e[1]).add_(e[0]))
|
|
return x
|
|
|
|
class Head_adaLN(nn.Module):
|
|
|
|
def __init__(self, dim, out_dim, patch_size, eps=1e-6, adaln_tembed_dim=512):
|
|
super().__init__()
|
|
self.dim = dim
|
|
self.out_dim = out_dim
|
|
self.patch_size = patch_size
|
|
self.eps = eps
|
|
self.adaln_tembed_dim = adaln_tembed_dim
|
|
|
|
# layers
|
|
out_dim = math.prod(patch_size) * out_dim
|
|
self.norm = WanLayerNorm(dim, eps)
|
|
self.head = nn.Linear(dim, out_dim)
|
|
|
|
# modulation
|
|
self.modulation = nn.Sequential(nn.SiLU(), nn.Linear(adaln_tembed_dim, 2 * self.dim, bias=True))
|
|
|
|
def forward(self, x, e, temp_length, **kwargs):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L1, C]
|
|
e(Tensor): Shape [B, C]
|
|
"""
|
|
B, N, C = x.shape
|
|
T = temp_length
|
|
self.modulation.to(torch.float32)
|
|
shift, scale = self.modulation(e).unsqueeze(2).chunk(2, dim=-1) # [B, T, 1, C]
|
|
return self.head(self.norm(x.view(B, T, -1, C).float()).mul_(1 + scale).add_(shift).view(B, N, C).to(x.dtype))
|
|
|
|
|
|
|
|
class MLPProj(torch.nn.Module):
|
|
|
|
def __init__(self, in_dim, out_dim, fl_pos_emb=False):
|
|
super().__init__()
|
|
|
|
self.proj = torch.nn.Sequential(
|
|
torch.nn.LayerNorm(in_dim), torch.nn.Linear(in_dim, in_dim),
|
|
torch.nn.GELU(), torch.nn.Linear(in_dim, out_dim),
|
|
torch.nn.LayerNorm(out_dim))
|
|
if fl_pos_emb: # NOTE: we only use this for `fl2v`
|
|
self.emb_pos = nn.Parameter(torch.zeros(1, 257 * 2, 1280))
|
|
|
|
def forward(self, image_embeds):
|
|
if hasattr(self, 'emb_pos'):
|
|
image_embeds = image_embeds + self.emb_pos.to(image_embeds.device)
|
|
clip_extra_context_tokens = self.proj(image_embeds)
|
|
return clip_extra_context_tokens
|
|
|
|
from .s2v.auxi_blocks import MotionEncoder_tc
|
|
|
|
|
|
class CausalAudioEncoder(nn.Module):
|
|
|
|
def __init__(self,
|
|
dim=5120,
|
|
num_layers=25,
|
|
out_dim=2048,
|
|
video_rate=8,
|
|
num_token=4,
|
|
need_global=False):
|
|
super().__init__()
|
|
self.encoder = MotionEncoder_tc(
|
|
in_dim=dim,
|
|
hidden_dim=out_dim,
|
|
num_heads=num_token,
|
|
need_global=need_global)
|
|
weight = torch.ones((1, num_layers, 1, 1)) * 0.01
|
|
|
|
self.weights = torch.nn.Parameter(weight)
|
|
self.act = torch.nn.SiLU()
|
|
|
|
def forward(self, features):
|
|
# features B * num_layers * dim * video_length
|
|
weights = self.act(self.weights)
|
|
weights_sum = weights.sum(dim=1, keepdims=True)
|
|
weighted_feat = ((features * weights) / weights_sum).sum(
|
|
dim=1) # b dim f
|
|
weighted_feat = weighted_feat.permute(0, 2, 1) # b f dim
|
|
res = self.encoder(weighted_feat) # b f n dim
|
|
|
|
return res # b f n dim
|
|
|
|
|
|
class AudioCrossAttention(WanT2VCrossAttention):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
class AudioInjector_WAN(nn.Module):
|
|
|
|
def __init__(self,
|
|
all_modules,
|
|
all_modules_names,
|
|
dim=2048,
|
|
num_heads=32,
|
|
inject_layer=[0, 27],
|
|
root_net=None,
|
|
enable_adain=False,
|
|
adain_dim=2048,
|
|
need_adain_ont=False,
|
|
attention_mode='sdpa'):
|
|
super().__init__()
|
|
self.injected_block_id = {}
|
|
audio_injector_id = 0
|
|
for mod_name, mod in zip(all_modules_names, all_modules):
|
|
if isinstance(mod, WanAttentionBlock):
|
|
for inject_id in inject_layer:
|
|
if f'transformer_blocks.{inject_id}' in mod_name:
|
|
self.injected_block_id[inject_id] = audio_injector_id
|
|
audio_injector_id += 1
|
|
|
|
self.injector = nn.ModuleList([
|
|
AudioCrossAttention(
|
|
in_features=dim,
|
|
out_features=dim,
|
|
num_heads=num_heads,
|
|
qk_norm=True,
|
|
attention_mode=attention_mode
|
|
) for _ in range(audio_injector_id)
|
|
])
|
|
self.injector_pre_norm_feat = nn.ModuleList([
|
|
nn.LayerNorm(
|
|
dim,
|
|
elementwise_affine=False,
|
|
eps=1e-6,
|
|
) for _ in range(audio_injector_id)
|
|
])
|
|
self.injector_pre_norm_vec = nn.ModuleList([
|
|
nn.LayerNorm(
|
|
dim,
|
|
elementwise_affine=False,
|
|
eps=1e-6,
|
|
) for _ in range(audio_injector_id)
|
|
])
|
|
if enable_adain:
|
|
self.injector_adain_layers = nn.ModuleList([
|
|
AdaLayerNorm(
|
|
output_dim=dim * 2, embedding_dim=adain_dim)
|
|
for _ in range(audio_injector_id)
|
|
])
|
|
if need_adain_ont:
|
|
self.injector_adain_output_layers = nn.ModuleList(
|
|
[nn.Linear(dim, dim) for _ in range(audio_injector_id)])
|
|
|
|
class WanModel(torch.nn.Module):
|
|
def __init__(self,
|
|
model_type='t2v',
|
|
patch_size=(1, 2, 2), text_len=512,
|
|
in_dim=16, dim=2048, in_features=5120, out_features=5120, ffn_dim=8192, ffn2_dim=8192,
|
|
freq_dim=256, text_dim=4096, out_dim=16, num_heads=16, num_layers=32, eps=1e-6,
|
|
qk_norm=True, cross_attn_norm=True,
|
|
attention_mode='sdpa', rope_func='comfy', rms_norm_function='default',
|
|
main_device=torch.device('cuda'), offload_device=torch.device('cpu'), dtype=torch.float16,
|
|
teacache_coefficients=[], magcache_ratios=[], vace_layers=None, vace_in_dim=None,
|
|
inject_sample_info=False, add_ref_conv=False, in_dim_ref_conv=16, add_control_adapter=False,
|
|
in_dim_control_adapter=24, use_motion_attn=False,
|
|
#s2v
|
|
cond_dim=0, audio_dim=1024, num_audio_token=4, enable_adain=False, zero_timestep=False, humo_audio=False,
|
|
adain_mode="attn_norm", audio_inject_layers=[0, 4, 8, 12, 16, 20, 24, 27, 30, 33, 36, 39],
|
|
# WanAnimate
|
|
is_wananimate=False, motion_encoder_dim=512,
|
|
# lynx
|
|
lynx_ip_layers=None, lynx_ref_layers=None,
|
|
# LongCat
|
|
is_longcat=False,
|
|
):
|
|
r"""
|
|
Initialize the diffusion model backbone.
|
|
|
|
Args:
|
|
model_type (`str`, *optional*, defaults to 't2v'):
|
|
Model variant - 't2v' (text-to-video) or 'i2v' (image-to-video)
|
|
patch_size (`tuple`, *optional*, defaults to (1, 2, 2)):
|
|
3D patch dimensions for video embedding (t_patch, h_patch, w_patch)
|
|
text_len (`int`, *optional*, defaults to 512):
|
|
Fixed length for text embeddings
|
|
in_dim (`int`, *optional*, defaults to 16):
|
|
Input video channels (C_in)
|
|
dim (`int`, *optional*, defaults to 2048):
|
|
Hidden dimension of the transformer
|
|
ffn_dim (`int`, *optional*, defaults to 8192):
|
|
Intermediate dimension in feed-forward network
|
|
freq_dim (`int`, *optional*, defaults to 256):
|
|
Dimension for sinusoidal time embeddings
|
|
text_dim (`int`, *optional*, defaults to 4096):
|
|
Input dimension for text embeddings
|
|
out_dim (`int`, *optional*, defaults to 16):
|
|
Output video channels (C_out)
|
|
num_heads (`int`, *optional*, defaults to 16):
|
|
Number of attention heads
|
|
num_layers (`int`, *optional*, defaults to 32):
|
|
Number of transformer blocks
|
|
qk_norm (`bool`, *optional*, defaults to True):
|
|
Enable query/key normalization
|
|
cross_attn_norm (`bool`, *optional*, defaults to False):
|
|
Enable cross-attention normalization
|
|
eps (`float`, *optional*, defaults to 1e-6):
|
|
Epsilon value for normalization layers
|
|
"""
|
|
|
|
super().__init__()
|
|
|
|
self.model_type = model_type
|
|
|
|
self.patch_size = patch_size
|
|
self.text_len = text_len
|
|
self.in_dim = in_dim
|
|
self.dim = dim
|
|
self.in_features = in_features
|
|
self.out_features = out_features
|
|
self.ffn_dim = ffn_dim
|
|
self.ffn2_dim = ffn2_dim
|
|
self.freq_dim = freq_dim
|
|
self.text_dim = text_dim
|
|
self.out_dim = out_dim
|
|
self.num_heads = num_heads
|
|
self.num_layers = num_layers
|
|
self.qk_norm = qk_norm
|
|
self.cross_attn_norm = cross_attn_norm
|
|
self.eps = eps
|
|
self.attention_mode = attention_mode
|
|
self.rope_func = rope_func
|
|
self.main_device = main_device
|
|
self.offload_device = offload_device
|
|
self.vace_layers = vace_layers
|
|
self.device = main_device
|
|
self.patched_linear = False
|
|
|
|
self.blocks_to_swap = -1
|
|
self.offload_txt_emb = False
|
|
self.offload_img_emb = False
|
|
self.vace_blocks_to_swap = -1
|
|
|
|
self.cache_device = offload_device
|
|
|
|
#init TeaCache variables
|
|
self.enable_teacache = False
|
|
self.rel_l1_thresh = 0.15
|
|
self.teacache_start_step= 0
|
|
self.teacache_end_step = -1
|
|
self.teacache_state = TeaCacheState(cache_device=self.cache_device)
|
|
self.teacache_coefficients = teacache_coefficients
|
|
self.teacache_use_coefficients = False
|
|
self.teacache_mode = 'e'
|
|
|
|
#init MagCache variables
|
|
self.enable_magcache = False
|
|
self.magcache_state = MagCacheState(cache_device=self.cache_device)
|
|
self.magcache_thresh = 0.24
|
|
self.magcache_K = 4
|
|
self.magcache_start_step = 0
|
|
self.magcache_end_step = -1
|
|
self.magcache_ratios = magcache_ratios
|
|
|
|
#init EasyCache variables
|
|
self.enable_easycache = False
|
|
self.easycache_thresh = 0.1
|
|
self.easycache_start_step = 0
|
|
self.easycache_end_step = -1
|
|
self.easycache_state = EasyCacheState(cache_device=self.cache_device)
|
|
|
|
self.slg_blocks = None
|
|
self.slg_start_percent = 0.0
|
|
self.slg_end_percent = 1.0
|
|
|
|
self.use_non_blocking = False
|
|
self.prefetch_blocks = 0
|
|
self.block_swap_debug = False
|
|
|
|
self.video_attention_split_steps = []
|
|
self.lora_scheduling_enabled = False
|
|
|
|
self.multitalk_model_type = "none"
|
|
|
|
self.lynx_ip_layers = lynx_ip_layers
|
|
self.lynx_ref_layers = lynx_ref_layers
|
|
|
|
self.humo_audio = humo_audio
|
|
|
|
self.motion_encoder_dim = motion_encoder_dim
|
|
|
|
self.base_dtype = dtype
|
|
|
|
self.is_ovi_audio_model = patch_size == [1]
|
|
|
|
self.audio_model = None
|
|
|
|
self.is_longcat = is_longcat
|
|
|
|
# embeddings
|
|
if not self.is_ovi_audio_model:
|
|
self.patch_embedding = nn.Conv3d(in_dim, dim, kernel_size=patch_size, stride=patch_size)
|
|
else:
|
|
from ...Ovi.audio_model_layers import ChannelLastConv1d, ConvMLP
|
|
self.patch_embedding = nn.Sequential(
|
|
ChannelLastConv1d(in_dim, dim, kernel_size=7, padding=3),
|
|
nn.SiLU(),
|
|
ConvMLP(dim, dim * 4, kernel_size=7, padding=3),
|
|
)
|
|
|
|
self.original_patch_embedding = self.patch_embedding
|
|
self.expanded_patch_embedding = self.patch_embedding
|
|
|
|
if model_type != 'no_cross_attn':
|
|
self.text_embedding = nn.Sequential(
|
|
nn.Linear(text_dim, dim), nn.GELU(approximate='tanh'),
|
|
nn.Linear(dim, dim))
|
|
|
|
if not is_longcat:
|
|
self.time_embedding = nn.Sequential(nn.Linear(freq_dim, dim), nn.SiLU(), nn.Linear(dim, dim))
|
|
self.time_projection = nn.Sequential(nn.SiLU(), nn.Linear(dim, dim * 6))
|
|
else:
|
|
from ...LongCat.layers import TimestepEmbedder
|
|
adaln_tembed_dim = 512
|
|
self.time_embedding = TimestepEmbedder(t_embed_dim=adaln_tembed_dim, frequency_embedding_size=freq_dim)
|
|
|
|
|
|
if vace_layers is not None:
|
|
self.vace_layers = [i for i in range(0, self.num_layers, 2)] if vace_layers is None else vace_layers
|
|
self.vace_in_dim = self.in_dim if vace_in_dim is None else vace_in_dim
|
|
|
|
self.vace_layers_mapping = {i: n for n, i in enumerate(self.vace_layers)}
|
|
|
|
# vace blocks
|
|
self.vace_blocks = nn.ModuleList([
|
|
VaceWanAttentionBlock('t2v_cross_attn', self.in_features, self.out_features, self.ffn_dim, self.ffn2_dim,self.num_heads, self.qk_norm,
|
|
self.cross_attn_norm, self.eps, block_id=i, attention_mode=self.attention_mode, rope_func=self.rope_func, rms_norm_function=rms_norm_function)
|
|
for i in self.vace_layers
|
|
])
|
|
|
|
# vace patch embeddings
|
|
self.vace_patch_embedding = nn.Conv3d(
|
|
self.vace_in_dim, self.dim, kernel_size=self.patch_size, stride=self.patch_size
|
|
)
|
|
self.blocks = nn.ModuleList([
|
|
BaseWanAttentionBlock('t2v_cross_attn', self.in_features, self.out_features, ffn_dim, self.ffn2_dim, num_heads,
|
|
qk_norm, cross_attn_norm, eps,
|
|
attention_mode=self.attention_mode, rope_func=self.rope_func, rms_norm_function=rms_norm_function,
|
|
block_id=self.vace_layers_mapping[i] if i in self.vace_layers else None, lynx_ip_layers=lynx_ip_layers, lynx_ref_layers=lynx_ref_layers, block_idx=i)
|
|
for i in range(num_layers)
|
|
])
|
|
else:
|
|
# blocks
|
|
if model_type == 't2v' or model_type == 's2v':
|
|
cross_attn_type = 't2v_cross_attn'
|
|
elif model_type == 'i2v' or model_type == 'fl2v':
|
|
cross_attn_type = 'i2v_cross_attn'
|
|
else:
|
|
cross_attn_type = 'no_cross_attn'
|
|
|
|
self.blocks = nn.ModuleList([
|
|
WanAttentionBlock(cross_attn_type, self.in_features, self.out_features, ffn_dim, ffn2_dim, num_heads,
|
|
qk_norm, cross_attn_norm, eps,
|
|
attention_mode=self.attention_mode, rope_func=self.rope_func, rms_norm_function=rms_norm_function,
|
|
use_motion_attn=(i % 4 == 0 and use_motion_attn), use_humo_audio_attn=self.humo_audio,
|
|
face_fuser_block = (i % 5 == 0 and is_wananimate), lynx_ip_layers=lynx_ip_layers, lynx_ref_layers=lynx_ref_layers,
|
|
block_idx=i, is_longcat=is_longcat)
|
|
for i in range(num_layers)
|
|
])
|
|
#MTV Crafter
|
|
if use_motion_attn:
|
|
self.pad_motion_tokens = torch.zeros(1, 1, 2048)
|
|
|
|
# head
|
|
if not is_longcat:
|
|
self.head = Head(dim, out_dim, patch_size, eps)
|
|
else:
|
|
self.head = Head_adaLN(dim, out_dim, patch_size, eps, adaln_tembed_dim=512)
|
|
|
|
d = self.dim // self.num_heads
|
|
self.rope_embedder = EmbedND_RifleX(d, 10000.0, [d - 4 * (d // 6), 2 * (d // 6), 2 * (d // 6)], num_frames=None, k=None)
|
|
self.cached_freqs = self.cached_shape = self.cached_cond = None
|
|
|
|
# buffers (don't use register_buffer otherwise dtype will be changed in to())
|
|
assert (dim % num_heads) == 0 and (dim // num_heads) % 2 == 0
|
|
|
|
if model_type == 'i2v' or model_type == 'fl2v':
|
|
self.img_emb = MLPProj(1280, dim, fl_pos_emb=model_type == 'fl2v')
|
|
|
|
#skyreels v2
|
|
if inject_sample_info:
|
|
self.fps_embedding = nn.Embedding(2, dim)
|
|
self.fps_projection = nn.Sequential(nn.Linear(dim, dim), nn.SiLU(), nn.Linear(dim, dim * 6))
|
|
#fun 1.1
|
|
if add_ref_conv:
|
|
self.ref_conv = nn.Conv2d(in_dim_ref_conv, dim, kernel_size=patch_size[1:], stride=patch_size[1:])
|
|
else:
|
|
self.ref_conv = None
|
|
|
|
if add_control_adapter:
|
|
from .wan_camera_adapter import SimpleAdapter
|
|
self.control_adapter = SimpleAdapter(in_dim_control_adapter, dim, kernel_size=patch_size[1:], stride=patch_size[1:])
|
|
else:
|
|
self.control_adapter = None
|
|
|
|
#S2V
|
|
self.zero_timestep = self.audio_injector = self.trainable_cond_mask =None
|
|
if cond_dim > 0:
|
|
self.cond_encoder = nn.Conv3d(
|
|
cond_dim,
|
|
self.dim,
|
|
kernel_size=self.patch_size,
|
|
stride=self.patch_size)
|
|
if self.model_type == 's2v':
|
|
self.enable_adain = enable_adain
|
|
self.casual_audio_encoder = CausalAudioEncoder(
|
|
dim=audio_dim,
|
|
out_dim=self.dim,
|
|
num_token=num_audio_token,
|
|
need_global=enable_adain)
|
|
all_modules, all_modules_names = torch_dfs(
|
|
self.blocks, parent_name="root.transformer_blocks")
|
|
self.audio_injector = AudioInjector_WAN(
|
|
all_modules,
|
|
all_modules_names,
|
|
dim=self.dim,
|
|
num_heads=self.num_heads,
|
|
inject_layer=audio_inject_layers,
|
|
root_net=self,
|
|
enable_adain=enable_adain,
|
|
adain_dim=self.dim,
|
|
need_adain_ont=adain_mode != "attn_norm",
|
|
attention_mode=attention_mode
|
|
)
|
|
self.trainable_cond_mask = nn.Embedding(3, self.dim)
|
|
|
|
self.frame_packer = FramePackMotioner(
|
|
inner_dim=self.dim,
|
|
num_heads=self.num_heads,
|
|
zip_frame_buckets=[1, 2, 16],
|
|
drop_mode='padd')
|
|
self.adain_mode = adain_mode
|
|
self.zero_timestep = zero_timestep
|
|
|
|
# HuMo Audio
|
|
if self.humo_audio:
|
|
from ...HuMo.audio_proj import AudioProjModel
|
|
self.audio_proj = AudioProjModel(seq_len=8, blocks=5, channels=1280,
|
|
intermediate_dim=512, output_dim=1536, context_tokens=16)
|
|
# WanAnimate
|
|
self.motion_encoder = self.pose_patch_embedding = self.face_encoder = self.face_adapter = None
|
|
if is_wananimate:
|
|
from .wananimate.motion_encoder import MotionExtractor
|
|
from .wananimate.face_blocks import FaceEncoder
|
|
self.pose_patch_embedding = nn.Conv3d(16, dim, kernel_size=patch_size, stride=patch_size)
|
|
self.motion_encoder = MotionExtractor()
|
|
|
|
self.face_encoder = FaceEncoder(
|
|
in_dim=motion_encoder_dim,
|
|
out_dim=self.dim,
|
|
num_heads=4,
|
|
dtype=dtype
|
|
)
|
|
|
|
def block_swap(self, blocks_to_swap, offload_txt_emb=False, offload_img_emb=False, vace_blocks_to_swap=None, prefetch_blocks=0, block_swap_debug=False):
|
|
# Clamp blocks_to_swap to valid range
|
|
blocks_to_swap = max(0, min(blocks_to_swap, len(self.blocks)))
|
|
|
|
log.info(f"Swapping {blocks_to_swap} transformer blocks")
|
|
self.blocks_to_swap = blocks_to_swap
|
|
self.prefetch_blocks = prefetch_blocks
|
|
self.block_swap_debug = block_swap_debug
|
|
|
|
self.offload_img_emb = offload_img_emb
|
|
self.offload_txt_emb = offload_txt_emb
|
|
|
|
total_offload_memory = 0
|
|
total_main_memory = 0
|
|
|
|
# Calculate the index where swapping starts
|
|
swap_start_idx = len(self.blocks) - blocks_to_swap
|
|
|
|
for b, block in tqdm(enumerate(self.blocks), total=len(self.blocks), desc="Initializing block swap"):
|
|
block_memory = get_module_memory_mb(block)
|
|
|
|
if b < swap_start_idx:
|
|
block.to(self.main_device)
|
|
total_main_memory += block_memory
|
|
else:
|
|
block.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
total_offload_memory += block_memory
|
|
|
|
if blocks_to_swap != -1 and vace_blocks_to_swap == 0:
|
|
vace_blocks_to_swap = 1
|
|
|
|
if vace_blocks_to_swap > 0 and self.vace_layers is not None:
|
|
# Clamp vace_blocks_to_swap to valid range
|
|
vace_blocks_to_swap = max(0, min(vace_blocks_to_swap, len(self.vace_blocks)))
|
|
self.vace_blocks_to_swap = vace_blocks_to_swap
|
|
|
|
# Calculate the index where VACE swapping starts
|
|
vace_swap_start_idx = len(self.vace_blocks) - vace_blocks_to_swap
|
|
|
|
for b, block in tqdm(enumerate(self.vace_blocks), total=len(self.vace_blocks), desc="Initializing vace block swap"):
|
|
block_memory = get_module_memory_mb(block)
|
|
|
|
if b < vace_swap_start_idx:
|
|
block.to(self.main_device)
|
|
total_main_memory += block_memory
|
|
else:
|
|
block.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
total_offload_memory += block_memory
|
|
|
|
mm.soft_empty_cache()
|
|
gc.collect()
|
|
|
|
log.info("----------------------")
|
|
log.info(f"Block swap memory summary:")
|
|
log.info(f"Transformer blocks on {self.offload_device}: {total_offload_memory:.2f}MB")
|
|
log.info(f"Transformer blocks on {self.main_device}: {total_main_memory:.2f}MB")
|
|
log.info(f"Total memory used by transformer blocks: {(total_offload_memory + total_main_memory):.2f}MB")
|
|
log.info(f"Non-blocking memory transfer: {self.use_non_blocking}")
|
|
log.info("----------------------")
|
|
|
|
def forward_vace(
|
|
self,
|
|
x,
|
|
vace_context,
|
|
seq_len,
|
|
kwargs
|
|
):
|
|
# embeddings
|
|
c = [self.vace_patch_embedding(u.unsqueeze(0).float()).to(x.dtype) for u in vace_context]
|
|
c = [u.flatten(2).transpose(1, 2) for u in c]
|
|
c = torch.cat([
|
|
torch.cat([u, u.new_zeros(1, seq_len - u.size(1), u.size(2))],
|
|
dim=1) for u in c
|
|
])
|
|
|
|
if x.shape[1] > c.shape[1]:
|
|
c = torch.cat([c.new_zeros(x.shape[0], x.shape[1] - c.shape[1], c.shape[2]), c], dim=1)
|
|
if c.shape[1] > x.shape[1]:
|
|
c = c[:, :x.shape[1]]
|
|
|
|
hints = []
|
|
current_c = c
|
|
vace_swap_start_idx = len(self.vace_blocks) - self.vace_blocks_to_swap if self.vace_blocks_to_swap > 0 else len(self.vace_blocks)
|
|
|
|
for b, block in enumerate(self.vace_blocks):
|
|
if b >= vace_swap_start_idx and self.vace_blocks_to_swap > 0:
|
|
block.to(self.main_device)
|
|
|
|
if b == 0:
|
|
c_processed = block.before_proj(current_c) + x
|
|
else:
|
|
c_processed = current_c
|
|
|
|
c_processed = block.forward(c_processed, **kwargs)[0]
|
|
|
|
# Store skip connection
|
|
c_skip = block.after_proj(c_processed)
|
|
hints.append(c_skip.to(
|
|
self.offload_device if self.vace_blocks_to_swap > 0 else self.main_device,
|
|
non_blocking=self.use_non_blocking
|
|
))
|
|
|
|
current_c = c_processed
|
|
|
|
if b >= vace_swap_start_idx and self.vace_blocks_to_swap > 0:
|
|
block.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
|
|
return hints
|
|
|
|
def audio_injector_forward(self, block_idx, x, audio_emb, scale=1.0):
|
|
if block_idx in self.audio_injector.injected_block_id.keys():
|
|
audio_attn_id = self.audio_injector.injected_block_id[block_idx]
|
|
num_frames = audio_emb.shape[1]# b f n c
|
|
|
|
input_x = x[:, :self.original_seq_len].clone() # b (f h w) c
|
|
input_x = rearrange(input_x, "b (t n) c -> (b t) n c", t=num_frames)
|
|
|
|
if self.enable_adain and self.adain_mode == "attn_norm":
|
|
audio_emb_global = self.audio_emb_global
|
|
audio_emb_global = rearrange(audio_emb_global,"b t n c -> (b t) n c")
|
|
attn_x = self.audio_injector.injector_adain_layers[audio_attn_id](input_x, temb=audio_emb_global[:, 0])
|
|
else:
|
|
attn_x = self.audio_injector.injector_pre_norm_feat[audio_attn_id](input_x)
|
|
|
|
attn_audio_emb = rearrange(audio_emb, "b t n c -> (b t) n c", t=num_frames)
|
|
residual_out = self.audio_injector.injector[audio_attn_id](
|
|
x=attn_x ,
|
|
context=attn_audio_emb * scale,
|
|
)
|
|
residual_out = rearrange(residual_out, "(b t) n c -> b (t n) c", t=num_frames)
|
|
x[:, :self.original_seq_len].add_(residual_out)
|
|
|
|
return x
|
|
|
|
def wananimate_pose_embedding(self, x, pose_latents, strength=1.0):
|
|
pose_latents = [self.pose_patch_embedding(u.unsqueeze(0).to(torch.float32)).to(x[0].dtype) for u in pose_latents]
|
|
for x_, pose_latents_ in zip(x, pose_latents):
|
|
x_[:, :, 1:].add_(pose_latents_, alpha=strength)
|
|
return x
|
|
|
|
|
|
def wananimate_face_embedding(self, face_pixel_values):
|
|
b,c,T,h,w = face_pixel_values.shape
|
|
face_pixel_values = rearrange(face_pixel_values, "b c t h w -> (b t) c h w")
|
|
|
|
encode_bs = 8
|
|
face_pixel_values_tmp = []
|
|
self.motion_encoder.to(self.main_device)
|
|
for i in range(math.ceil(face_pixel_values.shape[0]/encode_bs)):
|
|
face_pixel_values_tmp.append(self.motion_encoder(face_pixel_values[i*encode_bs:(i+1)*encode_bs]))
|
|
del face_pixel_values
|
|
self.motion_encoder.to(self.offload_device)
|
|
|
|
motion_vec = rearrange(torch.cat(face_pixel_values_tmp), "(b t) c -> b t c", t=T)
|
|
del face_pixel_values_tmp
|
|
self.face_encoder.to(self.main_device)
|
|
motion_vec = self.face_encoder(motion_vec.to(self.face_encoder.dtype))
|
|
self.face_encoder.to(self.offload_device)
|
|
|
|
B, L, H, C = motion_vec.shape
|
|
pad_face = torch.zeros(B, 1, H, C, device=motion_vec.device, dtype=motion_vec.dtype)
|
|
return torch.cat([pad_face, motion_vec], dim=1)
|
|
|
|
|
|
def wananimate_forward(self, block, x, motion_vec, strength=1.0, motion_masks=None):
|
|
adapter_args = [x, motion_vec, motion_masks]
|
|
residual_out = block.fuser_block(*adapter_args)
|
|
return x.add(residual_out, alpha=strength)
|
|
|
|
|
|
def rope_encode_comfy(self, t, h, w, freq_offset=0, t_start=0, ref_frame_shape=None, pose_frame_shape=None,
|
|
steps_t=None, steps_h=None, steps_w=None, ntk_alphas=[1,1,1], device=None, dtype=None):
|
|
|
|
patch_size = self.patch_size
|
|
t_len = ((t + (patch_size[0] // 2)) // patch_size[0])
|
|
h_len = ((h + (patch_size[1] // 2)) // patch_size[1])
|
|
w_len = ((w + (patch_size[2] // 2)) // patch_size[2])
|
|
|
|
if steps_t is None:
|
|
steps_t = t_len
|
|
if steps_h is None:
|
|
steps_h = h_len
|
|
if steps_w is None:
|
|
steps_w = w_len
|
|
|
|
# Main frames position IDs
|
|
img_ids = torch.zeros((steps_t, steps_h, steps_w, 3), device=device, dtype=dtype)
|
|
img_ids[:, :, :, 0] = img_ids[:, :, :, 0] + torch.linspace(t_start+freq_offset, t_start + (t_len - 1), steps=steps_t, device=device, dtype=dtype).reshape(-1, 1, 1)
|
|
img_ids[:, :, :, 1] = img_ids[:, :, :, 1] + torch.linspace(freq_offset, h_len - 1, steps=steps_h, device=device, dtype=dtype).reshape(1, -1, 1)
|
|
img_ids[:, :, :, 2] = img_ids[:, :, :, 2] + torch.linspace(freq_offset, w_len - 1, steps=steps_w, device=device, dtype=dtype).reshape(1, 1, -1)
|
|
img_ids = img_ids.reshape(1, -1, img_ids.shape[-1])
|
|
|
|
segments = [img_ids] # Start with main frames
|
|
|
|
# Reference frames position IDs
|
|
if ref_frame_shape is not None:
|
|
F_cond, H_cond, W_cond = ref_frame_shape[-3], ref_frame_shape[-2], ref_frame_shape[-1]
|
|
cond_f_len = ((F_cond + (self.patch_size[0] // 2)) // self.patch_size[0])
|
|
cond_h_len = ((H_cond + (self.patch_size[1] // 2)) // self.patch_size[1])
|
|
cond_w_len = ((W_cond + (self.patch_size[2] // 2)) // self.patch_size[2])
|
|
cond_img_ids = torch.zeros((cond_f_len, cond_h_len, cond_w_len, 3), device=device, dtype=dtype)
|
|
|
|
cond_img_ids[:, :, :, 0] = cond_img_ids[:, :, :, 0] + torch.linspace(0, cond_f_len - 1, steps=cond_f_len, device=device, dtype=dtype).reshape(-1, 1, 1)
|
|
cond_img_ids[:, :, :, 1] = cond_img_ids[:, :, :, 1] + torch.linspace(h_len, h_len + cond_h_len - 1, steps=cond_h_len, device=device, dtype=dtype).reshape(1, -1, 1)
|
|
cond_img_ids[:, :, :, 2] = cond_img_ids[:, :, :, 2] + torch.linspace(w_len, w_len + cond_w_len - 1, steps=cond_w_len, device=device, dtype=dtype).reshape(1, 1, -1)
|
|
|
|
segments.insert(0, cond_img_ids.reshape(1, -1, cond_img_ids.shape[-1])) # Ref frames come first
|
|
|
|
# Pose frames position IDs
|
|
if pose_frame_shape is not None:
|
|
F_pose, H_pose, W_pose = pose_frame_shape[-3], pose_frame_shape[-2], pose_frame_shape[-1]
|
|
|
|
downscale = H_pose != h
|
|
pose_f_len_full = ((F_pose + (self.patch_size[0] // 2)) // self.patch_size[0])
|
|
pose_h_len_full = (((H_pose * (2 if downscale else 1)) + (self.patch_size[1] // 2)) // self.patch_size[1]) # 2x height
|
|
pose_w_len_full = (((W_pose * (2 if downscale else 1)) + (self.patch_size[2] // 2)) // self.patch_size[2]) # 2x width
|
|
|
|
pose_img_ids = torch.zeros((pose_f_len_full, pose_h_len_full, pose_w_len_full, 3), device=device, dtype=dtype)
|
|
global_h_offset, global_w_offset = 0, 120 # global spatial offset to separate pose from main frames spatially (SCAIL uses 120 as offset)
|
|
pose_img_ids[:, :, :, 0] = pose_img_ids[:, :, :, 0] + torch.linspace(t_start+freq_offset, t_start + (pose_f_len_full - 1), steps=pose_f_len_full, device=device, dtype=dtype).reshape(-1, 1, 1)
|
|
pose_img_ids[:, :, :, 1] = pose_img_ids[:, :, :, 1] + torch.linspace(global_h_offset + freq_offset, global_h_offset + pose_h_len_full - 1, steps=pose_h_len_full, device=device, dtype=dtype).reshape(1, -1, 1)
|
|
pose_img_ids[:, :, :, 2] = pose_img_ids[:, :, :, 2] + torch.linspace(global_w_offset + freq_offset, global_w_offset + pose_w_len_full - 1, steps=pose_w_len_full, device=device, dtype=dtype).reshape(1, 1, -1)
|
|
|
|
segments.append(pose_img_ids.reshape(1, -1, pose_img_ids.shape[-1]))
|
|
|
|
combined_img_ids = torch.cat(segments, dim=1)
|
|
freqs = self.rope_embedder(combined_img_ids, ntk_alphas).movedim(1, 2)
|
|
|
|
# Downsample pose frequencies to match actual pose input resolution
|
|
if pose_frame_shape is not None and downscale:
|
|
pose_h_len_actual = ((H_pose + (self.patch_size[1] // 2)) // self.patch_size[1])
|
|
pose_w_len_actual = ((W_pose + (self.patch_size[2] // 2)) // self.patch_size[2])
|
|
|
|
pose_start_idx = freqs.shape[1] - pose_f_len_full * pose_h_len_full * pose_w_len_full
|
|
main_freqs, pose_freqs = freqs[:, :pose_start_idx], freqs[:, pose_start_idx:]
|
|
|
|
B, _, heads, dim, _, _ = pose_freqs.shape
|
|
# Reshape and pool: (B, L, heads, dim, 2, 2) -> pool H,W -> (B, L', heads, dim, 2, 2)
|
|
pose_freqs = pose_freqs.reshape(B, pose_f_len_full, pose_h_len_full, pose_w_len_full, heads, dim, 2, 2)
|
|
pose_freqs = pose_freqs.permute(0, 1, 4, 5, 6, 7, 2, 3).reshape(-1, pose_h_len_full, pose_w_len_full)
|
|
pose_freqs = F.avg_pool2d(pose_freqs, kernel_size=2, stride=2)
|
|
pose_freqs = pose_freqs.reshape(B, pose_f_len_full, heads, dim, 2, 2, pose_h_len_actual, pose_w_len_actual)
|
|
pose_freqs = pose_freqs.permute(0, 1, 6, 7, 2, 3, 4, 5).reshape(B, -1, heads, dim, 2, 2)
|
|
|
|
freqs = torch.cat([main_freqs, pose_freqs], dim=1)
|
|
|
|
return freqs
|
|
|
|
|
|
def forward(
|
|
self, x, t, context, seq_len,
|
|
is_uncond=False,
|
|
current_step_percentage=0.0, current_step=0, last_step=0, total_steps=50,
|
|
clip_fea=None, y=None,
|
|
device=torch.device('cuda'),
|
|
freqs=None,
|
|
enhance_enabled=False,
|
|
pred_id=None,
|
|
control_lora_enabled=False,
|
|
vace_data=None,
|
|
camera_embed=None,
|
|
unianim_data=None,
|
|
fps_embeds=None,
|
|
fun_ref=None, fun_camera=None,
|
|
audio_proj=None, audio_scale=1.0,
|
|
uni3c_data=None, controlnet=None,
|
|
add_cond=None, attn_cond=None,
|
|
nag_params={}, nag_context=None,
|
|
multitalk_audio=None,
|
|
ref_target_masks=None,
|
|
inner_t=None,
|
|
standin_input=None,
|
|
fantasy_portrait_input=None,
|
|
phantom_ref=None,
|
|
reverse_time=False,
|
|
ntk_alphas = [1.0, 1.0, 1.0],
|
|
mtv_motion_tokens=None, mtv_motion_rotary_emb=None,
|
|
mtv_freqs=None, mtv_strength=1.0,
|
|
s2v_audio_input=None, s2v_ref_latent=None, s2v_audio_scale=1.0,
|
|
s2v_ref_motion=None, s2v_pose=None, s2v_motion_frames=[1, 0],
|
|
humo_audio=None, humo_audio_scale=1.0,
|
|
wananim_pose_latents=None, wananim_face_pixel_values=None,
|
|
wananim_pose_strength=1.0, wananim_face_strength=1.0,
|
|
lynx_embeds=None,
|
|
x_ovi=None, seq_len_ovi=None, ovi_negative_text_embeds=None,
|
|
flashvsr_LQ_latent=None, flashvsr_strength=1.0,
|
|
num_cond_latents=None,
|
|
add_text_emb=None,
|
|
sdancer_input=None, # SteadyDancer
|
|
one_to_all_input=None, one_to_all_controlnet_strength=0.0, # One-to-All
|
|
scail_input=None, # SCAIL pose
|
|
):
|
|
r"""
|
|
Forward pass through the diffusion model
|
|
|
|
Args:
|
|
x (List[Tensor]):
|
|
List of input video tensors, each with shape [C_in, F, H, W]
|
|
t (Tensor):
|
|
Diffusion timesteps tensor of shape [B]
|
|
context (List[Tensor]):
|
|
List of text embeddings each with shape [L, C]
|
|
seq_len (`int`):
|
|
Maximum sequence length for positional encoding
|
|
clip_fea (Tensor, *optional*):
|
|
CLIP image features for image-to-video mode
|
|
y (List[Tensor], *optional*):
|
|
Conditional video inputs for image-to-video mode, same shape as x
|
|
|
|
Returns:
|
|
List[Tensor]:
|
|
List of denoised video tensors with original input shapes [C_out, F, H / 8, W / 8]
|
|
"""
|
|
# Stand-In only used on first positive pass, then cached in kv_cache
|
|
if is_uncond or current_step > 0:
|
|
standin_input = None
|
|
|
|
# MTV Crafter motion projection
|
|
if mtv_motion_tokens is not None:
|
|
bs, motion_seq_len = mtv_motion_tokens.shape[0], mtv_motion_tokens.shape[1]
|
|
mtv_motion_tokens = torch.cat([mtv_motion_tokens, self.pad_motion_tokens.to(mtv_motion_tokens).expand(bs, motion_seq_len, -1)], dim=-1)
|
|
|
|
# Fantasy Portrait
|
|
adapter_proj = ip_scale = None
|
|
if fantasy_portrait_input is not None:
|
|
if fantasy_portrait_input['start_percent'] <= current_step_percentage <= fantasy_portrait_input['end_percent']:
|
|
adapter_proj = fantasy_portrait_input.get("adapter_proj", None)
|
|
ip_scale = fantasy_portrait_input.get("strength", 1.0)
|
|
|
|
if self.lora_scheduling_enabled:
|
|
update_lora_step(self, current_step)
|
|
|
|
# lynx
|
|
lynx_x_ip = lynx_ref_feature = lynx_ref_buffer = lynx_ref_feature_extractor = None
|
|
lynx_ip_scale = lynx_ref_scale = 1.0
|
|
if lynx_embeds is not None:
|
|
lynx_ref_feature_extractor = lynx_embeds.get("ref_feature_extractor", False)
|
|
lynx_ref_blocks_to_use = lynx_embeds.get("ref_blocks_to_use", None)
|
|
if lynx_ref_blocks_to_use is None:
|
|
lynx_ref_blocks_to_use = list(range(len(self.blocks)))
|
|
if (lynx_embeds['start_percent'] <= current_step_percentage <= lynx_embeds['end_percent']) and not lynx_ref_feature_extractor:
|
|
if not is_uncond:
|
|
lynx_x_ip = lynx_embeds.get("ip_x", None)
|
|
lynx_ref_buffer = lynx_embeds.get("ref_buffer", None)
|
|
else:
|
|
lynx_x_ip = lynx_embeds.get("ip_x_uncond", None)
|
|
lynx_ref_buffer = lynx_embeds.get("ref_buffer_uncond", None)
|
|
lynx_x_ip = lynx_x_ip.to(self.main_device) if lynx_x_ip is not None else None
|
|
|
|
lynx_ip_scale = lynx_embeds.get("ip_scale", 1.0)
|
|
lynx_ref_scale = lynx_embeds.get("ref_scale", 1.0)
|
|
|
|
|
|
#s2v
|
|
if self.model_type == 's2v' and s2v_audio_input is not None:
|
|
if is_uncond:
|
|
s2v_audio_input = s2v_audio_input * 0 # to match original code
|
|
s2v_audio_input = torch.cat([s2v_audio_input[..., 0:1].repeat(1, 1, 1, s2v_motion_frames[0]), s2v_audio_input], dim=-1)
|
|
|
|
audio_emb_res = self.casual_audio_encoder(s2v_audio_input)
|
|
if self.enable_adain:
|
|
audio_emb_global, audio_emb = audio_emb_res
|
|
self.audio_emb_global = audio_emb_global[:, s2v_motion_frames[1]:].clone()
|
|
else:
|
|
audio_emb = audio_emb_res
|
|
merged_audio_emb = audio_emb[:, s2v_motion_frames[1]:, :]
|
|
|
|
# params
|
|
device = self.main_device
|
|
|
|
if freqs is not None and freqs.device != device:
|
|
freqs = freqs.to(device)
|
|
|
|
_, F, H, W = x[0].shape
|
|
ref_frame_shape = pose_frame_shape = None
|
|
|
|
sdancer_enabled = False
|
|
if sdancer_input is not None and sdancer_input['start_percent'] <= current_step_percentage <= sdancer_input['end_percent']:
|
|
sdancer_enabled = True
|
|
x_noise_clone = torch.stack(x)
|
|
|
|
# I2V
|
|
if y is not None:
|
|
if hasattr(self, "randomref_embedding_pose") and unianim_data is not None:
|
|
if unianim_data['start_percent'] <= current_step_percentage <= unianim_data['end_percent']:
|
|
random_ref_emb = unianim_data["random_ref"]
|
|
if random_ref_emb is not None:
|
|
y[0].add_(random_ref_emb, alpha=unianim_data["strength"])
|
|
x = [torch.cat([u, v], dim=0) for u, v in zip(x, y)]
|
|
|
|
suffix_frames = x[0].shape[1]
|
|
prefix_frames = 0
|
|
|
|
# One-to-all-Animation
|
|
onetoall_ref_block_samples = onetoall_freqs = prev_x = prev_control = None
|
|
onetoall_ref_scale = 1.0
|
|
onetoall_control_enabled = use_token_replace = False
|
|
e0_token_replace = token_replace_start = None
|
|
replace_token_num = token_replace_start = 0
|
|
if one_to_all_input is not None:
|
|
# reference condition
|
|
ref_cond_latent = one_to_all_input.get("ref_latent_pos", None) if not is_uncond else one_to_all_input.get("ref_latent_neg", None)
|
|
if ref_cond_latent is not None and one_to_all_input['ref_start_percent'] <= current_step_percentage <= one_to_all_input['ref_end_percent']:
|
|
onetoall_ref_scale = one_to_all_input.get("ref_strength", 1.0)
|
|
self.image_to_cond.to(self.main_device)
|
|
image_cond = self.image_to_cond(ref_cond_latent.to(self.main_device, self.base_dtype))[0]
|
|
self.image_to_cond.to(self.offload_device)
|
|
x = [torch.cat([v, u], dim=1) for v, u in zip([image_cond], x)]
|
|
seq_len += math.ceil((image_cond.shape[-1] * image_cond.shape[-2]) / 4 * image_cond.shape[-3])
|
|
F += 1
|
|
prefix_frames = 1
|
|
suffix_frames += 1
|
|
self.refextractor.to(self.main_device)
|
|
onetoall_ref_block_samples, onetoall_freqs = self.refextractor(ref_cond_latent, timestep=t)
|
|
self.refextractor.to(self.offload_device)
|
|
# pose controlnet
|
|
controlnet_tokens = one_to_all_input.get("controlnet_tokens", None)
|
|
if controlnet_tokens is not None and one_to_all_input['controlnet_start_percent'] <= current_step_percentage <= one_to_all_input['controlnet_end_percent']:
|
|
onetoall_control_enabled = one_to_all_controlnet_strength != 0.0
|
|
# token replace
|
|
if one_to_all_input.get("token_replace", False):
|
|
use_token_replace = True
|
|
num_latent_frames_to_replace = one_to_all_input.get("num_latent_frames_to_replace", 2)
|
|
t_token_replace = torch.zeros_like(t)
|
|
token_replace_start = (H // self.patch_size[1]) * (W // self.patch_size[2]) # skip first (ref) frame
|
|
replace_token_num = num_latent_frames_to_replace * token_replace_start # zero next frames
|
|
|
|
# SCAIL ref
|
|
if scail_input is not None:
|
|
ref_latent = scail_input.get("ref_latent_pos", None) if not is_uncond else scail_input.get("ref_latent_neg", None)
|
|
if ref_latent is not None and scail_input['ref_start_percent'] <= current_step_percentage <= scail_input['ref_end_percent']:
|
|
x = [torch.cat([v, u], dim=1) for v, u in zip([ref_latent], x)]
|
|
seq_len += math.ceil((ref_latent.shape[-1] * ref_latent.shape[-2]) / 4 * ref_latent.shape[-3])
|
|
F += 1
|
|
prefix_frames = 1
|
|
suffix_frames += 1
|
|
|
|
#uni3c controlnet
|
|
if uni3c_data is not None:
|
|
render_latent = uni3c_data["render_latent"].to(self.base_dtype)
|
|
hidden_states = x[0].unsqueeze(0).clone().float()
|
|
if hidden_states.shape[1] == 16: #T2V work around
|
|
hidden_states = torch.cat([hidden_states, torch.zeros_like(hidden_states[:, :4])], dim=1)
|
|
if hidden_states.shape[2] != render_latent.shape[2]: # temporal resample
|
|
render_latent = nn.functional.interpolate(render_latent, size=(hidden_states.shape[2], hidden_states.shape[3], hidden_states.shape[4]), mode='trilinear', align_corners=False)
|
|
render_latent = torch.cat([hidden_states[:, :20], render_latent], dim=1)
|
|
|
|
# SteadyDancer
|
|
if sdancer_enabled:
|
|
sdancer_cond = sdancer_input["cond_pos"] if not is_uncond else sdancer_input["cond_neg"]
|
|
condition_temporal = [self.condition_embedding_temporal(c.unsqueeze(0).float()).to(self.base_dtype) for c in [sdancer_cond]] # Temporal Motion Coherence Module.
|
|
sdancer_cond = sdancer_cond.unsqueeze(0)
|
|
bs, _, time_steps, _, _ = sdancer_cond.shape
|
|
condition_reshape = rearrange(sdancer_cond, 'b c t h w -> (b t) c h w')
|
|
condition_spatial = self.condition_embedding_spatial(condition_reshape.float()).to(self.base_dtype) # Spatial Structure Adaptive Extractor.
|
|
condition_spatial = rearrange(condition_spatial, '(b t) c h w -> b c t h w', t=time_steps, b=bs)
|
|
condition_fused = sdancer_cond + condition_temporal[0] * sdancer_input["pose_strength_temporal"] + condition_spatial * sdancer_input["pose_strength_spatial"] # Hierarchical Aggregation (1): condition, temporal condition, spatial condition
|
|
condition_aligned = self.condition_embedding_align(condition_fused.float(), x_noise_clone).to(self.base_dtype) # Frame-wise Attention Alignment Unit.
|
|
else:
|
|
# patch embed
|
|
if control_lora_enabled:
|
|
self.expanded_patch_embedding.to(self.main_device)
|
|
x = [self.expanded_patch_embedding(u.unsqueeze(0).to(torch.float32)).to(x[0].dtype) for u in x]
|
|
else:
|
|
self.original_patch_embedding.to(self.main_device)
|
|
x = [self.original_patch_embedding(u.unsqueeze(0).to(torch.float32)).to(x[0].dtype) for u in x]
|
|
|
|
# ovi audio model
|
|
if self.audio_model is not None:
|
|
x_ovi = [self.audio_model.original_patch_embedding(u.unsqueeze(0).to(torch.float32)).to(x_ovi[0].dtype) for u in x_ovi]
|
|
grid_sizes_ovi = torch.stack([torch.tensor(u.shape[1:2], dtype=torch.long) for u in x_ovi])
|
|
seq_lens_ovi = torch.tensor([u.size(1) for u in x_ovi], dtype=torch.int32)
|
|
x_ovi = torch.cat([torch.cat([u, u.new_zeros(1, seq_len_ovi - u.size(1), u.size(2))], dim=1) for u in x_ovi])
|
|
d = self.dim // self.num_heads
|
|
freqs_ovi = rope_params(1024, d - 4 * (d // 6), freqs_scaling=0.19676).to(self.main_device)
|
|
x_ovi = x_ovi.to(self.main_device, self.base_dtype)
|
|
|
|
# WanAnimate
|
|
motion_vec = None
|
|
if wananim_face_pixel_values is not None:
|
|
motion_vec = self.wananimate_face_embedding(wananim_face_pixel_values).to(self.base_dtype)
|
|
|
|
if wananim_pose_latents is not None:
|
|
x = self.wananimate_pose_embedding(x, wananim_pose_latents, strength=wananim_pose_strength)
|
|
|
|
# s2v pose embedding
|
|
if s2v_pose is not None:
|
|
x[0] = x[0] + self.cond_encoder(s2v_pose.to(self.cond_encoder.weight.dtype)).to(self.base_dtype)
|
|
|
|
# Fun camera
|
|
if self.control_adapter is not None and fun_camera is not None:
|
|
fun_camera = self.control_adapter(fun_camera)
|
|
x = [u + v for u, v in zip(x, fun_camera)]
|
|
|
|
# SteadyDancer
|
|
if sdancer_enabled:
|
|
ref_x = y[0][4:, :1] # reuse I2V input as reference, slice mask off
|
|
msk = torch.ones(4, 1, H, W, device=ref_x.device) # new mask goes in middle
|
|
ref_x = [torch.concat([ref_x, msk, ref_x])]
|
|
ref_c = sdancer_cond[0][:, :1]
|
|
ref_c = [torch.concat([ref_c, msk * 0, ref_c])] # zero mask for cond ref
|
|
# Condition Fusion/Injection, Hierarchical Aggregation (2): x, fused condition, aligned condition
|
|
x = [self.patch_embedding_fuse(torch.cat([u[None], c[None], a[None]], 1)) for u, c, a in zip(x, condition_fused, condition_aligned)]
|
|
# Condition Augmentation: x_cond, ref_x, ref_c
|
|
ref_x = [self.patch_embedding(r.unsqueeze(0).float()).to(self.base_dtype) for r in ref_x]
|
|
ref_c = [self.patch_embedding_ref_c(r[:16].unsqueeze(0).float()).to(self.base_dtype) for r in ref_c]
|
|
F += ref_x[0].shape[2] + ref_c[0].shape[2] # update frame count for rope
|
|
x = [torch.cat([r, u, v], dim=2) for r, u, v in zip(x, ref_x, ref_c)]
|
|
seq_len = torch.tensor([u.flatten(2).transpose(1, 2).size(1) for u in x], dtype=torch.int32).max() # update seq len
|
|
|
|
# grid sizes and seq len
|
|
grid_sizes = torch.stack([torch.tensor(u.shape[2:], device=device, dtype=torch.long) for u in x])
|
|
original_grid_sizes = grid_sizes.clone()
|
|
x = [u.flatten(2).transpose(1, 2) for u in x]
|
|
self.original_seq_len = x[0].shape[1]
|
|
|
|
# SCAIL pose
|
|
if scail_input is not None:
|
|
scail_pose_latents = scail_input.get("pose_latent", None)
|
|
if scail_pose_latents is not None and scail_input['pose_start_percent'] <= current_step_percentage <= scail_input['pose_end_percent']:
|
|
scail_x = [self.patch_embedding_pose(u.unsqueeze(0).to(torch.float32)).to(x[0].dtype) for u in [scail_pose_latents]]
|
|
scail_x = [u.flatten(2).transpose(1, 2) * scail_input.get("pose_strength", 1) for u in scail_x]
|
|
x = [torch.cat([u, v], dim=1) for u, v in zip(x, scail_x)]
|
|
seq_len += scail_x[0].shape[1]
|
|
pose_frame_shape = scail_pose_latents.shape
|
|
|
|
seq_lens = torch.tensor([u.size(1) for u in x], dtype=torch.int32)
|
|
assert seq_lens.max() <= seq_len, f"max seq len {seq_lens.max()} exceeds provided seq_len {seq_len}"
|
|
|
|
cond_mask_weight = None
|
|
if self.trainable_cond_mask is not None:
|
|
cond_mask_weight = self.trainable_cond_mask.weight.to(x[0]).unsqueeze(1).unsqueeze(1)
|
|
|
|
if add_cond is not None:
|
|
add_cond = self.add_conv_in(add_cond.to(self.add_conv_in.weight.dtype)).to(x[0].dtype)
|
|
add_cond = add_cond.flatten(2).transpose(1, 2)
|
|
x[0] = x[0] + self.add_proj(add_cond)
|
|
if attn_cond is not None:
|
|
ref_frame_shape = attn_cond.shape
|
|
grid_sizes = torch.stack([torch.tensor([u[0] + 1, u[1], u[2]]) for u in grid_sizes]).to(grid_sizes.device)
|
|
attn_cond = self.attn_conv_in(attn_cond.to(self.attn_conv_in.weight.dtype)).to(x[0].dtype)
|
|
attn_cond = attn_cond.flatten(2).transpose(1, 2)
|
|
x[0] = torch.cat([x[0], attn_cond], dim=1)
|
|
seq_len += attn_cond.size(1)
|
|
for block in self.blocks:
|
|
block.self_attn.mask_map = MaskMap(video_token_num=seq_len, num_frame=F+1)
|
|
|
|
if self.ref_conv is not None and fun_ref is not None:
|
|
fun_ref = self.ref_conv(fun_ref.to(self.ref_conv.weight.dtype)).flatten(2).transpose(1, 2)
|
|
grid_sizes = torch.stack([torch.tensor([u[0] + 1, u[1], u[2]]) for u in grid_sizes]).to(grid_sizes.device)
|
|
seq_len += fun_ref.size(1)
|
|
F += 1
|
|
x = [torch.cat([_fun_ref.unsqueeze(0), u], dim=1) for _fun_ref, u in zip(fun_ref, x)]
|
|
|
|
end_ref_latent=None
|
|
if s2v_ref_latent is not None:
|
|
end_ref_latent = s2v_ref_latent.squeeze(0)
|
|
elif phantom_ref is not None:
|
|
end_ref_latent = phantom_ref
|
|
F += end_ref_latent.size(1)
|
|
if end_ref_latent is not None:
|
|
end_ref_latent_frames = end_ref_latent.size(1)
|
|
end_ref_latent = self.original_patch_embedding(end_ref_latent.unsqueeze(0).to(torch.float32)).to(x[0].dtype)
|
|
end_ref_latent = end_ref_latent.flatten(2).transpose(1, 2)
|
|
if cond_mask_weight is not None:
|
|
end_ref_latent = end_ref_latent + cond_mask_weight[1]
|
|
grid_sizes = torch.stack([torch.tensor([u[0] + end_ref_latent_frames, u[1], u[2]]) for u in grid_sizes]).to(grid_sizes.device)
|
|
end_ref_latent_seq_len = end_ref_latent.size(1)
|
|
seq_len += end_ref_latent_seq_len
|
|
x = [torch.cat([u, end_ref_latent.unsqueeze(0)], dim=1) for end_ref_latent, u in zip(end_ref_latent, x)]
|
|
|
|
|
|
x = torch.cat([torch.cat([u, u.new_zeros(1, seq_len - u.size(1), u.size(2))], dim=1) for u in x])
|
|
|
|
if self.trainable_cond_mask is not None:
|
|
x = x + cond_mask_weight[0]
|
|
|
|
# StandIn LoRA input
|
|
x_ip = None
|
|
freq_offset = 0
|
|
if standin_input is not None:
|
|
ip_image = standin_input["ip_image_latent"]
|
|
|
|
if ip_image.dim() == 6 and ip_image.shape[3] == 1:
|
|
ip_image = ip_image.squeeze(1)
|
|
|
|
ip_image_patch = self.original_patch_embedding(ip_image.to(x.device).float()).to(self.base_dtype)
|
|
f_ip, h_ip, w_ip = ip_image_patch.shape[2:]
|
|
x_ip = ip_image_patch.flatten(2).transpose(1, 2) # [B, N, D]
|
|
freq_offset = standin_input["freq_offset"]
|
|
|
|
# region rope freqs
|
|
if freqs is None and "comfy" in self.rope_func: #comfy rope
|
|
# Create cache key from all relevant parameters
|
|
cache_key = (
|
|
F, H, W,
|
|
attn_cond is not None,
|
|
tuple(ref_frame_shape) if ref_frame_shape is not None else None,
|
|
tuple(pose_frame_shape) if pose_frame_shape is not None else None,
|
|
self.rope_embedder.k,
|
|
tuple(ntk_alphas),
|
|
)
|
|
|
|
# Check cache using key comparison
|
|
if (self.cached_freqs is not None and
|
|
hasattr(self, 'cached_key') and
|
|
self.cached_key == cache_key):
|
|
freqs = self.cached_freqs
|
|
else:
|
|
log.info("Generating new RoPE frequencies")
|
|
freqs = self.rope_encode_comfy(
|
|
F, H, W,
|
|
freq_offset=freq_offset,
|
|
ntk_alphas=ntk_alphas,
|
|
ref_frame_shape=ref_frame_shape,
|
|
pose_frame_shape=pose_frame_shape,
|
|
device=x.device,
|
|
dtype=x.dtype
|
|
)
|
|
|
|
if s2v_ref_latent is not None:
|
|
freqs_ref = self.rope_encode_comfy(
|
|
s2v_ref_latent.shape[2],
|
|
s2v_ref_latent.shape[3],
|
|
s2v_ref_latent.shape[4],
|
|
t_start=max(30, F + 9),
|
|
device=x.device,
|
|
dtype=x.dtype
|
|
)
|
|
freqs = torch.cat([freqs, freqs_ref], dim=1)
|
|
|
|
# Store cache with key
|
|
self.cached_freqs = freqs
|
|
self.cached_key = cache_key
|
|
|
|
# Stand-In RoPE frequencies
|
|
if x_ip is not None:
|
|
# Generate RoPE frequencies for x_ip
|
|
h_len = (H + 1) // 2
|
|
w_len = (W + 1) // 2
|
|
ip_img_ids = torch.zeros((f_ip, h_ip, w_ip, 3), device=x.device, dtype=x.dtype)
|
|
ip_img_ids[:, :, :, 0] = ip_img_ids[:, :, :, 0] + torch.linspace(0, f_ip - 1, steps=f_ip, device=x.device, dtype=x.dtype).reshape(-1, 1, 1)
|
|
ip_img_ids[:, :, :, 1] = ip_img_ids[:, :, :, 1] + torch.linspace(h_len + freq_offset, h_len + freq_offset + h_ip - 1, steps=h_ip, device=x.device, dtype=x.dtype).reshape(1, -1, 1)
|
|
ip_img_ids[:, :, :, 2] = ip_img_ids[:, :, :, 2] + torch.linspace(w_len + freq_offset, w_len + freq_offset + w_ip - 1, steps=w_ip, device=x.device, dtype=x.dtype).reshape(1, 1, -1)
|
|
ip_img_ids = repeat(ip_img_ids, "t h w c -> b (t h w) c", b=1)
|
|
freqs_ip = self.rope_embedder(ip_img_ids).movedim(1, 2)
|
|
|
|
# EchoShot cross attn freqs
|
|
inner_c = None
|
|
if inner_t is not None:
|
|
d = self.dim // self.num_heads
|
|
self.cross_freqs = rope_params(100, d).to(device=x.device)
|
|
|
|
if s2v_ref_motion is not None:
|
|
motion_encoded, freqs_motion = self.frame_packer(s2v_ref_motion, self)
|
|
motion_encoded = motion_encoded + cond_mask_weight[2]
|
|
x = torch.cat([x, motion_encoded], dim=1)
|
|
freqs = torch.cat([freqs, freqs_motion], dim=1)
|
|
|
|
# time embeddings
|
|
if t.dim() == 2 and not self.is_longcat:
|
|
b, f = t.shape
|
|
expanded_timesteps = True
|
|
else:
|
|
expanded_timesteps = False
|
|
|
|
if self.zero_timestep:
|
|
t = torch.cat([t, torch.zeros([1], dtype=t.dtype, device=t.device)])
|
|
|
|
if hasattr(self, "time_projection"):
|
|
time_embed_dtype = self.time_embedding[0].weight.dtype
|
|
if time_embed_dtype not in [torch.float16, torch.bfloat16, torch.float32]:
|
|
time_embed_dtype = self.base_dtype
|
|
e = self.time_embedding(sinusoidal_embedding_1d(self.freq_dim, t.flatten()).to(time_embed_dtype)) # b, dim
|
|
e0 = self.time_projection(e).unflatten(1, (6, self.dim)) # b, 6, dim
|
|
if use_token_replace:
|
|
e_token_replace = self.time_embedding(sinusoidal_embedding_1d(self.freq_dim, t_token_replace.flatten()).to(time_embed_dtype)) # b, dim
|
|
e0_token_replace = self.time_projection(e_token_replace).unflatten(1, (6, self.dim)) # b, 6, dim
|
|
else:
|
|
time_embed_dtype = self.time_embedding.mlp[0].weight.dtype
|
|
if time_embed_dtype not in [torch.float16, torch.bfloat16, torch.float32]:
|
|
time_embed_dtype = self.base_dtype
|
|
if len(t.shape) == 1:
|
|
t = t.unsqueeze(1).expand(-1, F) # [B, T]
|
|
self.time_embedding.to(torch.float32)
|
|
e = e0 = self.time_embedding(t.float().flatten(), dtype=torch.float32).reshape(1, F, -1)
|
|
|
|
|
|
if self.audio_model is not None:
|
|
#if t.dim() == 1:
|
|
# t_ovi = t.unsqueeze(1).expand(t.size(0), seq_len_ovi)
|
|
if t.dim() == 2:
|
|
last_timestep = t[:, -1:]
|
|
padding = last_timestep.expand(t.size(0), seq_len_ovi - t.size(1))
|
|
t_ovi = torch.cat([t, padding], dim=1)
|
|
|
|
e_ovi = self.audio_model.time_embedding(sinusoidal_embedding_1d(self.audio_model.freq_dim, t_ovi.flatten()).to(time_embed_dtype)).unsqueeze(0) # b, dim
|
|
e0_ovi = self.audio_model.time_projection(e_ovi).unflatten(2, (6, self.dim)).movedim(1, 2) # B, seq_len, 6, dim
|
|
else:
|
|
e_ovi = self.audio_model.time_embedding(sinusoidal_embedding_1d(self.audio_model.freq_dim, t.flatten()).to(time_embed_dtype)) # b, dim
|
|
e0_ovi = self.audio_model.time_projection(e_ovi).unflatten(1, (6, self.dim)) # b, 6, dim
|
|
|
|
|
|
#S2V zero timestep
|
|
if self.zero_timestep:
|
|
e = e[:-1]
|
|
zero_e0 = e0[-1:]
|
|
e0 = e0[:-1]
|
|
e0 = torch.cat([
|
|
e0.unsqueeze(2),
|
|
zero_e0.unsqueeze(2).repeat(e0.size(0), 1, 1, 1)
|
|
], dim=2)
|
|
e0 = [e0, self.original_seq_len]
|
|
|
|
if x_ip is not None:
|
|
timestep_ip = torch.zeros_like(t) # [B] with 0s
|
|
t_ip = self.time_embedding(sinusoidal_embedding_1d(self.freq_dim, timestep_ip.flatten()).to(time_embed_dtype)) # b, dim )
|
|
e0_ip = self.time_projection(t_ip).unflatten(1, (6, self.dim))
|
|
|
|
if fps_embeds is not None:
|
|
fps_embeds = torch.tensor(fps_embeds, dtype=torch.long, device=device)
|
|
|
|
fps_emb = self.fps_embedding(fps_embeds).to(e0.dtype)
|
|
if expanded_timesteps:
|
|
e0 = e0 + self.fps_projection(fps_emb).unflatten(1, (6, self.dim)).repeat(t.shape[1], 1, 1)
|
|
else:
|
|
e0 = e0 + self.fps_projection(fps_emb).unflatten(1, (6, self.dim))
|
|
|
|
if expanded_timesteps:
|
|
e = e.view(b, f, 1, 1, self.dim).expand(b, f, grid_sizes[0][1], grid_sizes[0][2], self.dim)
|
|
e0 = e0.view(b, f, 1, 1, 6, self.dim).expand(b, f, grid_sizes[0][1], grid_sizes[0][2], 6, self.dim)
|
|
|
|
e = e.flatten(1, 3)
|
|
e0 = e0.flatten(1, 3)
|
|
|
|
e0 = e0.transpose(1, 2)
|
|
if not e0.is_contiguous():
|
|
e0 = e0.contiguous()
|
|
|
|
e = e.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
|
|
# clip vision embedding
|
|
clip_embed = None
|
|
if clip_fea is not None and hasattr(self, "img_emb"):
|
|
if self.offload_img_emb:
|
|
self.img_emb.to(self.main_device)
|
|
clip_embed = self.img_emb(clip_fea.to(self.main_device)) # bs x 257 x dim
|
|
if sdancer_input is not None:
|
|
clip_fea_c = sdancer_input.get("clip_fea_c", None)
|
|
if clip_fea_c is not None:
|
|
clip_embed += self.img_emb(clip_fea_c.to(self.main_device))
|
|
if self.offload_img_emb:
|
|
self.img_emb.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
|
|
#context (text embedding)
|
|
if hasattr(self, "text_embedding") and context != []:
|
|
text_embed_dtype = self.text_embedding[0].weight.dtype
|
|
if text_embed_dtype not in [torch.float16, torch.bfloat16, torch.float32]:
|
|
text_embed_dtype = self.base_dtype
|
|
if self.offload_txt_emb:
|
|
self.text_embedding.to(self.main_device)
|
|
|
|
if inner_t is not None:
|
|
if nag_context is not None:
|
|
raise NotImplementedError("nag_context is not supported with EchoShot")
|
|
inner_c = [[u.shape[0] for u in context]]
|
|
|
|
if self.audio_model is not None:
|
|
if is_uncond and ovi_negative_text_embeds is not None:
|
|
context_ovi = ovi_negative_text_embeds
|
|
else:
|
|
context_ovi = context
|
|
context_ovi = self.audio_model.text_embedding(
|
|
torch.stack([torch.cat([u, u.new_zeros(self.text_len - u.size(0), u.size(1))]) for u in context_ovi]).to(text_embed_dtype))
|
|
|
|
tokens = context[0].shape[0]
|
|
context = torch.stack([torch.cat([u, u.new_zeros(self.text_len - u.size(0), u.size(1))]) for u in context]).to(text_embed_dtype)
|
|
|
|
if add_text_emb is not None:
|
|
self.text_projection.to(self.main_device)
|
|
add_text_emb = self.text_projection(add_text_emb.to(self.text_projection[0].weight.dtype)).to(text_embed_dtype)
|
|
context = torch.cat([add_text_emb, context], dim=1)
|
|
context = self.text_embedding(context)
|
|
|
|
if self.is_longcat:
|
|
context[:, tokens:] = 0
|
|
|
|
# NAG
|
|
if nag_context is not None:
|
|
nag_context = self.text_embedding(
|
|
torch.stack([
|
|
torch.cat(
|
|
[u, u.new_zeros(self.text_len - u.size(0), u.size(1))])
|
|
for u in nag_context
|
|
]).to(text_embed_dtype))
|
|
|
|
if self.offload_txt_emb:
|
|
self.text_embedding.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
|
|
seq_chunks = max(context.shape[0], clip_embed.shape[0] if clip_embed is not None else 0)
|
|
chunked_self_attention = seq_chunks > 1 and current_step in self.video_attention_split_steps
|
|
else:
|
|
context = None
|
|
chunked_self_attention = False
|
|
seq_chunks = 0
|
|
|
|
# MultiTalk
|
|
if multitalk_audio is not None:
|
|
self.multitalk_audio_proj.to(self.main_device)
|
|
audio_cond = multitalk_audio.to(device=x.device, dtype=self.base_dtype)
|
|
first_frame_audio_emb_s = audio_cond[:, :1, ...]
|
|
latter_frame_audio_emb = audio_cond[:, 1:, ...]
|
|
latter_frame_audio_emb = rearrange(latter_frame_audio_emb, "b (n_t n) w s c -> b n_t n w s c", n=4)
|
|
middle_index = self.multitalk_audio_proj.seq_len // 2
|
|
latter_first_frame_audio_emb = latter_frame_audio_emb[:, :, :1, :middle_index+1, ...]
|
|
latter_first_frame_audio_emb = rearrange(latter_first_frame_audio_emb, "b n_t n w s c -> b n_t (n w) s c")
|
|
latter_last_frame_audio_emb = latter_frame_audio_emb[:, :, -1:, middle_index:, ...]
|
|
latter_last_frame_audio_emb = rearrange(latter_last_frame_audio_emb, "b n_t n w s c -> b n_t (n w) s c")
|
|
latter_middle_frame_audio_emb = latter_frame_audio_emb[:, :, 1:-1, middle_index:middle_index+1, ...]
|
|
latter_middle_frame_audio_emb = rearrange(latter_middle_frame_audio_emb, "b n_t n w s c -> b n_t (n w) s c")
|
|
latter_frame_audio_emb_s = torch.concat([latter_first_frame_audio_emb, latter_middle_frame_audio_emb, latter_last_frame_audio_emb], dim=2)
|
|
multitalk_audio_embedding = self.multitalk_audio_proj(first_frame_audio_emb_s, latter_frame_audio_emb_s)
|
|
human_num = len(multitalk_audio_embedding)
|
|
multitalk_audio_embedding = torch.concat(multitalk_audio_embedding.split(1), dim=2).to(self.base_dtype)
|
|
self.multitalk_audio_proj.to(self.offload_device)
|
|
|
|
# convert ref_target_masks to token_ref_target_masks
|
|
token_ref_target_masks = None
|
|
if ref_target_masks is not None:
|
|
ref_target_masks = ref_target_masks.unsqueeze(0).to(torch.float32)
|
|
token_ref_target_masks = nn.functional.interpolate(ref_target_masks, size=(H // 2, W // 2), mode='nearest')
|
|
token_ref_target_masks = token_ref_target_masks.squeeze(0)
|
|
token_ref_target_masks = (token_ref_target_masks > 0)
|
|
token_ref_target_masks = token_ref_target_masks.view(token_ref_target_masks.shape[0], -1)
|
|
token_ref_target_masks = token_ref_target_masks.to(device, self.base_dtype)
|
|
|
|
humo_audio_input = None
|
|
if humo_audio is not None:
|
|
humo_audio_input = self.audio_proj(humo_audio.unsqueeze(0)).permute(0, 3, 1, 2)
|
|
|
|
humo_audio_seq_len = torch.tensor(humo_audio.shape[2] * humo_audio_input.shape[3], device=device)
|
|
humo_audio_input = humo_audio_input.flatten(2).transpose(1, 2) # 1, t*32, 1536
|
|
pad_len = int(humo_audio_seq_len - humo_audio_input.size(1))
|
|
if pad_len > 0:
|
|
humo_audio_input = torch.nn.functional.pad(humo_audio_input, (0, 0, 0, pad_len))
|
|
|
|
should_calc = True
|
|
#TeaCache
|
|
if self.enable_teacache and self.teacache_start_step <= current_step <= self.teacache_end_step:
|
|
accumulated_rel_l1_distance = torch.tensor(0.0, dtype=torch.float32, device=device)
|
|
if pred_id is None:
|
|
pred_id = self.teacache_state.new_prediction(cache_device=self.cache_device)
|
|
should_calc = True
|
|
else:
|
|
previous_modulated_input = self.teacache_state.get(pred_id)['previous_modulated_input']
|
|
previous_modulated_input = previous_modulated_input.to(device)
|
|
previous_residual = self.teacache_state.get(pred_id)['previous_residual']
|
|
accumulated_rel_l1_distance = self.teacache_state.get(pred_id)['accumulated_rel_l1_distance']
|
|
|
|
if self.teacache_use_coefficients:
|
|
rescale_func = np.poly1d(self.teacache_coefficients[self.teacache_mode])
|
|
temb = e if self.teacache_mode == 'e' else e0
|
|
accumulated_rel_l1_distance += rescale_func((
|
|
(temb.to(device) - previous_modulated_input).abs().mean() / previous_modulated_input.abs().mean()
|
|
).cpu().item())
|
|
del temb
|
|
else:
|
|
temb_relative_l1 = relative_l1_distance(previous_modulated_input, e0)
|
|
accumulated_rel_l1_distance = accumulated_rel_l1_distance.to(e0.device) + temb_relative_l1
|
|
del temb_relative_l1
|
|
|
|
|
|
if accumulated_rel_l1_distance < self.rel_l1_thresh:
|
|
should_calc = False
|
|
else:
|
|
should_calc = True
|
|
accumulated_rel_l1_distance = torch.tensor(0.0, dtype=torch.float32, device=device)
|
|
accumulated_rel_l1_distance = accumulated_rel_l1_distance.to(self.cache_device)
|
|
|
|
previous_modulated_input = e.to(self.cache_device).clone() if (self.teacache_use_coefficients and self.teacache_mode == 'e') else e0.to(self.cache_device).clone()
|
|
|
|
if not should_calc:
|
|
x = x.to(previous_residual.dtype) + previous_residual.to(x.device)
|
|
self.teacache_state.update(
|
|
pred_id,
|
|
accumulated_rel_l1_distance=accumulated_rel_l1_distance,
|
|
)
|
|
self.teacache_state.get(pred_id)['skipped_steps'].append(current_step)
|
|
|
|
# MagCache
|
|
if self.enable_magcache and self.magcache_start_step <= current_step <= self.magcache_end_step:
|
|
if pred_id is None:
|
|
pred_id = self.magcache_state.new_prediction(cache_device=self.cache_device)
|
|
should_calc = True
|
|
else:
|
|
accumulated_ratio = self.magcache_state.get(pred_id)['accumulated_ratio']
|
|
accumulated_err = self.magcache_state.get(pred_id)['accumulated_err']
|
|
accumulated_steps = self.magcache_state.get(pred_id)['accumulated_steps']
|
|
|
|
calibration_len = len(self.magcache_ratios) // 2
|
|
cur_mag_ratio = self.magcache_ratios[int((current_step*(calibration_len/total_steps)))]
|
|
|
|
accumulated_ratio *= cur_mag_ratio
|
|
accumulated_err += np.abs(1-accumulated_ratio)
|
|
accumulated_steps += 1
|
|
|
|
self.magcache_state.update(
|
|
pred_id,
|
|
accumulated_ratio=accumulated_ratio,
|
|
accumulated_steps=accumulated_steps,
|
|
accumulated_err=accumulated_err
|
|
)
|
|
|
|
if accumulated_err<=self.magcache_thresh and accumulated_steps<=self.magcache_K:
|
|
should_calc = False
|
|
x += self.magcache_state.get(pred_id)['residual_cache'].to(x.device)
|
|
self.magcache_state.get(pred_id)['skipped_steps'].append(current_step)
|
|
else:
|
|
should_calc = True
|
|
self.magcache_state.update(
|
|
pred_id,
|
|
accumulated_ratio=1.0,
|
|
accumulated_steps=0,
|
|
accumulated_err=0
|
|
)
|
|
|
|
# EasyCache
|
|
if self.enable_easycache and self.easycache_start_step <= current_step <= self.easycache_end_step:
|
|
if pred_id is None:
|
|
pred_id = self.easycache_state.new_prediction(cache_device=self.cache_device)
|
|
should_calc = True
|
|
else:
|
|
state = self.easycache_state.get(pred_id)
|
|
previous_raw_input = state.get('previous_raw_input')
|
|
previous_raw_output = state.get('previous_raw_output')
|
|
cache = state.get('cache')
|
|
cache_ovi = state.get('cache_ovi') if self.audio_model is not None else None
|
|
accumulated_error = state.get('accumulated_error')
|
|
k = state.get('k', 1)
|
|
|
|
if previous_raw_input is not None and previous_raw_output is not None:
|
|
raw_input = x.clone()
|
|
# Calculate input change
|
|
raw_input_change = (raw_input - previous_raw_input.to(raw_input.device)).abs().mean()
|
|
|
|
output_norm = (previous_raw_output.to(x.device)).abs().mean()
|
|
|
|
combined_pred_change = (raw_input_change / output_norm) * k
|
|
|
|
accumulated_error += combined_pred_change
|
|
|
|
# Predict output change
|
|
if accumulated_error < self.easycache_thresh:
|
|
should_calc = False
|
|
x = raw_input + cache.to(x.device)
|
|
if cache_ovi is not None:
|
|
x_ovi = x_ovi + cache_ovi.to(x_ovi.device)
|
|
state['skipped_steps'].append(current_step)
|
|
else:
|
|
should_calc = True
|
|
else:
|
|
should_calc = True
|
|
|
|
x = x.to(self.base_dtype)
|
|
if isinstance(e0, list):
|
|
e0 = [item.to(self.base_dtype) if torch.is_tensor(item) else item for item in e0]
|
|
else:
|
|
e0 = e0.to(self.base_dtype)
|
|
|
|
if self.enable_easycache:
|
|
original_x = x.clone().to(self.cache_device)
|
|
if x_ovi is not None:
|
|
original_x_ovi = x_ovi.clone().to(self.cache_device)
|
|
if should_calc:
|
|
if self.enable_teacache or self.enable_magcache:
|
|
original_x = x.clone().to(self.cache_device)
|
|
|
|
if hasattr(self, "dwpose_embedding") and unianim_data is not None:
|
|
if unianim_data['start_percent'] <= current_step_percentage <= unianim_data['end_percent']:
|
|
dwpose_emb = rearrange(unianim_data['dwpose'], 'b c f h w -> b (f h w) c').contiguous()
|
|
x.add_(dwpose_emb, alpha=unianim_data['strength'])
|
|
|
|
# arguments
|
|
kwargs = dict(
|
|
e=e0,
|
|
seq_lens=seq_lens,
|
|
grid_sizes=grid_sizes,
|
|
freqs=freqs,
|
|
context=context,
|
|
clip_embed=clip_embed,
|
|
current_step=torch.tensor(current_step),
|
|
last_step=torch.tensor(last_step, dtype=torch.bool),
|
|
chunked_self_attention=chunked_self_attention,
|
|
seq_chunks=seq_chunks,
|
|
camera_embed=camera_embed,
|
|
audio_proj=audio_proj,
|
|
num_latent_frames = F,
|
|
original_seq_len=self.original_seq_len,
|
|
enhance_enabled=enhance_enabled,
|
|
audio_scale=audio_scale,
|
|
nag_params=nag_params,
|
|
nag_context=nag_context if not is_uncond else None,
|
|
multitalk_audio_embedding=multitalk_audio_embedding if multitalk_audio is not None else None,
|
|
ref_target_masks=token_ref_target_masks if multitalk_audio is not None else None,
|
|
human_num=human_num if multitalk_audio is not None else 0,
|
|
inner_t=inner_t, inner_c=inner_c,
|
|
cross_freqs=self.cross_freqs if inner_t is not None and not is_uncond else None,
|
|
freqs_ip=freqs_ip if x_ip is not None else None,
|
|
e_ip=e0_ip if x_ip is not None else None,
|
|
adapter_proj=adapter_proj,
|
|
ip_scale=ip_scale,
|
|
reverse_time=reverse_time,
|
|
mtv_motion_tokens=mtv_motion_tokens, mtv_motion_rotary_emb=mtv_motion_rotary_emb, mtv_strength=mtv_strength, mtv_freqs=mtv_freqs,
|
|
humo_audio_input=humo_audio_input,
|
|
humo_audio_scale=humo_audio_scale,
|
|
lynx_x_ip=lynx_x_ip,
|
|
lynx_ip_scale=lynx_ip_scale,
|
|
lynx_ref_scale=lynx_ref_scale,
|
|
num_cond_latents=num_cond_latents,
|
|
onetoall_ref_scale=onetoall_ref_scale,
|
|
e_tr=e0_token_replace if use_token_replace else None,
|
|
tr_start=token_replace_start,
|
|
tr_num=replace_token_num,
|
|
)
|
|
if self.audio_model is not None:
|
|
kwargs['e_ovi'] = e0_ovi.to(self.base_dtype)
|
|
kwargs['context_ovi'] = context_ovi
|
|
kwargs['grid_sizes_ovi'] = grid_sizes_ovi
|
|
kwargs['seq_lens_ovi'] = seq_lens_ovi
|
|
kwargs['freqs_ovi'] = freqs_ovi
|
|
|
|
|
|
if vace_data is not None:
|
|
vace_hint_list = []
|
|
vace_scale_list = []
|
|
if isinstance(vace_data[0], dict):
|
|
for data in vace_data:
|
|
if (data["start"] <= current_step_percentage <= data["end"]) or \
|
|
(data["end"] > 0 and current_step == 0 and current_step_percentage >= data["start"]):
|
|
|
|
vace_hints = self.forward_vace(x, data["context"], data["seq_len"], kwargs)
|
|
vace_hint_list.append(vace_hints)
|
|
vace_scale_list.append(data["scale"][current_step])
|
|
else:
|
|
vace_hints = self.forward_vace(x, vace_data, seq_len, kwargs)
|
|
vace_hint_list.append(vace_hints)
|
|
vace_scale_list.append(1.0)
|
|
|
|
kwargs['vace_hints'] = vace_hint_list
|
|
kwargs['vace_context_scale'] = vace_scale_list
|
|
|
|
#uni3c controlnet
|
|
uni3c_controlnet_states = None
|
|
if uni3c_data is not None:
|
|
if (uni3c_data["start"] <= current_step_percentage <= uni3c_data["end"]) or \
|
|
(uni3c_data["end"] > 0 and current_step == 0 and current_step_percentage >= uni3c_data["start"]):
|
|
self.uni3c_controlnet.to(self.main_device)
|
|
with torch.autocast(device_type=mm.get_autocast_device(device), dtype=self.base_dtype, enabled=True):
|
|
uni3c_controlnet_states = self.uni3c_controlnet(
|
|
render_latent=render_latent.to(self.main_device, self.uni3c_controlnet.dtype),
|
|
render_mask=uni3c_data["render_mask"],
|
|
camera_embedding=uni3c_data["camera_embedding"],
|
|
temb=e.to(self.main_device),
|
|
device=self.offload_device)
|
|
self.uni3c_controlnet.to(self.offload_device)
|
|
|
|
# Asynchronous block offloading with CUDA streams and events
|
|
if torch.cuda.is_available():
|
|
cuda_stream = None #torch.cuda.Stream(device=device, priority=0) # todo causes issues on some systems
|
|
events = [torch.cuda.Event() for _ in self.blocks]
|
|
swap_start_idx = len(self.blocks) - self.blocks_to_swap if self.blocks_to_swap > 0 else len(self.blocks)
|
|
else:
|
|
cuda_stream = None
|
|
events = None
|
|
swap_start_idx = len(self.blocks)
|
|
|
|
# lynx ref
|
|
if lynx_ref_buffer is None and lynx_ref_feature_extractor:
|
|
lynx_ref_buffer = {}
|
|
|
|
for b, block in enumerate(self.blocks):
|
|
mm.throw_exception_if_processing_interrupted()
|
|
block_idx = f"{b:02d}"
|
|
if lynx_ref_buffer is not None and not lynx_ref_feature_extractor:
|
|
lynx_ref_feature = lynx_ref_buffer.get(block_idx, None)
|
|
else:
|
|
lynx_ref_feature = None
|
|
# FlashVSR
|
|
if flashvsr_LQ_latent is not None and b < len(flashvsr_LQ_latent):
|
|
x += flashvsr_LQ_latent[b].to(x) * flashvsr_strength
|
|
# Prefetch blocks if enabled
|
|
if self.prefetch_blocks > 0:
|
|
for prefetch_offset in range(1, self.prefetch_blocks + 1):
|
|
prefetch_idx = b + prefetch_offset
|
|
if prefetch_idx < len(self.blocks) and self.blocks_to_swap > 0 and prefetch_idx >= swap_start_idx:
|
|
context_mgr = torch.cuda.stream(cuda_stream) if torch.cuda.is_available() else nullcontext()
|
|
with context_mgr:
|
|
self.blocks[prefetch_idx].to(self.main_device, non_blocking=self.use_non_blocking)
|
|
if events is not None:
|
|
events[prefetch_idx].record(cuda_stream)
|
|
if self.block_swap_debug:
|
|
transfer_start = time.perf_counter()
|
|
# Wait for block to be ready
|
|
if b >= swap_start_idx and self.blocks_to_swap > 0:
|
|
if self.prefetch_blocks > 0 and events is not None:
|
|
if not events[b].query():
|
|
events[b].synchronize()
|
|
block.to(self.main_device)
|
|
if self.block_swap_debug:
|
|
transfer_end = time.perf_counter()
|
|
transfer_time = transfer_end - transfer_start
|
|
compute_start = time.perf_counter()
|
|
#skip layer guidance
|
|
if self.slg_blocks is not None:
|
|
if b in self.slg_blocks and is_uncond:
|
|
if self.slg_start_percent <= current_step_percentage <= self.slg_end_percent:
|
|
continue
|
|
|
|
x_onetoall_ref = None
|
|
if onetoall_ref_block_samples is not None:
|
|
interval_ref = len(self.blocks) / len(onetoall_ref_block_samples)
|
|
interval_ref = int(np.ceil(interval_ref))
|
|
x_onetoall_ref = onetoall_ref_block_samples[b // interval_ref]
|
|
|
|
# ---run block----#
|
|
x, x_ip, lynx_ref_feature, x_ovi = block(x, x_ip=x_ip, lynx_ref_feature=lynx_ref_feature, x_ovi=x_ovi, x_onetoall_ref=x_onetoall_ref, onetoall_freqs=onetoall_freqs, **kwargs)
|
|
# ---post block----#
|
|
|
|
if self.audio_injector is not None and s2v_audio_input is not None:
|
|
x = self.audio_injector_forward(b, x, merged_audio_emb, scale=s2v_audio_scale) #s2v
|
|
if block.has_face_fuser_block and motion_vec is not None:
|
|
x = self.wananimate_forward(block, x, motion_vec, strength=wananim_face_strength)
|
|
if self.block_swap_debug:
|
|
compute_end = time.perf_counter()
|
|
compute_time = compute_end - compute_start
|
|
to_cpu_transfer_start = time.perf_counter()
|
|
if b >= swap_start_idx and self.blocks_to_swap > 0:
|
|
block.to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
if self.block_swap_debug:
|
|
to_cpu_transfer_end = time.perf_counter()
|
|
to_cpu_transfer_time = to_cpu_transfer_end - to_cpu_transfer_start
|
|
log.info(f"Block {b}: transfer_time={transfer_time:.4f}s, compute_time={compute_time:.4f}s, to_cpu_transfer_time={to_cpu_transfer_time:.4f}s")
|
|
# lynx ref
|
|
if lynx_ref_feature_extractor:
|
|
if b in lynx_ref_blocks_to_use:
|
|
log.info(f"storing to lynx ref buffer for block {block_idx}")
|
|
lynx_ref_buffer[block_idx] = lynx_ref_feature
|
|
#uni3c controlnet
|
|
if uni3c_controlnet_states is not None and b < len(uni3c_controlnet_states):
|
|
x[:, :self.original_seq_len] += uni3c_controlnet_states[b].to(x) * uni3c_data["controlnet_weight"]
|
|
#controlnet
|
|
if (controlnet is not None) and (b % controlnet["controlnet_stride"] == 0) and (b // controlnet["controlnet_stride"] < len(controlnet["controlnet_states"])):
|
|
x[:, :self.original_seq_len] += controlnet["controlnet_states"][b // controlnet["controlnet_stride"]].to(x) * controlnet["controlnet_weight"]
|
|
# One-to-All-Animation controlnet
|
|
if onetoall_control_enabled:
|
|
if prev_x is not None and (b - 1) < len(self.controlnet.blocks):
|
|
#tqdm.write(f"Applying One-to-All ControlNet at block {b}")
|
|
if b == 1:
|
|
ctrl_in = prev_x + controlnet_tokens
|
|
elif prev_control is not None:
|
|
ctrl_in = prev_control
|
|
|
|
self.controlnet.blocks[b - 1].to(self.main_device)
|
|
control_out = self.controlnet.blocks[b - 1](ctrl_in, e0, seq_lens, freqs, e_tr=e0_token_replace, tr_num=replace_token_num,tr_start=token_replace_start, split_rope=False)
|
|
self.controlnet.blocks[b - 1].to(self.offload_device, non_blocking=self.use_non_blocking)
|
|
prev_control = control_out
|
|
|
|
control_out_proj = self.controlnet_zero[b - 1](control_out)
|
|
x = x + control_out_proj * one_to_all_controlnet_strength
|
|
if b < len(self.controlnet.blocks): # Store prev_x only while controlnet is active
|
|
prev_x = x
|
|
elif b == len(self.controlnet.blocks): # Controlnet done, free memory
|
|
prev_x = None
|
|
prev_control = None
|
|
if controlnet_tokens is not None:
|
|
del controlnet_tokens
|
|
controlnet_tokens = None
|
|
mm.soft_empty_cache()
|
|
|
|
if lynx_ref_feature_extractor:
|
|
return lynx_ref_buffer
|
|
|
|
if self.enable_teacache and (self.teacache_start_step <= current_step <= self.teacache_end_step) and pred_id is not None:
|
|
self.teacache_state.update(
|
|
pred_id,
|
|
previous_residual=(x.to(original_x.device) - original_x),
|
|
accumulated_rel_l1_distance=accumulated_rel_l1_distance,
|
|
previous_modulated_input=previous_modulated_input
|
|
)
|
|
elif self.enable_magcache and (self.magcache_start_step <= current_step <= self.magcache_end_step) and pred_id is not None:
|
|
self.magcache_state.update(
|
|
pred_id,
|
|
residual_cache=(x.to(original_x.device) - original_x)
|
|
)
|
|
elif self.enable_easycache and (self.easycache_start_step <= current_step <= self.easycache_end_step) and pred_id is not None:
|
|
x_out = x.clone().to(original_x.device)
|
|
output_change = (x_out - original_x).abs().mean()
|
|
input_change = (original_x - x_out).abs().mean()
|
|
self.easycache_state.update(
|
|
pred_id,
|
|
previous_raw_input=original_x,
|
|
previous_raw_output=x_out,
|
|
cache=x.to(original_x.device) - original_x,
|
|
k = output_change / input_change,
|
|
accumulated_error = 0.0,
|
|
cache_ovi = x_ovi.clone().to(original_x.device) - original_x_ovi if x_ovi is not None else None
|
|
)
|
|
|
|
|
|
|
|
if self.enable_easycache and (self.easycache_start_step <= current_step <= self.easycache_end_step) and pred_id is not None:
|
|
self.easycache_state.update(
|
|
pred_id,
|
|
previous_raw_output=x.clone(),
|
|
)
|
|
|
|
if self.ref_conv is not None and fun_ref is not None:
|
|
fun_ref_length = fun_ref.size(1)
|
|
x = x[:, fun_ref_length:]
|
|
#grid_sizes = torch.stack([torch.tensor([u[0] - 1, u[1], u[2]]) for u in grid_sizes]).to(grid_sizes.device)
|
|
|
|
if end_ref_latent is not None:
|
|
end_ref_latent_length = end_ref_latent.size(1)
|
|
x = x[:, :-end_ref_latent_length]
|
|
#grid_sizes = torch.stack([torch.tensor([u[0] - end_ref_latent_frames, u[1], u[2]]) for u in grid_sizes]).to(grid_sizes.device)
|
|
|
|
#if attn_cond is not None:
|
|
# x = x[:, :self.original_seq_len]
|
|
#grid_sizes = torch.stack([torch.tensor([u[0] - 1, u[1], u[2]]) for u in grid_sizes]).to(grid_sizes.device)
|
|
|
|
|
|
x = x[:, :self.original_seq_len]
|
|
|
|
x = self.head(x, e.to(x.device), temp_length=F,
|
|
e_tr=e_token_replace.to(x.device) if use_token_replace else None, tr_start=token_replace_start, tr_num=replace_token_num)
|
|
|
|
if x_ovi is not None:
|
|
x_ovi = self.audio_model.head(x_ovi, e_ovi.to(x_ovi.device))
|
|
grid_sizes_ovi = [gs[0] for gs in grid_sizes_ovi]
|
|
assert len(x) == len(grid_sizes_ovi)
|
|
x_ovi = [u[:gs] for u, gs in zip(x_ovi, grid_sizes_ovi)]
|
|
x_ovi = [u.float() for u in x_ovi]
|
|
|
|
x = self.unpatchify(x, original_grid_sizes)
|
|
x = [u[:, prefix_frames:suffix_frames, ...].float() for u in x]
|
|
return (x, x_ovi, pred_id) if pred_id is not None else (x, x_ovi, None)
|
|
|
|
def unpatchify(self, x, grid_sizes):
|
|
r"""
|
|
Reconstruct video tensors from patch embeddings.
|
|
|
|
Args:
|
|
x (List[Tensor]):
|
|
List of patchified features, each with shape [L, C_out * prod(patch_size)]
|
|
grid_sizes (Tensor):
|
|
Original spatial-temporal grid dimensions before patching,
|
|
shape [B, 3] (3 dimensions correspond to F_patches, H_patches, W_patches)
|
|
|
|
Returns:
|
|
List[Tensor]:
|
|
Reconstructed video tensors with shape [C_out, F, H / 8, W / 8]
|
|
"""
|
|
|
|
c = self.out_dim
|
|
out = []
|
|
for u, v in zip(x, grid_sizes.tolist()):
|
|
u = u[: math.prod(v)].view(*v, *self.patch_size, c)
|
|
u = torch.einsum("fhwpqrc->cfphqwr", u)
|
|
u = u.reshape(c, *[i * j for i, j in zip(v, self.patch_size)])
|
|
out.append(u)
|
|
return out
|