Add perf test for Typeface serialization.
Bug: 172891184
Test: atest TypefaceSerializationPerfTest
Change-Id: I3a0c8ba26350d6ca51a93a458e73362ac23c566f
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
new file mode 100644
index 0000000..5473690
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.perftests;
+
+import android.graphics.Typeface;
+import android.os.SharedMemory;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Map;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceSerializationPerfTest {
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testSerializeFontMap() throws Exception {
+ Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ Typeface.serializeFontMap(systemFontMap);
+ }
+ }
+
+ @Test
+ public void testDeserializeFontMap() throws Exception {
+ SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ buffer.position(0);
+ Typeface.deserializeFontMap(buffer);
+ }
+ }
+
+ @Test
+ public void testSetSystemFontMap() throws Exception {
+ SharedMemory memory = null;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
+ // (max_map_count).
+ Typeface.destroySystemFontMap();
+ Typeface.loadPreinstalledSystemFontMap();
+ if (memory != null) {
+ memory.close();
+ }
+ memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ state.resumeTiming();
+ Typeface.setSystemFontMap(memory);
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5743df5..eba7306 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -169,6 +169,8 @@
@UnsupportedAppUsage
public long native_instance;
+ private Runnable mCleaner;
+
/** @hide */
@IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
@Retention(RetentionPolicy.SOURCE)
@@ -1123,7 +1125,7 @@
}
native_instance = ni;
- sRegistry.registerNativeAllocation(this, native_instance);
+ mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
}
@@ -1237,6 +1239,13 @@
bos.write(value & 0xFF);
}
+ /** @hide */
+ public static Map<String, Typeface> getSystemFontMap() {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ return sSystemFontMap;
+ }
+ }
+
/**
* Deserialize font map and set it as system font map. This method should be called at most once
* per process.
@@ -1297,13 +1306,33 @@
}
}
- static {
+ /** @hide */
+ @VisibleForTesting
+ public static void destroySystemFontMap() {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ for (Typeface typeface : sSystemFontMap.values()) {
+ typeface.mCleaner.run();
+ }
+ sSystemFontMap.clear();
+ if (sSystemFontMapBuffer != null) {
+ SharedMemory.unmap(sSystemFontMapBuffer);
+ }
+ sSystemFontMapBuffer = null;
+ }
+ }
+
+ /** @hide */
+ public static void loadPreinstalledSystemFontMap() {
final HashMap<String, Typeface> systemFontMap = new HashMap<>();
initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
SystemFonts.getAliases());
setSystemFontMap(systemFontMap);
}
+ static {
+ loadPreinstalledSystemFontMap();
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index b88ffa6..6d626ad 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -145,6 +145,13 @@
return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> {
std::string path(fontPath.data(), fontPath.size());
sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str());
+ if (data == nullptr) {
+ // This may happen if:
+ // 1. When the process failed to open the file (e.g. invalid path or permission).
+ // 2. When the process failed to map the file (e.g. hitting max_map_count limit).
+ ALOGE("Failed to make SkData from file name: %s", path.c_str());
+ return nullptr;
+ }
const void* fontPtr = data->data();
size_t fontSize = data->size();
std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount);