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

Commit a744c7e8 authored by Ming-Shin Lu's avatar Ming-Shin Lu Committed by Android (Google) Code Review
Browse files

Merge "Add NotifyRendererRateLimiter for controlling ADPF hint invocation" into main

parents 88494897 9f95896f
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -3013,7 +3013,7 @@ public final class ViewRootImpl implements ViewParent,
     */
    public void notifyRendererOfExpensiveFrame() {
        if (mAttachInfo.mThreadedRenderer != null) {
            mAttachInfo.mThreadedRenderer.notifyExpensiveFrame();
            mAttachInfo.mThreadedRenderer.notifyExpensiveFrameWithRateLimit(null);
        }
    }
@@ -3023,11 +3023,8 @@ public final class ViewRootImpl implements ViewParent,
     * @hide
     */
    public void notifyRendererOfExpensiveFrame(String reason) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, reason);
        try {
            notifyRendererOfExpensiveFrame();
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        if (mAttachInfo.mThreadedRenderer != null) {
            mAttachInfo.mThreadedRenderer.notifyExpensiveFrameWithRateLimit(reason);
        }
    }
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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;

import static com.google.common.truth.Truth.assertThat;

import android.os.SystemClock;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class HardwareRendererTest {

    @Test
    public void testNotifyExpensiveFrameWithRateLimit() {
        final HardwareRenderer renderer = new HardwareRenderer();

        // Expect receiving the callback from rate limiter after notifying renderer
        int initialNotifyCount = renderer.notifyExpensiveFrameWithRateLimit("testing");
        assertThat(initialNotifyCount).isEqualTo(1);

        // Expect the rate limiter won't allow the burst calls of notifying renderer
        int currentNotifyCount = initialNotifyCount;
        for (int i = 0; i < 1000; i++) {
            currentNotifyCount = renderer.notifyExpensiveFrameWithRateLimit("testing");
        }
        assertThat(currentNotifyCount).isEqualTo(initialNotifyCount);

        // Expect the rate limiter allows the call of notifying renderer after the timeout
        SystemClock.sleep(100);
        currentNotifyCount = renderer.notifyExpensiveFrameWithRateLimit("testing");
        assertThat(currentNotifyCount).isGreaterThan(initialNotifyCount);
    }
}
+69 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace;
import android.util.Log;
import android.util.TimeUtils;
import android.view.Display;
@@ -46,6 +47,8 @@ import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.animation.AnimationUtils;

import com.android.internal.util.RateLimitingCache;

import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -189,6 +192,62 @@ public class HardwareRenderer {
    private @ActivityInfo.ColorMode int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
    private float mDesiredSdrHdrRatio = 1f;

    private final NotifyRendererRateLimiter mNotifyRendererWorkLoadRateLimiter =
            createNotifyRendererRateLimiter();

    /**
     * A class for rate limiting of notifying renderer IPC invocation (e.g. notifyExpensiveFrame)
     * that allows once per period to appropriate control for bursts of calls.
     */
    private static class NotifyRendererRateLimiter extends RateLimitingCache<Void> {
        private static final long DEFAULT_NOTIFY_PERIOD_MILLIS = 100;
        private final @NonNull RateLimitingCache.ValueFetcher<Void> mNotifyRendererRunnable;

        /** Counts when the rate limiter permits to notify the renderer */
        private int mNotifyCount;
        private @Nullable String mNotifyReason;

        NotifyRendererRateLimiter(@NonNull RateLimitingCache.ValueFetcher<Void> runnable,
                long periodMillis) {
            super(periodMillis);
            mNotifyRendererRunnable = runnable;
        }

        private int notifyIfAllow(String reason) {
            mNotifyReason = reason;
            get(mNotifyRendererRunnable);
            return mNotifyCount;
        }

        private void incrementNotifyCount() {
            mNotifyCount++;
        }

        private @Nullable String getNotifyReason() {
            return mNotifyReason;
        }
    }

    private NotifyRendererRateLimiter getNotifyRendererRateLimiter() {
        return mNotifyRendererWorkLoadRateLimiter;
    }

    private NotifyRendererRateLimiter createNotifyRendererRateLimiter() {
        return new NotifyRendererRateLimiter(() -> {
            final String notifyReason = getNotifyRendererRateLimiter().getNotifyReason();
            final boolean logForReason = notifyReason != null && !notifyReason.isEmpty();
            try {
                final String traceReason = logForReason ? notifyReason : "notifyExpensiveFrame";
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, traceReason);
                notifyExpensiveFrame();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            getNotifyRendererRateLimiter().incrementNotifyCount();
            return null;
        }, NotifyRendererRateLimiter.DEFAULT_NOTIFY_PERIOD_MILLIS);
    }

    /**
     * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default
     * to opaque with no light source configured.
@@ -1052,6 +1111,16 @@ public class HardwareRenderer {
        nNotifyExpensiveFrame(mNativeProxy);
    }

    /**
     * Notifies the hardware renderer from the UI thread about upcoming expensive frames with
     * rate limiting control.
     *
     * @hide
     */
    public int notifyExpensiveFrameWithRateLimit(String reason) {
        return mNotifyRendererWorkLoadRateLimiter.notifyIfAllow(reason);
    }

    /**
     * b/68769804, b/66945974: For low FPS experiments.
     *