Merge "Restrict maximum size of FontInterpolator font caches" into tm-qpr-dev
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index f0a8211..83e44b6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -18,8 +18,10 @@
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
+import android.util.LruCache
import android.util.MathUtils
import android.util.MathUtils.abs
+import androidx.annotation.VisibleForTesting
import java.lang.Float.max
import java.lang.Float.min
@@ -34,6 +36,10 @@
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_DEFAULT_VALUE = 0f
+// Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in
+// frame draw time on a Pixel 6.
+@VisibleForTesting const val FONT_CACHE_MAX_ENTRIES = 10
+
/** Provide interpolation of two fonts by adjusting font variation settings. */
class FontInterpolator {
@@ -81,8 +87,8 @@
// Font interpolator has two level caches: one for input and one for font with different
// variation settings. No synchronization is needed since FontInterpolator is not designed to be
// thread-safe and can be used only on UI thread.
- private val interpCache = hashMapOf<InterpKey, Font>()
- private val verFontCache = hashMapOf<VarFontKey, Font>()
+ private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES)
+ private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES)
// Mutable keys for recycling.
private val tmpInterpKey = InterpKey(null, null, 0f)
@@ -152,7 +158,7 @@
tmpVarFontKey.set(start, newAxes)
val axesCachedFont = verFontCache[tmpVarFontKey]
if (axesCachedFont != null) {
- interpCache[InterpKey(start, end, progress)] = axesCachedFont
+ interpCache.put(InterpKey(start, end, progress), axesCachedFont)
return axesCachedFont
}
@@ -160,8 +166,8 @@
// Font.Builder#build won't throw IOException since creating fonts from existing fonts will
// not do any IO work.
val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
- interpCache[InterpKey(start, end, progress)] = newFont
- verFontCache[VarFontKey(start, newAxes)] = newFont
+ interpCache.put(InterpKey(start, end, progress), newFont)
+ verFontCache.put(VarFontKey(start, newAxes), newFont)
return newFont
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 9e9929e..3ee97be 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -24,8 +24,10 @@
import android.graphics.Typeface
import android.graphics.fonts.Font
import android.text.Layout
+import android.util.LruCache
private const val DEFAULT_ANIMATION_DURATION: Long = 300
+private const val TYPEFACE_CACHE_MAX_ENTRIES = 5
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
/**
@@ -114,7 +116,7 @@
private val fontVariationUtils = FontVariationUtils()
- private val typefaceCache = HashMap<String, Typeface?>()
+ private val typefaceCache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
fun updateLayout(layout: Layout) {
textInterpolator.layout = layout
@@ -218,12 +220,12 @@
}
if (!fvar.isNullOrBlank()) {
- textInterpolator.targetPaint.typeface =
- typefaceCache.getOrElse(fvar) {
- textInterpolator.targetPaint.fontVariationSettings = fvar
+ textInterpolator.targetPaint.typeface = typefaceCache.get(fvar) ?: run {
+ textInterpolator.targetPaint.fontVariationSettings = fvar
+ textInterpolator.targetPaint.typeface?.also {
typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
- textInterpolator.targetPaint.typeface
}
+ }
}
if (color != null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 8a5c5b5..57a355f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -106,4 +106,29 @@
val reversedFont = interp.lerp(endFont, startFont, 0.5f)
assertThat(resultFont).isSameInstanceAs(reversedFont)
}
+
+ @Test
+ fun testCacheMaxSize() {
+ val interp = FontInterpolator()
+
+ val startFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 100")
+ .build()
+ val endFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 1")
+ .build()
+ val resultFont = interp.lerp(startFont, endFont, 0.5f)
+ for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) {
+ val f1 = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' ${i * 100}")
+ .build()
+ val f2 = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' $i")
+ .build()
+ interp.lerp(f1, f2, 0.5f)
+ }
+
+ val cachedFont = interp.lerp(startFont, endFont, 0.5f)
+ assertThat(resultFont).isNotSameInstanceAs(cachedFont)
+ }
}