Signed-off-by: mglyn <1203789601@qq.com> Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
4.7 KiB
Device-side video postprocessing
vLLM-Omni can convert decoded WAN2.2 video from normalized floating-point
[B, C, T, H, W] tensors to contiguous uint8 [B, T, H, W, C] frames before
the worker-to-engine device-to-host transfer. This reduces the transported video
payload by four times when the original IPC path widens bfloat16 to float32.
The optimization is disabled by default. Enable it with a structured diffusion configuration:
from vllm_omni import Omni
engine = Omni(
model="Wan-AI/Wan2.2-TI2V-5B-Diffusers",
video_output_transport={"enable_device_postprocess": True},
)
For the server CLI, pass the same object as JSON:
vllm serve Wan-AI/Wan2.2-TI2V-5B-Diffusers --omni \
--video-output-transport '{"enable_device_postprocess": true}'
For a deploy configuration, place it on the diffusion stage:
stages:
- stage_id: 0
video_output_transport:
enable_device_postprocess: true
Runtime contract
WAN2.2 emits decoded video through the typed DiffusionOutput.media field. The
model runner validates and splits the batch into request-local tensors before
preparing each video for transport. The worker then performs the D2H copy and
shared-memory IPC using the prepared representation. The engine uses the generic
media finalizer instead of a model-specific postprocessor.
The runtime leaves the video in normalized floating-point form when:
enable_device_postprocessis false;- the requested output type is not
np; or - frame interpolation still requires the floating-point tensor.
These are policy fallbacks, not malformed-contract errors. If the device cannot allocate the temporary conversion buffer, the runner logs a warning and prepares the request-local float representation instead. Invalid tensor layout, encoding, value range, lifecycle state, and non-memory runtime failures still fail before IPC.
Precision
Device preparation converts to float32 before denormalization and quantization. For bfloat16 WAN output this is more precise than the historical host path, which denormalizes in bfloat16. Relative to that path, values may differ by at most one level out of 255. Float32 inputs remain byte-identical after quantization.
Scope
WAN2.2 is the reference migration for the first contract version. Other video pipelines continue using their existing output paths until they can declare the same typed media contract without bypassing model-specific float consumers such as safety checks or audio/video packaging.
See RFC #6541 for the contract, lifecycle, batching rules, and migration plan.
Experimental registered-SHM video transport
video_output_transport.enable_registered_shm is a separate, default-off option
for CUDA request-mode workers. It copies eligible video tensors directly into
CUDA-registered shared memory and returns borrowed CPU views, removing the
intermediate pinned-to-SHM and SHM-to-consumer host copies. The D2H transfer still
occurs.
video_output_transport.enable_borrowed_frames independently enables borrowed
RGB AVFrames in the Videos API MP4 response encoder, removing the third host
copy into PyAV frame buffers. It also defaults to false. Eligible uint8 RGB
frames are wrapped with VideoFrame.from_numpy_buffer; other dtypes/layouts use
the existing encoder path. Color conversion, compression and muxing still occur.
Enable it through the same CLI, Python or deploy-stage configuration:
--video-output-transport '{"enable_registered_shm": true, "enable_borrowed_frames": true}'
Registered SHM applies only to prepared typed video media (currently WAN2.2) and
explicitly marked legacy video outputs (currently MiniMax-H3). H3 marks the video
entry of its (video, audio) output; audio, trajectory data, images and unmarked
legacy outputs keep their existing transport. Device-side postprocessing is
independent and does not need to be enabled for registered SHM.
Pre-encoded MP4 outputs bypass raw-frame transport and API-side encoding.
The existing SHM routing threshold is unchanged: the tensor or its backing storage must exceed 1,000,000 bytes. This is not a measured performance crossover. SHM is allocated and registered on every transfer, without pooling; small outputs can regress and registration latency can vary. Benchmark representative output sizes before opting in. CPU outputs, non-CUDA backends, and synchronous/step-mode transfers keep their existing path.
Borrowed views keep the unlinked mapping alive until their last user releases it. CUDA registration/copy failures fail the request; if CUDA cleanup cannot be confirmed, the worker retains the mapping for safety and must be restarted before further registered-SHM transfers.