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

Commit efed1663 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Track per-window information in ViewFrameInfo

FrameInfo will now be per-window; that is, per-ViewRootImpl.
Some of the information should remain “global” (it remain in Choreographer),
while some information is going to become ViewRootImpl-specific.
Before the information gets passed to the native layer,
 the ViewRootImpl-specific info will be stitched together
with the general Choreographer info.

This change is useful in order to correctly correlate frames with a specific
input event. In the unlikely scenario of a user touching two windows of the
same app simultaneously, this change will allow us to correctly measure the
latency of both frames produced by the windows.

Design doc: https://docs.google.com/document/d/1KMpMBlOxnl7zkWBCbXZZE6ZlaHEA4efYnN6WYK8n3FE/edit?resourcekey=0-eqooVNP0SskupljlTFvtOQ

Test: atest ViewFrameInfoTest
Bug: 169866723
Change-Id: Ib0bf9cd51cbcc0b9b70460c929c480eb490ec322
parent 26ca287d
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.FrameInfo;
import android.graphics.HardwareRenderer;
import android.graphics.Picture;
import android.graphics.Point;
@@ -610,8 +611,7 @@ public final class ThreadedRenderer extends HardwareRenderer {
     * @param attachInfo AttachInfo tied to the specified view.
     */
    void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
        choreographer.mFrameInfo.markDrawStart();
        attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart();

        updateRootDisplayList(view, callbacks);

@@ -629,7 +629,9 @@ public final class ThreadedRenderer extends HardwareRenderer {
            attachInfo.mPendingAnimatingRenderNodes = null;
        }

        int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
        final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo();

        int syncResult = syncAndDrawFrame(frameInfo);
        if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
            Log.w("OpenGLRenderer", "Surface lost, forcing relayout");
            // We lost our surface. For a relayout next frame which should give us a new
+81 −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.view;

import android.graphics.FrameInfo;

/**
 * The timing information of events taking place in ViewRootImpl
 * @hide
 */
public class ViewFrameInfo {
    public long drawStart;
    public long oldestInputEventTime; // the time of the oldest input event consumed for this frame
    public long newestInputEventTime; // the time of the newest input event consumed for this frame
    // Various flags set to provide extra metadata about the current frame. See flag definitions
    // inside FrameInfo.
    // @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
    public long flags;

    /**
     * Update the oldest event time.
     * @param eventTime the time of the input event
     */
    public void updateOldestInputEvent(long eventTime) {
        if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) {
            oldestInputEventTime = eventTime;
        }
    }

    /**
     * Update the newest event time.
     * @param eventTime the time of the input event
     */
    public void updateNewestInputEvent(long eventTime) {
        if (newestInputEventTime == 0 || eventTime > newestInputEventTime) {
            newestInputEventTime = eventTime;
        }
    }

    /**
     * Populate the missing fields using the data from ViewFrameInfo
     * @param frameInfo : the structure FrameInfo object to populate
     */
    public void populateFrameInfo(FrameInfo frameInfo) {
        frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
        frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
        frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime;
        frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime;
    }

    /**
     * Reset this data. Should typically be invoked after calling "populateFrameInfo".
     */
    public void reset() {
        drawStart = 0;
        oldestInputEventTime = 0;
        newestInputEventTime = 0;
        flags = 0;
    }

    /**
     * Record the current time, and store it in 'drawStart'
     */
    public void markDrawStart() {
        drawStart = System.nanoTime();
    }
}
+21 −3
Original line number Diff line number Diff line
@@ -423,7 +423,7 @@ public final class ViewRootImpl implements ViewParent,
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    int mHeight;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    Rect mDirty;
    private Rect mDirty;
    public boolean mIsAnimating;

    private boolean mUseMTRenderer;
@@ -446,6 +446,23 @@ public final class ViewRootImpl implements ViewParent,
    @UnsupportedAppUsage
    FallbackEventHandler mFallbackEventHandler;
    final Choreographer mChoreographer;
    protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();

    /**
     * Update the Choreographer's FrameInfo object with the timing information for the current
     * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
     * frame.
     * @return the updated FrameInfo object
     */
    protected @NonNull FrameInfo getUpdatedFrameInfo() {
        // Since Choreographer is a thread-local singleton while we can have multiple
        // ViewRootImpl's, populate the frame information from the current viewRootImpl before
        // starting the draw
        FrameInfo frameInfo = mChoreographer.mFrameInfo;
        mViewFrameInfo.populateFrameInfo(frameInfo);
        mViewFrameInfo.reset();
        return frameInfo;
    }

    // used in relayout to get SurfaceControl size
    // for BLAST adapter surface setup
@@ -2675,7 +2692,7 @@ public final class ViewRootImpl implements ViewParent,
                        // to resume them
                        mDirty.set(0, 0, mWidth, mHeight);
                    }
                    mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
                    mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED;
                }
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

@@ -8138,7 +8155,8 @@ public final class ViewRootImpl implements ViewParent,
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
            }
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
            mViewFrameInfo.updateOldestInputEvent(oldestEventTime);
            mViewFrameInfo.updateNewestInputEvent(eventTime);

            deliverInputEvent(q);
        }
+11 −29
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ public final class FrameInfo {
    public long[] frameInfo = new long[FRAME_INFO_SIZE];

    // Various flags set to provide extra metadata about the current frame
    private static final int FLAGS = 0;
    public static final int FLAGS = 0;

    // Is this the first-draw following a window layout?
    public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1;
@@ -60,35 +60,35 @@ public final class FrameInfo {
    @Retention(RetentionPolicy.SOURCE)
    public @interface FrameInfoFlags {}

    private static final int FRAME_TIMELINE_VSYNC_ID = 1;
    public static final int FRAME_TIMELINE_VSYNC_ID = 1;

    // The intended vsync time, unadjusted by jitter
    private static final int INTENDED_VSYNC = 2;
    public static final int INTENDED_VSYNC = 2;

    // Jitter-adjusted vsync time, this is what was used as input into the
    // animation & drawing system
    private static final int VSYNC = 3;
    public static final int VSYNC = 3;

    // The time of the oldest input event
    private static final int OLDEST_INPUT_EVENT = 4;
    public static final int OLDEST_INPUT_EVENT = 4;

    // The time of the newest input event
    private static final int NEWEST_INPUT_EVENT = 5;
    public static final int NEWEST_INPUT_EVENT = 5;

    // When input event handling started
    private static final int HANDLE_INPUT_START = 6;
    public static final int HANDLE_INPUT_START = 6;

    // When animation evaluations started
    private static final int ANIMATION_START = 7;
    public static final int ANIMATION_START = 7;

    // When ViewRootImpl#performTraversals() started
    private static final int PERFORM_TRAVERSALS_START = 8;
    public static final int PERFORM_TRAVERSALS_START = 8;

    // When View:draw() started
    private static final int DRAW_START = 9;
    public static final int DRAW_START = 9;

    // When the frame needs to be ready by
    private static final int FRAME_DEADLINE = 10;
    public static final int FRAME_DEADLINE = 10;

    // Must be the last one
    private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
@@ -99,22 +99,10 @@ public final class FrameInfo {
        frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
        frameInfo[INTENDED_VSYNC] = intendedVsync;
        frameInfo[VSYNC] = usedVsync;
        frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
        frameInfo[NEWEST_INPUT_EVENT] = 0;
        frameInfo[FLAGS] = 0;
        frameInfo[FRAME_DEADLINE] = frameDeadline;
    }

    /** checkstyle */
    public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) {
        if (inputEventOldestTime < frameInfo[OLDEST_INPUT_EVENT]) {
            frameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime;
        }
        if (inputEventTime > frameInfo[NEWEST_INPUT_EVENT]) {
            frameInfo[NEWEST_INPUT_EVENT] = inputEventTime;
        }
    }

    /** checkstyle */
    public void markInputHandlingStart() {
        frameInfo[HANDLE_INPUT_START] = System.nanoTime();
@@ -130,14 +118,8 @@ public final class FrameInfo {
        frameInfo[PERFORM_TRAVERSALS_START] = System.nanoTime();
    }

    /** checkstyle */
    public void markDrawStart() {
        frameInfo[DRAW_START] = System.nanoTime();
    }

    /** checkstyle */
    public void addFlags(@FrameInfoFlags long flags) {
        frameInfo[FLAGS] |= flags;
    }

}
+1 −5
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@
namespace android {
namespace uirenderer {

const std::string FrameInfoNames[] = {
const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = {
        "Flags",
        "FrameTimelineVsyncId",
        "IntendedVsync",
@@ -42,10 +42,6 @@ const std::string FrameInfoNames[] = {
        "GpuCompleted",
};

static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) ==
                      static_cast<int>(FrameInfoIndex::NumIndexes),
              "size mismatch: FrameInfoNames doesn't match the enum!");

static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
              "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");

Loading