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

Commit 67d47ab8 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Add mechanism to help manage multiple system perf hints from SysUI" into main

parents 84a42c8f c823bd02
Loading
Loading
Loading
Loading
+327 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.window;

import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.PerformanceHintManager;
import android.os.Trace;
import android.util.Log;
import android.view.SurfaceControl;

import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Random;
import java.util.function.Supplier;

/**
 * A helper class to manage performance related hints for a process.  This helper is used for both
 * long-lived and transient hints.
 *
 * @hide
 */
public class SystemPerformanceHinter {
    private static final String TAG = "SystemPerformanceHinter";

    // Change app and SF wakeup times to allow sf more time to composite a frame
    public static final int HINT_SF_EARLY_WAKEUP = 1 << 0;
    // Force max refresh rate
    public static final int HINT_SF_FRAME_RATE = 1 << 1;
    // Boost CPU & GPU clocks
    public static final int HINT_ADPF = 1 << 2;
    // Convenience constant for SF only flags
    public static final int HINT_SF = HINT_SF_EARLY_WAKEUP | HINT_SF_FRAME_RATE;
    // Convenience constant for all the flags
    public static final int HINT_ALL = HINT_SF_EARLY_WAKEUP | HINT_SF_FRAME_RATE | HINT_ADPF;

    // Hints that are applied per-display and require a display root surface
    private static final int HINT_PER_DISPLAY = HINT_SF_FRAME_RATE;
    // Hints that are global (not per-display)
    private static final int HINT_GLOBAL = HINT_SF_EARLY_WAKEUP | HINT_ADPF;

    @IntDef(prefix = {"HINT_"}, value = {
            HINT_SF_EARLY_WAKEUP,
            HINT_SF_FRAME_RATE,
            HINT_ADPF,
    })
    private @interface HintFlags {}

    /**
     * A provider for the root to apply SurfaceControl hints which will be inherited by all children
     * of that root.
     * @hide
     */
    public interface DisplayRootProvider {
        /**
         * @return the SurfaceControl to apply hints for the given displayId.
         */
        @Nullable SurfaceControl getRootForDisplay(int displayId);
    }

    /**
     * A session where high performance is needed.
     * @hide
     */
    public class HighPerfSession implements AutoCloseable {
        private final @HintFlags int hintFlags;
        private final String reason;
        private final int displayId;
        private final int traceCookie;

        protected HighPerfSession(@HintFlags int hintFlags, int displayId, @NonNull String reason) {
            this.hintFlags = hintFlags;
            this.reason = reason;
            this.displayId = displayId;
            this.traceCookie = new Random().nextInt();
            if (hintFlags != 0) {
                startSession(this);
            }
        }

        /**
         * Closes this session.
         */
        public void close() {
            if (hintFlags != 0) {
                endSession(this);
            }
        }

        public void finalize() {
            close();
        }
    }

    /**
     * A no-op implementation of a session.
     */
    private class NoOpHighPerfSession extends HighPerfSession {
        public NoOpHighPerfSession() {
            super(0 /* hintFlags */, -1 /* displayId */, "");
        }

        public void close() {
            // Do nothing
        }
    }

    // The active sessions
    private final ArrayList<HighPerfSession> mActiveSessions = new ArrayList<>();
    private final SurfaceControl.Transaction mTransaction;
    private final PerformanceHintManager mPerfHintManager;
    private @Nullable PerformanceHintManager.Session mAdpfSession;
    private @Nullable DisplayRootProvider mDisplayRootProvider;


    /**
     * Constructor for the hinter.
     * @hide
     */
    public SystemPerformanceHinter(@NonNull Context context,
            @Nullable DisplayRootProvider displayRootProvider) {
        this(context, displayRootProvider, null /* transactionSupplier */);
    }

    /**
     * Constructor for the hinter.
     * @hide
     */
    @VisibleForTesting
    public SystemPerformanceHinter(@NonNull Context context,
            @Nullable DisplayRootProvider displayRootProvider,
            @Nullable Supplier<SurfaceControl.Transaction> transactionSupplier) {
        mDisplayRootProvider = displayRootProvider;
        mPerfHintManager = context.getSystemService(PerformanceHintManager.class);
        mTransaction = transactionSupplier != null
                ? transactionSupplier.get()
                : new SurfaceControl.Transaction();
    }

    /**
     * Sets the current ADPF session, required if you are using HINT_ADPF.  It is the responsibility
     * of the caller to manage up the ADPF session.
     * @hide
     */
    public void setAdpfSession(PerformanceHintManager.Session adpfSession) {
        mAdpfSession = adpfSession;
    }

    /**
     * Starts a session that requires high performance.
     * @hide
     */
    public HighPerfSession startSession(@HintFlags int hintFlags, int displayId,
            @NonNull String reason) {
        if (mDisplayRootProvider == null && (hintFlags & HINT_SF_FRAME_RATE) != 0) {
            throw new IllegalArgumentException(
                    "Using SF frame rate hints requires a valid display root provider");
        }
        if (mAdpfSession == null && (hintFlags & HINT_ADPF) != 0) {
            throw new IllegalArgumentException("Using ADPF hints requires an ADPF session");
        }
        if ((hintFlags & HINT_PER_DISPLAY) != 0) {
            if (mDisplayRootProvider.getRootForDisplay(displayId) == null) {
                // Just log an error and return early if there is no root as there could be races
                // between when a display root is removed and when a hint session is requested
                Log.v(TAG, "No display root for displayId=" + displayId);
                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "PerfHint-NoDisplayRoot: " + displayId);
                return new NoOpHighPerfSession();
            }
        }
        return new HighPerfSession(hintFlags, displayId, reason);
    }

    /**
     * Starts a session that requires high performance.
     */
    private void startSession(HighPerfSession session) {
        int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
        int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
                session.displayId);
        mActiveSessions.add(session);
        int newGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
        int newPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
                session.displayId);

        boolean transactionChanged = false;
        // Per-display flags
        if (nowEnabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) {
            mTransaction.setFrameRateSelectionStrategy(
                    mDisplayRootProvider.getRootForDisplay(session.displayId),
                    FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
            transactionChanged = true;
            Trace.beginAsyncSection("PerfHint-framerate-" + session.reason, session.traceCookie);
        }

        // Global flags
        if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
            mTransaction.setEarlyWakeupStart();
            transactionChanged = true;
            Trace.beginAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie);
        }
        if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
            mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_UP);
            Trace.beginAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
        }
        if (transactionChanged) {
            mTransaction.apply();
        }
    }

    /**
     * Ends a session that requires high performance.
     */
    private void endSession(HighPerfSession session) {
        int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
        int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
                session.displayId);
        mActiveSessions.remove(session);
        int newGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
        int newPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
                session.displayId);

        boolean transactionChanged = false;
        // Per-display flags
        if (nowDisabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) {
            mTransaction.setFrameRateSelectionStrategy(
                    mDisplayRootProvider.getRootForDisplay(session.displayId),
                    FRAME_RATE_SELECTION_STRATEGY_SELF);
            transactionChanged = true;
            Trace.endAsyncSection("PerfHint-framerate-" + session.reason, session.traceCookie);
        }

        // Global flags
        if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
            mTransaction.setEarlyWakeupEnd();
            transactionChanged = true;
            Trace.endAsyncSection("PerfHint-early_wakeup" + session.reason, session.traceCookie);
        }
        if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
            mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
            Trace.endAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
        }
        if (transactionChanged) {
            mTransaction.apply();
        }
    }

    /**
     * Checks if checkFlags was previously not set and is now set.
     */
    private boolean nowEnabled(@HintFlags int oldFlags, @HintFlags int newFlags,
                               @HintFlags int checkFlags) {
        return (oldFlags & checkFlags) == 0 && (newFlags & checkFlags) != 0;
    }

    /**
     * Checks if checkFlags was previously set and is now not set.
     */
    private boolean nowDisabled(@HintFlags int oldFlags, @HintFlags int newFlags,
                                @HintFlags int checkFlags) {
        return (oldFlags & checkFlags) != 0 && (newFlags & checkFlags) == 0;
    }

    /**
     * @return the combined hint flags for all active sessions, filtered by {@param filterFlags}.
     */
    private @HintFlags int calculateActiveHintFlags(@HintFlags int filterFlags) {
        int flags = 0;
        for (int i = 0; i < mActiveSessions.size(); i++) {
            flags |= mActiveSessions.get(i).hintFlags & filterFlags;
        }
        return flags;
    }

    /**
     * @return the combined hint flags for all active sessions for a given display, filtered by
     *         {@param filterFlags}.
     */
    private @HintFlags int calculateActiveHintFlagsForDisplay(@HintFlags int filterFlags,
            int displayId) {
        int flags = 0;
        for (int i = 0; i < mActiveSessions.size(); i++) {
            final HighPerfSession session = mActiveSessions.get(i);
            if (session.displayId == displayId) {
                flags |= mActiveSessions.get(i).hintFlags & filterFlags;
            }
        }
        return flags;
    }

    /**
     * Dumps the existing sessions.
     */
    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG + ":");
        pw.println(innerPrefix + "Active sessions (" + mActiveSessions.size() + "):");
        for (int i = 0; i < mActiveSessions.size(); i++) {
            final HighPerfSession s = mActiveSessions.get(i);
            pw.println(innerPrefix + "  reason=" + s.reason
                    + " flags=" + s.hintFlags
                    + " display=" + s.displayId);
        }
    }
}
+389 −0

File added.

Preview size limit exceeded, changes collapsed.

+14 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.view.SurfaceControl;
import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
import android.window.SystemPerformanceHinter;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -58,6 +59,14 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
    /** {@link DisplayAreaContext} list, which is mapped by display IDs. */
    private final SparseArray<DisplayAreaContext> mDisplayAreaContexts = new SparseArray<>();

    private final SystemPerformanceHinter.DisplayRootProvider mPerfRootProvider =
            new SystemPerformanceHinter.DisplayRootProvider() {
                @Override
                public SurfaceControl getRootForDisplay(int displayId) {
                    return mLeashes.get(displayId);
                }
            };

    private final Context mContext;

    public RootTaskDisplayAreaOrganizer(Executor executor, Context context) {
@@ -229,6 +238,11 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
        return mDisplayAreaContexts.get(displayId);
    }

    @NonNull
    public SystemPerformanceHinter.DisplayRootProvider getPerformanceRootProvider() {
        return mPerfRootProvider;
    }

    public void dump(@NonNull PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        final String childPrefix = innerPrefix + "  ";
+13 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
import android.window.SystemPerformanceHinter;

import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
@@ -85,6 +86,7 @@ import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.performance.PerfHintController;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
@@ -296,6 +298,17 @@ public abstract class WMShellBaseModule {
        return new LaunchAdjacentController(syncQueue);
    }

    @WMSingleton
    @Provides
    static SystemPerformanceHinter provideSystemPerformanceHinter(Context context,
            ShellInit shellInit,
            ShellCommandHandler shellCommandHandler,
            RootTaskDisplayAreaOrganizer rootTdaOrganizer) {
        final PerfHintController perfHintController =
                new PerfHintController(context, shellInit, shellCommandHandler, rootTdaOrganizer);
        return perfHintController.getHinter();
    }

    //
    // Back animation
    //
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.wm.shell.performance

import android.content.Context
import android.os.PerformanceHintManager
import android.os.Process
import android.window.SystemPerformanceHinter
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import java.io.PrintWriter
import java.util.concurrent.TimeUnit

/**
 * Manages the performance hints to the system.
 */
class PerfHintController(private val mContext: Context,
                         shellInit: ShellInit,
                         private val mShellCommandHandler: ShellCommandHandler,
                         rootTdaOrganizer: RootTaskDisplayAreaOrganizer) {

    // The system perf hinter
    val hinter: SystemPerformanceHinter

    init {
        hinter = SystemPerformanceHinter(mContext,
                rootTdaOrganizer.performanceRootProvider)
        shellInit.addInitCallback(this::onInit, this)
    }

    private fun onInit() {
        mShellCommandHandler.addDumpCallback(this::dump, this)
        val perfHintMgr = mContext.getSystemService(PerformanceHintManager::class.java)
        val adpfSession = perfHintMgr!!.createHintSession(intArrayOf(Process.myTid()),
                TimeUnit.SECONDS.toNanos(1))
        hinter.setAdpfSession(adpfSession)
    }

    fun dump(pw: PrintWriter, prefix: String?) {
        hinter.dump(pw, prefix)
    }
}