Fix attention patch leak and shape detection in Wan Uni3C controlnet

This commit is contained in:
drozbay 2026-07-04 12:14:18 -06:00
parent 2c01c141b3
commit ac8122d089
2 changed files with 33 additions and 20 deletions

View File

@ -45,9 +45,9 @@ class Uni3CAttentionBlock(nn.Module):
operations.Linear(dim, ffn_dim, device=device, dtype=dtype), nn.GELU(approximate='tanh'),
operations.Linear(ffn_dim, dim, device=device, dtype=dtype))
def forward(self, x, temb, freqs, transformer_options={}):
def forward(self, x, temb, freqs):
norm_x, gate_msa = self.norm1(x, temb)
x = x + gate_msa * self.self_attn(norm_x, freqs, transformer_options=transformer_options)
x = x + gate_msa * self.self_attn(norm_x, freqs)
norm_x, gate_ff = self.norm2(x, temb)
x = x + gate_ff * self.ffn(norm_x)
return x
@ -59,14 +59,10 @@ class MaskCamEmbed(nn.Module):
add_channels=7,
mid_channels=256,
conv_out_dim=5120,
interp=False,
device=None, dtype=None, operations=None
):
super().__init__()
if interp:
self.mask_padding = [0, 0, 0, 0, 3, 3] # first and last frame conditioning
else:
self.mask_padding = [0, 0, 0, 0, 3, 0] # first frame conditioning
self.mask_padding = [0, 0, 0, 0, 3, 0] # first frame conditioning
self.mask_proj = nn.Sequential(
operations.Conv3d(add_channels, mid_channels, kernel_size=(4, 8, 8), stride=(4, 8, 8), device=device, dtype=dtype),
operations.GroupNorm(mid_channels // 8, mid_channels, device=device, dtype=dtype),
@ -129,6 +125,7 @@ class WanUni3CControlnet(nn.Module):
return freqs
def process_input(self, control_input, render_mask=None, camera_embedding=None):
# render_mask/camera_embedding are the checkpoint's extra conditioning path, not wired up yet
hidden = self.controlnet_patch_embedding(control_input.float()).to(control_input.dtype)
t_len, h_len, w_len = hidden.shape[2:]
freqs = self.rope_encode(t_len, h_len, w_len, device=hidden.device, dtype=hidden.dtype)
@ -146,7 +143,7 @@ class WanUni3CControlnet(nn.Module):
hidden = self.proj_in(hidden)
return hidden, freqs
def forward_block(self, block_index, hidden, temb, freqs, transformer_options={}):
hidden = self.controlnet_blocks[block_index](hidden, temb, freqs, transformer_options=transformer_options)
def forward_block(self, block_index, hidden, temb, freqs):
hidden = self.controlnet_blocks[block_index](hidden, temb, freqs)
residual = self.proj_out[block_index](hidden)
return hidden, residual

View File

@ -275,13 +275,18 @@ class ModelPatchLoader:
sd = converted_sd
num_layers = sum(1 for k in sd if k.startswith("proj_out.") and k.endswith(".weight"))
conv_out_dim = sd["controlnet_patch_embedding.weight"].shape[0]
if "proj_in.weight" in sd:
dim = sd["proj_in.weight"].shape[0]
else:
dim = conv_out_dim
model = comfy.ldm.wan.uni3c.WanUni3CControlnet(
in_channels=sd["controlnet_patch_embedding.weight"].shape[1],
conv_out_dim=sd["controlnet_patch_embedding.weight"].shape[0],
dim=sd["proj_out.0.weight"].shape[1],
conv_out_dim=conv_out_dim,
dim=dim,
ffn_dim=sd["controlnet_blocks.0.ffn.0.bias"].shape[0],
num_layers=num_layers,
time_embed_dim=sd["controlnet_blocks.0.norm1.linear.weight"].shape[1],
time_embed_dim=conv_out_dim,
out_proj_dim=sd["proj_out.0.weight"].shape[0],
add_channels=sd["controlnet_mask_embedding.mask_proj.0.weight"].shape[1],
mid_channels=sd["controlnet_mask_embedding.mask_proj.0.weight"].shape[0],
@ -548,6 +553,7 @@ class WanUni3CCnetPatch:
self.strength = strength
self.sigma_start = sigma_start
self.sigma_end = sigma_end
self.prepared_render = None
self.temp_data = None
def build_controlnet_input(self, x, dtype):
@ -558,9 +564,13 @@ class WanUni3CCnetPatch:
pad_shape[1] = 20 - hidden.shape[1]
hidden = torch.cat([hidden, torch.zeros(pad_shape, dtype=hidden.dtype, device=hidden.device)], dim=1)
render = self.render_latent.to(device=hidden.device, dtype=hidden.dtype)
if render.shape[2:] != hidden.shape[2:]:
render = torch.nn.functional.interpolate(render, size=hidden.shape[2:], mode="trilinear", align_corners=False)
render = self.prepared_render
if render is None or render.shape[2:] != hidden.shape[2:] or render.device != hidden.device or render.dtype != dtype:
render = self.render_latent.to(device=hidden.device)
if render.shape[2:] != hidden.shape[2:]:
render = torch.nn.functional.interpolate(render, size=hidden.shape[2:], mode="trilinear", align_corners=False)
render = render.to(dtype)
self.prepared_render = render
return torch.cat([hidden, render], dim=1)
def __call__(self, kwargs):
@ -577,19 +587,24 @@ class WanUni3CCnetPatch:
if sigma > self.sigma_start or sigma < self.sigma_end:
active = False
if active:
controlnet_input = self.build_controlnet_input(kwargs.get("x"), img.dtype)
temb = kwargs.get("vec")[:1]
if temb.ndim == 3:
temb = temb[:, 0]
hidden, freqs = self.model_patch.model.process_input(controlnet_input)
model = self.model_patch.model
cnet_dim = model.controlnet_blocks[0].norm1.linear.in_features
if temb.shape[-1] != cnet_dim:
raise RuntimeError("This Uni3C controlnet expects a Wan model with dim {}, the loaded model has dim {}".format(cnet_dim, temb.shape[-1]))
controlnet_input = self.build_controlnet_input(kwargs.get("x"), img.dtype)
hidden, freqs = model.process_input(controlnet_input)
self.temp_data = (hidden, temb.to(img.dtype), freqs)
if self.temp_data is not None and block_index < self.model_patch.model.num_layers:
num_layers = self.model_patch.model.num_layers
if self.temp_data is not None and block_index < num_layers:
hidden, temb, freqs = self.temp_data
hidden, residual = self.model_patch.model.forward_block(block_index, hidden, temb, freqs, transformer_options=transformer_options)
hidden, residual = self.model_patch.model.forward_block(block_index, hidden, temb, freqs)
img_offset = kwargs.get("img_offset", 0)
img[:, img_offset:img_offset + residual.shape[1]] += residual.to(img.dtype) * self.strength
if block_index >= self.model_patch.model.num_layers - 1:
if block_index >= num_layers - 1:
self.temp_data = None
else:
self.temp_data = (hidden, temb, freqs)
@ -599,6 +614,7 @@ class WanUni3CCnetPatch:
def to(self, device_or_dtype):
if isinstance(device_or_dtype, torch.device):
self.render_latent = self.render_latent.to(device_or_dtype)
self.prepared_render = None
self.temp_data = None
return self