Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 23093756 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh
Browse files

Add perf test for Typeface serialization.

Bug: 172891184
Test: atest TypefaceSerializationPerfTest
Change-Id: I3a0c8ba26350d6ca51a93a458e73362ac23c566f
parent 539bfca1
Loading
Loading
Loading
Loading
+83 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+31 −2
Original line number Diff line number Diff line
@@ -169,6 +169,8 @@ public class Typeface {
    @UnsupportedAppUsage
    public long native_instance;

    private Runnable mCleaner;

    /** @hide */
    @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
    @Retention(RetentionPolicy.SOURCE)
@@ -1123,7 +1125,7 @@ public class Typeface {
        }

        native_instance = ni;
        sRegistry.registerNativeAllocation(this, native_instance);
        mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
        mStyle = nativeGetStyle(ni);
        mWeight = nativeGetWeight(ni);
    }
@@ -1237,6 +1239,13 @@ public class Typeface {
        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 @@ public class Typeface {
        }
    }

    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;
+7 −0
Original line number Diff line number Diff line
@@ -145,6 +145,13 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki
    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);