Commit Graph

87 Commits

Author SHA1 Message Date
Niklas Haas 1d841635a4 swscale/ops: also include scaling ops in ff_sws_enum_op_lists()
Using the configured scaler from the SwsContext implicitly. This does affect
the output of libswscale/tests/sws_ops.c, which now prints about 4x as much
data (taking roughly 4x as long, but still within a second on my machine).

We can make this process a lot faster by forcing SWS_SCALE_POINT as the
scaler, which skips calculating any actual filter weights in favor of
generating a trivial 1-tap filter.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-05-15 18:53:05 +02:00
Niklas Haas eec9f712f5 swscale/ops: re-use ff_sws_op_list_generate() in ff_sws_enum_op_lists()
The only difference here is an extra ff_sws_add_filters() call, which is
a no-op because src w/h = dst w/h = 16.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-05-15 18:53:05 +02:00
Niklas Haas cac183f46f swscale/ops: don't silently suppress non-ENOTSUP errors
Matches the behavior to the comment.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-05-15 18:53:05 +02:00
Marvin Scholz 752cf875d8 swscale: replace fall-through comments 2026-04-28 12:29:37 +00:00
Niklas Haas 7a71a01a1b swscale/ops: nuke SwsComps.unused
Finally, remove the last relic of this accursed design mistake.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-16 23:25:17 +02:00
Niklas Haas 80bd6c0cd5 swscale/ops: don't strip range metadata for unused components
As alluded to by the previous commit, this is now no longer necessary to
prevent their print-out.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-16 23:23:36 +02:00
Niklas Haas 3680642e1b swscale/ops: simplify min/max range print check
This does come with a slight change in behavior, as we now don't print the
range information in the case that the range is only known for *unused*
components. However, in practice, that's already guaranteed by update_comps()
stripping the range info explicitly in this case.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-16 23:23:36 +02:00
Niklas Haas 9bb2b11d5b swscale/ops: add SwsCompMask parameter to print_q4()
Instead of implicitly excluding NAN values if ignore_den0 is set. This
gives callers more explicit control over which values to print, and in
doing so, makes sure "unintended" NaN values are properly printed as such.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-16 23:23:36 +02:00
Niklas Haas cf2d40f65d swscale/ops: add explicit clear mask to SwsClearOp
Instead of implicitly testing for NaN values. This is mostly a straightforward
translation, but we need some slight extra boilerplate to ensure the mask
is correctly updated when e.g. commuting past a swizzle.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-16 23:23:36 +02:00
Niklas Haas 4020607f0a swscale/ops: add SwsCompMask and related helpers
This new type will be used over the following commits to simplify the
codebase.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-16 23:23:36 +02:00
Niklas Haas cd1126a3cd swscale/ops: add assertion that comps_src is sane
This assertion guards against bugs like that fixed in the previous
commit.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-07 15:10:04 +00:00
Lynne 2b6fbcad6d swscale/vulkan: compile SPIR-V backed only if SPIR-V headers are found
Instead of making Vulkan depend on the headers, make the compilation of
the SPIR-V backend depend on the headers.

Sponsored-by: Sovereign Tech Fund
2026-04-04 19:02:27 +00:00
Lynne 47e4e95173 swscale/vulkan: add a native SPIR-V assembler backend
swscale gets runtime-defined assembly once again!

This commit splits the Vulkan backend into two, SPIR-V and GLSL,
enabling falling back onto the GLSL implementation if an instruction
is unavailable, or simply for testing.

Sponsored-by: Sovereign Tech Fund
2026-04-02 21:15:06 +02:00
Niklas Haas 85bef2c2bc swscale/ops: split SwsConst up into op-specific structs
It was a bit clunky, lacked semantic contextual information, and made it
harder to reason about the effects of extending this struct. There should be
zero runtime overhead as a result of the fact that this is already a big
union.

I made the changes in this commit by hand, but due to the length and noise
level of the commit, I used Opus 4.6 to verify that I did not accidentally
introduce any bugs or typos.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-02 11:48:15 +00:00
Niklas Haas 75b7e8904b swscale/ops: move comment (cosmetic)
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-02 11:48:15 +00:00
Niklas Haas dc705268c7 swscale/ops: define flags_identity as an enum constant
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-02 11:48:15 +00:00
Ramiro Polla 2517c328fc swscale/aarch64: add NEON sws_ops backend
This commit pieces together the previous few commits to implement the
NEON backend for sws_ops.

In essence, a tool which runs on the target (sws_ops_aarch64) is used
to enumerate all the functions that the backend needs to implement. The
list it generates is stored in the repository (ops_entries.c).

The list from above is used at build time by a code generator tool
(ops_asmgen) to implement all the sws_ops functions the NEON backend
supports, and generate a lookup function in C to retrieve the assembly
function pointers.

At runtime, the NEON backend fetches the function pointers to the
assembly functions and chains them together in a continuation-passing
style design, similar to the x86 backend.

The following speedup is observed from legacy swscale to NEON:
A520: Overall speedup=3.780x faster, min=0.137x max=91.928x
A720: Overall speedup=4.129x faster, min=0.234x max=92.424x

And the following from the C sws_ops implementation to NEON:
A520: Overall speedup=5.513x faster, min=0.927x max=14.169x
A720: Overall speedup=4.786x faster, min=0.585x max=20.157x

The slowdowns from legacy to NEON are the same for C/x86. Mostly low
bit-depth conversions that did not perform dithering in legacy.

The 0.585x outlier from C to NEON is gbrpf32le -> gbrapf32le, which is
mostly memcpy with the C implementation. All other conversions are
better.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
2026-03-30 11:38:35 +00:00
Niklas Haas f6a2d41fe2 swscale/ops: keep track of correct dither min/max
Mostly, this just affects the metadata in benign ways, e.g.:

 rgb24 -> yuv444p:
   [ u8 +++X] SWS_OP_READ         : 3 elem(s) packed >> 0
     min: {0, 0, 0, _}, max: {255, 255, 255, _}
   [ u8 +++X] SWS_OP_CONVERT      : u8 -> f32
     min: {0, 0, 0, _}, max: {255, 255, 255, _}
   [f32 ...X] SWS_OP_LINEAR       : matrix3+off3 [...]
     min: {16, 16, 16, _}, max: {235, 240, 240, _}
   [f32 ...X] SWS_OP_DITHER       : 16x16 matrix + {0 3 2 -1}
-    min: {33/2, 33/2, 33/2, _}, max: {471/2, 481/2, 481/2, _}
+    min: {16.001953, 16.001953, 16.001953, _}, max: {235.998047, 240.998047, 240.998047, _}
   [f32 +++X] SWS_OP_CONVERT      : f32 -> u8
     min: {16, 16, 16, _}, max: {235, 240, 240, _}
   [ u8 XXXX] SWS_OP_WRITE        : 3 elem(s) planar >> 0
     (X = unused, z = byteswapped, + = exact, 0 = zero)

However, it surprisingly actually includes a semantic change, whenever
converting from limited range to monob or monow:

 yuv444p -> monow:
   [ u8 +XXX] SWS_OP_READ         : 1 elem(s) planar >> 0
     min: {0, _, _, _}, max: {255, _, _, _}
   [ u8 +XXX] SWS_OP_CONVERT      : u8 -> f32
     min: {0, _, _, _}, max: {255, _, _, _}
   [f32 .XXX] SWS_OP_LINEAR       : luma [...]
     min: {-20/219, _, _, _}, max: {235/219, _, _, _}
   [f32 .XXX] SWS_OP_DITHER       : 16x16 matrix + {0 -1 -1 -1}
-    min: {179/438, _, _, _}, max: {689/438, _, _, _}
+    min: {-0.089371, _, _, _}, max: {2.071106, _, _, _}
+  [f32 .XXX] SWS_OP_MAX          : {0 0 0 0} <= x
+    min: {0, _, _, _}, max: {2.071106, _, _, _}
   [f32 .XXX] SWS_OP_MIN          : x <= {1 _ _ _}
-    min: {179/438, _, _, _}, max: {1, _, _, _}
+    min: {0, _, _, _}, max: {1, _, _, _}
   [f32 +XXX] SWS_OP_CONVERT      : f32 -> u8
     min: {0, _, _, _}, max: {1, _, _, _}
   [ u8 XXXX] SWS_OP_WRITE        : 1 elem(s) planar >> 3
     (X = unused, z = byteswapped, + = exact, 0 = zero)

Note the presence of an extra SWS_OP_MAX, to correctly clamp sub-blacks
(values below 16) to 0.0, rather than underflowing. This was previously
undetected because the dither was modelled as adding 0.5 to every pixel value,
but that's only true on average - not always.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 12:13:11 +02:00
Niklas Haas 915523e136 swscale/ops: add missing check on SwsDitherOp.y_offset
Doesn't actually affect anything in the currently generated ops lists.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 12:10:38 +02:00
Niklas Haas e8f6c9dbf2 swscale/ops: only print SWS_OP_SCALE denom if not 1
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas ff397a327a swscale/ops: remove unneeded conditional on describe_comp_flags
next->comps.unused[] is redundant with SWS_COMP_GARBAGE now.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas 804041045e swscale/ops: remove redundant unused mask from ops printout
This is now fully redundant with the previous op's output; because unused
components are always marked as garbage on the input side.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas 13388c0cac swscale/ops: test for SWS_COMP_GARBAGE instead of next->comps.unused
When printing/describing operations.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas 6fb0efb35c swscale/ops: strip value range from garbage components
Just removes the unnecessary value range after the WRITE, as a result of
the previous change and the fact that we already skipped printing these for
unused components.

 rgb24 -> bgr24:
   [ u8 XXXX -> +++X] SWS_OP_READ         : 3 elem(s) packed >> 0
     min: {0 0 0 _}, max: {255 255 255 _}
   [ u8 ...X -> +++X] SWS_OP_SWIZZLE      : 2103
     min: {0 0 0 _}, max: {255 255 255 _}
   [ u8 ...X -> XXXX] SWS_OP_WRITE        : 3 elem(s) packed >> 0
-    min: {0 0 0 _}, max: {255 255 255 _}
     (X = unused, z = byteswapped, + = exact, 0 = zero)

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas 7d94d9fc52 swscale/ops: mark all unused components as GARBAGE
This only affects the print-out of the SWS_OP_WRITE at the end of every op,
list, because the ops list print-out was otherwise already checking the unused
mask.

 rgb24 -> bgr24:
   [ u8 XXXX -> +++X] SWS_OP_READ         : 3 elem(s) packed >> 0
     min: {0 0 0 _}, max: {255 255 255 _}
   [ u8 ...X -> +++X] SWS_OP_SWIZZLE      : 2103
     min: {0 0 0 _}, max: {255 255 255 _}
-  [ u8 ...X -> +++X] SWS_OP_WRITE        : 3 elem(s) packed >> 0
+  [ u8 ...X -> XXXX] SWS_OP_WRITE        : 3 elem(s) packed >> 0
     min: {0 0 0 _}, max: {255 255 255 _}
     (X = unused, z = byteswapped, + = exact, 0 = zero)

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas 672c0ad69a swscale/ops: slightly refactor unused[] computation
Needed for the upcoming removal of op->comps.unused[]. This keeps the
dependency array entirely within the ff_sws_op_list_update_comps() function,
apart from being arguably simpler and easier to follow.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas c0cc7f341a swscale/ops: simplify SwsOpList.order_src/dst
Just define these directly as integer arrays; there's really no point in
having them re-use SwsSwizzleOp; the only place this was ever even remotely
relevant was in the no-op check, which any decent compiler should already
be capable of optimizing into a single 32-bit comparison.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-29 09:39:09 +00:00
Niklas Haas bf09910292 swscale/ops: add filter kernel to SwsReadWriteOp
This allows reads to directly embed filter kernels. This is because, in
practice, a filter needs to be combined with a read anyways. To accomplish
this, we define filter ops as their semantic high-level operation types, and
then have the optimizer fuse them with the corresponding read/write ops
(where possible).

Ultimately, something like this will be needed anyways for subsampled formats,
and doing it here is just incredibly clean and beneficial compared to each
of the several alternative designs I explored.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 18:50:13 +01:00
Niklas Haas 63140bff5e swscale/ops: define SWS_OP_FILTER_H/V
This commit merely adds the definitions. The implementations will follow.

It may seem a bit impractical to have these filter ops given that they
break the usual 1:1 association between operation inputs and outputs, but
the design path I chose will have these filter "pseudo-ops" end up migrating
towards the read/write for CPU implementations. (Which don't benefit from
any ability to hide the intermediate memory internally the way e.g. a fused
Vulkan compute shader might).

What we gain from this design, on the other hand, is considerably cleaner
high-level code, which doesn't need to concern itself with low-level
execution details at all, and can just freely insert these ops wherever
it needs to. The dispatch layer will take care of actually executing these
by implicitly splitting apart subpasses.

To handle out-of-range values and so on, the filters by necessity have to
also convert the pixel range. I have settled on using floating point types
as the canonical intermediate format - not only does this save us from having
to define e.g. I32 as a new intermediate format, but it also allows these
operations to chain naturally into SWS_OP_DITHER, which will basically
always be needed after a filter pass anyways.

The one exception here is for point sampling, which would rather preserve
the input type. I'll worry about this optimization at a later point in time.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 18:50:13 +01:00
Niklas Haas 4395e8f3a2 swscale/ops: add helper function to enumerate over all op lists
This moves the logic from tests/sws_ops into the library itself, where it
can be reused by e.g. the aarch64 asmgen backend to iterate over all possible
operation types it can expect to see.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas f62c837eb6 swscale/ops: move op-formatting code to helper function
Annoyingly, access to order_src/dst requires access to the SwsOpList, so
we have to append that data after the fact.

Maybe this is another incremental tick in favor of `SwsReadWriteOp` in the
ever-present question in my head of whether the plane order should go there
or into SwsOpList.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas 8fae195395 swscale/ops: avoid printing values for ignored components
Makes the list output a tiny bit tidier. This is cheap to support now thanks
to the print_q4() helper.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas 1caa548caf swscale/ops: refactor PRINTQ() macro
Instead of allocating a billion tiny temporary buffers, these helpers now
directly append to an AVBPrint. I decided to explicitly control whether or not
a value with denom 0 should be printed as "inf/nan" or as "_", because a lot
of ops have the implicit semantic of "den == 0 -> ignored". At the same time,
we don't want to obscure legitimate NAN/INF values when the do occur
unintentionally.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas 0d54a1b53a swscale/ops: remove , from comp min/max print-out for consistency
Interferes with an upcoming simplification, otherwise.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas a0bb0c2772 swscale/ops: use AVBPrint for assembling op descriptions
This commit does not yet touch the PRINTQ macro, but it gets rid of at least
one unnecessary hand-managed buffer.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas 95e6c68707 swscale/ops: print exact constant on SWS_OP_SCALE
More informative.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas f6d963553b swscale/ops: correctly uninit all ops in ff_sws_op_list_remove_at()
This only ever removed a single op, even with count > 1.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 16:48:13 +00:00
Niklas Haas 18962a3764 swscale/ops: loop over copied list instead of original (cosmetic)
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-18 09:09:44 +00:00
Niklas Haas eebc07aba7 swscale/ops: simplify ff_sws_op_list_print
Using the new ff_sws_op_type_name() helper.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 11:25:57 +01:00
Niklas Haas 1addde59f9 swscale/ops: add ff_sws_op_type_name
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 11:25:57 +01:00
Niklas Haas 1d16161a8b swscale/ops: use SwsCompFlags typedef instead of plain int
This improves the debugging experience. These are all internal structs so
there is no need to worry about ABI stability as a result of adding flags.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 11:25:57 +01:00
Niklas Haas b4bcb00cd3 swscale/ops: add and use ff_sws_op_list_input/output()
Makes various pieces of code that expect to get a SWS_OP_READ more robust,
and also allows us to generalize to introduce more input op types in the
future (in particular, I am looking ahead towards filter ops).

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-05 23:34:56 +00:00
Niklas Haas 68f3886460 swscale/ops_dispatch: split off compile/dispatch code from ops.c
This code is self-contained and logically distinct from the ops-related
helpers in ops.c, so it belongs in its own file.

Purely cosmetic; no functional change.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-05 23:34:56 +00:00
Niklas Haas 4178c4d430 swscale/ops: remove unneeded macro
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-05 23:34:56 +00:00
Niklas Haas 5384908e56 swscale/ops: move pass compilation logic to helper function
Purely cosmetic.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-05 23:34:56 +00:00
Niklas Haas 02e4b45f7f swscale/graph: reintroduce SwsFrame
AVFrame just really doesn't have the semantics we want. However, there a
tangible benefit to having SwsFrame act as a carbon copy of a (subset of)
AVFrame.

This partially reverts commit 67f3627267.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-01 21:57:53 +00:00
Niklas Haas 67f3627267 swscale/graph: nuke SwsImg
This has now become fully redundant with AVFrame, especially since the
existence of SwsPassBuffer. Delete it, simplifying a lot of things and
avoiding reinventing the wheel everywhere.

Also generally reduces overhead, since there is less redundant copying
going on.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas 363779a4bb swscale/ops: don't set src/dst_frame_ptr in op_pass_run()
Already set by setup().

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas 62dc591a80 swscale/ops: correctly shift pointers for last row handling
The current logic didn't take into account the possible plane shift. Just
re-compute the correctly shifted pointers using the row position.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas bca806d4f9 swscale/ops: avoid stack copies of SwsImg
Instead, precompute the correctly swizzled data and stride in setup()
and just reference the SwsOpExec fields directly.

To avoid the stack copies in handle_tail() we can introduce a temporary
array to hold just the pointers.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00