The old tail paths backed the source and destination pointers up by a
full vector and then advanced them by the remainder. When no full vector
had been processed, this placed dest before its allocation, followed by
a full-vector store. Process tails forward with LSX and byte stores and
use equivalent packed arithmetic so exactly dstW bytes are written.
Fixes: #23558
Reported-by: sebastinas
(cherry picked from commit 8b4fad11ac)
Odd-size luma planes are not exact multiples of the chroma plane; but the
sample grid is still matched as though it were. We need to account for this
when translating a luma sample to the corresponding chroma sample coordinates.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
(cherry-picked from commit fa1ca69a8b)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
This is needed for chroma subsampling, which requires a different filter
offset for chroma subsamples (according to the frame's chroma location).
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
(cherry-picked from commit 8215e9bbea)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
This is a complete rewrite of the math in swscale/utils.c initFilter(), using
floating point math and with a bit more polished UI and internals. I have
also included a substantial number of improvements, including a method to
numerically compute the true filter support size from the parameters, and a
more robust logic for the edge conditions. The upshot of these changes is
that the filter weight computation is now much simpler and faster, and with
fewer edge cases.
I copy/pasted the actual underlying kernel functions from libplacebo, so this
math is already quite battle-tested. I made some adjustments to the defaults
to align with the existing defaults in libswscale, for backwards compatibility.
Note that this commit introduces a lot more filter kernels than what we
actually expose; but they are cheap to carry around, don't take up binary
space, and will probably save some poor soul from incorrectly reimplementing
them in the future. Plus, I have plans to expand the list of functions down
the line, so it makes sense to just define them all, even if we don't
necessarily use them yet.
Backported-from: 475b11b2e0
Since this depended on changes to the public swscale API, I have instead
added the necessary enums/defines to filters.h itself for now.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
uyvytoyuv422 converts packed UYVY, whose macroblocks are pixel pairs, and
the SIMD code only handled even widths. On an odd width the trailing half
macroblock made the kernel write past the end of the Y/U/V destinations:
the AVX512ICL masked tail dropped the odd pixel and the fall-through
re-entered the SIMD loop, writing a full mmsize*2 chunk past the planes
(127 bytes of Y, 63 of U and 63 of V); the sse2/avx/avx2 scalar tail
wrote one byte past the Y plane.
Process only whole pairs and emit the trailing odd column from a small
per-row epilogue that matches uyvytoyuv422_c (ydst[w-1] = src[2w-1],
udst[cw-1] = src[2w-2], vdst[cw-1] = src[2w]).
All four SIMD variants are now bit-exact with the C reference for even and
odd widths and no longer overwrite the destination. Verified on AVX512ICL
hardware (Ryzen 9 9950X) with checkasm.
Found-by: Claude (Anthropic). Human-verified and reported by Omkhar Arasaratnam <omkhar@linkedin.com>.
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit 21782b7b31)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
interleaved_yuv_to_planar, shared by uyvytoyuv422, uyvytoyuv420,
yuyvtoyuv422 and yuyvtoyuv420, only handled even widths. The packed
UYVY/YUYV macroblocks are pixel pairs and the trailing half macroblock of
an odd width was mishandled:
- the slow path (width <= 31) decrements its pixel counter by two from an
odd value, so it never reaches zero and the loop runs far past the line,
overwriting the destination (observed as a crash in checkasm);
- the fast path (width >= 32) shifts the tail pointers back by width-32 and
reprocesses an overlapping, misaligned tuple, producing wrong samples and
dropping the last chroma column.
Process only whole pixel pairs and emit the trailing odd column from a
per-line epilogue that matches the C reference: for yuv422 one Y, U and V
sample; for yuv420 the Y of both lines of the pair with the chroma averaged
across them, and luma only for the final line when the height is odd. The
empty even part (width 0 or 1) is guarded so the slow path no longer enters
its run-past loop.
All four variants are now bit-exact with the C reference for even and odd
widths. Verified with checkasm under qemu-aarch64.
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit a554d0aa8a)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Fix two buffer overreads in the PowerPC yuv2planeX SIMD paths
that cause daily FATE checkasm-sw_scale ASAN failures on both
ppc64 (G5, altivec) and ppc64le (POWER9, VSX):
1. VSX LOAD_FILTER: vec_vsx_ld(joffset, filter) reads 16 bytes
at the given byte offset. When joffset >= filterSize*2 - 14
(e.g. joffset=30 for filterSize=16), this reads up to 14 bytes
past the 32-byte filter array. Fix by replacing the vector
load with vec_splats(f[j]) which only reads the single int16_t
element needed (the result is splatted to all lanes anyway).
2. GET_LS look-ahead overread: yuv2planeX_8_16 calls
yuv2planeX_8 twice per filter tap. Each call's GET_LS macro
speculatively loads the next 16-byte vector for pipelining.
On the second call, this look-ahead reads 16 bytes past the
last valid source element. Fix by tightening the SIMD loop
bound from (dstW - 15) to (dstW - 23), ensuring the farthest
speculative load stays within src[j][0..dstW-1]. The scalar
fallback handles the remaining 16-23 trailing pixels.
The ASAN reports from FATE:
ppc64 (altivec): stack-buffer-overflow in yuv2planeX_8_16_altivec
at swscale_ppc_template.c:56
ppc64le (VSX): unknown-crash in yuv2planeX_8_16_vsx
at swscale_ppc_template.c:52
Signed-off-by: Scott Boudreaux <scott@elyanlabs.com>
(cherry picked from commit d4673a97ac)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Part of the yuv2planeX ASAN fix - replace vec_vsx_ld with vec_splats
to avoid reading past the filter array.
Signed-off-by: Scott Boudreaux <scott@elyanlabs.com>
(cherry picked from commit dddc703cc1)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Previously scale_cascaded() assumed the whole source frame arrived in a
single sws_scale() call, and the dispatcher only routed full-frame calls
to it. A partial input slice fell through to ff_swscale() on the parent
dispatcher context, whose scaler state (c->desc) is never initialized in
cascade mode, causing a NULL dereference / crash.
Top-down sliced output is bit-exact with full-frame scaling; bottom-up
matches swscale's pre-existing (non-cascade) slice behaviour for
subsampled intermediate formats.
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit 10f2abc41f)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Fixes: out of array access
Fixes: DFVULN-694
*Reporter: Zhenpeng (Leo) Lin at depthfirst*
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit 43a0715e30)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Obviously noone should pass negative values, they make no sense, but better to
explicitly check
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit 7d0837a742)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Fixes: signed integer overflow: -1548257 * 2048 cannot be represented in type 'int'
Fixes: #21592
Found-by: HAORAN FANG
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Fixes: signed integer overflow: -2147483648 - 65536 cannot be represented in type 'int'
Fixes: #21588
Found-by: HAORAN FANG
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Fixes: integer overflow (does not replicate, but looks like it should overflow with some craftet parameters)
Fixes: #21584
Found-by: HAORAN FANG
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
When luma init switched to cascade the chroma init was skiped
Fixes: NULL pointer dereference
Fixes: #21583
Found-by: HAORAN FANG
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
The legacy API is defined by sws_init_context(), sws_scale() etc., whereas
the "modern" API is defined by just using sws_scale_frame() without prior
init call.
This int allows us to cleanly distinguish the type of context, paving the
way for some minor refactoring.
As an immediate benefit, we now gain a bunch of explict error checks to
ensure the API is used correctly (i.e. sws_scale() not called before
sws_init_context()).
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
This allows distinguishing between different types of failure, e.g.
AVERROR(EINVAL) on invalid pass dimensions.
Signed-off-by: Niklas Haas <git@haasn.dev>
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>
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>
This is a bit more forward-facing than a bare allocation, and importantly,
allows the `swscale/utils.c` code to remain agnostic about how to correctly
uninit this struct.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
By excluding the Vulkan makefile entirely when --disable-unstable is passed.
This also correctly avoids compiling e.g. unused GLSL compilers.
Fixes: #22295
See-Also: #22366
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
Add NEON alpha drop/insert using ldp+tbl+stp instead of ld4/st3 and
ld3/st4 structure operations. Both use a 2-register sliding-window
tbl with post-indexed addressing. Instruction scheduling targets
narrow in-order cores (A55) while remaining neutral on wide OoO.
Scalar tails use coalesced loads/stores (ldr+strh+lsr+strb for alpha
drop, ldrh+ldrb+orr+str for alpha insert) to reduce per-pixel
instruction count. Independent instructions placed between loads and
dependent operations to fill load-use latency on in-order cores.
checkasm --bench on Apple M3 Max (decicycles, 1920px):
rgb32tobgr24_c: 114.4 ( 1.00x)
rgb32tobgr24_neon: 64.3 ( 1.78x)
rgb24tobgr32_c: 128.9 ( 1.00x)
rgb24tobgr32_neon: 80.9 ( 1.59x)
C baseline is clang auto-vectorized; speedup is over compiler NEON.
Signed-off-by: David Christle <dev@christle.is>
Add a NEON rgb24tobgr24 using ld3/st3 to swap R and B channels in
packed 24bpp RGB buffers. Handles all input sizes with a 16-pixel
NEON fast path, 8-pixel NEON cleanup, and scalar tail.
checkasm --bench on Apple M3 Max (1920*3 = 5760 bytes):
rgb24tobgr24_c: 722.0 ( 1.00x)
rgb24tobgr24_neon: 94.9 ( 7.61x)
Signed-off-by: David Christle <dev@christle.is>
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>
Add ARM64 NEON-accelerated unscaled YUV-to-RGB conversion for planar
YUV input formats. This extends the existing NV12/NV21 NEON paths with
YUV420P, YUV422P, and YUVA420P support for all packed RGB output
formats (ARGB, RGBA, ABGR, BGRA, RGB24, BGR24) and planar GBRP.
Register with ff_yuv2rgb_init_aarch64() to also cover the scaled path.
checkasm: all 42 sw_yuv2rgb tests pass.
Speedup vs C at 1920px width (Apple M3 Max, avg of 20 runs):
yuv420p->rgb24: 4.3x yuv420p->argb: 3.1x
yuv422p->rgb24: 5.5x yuv422p->argb: 4.1x
yuva420p->argb: 3.5x yuva420p->rgba: 3.5x
Signed-off-by: David Christle <dev@christle.is>
The res variable (pixel residual count for widths not divisible by 16)
is computed once before the row loop, but DEALYUV2RGBLINERES and
DEALYUV2RGBLINERES32 destructively subtract 8 from it inside the loop
body. When srcSliceH > 2, subsequent row pairs get an incorrect
residual count, producing wrong output for the tail pixels.
Fix by recomputing res from the constant c->opts.dst_w at the top of
each row-pair iteration.
Signed-off-by: David Christle <dev@christle.is>
yuy2toyv12, vu9_to_vu12 and yvu9_to_yuy2 are unused. Removing
them saved 7808B of .text and 102B of .text.unlikely
as well as 24B of .bss here.
Thanks to James Almer for pointing out that yuy2toyv12 is unused.
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>