-
-
Notifications
You must be signed in to change notification settings - Fork 22.5k
Use libjpeg-turbo for improved jpg compatibility and speed #104347
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
Conversation
bb78edd
to
940bcdc
Compare
Did the same test as #104298 for impact on size, libjpeg-turbo (in the current implementation, without SIMD, etc.) seems to add 352 KiB, so that starts being significant (but can be justified if the gains are worth it).
I'm a bit wary seeing the 138k lines of code vs 25k for libjpeg. I believe some of them might not be needed and we can likely reduce this a bit. jpeg decoding and encoding is not the main workflow of Godot, where we mostly rely on webp/png for sprites, and VRAM compression for textures used in 3D, so I'm not sure how far we should go in using the most optimized paths for jpeg specifically. I expect enabling SIMD should bring more gains than the current 85% decoding time measured here, but from experience it also means significant added complexity on the buildsystem side (and potential edge cases for third-party platforms to handle where the arch assumptions may not match the ones we have for first-party platforms). |
what happens if you remove the jpeg module and only use jpg turbo |
Weirdly enough, compiling libjpeg-turbo (rebased on fc827bb) with It's tiny and within margin of error for LTO optimization possibilities, but basically this means that the jpgd module weighs practically nothing. That's why we were using such a lightweight library in the first place, little complexity and binary size impact. |
I haven't made any effort yet to remove the unused code, will do that tomorrow. The SIMD stuff is all via nasm, rather than spending time trying to get that working in godot i'll just provide some raw benchmark results from the command line version of libjpeg-turbo with/without SIMD, hopefully that will be enough information to make a decision. That last result is very strange given that disabling the jpg modules also disables the dependent thorvg and basis-universal modules too... I imagine if you removed the dependency check they would hit linker errors. |
Oh and for extra context, I am working on a game which pulls images from the internet at run time, much like the museum and many things does, so can't be in control of the process. |
Yeah I was surprised too at first but the basis_universal module only depends on jpgd for editor builds, and I'm testing with a release export template (since it's the one where size matters most). thorvg seems to be using its own version of jpgd currently, not our copy in |
940bcdc
to
62ee999
Compare
Comparison time
The lines of code count might be slightly off as I have added the copyright stuff for libjpeg but not libjpeg-turbo yet. I only did a single run of the compile times so there might be some variance there. As for SIMD, I haven't put that in godot but here is the difference it makes using the command line libjpeg-turbo tools.
Thoughts? Personally I don't think SIMD is worth the effort and complexity, despite the performance. |
I'm surprised that there's virtually no performance difference between the reference libjpeg implementation and the minimal single-header jpeg-compressor. I guess Rich did a good job there to get on par with libjpeg.
Yeah I agree. Not including SIMD reduces the LOCs quite a bit and the complexity significantly. We can always add it later if/when we evaluate that it's worth the trouble. I\ve played around quickly with using libjpeg-turbo for thorvg, and removing jpeg-compressor: https://github.com/akien-mga/godot/commits/libjpeg_turbo/ If we go this route, I would suggest we fully remove My patch should also fix the MSVC build but more work is likely needed to make With the current version of this PR (62ee999) and my patch removing jpeg-compressor, I get (
So the diff between Note: The |
That all sounds good. I'll cherry pick your additions and carry on with the rest of the work 👍 |
62ee999
to
270dafc
Compare
@@ -5,3 +5,4 @@ deadlock:tests/core/templates/test_command_queue.h | |||
deadlock:modules/text_server_adv/text_server_adv.cpp | |||
deadlock:modules/text_server_fb/text_server_fb.cpp | |||
race:modules/navigation/nav_map_3d.cpp | |||
race:thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume it is ok to suppress errors in third party code? Or does that still leave us vulnerable to crashes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The best I can do is try to file a bug report with @hermet at Thorvg
c7ceb66
to
24374ba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested locally, it works as expected.
This has a positive impact on MovieWriter speed when using AVI (MJPEG) output. Example with godot-reflection's benchmark mode with JPEG quality set to 100. The time represented is the time it takes to record a movie of the full benchmark run (1 minute at 60 FPS):
Before | After |
---|---|
2m43s (36% of real-time speed) | 2m18s (43% of real-time speed) |
On the other hand, binary size grows a fair bit for stripped Linux x86_64 release export templates (production=yes lto=full
):
Before | After |
---|---|
72,679,096 bytes | 73,150,360 bytes (+ 471 KB) |
I'm not sure why the size increase is so large compared to what @akien-mga reported.
10e5dbb
to
fb7f3a6
Compare
b0270e3
to
3263a32
Compare
This PR is now back in a working state and ready for review. If any of you want me to keep it updated with master after today, add a comment, or perhaps let me know which meeting to attend. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a final review pass and some light testing, everything looks good to me.
Just pointed out a few minor adjustments to copyright, documentation and buildsystem stuff.
Final size impact comparison on a Linux release export template:
$ lsmb bin
-rwxr-xr-x. 1 akien akien 67.70MB May 1 11:17 godot.linuxbsd.template_release.x86_64.libjpeg-turbo
-rwxr-xr-x. 1 akien akien 67.36MB May 1 11:14 godot.linuxbsd.template_release.x86_64.master
So it adds 340 KiB.
23791f2
to
0132b24
Compare
I've made all the requested changes in a new commit to make re-reviewing easier, will squash them together before merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
86d436c
to
d133a17
Compare
I got a linking issue when building with the jpg module first, and then with the module disabled:
I'll look into it, SCons should normally properly recompile the dependent files if the config changed, so maybe we missed something. |
The problem comes from my use of our Building first with:
and then:
I see:
while the Without This shouldn't block merging this PR, it's an orthogonal issue. |
On the other hand there's this one change that should be added too, even though currently it has no impact. It registers the svg module's two optional dependencies as such: diff --git a/modules/svg/config.py b/modules/svg/config.py
index d22f9454ed..d37f02188f 100644
--- a/modules/svg/config.py
+++ b/modules/svg/config.py
@@ -1,4 +1,5 @@
def can_build(env, platform):
+ env.module_add_dependencies("svg", ["jpg", "webp"], True)
return True
( |
I linked a few more issues I could find which this fixes. Should be ready to merge after CI passes. |
Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
Thanks! Amazing work 🎉 |
I couldn't have done it without you, thanks! |
Alternative to #104298 using libjpeg-turbo instead. In my testing it runs in 85% of the time of libjpeg, with further improvement possible if we enable SIMD.
Image::load_jpg_from_buffer()
#45523