Historical recipe — not current setup guidance. Measurements and implementation links below belong to their original workload and revision. Deployment labels and defaults may be obsolete. Use the reviewed performance recipes; browse historical evidence.

AVPlumber · Mixer optimization cookbook

cuda_transform, the shared geometry node

Implementation links are pinned to develop at c20464a5 (2026-10-09). The mixer has changed since; this page was checked against develop at 8fa15c92 on 2026-10-13. Measurements retain their original workload and scope. Watch the current 192-input L4 demo.

Current scope. Source transforms are implemented. GPU smoke checks now cover source normalization and rendition conversion as well as the primitive. No full 188-source performance comparison is claimed.

cuda_transform takes one CUDA frame and gives one frame per output canvas. Each output has its own size, sw_format and layers that crop, scale and place the input, and may take a lower rate. It is the rect compositor's drawing without input matching or a clock. An output that would only repeat the input gets the input frame itself: no draw, no copy. The 88-source show it was written beside had no stage for it, and nothing was measured in a show.

Status: Implemented; primitive and mixer integration GPU smokes pass
cuda_transform: one frame in, one frame per output, and the pass-through for an output that would only repeat the input One input frame enters the node. Two outputs are drawn by the rect compositor's draw module into frames of their own canvas: one cropped and scaled, one at a lower rate that takes the first frame of each slot. A third output has one unblended layer that takes the whole frame to a canvas of the same size and sw_format, so the node hands it the input frame itself, with no draw, no copy and no stream synchronisation. One frame in, one frame per output · decided again for every frame Input frame CUDA or CUarray cuda_transform draw: the compositor's module crop, scale, place · black background one stream synchronisation per frame pass on: whole frame, 1:1, same size and sw_format Output · its own canvas drawn into a new frame Output · with fps first frame of each slot, drawn Output · same as the input the input frame itself Blue: drawn by the same code the compositors draw with. Green: the same frame handed on, by reference. When every due output is passed on, the node does no GPU work for that frame.
Schematic, not a measurement. Three outputs as an example; a node takes any number. A CUarray input is passed on only to an output that sets pass_arrays.

What it is, and what it shares with the compositors

One input edge, one named device (hwaccel) and a list of outputs. Each output names its edge or edges (dst), a canvas (width, height, sw_format, default nv12) and layers: the compositors' layer objects with crop, destination rectangle and fit, all reading the one input. Where no layer draws, the canvas keeps its background. Every input frame gives one frame on each output that is due, with the input's timestamp and metadata.

The node holds no drawing code of its own. It calls CudaRectDraw, the draw module and kernels of cuda_rect_overlay, mixer_compositor and mixer_keyer, with layers parsed and resolved by the same functions. What a node does around that module to own a canvas, the canvas parameters, the pool of linear CUDA frames, a blank frame from it and the source's properties copied onto it, moved out of the compositor into cuda_rect_frame.hpp, which the compositor nodes (many inputs, one canvas) and this node (one input, many canvases) both include. The compositor nodes' behaviour did not change.

The pass-through rule

An output gets the input frame itself, a new reference to the same buffers, when its canvas would only repeat the input:

The rule is checked for every frame, so a source that changes size is scaled when it has to be, and the node logs when an output starts or stops passing frames on (cuda_transform: output 2 passes the input frame on, or draws the input frame). A passed frame costs no draw, no copy and no stream synchronisation.

pass_arrays is off by default because a CUarray is a different kind of storage from the linear CUDA frames the node draws. Set it only for consumers that read arrays as well. Without it, a CUarray input to a 1:1 output is drawn into a linear frame.

Run on the L4

tests/cuda/smoke_transform.py passes on the L4 host. It feeds a 192×108 NV12 source at 30 fps into one node with four outputs: a letterbox on a 64×48 canvas, a crop scaled onto 96×64 at 15 fps, a 1:1 copy and a 1:1 copy at 15 fps. Beside the node, four cuda_rect_overlay nodes draw the same layers from the same frames.

Not covered by the smoke test: a CUarray or browser-texture input, 10-bit formats, 1080p sizes, a full output edge, and any CPU, latency or VRAM figure. The comparison is against the same kernels, so it says nothing about picture quality against scale_cuda.

Limits as built

Where the mixer can and cannot use it today

The 88-source show had no stage for it. All scaling happens inside the compositors, which draw with the same module, and every output is canvas-size, so its graph holds no cuda_transform. Nothing about the node was measured in a show: no CPU, GPU, VRAM or latency figure exists.

At a glance

Where in the code

Smoke test run on the L4 host (one NVIDIA L4, 16 vCPUs). The list of where the node cannot stand in comes from reading the mixer's graph code, not from a build or a run. The 2026-10-06 mixer integration smoke also passed source normalization and rendition conversion. This does not establish large-show throughput or validate every optional transform configuration.