Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
15bdf83
gpu: open the Linux Vulkan device with video queues, and decode and e…
imbcmdth Oct 18, 2026
9d1cd40
gpu: copy a Vulkan decoder's picture into textures wgpu-core counts a…
imbcmdth Oct 18, 2026
429873a
tests: the decode and encode cases on the device run on Linux on Vulkan
imbcmdth Oct 18, 2026
7917eb7
lanes: a self-clocked reader's queued arrivals count against the room…
imbcmdth Oct 18, 2026
5eab2ae
docs: the Linux decoder and encoder on the sidecar's Vulkan device
imbcmdth Oct 18, 2026
f695987
hardware: the Vulkan card pass names the device its hwupload goes to
imbcmdth Oct 18, 2026
b96fbd7
gpu: smaller allocator blocks on the Linux device, and one thread for…
imbcmdth Oct 18, 2026
a36ffc4
plans: the Linux half on the GTX 1050, and what needs the L4 now
imbcmdth Oct 18, 2026
a98728b
encode: an encoder on the device asked for yuv420p codes nv12, and sa…
imbcmdth Oct 18, 2026
bcce48e
gpu: the semaphore signaller waits on wgpu's timeline semaphore, not …
imbcmdth Oct 18, 2026
a0fd323
hold: a port feed that comes up while its clock is behind real time i…
imbcmdth Oct 18, 2026
b867844
feeds: FFRWD_TRACE_FEED traces a port feed and its hold to a file, an…
imbcmdth Oct 18, 2026
8f6b9d9
gpu: the Vulkan route copies a decoder's pictures into a pool of targ…
imbcmdth Oct 19, 2026
345957e
cli, feeds: a region holding a port feed loads libav, FFRWD_ENCODE_FE…
imbcmdth Oct 19, 2026
b0ae6a1
lower: a feeder's input with a declared shape is probed for half a se…
imbcmdth Oct 19, 2026
eaa46de
trace: the peak of each sound run a hold hands and a node emits, and …
imbcmdth Oct 19, 2026
a833db8
trace: the luma of every frame a node emits, and FFRWD_TEE_OUT copies…
imbcmdth Oct 19, 2026
577ffb0
lower: a feeder's input is probed as ffmpeg's defaults say again
imbcmdth Oct 19, 2026
912bf25
feeds: a feeder that rejoins while the one before waits for room is r…
imbcmdth Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cli/ffrwd/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -3506,7 +3506,11 @@ def for_run(cls, member: str = "") -> _Log:
def append(self, chunk: bytes) -> None:
with self._turn:
if self._file is not None:
# Flushed as it comes: a member killed with the run, or a
# host killed before it closes the file, leaves what it
# wrote rather than an empty `.part`.
self._file.write(chunk)
self._file.flush()
self._chunks.append(chunk)
self._held += len(chunk)
while self._held - len(self._chunks[0]) >= self._keep:
Expand Down
5 changes: 4 additions & 1 deletion cli/ffrwd/hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,17 @@ def _same(*names: str) -> dict[str, str]:
# encoder refuses.
codec_sw_formats={"hevc": {"yuv444p": "yuv444p", "yuvj444p": "yuv444p"}},
),
# Vulkan. `hwupload` makes no device of its own here either: ffmpeg
# refuses it ("A hardware device reference is required to upload frames
# to") unless the command names one.
_Family(
hwaccels=frozenset({"vulkan"}),
output_format="vulkan",
encoder_suffix="_vulkan",
filter_suffix="_vulkan",
upload="hwupload",
encoder_needs_device=True,
upload_device="",
upload_device="vulkan",
variants={
"scale": {"width": "w", "height": "h", "w": "w", "h": "h"},
"overlay": _same("x", "y"),
Expand Down
19 changes: 18 additions & 1 deletion cli/ffrwd/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6671,7 +6671,8 @@ def rewrite(ref: FrameRef) -> FrameRef:
rows_modules=self._rows_modules(sidecar, members),
lookahead=sidecar.lookahead + self.encode_delays.get(sidecar.id, 0),
av=bool(self.decodes.get(sidecar.id) or self.encodes.get(sidecar.id))
or any(node.filter in (DEVICE_FILTER, DECODE, ENCODE) for node in nodes.values()),
or any(node.filter in (DEVICE_FILTER, DECODE, ENCODE) for node in nodes.values())
or self._holds_a_port_feed(members),
graph=Graph(
input_paths=[PIPE] * len(read_order),
sources={alias: index for index, alias in enumerate(read_order)},
Expand Down Expand Up @@ -6851,6 +6852,22 @@ def _region_tags(
return ()
return tuple(tuple(found.get(alias, {}).items()) for alias in order)

def _holds_a_port_feed(self, members: Sequence[str]) -> bool:
"""Whether a node of this region holds an input a port serves: its
feeder may send the picture coded (a run-time lateral copies an
input's own h264), which the sidecar decodes with libavcodec whatever
the output's encoder is."""
for name in members:
shape = self.node_shapes.get(name)
if shape is None:
continue
node = self.g.nodes[name]
for port in shape.inputs:
hold = port.pairing.hold
if hold is not None and hold.port_param is not None and port.name not in node.ports:
return True
return False

def _region_listens(
self, members: Sequence[str], names: Mapping[str, str]
) -> tuple[tuple[int, str, str], ...]:
Expand Down
12 changes: 10 additions & 2 deletions cli/tests/test_hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ def test_nothing_changes_without_a_hardware_encoder_or_a_hwaccel_or_the_filters(
def test_the_vulkan_family_sends_a_picture_up_again_before_its_encoder() -> None:
"""The flips have Vulkan variants and ride the card; `pad` has none, so
the picture comes down for it and, the encoder reading only the card's
frames, goes up again ahead of the file."""
frames, goes up again ahead of the file. `hwupload` makes no device of
its own, so the command names one for the filtergraph."""
g = _graph(
[
_node("n1", "hflip", {}, ["src:f:v:0"]),
Expand All @@ -302,7 +303,14 @@ def test_the_vulkan_family_sends_a_picture_up_again_before_its_encoder() -> None
options={"f": {"hwaccel": "vulkan"}},
)
kept = keep_on_the_card(g, _reference(), _FILM)
assert kept.input_options == {"f": {"hwaccel": "vulkan", "hwaccel_output_format": "vulkan"}}
assert kept.input_options == {
"f": {
"init_hw_device": "vulkan=up",
"filter_hw_device": "up",
"hwaccel": "vulkan",
"hwaccel_output_format": "vulkan",
}
}
assert _filters(kept) == [
("n1", "hflip_vulkan", {}, ["src:f:v:0"]),
("n2", "scale_vulkan", {"w": 1280, "h": -2}, ["n1"]),
Expand Down
14 changes: 14 additions & 0 deletions cli/tests/test_node_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,20 @@ def impure(params: Mapping[str, object], bound: Sequence[str]) -> dict[str, obje
assert sidecar.impure == ("matte.wasm",)


def test_a_node_holding_an_input_a_port_serves_loads_libav_whatever_the_encoder(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""A feeder may send its picture coded, as a run-time lateral copies an
input's own h264, and the sidecar decodes it with libavcodec: the region
is told where libav is with an encoder of ffmpeg's out, as with one of
its own. A node holding no port feed is not."""
query = "COPY (SELECT {}" + _FROM + " WITH (video_codec 'libx264')"
(fed,) = _plan(query.format("inset(f.video[1], port => 9100)"), monkeypatch).sidecars
assert fed.av
(plain,) = _plan(query.format("dim(f.video[1], spot(f.video[1]))"), monkeypatch).sidecars
assert not plain.av


# -- a port held on the GPU takes the region's edges coded -------------------


Expand Down
2 changes: 1 addition & 1 deletion docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ ffmpeg -i tests/fixtures/testsrc.mp4 -map 0:v:0 -c:0 copy -avoid_negative_ts dis
copy -color_range:0 tv -colorspace:0 bt709 boxed.mp4
```

With `memory => 'host'` the same module holds nothing on the GPU, and its region takes raw frames on both pipes unless the declaration says `SET ffrwd.edges = 'coded'`. The region's `decode` is told `hw=auto`: where the machine's decoder and the device share one (D3D12 on Windows, Metal on macOS) the decoder's frames land on the device directly; elsewhere the decoded picture reaches it through the sidecar's own upload at the module's input and comes back through one download at the output, which the `upload` and `download` rows of `--profile` count.
With `memory => 'host'` the same module holds nothing on the GPU, and its region takes raw frames on both pipes unless the declaration says `SET ffrwd.edges = 'coded'`. The region's `decode` is told `hw=auto`: where the machine's decoder and the device share one (D3D12 on Windows, Metal on macOS, Vulkan on Linux) the decoder's frames land on the device directly; elsewhere the decoded picture reaches it through the sidecar's own upload at the module's input and comes back through one download at the output, which the `upload` and `download` rows of `--profile` count.

The device is on the adapter the sidecar picks by default, the one `ffrwd setup gpu` marks in its list. `--gpu-adapter` on `run` or `compile`, or `FFRWD_GPU_ADAPTER` where the flag is absent, names another, by its index in that list or by part of its name: `ffrwd run --gpu-adapter radeon -f query.sql` opens the device on that adapter, so `gpu_box` and the decoder and encoder sharing the device run there, and the printed command carries `-gpu-adapter radeon` after the `-gpu` grant. With neither, the command is the one above. Where the adapter has no decoder for the stream, `hw=auto` falls back as it does anywhere, to a decoder on a device of its own or to software.

Expand Down
Loading
Loading