Pārlūkot izejas kodu

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).
Nico Weber 1 gadu atpakaļ
vecāks
revīzija
27e369cd19

+ 20 - 4
Userland/Libraries/LibGfx/ICC/Profile.cpp

@@ -1219,14 +1219,21 @@ ErrorOr<FloatVector3> 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<Lut16TagData const&>(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<Lut8TagData const&>(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<LutAToBTagData const&>(tag_data);
@@ -1236,10 +1243,19 @@ ErrorOr<FloatVector3> 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<u8, 4>(color);
+    m_to_pcs_clut_cache->value = result;
+
+    return result;
 }
 
 ErrorOr<FloatVector3> Profile::to_pcs(ReadonlyBytes color) const

+ 6 - 0
Userland/Libraries/LibGfx/ICC/Profile.h

@@ -321,6 +321,12 @@ private:
     FloatMatrix3x3 rgb_to_xyz_matrix() const;
 
     mutable Optional<FloatMatrix3x3> m_cached_xyz_to_rgb_matrix;
+
+    struct OneElementCLUTCache {
+        Vector<u8, 4> key;
+        FloatVector3 value;
+    };
+    mutable Optional<OneElementCLUTCache> m_to_pcs_clut_cache;
 };
 
 }