The spec allows single color channels to decode to NaN in
void-extent blocks. Prior to this fix the 3.x compressor
would force the entire texel to NaN if component 0 was NaN.
Replace conversions between storage types (unorm16, LNS16),
decode types(float16), and codec types (float), with vectorized
versions of the same algorithms.
This PR implements the ability to convert floats stored in vfloat4 vectors to fp16 bit patterns stored in vint4 vectors. Scalar versions are also provided as overloaded functions of the same name, for sake of convenience.
The code has been refactored to take advantage of the vectorization opportunities this gives, in particular using NEON (Arm) and F16C (x86-64) ISA support for float<>fp16 conversion. The F16C support is tied to the AVX2 enable config option, it is not a separate enable. In cases where the soft-float code is not needed, code size reduces by ~5KB.
Prior to this PR we had two vec4 implementations for floats, the legacy "float4" and the newer "vfloat4". The newer vfloat4 is a superset of the capabilities of the float4, and using it everywhere would give other opportunities to optimize by e.g. using the conditional select functionality rather than scalarizing chunks of vector code.
This patch is just the basic migration - where possible I've translated the original code without additional refactoring. Further optimizations to make better use of the vfloat4 functionality will come later. Overall this patch has no impact on IQ or performance, but does give a slight reduction in code size.
Replace manual min/max/clamp code with template functions. It's more literate as a style, makes it easier to spot vectorizability opportunities, and actually improves code size slightly.
This implementation alters the algorithm and vectorizes these two functions.
The algorithm change will have a minor image quality impact; it now uses a faster approximation for selecting which component to assign to a second plane when choosing a partition. There is an insignificant IQ hit on -medium (under < 0.01dB), as we rarely use two planes anyway, and a slightly larger one on -thorough (under < -0.05dB). The significant (>10%) performance gain on -thorough is worth the quality loss (i.e. users are more likely to use -thorough if it's faster, and the overall IQ gain vs medium is still net positive).
This implementation changes the core codec API for images so they they are no longer 3D arrays (which require a pointer tree to get allocated and created). Instead images are passed as an array of 2D image slices, building up 3D images in layers. Each slice consists of tightly packed 4 channel data with no padding between rows.
The common case usage of ASTC is 2D images, so in the common case the slice array consists of just a single slice, while leaving support for 3D as an option in the API when it's needed. In addition passing raw data slices loaded from other libraries is now possible, as there is no longer need for the pointer preamble. This can remove additional memory allocation and copy overheads
Slight performance improvement for -fastest is expected - but will be < 1%. The main improvement here is just improved ease of integration with data from other sources, and the ability to avoid two memory allocations for the same data.
Note that this change is ONLY the API change; the command line wrapper is still creating copies of the data from stb_image and tiny_exr, which isn't really needed. This will get fixed in a later PR.
The current code makes a copy of the original data (imageblock.orig_data), transferring into working buffers (imageblock.data_[rgba]) for each encoding trial. The working buffer is overwritten by each trial, so we have to repopulate the working buffer after each trial which is a significant (10-15%) overhead for the fast searches. In reality the output of the decompression is never really used - we only need to compute RMS error for the trial (which can be done inline) and throw the output away.
This patch removes the orig_data copy of the data, and uses only the data_[rgba] fields. Data encoding transforms at the start/end of the block are done in-place, and the data is no longer overwritten during each trial. Some parts of the code needed to be adapted to the fact that the "working" encoding of the data is not the original encoding.
PSNR is identical for LDR images, but has minor changes for HDR images. This is because we now compute derivatives on a converted form of the modified data, so we have an additional conversion round-trip which can add some rounding. On our test set this actually slightly improves PSNR, but I suspect this is just luck.
Code to use this for HDR images is also implemented for the CLI
wrapper, but currently the HDR functionality in the wrapper is
still routed though FP16. Local testing indicates that this works
as expected.