Leon Scroggins III 6c5864c098 Do not cache AVDs that are off screen
Bug: 128805564
Test: Manual + systrace; hwui_unit_tests; CtsUiRenderingTestCases

Only update a VectorDrawable's cache if it is onscreen. This fixes a
Twitter use case where the app has a ProgressBar that is exactly one
pixel offscreen. Prior to this CL, we repeatedly drew the ProgressBar's
AVD to a GPU surface, even though we clip it out later and never draw
that GPU surface. Now, we recognize that the AVD is outside of the
bounds of the screen, so we never draw to the GPU surface.

TreeInfo:
- store the size of the screen, retrieved from
  CanvasContext::getNextFrameSize.
SkiaDisplayList:
- Store the matrix at the time of recording a VectorDrawable. Concat
  that with the current matrix to determine whether the VD is on screen,
  based on the TreeInfo. If it is offscreen, do not add it to the list
  of AVDs that need to be updated ahead of rendering.
- In addition, if it is offscreen (or not dirty), do not call
  setPropertyChangeWillBeConsumed(true). This prevents triggering
  dispatchFrameCallbacks to update on the RenderThread when there is no
  need to. This also mimics what would happen if the View/RenderNode had
  been completely offscreen.
- Add a method to append an AVD to mVectorDrawables. Now that the vector
  is of Pairs, this simplifies the call sites. Add a second helper to
  just add an AVD without a matrix, for use in tests.
SkiaRecordingCanvas:
- get the current matrix and store it in the display list along with the
  AVD.
CanvasContext:
- add getNextFrameSize, for reporting the size of the next frame without
  dequeuing it
VectorDrawable.cpp:
- call quickReject to potentially short circuit drawing. This is for a
  hypothetical use case (verified in a test app) where the containing
  RenderNode is partially onscreen, but the AVD itself is not. Even
  without the change to VectorDrawable.cpp, we skip uploading to the GPU
  cache, the SkiaDisplayList still attempts to draw it. This change
  keeps us from drawing it at all.
SkiaDisplayListTests.cpp:
- Now that I've hidden mVectorDrawables, call the new public APIs.
- prepareListAndChildren test
  - for the clean VD, assert that getPropertyChangeWillBeConsumed
    returns FALSE. This is due to the behavior change that we do not
    set it unless the VD is dirty.
  - set the bounds, so our onscreen check works.
- Add another test for prepareListAndChildren, which puts VDs offscreen.

Change-Id: Iae0a07adcf58e7884e0854720de644e7b2faf2bf
2019-04-12 14:31:31 -04:00

133 lines
4.4 KiB
C++

/*
* Copyright (C) 2014 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.
*/
#pragma once
#include "Properties.h"
#include "utils/Macros.h"
#include <utils/Timers.h>
#include "SkSize.h"
#include <string>
namespace android {
namespace uirenderer {
namespace renderthread {
class CanvasContext;
}
class DamageAccumulator;
class LayerUpdateQueue;
class RenderNode;
class RenderState;
class ErrorHandler {
public:
virtual void onError(const std::string& message) = 0;
protected:
virtual ~ErrorHandler() = default;
};
class TreeObserver {
public:
// Called when a RenderNode's parent count hits 0.
// Due to the unordered nature of tree pushes, once prepareTree
// is finished it is possible that the node was "resurrected" and has
// a non-zero parent count.
virtual void onMaybeRemovedFromTree(RenderNode* node) = 0;
protected:
virtual ~TreeObserver() = default;
};
// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
class TreeInfo {
PREVENT_COPY_AND_ASSIGN(TreeInfo);
public:
enum TraversalMode {
// The full monty - sync, push, run animators, etc... Used by DrawFrameTask
// May only be used if both the UI thread and RT thread are blocked on the
// prepare
MODE_FULL,
// Run only what can be done safely on RT thread. Currently this only means
// animators, but potentially things like SurfaceTexture updates
// could be handled by this as well if there are no listeners
MODE_RT_ONLY,
};
TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext);
TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
// textures if we run out of cache space.
bool prepareTextures;
renderthread::CanvasContext& canvasContext;
// TODO: buildLayer uses this to suppress running any animations, but this
// should probably be refactored somehow. The reason this is done is
// because buildLayer is not setup for injecting the animationHook, as well
// as this being otherwise wasted work as all the animators will be
// re-evaluated when the frame is actually drawn
bool runAnimations = true;
// Must not be null during actual usage
DamageAccumulator* damageAccumulator = nullptr;
int64_t damageGenerationId = 0;
LayerUpdateQueue* layerUpdateQueue = nullptr;
ErrorHandler* errorHandler = nullptr;
bool updateWindowPositions = false;
int disableForceDark;
const SkISize screenSize;
struct Out {
bool hasFunctors = false;
// This is only updated if evaluateAnimations is true
bool hasAnimations = false;
// This is set to true if there is an animation that RenderThread cannot
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
bool requiresUiRedraw = false;
// This is set to true if draw() can be called this frame
// false means that we must delay until the next vsync pulse as frame
// production is outrunning consumption
// NOTE that if this is false CanvasContext will set either requiresUiRedraw
// *OR* will post itself for the next vsync automatically, use this
// only to avoid calling draw()
bool canDrawThisFrame = true;
// Sentinel for animatedImageDelay meaning there is no need to post such
// a message.
static constexpr nsecs_t kNoAnimatedImageDelay = -1;
// This is used to post a message to redraw when it is time to draw the
// next frame of an AnimatedImageDrawable.
nsecs_t animatedImageDelay = kNoAnimatedImageDelay;
} out;
// This flag helps to disable projection for receiver nodes that do not have any backward
// projected children.
bool hasBackwardProjectedNodes = false;
// TODO: Damage calculations
};
} /* namespace uirenderer */
} /* namespace android */