If the pass we are allocating output buffers for has a plane copy map,
we can optimize away the internal buffer by setting it to a ref of its
input buffer, if one exists.
In theory, we can also do this if there is not an input buffer, by
directly referencing a sentinel or placeholder SwsPassBuffer
corresponding to the original input image, but this will require a bit
more work so I have decided to hold off on it for now.
Signed-off-by: Niklas Haas <git@haasn.dev>
The current code only checks to see if the entire operation is a no-op,
but doesn't allow removing unneeded components from the operation.
Results in a large number of minor improvements, e.g.
rgba 16x16 -> ya8 16x16:
u8_read_packed_xyzw
u8_to_f32_xyzw
f32_linear_x_xxx00
f32_dither_x_0_16x16
- f32_min_xw
+ f32_min_x
f32_to_u8_xw
u8_permute_xyz_y_w
u8_write_packed_xy
or:
rgba 16x16 -> yuva444p10le 16x16:
u8_read_packed_xyzw
u8_to_f32_xyzw
f32_linear_xyzw_xxx0x_xxx0x_xxx0x_000x0
f32_dither_xyzw_0_3_2_5_16x16
- f32_min_xyzw
+ f32_min_w
f32_to_u16_xyzw
u16_write_planar_xyzw
And some major ones, e.g.
yuva444p 16x16 -> gbrap 16x16:
- u8_read_planar_xyzw
- u8_to_f32_xyzw
+ u8_read_planar_x
+ u8_write_planar_x
+ Sub-pass #1:
+ u8_read_planar_xyz
+ u8_to_f32_xyz
f32_linear_xyz_x0x0x_xxx0x_xx00x
f32_dither_xyz_0_3_2_16x16
- f32_max_xyzw
- f32_min_xyzw
- f32_to_u8_xyzw
- u8_write_planar_xyzw
+ f32_max_xyz
+ f32_min_xyz
+ f32_to_u8_xyz
+ u8_write_planar_xyz
Which is now split between two subpasses, one for the no-op alpha
copy and one for the yuv444 -> gbrp conversion. This has been
previously blocked by the SWS_OP_MIN/MAX clamp on the RGB channels
marking the alpha channel as dirty, even though it should be a no-op
on the alpha channel.
Signed-off-by: Niklas Haas <git@haasn.dev>
This is more true to the name; these dependencies track *components*,
not planes - in both directions.
Generates a bunch of benign diffs.
Signed-off-by: Niklas Haas <git@haasn.dev>
This allows already referenced planes to be skipped (and avoids UB in
this case).
Note that this is already safe for the other backends, because reading
from and writing to the same pointer via e.g. AVX operations is also
effectively a no-op.
See-Also: 1e071c8585
Signed-off-by: Niklas Haas <git@haasn.dev>
This can be set opportunistically by passes which are effectively
memcopies. Information about end-to-end plane copies will also propagate
upwards to the SwsGraph, and can be used there by the caller.
The reason we need to solve it this way is deeply tied to the way
SwsGraph is designed; specifically the fact that it can be used with
arbitrary frames and also lives per-field (rather than per-frame),
meaning that we can't just directly mutate the output frame somehow from
the run() call to ref the output planes.
Signed-off-by: Niklas Haas <git@haasn.dev>
This basically guarantees that optimized packed routines have enough
space to write a few bytes past the end of the line.
Signed-off-by: Niklas Haas <git@haasn.dev>
The final edge clamp computes the source position using int
multiplication before shifting. With sufficiently wide inputs this
overflows, which may suppress the clamp and leave the last output
pixels interpolated with the padding byte.
Promote the multiplication to int64_t in the C, MMXEXT and VSX
implementations.
Add a regression test covering the rightmost pixel of a wide upscale,
which is wrong before this change on both the C and the MMXEXT path.
Fixes: signed integer overflow: 15 * 255918080 cannot be represented in type 'int'
Fixes: #21591
Signed-off-by: iSold Leo <s@qwqlog.com>
These may differ from the regular op lists in nontrivial ways, due to e.g.
different optimization steps being taken.
In practice, it seems this just adds the extra LUT_3D uops, but we don't
know that for sure, so better to brute force the list. That said, I do
think we can safely skip the extra backend flags in this case, at least.
Even in the worst case scenario, that would just force a fallback to the
C reference backend.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
The value range normalization and input range clamp will normally be
optimized away.
This commit also moves the legacy 3dlut pass to be legacy-exclusive, as the
ops code now uses the new logic. This ordering ensures that the conversion
works on every commit in isolation.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
Instead of generating the 3DLUT and applying it right away, init_passes()
now just generates it and passes it as a parameter to add_convert_pass(),
which will forward it to the underlying implementation.
I pre-emptively moved the function to the legacy section, in anticipation
of the following commit which will make this legacy-only.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
Since this depends on the overall picture parameters anyways, updating it
directly from the private setup() function was always a bit hacky. More
importantly, this needed for the ops-based 3DLUT implementation, which is
already using the pass priv pointer to store the compiled function.
Overall, simpler to just lift it to the top level and update it directly as
needed.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
The structure for the tetrahedral interpolation deviates slightly from the
naive formulation in lut3d.c; instead of branching into every separate case,
we sort the weights and offsets using a series of conditional swaps. This
actually performs identically on my end, but results in code that is much
closer to what SIMD will be doing. That should hopefully serve as a better
reference for future SIMD implementors. (Myself included)
I also reordered the dynamic tone-mapping code a bit to better indicate the
sources of live register pressure that will manifest in the real SIMD kernel.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
After extensive testing, prototyping and benchmarking across a range of
systems, I determined that the optimal data layout for the SIMD 3DLUT is
essentially exactly the one we have. 16-bit integers are near optimal for
quality vs compactness, and crucially, x86 lets us load the entire packed
3DLUT entry with a single `vpgatherdq` instruction.
This dwarfs the loss from needing to cast the resulting 16-bit integers
back to f32 and renormalize.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
av_round16f() was incorrectly scaling to 65534, which would correspond to
a neutral PT offset of 32767 = (1 << 15) - 1, but the code was assuming a
value of (1 << 15). To fix it, and make the PT channel correctly symmetric
around the intended neutral value, we have to map PT = +0.5 to 65536, an
unrepresentable value. This is not an issue because the PT channel values are
strictly inside some subset of [-0.5, 0.5] in practice, for real in-gamut
color values - it's already an envelope that includes quite a bit of safety
margin.
Similarly, av_round16f() is also the wrong tool for the I/RGB channels,
because it incorrectly scaled those to 65534, an off by one of the intended
unorm16 full range peak of 65535. This silently resulted in e.g. RGBA64
true white (65535) not round-tripping through the 3DLUT.
Fix both by splitting this helper into two separate helpers, each of them
fixed to the correct, intended value range.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
The ops-based 3DLUT approach will take care of appropriately normalizing
the input to the expected domain; so the format choice no longer matters here
except for the lut3d_apply() function, which will only be used by the legacy
reference code path. So we can just continue hard-coding the format there.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
Instead of clamping in the application function. Trivial simplicity gain
for the SIMD code.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
This was originally introduced to make matching linear ops against
implementations faster. However, since this is now handled on the uops
level, there is no more reason to carry this metadata on the ops level.
Simplifies a lot of places in the code. It will simplify even more, once
the linear optimizations are moved to the uops level.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
Introduce a unified FORWARD() helper macro that can be used for any type of
op, whether it is independent per component or more complex. By initializing
every op to the same IDENTITY state, we can leverage the monoid property to
make this work for naive propagations as well.
As an aside, we also properly zero out the unrelated fields when discarding
a component (i.e. marking it as GARBAGE).
This will make a couple of up-coming refactors a bit easier.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
This violates the documentation (monoid property).
It's a bit arbitrary whether to consider this an OR-type or AND-type flag,
since mixing swapped and non-swapped components is almost surely a bug, but
keeping it as an OR-type makes sure such cases at least show up in the
result.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
ops_asmgen.c contains common aarch64 asm generation code that will be
used by CPS backend and the JIT backend.
The standalone tool with CPS-specific code is now ops_static.c.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This will serve multiple purposes:
- Allow JIT code to populate operands from a deduplicated data pool;
- Simplify the future implementation of integer linear operation, since
mul/mla take full vector registers as coefficients, and not elements.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
Use vtmp register index relative to the current matrix column instead
of reusing the position of the value from the packed data.
The i_coeff and derived values will be removed in the following commit.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
Refer to offset[i] or coeff[i][j] instead of position of the value from
the packed data.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
The save_mask setup makes copies of input data that would be clobbered
by the linear operation itself. This setup is not entirely necessary
with JIT, since we may have separate input and output vector registers,
so the code is moved into the setup function which will be CPS-only.
This doubles the amount of temp registers needed, since they must all
be copied before both loops.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This change is done separately from the next commit (which uses the new
temp vectors in the linear op) to make it easier to track changes to
ops_neon.gen.S.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
For CPS these will continue being the same vector register numbers,
because of the fixed ABI, but for JIT we will be able to use different
input/output vector registers to implicitly perform swizzles.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This changes nothing for the CPS code, but will allow the JIT compiler
to allocate and reorder vector registers without them being affected by
reshaping.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
Currently this is redundant, since all CPS functions share the same
frame.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This changes little for the current CPS code, which has fairly simple
setup code. But in JIT we will factor out much more code from the main
loop into the setup section.
This also frees up the register used by exec (x0) earlier on, so it may
be reused earlier in JIT code.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
Currently, for CPS code, both masks are the same, similar to how the C
and x86 backends do it.
But the JIT compiler will have more precise input and output masks.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
The asmgen_process() function is now more generic, which will allow us
to use it from the JIT compiler.
The naming of the function, along with the setup code and main loop,
are moved to asmgen_process_cps().
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This paves the way for performing operation setup differently for the
JIT backend, which will have constant values factored out of the loop.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This will help the JIT compiler by letting us provide a separate set of
registers for each operation.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This reduces some unnecessary vector reshaping, and also makes the
intention clearer at the call-sites.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This makes each vector register's purpose clearer, and will let the JIT
compiler later factor loading of constant values out of the main loop.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This reduces the line length for instructions that access impl->priv.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
There is no need to pack the vector register usage for this function.
This change creates holes in the usage of const vector registers, but
populates the RasmOp const array directly, which will help for the
implementation of the JIT compiler.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This way, we don't depend on the RasmOp having been correctly reshaped
prior to being used.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>
This way, we don't depend on the RasmOp having been correctly reshaped
prior to being used.
Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <ramiro.polla@gmail.com>