From 27e369cd191e95eedda9a4231c94e4264128575c Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sun, 18 Feb 2024 22:24:37 -0500 Subject: [PATCH] LibGfx/ICC: Add a one-element cache for CLUT conversions Our current CLUT code is pretty slow. A one-element cache can make it quite a bit faster for images that have long runs of a single color, such as illustrations. It also seems to help with photos some (in `0000711.pdf` page 1) -- I suppose even them have enough repeating pixels for it to be worth it. Some numbers, with `pdf --render-bench`: `0000711.pdf --page 1` (high-res photo) before: 2.9s after: 2.43s `0000277.pdf --page 19` (my nemesis PDF, large-ish illustration) before: 2.17s after: 0.58s (!) `0000502.pdf --page 2` (wat hoe dat) before: 0.66s after: 0.27s `0000521.pdf --page 10 ` (japanese) before: 0.52s after: 0.29s `0000364.pdf --page 1` (4 min test case) before: 0.48s after: 0.19s Thanks to that last one, reduces the time for `time Meta/test_pdf.py ~/Downloads/0000` from 4m22s to 1m28s. Helps quite a bit with #23157 (but high-res photos are still too slow). --- Userland/Libraries/LibGfx/ICC/Profile.cpp | 24 +++++++++++++++++++---- Userland/Libraries/LibGfx/ICC/Profile.h | 6 ++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibGfx/ICC/Profile.cpp b/Userland/Libraries/LibGfx/ICC/Profile.cpp index 44503392ae..f4d4945859 100644 --- a/Userland/Libraries/LibGfx/ICC/Profile.cpp +++ b/Userland/Libraries/LibGfx/ICC/Profile.cpp @@ -1219,14 +1219,21 @@ ErrorOr Profile::to_pcs_a_to_b(TagData const& tag_data, ReadonlyBy // Assumes a "normal" device_class() (i.e. not DeviceLink). VERIFY(number_of_components_in_color_space(connection_space()) == 3); + if (m_to_pcs_clut_cache.has_value() && m_to_pcs_clut_cache->key == color) + return m_to_pcs_clut_cache->value; + + FloatVector3 result; + switch (tag_data.type()) { case Lut16TagData::Type: { auto const& a_to_b = static_cast(tag_data); - return a_to_b.evaluate(data_color_space(), connection_space(), color); + result = TRY(a_to_b.evaluate(data_color_space(), connection_space(), color)); + break; } case Lut8TagData::Type: { auto const& a_to_b = static_cast(tag_data); - return a_to_b.evaluate(data_color_space(), connection_space(), color); + result = TRY(a_to_b.evaluate(data_color_space(), connection_space(), color)); + break; } case LutAToBTagData::Type: { auto const& a_to_b = static_cast(tag_data); @@ -1236,10 +1243,19 @@ ErrorOr Profile::to_pcs_a_to_b(TagData const& tag_data, ReadonlyBy if (a_to_b.number_of_output_channels() != number_of_components_in_color_space(connection_space())) return Error::from_string_literal("ICC::Profile::to_pcs_a_to_b: mAB output channel count does not match profile connection space size"); - return a_to_b.evaluate(connection_space(), color); + result = TRY(a_to_b.evaluate(connection_space(), color)); + break; } + default: + VERIFY_NOT_REACHED(); } - VERIFY_NOT_REACHED(); + + if (!m_to_pcs_clut_cache.has_value()) + m_to_pcs_clut_cache = OneElementCLUTCache {}; + m_to_pcs_clut_cache->key = Vector(color); + m_to_pcs_clut_cache->value = result; + + return result; } ErrorOr Profile::to_pcs(ReadonlyBytes color) const diff --git a/Userland/Libraries/LibGfx/ICC/Profile.h b/Userland/Libraries/LibGfx/ICC/Profile.h index 91a6fc06dc..9178f8d4d9 100644 --- a/Userland/Libraries/LibGfx/ICC/Profile.h +++ b/Userland/Libraries/LibGfx/ICC/Profile.h @@ -321,6 +321,12 @@ private: FloatMatrix3x3 rgb_to_xyz_matrix() const; mutable Optional m_cached_xyz_to_rgb_matrix; + + struct OneElementCLUTCache { + Vector key; + FloatVector3 value; + }; + mutable Optional m_to_pcs_clut_cache; }; }