[kernels] Reorganize ops/diffusion by operator domain behind a lazy facade (#35114)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Xiaoyu Zhang
2026-08-18 20:37:43 +08:00
committed by GitHub
co-authored by Claude Opus 5
parent 7605529bdf
commit ae6945e112
167 changed files with 4813 additions and 4340 deletions
@@ -111,15 +111,15 @@ in-flight row as shipped.
| Pattern | Trace keywords | Primary code | Existing path | Skill should conclude | | Pattern | Trace keywords | Primary code | Existing path | Skill should conclude |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| Fused residual + norm + scale + shift | residual add, norm, scale, shift, gate around DiT blocks | `python/sglang/kernels/ops/diffusion/cutedsl/scale_residual_norm_scale_shift.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py` | `fused_scale_residual_norm_scale_shift(...)` | Treat split residual + norm + modulation as a missing existing diffusion fusion first. | | Fused residual + norm + scale + shift | residual add, norm, scale, shift, gate around DiT blocks | `python/sglang/kernels/ops/diffusion/norm/scale_residual_norm_cutedsl.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py` | `fused_scale_residual_norm_scale_shift(...)` | Treat split residual + norm + modulation as a missing existing diffusion fusion first. |
| Fused norm + scale + shift | norm followed by scale / shift elementwise kernels | `python/sglang/kernels/ops/diffusion/cutedsl/scale_residual_norm_scale_shift.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py` | `fused_norm_scale_shift(...)` | Existing modulation fusion already covers this family. | | Fused norm + scale + shift | norm followed by scale / shift elementwise kernels | `python/sglang/kernels/ops/diffusion/norm/scale_residual_norm_cutedsl.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py` | `fused_norm_scale_shift(...)` | Existing modulation fusion already covers this family. |
| Triton scale / shift and gate-select kernels | tiny scale / shift or gate-select kernels dominate modulation blocks | `python/sglang/kernels/ops/diffusion/triton/scale_shift.py`<br>`python/sglang/multimodal_gen/runtime/layers/elementwise.py` | `fuse_scale_shift_kernel(...)` and `fuse_layernorm_scale_shift_gate_select01_kernel(...)` | Check whether the runtime is missing these existing Triton fusions. | | Triton scale / shift and gate-select kernels | tiny scale / shift or gate-select kernels dominate modulation blocks | `python/sglang/kernels/ops/diffusion/modulate/scale_shift_triton.py`<br>`python/sglang/multimodal_gen/runtime/layers/elementwise.py` | `fuse_scale_shift_kernel(...)` and `fuse_layernorm_scale_shift_gate_select01_kernel(...)` | Check whether the runtime is missing these existing Triton fusions. |
| Fused add-RMSNorm and one-pass RMSNorm | residual add plus RMSNorm still split on short hidden sizes | `python/sglang/multimodal_gen/runtime/layers/layernorm.py`<br>`python/sglang/kernels/ops/diffusion/triton/rmsnorm_onepass.py` | `fused_add_rmsnorm(...)` and `triton_one_pass_rms_norm(...)` | For short hidden-size diffusion blocks, this is already an established fusion family. | | Fused add-RMSNorm and one-pass RMSNorm | residual add plus RMSNorm still split on short hidden sizes | `python/sglang/multimodal_gen/runtime/layers/layernorm.py`<br>`python/sglang/kernels/ops/diffusion/norm/rmsnorm_onepass_triton.py` | `fused_add_rmsnorm(...)` and `triton_one_pass_rms_norm(...)` | For short hidden-size diffusion blocks, this is already an established fusion family. |
| Fused diffusion QK norm + RoPE | split QK norm and RoPE in diffusion attention blocks | `python/sglang/kernels/ops/diffusion/qknorm_rope.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py::apply_qk_norm_rope` | `fused_inplace_qknorm_rope(...)`, with fallback to QK norm plus `apply_flashinfer_rope_qk_inplace(...)` | Distinguish between missing fused qknorm + rope and the existing FlashInfer RoPE fallback. | | Fused diffusion QK norm + RoPE | split QK norm and RoPE in diffusion attention blocks | `python/sglang/kernels/ops/diffusion/rope/qknorm_rope_jit.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py::apply_qk_norm_rope` | `fused_inplace_qknorm_rope(...)`, with fallback to QK norm plus `apply_flashinfer_rope_qk_inplace(...)` | Distinguish between missing fused qknorm + rope and the existing FlashInfer RoPE fallback. |
| Z-Image fused `norm(x) * tanh(scale) + shift` | `fused_norm_tanh_mul_add`<br>`tanh(gate) * rmsnorm(x)` | `python/sglang/kernels/ops/diffusion/cutedsl/norm_tanh_mul_add_norm_scale.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py` | CuTeDSL kernel plus runtime helper for Z-Image residual-form modulation | Treat split Z-Image residual-form modulation as a missing existing diffusion fusion, not a novel idea. | | Z-Image fused `norm(x) * tanh(scale) + shift` | `fused_norm_tanh_mul_add`<br>`tanh(gate) * rmsnorm(x)` | `python/sglang/kernels/ops/diffusion/cutedsl/norm_tanh_mul_add_norm_scale.py`<br>`python/sglang/multimodal_gen/runtime/layers/layernorm.py` | CuTeDSL kernel plus runtime helper for Z-Image residual-form modulation | Treat split Z-Image residual-form modulation as a missing existing diffusion fusion, not a novel idea. |
| Z-Image fused residual modulation + next norm-scale | `fused_norm_tanh_mul_add_norm_scale`<br>`residual + tanh(gate) * rmsnorm(x)`<br>`ffn_norm1(x) * scale_mlp` | `python/sglang/kernels/ops/diffusion/cutedsl/norm_tanh_mul_add_norm_scale.py`<br>`python/sglang/multimodal_gen/runtime/models/dits/zimage.py` | One CuTeDSL kernel fuses the first residual-form modulation and the next normalization / scale stage | If you see this chain split in Z-Image traces, report it as a missing existing mainline fusion family. | | Z-Image fused residual modulation + next norm-scale | `fused_norm_tanh_mul_add_norm_scale`<br>`residual + tanh(gate) * rmsnorm(x)`<br>`ffn_norm1(x) * scale_mlp` | `python/sglang/kernels/ops/diffusion/cutedsl/norm_tanh_mul_add_norm_scale.py`<br>`python/sglang/multimodal_gen/runtime/models/dits/zimage.py` | One CuTeDSL kernel fuses the first residual-form modulation and the next normalization / scale stage | If you see this chain split in Z-Image traces, report it as a missing existing mainline fusion family. |
| LTX2 fused Ada values | `ltx2_ada_values9`<br>`get_ada_values`<br>`scale_shift_table + timestep.reshape` | `python/sglang/kernels/ops/diffusion/triton/ltx2_ada_values.py`<br>`python/sglang/multimodal_gen/runtime/models/dits/ltx_2.py` | PR `#29390` fuses LTX-2.3 Ada value materialization for video/audio streams and reuses the 9 Ada tensors across self-attention, MLP, and prompt-cross-attention blocks | Treat repeated Ada add/reshape/slice ladders in LTX2 traces as a missing shipped SGLang fusion first. | | LTX2 fused Ada values | `ltx2_ada_values9`<br>`get_ada_values`<br>`scale_shift_table + timestep.reshape` | `python/sglang/kernels/ops/diffusion/modulate/ltx2_ada_values_triton.py`<br>`python/sglang/multimodal_gen/runtime/models/dits/ltx_2.py` | PR `#29390` fuses LTX-2.3 Ada value materialization for video/audio streams and reuses the 9 Ada tensors across self-attention, MLP, and prompt-cross-attention blocks | Treat repeated Ada add/reshape/slice ladders in LTX2 traces as a missing shipped SGLang fusion first. |
| LTX2 residual-gate add | `diffusion_residual_gate_add`<br>`residual_gate_add`<br>`residual + update * gate` | `python/sglang/kernels/ops/diffusion/residual_gate_add.py`<br>`python/sglang/kernels/jit/csrc/diffusion/residual_gate_add.cuh`<br>`python/sglang/multimodal_gen/runtime/models/dits/ltx_2.py` | PR `#29361` fuses LTX2 `residual + update * gate` sites for attention, cross-attention, and feed-forward updates into one CUDA custom op when dtype, shape, device, and contiguity guards pass | Treat split add/mul gate ladders in LTX2 traces as a missing shipped SGLang fusion first. | | LTX2 residual-gate add | `diffusion_residual_gate_add`<br>`residual_gate_add`<br>`residual + update * gate` | `python/sglang/kernels/ops/diffusion/modulate/residual_gate_add_jit.py`<br>`python/sglang/kernels/jit/csrc/diffusion/residual_gate_add.cuh`<br>`python/sglang/multimodal_gen/runtime/models/dits/ltx_2.py` | PR `#29361` fuses LTX2 `residual + update * gate` sites for attention, cross-attention, and feed-forward updates into one CUDA custom op when dtype, shape, device, and contiguity guards pass | Treat split add/mul gate ladders in LTX2 traces as a missing shipped SGLang fusion first. |
| Nunchaku fused GELU MLP | `_fused_gelu_mlp`<br>`fused_gelu_mlp` | `python/sglang/multimodal_gen/runtime/models/dits/flux.py` | Nunchaku path fuses `fc1 GEMM + GELU + shift + re-quant + fc2.lora_down` before the second GEMM | Treat split GELU-MLP on Nunchaku checkpoints as an existing fused family, not a new discovery. | | Nunchaku fused GELU MLP | `_fused_gelu_mlp`<br>`fused_gelu_mlp` | `python/sglang/multimodal_gen/runtime/models/dits/flux.py` | Nunchaku path fuses `fc1 GEMM + GELU + shift + re-quant + fc2.lora_down` before the second GEMM | Treat split GELU-MLP on Nunchaku checkpoints as an existing fused family, not a new discovery. |
## 5. Diffusion kernel-overlap and async-communication families ## 5. Diffusion kernel-overlap and async-communication families
+1
View File
@@ -107,6 +107,7 @@ BACKEND_METHODS: Dict[KernelBackend, str] = {
KernelBackend.JIT: "forward_jit", KernelBackend.JIT: "forward_jit",
KernelBackend.AOT: "forward_aot", KernelBackend.AOT: "forward_aot",
KernelBackend.CUTE_DSL: "forward_cute_dsl", KernelBackend.CUTE_DSL: "forward_cute_dsl",
KernelBackend.FLYDSL: "forward_flydsl",
KernelBackend.FLASHINFER: "forward_flashinfer", KernelBackend.FLASHINFER: "forward_flashinfer",
KernelBackend.DEEPGEMM: "forward_deepgemm", KernelBackend.DEEPGEMM: "forward_deepgemm",
KernelBackend.AITER: "forward_aiter", KernelBackend.AITER: "forward_aiter",
@@ -0,0 +1,153 @@
# `sglang.kernels.ops.diffusion`
Fused kernels for diffusion (multimodal-generation) models — DiT transformer
blocks, VAE encoders/decoders, and the sequence-parallel plumbing around them.
Unlike the LLM operator groups, almost nothing here is a general-purpose
operator. Each kernel replaces a **specific eager op chain in a specific
model**, and its value comes as much from *which rounding boundaries it
reproduces* as from its bandwidth. Multi-step denoising amplifies a per-step
rounding difference into visible quality loss, so "close enough" is a
different product from "bit-exact", and the two are gated differently.
## Import surface
```python
from sglang.kernels.ops.diffusion import fused_rmsnorm_scale_shift_bitexact
```
**Import from the package, never from a submodule.** The internal layout is
free to move; the facade is not. `test_import_surface.py` enforces this, with
a small allowlist for tests that deliberately exercise one backend.
Resolution is lazy (PEP 562): the backends have disjoint heavy dependencies
(Triton, CUTLASS/CuTe-DSL, FlyDSL on ROCm, MLX on Apple), so an eager
re-export would make all of them import-time requirements everywhere.
## Layout
One subpackage per **operator domain**; the backend is a **filename suffix**
(`_triton`, `_jit`, `_cutedsl`, `_flydsl`, or `_bitexact` where that says more).
This matches `ops/attention` and `ops/gemm`, and it keeps every implementation
of one logical op in one directory.
```
norm/ RMSNorm / LayerNorm / GroupNorm and their fused epilogues
modulate/ adaLN modulate, gating, timestep conditioning
rope/ rotary embeddings and the QK-norm chains fused into them
activation/ SiLU / GLU / GELU fusions
attention/ sparse linear attention, gated delta-net
layout/ pure data movement: USP/Ulysses relayout, varlen pack, causal pad
common/ numerics primitives, platform predicates, non-Triton fallbacks
sites/ request-scoped mount policy — NOT kernels (see below)
ext/ JIT C++/CUDA extensions (Hunyuan3D raster/inpaint) — NOT kernels
```
## The two numerical contracts
**Bit-exact (`torch.equal` vs the eager chain) → mounted unconditionally.**
These kernels reproduce every aten rounding boundary, sometimes down to the
reduction tree: `norm/layernorm_modulate_triton.py` replicates torch 2.11's
`vectorized_layer_norm_kernel` (128-thread Welford, `_rcp4` guarded
reciprocal, `shfl.down` fold order, `div.rn` + `MUFU.RSQ`), and
`norm/rmsnorm_scale_shift_bitexact.py` replicates flashinfer's CuTe-DSL
`RMSNormKernel` fragment order and `shfl.bfly` fold. They still verify
themselves against the live eager chain on first sight via
`sites/bitexact_gate.py` and fall back permanently on mismatch — the
dispatch they replicate can change under them.
**Not bit-exact → quality-gated.** Mounted onto marked `nn.Module` sites only
for `quality="high"` requests, at batch boundaries, all-or-nothing per
transformer (`sites/quality_gate.py`). A plain fp32 single-pass norm fusion
looks harmless and is not: on ERNIE-Image it moved the 50-step trajectory to
PSNR 18.83 dB at `quality=high`, which is what motivated the bit-exact
rewrite.
## Entry-point protocol
Every public kernel is a **predicate + kernel** pair:
```python
if can_use_<op>(...):
out = <op>(...)
else:
out = <reference chain>
```
The kernel raises on an unsupported input. It does not return `None` — a
silent `None` is too easy to forget to check, and the failure mode is a
wrong-looking image rather than an exception.
## Selection matrix
Several norms look interchangeable and are not. Start here.
### Norm + scale/shift (adaLN)
| Entry point | Backend | Contract | Applies to |
|---|---|---|---|
| `fused_rmsnorm_scale_shift_bitexact` | Triton | bit-exact vs flashinfer CuTe RMSNorm + aten modulate | bf16, contiguous rows, `H == 64 * threads_per_row` |
| `fused_scale_residual_rmsnorm_scale_shift_bitexact` | Triton | bit-exact, incl. the preceding residual-gate add | as above |
| `fused_layernorm_modulate` | Triton | bit-exact vs aten `vectorized_layer_norm` | bf16, `N % 4 == 0`, 16B-aligned |
| `fused_norm_scale_shift` / `fused_scale_residual_norm_scale_shift` | CuTe-DSL | fp32 statistics, close | fp16/bf16/fp32, LN or RMS, many broadcast modes |
| `flydsl_norm_scale_shift` / `flydsl_fused_residual_norm_scale_shift` | FlyDSL | close | **ROCm gfx950 only** |
| `fuse_layernorm_scale_shift_gate_select01_kernel` | Triton | close | per-token select between two modulation rows (Qwen-Image) |
| `norm_infer` / `rms_norm_fn` | Triton (+torch/NPU/MPS fallbacks) | close | the generic entry point; use when nothing above fits |
### Norm variants
| Entry point | Backend | Contract | Applies to |
|---|---|---|---|
| `triton_group_norm_silu` / `apply_group_norm_silu` | Triton | close | NCHW-contiguous, any channels-per-group, always applies SiLU |
| `group_norm_silu_4d` / `group_norm_silu_rows` | Triton | close | **channels_last only**; power-of-two `C <= 2048`; optional SiLU. This is what lets a VAE decoder run channels_last end-to-end with no `nchwToNhwc` |
| `wan_rmsnorm_silu` | Triton | close | `channels_last_3d` 5D, Wan VAE channel-first RMSNorm + SiLU |
| `rmsnorm_scale` / `rmsnorm_tanh_residual` | Triton | bf16-native statistics | Z-Image (matches its own reference exactly), Ideogram 4 (gated) |
| `zimage_qk_rmsnorm_native` | Triton | bit-exact | Z-Image per-head QK RMSNorm |
| `fused_qk_head_layernorm` | Triton | bit-exact | per-head LN on q/k, `dim_head % 4 == 0`, `<= 128` |
| `triton_one_pass_rms_norm` | Triton | close | standalone RMSNorm, one pass |
### RoPE / QK-norm
| Entry point | Backend | Contract |
|---|---|---|
| `fused_inplace_qknorm_rope` | JIT CUDA | one bf16 rounding step vs split baseline; `round_norm_before_rope=True` makes it exact |
| `fused_qknorm_rope_pack_kv` | JIT CUDA | as above, also packs prefix K/V |
| `fused_rope_rotate_half_bitexact` | Triton | bit-exact (elementwise only) |
| `ltx2_qknorm_split_rope_cuda` | JIT CUDA | close; **validated on B200** |
| `apply_rotary_embedding` | Triton (+fallbacks) | close; the generic entry point |
| `hunyuan_qkv_rope_pack` | Triton | bit-exact; packs QKV and applies RoPE in one pass |
### Data movement (all bit-exact by construction)
`usp_merge_heads`, `pack_qkv_destination_major`, `fused_pack_qkv`,
`fused_scatter_to_padded`, `fused_causal_conv3d_cat_pad_cuda`,
`cat_pad_channels_last_3d`, `dup_up3d_add`, `fused_temb_table_slices`,
`ltx2_ada_values9`.
`fused_temb_table_slices` is worth knowing about: the eager
`(table + temb.float()).chunk(6, dim=2)` materializes ~8 GB of fp32 at
704p/121f *and* hands six strided slices downstream, whose `.contiguous()`
calls copy each one again.
## What is not a kernel
`sites/` rewrites `nn.Module` trees (mark / mount / unmount) and `ext/` builds
C++/CUDA extensions that have no backend dimension and no numerical contract.
They live here because they are diffusion-specific and share this package's
build machinery, but they are deliberately in their own directories: nothing
in `sites/` or `ext/` belongs in an operator domain, and `sites/` is the one
place allowed to reference `multimodal_gen` types (lazily, inside functions) —
inspecting model modules is its whole job.
## Adding a kernel
1. Put it in the operator domain it belongs to, with a backend suffix.
2. Export it from `__init__.py` (`_EXPORTS`) and register a `KernelSpec`
(`_SPECS`) — `test_import_surface.py` checks both resolve.
3. Give it a `can_use_*` predicate; raise, don't return `None`.
4. State the numerical contract in the module docstring, including which
shapes it was verified on.
5. If it is not bit-exact, gate it through `sites/`. Do not mount it by
default.
6. Test it in the domain suite (`test/registered/kernels/ops/diffusion/`), and
the model wiring in `test_model_fast_paths.py`.
+452 -96
View File
@@ -1,15 +1,34 @@
"""Registered diffusion-model kernels and their public wrappers. """Fused kernels for diffusion (multimodal-generation) models.
Hot paths import concrete implementations from submodules. The package-level This module is the **only** supported import surface for these kernels::
wrappers remain available for backward compatibility.
from sglang.kernels.ops.diffusion import fused_rmsnorm_scale_shift_bitexact
Importing a submodule directly (``...diffusion.norm.norm_triton``) couples the
caller to the file layout; ``test_import_surface.py`` guards against it. The
one exception is a test that deliberately exercises a single backend.
Layout -- one subpackage per **operator domain** (``norm``, ``modulate``,
``rope``, ``activation``, ``attention``, ``layout``) with the backend carried
as a filename suffix (``_triton`` / ``_jit`` / ``_cutedsl`` / ``_flydsl``, or
``_bitexact`` where that is the more informative label), matching how
``ops/attention`` and ``ops/gemm`` are organized. ``common`` holds shared
numerics and platform plumbing, ``sites`` the request-scoped mount policy, and
``ext`` the JIT C++/CUDA extensions that are not kernels. Start from
``README.md``: several norms look interchangeable and are not.
Resolution is lazy (PEP 562). The backends have disjoint, heavy dependencies
-- Triton, CUTLASS/CuTe-DSL, FlyDSL (ROCm), MLX (Apple) -- so an eager
re-export would turn every one of them into a hard import-time requirement on
every platform. ``_EXPORTS`` maps a symbol to its module and the import
happens on first attribute access.
""" """
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import Any
from sglang.kernels.registry import register_kernel from sglang.kernels.registry import register_kernel
from sglang.kernels.selector import get_kernel
from sglang.kernels.spec import ( from sglang.kernels.spec import (
CapabilityRequirement, CapabilityRequirement,
FormatSignature, FormatSignature,
@@ -17,106 +36,443 @@ from sglang.kernels.spec import (
KernelSpec, KernelSpec,
) )
if TYPE_CHECKING:
import torch
from torch import nn
_CUDA = frozenset({CapabilityRequirement.CUDA}) _CUDA = frozenset({CapabilityRequirement.CUDA})
_HIP = frozenset({CapabilityRequirement.HIP})
register_kernel( # ---------------------------------------------------------------------------
KernelSpec( # Registry inventory. Metadata only -- registering imports neither torch nor a
op="diffusion.apply_group_norm_silu", # backend and triggers no JIT build. Ops carrying several backends (e.g.
backend=KernelBackend.TRITON, # ``scale_residual_norm_scale_shift`` in Triton, CuTe-DSL and FlyDSL) are
target="sglang.kernels.ops.diffusion.group_norm_silu:apply_group_norm_silu", # inventory: callers name the one they want via ``select_kernel``.
capabilities=_CUDA, # ---------------------------------------------------------------------------
format_signature=FormatSignature(description="fused GroupNorm + SiLU"), _SPECS: tuple[tuple[str, KernelBackend, str, frozenset, str], ...] = (
description="Fused group-norm + SiLU (Triton).", (
) "diffusion.apply_group_norm_silu",
) KernelBackend.TRITON,
register_kernel( "norm.group_norm_silu:apply_group_norm_silu",
KernelSpec( _CUDA,
op="diffusion.residual_gate_add", "Fused GroupNorm + SiLU.",
backend=KernelBackend.JIT, ),
target="sglang.kernels.ops.diffusion.residual_gate_add:residual_gate_add", (
capabilities=_CUDA, "diffusion.group_norm_silu_4d",
format_signature=FormatSignature(description="residual + gate * update"), KernelBackend.TRITON,
description="Fused residual gate-add (sglang.kernels.jit).", "norm.group_norm_silu_twopass_triton:group_norm_silu_4d",
) _CUDA,
) "Channels-last two-pass GroupNorm(+SiLU), 4D.",
register_kernel( ),
KernelSpec( (
op="diffusion.fused_inplace_qknorm_rope", "diffusion.group_norm_silu_rows",
backend=KernelBackend.JIT, KernelBackend.TRITON,
target="sglang.kernels.ops.diffusion.qknorm_rope:fused_inplace_qknorm_rope", "norm.group_norm_silu_twopass_triton:group_norm_silu_rows",
capabilities=_CUDA, _CUDA,
format_signature=FormatSignature( "Channels-last two-pass GroupNorm(+SiLU) over (N, L, C) rows.",
in_place=True, description="fused in-place QK-norm + RoPE" ),
(
"diffusion.wan_rmsnorm_silu",
KernelBackend.TRITON,
"norm.wan_rmsnorm_silu_triton:wan_rmsnorm_silu",
_CUDA,
"Wan VAE channels_last_3d RMSNorm + SiLU.",
),
(
"diffusion.rmsnorm_scale_shift",
KernelBackend.TRITON,
"norm.rmsnorm_scale_shift_bitexact:fused_rmsnorm_scale_shift_bitexact",
_CUDA,
"Bit-exact RMSNorm + adaLN scale/shift.",
),
(
"diffusion.scale_residual_norm_scale_shift",
KernelBackend.TRITON,
"norm.rmsnorm_scale_shift_bitexact:fused_scale_residual_rmsnorm_scale_shift_bitexact",
_CUDA,
"Bit-exact residual-gate add + RMSNorm + scale/shift.",
),
(
"diffusion.scale_residual_norm_scale_shift",
KernelBackend.CUTE_DSL,
"norm.scale_residual_norm_cutedsl:fused_scale_residual_norm_scale_shift",
_CUDA,
"CuTe-DSL residual + norm + scale/shift.",
),
(
"diffusion.scale_residual_norm_scale_shift",
KernelBackend.FLYDSL,
"norm.fused_residual_norm_flydsl:flydsl_fused_residual_norm_scale_shift",
_HIP,
"FlyDSL (ROCm gfx950) residual + norm + scale/shift.",
),
(
"diffusion.norm_scale_shift",
KernelBackend.CUTE_DSL,
"norm.scale_residual_norm_cutedsl:fused_norm_scale_shift",
_CUDA,
"CuTe-DSL norm + scale/shift.",
),
(
"diffusion.norm_scale_shift",
KernelBackend.FLYDSL,
"norm.fused_residual_norm_flydsl:flydsl_norm_scale_shift",
_HIP,
"FlyDSL (ROCm gfx950) norm + scale/shift.",
),
(
"diffusion.layernorm_modulate",
KernelBackend.TRITON,
"norm.layernorm_modulate_triton:fused_layernorm_modulate",
_CUDA,
"Bit-exact LayerNorm + adaLN modulate.",
),
(
"diffusion.qk_head_layernorm",
KernelBackend.TRITON,
"norm.layernorm_modulate_triton:fused_qk_head_layernorm",
_CUDA,
"Bit-exact per-head LayerNorm for q/k.",
),
(
"diffusion.qk_rmsnorm_native",
KernelBackend.TRITON,
"norm.zimage_qk_rmsnorm_triton:zimage_qk_rmsnorm_native",
_CUDA,
"Z-Image bf16-native per-head QK RMSNorm.",
),
(
"diffusion.rmsnorm_scale",
KernelBackend.TRITON,
"norm.native_bf16_rmsnorm_triton:rmsnorm_scale",
_CUDA,
"BF16-native RMSNorm * scale.",
),
(
"diffusion.rmsnorm_tanh_residual",
KernelBackend.TRITON,
"norm.native_bf16_rmsnorm_triton:rmsnorm_tanh_residual",
_CUDA,
"BF16-native x + tanh(gate) * RMSNorm(y).",
),
(
"diffusion.modulate_scale_shift",
KernelBackend.JIT,
"modulate.modulate_scale_shift_jit:modulate_scale_shift",
_CUDA,
"Bit-exact adaLN modulate x * (1 + scale) + shift.",
),
(
"diffusion.residual_gate_add",
KernelBackend.JIT,
"modulate.residual_gate_add_jit:residual_gate_add",
_CUDA,
"Fused residual + gate * update.",
),
(
"diffusion.timestep_embedding",
KernelBackend.JIT,
"modulate.timestep_embedding_jit:timestep_embedding",
_CUDA,
"Sinusoidal timestep embedding.",
),
(
"diffusion.temb_table_slices",
KernelBackend.TRITON,
"modulate.wan_temb_table_slices_triton:fused_temb_table_slices",
_CUDA,
"Contiguous adaLN slices for Wan2.2-TI2V.",
),
(
"diffusion.ltx2_ada_values",
KernelBackend.TRITON,
"modulate.ltx2_ada_values_triton:ltx2_ada_values9",
_CUDA,
"LTX-2 nine-way adaLN value split.",
),
(
"diffusion.fused_inplace_qknorm_rope",
KernelBackend.JIT,
"rope.qknorm_rope_jit:fused_inplace_qknorm_rope",
_CUDA,
"Fused in-place QK RMS-norm + RoPE.",
),
(
"diffusion.ltx2_qknorm_split_rope",
KernelBackend.JIT,
"rope.ltx2_qknorm_split_rope_jit:ltx2_qknorm_split_rope_cuda",
_CUDA,
"LTX-2 QK-norm + split RoPE.",
),
(
"diffusion.rope_rotate_half",
KernelBackend.TRITON,
"rope.rope_rotate_half_bitexact:fused_rope_rotate_half_bitexact",
_CUDA,
"Bit-exact rotate-half RoPE.",
),
(
"diffusion.hunyuan_qkv_rope_pack",
KernelBackend.TRITON,
"rope.hunyuan_qkv_pack_triton:hunyuan_qkv_rope_pack",
_CUDA,
"HunyuanVideo QKV pack + RoPE.",
),
(
"diffusion.silu_mul",
KernelBackend.TRITON,
"activation.silu_mul_bitexact:fused_silu_mul_bitexact",
_CUDA,
"Bit-exact silu(a) * b for split-projection SwiGLU.",
),
(
"diffusion.bias_silu",
KernelBackend.TRITON,
"activation.sana_conv_post_triton:fused_bias_silu",
_CUDA,
"Bit-exact conv bias + SiLU (Sana GLUMB).",
),
(
"diffusion.bias_glu",
KernelBackend.TRITON,
"activation.sana_conv_post_triton:fused_bias_glu",
_CUDA,
"Bit-exact conv bias + GLU (Sana GLUMB).",
),
(
"diffusion.linear_gelu_tanh",
KernelBackend.AOT,
"sites.fused_linear_gelu_site:fused_linear_gelu_tanh",
_CUDA,
"Linear + tanh-GELU via the cublasLt epilogue.",
),
(
"diffusion.sparse_linear_attn_fwd",
KernelBackend.TRITON,
"attention.sparse_linear_attn_triton:_attn_fwd",
_CUDA,
"Sparse linear attention forward.",
),
(
"diffusion.bigdn",
KernelBackend.TRITON,
"attention.sana_wm_gdn_triton:fused_bigdn_func",
_CUDA,
"Sana-WM bidirectional gated delta-net.",
),
(
"diffusion.usp_merge_heads",
KernelBackend.JIT,
"layout.usp_relayout_jit:usp_merge_heads",
_CUDA,
"USP all-to-all output head merge.",
),
(
"diffusion.pack_qkv_destination_major",
KernelBackend.TRITON,
"layout.ulysses_qkv_triton:pack_qkv_destination_major",
_CUDA,
"Ulysses destination-major QKV pack.",
),
(
"diffusion.varlen_pack_qkv",
KernelBackend.TRITON,
"layout.varlen_pack_pad_triton:fused_pack_qkv",
_CUDA,
"Varlen gather of Q/K/V at valid positions.",
),
(
"diffusion.varlen_scatter_to_padded",
KernelBackend.TRITON,
"layout.varlen_pack_pad_triton:fused_scatter_to_padded",
_CUDA,
"Varlen scatter back to the dense layout.",
),
(
"diffusion.causal_conv3d_cat_pad",
KernelBackend.JIT,
"layout.causal_conv3d_cat_pad_jit:fused_causal_conv3d_cat_pad_cuda",
_CUDA,
"Causal Conv3d cat + pad.",
),
(
"diffusion.causal_conv3d_cat_pad",
KernelBackend.TRITON,
"layout.causal_conv3d_cat_pad_triton:fused_causal_conv3d_cat_pad",
_CUDA,
"Causal Conv3d cat + pad (Triton).",
),
(
"diffusion.cat_pad_channels_last_3d",
KernelBackend.TRITON,
"layout.wan_causal_cache_triton:cat_pad_channels_last_3d",
_CUDA,
"Wan causal VAE cat + pad in channels_last_3d.",
),
(
"diffusion.dup_up3d_add",
KernelBackend.TRITON,
"layout.wan_causal_cache_triton:dup_up3d_add",
_CUDA,
"Wan causal VAE main + DupUp3D(src).",
), ),
description="Fused QK-norm + RoPE (sglang.kernels.jit).",
)
) )
# Migrated from multimodal_gen (RFC #29630, Phase 2.5). Hot paths import the
# Triton symbol directly; the registry entry remains for namespace discovery. for _op, _backend, _target, _caps, _description in _SPECS:
register_kernel( register_kernel(
KernelSpec( KernelSpec(
op="diffusion.sparse_linear_attn_fwd", op=_op,
backend=KernelBackend.TRITON, backend=_backend,
target="sglang.kernels.ops.diffusion.sparse_linear_attn_kernels:_attn_fwd", target=f"sglang.kernels.ops.diffusion.{_target}",
capabilities=_CUDA, capabilities=_caps,
format_signature=FormatSignature(description="sparse linear attention fwd"), format_signature=FormatSignature(description=_description),
description="Sparse linear attention forward (Triton).", description=_description,
) )
)
def apply_group_norm_silu(
x: torch.Tensor, norm: nn.Module, activation: nn.Module
) -> torch.Tensor:
"""Fused GroupNorm + SiLU (falls back to eager when unsupported)."""
return get_kernel("diffusion.apply_group_norm_silu", KernelBackend.TRITON)(
x, norm, activation
) )
# ---------------------------------------------------------------------------
def residual_gate_add( # Public export table: symbol -> owning submodule. Sorted by domain, module,
residual: torch.Tensor, update: torch.Tensor, gate: torch.Tensor # then symbol; a new public kernel belongs here and nowhere else.
) -> torch.Tensor: # ---------------------------------------------------------------------------
"""Fused ``residual + gate * update``.""" _EXPORTS: dict[str, str] = {
return get_kernel("diffusion.residual_gate_add", KernelBackend.JIT)( # Normalization: RMSNorm / LayerNorm / GroupNorm and their fused epilogues
residual, update, gate "FLYDSL_NORM_MIN_ALIGNED_DIM": "norm.fused_residual_norm_flydsl",
) "flydsl_fused_residual_norm_scale_shift": "norm.fused_residual_norm_flydsl",
"flydsl_norm_scale_shift": "norm.fused_residual_norm_flydsl",
"apply_group_norm_silu": "norm.group_norm_silu",
"triton_group_norm_silu": "norm.group_norm_silu_triton",
"can_use_group_norm_silu_4d": "norm.group_norm_silu_twopass_triton",
"can_use_group_norm_silu_rows": "norm.group_norm_silu_twopass_triton",
"group_norm_silu_4d": "norm.group_norm_silu_twopass_triton",
"group_norm_silu_rows": "norm.group_norm_silu_twopass_triton",
"can_use_fused_layernorm_modulate": "norm.layernorm_modulate_triton",
"can_use_fused_qk_head_layernorm": "norm.layernorm_modulate_triton",
"fused_layernorm_modulate": "norm.layernorm_modulate_triton",
"fused_layernorm_modulate_raw": "norm.layernorm_modulate_triton",
"fused_qk_head_layernorm": "norm.layernorm_modulate_triton",
"is_plain_layer_norm": "norm.layernorm_modulate_triton",
"rmsnorm_scale": "norm.native_bf16_rmsnorm_triton",
"rmsnorm_tanh_residual": "norm.native_bf16_rmsnorm_triton",
"norm_infer": "norm.norm_triton",
"rms_norm_fn": "norm.norm_triton",
"triton_one_pass_rms_norm": "norm.rmsnorm_onepass_triton",
"can_use_fused_rmsnorm_scale_shift": "norm.rmsnorm_scale_shift_bitexact",
"can_use_fused_scale_residual_rmsnorm_scale_shift": "norm.rmsnorm_scale_shift_bitexact",
"fused_rmsnorm_scale_shift_bitexact": "norm.rmsnorm_scale_shift_bitexact",
"fused_scale_residual_rmsnorm_scale_shift_bitexact": "norm.rmsnorm_scale_shift_bitexact",
"fused_norm_scale_shift": "norm.scale_residual_norm_cutedsl",
"fused_scale_residual_norm_scale_shift": "norm.scale_residual_norm_cutedsl",
"validate_scale_shift": "norm.scale_residual_norm_cutedsl",
"can_use_wan_rmsnorm_silu": "norm.wan_rmsnorm_silu_triton",
"wan_rmsnorm_silu": "norm.wan_rmsnorm_silu_triton",
"can_use_qk_rmsnorm_native": "norm.zimage_qk_rmsnorm_triton",
"zimage_qk_rmsnorm_native": "norm.zimage_qk_rmsnorm_triton",
# adaLN modulation, gating and timestep conditioning
"indexed_gate_bf16": "modulate.indexed_modulation_triton",
"indexed_gate_bf16_": "modulate.indexed_modulation_triton",
"indexed_scale_shift_bf16_": "modulate.indexed_modulation_triton",
"ltx2_ada_values9": "modulate.ltx2_ada_values_triton",
"can_use_modulate_scale_shift_cuda": "modulate.modulate_scale_shift_jit",
"modulate_scale_shift": "modulate.modulate_scale_shift_jit",
"modulate_scale_shift_cuda": "modulate.modulate_scale_shift_jit",
"can_use_residual_gate_add_cuda": "modulate.residual_gate_add_jit",
"residual_gate_add": "modulate.residual_gate_add_jit",
"residual_gate_add_cuda": "modulate.residual_gate_add_jit",
"fuse_layernorm_scale_shift_gate_select01_kernel": "modulate.scale_shift_triton",
"fuse_residual_layernorm_scale_shift_gate_select01_kernel": "modulate.scale_shift_triton",
"fuse_scale_shift_kernel": "modulate.scale_shift_triton",
"try_fused_scaled_residual_add_exact": "modulate.scale_shift_triton",
"timestep_embedding": "modulate.timestep_embedding_jit",
"can_use_fused_temb_table_slices": "modulate.wan_temb_table_slices_triton",
"fused_temb_table_slices": "modulate.wan_temb_table_slices_triton",
# Rotary embeddings and the QK-norm chains fused around them
"hunyuan_qkv_rope_pack": "rope.hunyuan_qkv_pack_triton",
"can_use_ltx2_qknorm_split_rope_cuda": "rope.ltx2_qknorm_split_rope_jit",
"ltx2_qknorm_split_rope_cuda": "rope.ltx2_qknorm_split_rope_jit",
"apply_ltx2_split_rotary_emb": "rope.ltx2_rotary_triton",
"can_use_fused_inplace_qknorm_rope": "rope.qknorm_rope_jit",
"fused_inplace_qknorm_rope": "rope.qknorm_rope_jit",
"fused_qknorm_rope_pack_kv": "rope.qknorm_rope_jit",
"can_use_fused_rope_rotate_half": "rope.rope_rotate_half_bitexact",
"fused_rope_rotate_half_bitexact": "rope.rope_rotate_half_bitexact",
"apply_rotary_embedding": "rope.rotary_triton",
# Activation-function fusions
"can_use_fused_bias_glu": "activation.sana_conv_post_triton",
"can_use_fused_bias_silu": "activation.sana_conv_post_triton",
"fused_bias_glu": "activation.sana_conv_post_triton",
"fused_bias_silu": "activation.sana_conv_post_triton",
"can_use_fused_silu_mul": "activation.silu_mul_bitexact",
"fused_packed_silu_mul_bitexact": "activation.silu_mul_bitexact",
"fused_silu_mul_bitexact": "activation.silu_mul_bitexact",
# Diffusion attention kernels
"cam_scan_bidi_chunkwise": "attention.sana_wm_gdn_chunkwise_triton",
"fused_bigdn_func": "attention.sana_wm_gdn_triton",
"fused_qk_inv_rms": "attention.sana_wm_gdn_triton",
"prepare_rope_tables": "attention.sana_wm_gdn_triton",
"_attn_fwd": "attention.sparse_linear_attn_triton",
"get_block_map": "attention.sparse_linear_attn_triton",
# Data movement: bitwise identical to the aten chains they replace
"can_use_fused_causal_conv3d_cat_pad_cuda": "layout.causal_conv3d_cat_pad_jit",
"fused_causal_conv3d_cat_pad_cuda": "layout.causal_conv3d_cat_pad_jit",
"fused_causal_conv3d_cat_pad": "layout.causal_conv3d_cat_pad_triton",
"pack_qkv_destination_major": "layout.ulysses_qkv_triton",
"can_use_usp_merge_heads": "layout.usp_relayout_jit",
"usp_merge_heads": "layout.usp_relayout_jit",
"build_inv_indices": "layout.varlen_pack_pad_triton",
"fused_pack_qkv": "layout.varlen_pack_pad_triton",
"fused_scatter_to_padded": "layout.varlen_pack_pad_triton",
"cat_pad_channels_last_3d": "layout.wan_causal_cache_triton",
"dup_up3d_add": "layout.wan_causal_cache_triton",
# Fusion-site policy: quality gate, first-sight verification, mount
"BitExactFusionGate": "sites.bitexact_gate",
"flashinfer_rmsnorm_diagnostic_hint": "sites.bitexact_gate",
"tensors_equal": "sites.bitexact_gate",
"fused_gate_rmsnorm_active": "sites.fused_gate_rmsnorm_site",
"fused_rmsnorm_scale": "sites.fused_gate_rmsnorm_site",
"fused_rmsnorm_tanh_residual": "sites.fused_gate_rmsnorm_site",
"mark_fused_gate_rmsnorm_site": "sites.fused_gate_rmsnorm_site",
"mount_fused_gate_rmsnorm": "sites.fused_gate_rmsnorm_site",
"unmount_fused_gate_rmsnorm": "sites.fused_gate_rmsnorm_site",
"can_use_linear_gelu": "sites.fused_linear_gelu_site",
"fused_gelu_active": "sites.fused_linear_gelu_site",
"fused_linear_gelu_tanh": "sites.fused_linear_gelu_site",
"mark_fused_gelu_site": "sites.fused_linear_gelu_site",
"mount_fused_linear_gelu": "sites.fused_linear_gelu_site",
"unmount_fused_linear_gelu": "sites.fused_linear_gelu_site",
"can_use_ln_modulate": "sites.fused_ln_modulate_site",
"fused_ln_modulate": "sites.fused_ln_modulate_site",
"fused_ln_modulate_active": "sites.fused_ln_modulate_site",
"mark_fused_ln_modulate_site": "sites.fused_ln_modulate_site",
"mount_fused_ln_modulate": "sites.fused_ln_modulate_site",
"unmount_fused_ln_modulate": "sites.fused_ln_modulate_site",
"mark_hunyuan_qknorm_site": "sites.hunyuan_qknorm_site",
"mount_hunyuan_qknorm": "sites.hunyuan_qknorm_site",
"try_hunyuan_qknorm": "sites.hunyuan_qknorm_site",
"unmount_hunyuan_qknorm": "sites.hunyuan_qknorm_site",
"can_use_ltx2_rms_norm_modulate": "sites.ltx2_rmsnorm_modulate_site",
"fused_ltx2_rms_norm_modulate": "sites.ltx2_rmsnorm_modulate_site",
"ltx2_rms_norm_modulate_active": "sites.ltx2_rmsnorm_modulate_site",
"mark_ltx2_rms_norm_modulate_site": "sites.ltx2_rmsnorm_modulate_site",
"mount_ltx2_rms_norm_modulate": "sites.ltx2_rmsnorm_modulate_site",
"unmount_ltx2_rms_norm_modulate": "sites.ltx2_rmsnorm_modulate_site",
"QualityGatedFusion": "sites.quality_gate",
# JIT C++/CUDA extensions (not kernels, not in the registry)
"interpolate": "ext.hunyuan3d_rasterizer",
"rasterize": "ext.hunyuan3d_rasterizer",
"meshVerticeInpaint": "ext.mesh_processor",
}
def fused_inplace_qknorm_rope( def __getattr__(name: str) -> Any:
q: torch.Tensor, """Resolve a public symbol to its submodule on first access (PEP 562)."""
k: torch.Tensor, module = _EXPORTS.get(name)
q_weight: torch.Tensor, if module is None:
k_weight: torch.Tensor, raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
cos_sin_cache: torch.Tensor, from importlib import import_module
positions: torch.Tensor,
*, value = getattr(import_module(f"{__name__}.{module}"), name)
is_neox: bool, globals()[name] = value # cache; later lookups skip __getattr__ entirely
eps: float = 1e-6, return value
head_dim: int = 0,
rope_dim: int = 0,
) -> None:
"""Fused in-place QK RMS-norm + RoPE."""
return get_kernel("diffusion.fused_inplace_qknorm_rope", KernelBackend.JIT)(
q,
k,
q_weight,
k_weight,
cos_sin_cache,
positions,
is_neox=is_neox,
eps=eps,
head_dim=head_dim,
rope_dim=rope_dim,
)
__all__ = [ def __dir__() -> list[str]:
"apply_group_norm_silu", return sorted(set(globals()) | set(_EXPORTS))
"residual_gate_add",
"fused_inplace_qknorm_rope",
] __all__ = sorted(_EXPORTS)
@@ -0,0 +1 @@
"""Activation-function fusions (SiLU/GLU/GELU epilogues)."""
@@ -7,7 +7,7 @@ import torch
import triton # type: ignore import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.ops.diffusion.triton.numerics import round_bf16_to_fp32 from sglang.kernels.ops.diffusion.common.numerics import round_bf16_to_fp32
@triton.jit @triton.jit
@@ -20,7 +20,7 @@ import torch
import triton # type: ignore import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.ops.diffusion.triton.numerics import round_bf16_to_fp32 from sglang.kernels.ops.diffusion.common.numerics import round_bf16_to_fp32
from sglang.srt.utils.custom_op import register_custom_op from sglang.srt.utils.custom_op import register_custom_op
@@ -0,0 +1 @@
"""Diffusion attention kernels: sparse linear attention and gated delta-net."""
@@ -1619,7 +1619,7 @@ def fused_bigdn_bidi_chunkwise(
def _default_dot_prec() -> int: def _default_dot_prec() -> int:
try: try:
from sglang.kernels.ops.diffusion.triton.sana_wm_gdn import ( from sglang.kernels.ops.diffusion.attention.sana_wm_gdn_triton import (
_resolve_launch_config, _resolve_launch_config,
) )
@@ -220,7 +220,7 @@ def fused_bigdn_func(
Thin entry point kept for call-site stability; delegates to Thin entry point kept for call-site stability; delegates to
:func:`fused_bigdn_bidi_chunkwise` from ``sana_wm_gdn_chunkwise``. :func:`fused_bigdn_bidi_chunkwise` from ``sana_wm_gdn_chunkwise``.
""" """
from sglang.kernels.ops.diffusion.triton.sana_wm_gdn_chunkwise import ( from sglang.kernels.ops.diffusion.attention.sana_wm_gdn_chunkwise_triton import (
fused_bigdn_bidi_chunkwise, fused_bigdn_bidi_chunkwise,
) )
@@ -0,0 +1,6 @@
"""Shared infrastructure for the diffusion kernels -- no kernels of its own.
- ``numerics`` : rounding/opmath primitives the bit-exact kernels are built from
- ``platform`` : device predicates and the Triton-vs-fallback selector
- ``fallback_*``: pure-torch / NPU / MPS implementations for Triton-less devices
"""
@@ -2,7 +2,7 @@
Triton is not available on macOS / Metal, so these pure-PyTorch (and Triton is not available on macOS / Metal, so these pure-PyTorch (and
optionally MLX-accelerated) implementations replace the Triton kernels optionally MLX-accelerated) implementations replace the Triton kernels
at import time when ``current_platform.is_mps()`` is True. at import time when the live platform is MPS (see ``common.platform``).
MLX acceleration (opt-in via ``SGLANG_USE_MLX=1``): MLX acceleration (opt-in via ``SGLANG_USE_MLX=1``):
Norm ops use ``mx.fast.rms_norm`` / ``mx.fast.layer_norm`` — single fused Norm ops use ``mx.fast.rms_norm`` / ``mx.fast.layer_norm`` — single fused
@@ -17,13 +17,13 @@ from torch import Tensor
from sglang.srt.utils.tensor_bridge import mlx_to_torch, torch_to_mlx, use_mlx from sglang.srt.utils.tensor_bridge import mlx_to_torch, torch_to_mlx, use_mlx
from .torch_fallback import ( from .fallback_torch import (
apply_rotary_embedding_native as apply_rotary_embedding_native, apply_rotary_embedding_native as apply_rotary_embedding_native,
) )
from .torch_fallback import ( from .fallback_torch import (
fuse_scale_shift_kernel_native as fuse_scale_shift_kernel_native, fuse_scale_shift_kernel_native as fuse_scale_shift_kernel_native,
) )
from .torch_fallback import ( from .fallback_torch import (
norm_infer_native, norm_infer_native,
rms_norm_fn_native, rms_norm_fn_native,
triton_one_pass_rms_norm_native, triton_one_pass_rms_norm_native,
@@ -0,0 +1,107 @@
"""Platform predicates and the import-time fallback selector.
Several diffusion Triton kernels have no Triton on the live device (Ascend
NPU, Apple MPS, MUSA, CPU) and must resolve to a pure-``torch`` — or
MLX-accelerated — implementation. That choice is made once at import time,
which used to mean a hand-rolled four-branch ``if`` block repeated in every
such module, each importing ``current_platform`` directly.
This module owns both halves of that:
- :func:`platform_key` — the one place the diffusion kernels ask what device
they are on;
- :func:`select_impl` — the one place the Triton-vs-fallback choice is made.
Layering note: the authority on "what platform is this" is the
``multimodal_gen`` platform plugin registry, because it is the only one that
consults out-of-tree vendor plugins (NPU/MUSA). ``kernels.spec.PlatformInfo``
cannot replace it until it grows MPS/MUSA members and plugin support (see the
``DeviceType`` TODO in ``kernels/spec.py``). Until then the dependency is
deliberately confined to this single file and resolved lazily, so no other
kernel module imports upward.
"""
from __future__ import annotations
from typing import Callable, TypeVar
F = TypeVar("F", bound=Callable)
_CUDA_LIKE = frozenset({"cuda", "hip"})
def platform_key() -> str:
"""Return the live device family: ``cuda``/``hip``/``npu``/``mps``/``musa``/``cpu``.
Deliberately *not* memoized: :func:`select_impl` calls it at module import
time, and latching that first answer would freeze the choice before the
platform plugin has resolved. Use :func:`is_cuda` / :func:`is_hip` on hot
paths -- they delegate straight to the platform's own cached predicates.
"""
from sglang.multimodal_gen.runtime.platforms import current_platform
for name in ("cuda", "hip", "npu", "mps", "musa"):
if getattr(current_platform, f"is_{name}")():
return name
return "cpu"
def is_cuda() -> bool:
"""Cheap enough for a per-call kernel guard.
Delegates to ``current_platform.is_cuda``, which is ``lru_cache``d on the
platform object -- the same call the pre-refactor guards made. Going
through :func:`platform_key` instead would add an import plus a chain of
``getattr`` lookups to every fused-elementwise dispatch.
"""
from sglang.multimodal_gen.runtime.platforms import current_platform
return current_platform.is_cuda()
def is_hip() -> bool:
"""See :func:`is_cuda`; delegates to the platform's cached predicate."""
from sglang.multimodal_gen.runtime.platforms import current_platform
return current_platform.is_hip()
def has_triton() -> bool:
"""True when the live device runs the Triton implementations."""
return platform_key() in _CUDA_LIKE
def lazy_fallback(kind: str, name: str) -> Callable:
"""Name a fallback without importing its module.
``select_impl`` is handed every candidate at once, so a plain import here
would pull in *all* fallback modules on every platform -- including MLX on
CUDA hosts. The returned shim imports ``common.fallback_<kind>`` on its
first call instead, which for the unselected candidates never happens.
"""
def _call(*args, **kwargs):
from importlib import import_module
impl = getattr(
import_module(f"sglang.kernels.ops.diffusion.common.fallback_{kind}"), name
)
return impl(*args, **kwargs)
_call.__name__ = name
_call.__qualname__ = f"{kind}_fallback.{name}"
return _call
def select_impl(triton_impl: F, **fallbacks: F) -> F:
"""Pick ``triton_impl`` on CUDA/HIP, else the fallback for this platform.
Callers pass the fallbacks they actually have, keyed by platform
(``npu=``, ``mps=``, ``musa=``, ``cpu=``); an unlisted platform keeps the
Triton implementation, which is what the pre-existing per-module ``if``
chains did. Keeping the whole decision in one call means a module's
exported name is bound exactly once, so the fallback wiring stays greppable
and can later be replaced wholesale by a ``BaseFusedOp`` dispatch without
touching call sites.
"""
return fallbacks.get(platform_key(), triton_impl)
@@ -0,0 +1,7 @@
"""JIT-built C++/CUDA extensions that are not kernels.
Mesh rasterization and texture inpainting for Hunyuan3D: no backend dimension,
no numerical contract, not in the kernel registry. Kept beside the diffusion
kernels because they share the JIT build/recovery machinery in
:mod:`.loader`.
"""
@@ -13,7 +13,7 @@ from typing import Tuple
import torch import torch
from sglang.kernels.ops.diffusion.render import load_extension_with_recovery from sglang.kernels.ops.diffusion.ext.loader import load_extension_with_recovery
_abs_path = os.path.dirname(os.path.abspath(__file__)) _abs_path = os.path.dirname(os.path.abspath(__file__))
_custom_rasterizer_kernel = None _custom_rasterizer_kernel = None
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#include "rasterizer.h"
void rasterizeTriangleCPU(int idx, float *vt0, float *vt1, float *vt2,
int width, int height, INT64 *zbuffer, float *d,
float occlusion_truncation) {
float x_min = std::min(vt0[0], std::min(vt1[0], vt2[0]));
float x_max = std::max(vt0[0], std::max(vt1[0], vt2[0]));
float y_min = std::min(vt0[1], std::min(vt1[1], vt2[1]));
float y_max = std::max(vt0[1], std::max(vt1[1], vt2[1]));
for (int px = x_min; px < x_max + 1; ++px) {
if (px < 0 || px >= width)
continue;
for (int py = y_min; py < y_max + 1; ++py) {
if (py < 0 || py >= height)
continue;
float vt[2] = {px + 0.5f, py + 0.5f};
float baryCentricCoordinate[3];
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, baryCentricCoordinate);
if (isBarycentricCoordInBounds(baryCentricCoordinate)) {
int pixel = py * width + px;
if (zbuffer == 0) {
zbuffer[pixel] = (INT64)(idx + 1);
continue;
}
float depth = baryCentricCoordinate[0] * vt0[2] +
baryCentricCoordinate[1] * vt1[2] +
baryCentricCoordinate[2] * vt2[2];
float depth_thres = 0;
if (d) {
depth_thres = d[pixel] * 0.49999f + 0.5f + occlusion_truncation;
}
int z_quantize = depth * (2 << 17);
INT64 token = (INT64)z_quantize * MAXINT + (INT64)(idx + 1);
if (depth < depth_thres)
continue;
zbuffer[pixel] = std::min(zbuffer[pixel], token);
}
}
}
}
void barycentricFromImgcoordCPU(float *V, int *F, int *findices, INT64 *zbuffer,
int width, int height, int num_vertices,
int num_faces, float *barycentric_map,
int pix) {
INT64 f = zbuffer[pix] % MAXINT;
if (f == (MAXINT - 1)) {
findices[pix] = 0;
barycentric_map[pix * 3] = 0;
barycentric_map[pix * 3 + 1] = 0;
barycentric_map[pix * 3 + 2] = 0;
return;
}
findices[pix] = f;
f -= 1;
float barycentric[3] = {0, 0, 0};
if (f >= 0) {
float vt[2] = {float(pix % width) + 0.5f, float(pix / width) + 0.5f};
float *vt0_ptr = V + (F[f * 3] * 4);
float *vt1_ptr = V + (F[f * 3 + 1] * 4);
float *vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[2] = {
(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f};
float vt1[2] = {
(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f};
float vt2[2] = {
(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f};
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, barycentric);
barycentric[0] = barycentric[0] / vt0_ptr[3];
barycentric[1] = barycentric[1] / vt1_ptr[3];
barycentric[2] = barycentric[2] / vt2_ptr[3];
float w = 1.0f / (barycentric[0] + barycentric[1] + barycentric[2]);
barycentric[0] *= w;
barycentric[1] *= w;
barycentric[2] *= w;
}
barycentric_map[pix * 3] = barycentric[0];
barycentric_map[pix * 3 + 1] = barycentric[1];
barycentric_map[pix * 3 + 2] = barycentric[2];
}
void rasterizeImagecoordsKernelCPU(float *V, int *F, float *d, INT64 *zbuffer,
float occlusion_trunc, int width, int height,
int num_vertices, int num_faces, int f) {
float *vt0_ptr = V + (F[f * 3] * 4);
float *vt1_ptr = V + (F[f * 3 + 1] * 4);
float *vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[3] = {(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f,
vt0_ptr[2] / vt0_ptr[3] * 0.49999f + 0.5f};
float vt1[3] = {(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f,
vt1_ptr[2] / vt1_ptr[3] * 0.49999f + 0.5f};
float vt2[3] = {(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f,
vt2_ptr[2] / vt2_ptr[3] * 0.49999f + 0.5f};
rasterizeTriangleCPU(f, vt0, vt1, vt2, width, height, zbuffer, d,
occlusion_trunc);
}
std::vector<torch::Tensor> rasterize_image_cpu(torch::Tensor V, torch::Tensor F,
torch::Tensor D, int width,
int height,
float occlusion_truncation,
int use_depth_prior) {
int num_faces = F.size(0);
int num_vertices = V.size(0);
auto options =
torch::TensorOptions().dtype(torch::kInt32).requires_grad(false);
auto INT64_options =
torch::TensorOptions().dtype(torch::kInt64).requires_grad(false);
auto findices = torch::zeros({height, width}, options);
INT64 maxint = (INT64)MAXINT * (INT64)MAXINT + (MAXINT - 1);
auto z_min = torch::ones({height, width}, INT64_options) * (int64_t)maxint;
if (!use_depth_prior) {
for (int i = 0; i < num_faces; ++i) {
rasterizeImagecoordsKernelCPU(V.data_ptr<float>(), F.data_ptr<int>(), 0,
(INT64 *)z_min.data_ptr<int64_t>(),
occlusion_truncation, width, height,
num_vertices, num_faces, i);
}
} else {
for (int i = 0; i < num_faces; ++i)
rasterizeImagecoordsKernelCPU(
V.data_ptr<float>(), F.data_ptr<int>(), D.data_ptr<float>(),
(INT64 *)z_min.data_ptr<int64_t>(), occlusion_truncation, width,
height, num_vertices, num_faces, i);
}
auto float_options =
torch::TensorOptions().dtype(torch::kFloat32).requires_grad(false);
auto barycentric = torch::zeros({height, width, 3}, float_options);
for (int i = 0; i < width * height; ++i)
barycentricFromImgcoordCPU(
V.data_ptr<float>(), F.data_ptr<int>(), findices.data_ptr<int>(),
(INT64 *)z_min.data_ptr<int64_t>(), width, height, num_vertices,
num_faces, barycentric.data_ptr<float>(), i);
return {findices, barycentric};
}
std::vector<torch::Tensor>
rasterize_image(torch::Tensor V, torch::Tensor F, torch::Tensor D, int width,
int height, float occlusion_truncation, int use_depth_prior) {
#ifdef CUDA_ENABLED
return rasterize_image_gpu(V, F, D, width, height, occlusion_truncation,
use_depth_prior);
#else
return rasterize_image_cpu(V, F, D, width, height, occlusion_truncation,
use_depth_prior);
#endif
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("rasterize_image", &rasterize_image, "Custom image rasterization");
}
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#ifndef RASTERIZER_H_
#define RASTERIZER_H_
#include <ATen/ATen.h>
#include <torch/extension.h>
#include <vector>
#ifdef CUDA_ENABLED
#include <ATen/cuda/CUDAContext.h>
#else
#define __host__
#define __device__
#endif
#define INT64 unsigned long long
#define MAXINT 2147483647
__host__ __device__ inline float calculateSignedArea2(float *a, float *b,
float *c) {
return ((c[0] - a[0]) * (b[1] - a[1]) - (b[0] - a[0]) * (c[1] - a[1]));
}
__host__ __device__ inline void
calculateBarycentricCoordinate(float *a, float *b, float *c, float *p,
float *barycentric) {
float beta_tri = calculateSignedArea2(a, p, c);
float gamma_tri = calculateSignedArea2(a, b, p);
float area = calculateSignedArea2(a, b, c);
if (area == 0) {
barycentric[0] = -1.0;
barycentric[1] = -1.0;
barycentric[2] = -1.0;
return;
}
float tri_inv = 1.0 / area;
float beta = beta_tri * tri_inv;
float gamma = gamma_tri * tri_inv;
float alpha = 1.0 - beta - gamma;
barycentric[0] = alpha;
barycentric[1] = beta;
barycentric[2] = gamma;
}
__host__ __device__ inline bool
isBarycentricCoordInBounds(float *barycentricCoord) {
return barycentricCoord[0] >= 0.0 && barycentricCoord[0] <= 1.0 &&
barycentricCoord[1] >= 0.0 && barycentricCoord[1] <= 1.0 &&
barycentricCoord[2] >= 0.0 && barycentricCoord[2] <= 1.0;
}
std::vector<torch::Tensor> rasterize_image_gpu(torch::Tensor V, torch::Tensor F,
torch::Tensor D, int width,
int height,
float occlusion_truncation,
int use_depth_prior);
#endif
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#include "rasterizer.h"
__device__ void rasterizeTriangleGPU(int idx, float *vt0, float *vt1,
float *vt2, int width, int height,
INT64 *zbuffer, float *d,
float occlusion_truncation) {
float x_min = std::min(vt0[0], std::min(vt1[0], vt2[0]));
float x_max = std::max(vt0[0], std::max(vt1[0], vt2[0]));
float y_min = std::min(vt0[1], std::min(vt1[1], vt2[1]));
float y_max = std::max(vt0[1], std::max(vt1[1], vt2[1]));
for (int px = x_min; px < x_max + 1; ++px) {
if (px < 0 || px >= width)
continue;
for (int py = y_min; py < y_max + 1; ++py) {
if (py < 0 || py >= height)
continue;
float vt[2] = {px + 0.5f, py + 0.5f};
float baryCentricCoordinate[3];
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, baryCentricCoordinate);
if (isBarycentricCoordInBounds(baryCentricCoordinate)) {
int pixel = py * width + px;
if (zbuffer == 0) {
atomicExch(&zbuffer[pixel], (INT64)(idx + 1));
continue;
}
float depth = baryCentricCoordinate[0] * vt0[2] +
baryCentricCoordinate[1] * vt1[2] +
baryCentricCoordinate[2] * vt2[2];
float depth_thres = 0;
if (d) {
depth_thres = d[pixel] * 0.49999f + 0.5f + occlusion_truncation;
}
int z_quantize = depth * (2 << 17);
INT64 token = (INT64)z_quantize * MAXINT + (INT64)(idx + 1);
if (depth < depth_thres)
continue;
atomicMin(&zbuffer[pixel], token);
}
}
}
}
__global__ void barycentricFromImgcoordGPU(float *V, int *F, int *findices,
INT64 *zbuffer, int width,
int height, int num_vertices,
int num_faces,
float *barycentric_map) {
int pix = blockIdx.x * blockDim.x + threadIdx.x;
if (pix >= width * height)
return;
INT64 f = zbuffer[pix] % MAXINT;
if (f == (MAXINT - 1)) {
findices[pix] = 0;
barycentric_map[pix * 3] = 0;
barycentric_map[pix * 3 + 1] = 0;
barycentric_map[pix * 3 + 2] = 0;
return;
}
findices[pix] = f;
f -= 1;
float barycentric[3] = {0, 0, 0};
if (f >= 0) {
float vt[2] = {float(pix % width) + 0.5f, float(pix / width) + 0.5f};
float *vt0_ptr = V + (F[f * 3] * 4);
float *vt1_ptr = V + (F[f * 3 + 1] * 4);
float *vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[2] = {
(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f};
float vt1[2] = {
(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f};
float vt2[2] = {
(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f};
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, barycentric);
barycentric[0] = barycentric[0] / vt0_ptr[3];
barycentric[1] = barycentric[1] / vt1_ptr[3];
barycentric[2] = barycentric[2] / vt2_ptr[3];
float w = 1.0f / (barycentric[0] + barycentric[1] + barycentric[2]);
barycentric[0] *= w;
barycentric[1] *= w;
barycentric[2] *= w;
}
barycentric_map[pix * 3] = barycentric[0];
barycentric_map[pix * 3 + 1] = barycentric[1];
barycentric_map[pix * 3 + 2] = barycentric[2];
}
__global__ void rasterizeImagecoordsKernelGPU(float *V, int *F, float *d,
INT64 *zbuffer,
float occlusion_trunc, int width,
int height, int num_vertices,
int num_faces) {
int f = blockIdx.x * blockDim.x + threadIdx.x;
if (f >= num_faces)
return;
float *vt0_ptr = V + (F[f * 3] * 4);
float *vt1_ptr = V + (F[f * 3 + 1] * 4);
float *vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[3] = {(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f,
vt0_ptr[2] / vt0_ptr[3] * 0.49999f + 0.5f};
float vt1[3] = {(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f,
vt1_ptr[2] / vt1_ptr[3] * 0.49999f + 0.5f};
float vt2[3] = {(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f,
(0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f,
vt2_ptr[2] / vt2_ptr[3] * 0.49999f + 0.5f};
rasterizeTriangleGPU(f, vt0, vt1, vt2, width, height, zbuffer, d,
occlusion_trunc);
}
std::vector<torch::Tensor> rasterize_image_gpu(torch::Tensor V, torch::Tensor F,
torch::Tensor D, int width,
int height,
float occlusion_truncation,
int use_depth_prior) {
int device_id = V.get_device();
cudaSetDevice(device_id);
int num_faces = F.size(0);
int num_vertices = V.size(0);
auto options = torch::TensorOptions()
.dtype(torch::kInt32)
.device(torch::kCUDA, device_id)
.requires_grad(false);
auto INT64_options = torch::TensorOptions()
.dtype(torch::kInt64)
.device(torch::kCUDA, device_id)
.requires_grad(false);
auto findices = torch::zeros({height, width}, options);
INT64 maxint = (INT64)MAXINT * (INT64)MAXINT + (MAXINT - 1);
auto z_min = torch::ones({height, width}, INT64_options) * (int64_t)maxint;
if (!use_depth_prior) {
rasterizeImagecoordsKernelGPU<<<(num_faces + 255) / 256, 256, 0,
at::cuda::getCurrentCUDAStream()>>>(
V.data_ptr<float>(), F.data_ptr<int>(), 0,
(INT64 *)z_min.data_ptr<int64_t>(), occlusion_truncation, width, height,
num_vertices, num_faces);
} else {
rasterizeImagecoordsKernelGPU<<<(num_faces + 255) / 256, 256, 0,
at::cuda::getCurrentCUDAStream()>>>(
V.data_ptr<float>(), F.data_ptr<int>(), D.data_ptr<float>(),
(INT64 *)z_min.data_ptr<int64_t>(), occlusion_truncation, width, height,
num_vertices, num_faces);
}
auto float_options = torch::TensorOptions()
.dtype(torch::kFloat32)
.device(torch::kCUDA, device_id)
.requires_grad(false);
auto barycentric = torch::zeros({height, width, 3}, float_options);
barycentricFromImgcoordGPU<<<(width * height + 255) / 256, 256, 0,
at::cuda::getCurrentCUDAStream()>>>(
V.data_ptr<float>(), F.data_ptr<int>(), findices.data_ptr<int>(),
(INT64 *)z_min.data_ptr<int64_t>(), width, height, num_vertices,
num_faces, barycentric.data_ptr<float>());
return {findices, barycentric};
}
@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import logging
import os import os
import shutil import shutil
import sys import sys
@@ -8,9 +9,7 @@ from typing import Any, Sequence
import torch import torch
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger logger = logging.getLogger(__name__)
logger = init_logger(__name__)
def _get_build_directory(name: str) -> Path: def _get_build_directory(name: str) -> Path:
@@ -13,7 +13,7 @@ from typing import Tuple
import numpy as np import numpy as np
from sglang.kernels.ops.diffusion.render import load_extension_with_recovery from sglang.kernels.ops.diffusion.ext.loader import load_extension_with_recovery
_abs_path = os.path.dirname(os.path.abspath(__file__)) _abs_path = os.path.dirname(os.path.abspath(__file__))
_mesh_processor_kernel = None _mesh_processor_kernel = None
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#include <algorithm>
#include <cmath>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <queue>
#include <torch/extension.h>
#include <vector>
namespace py = pybind11;
using namespace std;
std::pair<py::array_t<float>, py::array_t<uint8_t>>
meshVerticeInpaint_smooth(py::array_t<float> texture, py::array_t<uint8_t> mask,
py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
py::array_t<int> pos_idx, py::array_t<int> uv_idx) {
auto texture_buf = texture.request();
auto mask_buf = mask.request();
auto vtx_pos_buf = vtx_pos.request();
auto vtx_uv_buf = vtx_uv.request();
auto pos_idx_buf = pos_idx.request();
auto uv_idx_buf = uv_idx.request();
int texture_height = texture_buf.shape[0];
int texture_width = texture_buf.shape[1];
int texture_channel = texture_buf.shape[2];
float *texture_ptr = static_cast<float *>(texture_buf.ptr);
uint8_t *mask_ptr = static_cast<uint8_t *>(mask_buf.ptr);
int vtx_num = vtx_pos_buf.shape[0];
float *vtx_pos_ptr = static_cast<float *>(vtx_pos_buf.ptr);
float *vtx_uv_ptr = static_cast<float *>(vtx_uv_buf.ptr);
int *pos_idx_ptr = static_cast<int *>(pos_idx_buf.ptr);
int *uv_idx_ptr = static_cast<int *>(uv_idx_buf.ptr);
vector<float> vtx_mask(vtx_num, 0.0f);
vector<vector<float>> vtx_color(vtx_num,
vector<float>(texture_channel, 0.0f));
vector<int> uncolored_vtxs;
vector<vector<int>> G(vtx_num);
for (int i = 0; i < uv_idx_buf.shape[0]; ++i) {
for (int k = 0; k < 3; ++k) {
int vtx_uv_idx = uv_idx_ptr[i * 3 + k];
int vtx_idx = pos_idx_ptr[i * 3 + k];
int uv_v = round(vtx_uv_ptr[vtx_uv_idx * 2] * (texture_width - 1));
int uv_u =
round((1.0 - vtx_uv_ptr[vtx_uv_idx * 2 + 1]) * (texture_height - 1));
if (mask_ptr[uv_u * texture_width + uv_v] > 0) {
vtx_mask[vtx_idx] = 1.0f;
for (int c = 0; c < texture_channel; ++c) {
vtx_color[vtx_idx][c] =
texture_ptr[(uv_u * texture_width + uv_v) * texture_channel + c];
}
} else {
uncolored_vtxs.push_back(vtx_idx);
}
G[pos_idx_ptr[i * 3 + k]].push_back(pos_idx_ptr[i * 3 + (k + 1) % 3]);
}
}
int smooth_count = 2;
int last_uncolored_vtx_count = 0;
while (smooth_count > 0) {
int uncolored_vtx_count = 0;
for (int vtx_idx : uncolored_vtxs) {
vector<float> sum_color(texture_channel, 0.0f);
float total_weight = 0.0f;
array<float, 3> vtx_0 = {vtx_pos_ptr[vtx_idx * 3],
vtx_pos_ptr[vtx_idx * 3 + 1],
vtx_pos_ptr[vtx_idx * 3 + 2]};
for (int connected_idx : G[vtx_idx]) {
if (vtx_mask[connected_idx] > 0) {
array<float, 3> vtx1 = {vtx_pos_ptr[connected_idx * 3],
vtx_pos_ptr[connected_idx * 3 + 1],
vtx_pos_ptr[connected_idx * 3 + 2]};
float dist_weight = 1.0f / max(sqrt(pow(vtx_0[0] - vtx1[0], 2) +
pow(vtx_0[1] - vtx1[1], 2) +
pow(vtx_0[2] - vtx1[2], 2)),
1E-4);
dist_weight = dist_weight * dist_weight;
for (int c = 0; c < texture_channel; ++c) {
sum_color[c] += vtx_color[connected_idx][c] * dist_weight;
}
total_weight += dist_weight;
}
}
if (total_weight > 0.0f) {
for (int c = 0; c < texture_channel; ++c) {
vtx_color[vtx_idx][c] = sum_color[c] / total_weight;
}
vtx_mask[vtx_idx] = 1.0f;
} else {
uncolored_vtx_count++;
}
}
if (last_uncolored_vtx_count == uncolored_vtx_count) {
smooth_count--;
} else {
smooth_count++;
}
last_uncolored_vtx_count = uncolored_vtx_count;
}
py::array_t<float> new_texture(texture_buf.size);
py::array_t<uint8_t> new_mask(mask_buf.size);
auto new_texture_buf = new_texture.request();
auto new_mask_buf = new_mask.request();
float *new_texture_ptr = static_cast<float *>(new_texture_buf.ptr);
uint8_t *new_mask_ptr = static_cast<uint8_t *>(new_mask_buf.ptr);
std::copy(texture_ptr, texture_ptr + texture_buf.size, new_texture_ptr);
std::copy(mask_ptr, mask_ptr + mask_buf.size, new_mask_ptr);
for (int face_idx = 0; face_idx < uv_idx_buf.shape[0]; ++face_idx) {
for (int k = 0; k < 3; ++k) {
int vtx_uv_idx = uv_idx_ptr[face_idx * 3 + k];
int vtx_idx = pos_idx_ptr[face_idx * 3 + k];
if (vtx_mask[vtx_idx] == 1.0f) {
int uv_v = round(vtx_uv_ptr[vtx_uv_idx * 2] * (texture_width - 1));
int uv_u = round((1.0 - vtx_uv_ptr[vtx_uv_idx * 2 + 1]) *
(texture_height - 1));
for (int c = 0; c < texture_channel; ++c) {
new_texture_ptr[(uv_u * texture_width + uv_v) * texture_channel + c] =
vtx_color[vtx_idx][c];
}
new_mask_ptr[uv_u * texture_width + uv_v] = 255;
}
}
}
new_texture.resize({texture_height, texture_width, 3});
new_mask.resize({texture_height, texture_width});
return std::make_pair(new_texture, new_mask);
}
std::pair<py::array_t<float>, py::array_t<uint8_t>>
meshVerticeInpaint(py::array_t<float> texture, py::array_t<uint8_t> mask,
py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
py::array_t<int> pos_idx, py::array_t<int> uv_idx,
const std::string &method = "smooth") {
if (method == "smooth") {
return meshVerticeInpaint_smooth(texture, mask, vtx_pos, vtx_uv, pos_idx,
uv_idx);
} else {
throw std::invalid_argument("Invalid method. Use 'smooth'.");
}
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("meshVerticeInpaint", &meshVerticeInpaint,
"Mesh-aware texture inpainting", py::arg("texture"), py::arg("mask"),
py::arg("vtx_pos"), py::arg("vtx_uv"), py::arg("pos_idx"),
py::arg("uv_idx"), py::arg("method") = "smooth");
}
@@ -0,0 +1,5 @@
"""Pure data-movement kernels: sequence-parallel relayout, varlen pack/scatter, causal padding.
Every kernel here only moves values (plus zero fill), so each is bitwise
identical to the aten chain it replaces.
"""
@@ -0,0 +1 @@
"""adaLN modulation: ``x * (1 + scale) + shift``, gating, and timestep conditioning."""
@@ -4,7 +4,7 @@ import torch
import triton import triton
import triton.language as tl import triton.language as tl
from sglang.kernels.ops.diffusion.triton.numerics import round_bf16_to_fp32 from sglang.kernels.ops.diffusion.common.numerics import round_bf16_to_fp32
@triton.jit @triton.jit
@@ -2,8 +2,13 @@ import torch
import triton # type: ignore import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.ops.diffusion.triton.numerics import mul_rn_f32 from sglang.kernels.ops.diffusion.common.numerics import mul_rn_f32
from sglang.multimodal_gen.runtime.platforms import current_platform from sglang.kernels.ops.diffusion.common.platform import (
is_cuda,
is_hip,
lazy_fallback,
select_impl,
)
@triton.jit @triton.jit
@@ -33,7 +38,7 @@ def try_fused_scaled_residual_add_exact(
) -> torch.Tensor | None: ) -> torch.Tensor | None:
"""Fuse ``residual + x * scale`` without changing eager FP32 rounding.""" """Fuse ``residual + x * scale`` without changing eager FP32 rounding."""
if ( if (
not current_platform.is_cuda() not is_cuda()
or torch.is_grad_enabled() or torch.is_grad_enabled()
or torch.compiler.is_compiling() or torch.compiler.is_compiling()
or residual.dtype != torch.float32 or residual.dtype != torch.float32
@@ -422,7 +427,7 @@ def fuse_scale_shift_kernel(
# Compact scale [B, F, 1, C] -> [B*F, C] (per-frame) # Compact scale [B, F, 1, C] -> [B*F, C] (per-frame)
scale_reshaped = scale.squeeze(2).reshape(-1, C).contiguous() scale_reshaped = scale.squeeze(2).reshape(-1, C).contiguous()
if shift.dim() == 4 and current_platform.is_hip(): if shift.dim() == 4 and is_hip():
# ROCm has no fused CUTLASS scale-shift kernel, so this native path # ROCm has no fused CUTLASS scale-shift kernel, so this native path
# handles the causal Wan / LingBot output AdaLN, which passes a # handles the causal Wan / LingBot output AdaLN, which passes a
# per-frame shift [B, F, 1, C]. Broadcast it across each frame's # per-frame shift [B, F, 1, C]. Broadcast it across each frame's
@@ -728,24 +733,10 @@ def fuse_residual_layernorm_scale_shift_gate_select01_kernel(
return output, residual_out, gate_out return output, residual_out, gate_out
if current_platform.is_npu(): fuse_scale_shift_kernel = select_impl(
from .npu_fallback import fuse_scale_shift_native fuse_scale_shift_kernel,
npu=lazy_fallback("npu", "fuse_scale_shift_native"),
fuse_scale_shift_kernel = fuse_scale_shift_native mps=lazy_fallback("mps", "fuse_scale_shift_kernel_native"),
musa=lazy_fallback("torch", "fuse_scale_shift_kernel_native"),
if current_platform.is_mps(): cpu=lazy_fallback("torch", "fuse_scale_shift_kernel_native"),
from .mps_fallback import fuse_scale_shift_kernel_native )
fuse_scale_shift_kernel = fuse_scale_shift_kernel_native
if current_platform.is_musa():
from .torch_fallback import fuse_scale_shift_kernel_native
fuse_scale_shift_kernel = fuse_scale_shift_kernel_native
if current_platform.is_cpu():
from .torch_fallback import (
fuse_scale_shift_kernel_native,
)
fuse_scale_shift_kernel = fuse_scale_shift_kernel_native
@@ -0,0 +1,7 @@
"""Normalization kernels: RMSNorm / LayerNorm / GroupNorm and their fused epilogues.
Which implementation to pick is documented in the selection matrix in
``sglang/kernels/ops/diffusion/README.md`` -- there are several per norm type
and they differ by numerical contract (bit-exact vs close), activation layout
and backend, not by speed alone.
"""
@@ -0,0 +1 @@
"""CuTe-DSL building blocks shared by the CUTLASS-backed norm fusions."""
@@ -5,7 +5,7 @@ import cutlass.cute as cute
import torch import torch
from einops import rearrange from einops import rearrange
from sglang.kernels.ops.diffusion.cutedsl.common.reduce import ( from sglang.kernels.ops.diffusion.norm.cutedsl_common.reduce import (
cta_reduce_sum, cta_reduce_sum,
warp_reduce_sum, warp_reduce_sum,
) )
@@ -18,7 +18,7 @@ def apply_group_norm_silu(
and norm.weight is not None and norm.weight is not None
and norm.bias is not None and norm.bias is not None
): ):
from sglang.kernels.ops.diffusion.triton.group_norm_silu import ( from sglang.kernels.ops.diffusion.norm.group_norm_silu_triton import (
triton_group_norm_silu, triton_group_norm_silu,
) )
@@ -16,8 +16,9 @@ kernel for the channels_last VAE decoder fast path with a different contract:
pure elementwise kernel; pure elementwise kernel;
- optional SiLU epilogue (``apply_silu=False`` gives plain GroupNorm); - optional SiLU epilogue (``apply_silu=False`` gives plain GroupNorm);
- restricted static shapes: power-of-two ``C <= 2048`` that ``num_groups`` - restricted static shapes: power-of-two ``C <= 2048`` that ``num_groups``
divides. Callers must treat a ``None`` return as "unsupported" and fall divides. Support is a predicate (``can_use_group_norm_silu_4d`` /
back to their reference path. ``can_use_group_norm_silu_rows``); the kernels raise on an unsupported
input rather than silently returning ``None``.
""" """
import torch import torch
@@ -185,6 +186,39 @@ def _twopass_supported(x, weight, bias, num_groups) -> bool:
return triton.next_power_of_2(c) == c and c <= _MAX_CHANNELS return triton.next_power_of_2(c) == c and c <= _MAX_CHANNELS
def can_use_group_norm_silu_4d(
x: torch.Tensor,
weight: torch.Tensor,
bias: torch.Tensor,
num_groups: int,
) -> bool:
"""Whether :func:`group_norm_silu_4d` supports this channels_last input."""
if x.dim() != 4 or not _twopass_supported(x, weight, bias, num_groups):
return False
_, c, h, w = x.shape
# c > 1 and a non-trivial spatial extent make the channels_last check
# unambiguous (degenerate shapes are contiguous in both formats).
return (
c > 1
and (h > 1 or w > 1)
and x.is_contiguous(memory_format=torch.channels_last)
)
def can_use_group_norm_silu_rows(
x3: torch.Tensor,
weight: torch.Tensor,
bias: torch.Tensor,
num_groups: int,
) -> bool:
"""Whether :func:`group_norm_silu_rows` supports this (N, L, C) input."""
return (
x3.dim() == 3
and x3.is_contiguous()
and _twopass_supported(x3, weight, bias, num_groups)
)
def group_norm_silu_4d( def group_norm_silu_4d(
x: torch.Tensor, x: torch.Tensor,
weight: torch.Tensor, weight: torch.Tensor,
@@ -192,24 +226,16 @@ def group_norm_silu_4d(
num_groups: int, num_groups: int,
eps: float, eps: float,
apply_silu: bool = True, apply_silu: bool = True,
) -> torch.Tensor | None: ) -> torch.Tensor:
"""Fused GroupNorm(+SiLU) for a channels_last 4D (N, C, H, W) activation. """Fused GroupNorm(+SiLU) for a channels_last 4D (N, C, H, W) activation.
Runs the rows kernel on the free (N, H*W, C) view (no layout copy) and Runs the rows kernel on the free (N, H*W, C) view (no layout copy) and
preserves the channels_last output layout. Returns ``None`` when the preserves the channels_last output layout. Guard with
input is unsupported; callers must fall back to their reference path. :func:`can_use_group_norm_silu_4d`.
""" """
if x.dim() != 4 or not _twopass_supported(x, weight, bias, num_groups): if not can_use_group_norm_silu_4d(x, weight, bias, num_groups):
return None raise ValueError("unsupported input for group_norm_silu_4d")
n_batch, c, h, w = x.shape n_batch, c, h, w = x.shape
# c > 1 and a non-trivial spatial extent make the channels_last check
# unambiguous (degenerate shapes are contiguous in both formats).
if not (
c > 1
and (h > 1 or w > 1)
and x.is_contiguous(memory_format=torch.channels_last)
):
return None
x3 = x.permute(0, 2, 3, 1).reshape(n_batch, h * w, c) x3 = x.permute(0, 2, 3, 1).reshape(n_batch, h * w, c)
y3 = _gn_silu_rows(x3, weight, bias, num_groups, eps, apply_silu) y3 = _gn_silu_rows(x3, weight, bias, num_groups, eps, apply_silu)
return y3.reshape(n_batch, h, w, c).permute(0, 3, 1, 2) return y3.reshape(n_batch, h, w, c).permute(0, 3, 1, 2)
@@ -222,19 +248,19 @@ def group_norm_silu_rows(
num_groups: int, num_groups: int,
eps: float, eps: float,
apply_silu: bool = True, apply_silu: bool = True,
) -> torch.Tensor | None: ) -> torch.Tensor:
"""Fused GroupNorm(+SiLU) over (N, L, C) rows (C = channels, innermost). """Fused GroupNorm(+SiLU) over (N, L, C) rows (C = channels, innermost).
Returns ``None`` when the input is unsupported; callers must fall back. Guard with :func:`can_use_group_norm_silu_rows`.
""" """
if x3.dim() != 3 or not x3.is_contiguous(): if not can_use_group_norm_silu_rows(x3, weight, bias, num_groups):
return None raise ValueError("unsupported input for group_norm_silu_rows")
if not _twopass_supported(x3, weight, bias, num_groups):
return None
return _gn_silu_rows(x3, weight, bias, num_groups, eps, apply_silu) return _gn_silu_rows(x3, weight, bias, num_groups, eps, apply_silu)
__all__ = [ __all__ = [
"can_use_group_norm_silu_4d",
"can_use_group_norm_silu_rows",
"group_norm_silu_4d", "group_norm_silu_4d",
"group_norm_silu_rows", "group_norm_silu_rows",
] ]
@@ -47,7 +47,7 @@ import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.jit.utils import get_jit_cuda_arch from sglang.kernels.jit.utils import get_jit_cuda_arch
from sglang.kernels.ops.diffusion.triton.numerics import ( from sglang.kernels.ops.diffusion.common.numerics import (
cuda_rsqrtf, cuda_rsqrtf,
div_rn_f32, div_rn_f32,
round_bf16_to_fp32, round_bf16_to_fp32,
@@ -5,7 +5,7 @@ import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from torch import Tensor from torch import Tensor
from sglang.multimodal_gen.runtime.platforms import current_platform from sglang.kernels.ops.diffusion.common.platform import lazy_fallback, select_impl
from sglang.srt.utils.custom_op import register_custom_op from sglang.srt.utils.custom_op import register_custom_op
@@ -647,14 +647,13 @@ def norm_infer(
return out return out
if current_platform.is_mps(): norm_infer = select_impl(
from .mps_fallback import norm_infer_native, rms_norm_fn_native norm_infer,
mps=lazy_fallback("mps", "norm_infer_native"),
norm_infer = norm_infer_native cpu=lazy_fallback("torch", "norm_infer_native"),
rms_norm_fn = rms_norm_fn_native )
rms_norm_fn = select_impl(
if current_platform.is_cpu(): rms_norm_fn,
from .torch_fallback import norm_infer_native, rms_norm_fn_native mps=lazy_fallback("mps", "rms_norm_fn_native"),
cpu=lazy_fallback("torch", "rms_norm_fn_native"),
norm_infer = norm_infer_native )
rms_norm_fn = rms_norm_fn_native
@@ -3,7 +3,7 @@ import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.kernel_api_logging import debug_kernel_api from sglang.kernels.kernel_api_logging import debug_kernel_api
from sglang.multimodal_gen.runtime.platforms import current_platform from sglang.kernels.ops.diffusion.common.platform import lazy_fallback, select_impl
from sglang.srt.utils.custom_op import register_custom_op from sglang.srt.utils.custom_op import register_custom_op
@@ -69,15 +69,9 @@ def triton_one_pass_rms_norm(x: torch.Tensor, w: torch.Tensor, eps: float = 1e-6
return _triton_one_pass_rms_norm_cuda(x, w, eps) return _triton_one_pass_rms_norm_cuda(x, w, eps)
if current_platform.is_mps(): triton_one_pass_rms_norm = select_impl(
from .mps_fallback import triton_one_pass_rms_norm_native triton_one_pass_rms_norm,
# MPS keeps the api-logging wrapper the Triton entry point carries.
@debug_kernel_api mps=debug_kernel_api(lazy_fallback("mps", "triton_one_pass_rms_norm_native")),
def triton_one_pass_rms_norm(x: torch.Tensor, w: torch.Tensor, eps: float = 1e-6): cpu=lazy_fallback("torch", "triton_one_pass_rms_norm_native"),
return triton_one_pass_rms_norm_native(x, w, eps) )
if current_platform.is_cpu():
from .torch_fallback import triton_one_pass_rms_norm_native
triton_one_pass_rms_norm = triton_one_pass_rms_norm_native
@@ -55,7 +55,7 @@ import torch
import triton # type: ignore import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.ops.diffusion.triton.numerics import ( from sglang.kernels.ops.diffusion.common.numerics import (
mul_rn_f32, mul_rn_f32,
round_bf16_to_fp32, round_bf16_to_fp32,
rsqrt_approx_f32, rsqrt_approx_f32,
@@ -5,12 +5,12 @@ import cutlass
import cutlass.cute as cute import cutlass.cute as cute
import torch import torch
from sglang.kernels.ops.diffusion.cutedsl.common.norm_fusion import ( from sglang.kernels.ops.diffusion.norm.cutedsl_common.norm_fusion import (
apply_norm_cta, apply_norm_cta,
broadcast_tensor_for_bsfd, broadcast_tensor_for_bsfd,
tensor_slice_for_bsfd, tensor_slice_for_bsfd,
) )
from sglang.kernels.ops.diffusion.cutedsl.utils import ( from sglang.kernels.ops.diffusion.norm.cutedsl_utils import (
WARP_SIZE, WARP_SIZE,
to_fake_cute_args, to_fake_cute_args,
) )
@@ -263,7 +263,7 @@ def fused_norm_scale_shift(
D must be a multiple of 256 and <= 8192 to enable LDG.128 vectorized loads per D must be a multiple of 256 and <= 8192 to enable LDG.128 vectorized loads per
thread and avoid predicated loads (e.g., bounds checks such as `index < D`). thread and avoid predicated loads (e.g., bounds checks such as `index < D`).
""" """
from sglang.kernels.ops.diffusion.norm_scale_shift_native import ( from sglang.kernels.ops.diffusion.norm.norm_scale_shift_jit import (
try_fused_norm_scale_shift as _try_qwen_native_norm_scale_shift, try_fused_norm_scale_shift as _try_qwen_native_norm_scale_shift,
) )
@@ -349,7 +349,7 @@ def fused_scale_residual_norm_scale_shift(
D must be a multiple of 256 and <= 8192 to enable LDG.128 vectorized loads per D must be a multiple of 256 and <= 8192 to enable LDG.128 vectorized loads per
thread and avoid predicated loads (e.g., bounds checks such as `index < D`). thread and avoid predicated loads (e.g., bounds checks such as `index < D`).
""" """
from sglang.kernels.ops.diffusion.norm_scale_shift_native import ( from sglang.kernels.ops.diffusion.norm.norm_scale_shift_jit import (
try_fused_scale_residual_norm_scale_shift as _try_qwen_native_residual_path, try_fused_scale_residual_norm_scale_shift as _try_qwen_native_residual_path,
) )
@@ -11,9 +11,8 @@ the same dtype boundary as eager ``WanRMS_norm.forward`` (including the
aten promotion to fp32 at ``* gamma`` for half-precision x with fp32 affine aten promotion to fp32 at ``* gamma`` for half-precision x with fp32 affine
params -- the autocast case), SiLU in fp32. Bitwise equality with aten is params -- the autocast case), SiLU in fp32. Bitwise equality with aten is
still not guaranteed (different reduction and SiLU paths), so callers must still not guaranteed (different reduction and SiLU paths), so callers must
keep this behind an opt-in gate. ``wan_rmsnorm_silu`` returns ``None`` for keep this behind an opt-in gate. Support is a predicate
unsupported inputs (see ``can_use_wan_rmsnorm_silu``); callers must fall (``can_use_wan_rmsnorm_silu``); the kernel raises on an unsupported input.
back to their reference path.
""" """
from __future__ import annotations from __future__ import annotations
@@ -175,13 +174,13 @@ def wan_rmsnorm_silu(
bias: torch.Tensor | None = None, bias: torch.Tensor | None = None,
rms_scale: float | None = None, rms_scale: float | None = None,
eps: float = 1e-12, eps: float = 1e-12,
) -> torch.Tensor | None: ) -> torch.Tensor:
"""Fused ``SiLU(F.normalize(x, dim=1) * rms_scale * gamma + bias)``. """Fused ``SiLU(F.normalize(x, dim=1) * rms_scale * gamma + bias)``.
Returns ``None`` when the input is unsupported; callers must fall back. Guard with :func:`can_use_wan_rmsnorm_silu`.
""" """
if not can_use_wan_rmsnorm_silu(x, gamma, bias): if not can_use_wan_rmsnorm_silu(x, gamma, bias):
return None raise ValueError("unsupported input for wan_rmsnorm_silu")
channels = x.shape[1] channels = x.shape[1]
gamma = gamma.reshape(channels).contiguous() gamma = gamma.reshape(channels).contiguous()
@@ -1,140 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#include "rasterizer.h"
void rasterizeTriangleCPU(int idx, float* vt0, float* vt1, float* vt2, int width, int height, INT64* zbuffer, float* d, float occlusion_truncation) {
float x_min = std::min(vt0[0], std::min(vt1[0],vt2[0]));
float x_max = std::max(vt0[0], std::max(vt1[0],vt2[0]));
float y_min = std::min(vt0[1], std::min(vt1[1],vt2[1]));
float y_max = std::max(vt0[1], std::max(vt1[1],vt2[1]));
for (int px = x_min; px < x_max + 1; ++px) {
if (px < 0 || px >= width)
continue;
for (int py = y_min; py < y_max + 1; ++py) {
if (py < 0 || py >= height)
continue;
float vt[2] = {px + 0.5f, py + 0.5f};
float baryCentricCoordinate[3];
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, baryCentricCoordinate);
if (isBarycentricCoordInBounds(baryCentricCoordinate)) {
int pixel = py * width + px;
if (zbuffer == 0) {
zbuffer[pixel] = (INT64)(idx + 1);
continue;
}
float depth = baryCentricCoordinate[0] * vt0[2] + baryCentricCoordinate[1] * vt1[2] + baryCentricCoordinate[2] * vt2[2];
float depth_thres = 0;
if (d) {
depth_thres = d[pixel] * 0.49999f + 0.5f + occlusion_truncation;
}
int z_quantize = depth * (2<<17);
INT64 token = (INT64)z_quantize * MAXINT + (INT64)(idx + 1);
if (depth < depth_thres)
continue;
zbuffer[pixel] = std::min(zbuffer[pixel], token);
}
}
}
}
void barycentricFromImgcoordCPU(float* V, int* F, int* findices, INT64* zbuffer, int width, int height, int num_vertices, int num_faces,
float* barycentric_map, int pix)
{
INT64 f = zbuffer[pix] % MAXINT;
if (f == (MAXINT-1)) {
findices[pix] = 0;
barycentric_map[pix * 3] = 0;
barycentric_map[pix * 3 + 1] = 0;
barycentric_map[pix * 3 + 2] = 0;
return;
}
findices[pix] = f;
f -= 1;
float barycentric[3] = {0, 0, 0};
if (f >= 0) {
float vt[2] = {float(pix % width) + 0.5f, float(pix / width) + 0.5f};
float* vt0_ptr = V + (F[f * 3] * 4);
float* vt1_ptr = V + (F[f * 3 + 1] * 4);
float* vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[2] = {(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f};
float vt1[2] = {(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f};
float vt2[2] = {(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f};
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, barycentric);
barycentric[0] = barycentric[0] / vt0_ptr[3];
barycentric[1] = barycentric[1] / vt1_ptr[3];
barycentric[2] = barycentric[2] / vt2_ptr[3];
float w = 1.0f / (barycentric[0] + barycentric[1] + barycentric[2]);
barycentric[0] *= w;
barycentric[1] *= w;
barycentric[2] *= w;
}
barycentric_map[pix * 3] = barycentric[0];
barycentric_map[pix * 3 + 1] = barycentric[1];
barycentric_map[pix * 3 + 2] = barycentric[2];
}
void rasterizeImagecoordsKernelCPU(float* V, int* F, float* d, INT64* zbuffer, float occlusion_trunc, int width, int height, int num_vertices, int num_faces, int f)
{
float* vt0_ptr = V + (F[f * 3] * 4);
float* vt1_ptr = V + (F[f * 3 + 1] * 4);
float* vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[3] = {(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f, vt0_ptr[2] / vt0_ptr[3] * 0.49999f + 0.5f};
float vt1[3] = {(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f, vt1_ptr[2] / vt1_ptr[3] * 0.49999f + 0.5f};
float vt2[3] = {(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f, vt2_ptr[2] / vt2_ptr[3] * 0.49999f + 0.5f};
rasterizeTriangleCPU(f, vt0, vt1, vt2, width, height, zbuffer, d, occlusion_trunc);
}
std::vector<torch::Tensor> rasterize_image_cpu(torch::Tensor V, torch::Tensor F, torch::Tensor D,
int width, int height, float occlusion_truncation, int use_depth_prior)
{
int num_faces = F.size(0);
int num_vertices = V.size(0);
auto options = torch::TensorOptions().dtype(torch::kInt32).requires_grad(false);
auto INT64_options = torch::TensorOptions().dtype(torch::kInt64).requires_grad(false);
auto findices = torch::zeros({height, width}, options);
INT64 maxint = (INT64)MAXINT * (INT64)MAXINT + (MAXINT - 1);
auto z_min = torch::ones({height, width}, INT64_options) * (int64_t)maxint;
if (!use_depth_prior) {
for (int i = 0; i < num_faces; ++i) {
rasterizeImagecoordsKernelCPU(V.data_ptr<float>(), F.data_ptr<int>(), 0,
(INT64*)z_min.data_ptr<int64_t>(), occlusion_truncation, width, height, num_vertices, num_faces, i);
}
} else {
for (int i = 0; i < num_faces; ++i)
rasterizeImagecoordsKernelCPU(V.data_ptr<float>(), F.data_ptr<int>(), D.data_ptr<float>(),
(INT64*)z_min.data_ptr<int64_t>(), occlusion_truncation, width, height, num_vertices, num_faces, i);
}
auto float_options = torch::TensorOptions().dtype(torch::kFloat32).requires_grad(false);
auto barycentric = torch::zeros({height, width, 3}, float_options);
for (int i = 0; i < width * height; ++i)
barycentricFromImgcoordCPU(V.data_ptr<float>(), F.data_ptr<int>(),
findices.data_ptr<int>(), (INT64*)z_min.data_ptr<int64_t>(), width, height, num_vertices, num_faces, barycentric.data_ptr<float>(), i);
return {findices, barycentric};
}
std::vector<torch::Tensor> rasterize_image(torch::Tensor V, torch::Tensor F, torch::Tensor D,
int width, int height, float occlusion_truncation, int use_depth_prior)
{
#ifdef CUDA_ENABLED
return rasterize_image_gpu(V, F, D, width, height, occlusion_truncation, use_depth_prior);
#else
return rasterize_image_cpu(V, F, D, width, height, occlusion_truncation, use_depth_prior);
#endif
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("rasterize_image", &rasterize_image, "Custom image rasterization");
}
@@ -1,56 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#ifndef RASTERIZER_H_
#define RASTERIZER_H_
#include <torch/extension.h>
#include <vector>
#include <ATen/ATen.h>
#ifdef CUDA_ENABLED
#include <ATen/cuda/CUDAContext.h>
#else
#define __host__
#define __device__
#endif
#define INT64 unsigned long long
#define MAXINT 2147483647
__host__ __device__ inline float calculateSignedArea2(float* a, float* b, float* c) {
return ((c[0] - a[0]) * (b[1] - a[1]) - (b[0] - a[0]) * (c[1] - a[1]));
}
__host__ __device__ inline void calculateBarycentricCoordinate(float* a, float* b, float* c, float* p,
float* barycentric)
{
float beta_tri = calculateSignedArea2(a, p, c);
float gamma_tri = calculateSignedArea2(a, b, p);
float area = calculateSignedArea2(a, b, c);
if (area == 0) {
barycentric[0] = -1.0;
barycentric[1] = -1.0;
barycentric[2] = -1.0;
return;
}
float tri_inv = 1.0 / area;
float beta = beta_tri * tri_inv;
float gamma = gamma_tri * tri_inv;
float alpha = 1.0 - beta - gamma;
barycentric[0] = alpha;
barycentric[1] = beta;
barycentric[2] = gamma;
}
__host__ __device__ inline bool isBarycentricCoordInBounds(float* barycentricCoord) {
return barycentricCoord[0] >= 0.0 && barycentricCoord[0] <= 1.0 &&
barycentricCoord[1] >= 0.0 && barycentricCoord[1] <= 1.0 &&
barycentricCoord[2] >= 0.0 && barycentricCoord[2] <= 1.0;
}
std::vector<torch::Tensor> rasterize_image_gpu(torch::Tensor V, torch::Tensor F, torch::Tensor D,
int width, int height, float occlusion_truncation, int use_depth_prior);
#endif
@@ -1,130 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#include "rasterizer.h"
__device__ void rasterizeTriangleGPU(int idx, float* vt0, float* vt1, float* vt2, int width, int height, INT64* zbuffer, float* d, float occlusion_truncation) {
float x_min = std::min(vt0[0], std::min(vt1[0],vt2[0]));
float x_max = std::max(vt0[0], std::max(vt1[0],vt2[0]));
float y_min = std::min(vt0[1], std::min(vt1[1],vt2[1]));
float y_max = std::max(vt0[1], std::max(vt1[1],vt2[1]));
for (int px = x_min; px < x_max + 1; ++px) {
if (px < 0 || px >= width)
continue;
for (int py = y_min; py < y_max + 1; ++py) {
if (py < 0 || py >= height)
continue;
float vt[2] = {px + 0.5f, py + 0.5f};
float baryCentricCoordinate[3];
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, baryCentricCoordinate);
if (isBarycentricCoordInBounds(baryCentricCoordinate)) {
int pixel = py * width + px;
if (zbuffer == 0) {
atomicExch(&zbuffer[pixel], (INT64)(idx + 1));
continue;
}
float depth = baryCentricCoordinate[0] * vt0[2] + baryCentricCoordinate[1] * vt1[2] + baryCentricCoordinate[2] * vt2[2];
float depth_thres = 0;
if (d) {
depth_thres = d[pixel] * 0.49999f + 0.5f + occlusion_truncation;
}
int z_quantize = depth * (2<<17);
INT64 token = (INT64)z_quantize * MAXINT + (INT64)(idx + 1);
if (depth < depth_thres)
continue;
atomicMin(&zbuffer[pixel], token);
}
}
}
}
__global__ void barycentricFromImgcoordGPU(float* V, int* F, int* findices, INT64* zbuffer, int width, int height, int num_vertices, int num_faces,
float* barycentric_map)
{
int pix = blockIdx.x * blockDim.x + threadIdx.x;
if (pix >= width * height)
return;
INT64 f = zbuffer[pix] % MAXINT;
if (f == (MAXINT-1)) {
findices[pix] = 0;
barycentric_map[pix * 3] = 0;
barycentric_map[pix * 3 + 1] = 0;
barycentric_map[pix * 3 + 2] = 0;
return;
}
findices[pix] = f;
f -= 1;
float barycentric[3] = {0, 0, 0};
if (f >= 0) {
float vt[2] = {float(pix % width) + 0.5f, float(pix / width) + 0.5f};
float* vt0_ptr = V + (F[f * 3] * 4);
float* vt1_ptr = V + (F[f * 3 + 1] * 4);
float* vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[2] = {(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f};
float vt1[2] = {(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f};
float vt2[2] = {(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f};
calculateBarycentricCoordinate(vt0, vt1, vt2, vt, barycentric);
barycentric[0] = barycentric[0] / vt0_ptr[3];
barycentric[1] = barycentric[1] / vt1_ptr[3];
barycentric[2] = barycentric[2] / vt2_ptr[3];
float w = 1.0f / (barycentric[0] + barycentric[1] + barycentric[2]);
barycentric[0] *= w;
barycentric[1] *= w;
barycentric[2] *= w;
}
barycentric_map[pix * 3] = barycentric[0];
barycentric_map[pix * 3 + 1] = barycentric[1];
barycentric_map[pix * 3 + 2] = barycentric[2];
}
__global__ void rasterizeImagecoordsKernelGPU(float* V, int* F, float* d, INT64* zbuffer, float occlusion_trunc, int width, int height, int num_vertices, int num_faces)
{
int f = blockIdx.x * blockDim.x + threadIdx.x;
if (f >= num_faces)
return;
float* vt0_ptr = V + (F[f * 3] * 4);
float* vt1_ptr = V + (F[f * 3 + 1] * 4);
float* vt2_ptr = V + (F[f * 3 + 2] * 4);
float vt0[3] = {(vt0_ptr[0] / vt0_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt0_ptr[1] / vt0_ptr[3]) * (height - 1) + 0.5f, vt0_ptr[2] / vt0_ptr[3] * 0.49999f + 0.5f};
float vt1[3] = {(vt1_ptr[0] / vt1_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt1_ptr[1] / vt1_ptr[3]) * (height - 1) + 0.5f, vt1_ptr[2] / vt1_ptr[3] * 0.49999f + 0.5f};
float vt2[3] = {(vt2_ptr[0] / vt2_ptr[3] * 0.5f + 0.5f) * (width - 1) + 0.5f, (0.5f + 0.5f * vt2_ptr[1] / vt2_ptr[3]) * (height - 1) + 0.5f, vt2_ptr[2] / vt2_ptr[3] * 0.49999f + 0.5f};
rasterizeTriangleGPU(f, vt0, vt1, vt2, width, height, zbuffer, d, occlusion_trunc);
}
std::vector<torch::Tensor> rasterize_image_gpu(torch::Tensor V, torch::Tensor F, torch::Tensor D,
int width, int height, float occlusion_truncation, int use_depth_prior)
{
int device_id = V.get_device();
cudaSetDevice(device_id);
int num_faces = F.size(0);
int num_vertices = V.size(0);
auto options = torch::TensorOptions().dtype(torch::kInt32).device(torch::kCUDA, device_id).requires_grad(false);
auto INT64_options = torch::TensorOptions().dtype(torch::kInt64).device(torch::kCUDA, device_id).requires_grad(false);
auto findices = torch::zeros({height, width}, options);
INT64 maxint = (INT64)MAXINT * (INT64)MAXINT + (MAXINT - 1);
auto z_min = torch::ones({height, width}, INT64_options) * (int64_t)maxint;
if (!use_depth_prior) {
rasterizeImagecoordsKernelGPU<<<(num_faces+255)/256,256,0,at::cuda::getCurrentCUDAStream()>>>(V.data_ptr<float>(), F.data_ptr<int>(), 0,
(INT64*)z_min.data_ptr<int64_t>(), occlusion_truncation, width, height, num_vertices, num_faces);
} else {
rasterizeImagecoordsKernelGPU<<<(num_faces+255)/256,256,0,at::cuda::getCurrentCUDAStream()>>>(V.data_ptr<float>(), F.data_ptr<int>(), D.data_ptr<float>(),
(INT64*)z_min.data_ptr<int64_t>(), occlusion_truncation, width, height, num_vertices, num_faces);
}
auto float_options = torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA, device_id).requires_grad(false);
auto barycentric = torch::zeros({height, width, 3}, float_options);
barycentricFromImgcoordGPU<<<(width * height + 255)/256, 256, 0, at::cuda::getCurrentCUDAStream()>>>(V.data_ptr<float>(), F.data_ptr<int>(),
findices.data_ptr<int>(), (INT64*)z_min.data_ptr<int64_t>(), width, height, num_vertices, num_faces, barycentric.data_ptr<float>());
return {findices, barycentric};
}
@@ -1,163 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Adapted from Hunyuan3D-2: https://github.com/Tencent/Hunyuan3D-2
// Original license: TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <torch/extension.h>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
namespace py = pybind11;
using namespace std;
std::pair<py::array_t<float>,
py::array_t<uint8_t>> meshVerticeInpaint_smooth(py::array_t<float> texture,
py::array_t<uint8_t> mask,
py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
py::array_t<int> pos_idx, py::array_t<int> uv_idx) {
auto texture_buf = texture.request();
auto mask_buf = mask.request();
auto vtx_pos_buf = vtx_pos.request();
auto vtx_uv_buf = vtx_uv.request();
auto pos_idx_buf = pos_idx.request();
auto uv_idx_buf = uv_idx.request();
int texture_height = texture_buf.shape[0];
int texture_width = texture_buf.shape[1];
int texture_channel = texture_buf.shape[2];
float* texture_ptr = static_cast<float*>(texture_buf.ptr);
uint8_t* mask_ptr = static_cast<uint8_t*>(mask_buf.ptr);
int vtx_num = vtx_pos_buf.shape[0];
float* vtx_pos_ptr = static_cast<float*>(vtx_pos_buf.ptr);
float* vtx_uv_ptr = static_cast<float*>(vtx_uv_buf.ptr);
int* pos_idx_ptr = static_cast<int*>(pos_idx_buf.ptr);
int* uv_idx_ptr = static_cast<int*>(uv_idx_buf.ptr);
vector<float> vtx_mask(vtx_num, 0.0f);
vector<vector<float>> vtx_color(vtx_num, vector<float>(texture_channel, 0.0f));
vector<int> uncolored_vtxs;
vector<vector<int>> G(vtx_num);
for (int i = 0; i < uv_idx_buf.shape[0]; ++i) {
for (int k = 0; k < 3; ++k) {
int vtx_uv_idx = uv_idx_ptr[i * 3 + k];
int vtx_idx = pos_idx_ptr[i * 3 + k];
int uv_v = round(vtx_uv_ptr[vtx_uv_idx * 2] * (texture_width - 1));
int uv_u = round((1.0 - vtx_uv_ptr[vtx_uv_idx * 2 + 1]) * (texture_height - 1));
if (mask_ptr[uv_u * texture_width + uv_v] > 0) {
vtx_mask[vtx_idx] = 1.0f;
for (int c = 0; c < texture_channel; ++c) {
vtx_color[vtx_idx][c] = texture_ptr[(uv_u * texture_width + uv_v) * texture_channel + c];
}
}else{
uncolored_vtxs.push_back(vtx_idx);
}
G[pos_idx_ptr[i * 3 + k]].push_back(pos_idx_ptr[i * 3 + (k + 1) % 3]);
}
}
int smooth_count = 2;
int last_uncolored_vtx_count = 0;
while (smooth_count>0) {
int uncolored_vtx_count = 0;
for (int vtx_idx : uncolored_vtxs) {
vector<float> sum_color(texture_channel, 0.0f);
float total_weight = 0.0f;
array<float, 3> vtx_0 = {vtx_pos_ptr[vtx_idx * 3],
vtx_pos_ptr[vtx_idx * 3 + 1], vtx_pos_ptr[vtx_idx * 3 + 2]};
for (int connected_idx : G[vtx_idx]) {
if (vtx_mask[connected_idx] > 0) {
array<float, 3> vtx1 = {vtx_pos_ptr[connected_idx * 3],
vtx_pos_ptr[connected_idx * 3 + 1], vtx_pos_ptr[connected_idx * 3 + 2]};
float dist_weight = 1.0f / max(sqrt(pow(vtx_0[0] - vtx1[0], 2) + pow(vtx_0[1] - vtx1[1], 2) + \
pow(vtx_0[2] - vtx1[2], 2)), 1E-4);
dist_weight = dist_weight * dist_weight;
for (int c = 0; c < texture_channel; ++c) {
sum_color[c] += vtx_color[connected_idx][c] * dist_weight;
}
total_weight += dist_weight;
}
}
if (total_weight > 0.0f) {
for (int c = 0; c < texture_channel; ++c) {
vtx_color[vtx_idx][c] = sum_color[c] / total_weight;
}
vtx_mask[vtx_idx] = 1.0f;
} else {
uncolored_vtx_count++;
}
}
if(last_uncolored_vtx_count==uncolored_vtx_count){
smooth_count--;
}else{
smooth_count++;
}
last_uncolored_vtx_count = uncolored_vtx_count;
}
py::array_t<float> new_texture(texture_buf.size);
py::array_t<uint8_t> new_mask(mask_buf.size);
auto new_texture_buf = new_texture.request();
auto new_mask_buf = new_mask.request();
float* new_texture_ptr = static_cast<float*>(new_texture_buf.ptr);
uint8_t* new_mask_ptr = static_cast<uint8_t*>(new_mask_buf.ptr);
std::copy(texture_ptr, texture_ptr + texture_buf.size, new_texture_ptr);
std::copy(mask_ptr, mask_ptr + mask_buf.size, new_mask_ptr);
for (int face_idx = 0; face_idx < uv_idx_buf.shape[0]; ++face_idx) {
for (int k = 0; k < 3; ++k) {
int vtx_uv_idx = uv_idx_ptr[face_idx * 3 + k];
int vtx_idx = pos_idx_ptr[face_idx * 3 + k];
if (vtx_mask[vtx_idx] == 1.0f) {
int uv_v = round(vtx_uv_ptr[vtx_uv_idx * 2] * (texture_width - 1));
int uv_u = round((1.0 - vtx_uv_ptr[vtx_uv_idx * 2 + 1]) * (texture_height - 1));
for (int c = 0; c < texture_channel; ++c) {
new_texture_ptr[(uv_u * texture_width + uv_v) * texture_channel + c] = vtx_color[vtx_idx][c];
}
new_mask_ptr[uv_u * texture_width + uv_v] = 255;
}
}
}
new_texture.resize({texture_height, texture_width, 3});
new_mask.resize({texture_height, texture_width});
return std::make_pair(new_texture, new_mask);
}
std::pair<py::array_t<float>, py::array_t<uint8_t>> meshVerticeInpaint(py::array_t<float> texture,
py::array_t<uint8_t> mask,
py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
py::array_t<int> pos_idx, py::array_t<int> uv_idx, const std::string& method = "smooth") {
if (method == "smooth") {
return meshVerticeInpaint_smooth(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
} else {
throw std::invalid_argument("Invalid method. Use 'smooth'.");
}
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("meshVerticeInpaint", &meshVerticeInpaint, "Mesh-aware texture inpainting",
py::arg("texture"), py::arg("mask"),
py::arg("vtx_pos"), py::arg("vtx_uv"),
py::arg("pos_idx"), py::arg("uv_idx"),
py::arg("method") = "smooth");
}
@@ -0,0 +1 @@
"""Rotary embeddings and the QK-norm chains fused around them."""
@@ -29,7 +29,7 @@ import torch
import triton # type: ignore import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.kernels.ops.diffusion.triton.numerics import round_bf16_to_fp32 from sglang.kernels.ops.diffusion.common.numerics import round_bf16_to_fp32
from sglang.srt.utils.custom_op import register_custom_op from sglang.srt.utils.custom_op import register_custom_op
@@ -2,7 +2,7 @@ import torch
import triton # type: ignore import triton # type: ignore
import triton.language as tl # type: ignore import triton.language as tl # type: ignore
from sglang.multimodal_gen.runtime.platforms import current_platform from sglang.kernels.ops.diffusion.common.platform import lazy_fallback, select_impl
@triton.autotune( @triton.autotune(
@@ -125,17 +125,9 @@ def apply_rotary_embedding(
return output return output
if current_platform.is_npu(): apply_rotary_embedding = select_impl(
from .npu_fallback import apply_rotary_embedding_native apply_rotary_embedding,
npu=lazy_fallback("npu", "apply_rotary_embedding_native"),
apply_rotary_embedding = apply_rotary_embedding_native mps=lazy_fallback("mps", "apply_rotary_embedding_native"),
cpu=lazy_fallback("torch", "apply_rotary_embedding_native"),
if current_platform.is_mps(): )
from .mps_fallback import apply_rotary_embedding_native
apply_rotary_embedding = apply_rotary_embedding_native
if current_platform.is_cpu():
from .torch_fallback import apply_rotary_embedding_native
apply_rotary_embedding = apply_rotary_embedding_native
@@ -0,0 +1,14 @@
"""Request-scoped fusion *policy* -- module-tree rewriting, not kernels.
A fusion whose result is not bit-exact vs the reference chain may not be on by
default: multi-step denoising amplifies per-step rounding differences into
visible quality loss. Such fusions are mounted onto marked ``nn.Module`` sites
only for ``quality="high"`` requests, at batch boundaries, all-or-nothing per
transformer (:mod:`.quality_gate`). Fusions that *are* bit-exact mount
unconditionally but still verify themselves against the live eager chain on
first sight and fall back permanently on mismatch (:mod:`.bitexact_gate`).
Because these modules inspect and rewrite model modules, they are the one place
in this package allowed to reference ``multimodal_gen`` types, and they do so
lazily inside functions.
"""
@@ -4,7 +4,7 @@ Adaln-style DiT blocks (Ideogram 4) spend four elementwise chains per block on
modulate/gate around each RMSNorm: ``RMSNorm(x) * scale`` before modulate/gate around each RMSNorm: ``RMSNorm(x) * scale`` before
attention/FFN and ``x + tanh(gate) * RMSNorm(out)`` after. Shared BF16-native attention/FFN and ``x + tanh(gate) * RMSNorm(out)`` after. Shared BF16-native
Triton kernels Triton kernels
(:mod:`sglang.kernels.ops.diffusion.triton.native_bf16_rmsnorm`) fuse each (:mod:`sglang.kernels.ops.diffusion.norm.native_bf16_rmsnorm_triton`) fuse each
chain into a single kernel (RMSNorm + tanh + mul + add in one pass). chain into a single kernel (RMSNorm + tanh + mul + add in one pass).
Z-Image mounts those kernels unconditionally because they reproduce its own Z-Image mounts those kernels unconditionally because they reproduce its own
@@ -29,7 +29,7 @@ from importlib import import_module
import torch import torch
import torch.nn as nn import torch.nn as nn
from sglang.kernels.ops.diffusion.quality_gate import QualityGatedFusion from sglang.kernels.ops.diffusion.sites.quality_gate import QualityGatedFusion
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -50,7 +50,7 @@ def fused_rmsnorm_scale(
x: torch.Tensor, weight: torch.Tensor, scale: torch.Tensor, eps: float x: torch.Tensor, weight: torch.Tensor, scale: torch.Tensor, eps: float
) -> torch.Tensor | None: ) -> torch.Tensor | None:
"""``RMSNorm(x, weight, eps) * scale`` in one Triton kernel (or None).""" """``RMSNorm(x, weight, eps) * scale`` in one Triton kernel (or None)."""
from sglang.kernels.ops.diffusion.triton.native_bf16_rmsnorm import ( from sglang.kernels.ops.diffusion.norm.native_bf16_rmsnorm_triton import (
rmsnorm_scale, rmsnorm_scale,
) )
@@ -65,7 +65,7 @@ def fused_rmsnorm_tanh_residual(
eps: float, eps: float,
) -> torch.Tensor | None: ) -> torch.Tensor | None:
"""``residual + tanh(gate) * RMSNorm(x, weight, eps)`` fused (or None).""" """``residual + tanh(gate) * RMSNorm(x, weight, eps)`` fused (or None)."""
from sglang.kernels.ops.diffusion.triton.native_bf16_rmsnorm import ( from sglang.kernels.ops.diffusion.norm.native_bf16_rmsnorm_triton import (
rmsnorm_tanh_residual, rmsnorm_tanh_residual,
) )
@@ -32,7 +32,7 @@ import torch
import torch.nn as nn import torch.nn as nn
from sglang.kernels.jit.utils import get_jit_cuda_arch from sglang.kernels.jit.utils import get_jit_cuda_arch
from sglang.kernels.ops.diffusion.quality_gate import QualityGatedFusion from sglang.kernels.ops.diffusion.sites.quality_gate import QualityGatedFusion
from sglang.srt.utils.custom_op import register_custom_op from sglang.srt.utils.custom_op import register_custom_op
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -123,12 +123,12 @@ def _static_reject_reason(linear: Any) -> str | None:
return None return None
def can_fuse_linear_gelu_static(linear: Any) -> bool: def can_use_linear_gelu_static(linear: Any) -> bool:
"""Input-independent guards: whether ``linear`` may ever use the epilogue.""" """Input-independent guards: whether ``linear`` may ever use the epilogue."""
return _static_reject_reason(linear) is None return _static_reject_reason(linear) is None
def can_fuse_linear_gelu(linear: Any, x: torch.Tensor) -> bool: def can_use_linear_gelu(linear: Any, x: torch.Tensor) -> bool:
"""Whether ``gelu(linear(x))`` can use the fused cublasLt epilogue now.""" """Whether ``gelu(linear(x))`` can use the fused cublasLt epilogue now."""
if not (x.is_cuda and x.dtype in (torch.bfloat16, torch.float16)): if not (x.is_cuda and x.dtype in (torch.bfloat16, torch.float16)):
return False return False
@@ -141,7 +141,7 @@ def can_fuse_linear_gelu(linear: Any, x: torch.Tensor) -> bool:
return False return False
if getattr(linear, "weight", None) is None or x.dtype != linear.weight.dtype: if getattr(linear, "weight", None) is None or x.dtype != linear.weight.dtype:
return False return False
return can_fuse_linear_gelu_static(linear) return can_use_linear_gelu_static(linear)
def mark_fused_gelu_site(module: nn.Module, linear_attr: str) -> None: def mark_fused_gelu_site(module: nn.Module, linear_attr: str) -> None:
@@ -20,7 +20,7 @@ import torch
import torch.nn.functional as F import torch.nn.functional as F
from torch import nn from torch import nn
from sglang.kernels.ops.diffusion.quality_gate import QualityGatedFusion from sglang.kernels.ops.diffusion.sites.quality_gate import QualityGatedFusion
_SITE_MARKER_ATTR = "_sgl_fused_ln_modulate_site" _SITE_MARKER_ATTR = "_sgl_fused_ln_modulate_site"
_SITE_ENABLED_ATTR = "_sgl_fused_ln_modulate_enabled" _SITE_ENABLED_ATTR = "_sgl_fused_ln_modulate_enabled"
@@ -50,7 +50,7 @@ def unmount_fused_ln_modulate(root: nn.Module) -> None:
_FUSION.unmount(root) _FUSION.unmount(root)
def can_fuse_ln_modulate( def can_use_ln_modulate(
x: torch.Tensor, scale: torch.Tensor, shift: torch.Tensor x: torch.Tensor, scale: torch.Tensor, shift: torch.Tensor
) -> bool: ) -> bool:
"""Per-call guard: the folded affine is a [D] row, so batch must be 1.""" """Per-call guard: the folded affine is a [D] row, so batch must be 1."""
@@ -8,7 +8,7 @@ from functools import cache
import torch import torch
import torch.nn as nn import torch.nn as nn
from sglang.kernels.ops.diffusion.quality_gate import QualityGatedFusion from sglang.kernels.ops.diffusion.sites.quality_gate import QualityGatedFusion
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -21,11 +21,11 @@ from __future__ import annotations
import torch import torch
from torch import nn from torch import nn
from sglang.kernels.ops.diffusion.quality_gate import QualityGatedFusion from sglang.kernels.ops.diffusion.norm.rmsnorm_scale_shift_bitexact import (
from sglang.kernels.ops.diffusion.triton.rmsnorm_scale_shift_bitexact import (
can_use_fused_rmsnorm_scale_shift, can_use_fused_rmsnorm_scale_shift,
fused_rmsnorm_scale_shift_bitexact, fused_rmsnorm_scale_shift_bitexact,
) )
from sglang.kernels.ops.diffusion.sites.quality_gate import QualityGatedFusion
_SITE_MARKER_ATTR = "_sgl_ltx2_rms_norm_modulate_site" _SITE_MARKER_ATTR = "_sgl_ltx2_rms_norm_modulate_site"
_SITE_ENABLED_ATTR = "_sgl_ltx2_rms_norm_modulate_enabled" _SITE_ENABLED_ATTR = "_sgl_ltx2_rms_norm_modulate_enabled"
@@ -65,7 +65,7 @@ def _ones_weight(x: torch.Tensor) -> torch.Tensor:
return w return w
def can_fuse_ltx2_rms_norm_modulate( def can_use_ltx2_rms_norm_modulate(
x: torch.Tensor, scale: torch.Tensor, shift: torch.Tensor x: torch.Tensor, scale: torch.Tensor, shift: torch.Tensor
) -> bool: ) -> bool:
if x.dtype is not torch.bfloat16 or not x.is_cuda: if x.dtype is not torch.bfloat16 or not x.is_cuda:
+1
View File
@@ -43,6 +43,7 @@ class KernelBackend(str, Enum):
JIT = "jit" # sglang.kernels.jit (nvcc / hipcc) JIT = "jit" # sglang.kernels.jit (nvcc / hipcc)
AOT = "aot" # sgl_kernel wheel (CUDA / ROCm builds) AOT = "aot" # sgl_kernel wheel (CUDA / ROCm builds)
CUTE_DSL = "cute_dsl" CUTE_DSL = "cute_dsl"
FLYDSL = "flydsl" # FlyDSL MLIR compiler (device=HIP, gfx950)
FLASHINFER = "flashinfer" FLASHINFER = "flashinfer"
DEEPGEMM = "deepgemm" DEEPGEMM = "deepgemm"
AITER = "aiter" # AMD aiter library (device=HIP) AITER = "aiter" # AMD aiter library (device=HIP)
@@ -10,33 +10,33 @@ framework-specific optimization workflow.
- `python/sglang/multimodal_gen/runtime/layers/elementwise.py` - `python/sglang/multimodal_gen/runtime/layers/elementwise.py`
- `python/sglang/multimodal_gen/runtime/layers/fused_scale_shift_gate.py` - `python/sglang/multimodal_gen/runtime/layers/fused_scale_shift_gate.py`
- `python/sglang/multimodal_gen/runtime/layers/rotary_embedding/utils.py` - `python/sglang/multimodal_gen/runtime/layers/rotary_embedding/utils.py`
- `python/sglang/kernels/ops/diffusion/triton/scale_shift.py` - `python/sglang/kernels/ops/diffusion/modulate/scale_shift_triton.py`
- `python/sglang/kernels/ops/diffusion/modulate_scale_shift.py` - `python/sglang/kernels/ops/diffusion/modulate/modulate_scale_shift_jit.py`
- `python/sglang/kernels/ops/diffusion/fused_ln_modulate.py` - `python/sglang/kernels/ops/diffusion/sites/fused_ln_modulate_site.py`
- `python/sglang/kernels/ops/diffusion/quality_gate.py` - `python/sglang/kernels/ops/diffusion/sites/quality_gate.py`
- `python/sglang/kernels/ops/diffusion/bitexact_gate.py` - `python/sglang/kernels/ops/diffusion/sites/bitexact_gate.py`
- `python/sglang/kernels/ops/diffusion/group_norm_silu.py` - `python/sglang/kernels/ops/diffusion/norm/group_norm_silu.py`
- `python/sglang/kernels/ops/diffusion/triton/group_norm_silu.py` - `python/sglang/kernels/ops/diffusion/norm/group_norm_silu_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/group_norm_silu_twopass.py` - `python/sglang/kernels/ops/diffusion/norm/group_norm_silu_twopass_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/norm.py` - `python/sglang/kernels/ops/diffusion/norm/norm_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/rmsnorm_onepass.py` - `python/sglang/kernels/ops/diffusion/norm/rmsnorm_onepass_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/layernorm_modulate.py` - `python/sglang/kernels/ops/diffusion/norm/layernorm_modulate_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/native_bf16_rmsnorm.py` - `python/sglang/kernels/ops/diffusion/norm/native_bf16_rmsnorm_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/zimage_native_norm.py` - `python/sglang/kernels/ops/diffusion/norm/zimage_qk_rmsnorm_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/rotary.py` - `python/sglang/kernels/ops/diffusion/rope/rotary_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/ltx2_rotary.py` - `python/sglang/kernels/ops/diffusion/rope/ltx2_rotary_triton.py`
- `python/sglang/kernels/ops/diffusion/ltx2_qknorm_split_rope.py` - `python/sglang/kernels/ops/diffusion/rope/ltx2_qknorm_split_rope_jit.py`
- `python/sglang/kernels/ops/diffusion/ltx2_rmsnorm_modulate.py` - `python/sglang/kernels/ops/diffusion/sites/ltx2_rmsnorm_modulate_site.py`
- `python/sglang/kernels/ops/diffusion/triton/indexed_modulation.py` - `python/sglang/kernels/ops/diffusion/modulate/indexed_modulation_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/ulysses_qkv.py` - `python/sglang/kernels/ops/diffusion/layout/ulysses_qkv_triton.py`
- `python/sglang/kernels/ops/diffusion/usp_relayout.py` - `python/sglang/kernels/ops/diffusion/layout/usp_relayout_jit.py`
- `python/sglang/multimodal_gen/runtime/layers/usp.py` - `python/sglang/multimodal_gen/runtime/layers/usp.py`
- `python/sglang/multimodal_gen/runtime/models/dits/minimax_h3.py` - `python/sglang/multimodal_gen/runtime/models/dits/minimax_h3.py`
- `python/sglang/kernels/ops/diffusion/residual_gate_add.py` - `python/sglang/kernels/ops/diffusion/modulate/residual_gate_add_jit.py`
- `python/sglang/kernels/jit/csrc/diffusion/residual_gate_add.cuh` - `python/sglang/kernels/jit/csrc/diffusion/residual_gate_add.cuh`
- `python/sglang/kernels/ops/diffusion/triton/varlen_pack_pad.py` - `python/sglang/kernels/ops/diffusion/layout/varlen_pack_pad_triton.py`
- `python/sglang/kernels/ops/diffusion/triton/wan_causal_cache.py` - `python/sglang/kernels/ops/diffusion/layout/wan_causal_cache_triton.py`
- `python/sglang/kernels/ops/diffusion/cutedsl/scale_residual_norm_scale_shift.py` - `python/sglang/kernels/ops/diffusion/norm/scale_residual_norm_cutedsl.py`
- `python/sglang/multimodal_gen/runtime/models/vaes/fast_path_gate.py` - `python/sglang/multimodal_gen/runtime/models/vaes/fast_path_gate.py`
- `python/sglang/multimodal_gen/runtime/models/vaes/flux2_vae_cuda_opt.py` - `python/sglang/multimodal_gen/runtime/models/vaes/flux2_vae_cuda_opt.py`
- `python/sglang/multimodal_gen/runtime/models/vaes/wan_vae_cuda_opt.py` - `python/sglang/multimodal_gen/runtime/models/vaes/wan_vae_cuda_opt.py`
@@ -249,7 +249,7 @@ framework-specific optimization workflow.
**QK Norm + RoPE Optimization** **QK Norm + RoPE Optimization**
- Entry point: `apply_qk_norm_rope` in `layernorm.py`. - Entry point: `apply_qk_norm_rope` in `layernorm.py`.
- Fast path: JIT fused inplace QK norm + RoPE from `python/sglang/kernels/ops/diffusion/qknorm_rope.py` via `fused_inplace_qknorm_rope`. - Fast path: JIT fused inplace QK norm + RoPE from `python/sglang/kernels/ops/diffusion/rope/qknorm_rope_jit.py` via `fused_inplace_qknorm_rope`.
- Toggle: `SGLANG_ENABLE_FUSED_QKNORM_ROPE=1` keeps the fused path enabled by default. - Toggle: `SGLANG_ENABLE_FUSED_QKNORM_ROPE=1` keeps the fused path enabled by default.
- Preconditions for fused path: - Preconditions for fused path:
- CUDA only. - CUDA only.
@@ -310,12 +310,12 @@ framework-specific optimization workflow.
- LTX2 split RoPE: `apply_ltx2_split_rotary_emb` in `ltx_2.py`. - LTX2 split RoPE: `apply_ltx2_split_rotary_emb` in `ltx_2.py`.
- LTX2 RMSNorm+modulate and FFN GELU epilogue under `quality="high"`: - LTX2 RMSNorm+modulate and FFN GELU epilogue under `quality="high"`:
`mark_ltx2_rms_norm_modulate_site` / `fused_ltx2_rms_norm_modulate` in `mark_ltx2_rms_norm_modulate_site` / `fused_ltx2_rms_norm_modulate` in
`kernels/ops/diffusion/ltx2_rmsnorm_modulate.py` (mount-based `kernels/ops/diffusion/sites/ltx2_rmsnorm_modulate_site.py` (mount-based
`QualityGatedFusion`, not a first-sight `BitExactFusionGate` — the fused `QualityGatedFusion`, not a first-sight `BitExactFusionGate` — the fused
kernel is <=1 ULP off aten, so it is request-gated instead of verified), kernel is <=1 ULP off aten, so it is request-gated instead of verified),
wired at the six `LTX2TransformerBlock` adaLN sites in `ltx_2.py`. wired at the six `LTX2TransformerBlock` adaLN sites in `ltx_2.py`.
- LTX2 residual-gate add: `ltx_2.py` calls `residual_gate_add` from - LTX2 residual-gate add: `ltx_2.py` calls `residual_gate_add` from
`kernels/ops/diffusion/residual_gate_add.py` directly for attention, `kernels/ops/diffusion/modulate/residual_gate_add_jit.py` directly for attention,
cross-attention, and MLP residual updates. cross-attention, and MLP residual updates.
- Wan causal VAE: `cat_pad_channels_last_3d` and `dup_up3d_add` in - Wan causal VAE: `cat_pad_channels_last_3d` and `dup_up3d_add` in
`wanvae.py`, backed by `triton/wan_causal_cache.py`. `wanvae.py`, backed by `triton/wan_causal_cache.py`.
@@ -36,10 +36,7 @@ logger = init_logger(__name__)
# ==================================SLA Functions=================================== # ==================================SLA Functions===================================
from sglang.kernels.ops.diffusion.sparse_linear_attn_kernels import ( from sglang.kernels.ops.diffusion import _attn_fwd, get_block_map
_attn_fwd,
get_block_map,
)
def _get_cuda_arch(device_index: int) -> str: def _get_cuda_arch(device_index: int) -> str:
@@ -12,7 +12,7 @@ import torch.nn as nn
from torch.nn.attention import SDPBackend, sdpa_kernel from torch.nn.attention import SDPBackend, sdpa_kernel
from sglang.kernels.ops.attention.flash_attention import flash_attn_varlen_func from sglang.kernels.ops.attention.flash_attention import flash_attn_varlen_func
from sglang.kernels.ops.diffusion.triton.varlen_pack_pad import ( from sglang.kernels.ops.diffusion import (
build_inv_indices, build_inv_indices,
fused_pack_qkv, fused_pack_qkv,
fused_scatter_to_padded, fused_scatter_to_padded,
@@ -1,6 +1,6 @@
import torch import torch
from sglang.kernels.ops.diffusion.triton.scale_shift import fuse_scale_shift_kernel from sglang.kernels.ops.diffusion import fuse_scale_shift_kernel
from sglang.multimodal_gen.runtime.layers.custom_op import CustomOp from sglang.multimodal_gen.runtime.layers.custom_op import CustomOp
@@ -10,7 +10,7 @@ from sglang.multimodal_gen.runtime.platforms import current_platform
_is_cuda = current_platform.is_cuda() _is_cuda = current_platform.is_cuda()
if _is_cuda: if _is_cuda:
from sglang.kernels.ops.diffusion.triton.scale_shift import ( from sglang.kernels.ops.diffusion import (
fuse_layernorm_scale_shift_gate_select01_kernel, fuse_layernorm_scale_shift_gate_select01_kernel,
fuse_residual_layernorm_scale_shift_gate_select01_kernel, fuse_residual_layernorm_scale_shift_gate_select01_kernel,
) )
@@ -11,12 +11,12 @@ import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
from sglang.kernels.ops.diffusion.qknorm_rope import ( from sglang.kernels.ops.diffusion import (
can_use_fused_inplace_qknorm_rope, can_use_fused_inplace_qknorm_rope,
fuse_scale_shift_kernel,
fused_inplace_qknorm_rope, fused_inplace_qknorm_rope,
triton_one_pass_rms_norm,
) )
from sglang.kernels.ops.diffusion.triton.rmsnorm_onepass import triton_one_pass_rms_norm
from sglang.kernels.ops.diffusion.triton.scale_shift import fuse_scale_shift_kernel
from sglang.kernels.ops.layernorm.norm import ( from sglang.kernels.ops.layernorm.norm import (
can_use_fused_inplace_qknorm, can_use_fused_inplace_qknorm,
fused_inplace_qknorm, fused_inplace_qknorm,
@@ -58,7 +58,7 @@ if _is_xpu:
from sgl_kernel import fused_inplace_qknorm_rope from sgl_kernel import fused_inplace_qknorm_rope
if not _is_cpu: if not _is_cpu:
from sglang.kernels.ops.diffusion.triton.norm import norm_infer, rms_norm_fn from sglang.kernels.ops.diffusion import norm_infer, rms_norm_fn
# Copied and adapted from sglang # Copied and adapted from sglang
@@ -614,9 +614,7 @@ class _ScaleResidualNormScaleShift(CustomOp):
) )
return self.forward_native(residual, x, gate, shift, scale) return self.forward_native(residual, x, gate, shift, scale)
from sglang.kernels.ops.diffusion.cutedsl.scale_residual_norm_scale_shift import ( from sglang.kernels.ops.diffusion import fused_scale_residual_norm_scale_shift
fused_scale_residual_norm_scale_shift,
)
if isinstance(gate, int) and gate != 1: if isinstance(gate, int) and gate != 1:
raise ValueError( raise ValueError(
@@ -647,7 +645,7 @@ class _ScaleResidualNormScaleShift(CustomOp):
return self.forward_native(residual, x, gate, shift, scale) return self.forward_native(residual, x, gate, shift, scale)
try: try:
from sglang.kernels.ops.diffusion.flydsl.fused_residual_norm import ( from sglang.kernels.ops.diffusion import (
FLYDSL_NORM_MIN_ALIGNED_DIM, FLYDSL_NORM_MIN_ALIGNED_DIM,
flydsl_fused_residual_norm_scale_shift, flydsl_fused_residual_norm_scale_shift,
) )
@@ -792,9 +790,7 @@ class _NormScaleShift(CustomOp):
) )
return self.forward_native(x, shift, scale) return self.forward_native(x, shift, scale)
from sglang.kernels.ops.diffusion.cutedsl.scale_residual_norm_scale_shift import ( from sglang.kernels.ops.diffusion import fused_norm_scale_shift
fused_norm_scale_shift,
)
return fused_norm_scale_shift( return fused_norm_scale_shift(
x.contiguous(), x.contiguous(),
@@ -816,7 +812,7 @@ class _NormScaleShift(CustomOp):
return self.forward_native(x, shift, scale) return self.forward_native(x, shift, scale)
try: try:
from sglang.kernels.ops.diffusion.flydsl.fused_residual_norm import ( from sglang.kernels.ops.diffusion import (
FLYDSL_NORM_MIN_ALIGNED_DIM, FLYDSL_NORM_MIN_ALIGNED_DIM,
flydsl_norm_scale_shift, flydsl_norm_scale_shift,
) )
@@ -18,13 +18,15 @@ from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
logger = init_logger(__name__) logger = init_logger(__name__)
if current_platform.is_cuda(): if current_platform.is_cuda():
from sglang.kernels.ops.diffusion.causal_conv3d_cat_pad import ( from sglang.kernels.ops.diffusion import (
can_use_fused_causal_conv3d_cat_pad_cuda, can_use_fused_causal_conv3d_cat_pad_cuda,
fused_causal_conv3d_cat_pad_cuda,
) )
from sglang.kernels.ops.diffusion.triton.causal_conv3d_pad import ( from sglang.kernels.ops.diffusion import (
fused_causal_conv3d_cat_pad as fused_causal_conv3d_cat_pad_triton, fused_causal_conv3d_cat_pad as fused_causal_conv3d_cat_pad_triton,
) )
from sglang.kernels.ops.diffusion import (
fused_causal_conv3d_cat_pad_cuda,
)
else: else:
can_use_fused_causal_conv3d_cat_pad_cuda = None can_use_fused_causal_conv3d_cat_pad_cuda = None
fused_causal_conv3d_cat_pad_cuda = None fused_causal_conv3d_cat_pad_cuda = None
@@ -5,7 +5,7 @@ from typing import Optional, Tuple
import torch import torch
from sglang.kernels.kernel_api_logging import debug_kernel_api from sglang.kernels.kernel_api_logging import debug_kernel_api
from sglang.kernels.ops.diffusion.triton.rotary import apply_rotary_embedding from sglang.kernels.ops.diffusion import apply_rotary_embedding
from sglang.multimodal_gen.runtime.platforms import current_platform from sglang.multimodal_gen.runtime.platforms import current_platform
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
from sglang.srt.utils.custom_op import register_custom_op_from_extern from sglang.srt.utils.custom_op import register_custom_op_from_extern
@@ -9,10 +9,7 @@ import torch.distributed._functional_collectives as ft_c
from torch.distributed.tensor.experimental._attention import _cp_options from torch.distributed.tensor.experimental._attention import _cp_options
from sglang.kernels.ops.attention.flash_attention import flash_attn_varlen_func from sglang.kernels.ops.attention.flash_attention import flash_attn_varlen_func
from sglang.kernels.ops.diffusion.triton.ulysses_qkv import ( from sglang.kernels.ops.diffusion import pack_qkv_destination_major, usp_merge_heads
pack_qkv_destination_major,
)
from sglang.kernels.ops.diffusion.usp_relayout import usp_merge_heads
from sglang.multimodal_gen.runtime.distributed.parallel_state import ( from sglang.multimodal_gen.runtime.distributed.parallel_state import (
get_ring_ctx, get_ring_ctx,
get_sp_group, get_sp_group,
@@ -22,9 +22,7 @@ from diffusers.models.embeddings import (
get_timestep_embedding as timestep_embedding_diffusers, get_timestep_embedding as timestep_embedding_diffusers,
) )
from sglang.kernels.ops.diffusion.timestep_embedding import ( from sglang.kernels.ops.diffusion import timestep_embedding as timestep_embedding_cuda
timestep_embedding as timestep_embedding_cuda,
)
from sglang.multimodal_gen.runtime.layers.activation import get_act_fn from sglang.multimodal_gen.runtime.layers.activation import get_act_fn
from sglang.multimodal_gen.runtime.layers.linear import ColumnParallelLinear from sglang.multimodal_gen.runtime.layers.linear import ColumnParallelLinear
from sglang.multimodal_gen.runtime.layers.mlp import MLP from sglang.multimodal_gen.runtime.layers.mlp import MLP
@@ -14,7 +14,7 @@ import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
from sglang.kernels.ops.diffusion.qknorm_rope import ( from sglang.kernels.ops.diffusion import (
can_use_fused_inplace_qknorm_rope, can_use_fused_inplace_qknorm_rope,
fused_qknorm_rope_pack_kv, fused_qknorm_rope_pack_kv,
) )
@@ -22,21 +22,17 @@ from diffusers.models.embeddings import TimestepEmbedding, Timesteps
from sglang.kernels.ops.activation.activation import ( from sglang.kernels.ops.activation.activation import (
gelu_and_mul_with_activation_rounding, gelu_and_mul_with_activation_rounding,
) )
from sglang.kernels.ops.diffusion.bitexact_gate import ( from sglang.kernels.ops.diffusion import (
BitExactFusionGate, BitExactFusionGate,
flashinfer_rmsnorm_diagnostic_hint,
tensors_equal,
)
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add
from sglang.kernels.ops.diffusion.triton.rmsnorm_scale_shift_bitexact import (
can_use_fused_rmsnorm_scale_shift, can_use_fused_rmsnorm_scale_shift,
can_use_fused_scale_residual_rmsnorm_scale_shift,
fused_rmsnorm_scale_shift_bitexact,
fused_scale_residual_rmsnorm_scale_shift_bitexact,
)
from sglang.kernels.ops.diffusion.triton.rope_rotate_half_bitexact import (
can_use_fused_rope_rotate_half, can_use_fused_rope_rotate_half,
can_use_fused_scale_residual_rmsnorm_scale_shift,
flashinfer_rmsnorm_diagnostic_hint,
fused_rmsnorm_scale_shift_bitexact,
fused_rope_rotate_half_bitexact, fused_rope_rotate_half_bitexact,
fused_scale_residual_rmsnorm_scale_shift_bitexact,
residual_gate_add,
tensors_equal,
) )
from sglang.multimodal_gen.configs.models.dits.ernie_image import ( from sglang.multimodal_gen.configs.models.dits.ernie_image import (
ErnieImageDitConfig, ErnieImageDitConfig,
@@ -28,25 +28,21 @@ from diffusers.models.normalization import (
) )
from torch.nn import LayerNorm as LayerNorm from torch.nn import LayerNorm as LayerNorm
from sglang.kernels.ops.diffusion.bitexact_gate import BitExactFusionGate from sglang.kernels.ops.diffusion import (
from sglang.kernels.ops.diffusion.fused_linear_gelu import ( BitExactFusionGate,
can_fuse_linear_gelu, can_use_fused_layernorm_modulate,
can_use_linear_gelu,
can_use_ln_modulate,
fused_gelu_active, fused_gelu_active,
fused_layernorm_modulate,
fused_linear_gelu_tanh, fused_linear_gelu_tanh,
mark_fused_gelu_site,
)
from sglang.kernels.ops.diffusion.fused_ln_modulate import (
can_fuse_ln_modulate,
fused_ln_modulate, fused_ln_modulate,
fused_ln_modulate_active, fused_ln_modulate_active,
mark_fused_ln_modulate_site,
)
from sglang.kernels.ops.diffusion.modulate_scale_shift import modulate_scale_shift
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add
from sglang.kernels.ops.diffusion.triton.layernorm_modulate import (
can_use_fused_layernorm_modulate,
fused_layernorm_modulate,
is_plain_layer_norm, is_plain_layer_norm,
mark_fused_gelu_site,
mark_fused_ln_modulate_site,
modulate_scale_shift,
residual_gate_add,
) )
from sglang.multimodal_gen.configs.models.dits.flux import FluxConfig from sglang.multimodal_gen.configs.models.dits.flux import FluxConfig
from sglang.multimodal_gen.runtime.distributed import ( from sglang.multimodal_gen.runtime.distributed import (
@@ -182,7 +178,7 @@ def _flux_norm_modulate(
out = _flux_fused_ln_modulate(norm, x, scale, shift) out = _flux_fused_ln_modulate(norm, x, scale, shift)
if out is not None: if out is not None:
return out return out
if fused_ln_modulate_active(site) and can_fuse_ln_modulate(x, scale, shift): if fused_ln_modulate_active(site) and can_use_ln_modulate(x, scale, shift):
return fused_ln_modulate(x, scale, shift, norm.eps) return fused_ln_modulate(x, scale, shift, norm.eps)
return modulate_scale_shift(norm(x), scale, shift) return modulate_scale_shift(norm(x), scale, shift)
@@ -396,7 +392,7 @@ class FluxGELU(nn.Module):
mark_fused_gelu_site(self, "proj") mark_fused_gelu_site(self, "proj")
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
if fused_gelu_active(self) and can_fuse_linear_gelu(self.proj, hidden_states): if fused_gelu_active(self) and can_use_linear_gelu(self.proj, hidden_states):
return fused_linear_gelu_tanh( return fused_linear_gelu_tanh(
hidden_states, self.proj.weight, self.proj.bias hidden_states, self.proj.weight, self.proj.bias
) )
@@ -420,7 +416,7 @@ class FluxFusedGELUProj(nn.Module):
mark_fused_gelu_site(self, "proj") mark_fused_gelu_site(self, "proj")
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
if fused_gelu_active(self) and can_fuse_linear_gelu(self.proj, hidden_states): if fused_gelu_active(self) and can_use_linear_gelu(self.proj, hidden_states):
return fused_linear_gelu_tanh( return fused_linear_gelu_tanh(
hidden_states, self.proj.weight, self.proj.bias hidden_states, self.proj.weight, self.proj.bias
) )
@@ -896,7 +892,7 @@ class FluxSingleTransformerBlock(nn.Module):
hidden_states = gate * hidden_states hidden_states = gate * hidden_states
hidden_states = residual + hidden_states hidden_states = residual + hidden_states
else: else:
if fused_gelu_active(self) and can_fuse_linear_gelu( if fused_gelu_active(self) and can_use_linear_gelu(
self.proj_mlp, norm_hidden_states self.proj_mlp, norm_hidden_states
): ):
mlp_hidden_states = fused_linear_gelu_tanh( mlp_hidden_states = fused_linear_gelu_tanh(
@@ -21,15 +21,13 @@ from diffusers.models.attention import AttentionModuleMixin
from diffusers.models.embeddings import TimestepEmbedding, Timesteps from diffusers.models.embeddings import TimestepEmbedding, Timesteps
from diffusers.models.normalization import AdaLayerNormContinuous from diffusers.models.normalization import AdaLayerNormContinuous
from sglang.kernels.ops.diffusion.bitexact_gate import BitExactFusionGate from sglang.kernels.ops.diffusion import (
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add BitExactFusionGate,
from sglang.kernels.ops.diffusion.triton.layernorm_modulate import (
can_use_fused_layernorm_modulate, can_use_fused_layernorm_modulate,
fused_layernorm_modulate_raw, fused_layernorm_modulate_raw,
is_plain_layer_norm,
)
from sglang.kernels.ops.diffusion.triton.silu_mul_bitexact import (
fused_packed_silu_mul_bitexact, fused_packed_silu_mul_bitexact,
is_plain_layer_norm,
residual_gate_add,
) )
from sglang.multimodal_gen.configs.models.dits.flux import FluxConfig from sglang.multimodal_gen.configs.models.dits.flux import FluxConfig
from sglang.multimodal_gen.runtime.distributed import ( from sglang.multimodal_gen.runtime.distributed import (
@@ -18,23 +18,19 @@ import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
from sglang.kernels.ops.diffusion.bitexact_gate import ( from sglang.kernels.ops.diffusion import (
BitExactFusionGate, BitExactFusionGate,
tensors_equal,
)
from sglang.kernels.ops.diffusion.fused_linear_gelu import (
can_fuse_linear_gelu,
fused_gelu_active,
fused_linear_gelu_tanh,
mark_fused_gelu_site,
)
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add
from sglang.kernels.ops.diffusion.triton.layernorm_modulate import (
can_use_fused_layernorm_modulate, can_use_fused_layernorm_modulate,
can_use_fused_qk_head_layernorm, can_use_fused_qk_head_layernorm,
can_use_linear_gelu,
fused_gelu_active,
fused_layernorm_modulate, fused_layernorm_modulate,
fused_linear_gelu_tanh,
fused_qk_head_layernorm, fused_qk_head_layernorm,
is_plain_layer_norm, is_plain_layer_norm,
mark_fused_gelu_site,
residual_gate_add,
tensors_equal,
) )
from sglang.multimodal_gen.configs.models.dits.glmimage import GlmImageDitConfig from sglang.multimodal_gen.configs.models.dits.glmimage import GlmImageDitConfig
from sglang.multimodal_gen.runtime.distributed.parallel_state import ( from sglang.multimodal_gen.runtime.distributed.parallel_state import (
@@ -456,7 +452,7 @@ class GlmImageGELU(nn.Module):
mark_fused_gelu_site(self, "proj") mark_fused_gelu_site(self, "proj")
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
if fused_gelu_active(self) and can_fuse_linear_gelu(self.proj, hidden_states): if fused_gelu_active(self) and can_use_linear_gelu(self.proj, hidden_states):
return fused_linear_gelu_tanh( return fused_linear_gelu_tanh(
hidden_states, self.proj.weight, self.proj.bias hidden_states, self.proj.weight, self.proj.bias
) )
@@ -8,22 +8,16 @@ import numpy as np
import torch import torch
import torch.nn as nn import torch.nn as nn
from sglang.kernels.ops.diffusion.bitexact_gate import ( from sglang.kernels.ops.diffusion import (
BitExactFusionGate, BitExactFusionGate,
tensors_equal, can_use_linear_gelu,
)
from sglang.kernels.ops.diffusion.fused_linear_gelu import (
can_fuse_linear_gelu,
fused_gelu_active, fused_gelu_active,
fused_linear_gelu_tanh, fused_linear_gelu_tanh,
mark_fused_gelu_site,
)
from sglang.kernels.ops.diffusion.hunyuan_qknorm import (
mark_hunyuan_qknorm_site,
try_hunyuan_qknorm,
)
from sglang.kernels.ops.diffusion.triton.hunyuan_qkv_pack import (
hunyuan_qkv_rope_pack, hunyuan_qkv_rope_pack,
mark_fused_gelu_site,
mark_hunyuan_qknorm_site,
tensors_equal,
try_hunyuan_qknorm,
) )
from sglang.multimodal_gen.configs.models.dits import HunyuanVideoConfig from sglang.multimodal_gen.configs.models.dits import HunyuanVideoConfig
from sglang.multimodal_gen.configs.models.fsdp import ( from sglang.multimodal_gen.configs.models.fsdp import (
@@ -115,7 +109,7 @@ class HunyuanMLP(MLP):
mark_fused_gelu_site(self, "fc_in") mark_fused_gelu_site(self, "fc_in")
def forward(self, x: torch.Tensor) -> torch.Tensor: def forward(self, x: torch.Tensor) -> torch.Tensor:
if fused_gelu_active(self) and can_fuse_linear_gelu(self.fc_in, x): if fused_gelu_active(self) and can_use_linear_gelu(self.fc_in, x):
x = fused_linear_gelu_tanh(x, self.fc_in.weight, self.fc_in.bias) x = fused_linear_gelu_tanh(x, self.fc_in.weight, self.fc_in.bias)
else: else:
x, _ = self.fc_in(x) x, _ = self.fc_in(x)
@@ -7,24 +7,18 @@ import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
from sglang.kernels.ops.diffusion.bitexact_gate import ( from sglang.kernels.ops.diffusion import (
BitExactFusionGate, BitExactFusionGate,
tensors_equal, can_use_fused_silu_mul,
)
from sglang.kernels.ops.diffusion.fused_gate_rmsnorm import (
fused_gate_rmsnorm_active, fused_gate_rmsnorm_active,
fused_rmsnorm_scale, fused_rmsnorm_scale,
fused_rmsnorm_tanh_residual, fused_rmsnorm_tanh_residual,
mark_fused_gate_rmsnorm_site,
)
from sglang.kernels.ops.diffusion.modulate_scale_shift import modulate_scale_shift
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add
from sglang.kernels.ops.diffusion.triton.rope_rotate_half_bitexact import (
fused_rope_rotate_half_bitexact, fused_rope_rotate_half_bitexact,
)
from sglang.kernels.ops.diffusion.triton.silu_mul_bitexact import (
can_use_fused_silu_mul,
fused_silu_mul_bitexact, fused_silu_mul_bitexact,
mark_fused_gate_rmsnorm_site,
modulate_scale_shift,
residual_gate_add,
tensors_equal,
) )
from sglang.multimodal_gen.configs.models.dits.ideogram import Ideogram4DiTConfig from sglang.multimodal_gen.configs.models.dits.ideogram import Ideogram4DiTConfig
from sglang.multimodal_gen.configs.models.fsdp import is_layer from sglang.multimodal_gen.configs.models.fsdp import is_layer
@@ -73,9 +73,7 @@ def _fused_qknorm_rope_enabled() -> bool:
def _can_use_fused_qknorm_rope(head_dim: int, dtype: torch.dtype) -> bool: def _can_use_fused_qknorm_rope(head_dim: int, dtype: torch.dtype) -> bool:
from sglang.kernels.ops.diffusion.qknorm_rope import ( from sglang.kernels.ops.diffusion import can_use_fused_inplace_qknorm_rope
can_use_fused_inplace_qknorm_rope,
)
return can_use_fused_inplace_qknorm_rope(head_dim, head_dim, False, dtype) return can_use_fused_inplace_qknorm_rope(head_dim, head_dim, False, dtype)
@@ -119,9 +117,7 @@ def norm_scale_shift(
pass ``scale + 1``), kept off the checkpoint so the identity load is unaffected. pass ``scale + 1``), kept off the checkpoint so the identity load is unaffected.
""" """
if x.is_cuda and x.shape[-1] % 256 == 0 and x.shape[-1] <= 8192: if x.is_cuda and x.shape[-1] % 256 == 0 and x.shape[-1] <= 8192:
from sglang.kernels.ops.diffusion.cutedsl.scale_residual_norm_scale_shift import ( from sglang.kernels.ops.diffusion import fused_norm_scale_shift
fused_norm_scale_shift,
)
return fused_norm_scale_shift( return fused_norm_scale_shift(
x.contiguous(), x.contiguous(),
@@ -358,9 +354,7 @@ class Attention(nn.Module):
and _fused_qknorm_rope_enabled() and _fused_qknorm_rope_enabled()
and _can_use_fused_qknorm_rope(hd, q.dtype) and _can_use_fused_qknorm_rope(hd, q.dtype)
): ):
from sglang.kernels.ops.diffusion.qknorm_rope import ( from sglang.kernels.ops.diffusion import fused_inplace_qknorm_rope
fused_inplace_qknorm_rope,
)
b, s = qkv.shape[0], qkv.shape[1] b, s = qkv.shape[0], qkv.shape[1]
q = q.view(b, s, self.local_heads, hd) q = q.view(b, s, self.local_heads, hd)
@@ -10,28 +10,22 @@ import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
from sglang.kernels.ops.diffusion.bitexact_gate import BitExactFusionGate from sglang.kernels.ops.diffusion import (
from sglang.kernels.ops.diffusion.fused_linear_gelu import ( BitExactFusionGate,
can_fuse_linear_gelu, can_use_linear_gelu,
can_use_ltx2_qknorm_split_rope_cuda,
can_use_ltx2_rms_norm_modulate,
can_use_modulate_scale_shift_cuda,
fused_gelu_active, fused_gelu_active,
fused_linear_gelu_tanh, fused_linear_gelu_tanh,
mark_fused_gelu_site,
)
from sglang.kernels.ops.diffusion.ltx2_qknorm_split_rope import (
can_use_ltx2_qknorm_split_rope_cuda,
ltx2_qknorm_split_rope_cuda,
)
from sglang.kernels.ops.diffusion.ltx2_rmsnorm_modulate import (
can_fuse_ltx2_rms_norm_modulate,
fused_ltx2_rms_norm_modulate, fused_ltx2_rms_norm_modulate,
ltx2_qknorm_split_rope_cuda,
ltx2_rms_norm_modulate_active, ltx2_rms_norm_modulate_active,
mark_fused_gelu_site,
mark_ltx2_rms_norm_modulate_site, mark_ltx2_rms_norm_modulate_site,
)
from sglang.kernels.ops.diffusion.modulate_scale_shift import (
can_use_modulate_scale_shift_cuda,
modulate_scale_shift_cuda, modulate_scale_shift_cuda,
residual_gate_add,
) )
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add
from sglang.multimodal_gen.configs.models.dits.ltx_2 import LTX2ArchConfig, LTX2Config from sglang.multimodal_gen.configs.models.dits.ltx_2 import LTX2ArchConfig, LTX2Config
from sglang.multimodal_gen.configs.models.fsdp import ( from sglang.multimodal_gen.configs.models.fsdp import (
is_blocks_or_transformer_blocks, is_blocks_or_transformer_blocks,
@@ -209,7 +203,7 @@ def _ltx2_rms_norm_modulate(
default). The fused kernel is not bit-exact (<=1 bf16 ULP) so it is gated default). The fused kernel is not bit-exact (<=1 bf16 ULP) so it is gated
on the request-scoped mount rather than a runtime self-check. on the request-scoped mount rather than a runtime self-check.
""" """
if ltx2_rms_norm_modulate_active(block) and can_fuse_ltx2_rms_norm_modulate( if ltx2_rms_norm_modulate_active(block) and can_use_ltx2_rms_norm_modulate(
x, scale, shift x, scale, shift
): ):
return fused_ltx2_rms_norm_modulate(x, scale, shift, eps) return fused_ltx2_rms_norm_modulate(x, scale, shift, eps)
@@ -256,9 +250,7 @@ def _ltx2_try_fused_ada_values9(
return None return None
try: try:
from sglang.kernels.ops.diffusion.triton.ltx2_ada_values import ( from sglang.kernels.ops.diffusion import ltx2_ada_values9
ltx2_ada_values9,
)
return ltx2_ada_values9(scale_shift_table, timestep) return ltx2_ada_values9(scale_shift_table, timestep)
except Exception as exc: except Exception as exc:
@@ -338,9 +330,7 @@ def apply_split_rotary_emb(
and cos.is_cuda and cos.is_cuda
and sin.is_cuda and sin.is_cuda
): ):
from sglang.kernels.ops.diffusion.triton.ltx2_rotary import ( from sglang.kernels.ops.diffusion import apply_ltx2_split_rotary_emb
apply_ltx2_split_rotary_emb,
)
return apply_ltx2_split_rotary_emb(x, cos, sin) return apply_ltx2_split_rotary_emb(x, cos, sin)
@@ -1080,7 +1070,7 @@ class LTX2FeedForward(nn.Module):
mark_fused_gelu_site(self, "proj_in") mark_fused_gelu_site(self, "proj_in")
def forward(self, x: torch.Tensor) -> torch.Tensor: def forward(self, x: torch.Tensor) -> torch.Tensor:
if fused_gelu_active(self) and can_fuse_linear_gelu(self.proj_in, x): if fused_gelu_active(self) and can_use_linear_gelu(self.proj_in, x):
x = fused_linear_gelu_tanh(x, self.proj_in.weight, self.proj_in.bias) x = fused_linear_gelu_tanh(x, self.proj_in.weight, self.proj_in.bias)
else: else:
x, _ = self.proj_in(x) x, _ = self.proj_in(x)
@@ -20,11 +20,9 @@ from safetensors.torch import safe_open
from sglang.kernels.ops.activation.activation import ( from sglang.kernels.ops.activation.activation import (
silu_and_mul_with_activation_rounding_, silu_and_mul_with_activation_rounding_,
) )
from sglang.kernels.ops.diffusion.qknorm_rope import ( from sglang.kernels.ops.diffusion import (
can_use_fused_inplace_qknorm_rope, can_use_fused_inplace_qknorm_rope,
fused_inplace_qknorm_rope, fused_inplace_qknorm_rope,
)
from sglang.kernels.ops.diffusion.triton.indexed_modulation import (
indexed_gate_bf16, indexed_gate_bf16,
indexed_gate_bf16_, indexed_gate_bf16_,
indexed_scale_shift_bf16_, indexed_scale_shift_bf16_,
@@ -14,8 +14,8 @@ from diffusers.models.embeddings import TimestepEmbedding, Timesteps
from diffusers.models.modeling_outputs import Transformer2DModelOutput from diffusers.models.modeling_outputs import Transformer2DModelOutput
from diffusers.models.normalization import AdaLayerNormContinuous from diffusers.models.normalization import AdaLayerNormContinuous
from sglang.kernels.ops.diffusion.fused_linear_gelu import ( from sglang.kernels.ops.diffusion import (
can_fuse_linear_gelu, can_use_linear_gelu,
fused_gelu_active, fused_gelu_active,
fused_linear_gelu_tanh, fused_linear_gelu_tanh,
mark_fused_gelu_site, mark_fused_gelu_site,
@@ -842,7 +842,7 @@ class QwenImageGELU(nn.Module):
mark_fused_gelu_site(self, "proj") mark_fused_gelu_site(self, "proj")
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
if fused_gelu_active(self) and can_fuse_linear_gelu(self.proj, hidden_states): if fused_gelu_active(self) and can_use_linear_gelu(self.proj, hidden_states):
return fused_linear_gelu_tanh( return fused_linear_gelu_tanh(
hidden_states, self.proj.weight, self.proj.bias hidden_states, self.proj.weight, self.proj.bias
) )
@@ -5,18 +5,16 @@ import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
from diffusers.models.embeddings import PixArtAlphaTextProjection, TimestepEmbedding from diffusers.models.embeddings import PixArtAlphaTextProjection, TimestepEmbedding
from sglang.kernels.ops.diffusion.bitexact_gate import BitExactFusionGate from sglang.kernels.ops.diffusion import (
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add BitExactFusionGate,
from sglang.kernels.ops.diffusion.triton.layernorm_modulate import (
can_use_fused_layernorm_modulate,
fused_layernorm_modulate_raw,
is_plain_layer_norm,
)
from sglang.kernels.ops.diffusion.triton.sana_conv_post import (
can_use_fused_bias_glu, can_use_fused_bias_glu,
can_use_fused_bias_silu, can_use_fused_bias_silu,
can_use_fused_layernorm_modulate,
fused_bias_glu, fused_bias_glu,
fused_bias_silu, fused_bias_silu,
fused_layernorm_modulate_raw,
is_plain_layer_norm,
residual_gate_add,
) )
from sglang.multimodal_gen.configs.models.dits.sana import SanaConfig from sglang.multimodal_gen.configs.models.dits.sana import SanaConfig
from sglang.multimodal_gen.runtime.layers.layernorm import RMSNorm from sglang.multimodal_gen.runtime.layers.layernorm import RMSNorm

Some files were not shown because too many files have changed in this diff Show More