Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace decoders with libvips, add tests and rename package #1

Open
wants to merge 30 commits into
base: main
Choose a base branch
from

Conversation

Rodrigodd
Copy link

This PR integrates libvips to get a better algorithm for downsampling images (mangas frequently use half-tone shading, which is very susceptible to aliasing when downsampling). As a bonus, it will allow moving all image format implementations upstream, decreasing the maintenance burden. Libvips also aims to efficiently partially decoding images, possibly decreasing CPU and memory usage (but this still need be measure).

Early discussions: Rodrigodd#1, tachiyomiorg#15

From my tests, the downsampling filter didn't help much. subsampling-scale-image-view will still draw tiles up to 2x the screen resolution, and halftone patterns in manga are not small enough to be filtered out.

This PR is still missing:

@AntsyLich
Copy link
Member

About the benchmarks (that was in the TODO of Rodrigodd#1) can you also include one for the old decoder. someone was interested in comparison.

@AntsyLich
Copy link
Member

Also change the library/src/(androidTest|main)/java folder to library/src/(androidTest|main)/kotlin

@AntsyLich
Copy link
Member

AntsyLich commented Dec 1, 2024

Also we don't want to allow every arbitrary format so there should be a whitelist (refer to the ImageType.Format enum)

@AntsyLich
Copy link
Member

And for tests you can use https://github.com/ReactiveCircus/android-emulator-runner (there is a lit of production usage at the bottom)

@Rodrigodd
Copy link
Author

I added a benchmark module, but I could not retrieve the profile information. The gradlew task errors out in a adb command timeout, and I could not find the profile files in my phone. The benchmark still need to sample regions in a more suitable distribution, test more image formats, etc. And then rebase it to before making all changes, to compare the perfomance.

I also cherry-pick @wwww-wwww fix for the color profile. But loading the test images he provide me on Mihon makes it crash. I still could not reproduce it in a unit-test and neither Debug Mihon directly. But I don't know if his changes is causing it, I only test the images after the changes.

I don't have much time throughout the week, but I will continue to slowly work on those.

Also only run assembleDebug for now, full assemble is too slow.
Also rely on the existing build tool of each external project, instead
of patching CMake into them.

Most `ExternalProject_Add` wrappers were copied from Komelia, at
[https://github.com/Snd-R/Komelia/tree/8b938397e5c8743d4e995d2cc16203367de6f67d/image-decoder/native/cmake],
but modified to make sure all libraries are staticaly linked.
Took me a while to figure out how the API work
Was trying to optimize by cropping the image to the desired region, and
then resizing only that, but libvips seems to already be smart enough,
so that is not necessary.
The intermediate buffer is not necessary, the `decode` function will
already take care of outputing in the correct format.
Was looping to detect memory corruption. Not needed anymore.
And groupId/artifactId as `dev.mihon:image-decoder`
Rodrigodd and others added 7 commits December 7, 2024 20:27
Only `bounds` were being used, so I only keeped that.
This folder is genrated by clangd
Mihon was crashing somethimes crashing when decoding the first image. I
notice Mihon was decoding two images at the same time, which made me
think it could be a threading issue. Reading libvips documentation, it
states that it is safe, but VIPS_INIT must only be called in a
single-thread. I was calling VIPS_INIT for every decoder created, which
I believe was the issue.

This commit moves VIPS_INIT to JNI_OnLoad, which is called only once
when the library is loaded.
The emulator is failing to create due to lack of space. Maybe the
previous build is using too much? I removed the build step, but it will
still be built after the emulator is created, let's see if it will still
break.
This will just check if the benchmark can run without errors.
I am not very confident that building with the cache will work, even
more if we modify the CMakeLists.txt file between builds. But waiting
>20min per build is very painful (and there are both Debug and Release
builds).
@Rodrigodd
Copy link
Author

Got the tests to run on CI, and I got the benchmarks to work in both the old and new implementation. I could not get a reliable benchmark on my device, but it roughly appears that both implementations have the same performance:

OLD:

Name        | Median    | Min       | Max       | Stddev    | Alloc
decodeAvif  | 10.47 ms  | 5.54 ms   | 13.62 ms  | 2.13 ms   |  10
decodeAvif  | 10.00 ms  | 6.27 ms   | 15.39 ms  | 2.07 ms   |  10
decodeAvif  | 10.48 ms  | 6.65 ms   | 14.90 ms  | 2.32 ms   |  10

decodeHeif  | 10.28 ms  | 5.26 ms   | 13.82 ms  | 2.20 ms   |  10
decodeHeif  | 10.26 ms  | 6.77 ms   | 15.40 ms  | 2.16 ms   |  10
decodeHeif  | 10.71 ms  | 7.54 ms   | 16.04 ms  | 1.57 ms   |  10

decodeWebp  | 15.60 ms  | 10.76 ms  | 23.18 ms  | 3.01 ms   |  10
decodeWebp  | 15.42 ms  | 10.84 ms  | 22.30 ms  | 3.20 ms   |  10
decodeWebp  | 16.01 ms  | 10.42 ms  | 23.02 ms  | 2.97 ms   |  10

decodeJpg   | 15.26 ms  | 10.25 ms  | 21.74 ms  | 2.59 ms   |  10
decodeJpg   | 14.77 ms  | 10.32 ms  | 22.51 ms  | 2.70 ms   |  10
decodeJpg   | 15.81 ms  | 10.58 ms  | 23.93 ms  | 3.08 ms   |  10

decodeJxl   | 14.76 ms  | 10.20 ms  | 21.78 ms  | 2.10 ms   |  10
decodeJxl   | 15.72 ms  | 10.34 ms  | 22.54 ms  | 3.00 ms   |  10
decodeJxl   | 15.54 ms  | 10.67 ms  | 22.72 ms  | 3.30 ms   |  10

decodePng   | 15.12 ms  | 11.53 ms  | 20.77 ms  | 2.01 ms   |  10
decodePng   | 15.67 ms  | 10.41 ms  | 21.93 ms  | 3.04 ms   |  10
decodePng   | 14.97 ms  | 10.81 ms  | 22.23 ms  | 2.58 ms   |  10

VIPS:

Name        | Median    | Min       | Max       | Stddev    | Alloc
decodeAvif  | 10.01 ms  | 5.50 ms   | 14.37 ms  | 2.22 ms   |  10
decodeAvif  | 10.82 ms  | 6.67 ms   | 17.02 ms  | 1.95 ms   |  10
decodeAvif  | 10.45 ms  | 6.97 ms   | 14.97 ms  | 1.94 ms   |  10

decodeHeif  | 10.63 ms  | 8.01 ms   | 14.63 ms  | 1.58 ms   |  10
decodeHeif  | 10.54 ms  | 6.94 ms   | 14.55 ms  | 1.86 ms   |  10
decodeHeif  | 10.91 ms  | 7.16 ms   | 16.45 ms  | 1.97 ms   |  10

decodeWebp  | 15.16 ms  | 11.34 ms  | 20.35 ms  | 1.81 ms   |  10
decodeWebp  | 15.79 ms  | 10.42 ms  | 22.92 ms  | 2.97 ms   |  10
decodeWebp  | 15.62 ms  | 11.18 ms  | 23.88 ms  | 3.34 ms   |  10

decodeJpg   | 15.50 ms  | 10.34 ms  | 21.96 ms  | 2.99 ms   |  10
decodeJpg   | 15.84 ms  | 10.73 ms  | 21.13 ms  | 2.54 ms   |  10
decodeJpg   | 15.24 ms  | 11.35 ms  | 21.44 ms  | 1.79 ms   |  10

decodeJxl   | 14.67 ms  | 10.58 ms  | 21.21 ms  | 2.51 ms   |  10
decodeJxl   | 14.68 ms  | 10.57 ms  | 21.34 ms  | 2.60 ms   |  10
decodeJxl   | 15.66 ms  | 10.32 ms  | 22.69 ms  | 3.08 ms   |  10

decodePng   | 15.55 ms  | 10.70 ms  | 21.56 ms  | 2.62 ms   |  10
decodePng   | 15.10 ms  | 11.43 ms  | 21.22 ms  | 1.72 ms   |  10
decodePng   | 14.86 ms  | 10.51 ms  | 21.59 ms  | 2.59 ms   |  10

I think the only thing missing now is the resizing in linear color space. From some tests, I see that getting it to work would have a performance cost of 2.5x to 5x to 10x, depending on how it is implemented or measured. Here is the benchmark I used, but I only measure the performance, I didn't evaluated if the results are any good (I also do not yet know how to do that), so the implementations in that benchmark may be flawed.

@AntsyLich, @wwww-wwww it is important to get linear color space resizing before merging this? I think it is important for manga, due to the heavy use of half-tone shading. But the previous implementation had that? That is, would merging this bring a regression?

@AntsyLich
Copy link
Member

Did you mean the previous implementation had color space resizing?

@Rodrigodd
Copy link
Author

Yeah, I am asking if the previous implementation already does the resizing in a linear light color space, or it is just a new feature we want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants