# SPDX-License-Identifier: Apache-2.0 """StableDiffusion3 Transformer model implementation. NOTE: This initial implementation uses diffusers' JointTransformerBlock directly. A native SGLang attention implementation is needed for FlashAttention, TP/SP, quantization, and LoRA support. """ from typing import Any import torch import torch.nn as nn from diffusers.models.attention import JointTransformerBlock from diffusers.models.embeddings import CombinedTimestepTextProjEmbeddings, PatchEmbed from diffusers.models.normalization import AdaLayerNormContinuous from sglang.multimodal_gen.configs.models.dits.stablediffusion3 import ( StableDiffusion3TransformerConfig, ) from sglang.multimodal_gen.runtime.managers.memory_managers.layerwise_offload import ( LayerwiseOffloadableModuleMixin, ) from sglang.multimodal_gen.runtime.models.dits.base import CachableDiT from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger logger = init_logger(__name__) class SD3Transformer2DModel(CachableDiT, LayerwiseOffloadableModuleMixin): _supports_gradient_checkpointing = True _no_split_modules = ["JointTransformerBlock"] _skip_layerwise_casting_patterns = ["pos_embed", "norm"] layer_names = ["transformer_blocks"] def __init__( self, config: StableDiffusion3TransformerConfig, hf_config: dict[str, Any] | None = None, quant_config=None, ): super().__init__(config=config, hf_config=hf_config) self.config = config arch_config = config.arch_config sample_size = arch_config.sample_size patch_size = arch_config.patch_size in_channels = arch_config.in_channels num_layers = arch_config.num_layers attention_head_dim = arch_config.attention_head_dim num_attention_heads = arch_config.num_attention_heads joint_attention_dim = arch_config.joint_attention_dim caption_projection_dim = arch_config.caption_projection_dim pooled_projection_dim = arch_config.pooled_projection_dim out_channels = arch_config.out_channels pos_embed_max_size = arch_config.pos_embed_max_size dual_attention_layers = arch_config.dual_attention_layers qk_norm = arch_config.qk_norm self.out_channels = out_channels if out_channels is not None else in_channels self.inner_dim = num_attention_heads * attention_head_dim self.patch_size = patch_size self.pos_embed = PatchEmbed( height=sample_size, width=sample_size, patch_size=patch_size, in_channels=in_channels, embed_dim=self.inner_dim, pos_embed_max_size=pos_embed_max_size, ) self.time_text_embed = CombinedTimestepTextProjEmbeddings( embedding_dim=self.inner_dim, pooled_projection_dim=pooled_projection_dim ) self.context_embedder = nn.Linear(joint_attention_dim, caption_projection_dim) self.transformer_blocks = nn.ModuleList( [ JointTransformerBlock( dim=self.inner_dim, num_attention_heads=num_attention_heads, attention_head_dim=attention_head_dim, context_pre_only=i == num_layers - 1, qk_norm=qk_norm, use_dual_attention=i in dual_attention_layers, ) for i in range(num_layers) ] ) self.norm_out = AdaLayerNormContinuous( self.inner_dim, self.inner_dim, elementwise_affine=False, eps=1e-6 ) self.proj_out = nn.Linear( self.inner_dim, patch_size * patch_size * self.out_channels, bias=True ) self.gradient_checkpointing = False def forward( self, hidden_states: torch.Tensor, encoder_hidden_states: torch.Tensor | None = None, pooled_projections: torch.Tensor | None = None, timestep: torch.LongTensor | None = None, block_controlnet_hidden_states: list | None = None, guidance: torch.Tensor | None = None, joint_attention_kwargs: dict[str, Any] | None = None, skip_layers: list[int] | None = None, ) -> torch.Tensor: if encoder_hidden_states is None: raise ValueError("encoder_hidden_states must be provided.") if pooled_projections is None: raise ValueError("pooled_projections must be provided.") encoder_embeddings = encoder_hidden_states height, width = hidden_states.shape[-2:] hidden_states = self.pos_embed(hidden_states) temb = self.time_text_embed(timestep, pooled_projections) encoder_embeddings = self.context_embedder(encoder_embeddings) skip_layer_set = set(skip_layers) if skip_layers else set() if block_controlnet_hidden_states is not None: interval_control = len(self.transformer_blocks) / len( block_controlnet_hidden_states ) else: interval_control = 0 for index_block, block in enumerate(self.transformer_blocks): if index_block not in skip_layer_set: encoder_embeddings, hidden_states = block( hidden_states=hidden_states, encoder_hidden_states=encoder_embeddings, temb=temb, joint_attention_kwargs=joint_attention_kwargs, ) # controlnet residual if ( block_controlnet_hidden_states is not None and block.context_pre_only is False ): hidden_states = ( hidden_states + block_controlnet_hidden_states[ int(index_block / interval_control) ] ) hidden_states = self.norm_out(hidden_states, temb) hidden_states = self.proj_out(hidden_states) # unpatchify patch_size = self.patch_size height = height // patch_size width = width // patch_size hidden_states = hidden_states.reshape( shape=( hidden_states.shape[0], height, width, patch_size, patch_size, self.out_channels, ) ) hidden_states = hidden_states.permute(0, 5, 1, 3, 2, 4) output = hidden_states.reshape( shape=( hidden_states.shape[0], self.out_channels, height * patch_size, width * patch_size, ) ) return output # Entry class for registry EntryClass = SD3Transformer2DModel