63 Commits

Author SHA1 Message Date
Niklas Haas
0e983a0604 swscale: align allocated frame buffers to SwsPass hints
This avoids hitting the slow memcpy fallback paths altogether, whenever
swscale.c is handling plane allocation.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-10 15:12:18 +02:00
Niklas Haas
5441395a48 swscale/graph: add optimal alignment/padding hints
Allows the pass buffer allocator to make smarter decisions based on the actual
alignment requirements of the specific pass.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-04-10 15:12:18 +02:00
Niklas Haas
814f862832 swscale/graph: add scaling ops when required
The question of whether to do vertical or horizontal scaling first is a tricky
one. There are several valid philosophies:

1. Prefer horizontal scaling on the smaller pixel size, since this lowers the
   cost of gather-based kernels.
2. Prefer minimizing the number of total filter taps, i.e. minimizing the size
   of the intermediate image.
3. Prefer minimizing the number of rows horizontal scaling is applied to.

Empirically, I'm still not sure which approach is best overall, and it probably
depends at least a bit on the exact filter kernels in use. But for now, I
opted to implement approach 3, which seems to work well. I will re-evaluate
this once the filter kernels are actually finalized.

The 'scale' in 'libswscale' can now stand for 'scaling'.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 18:50:14 +01:00
Niklas Haas
53ee892035 swscale/graph: add way to roll back passes
When an op list needs to be decomposed into a more complicated sequence
of passes, the compile() code may need to roll back passes that have already
been partially compiled, if a later pass fails to compile.

This matters for subpass splitting (e.g. for filtering), as well as for
plane splitting.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-28 18:50:13 +01:00
Niklas Haas
5230624619 swscale/graph: allocate output buffers after adding all passes
There's no reason to immediately allocate all of these; we can do it at the
end when we know for sure which passes we have.

This will matter especially if we ever add a way to remove passes again after
adding them (spoiler: we will).

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-18 09:09:44 +00:00
Huihui_Huang
6e37545d6b swscale: fix possible lut leak in adapt_colors()
adapt_colors() allocates a SwsLut3D before calling add_convert_pass(). If add_convert_pass() fails, the function returns without freeing the previously allocated lut. Free lut on that error path.

Signed-off-by: Huihui_Huang <hhhuang@smu.edu.sg>
2026-03-17 15:56:40 +08:00
Niklas Haas
3503b19711 swscale: add enum SwsScaler, SwsContext.scaler to replace legacy flags
Another step towards a cleaner API, with a cleaner separation of purposes.
Also avoids wasting a whopping one third of the flag space on what really
shouldn't have been a flag to begin with.

I pre-emptively decided to separate the scaler selection between "scaler"
and "scaler_sub", the latter defining what's used for things like 4:2:0
subsampling.

This allows us to get rid of the awkwardly defined SWS_BICUBLIN flag, in favor
of that just being the natural consequence of using a different scaler_sub.

Lastly, I also decided to pre-emptively axe the poorly defined and
questionable SWS_X scaler, which I doubt ever saw much use. The old flag
is still available as a deprecated flag, anyhow.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-12 22:09:04 +01:00
Niklas Haas
36c31fd5ba swscale: don't hard code number of scaler params
In case we ever need to increase this number in the future.
I won't bother bumping the ABI version for this new #define, since it doesn't
affect ABI, and I'm about to bump the ABI version in a following commit.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-12 22:08:07 +01:00
Niklas Haas
e7c84a8e6a swscale/ops_dispatch: infer destination format from SwsOpList
This is now redundant.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-12 21:02:48 +00:00
Niklas Haas
b5db7c7354 swscale/ops_dispatch: have ff_sws_compile_pass() take ownership of ops
More useful than just allowing it to "modify" the ops; in practice this means
the contents will be undefined anyways - might as well have this function
take care of freeing it afterwards as well.

Will make things simpler with regards to subpass splitting.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-12 21:02:48 +00:00
Niklas Haas
563cc8216b swscale/graph: allow setup() to return an error code
Useful for a handful of reasons, including Vulkan (which depends on external
device resources), but also a change I want to make to the tail handling.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-12 21:02:48 +00:00
Niklas Haas
6c92ab6a4e swscale/graph: remove redundant check
Such formats are already rejected by ff_sws_decode/encode_pixfmt().

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-12 21:02:48 +00:00
Niklas Haas
9571f5cf15 swscale/graph: simplify ff_sws_graph_add_pass() usages
Now that this function returns a status code and takes care of cleanup on
failure, many call-sites can just return the function directly.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 12:01:51 +01:00
Niklas Haas
2e29833832 swscale/graph: have ff_sws_graph_add_pass() free priv on failure
This is arguably more convenient for most downstream users, as will be
more prominently seen in the next commit.

Also allows this code to re-use a pass_free() helper with the graph uninit.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 12:01:51 +01:00
Niklas Haas
42a47838ea swscale/graph: add setup()/free() to ff_sws_graph_add_pass() signature
This is just slightly common enough a pattern that it IMO makes sense to do
so. This will also make more sense after the following commits.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 12:01:51 +01:00
Niklas Haas
254c07bf60 swscale/graph: rename sws_filter_run_t to SwsPassFunc
This name is weirdly out-of-place in the libswscale naming convention.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 12:01:51 +01:00
Niklas Haas
fdc0a66cbd swscale/graph: skip threading for single-slice passes
This condition was weaker than necessary.

In particular, graph->num_thread == 1 guarantees pass->num_slices == 1.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 12:01:51 +01:00
Niklas Haas
a534156083 swscale/graph: pass SWS_OP_FLAG_OPTIMIZE
Instead of optimizing it with an explicit call. May enable more optimizations
in the future.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 11:25:58 +01:00
Niklas Haas
f77ab892f5 swscale/ops_dispatch: print op list on successful compile
Instead of once at the start of add_convert_pass(). This makes much
more sense in light of the fact that we want to start e.g. splitting
passes apart.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-09 11:25:58 +01:00
Niklas Haas
1c2a300f66 swscale/graph: remove pointless helper
Basically identical to ff_sws_graph_add_pass() after the previous commit.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-05 23:34:56 +00:00
Niklas Haas
d9e594ca96 swscale/graph: have ff_sws_graph_add_pass() return an error code
This allows distinguishing between different types of failure, e.g.
AVERROR(EINVAL) on invalid pass dimensions.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-05 23:34:56 +00:00
Niklas Haas
0540b42657 swscale/graph: correctly handle single threaded mode
The code was evidently designed at one point in time to support "direct"
execution (not via a thread pool) for num_threads == 1, but this was never
implemented.

As a side benefit, reduces context creation overhead in single threaded
mode (relevant e.g. inside the libswscale self test), due to not needing to
spawn and destroy several thousand worker threads.

Co-authored-by: Ramiro Polla <ramiro.polla@gmail.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-03 23:13:41 +00:00
Niklas Haas
b2266d6656 swscale/graph: correctly adjust field height for interlaced frames
This is a pre-existing bug from 67f3627267.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-03-01 21:57:53 +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
4d7b1c3685 swscale/graph: move frame->field init logic to SwsGraph
And have ff_sws_graph_run() just take a bare AVFrame. This will help with
an upcoming change, aside from being a bit friendlier towards API users
in general.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas
846823b174 swscale/graph: don't pointlessly align data buffers
Since d67d81a374, enabling asm explicitly requires aligned malloc,
so this FFALIGN accomplishes nothing.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas
5121665463 swscale/graph: use AVFrame to track internal allocation
This commit replaces the AVBufferRef inside SwsPassBuffer by an AVFrame, in
anticipation of the SwsImg removal.

Incidentally, we could also now just use av_frame_get_buffer() here, but
at the cost of breaking the 1:1 relationship between planes and buffers,
which is required for per-plane refcopies.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas
e9d1ed3fdc swscale/graph: avoid stack copies of SwsImg
In the process of nuking this abstraction in favor of AVFrame.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas
d89765eb28 swscale/graph: simplify output buffer management
This function was originally written to support the use case of e.g.
partially allocated planes that implicitly reference the original input
image, but I've decided that this is stupid and doesn't currently work
anyways.

Plus, I have plans to kill SwsImg, so we need to simplify this mess.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-27 16:18:34 +00:00
Niklas Haas
841ca7a2cb swscale/format: pass SwsFormat by ref instead of by value where possible
The one exception is in adapt_colors(), which mutates these structs on
its own stack anyways.
2026-02-26 18:08:49 +00:00
Lynne
362414afba swscale: add support for processing hardware frames
Sponsored-by: Sovereign Tech Fund
2026-02-26 14:10:22 +01:00
Lynne
ad452205b6 swscale/ops: add SwsOpBackend.hw_format
Allows to filter hardware formats.

Sponsored-by: Sovereign Tech Fund
2026-02-26 14:10:22 +01:00
Lynne
c911295f09 swscale: forward original frame pointers to ops.c backend
Sponsored-by: Sovereign Tech Fund
2026-02-26 14:10:21 +01:00
Ramiro Polla
c7c8c31302 swscale/tests/sws_ops: print range values in the output
This gives more information about each operation and helps catch issues
earlier on.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
2026-02-24 19:27:51 +01:00
Niklas Haas
d918551650 swscale/graph: switch SwsPass.output to refstruct
Allows multiple passes to share a single output buffer reference. We always
allocate an output buffer so that subpasses can share the same output buffer
reference while still allowing that reference to implicitly point to the
final output image.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
cc346b232d swscale/graph: store current pass input instead of global args
The global args to ff_sws_graph_run() really shouldn't matter inside thread
workers. If they ever do, it indicates a leaky abstraction. The only reason
it was needed in the first place was because of the way the input/output
buffers implicitly defaulted to the global args.

However, we can solve this much more elegantly by just calculating it in
ff_sws_graph_run() directly and storing the computed SwsImg inside the
execution state.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
1e071c8585 swscale/graph: omit memcpy() if src and dst are identical
This allows already referenced planes to be skipped, in the case of e.g.
only some of the output planes being sucessfully referenced. Also avoids
what is technically UB, if the user happens to call ff_sws_graph_run() after
already having ref'd an image.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
b98751b13c swscale/graph: set up palette using current input image
Using the original input image here is completely wrong - the format/palette
could have been set to anything else in the meantime. At best, we would want to
use the original input to add_legacy_sws_pass(), but it's impossible for this
to differ from the per-pass input. The only time legacy subpasses are added
is when using cascaded contexts, but in this case, the only context actually
reading from the palette format would be the first one.

I'm not entirely sure why this code was originally written this way, but
I'm reasonably confident that it's not at all necessary. Tested extensively
on both FATE, the self-test, and real-world files.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
0b446cdccd swscale/graph: switch to an AVBufferRef per plane
This annoyingly requires recreating some of the logic inside av_img_alloc(),
because there's no good existing current helper accessible from libswscale
that gives per-plane allocations like this.

The new code is based off the calculations inside libavframe/bufferpool.c.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
afa08f4971 swscale/graph: duplicate buffer dimensions in SwsPassBuffer
When multiple passes share a buffer reference, the true buffer dimensions
may be different for each pass, depending on slice alignment. So we can't
rely on the pass dimensions being representative.

Instead, store this information in the SwsPassBuffer itself.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
fe25e54d0f swscale/graph: move output image into separate struct
I want to add more metadata to this and also turn it into a refstruct,
but get the cosmetic diff out of the way first.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
18060a8820 swscale/graph: simplify ff_sws_graph_run() API
There's little reason not to directly take an SwsImg here; it's already an
internally visible struct.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
e1fd274706 swscale/graph: check output plane pointer instead of pixel format
To see if the output buffers are allocated or not.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-23 19:39:17 +00:00
Niklas Haas
e96332cb65 swscale/ops: add ff_sws_op_list_is_noop()
And use it in ff_sws_compile_pass() instead of hard-coding the check there.
This check will become more sophisticated in the following commits.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-02-19 19:44:46 +00:00
Niklas Haas
d5174f9e5b swscale/format: add source format info to ff_sws_encode_colors()
Specifically, I need access to this for generating a better dither matrix.
2025-12-15 14:31:58 +00:00
Arpad Panyik
ef651b84ce swscale: Refactor XYZ+RGB state and add function hooks
Prepare for xyz12Torgb48 architecture-specific optimizations in
subsequent patches by:
 - Grouping XYZ+RGB gamma LUTs and 3x3 matrices into SwsColorXform
   (ctx->xyz2rgb and ctx->rgb2xyz), replacing scattered fields.
 - Dropping the unused last matrix column giving the same or smaller
   SwsInternal size.
 - Renaming ff_xyz12Torgb48 and ff_rgb48Toxyz12 and routing calls via
   the new per-context function pointer (ctx->xyz12Torgb48 and
   ctx->rgb48Toxyz12) in graph.c and swscale.c.
 - Adding ff_sws_init_xyzdsp and invoking it in swscale init paths
   (normal and unscaled).
 - Making fill_xyztables public to ease its setup later in checkasm.

These modifications do not introduce any functional changes.

Signed-off-by: Arpad Panyik <Arpad.Panyik@arm.com>
2025-12-05 10:28:18 +00:00
Niklas Haas
4ec2bffe62 configure: allow disabling experimental swscale code
In theory we can also expand this to disable e.g. experimental codecs.
2025-09-01 19:28:36 +02:00
Niklas Haas
cc42bc1f4b swscale/graph: allow experimental use of new format handler
The humor originally contained in this commit message has been
redacted to comply with the strict FFmpeg code quality standards.
2025-09-01 19:28:36 +02:00
Niklas Haas
c18676aae4 swscale/graph: pass per-pass image pointers to setup()
This behavior had no real justification and was just incredibly confusing,
since the in/out pointers passet to setup() did not match those passed to
run(), all for what is arguably an exception anyways (the palette setup).
2025-09-01 19:27:53 +02:00