Merge "Change how memory policy is configured"
This commit is contained in:
@@ -6662,6 +6662,9 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
|
||||
// Pass the current context to HardwareRenderer
|
||||
HardwareRenderer.setContextForInit(getSystemContext());
|
||||
if (data.persistent) {
|
||||
HardwareRenderer.setIsSystemOrPersistent();
|
||||
}
|
||||
|
||||
// Instrumentation info affects the class loader, so load it before
|
||||
// setting up the app context.
|
||||
|
||||
@@ -196,8 +196,6 @@ public final class ThreadedRenderer extends HardwareRenderer {
|
||||
*/
|
||||
public static boolean sRendererEnabled = true;
|
||||
|
||||
public static boolean sTrimForeground = false;
|
||||
|
||||
/**
|
||||
* Controls whether or not the renderer should aggressively trim
|
||||
* memory. Note that this must not be set for any process that uses
|
||||
@@ -205,9 +203,10 @@ public final class ThreadedRenderer extends HardwareRenderer {
|
||||
* that do not go into the background.
|
||||
*/
|
||||
public static void enableForegroundTrimming() {
|
||||
sTrimForeground = true;
|
||||
// TODO: Remove
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize HWUI for being in a system process like system_server
|
||||
* Should not be called in non-system processes
|
||||
@@ -218,9 +217,8 @@ public final class ThreadedRenderer extends HardwareRenderer {
|
||||
// process.
|
||||
if (!ActivityManager.isHighEndGfx()) {
|
||||
sRendererEnabled = false;
|
||||
} else {
|
||||
enableForegroundTrimming();
|
||||
}
|
||||
setIsSystemOrPersistent();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1766,9 +1766,6 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mAppVisibilityChanged = true;
|
||||
scheduleTraversals();
|
||||
}
|
||||
if (!mAppVisible) {
|
||||
WindowManagerGlobal.trimForeground();
|
||||
}
|
||||
AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ package android.view;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ComponentCallbacks2;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.Configuration;
|
||||
@@ -524,9 +522,6 @@ public final class WindowManagerGlobal {
|
||||
}
|
||||
allViewsRemoved = mRoots.isEmpty();
|
||||
}
|
||||
if (ThreadedRenderer.sTrimForeground) {
|
||||
doTrimForeground();
|
||||
}
|
||||
|
||||
// If we don't have any views anymore in our process, we no longer need the
|
||||
// InsetsAnimationThread to save some resources.
|
||||
@@ -543,65 +538,9 @@ public final class WindowManagerGlobal {
|
||||
return index;
|
||||
}
|
||||
|
||||
public static boolean shouldDestroyEglContext(int trimLevel) {
|
||||
// On low-end gfx devices we trim when memory is moderate;
|
||||
// on high-end devices we do this when low.
|
||||
if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
|
||||
return true;
|
||||
}
|
||||
if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
|
||||
&& !ActivityManager.isHighEndGfx()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
|
||||
public void trimMemory(int level) {
|
||||
|
||||
if (shouldDestroyEglContext(level)) {
|
||||
// Destroy all hardware surfaces and resources associated to
|
||||
// known windows
|
||||
synchronized (mLock) {
|
||||
for (int i = mRoots.size() - 1; i >= 0; --i) {
|
||||
mRoots.get(i).destroyHardwareResources();
|
||||
}
|
||||
}
|
||||
// Force a full memory flush
|
||||
level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
|
||||
}
|
||||
|
||||
ThreadedRenderer.trimMemory(level);
|
||||
|
||||
if (ThreadedRenderer.sTrimForeground) {
|
||||
doTrimForeground();
|
||||
}
|
||||
}
|
||||
|
||||
public static void trimForeground() {
|
||||
if (ThreadedRenderer.sTrimForeground) {
|
||||
WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
|
||||
wm.doTrimForeground();
|
||||
}
|
||||
}
|
||||
|
||||
private void doTrimForeground() {
|
||||
boolean hasVisibleWindows = false;
|
||||
synchronized (mLock) {
|
||||
for (int i = mRoots.size() - 1; i >= 0; --i) {
|
||||
final ViewRootImpl root = mRoots.get(i);
|
||||
if (root.mView != null && root.getHostVisibility() == View.VISIBLE
|
||||
&& root.mAttachInfo.mThreadedRenderer != null) {
|
||||
hasVisibleWindows = true;
|
||||
} else {
|
||||
root.destroyHardwareResources();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasVisibleWindows) {
|
||||
ThreadedRenderer.trimMemory(
|
||||
ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpGfxInfo(FileDescriptor fd, String[] args) {
|
||||
|
||||
@@ -47,9 +47,7 @@ import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import sun.misc.Cleaner;
|
||||
|
||||
@@ -1141,6 +1139,16 @@ public class HardwareRenderer {
|
||||
ProcessInitializer.sInstance.setContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the current process is a system or persistent process. Used to influence
|
||||
* the chosen memory usage policy.
|
||||
*
|
||||
* @hide
|
||||
**/
|
||||
public static void setIsSystemOrPersistent() {
|
||||
nSetIsSystemOrPersistent(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if HardwareRender will produce output.
|
||||
*
|
||||
@@ -1204,30 +1212,6 @@ public class HardwareRenderer {
|
||||
private static class ProcessInitializer {
|
||||
static ProcessInitializer sInstance = new ProcessInitializer();
|
||||
|
||||
// Magic values from android/data_space.h
|
||||
private static final int INTERNAL_DATASPACE_SRGB = 142671872;
|
||||
private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
|
||||
private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
|
||||
|
||||
private enum Dataspace {
|
||||
DISPLAY_P3(ColorSpace.Named.DISPLAY_P3, INTERNAL_DATASPACE_DISPLAY_P3),
|
||||
SCRGB(ColorSpace.Named.EXTENDED_SRGB, INTERNAL_DATASPACE_SCRGB),
|
||||
SRGB(ColorSpace.Named.SRGB, INTERNAL_DATASPACE_SRGB);
|
||||
|
||||
private final ColorSpace.Named mColorSpace;
|
||||
private final int mNativeDataspace;
|
||||
Dataspace(ColorSpace.Named colorSpace, int nativeDataspace) {
|
||||
this.mColorSpace = colorSpace;
|
||||
this.mNativeDataspace = nativeDataspace;
|
||||
}
|
||||
|
||||
static Optional<Dataspace> find(ColorSpace colorSpace) {
|
||||
return Stream.of(Dataspace.values())
|
||||
.filter(d -> ColorSpace.get(d.mColorSpace).equals(colorSpace))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean mInitialized = false;
|
||||
private boolean mDisplayInitialized = false;
|
||||
|
||||
@@ -1296,6 +1280,7 @@ public class HardwareRenderer {
|
||||
initDisplayInfo();
|
||||
|
||||
nSetIsHighEndGfx(ActivityManager.isHighEndGfx());
|
||||
nSetIsLowRam(ActivityManager.isLowRamDeviceStatic());
|
||||
// Defensively clear out the context in case we were passed a context that can leak
|
||||
// if we live longer than it, e.g. an activity context.
|
||||
mContext = null;
|
||||
@@ -1314,26 +1299,55 @@ public class HardwareRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
|
||||
if (display == null) {
|
||||
final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
|
||||
if (defaultDisplay == null) {
|
||||
Log.d(LOG_TAG, "Failed to find default display for display-based configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
Dataspace wideColorDataspace =
|
||||
Optional.ofNullable(display.getPreferredWideGamutColorSpace())
|
||||
.flatMap(Dataspace::find)
|
||||
// Default to SRGB if the display doesn't support wide color
|
||||
.orElse(Dataspace.SRGB);
|
||||
final Display[] allDisplays = dm.getDisplays();
|
||||
if (allDisplays.length == 0) {
|
||||
Log.d(LOG_TAG, "Failed to query displays");
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the physical screen dimensions from the active display mode
|
||||
// Strictly speaking the screen resolution may not always be constant - it is for
|
||||
// sizing the font cache for the underlying rendering thread. Since it's a
|
||||
// heuristic we don't need to be always 100% correct.
|
||||
Mode activeMode = display.getMode();
|
||||
nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
|
||||
display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
|
||||
display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
|
||||
final Mode activeMode = defaultDisplay.getMode();
|
||||
final ColorSpace defaultWideColorSpace =
|
||||
defaultDisplay.getPreferredWideGamutColorSpace();
|
||||
int wideColorDataspace = defaultWideColorSpace != null
|
||||
? defaultWideColorSpace.getDataSpace() : 0;
|
||||
// largest width & height are used to size the default HWUI cache sizes. So find the
|
||||
// largest display resolution we could encounter & use that as the guidance. The actual
|
||||
// memory policy in play will interpret these values differently.
|
||||
int largestWidth = activeMode.getPhysicalWidth();
|
||||
int largestHeight = activeMode.getPhysicalHeight();
|
||||
|
||||
for (int i = 0; i < allDisplays.length; i++) {
|
||||
final Display display = allDisplays[i];
|
||||
// Take the first wide gamut dataspace as the source of truth
|
||||
// Possibly should do per-HardwareRenderer wide gamut dataspace so we can use the
|
||||
// target display's ideal instead
|
||||
if (wideColorDataspace == 0) {
|
||||
ColorSpace cs = display.getPreferredWideGamutColorSpace();
|
||||
if (cs != null) {
|
||||
wideColorDataspace = cs.getDataSpace();
|
||||
}
|
||||
}
|
||||
Mode[] modes = display.getSupportedModes();
|
||||
for (int j = 0; j < modes.length; j++) {
|
||||
Mode mode = modes[j];
|
||||
int width = mode.getPhysicalWidth();
|
||||
int height = mode.getPhysicalHeight();
|
||||
if ((width * height) > (largestWidth * largestHeight)) {
|
||||
largestWidth = width;
|
||||
largestHeight = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nInitDisplayInfo(largestWidth, largestHeight, defaultDisplay.getRefreshRate(),
|
||||
wideColorDataspace, defaultDisplay.getAppVsyncOffsetNanos(),
|
||||
defaultDisplay.getPresentationDeadlineNanos());
|
||||
|
||||
mDisplayInitialized = true;
|
||||
}
|
||||
@@ -1418,6 +1432,10 @@ public class HardwareRenderer {
|
||||
|
||||
private static native void nSetIsHighEndGfx(boolean isHighEndGfx);
|
||||
|
||||
private static native void nSetIsLowRam(boolean isLowRam);
|
||||
|
||||
private static native void nSetIsSystemOrPersistent(boolean isSystemOrPersistent);
|
||||
|
||||
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
|
||||
|
||||
private static native void nDestroy(long nativeProxy, long rootRenderNode);
|
||||
|
||||
@@ -521,6 +521,7 @@ cc_defaults {
|
||||
"Interpolator.cpp",
|
||||
"LightingInfo.cpp",
|
||||
"Matrix.cpp",
|
||||
"MemoryPolicy.cpp",
|
||||
"PathParser.cpp",
|
||||
"Properties.cpp",
|
||||
"PropertyValuesAnimatorSet.cpp",
|
||||
|
||||
@@ -93,12 +93,14 @@ void DeviceInfo::setWideColorDataspace(ADataSpace dataspace) {
|
||||
case ADATASPACE_SCRGB:
|
||||
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
|
||||
break;
|
||||
default:
|
||||
ALOGW("Unknown dataspace %d", dataspace);
|
||||
// Treat unknown dataspaces as sRGB, so fall through
|
||||
[[fallthrough]];
|
||||
case ADATASPACE_SRGB:
|
||||
// when sRGB is returned, it means wide color gamut is not supported.
|
||||
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
|
||||
break;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
static constexpr auto kThreadTimeout = 60000_ms;
|
||||
|
||||
class AHBUploader;
|
||||
// This helper uploader classes allows us to upload using either EGL or Vulkan using the same
|
||||
// interface.
|
||||
@@ -80,7 +82,7 @@ public:
|
||||
}
|
||||
|
||||
void postIdleTimeoutCheck() {
|
||||
mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
|
||||
mUploadThread->queue().postDelayed(kThreadTimeout, [this]() { this->idleTimeoutCheck(); });
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -97,7 +99,7 @@ private:
|
||||
|
||||
bool shouldTimeOutLocked() {
|
||||
nsecs_t durationSince = systemTime() - mLastUpload;
|
||||
return durationSince > 2000_ms;
|
||||
return durationSince > kThreadTimeout;
|
||||
}
|
||||
|
||||
void idleTimeoutCheck() {
|
||||
|
||||
69
libs/hwui/MemoryPolicy.cpp
Normal file
69
libs/hwui/MemoryPolicy.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
#include "MemoryPolicy.h"
|
||||
|
||||
#include <android-base/properties.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "Properties.h"
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
constexpr static MemoryPolicy sDefaultMemoryPolicy;
|
||||
constexpr static MemoryPolicy sPersistentOrSystemPolicy{
|
||||
.contextTimeout = 10_s,
|
||||
.useAlternativeUiHidden = true,
|
||||
};
|
||||
constexpr static MemoryPolicy sLowRamPolicy{
|
||||
.useAlternativeUiHidden = true,
|
||||
.purgeScratchOnly = false,
|
||||
};
|
||||
constexpr static MemoryPolicy sExtremeLowRam{
|
||||
.initialMaxSurfaceAreaScale = 0.2f,
|
||||
.surfaceSizeMultiplier = 5 * 4.0f,
|
||||
.backgroundRetentionPercent = 0.2f,
|
||||
.contextTimeout = 5_s,
|
||||
.minimumResourceRetention = 1_s,
|
||||
.useAlternativeUiHidden = true,
|
||||
.purgeScratchOnly = false,
|
||||
.releaseContextOnStoppedOnly = true,
|
||||
};
|
||||
|
||||
const MemoryPolicy& loadMemoryPolicy() {
|
||||
if (Properties::isSystemOrPersistent) {
|
||||
return sPersistentOrSystemPolicy;
|
||||
}
|
||||
std::string memoryPolicy = base::GetProperty(PROPERTY_MEMORY_POLICY, "");
|
||||
if (memoryPolicy == "default") {
|
||||
return sDefaultMemoryPolicy;
|
||||
}
|
||||
if (memoryPolicy == "lowram") {
|
||||
return sLowRamPolicy;
|
||||
}
|
||||
if (memoryPolicy == "extremelowram") {
|
||||
return sExtremeLowRam;
|
||||
}
|
||||
|
||||
if (Properties::isLowRam) {
|
||||
return sLowRamPolicy;
|
||||
}
|
||||
return sDefaultMemoryPolicy;
|
||||
}
|
||||
|
||||
} // namespace android::uirenderer
|
||||
62
libs/hwui/MemoryPolicy.h
Normal file
62
libs/hwui/MemoryPolicy.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 "utils/TimeUtils.h"
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
// Values mirror those from ComponentCallbacks2.java
|
||||
enum class TrimLevel {
|
||||
COMPLETE = 80,
|
||||
MODERATE = 60,
|
||||
BACKGROUND = 40,
|
||||
UI_HIDDEN = 20,
|
||||
RUNNING_CRITICAL = 15,
|
||||
RUNNING_LOW = 10,
|
||||
RUNNING_MODERATE = 5,
|
||||
};
|
||||
|
||||
struct MemoryPolicy {
|
||||
// The initial scale factor applied to the display resolution. The default is 1, but
|
||||
// lower values may be used to start with a smaller initial cache size. The cache will
|
||||
// be adjusted if larger frames are actually rendered
|
||||
float initialMaxSurfaceAreaScale = 1.0f;
|
||||
// The foreground cache size multiplier. The surface area of the screen will be multiplied
|
||||
// by this
|
||||
float surfaceSizeMultiplier = 12.0f * 4.0f;
|
||||
// How much of the foreground cache size should be preserved when going into the background
|
||||
float backgroundRetentionPercent = 0.5f;
|
||||
// How long after the last renderer goes away before the GPU context is released. A value
|
||||
// of 0 means only drop the context on background TRIM signals
|
||||
nsecs_t contextTimeout = 0_ms;
|
||||
// The minimum amount of time to hold onto items in the resource cache
|
||||
// The actual time used will be the max of this & when frames were actually rendered
|
||||
nsecs_t minimumResourceRetention = 10_s;
|
||||
// If false, use only TRIM_UI_HIDDEN to drive background cache limits;
|
||||
// If true, use all signals (such as all contexts are stopped) to drive the limits
|
||||
bool useAlternativeUiHidden = false;
|
||||
// Whether or not to only purge scratch resources when triggering UI Hidden or background
|
||||
// collection
|
||||
bool purgeScratchOnly = true;
|
||||
// EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped
|
||||
bool releaseContextOnStoppedOnly = false;
|
||||
};
|
||||
|
||||
const MemoryPolicy& loadMemoryPolicy();
|
||||
|
||||
} // namespace android::uirenderer
|
||||
@@ -87,6 +87,10 @@ int Properties::targetCpuTimePercentage = 70;
|
||||
|
||||
bool Properties::enableWebViewOverlays = true;
|
||||
|
||||
bool Properties::isHighEndGfx = true;
|
||||
bool Properties::isLowRam = false;
|
||||
bool Properties::isSystemOrPersistent = false;
|
||||
|
||||
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
|
||||
|
||||
DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
|
||||
|
||||
@@ -193,6 +193,8 @@ enum DebugLevel {
|
||||
*/
|
||||
#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
|
||||
|
||||
#define PROPERTY_MEMORY_POLICY "debug.hwui.app_memory_policy"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Misc
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -292,16 +294,27 @@ public:
|
||||
|
||||
static bool enableWebViewOverlays;
|
||||
|
||||
static bool isHighEndGfx;
|
||||
static bool isLowRam;
|
||||
static bool isSystemOrPersistent;
|
||||
|
||||
static StretchEffectBehavior getStretchEffectBehavior() {
|
||||
return stretchEffectBehavior;
|
||||
}
|
||||
|
||||
static void setIsHighEndGfx(bool isHighEndGfx) {
|
||||
Properties::isHighEndGfx = isHighEndGfx;
|
||||
stretchEffectBehavior = isHighEndGfx ?
|
||||
StretchEffectBehavior::ShaderHWUI :
|
||||
StretchEffectBehavior::UniformScale;
|
||||
}
|
||||
|
||||
static void setIsLowRam(bool isLowRam) { Properties::isLowRam = isLowRam; }
|
||||
|
||||
static void setIsSystemOrPersistent(bool isSystemOrPersistent) {
|
||||
Properties::isSystemOrPersistent = isSystemOrPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for testing. Typical configuration of stretch behavior is done
|
||||
* through setIsHighEndGfx
|
||||
|
||||
@@ -271,6 +271,16 @@ static void android_view_ThreadedRenderer_setIsHighEndGfx(JNIEnv* env, jobject c
|
||||
Properties::setIsHighEndGfx(jIsHighEndGfx);
|
||||
}
|
||||
|
||||
static void android_view_ThreadedRenderer_setIsLowRam(JNIEnv* env, jobject clazz,
|
||||
jboolean isLowRam) {
|
||||
Properties::setIsLowRam(isLowRam);
|
||||
}
|
||||
|
||||
static void android_view_ThreadedRenderer_setIsSystemOrPersistent(JNIEnv* env, jobject clazz,
|
||||
jboolean isSystemOrPersistent) {
|
||||
Properties::setIsSystemOrPersistent(isSystemOrPersistent);
|
||||
}
|
||||
|
||||
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
|
||||
jlong proxyPtr, jlongArray frameInfo,
|
||||
jint frameInfoSize) {
|
||||
@@ -949,6 +959,9 @@ static const JNINativeMethod gMethods[] = {
|
||||
{"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
|
||||
{"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
|
||||
{"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx},
|
||||
{"nSetIsLowRam", "(Z)V", (void*)android_view_ThreadedRenderer_setIsLowRam},
|
||||
{"nSetIsSystemOrPersistent", "(Z)V",
|
||||
(void*)android_view_ThreadedRenderer_setIsSystemOrPersistent},
|
||||
{"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
|
||||
{"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
|
||||
{"nRegisterAnimatingRenderNode", "(JJ)V",
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
|
||||
#include "CacheManager.h"
|
||||
|
||||
#include <GrContextOptions.h>
|
||||
#include <SkExecutor.h>
|
||||
#include <SkGraphics.h>
|
||||
#include <SkMathPriv.h>
|
||||
#include <math.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "CanvasContext.h"
|
||||
#include "DeviceInfo.h"
|
||||
#include "Layer.h"
|
||||
#include "Properties.h"
|
||||
@@ -25,40 +35,34 @@
|
||||
#include "pipeline/skia/SkiaMemoryTracer.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
#include "thread/CommonPool.h"
|
||||
#include <utils/Trace.h>
|
||||
|
||||
#include <GrContextOptions.h>
|
||||
#include <SkExecutor.h>
|
||||
#include <SkGraphics.h>
|
||||
#include <SkMathPriv.h>
|
||||
#include <math.h>
|
||||
#include <set>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
|
||||
// This multiplier was selected based on historical review of cache sizes relative
|
||||
// to the screen resolution. This is meant to be a conservative default based on
|
||||
// that analysis. The 4.0f is used because the default pixel format is assumed to
|
||||
// be ARGB_8888.
|
||||
#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
|
||||
#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
|
||||
CacheManager::CacheManager(RenderThread& thread)
|
||||
: mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {
|
||||
mMaxSurfaceArea = static_cast<size_t>((DeviceInfo::getWidth() * DeviceInfo::getHeight()) *
|
||||
mMemoryPolicy.initialMaxSurfaceAreaScale);
|
||||
setupCacheLimits();
|
||||
}
|
||||
|
||||
void CacheManager::setupCacheLimits() {
|
||||
mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
|
||||
mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
|
||||
// This sets the maximum size for a single texture atlas in the GPU font cache. If
|
||||
// necessary, the cache can allocate additional textures that are counted against the
|
||||
// total cache limits provided to Skia.
|
||||
mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
|
||||
// This sets the maximum size of the CPU font cache to be at least the same size as the
|
||||
// total number of GPU font caches (i.e. 4 separate GPU atlases).
|
||||
mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
|
||||
mBackgroundCpuFontCacheBytes = mMaxCpuFontCacheBytes * mMemoryPolicy.backgroundRetentionPercent;
|
||||
|
||||
CacheManager::CacheManager()
|
||||
: mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight())
|
||||
, mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER)
|
||||
, mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE)
|
||||
// This sets the maximum size for a single texture atlas in the GPU font cache. If
|
||||
// necessary, the cache can allocate additional textures that are counted against the
|
||||
// total cache limits provided to Skia.
|
||||
, mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea))
|
||||
// This sets the maximum size of the CPU font cache to be at least the same size as the
|
||||
// total number of GPU font caches (i.e. 4 separate GPU atlases).
|
||||
, mMaxCpuFontCacheBytes(
|
||||
std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit()))
|
||||
, mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
|
||||
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
|
||||
if (mGrContext) {
|
||||
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::reset(sk_sp<GrDirectContext> context) {
|
||||
@@ -69,6 +73,7 @@ void CacheManager::reset(sk_sp<GrDirectContext> context) {
|
||||
if (context) {
|
||||
mGrContext = std::move(context);
|
||||
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
|
||||
mLastDeferredCleanup = systemTime(CLOCK_MONOTONIC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +101,7 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void
|
||||
contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
|
||||
}
|
||||
|
||||
void CacheManager::trimMemory(TrimMemoryMode mode) {
|
||||
void CacheManager::trimMemory(TrimLevel mode) {
|
||||
if (!mGrContext) {
|
||||
return;
|
||||
}
|
||||
@@ -104,21 +109,28 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
|
||||
// flush and submit all work to the gpu and wait for it to finish
|
||||
mGrContext->flushAndSubmit(/*syncCpu=*/true);
|
||||
|
||||
if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) {
|
||||
mode = TrimLevel::COMPLETE;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case TrimMemoryMode::Complete:
|
||||
case TrimLevel::COMPLETE:
|
||||
mGrContext->freeGpuResources();
|
||||
SkGraphics::PurgeAllCaches();
|
||||
mRenderThread.destroyRenderingContext();
|
||||
break;
|
||||
case TrimMemoryMode::UiHidden:
|
||||
case TrimLevel::UI_HIDDEN:
|
||||
// Here we purge all the unlocked scratch resources and then toggle the resources cache
|
||||
// limits between the background and max amounts. This causes the unlocked resources
|
||||
// that have persistent data to be purged in LRU order.
|
||||
mGrContext->purgeUnlockedResources(true);
|
||||
mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
|
||||
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
|
||||
SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
|
||||
mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly);
|
||||
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
|
||||
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,11 +159,29 @@ void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
|
||||
}
|
||||
|
||||
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
|
||||
log.appendFormat(R"(Memory policy:
|
||||
Max surface area: %zu
|
||||
Max resource usage: %.2fMB (x%.0f)
|
||||
Background retention: %.0f%% (altUiHidden = %s)
|
||||
)",
|
||||
mMaxSurfaceArea, mMaxResourceBytes / 1000000.f,
|
||||
mMemoryPolicy.surfaceSizeMultiplier,
|
||||
mMemoryPolicy.backgroundRetentionPercent * 100.0f,
|
||||
mMemoryPolicy.useAlternativeUiHidden ? "true" : "false");
|
||||
if (Properties::isSystemOrPersistent) {
|
||||
log.appendFormat(" IsSystemOrPersistent\n");
|
||||
}
|
||||
log.appendFormat(" GPU Context timeout: %" PRIu64 "\n", ns2s(mMemoryPolicy.contextTimeout));
|
||||
size_t stoppedContexts = 0;
|
||||
for (auto context : mCanvasContexts) {
|
||||
if (context->isStopped()) stoppedContexts++;
|
||||
}
|
||||
log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts);
|
||||
|
||||
if (!mGrContext) {
|
||||
log.appendFormat("No valid cache instance.\n");
|
||||
log.appendFormat("No GPU context.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
|
||||
{"skia/sk_resource_cache/bitmap_", "Bitmaps"},
|
||||
{"skia/sk_resource_cache/rrect-blur_", "Masks"},
|
||||
@@ -199,6 +229,8 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
|
||||
}
|
||||
|
||||
void CacheManager::onFrameCompleted() {
|
||||
cancelDestroyContext();
|
||||
mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
|
||||
if (ATRACE_ENABLED()) {
|
||||
static skiapipeline::ATraceMemoryDump tracer;
|
||||
tracer.startFrame();
|
||||
@@ -210,11 +242,82 @@ void CacheManager::onFrameCompleted() {
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::performDeferredCleanup(nsecs_t cleanupOlderThanMillis) {
|
||||
if (mGrContext) {
|
||||
mGrContext->performDeferredCleanup(
|
||||
std::chrono::milliseconds(cleanupOlderThanMillis),
|
||||
/* scratchResourcesOnly */true);
|
||||
void CacheManager::onThreadIdle() {
|
||||
if (!mGrContext || mFrameCompletions.size() == 0) return;
|
||||
|
||||
const nsecs_t now = systemTime(CLOCK_MONOTONIC);
|
||||
// Rate limiting
|
||||
if ((now - mLastDeferredCleanup) < 25_ms) {
|
||||
mLastDeferredCleanup = now;
|
||||
const nsecs_t frameCompleteNanos = mFrameCompletions[0];
|
||||
const nsecs_t frameDiffNanos = now - frameCompleteNanos;
|
||||
const nsecs_t cleanupMillis =
|
||||
ns2ms(std::max(frameDiffNanos, mMemoryPolicy.minimumResourceRetention));
|
||||
mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
|
||||
mMemoryPolicy.purgeScratchOnly);
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::scheduleDestroyContext() {
|
||||
if (mMemoryPolicy.contextTimeout > 0) {
|
||||
mRenderThread.queue().postDelayed(mMemoryPolicy.contextTimeout,
|
||||
[this, genId = mGenerationId] {
|
||||
if (mGenerationId != genId) return;
|
||||
// GenID should have already stopped this, but just in
|
||||
// case
|
||||
if (!areAllContextsStopped()) return;
|
||||
mRenderThread.destroyRenderingContext();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::cancelDestroyContext() {
|
||||
if (mIsDestructionPending) {
|
||||
mIsDestructionPending = false;
|
||||
mGenerationId++;
|
||||
}
|
||||
}
|
||||
|
||||
bool CacheManager::areAllContextsStopped() {
|
||||
for (auto context : mCanvasContexts) {
|
||||
if (!context->isStopped()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CacheManager::checkUiHidden() {
|
||||
if (!mGrContext) return;
|
||||
|
||||
if (mMemoryPolicy.useAlternativeUiHidden && areAllContextsStopped()) {
|
||||
trimMemory(TrimLevel::UI_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::registerCanvasContext(CanvasContext* context) {
|
||||
mCanvasContexts.push_back(context);
|
||||
cancelDestroyContext();
|
||||
}
|
||||
|
||||
void CacheManager::unregisterCanvasContext(CanvasContext* context) {
|
||||
std::erase(mCanvasContexts, context);
|
||||
checkUiHidden();
|
||||
if (mCanvasContexts.empty()) {
|
||||
scheduleDestroyContext();
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::onContextStopped(CanvasContext* context) {
|
||||
checkUiHidden();
|
||||
if (mMemoryPolicy.releaseContextOnStoppedOnly && areAllContextsStopped()) {
|
||||
scheduleDestroyContext();
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::notifyNextFrameSize(int width, int height) {
|
||||
int frameArea = width * height;
|
||||
if (frameArea > mMaxSurfaceArea) {
|
||||
mMaxSurfaceArea = frameArea;
|
||||
setupCacheLimits();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
#endif
|
||||
#include <SkSurface.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "MemoryPolicy.h"
|
||||
#include "utils/RingBuffer.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
|
||||
namespace android {
|
||||
@@ -35,17 +39,15 @@ class RenderState;
|
||||
|
||||
namespace renderthread {
|
||||
|
||||
class IRenderPipeline;
|
||||
class RenderThread;
|
||||
class CanvasContext;
|
||||
|
||||
class CacheManager {
|
||||
public:
|
||||
enum class TrimMemoryMode { Complete, UiHidden };
|
||||
|
||||
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
|
||||
void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
|
||||
#endif
|
||||
void trimMemory(TrimMemoryMode mode);
|
||||
void trimMemory(TrimLevel mode);
|
||||
void trimStaleResources();
|
||||
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
|
||||
void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
|
||||
@@ -53,30 +55,50 @@ public:
|
||||
size_t getCacheSize() const { return mMaxResourceBytes; }
|
||||
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
|
||||
void onFrameCompleted();
|
||||
void notifyNextFrameSize(int width, int height);
|
||||
|
||||
void performDeferredCleanup(nsecs_t cleanupOlderThanMillis);
|
||||
void onThreadIdle();
|
||||
|
||||
void registerCanvasContext(CanvasContext* context);
|
||||
void unregisterCanvasContext(CanvasContext* context);
|
||||
void onContextStopped(CanvasContext* context);
|
||||
|
||||
private:
|
||||
friend class RenderThread;
|
||||
|
||||
explicit CacheManager();
|
||||
explicit CacheManager(RenderThread& thread);
|
||||
void setupCacheLimits();
|
||||
bool areAllContextsStopped();
|
||||
void checkUiHidden();
|
||||
void scheduleDestroyContext();
|
||||
void cancelDestroyContext();
|
||||
|
||||
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
|
||||
void reset(sk_sp<GrDirectContext> grContext);
|
||||
#endif
|
||||
void destroy();
|
||||
|
||||
const size_t mMaxSurfaceArea;
|
||||
RenderThread& mRenderThread;
|
||||
const MemoryPolicy& mMemoryPolicy;
|
||||
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
|
||||
sk_sp<GrDirectContext> mGrContext;
|
||||
#endif
|
||||
|
||||
const size_t mMaxResourceBytes;
|
||||
const size_t mBackgroundResourceBytes;
|
||||
size_t mMaxSurfaceArea = 0;
|
||||
|
||||
const size_t mMaxGpuFontAtlasBytes;
|
||||
const size_t mMaxCpuFontCacheBytes;
|
||||
const size_t mBackgroundCpuFontCacheBytes;
|
||||
size_t mMaxResourceBytes = 0;
|
||||
size_t mBackgroundResourceBytes = 0;
|
||||
|
||||
size_t mMaxGpuFontAtlasBytes = 0;
|
||||
size_t mMaxCpuFontCacheBytes = 0;
|
||||
size_t mBackgroundCpuFontCacheBytes = 0;
|
||||
|
||||
std::vector<CanvasContext*> mCanvasContexts;
|
||||
RingBuffer<uint64_t, 100> mFrameCompletions;
|
||||
|
||||
nsecs_t mLastDeferredCleanup = 0;
|
||||
bool mIsDestructionPending = false;
|
||||
uint32_t mGenerationId = 0;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
|
||||
@@ -42,9 +42,6 @@
|
||||
#include "utils/GLUtils.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
|
||||
#define TRIM_MEMORY_COMPLETE 80
|
||||
#define TRIM_MEMORY_UI_HIDDEN 20
|
||||
|
||||
#define LOG_FRAMETIME_MMA 0
|
||||
|
||||
#if LOG_FRAMETIME_MMA
|
||||
@@ -122,6 +119,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
|
||||
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
|
||||
, mContentDrawBounds(0, 0, 0, 0)
|
||||
, mRenderPipeline(std::move(renderPipeline)) {
|
||||
mRenderThread.cacheManager().registerCanvasContext(this);
|
||||
rootRenderNode->makeRoot();
|
||||
mRenderNodes.emplace_back(rootRenderNode);
|
||||
mProfiler.setDensity(DeviceInfo::getDensity());
|
||||
@@ -133,6 +131,7 @@ CanvasContext::~CanvasContext() {
|
||||
node->clearRoot();
|
||||
}
|
||||
mRenderNodes.clear();
|
||||
mRenderThread.cacheManager().unregisterCanvasContext(this);
|
||||
}
|
||||
|
||||
void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
|
||||
@@ -154,6 +153,7 @@ void CanvasContext::destroy() {
|
||||
freePrefetchedLayers();
|
||||
destroyHardwareResources();
|
||||
mAnimationContext->destroy();
|
||||
mRenderThread.cacheManager().onContextStopped(this);
|
||||
}
|
||||
|
||||
static void setBufferCount(ANativeWindow* window) {
|
||||
@@ -251,6 +251,7 @@ void CanvasContext::setStopped(bool stopped) {
|
||||
mGenerationID++;
|
||||
mRenderThread.removeFrameCallback(this);
|
||||
mRenderPipeline->onStop();
|
||||
mRenderThread.cacheManager().onContextStopped(this);
|
||||
} else if (mIsDirty && hasSurface()) {
|
||||
mRenderThread.postFrameCallback(this);
|
||||
}
|
||||
@@ -461,7 +462,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
|
||||
}
|
||||
|
||||
void CanvasContext::stopDrawing() {
|
||||
cleanupResources();
|
||||
mRenderThread.removeFrameCallback(this);
|
||||
mAnimationContext->pauseAnimators();
|
||||
mGenerationID++;
|
||||
@@ -648,25 +648,10 @@ nsecs_t CanvasContext::draw() {
|
||||
}
|
||||
}
|
||||
|
||||
cleanupResources();
|
||||
mRenderThread.cacheManager().onFrameCompleted();
|
||||
return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
|
||||
}
|
||||
|
||||
void CanvasContext::cleanupResources() {
|
||||
auto& tracker = mJankTracker.frames();
|
||||
auto size = tracker.size();
|
||||
auto capacity = tracker.capacity();
|
||||
if (size == capacity) {
|
||||
nsecs_t nowNanos = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
nsecs_t frameCompleteNanos =
|
||||
tracker[0].get(FrameInfoIndex::FrameCompleted);
|
||||
nsecs_t frameDiffNanos = nowNanos - frameCompleteNanos;
|
||||
nsecs_t cleanupMillis = ns2ms(std::max(frameDiffNanos, 10_s));
|
||||
mRenderThread.cacheManager().performDeferredCleanup(cleanupMillis);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContext::reportMetricsWithPresentTime() {
|
||||
{ // acquire lock
|
||||
std::scoped_lock lock(mFrameMetricsReporterMutex);
|
||||
@@ -790,6 +775,7 @@ SkISize CanvasContext::getNextFrameSize() const {
|
||||
SkISize size;
|
||||
size.fWidth = ANativeWindow_getWidth(anw);
|
||||
size.fHeight = ANativeWindow_getHeight(anw);
|
||||
mRenderThread.cacheManager().notifyNextFrameSize(size.fWidth, size.fHeight);
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -868,18 +854,6 @@ void CanvasContext::destroyHardwareResources() {
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContext::trimMemory(RenderThread& thread, int level) {
|
||||
ATRACE_CALL();
|
||||
if (!thread.getGrContext()) return;
|
||||
ATRACE_CALL();
|
||||
if (level >= TRIM_MEMORY_COMPLETE) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
thread.destroyRenderingContext();
|
||||
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
|
||||
}
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
|
||||
return mRenderPipeline->createTextureLayer();
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ public:
|
||||
void setSurfaceControl(ASurfaceControl* surfaceControl);
|
||||
bool pauseSurface();
|
||||
void setStopped(bool stopped);
|
||||
bool isStopped() { return mStopped || !hasSurface(); }
|
||||
bool hasSurface() const { return mNativeSurface.get(); }
|
||||
void allocateBuffers();
|
||||
|
||||
@@ -148,7 +149,6 @@ public:
|
||||
void markLayerInUse(RenderNode* node);
|
||||
|
||||
void destroyHardwareResources();
|
||||
static void trimMemory(RenderThread& thread, int level);
|
||||
|
||||
DeferredLayerUpdater* createTextureLayer();
|
||||
|
||||
@@ -330,8 +330,6 @@ private:
|
||||
|
||||
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
|
||||
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
|
||||
|
||||
void cleanupResources();
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
|
||||
@@ -196,7 +196,8 @@ void RenderProxy::trimMemory(int level) {
|
||||
// Avoid creating a RenderThread to do a trimMemory.
|
||||
if (RenderThread::hasInstance()) {
|
||||
RenderThread& thread = RenderThread::getInstance();
|
||||
thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); });
|
||||
const auto trimLevel = static_cast<TrimLevel>(level);
|
||||
thread.queue().post([&thread, trimLevel]() { thread.trimMemory(trimLevel); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +206,7 @@ void RenderProxy::purgeCaches() {
|
||||
RenderThread& thread = RenderThread::getInstance();
|
||||
thread.queue().post([&thread]() {
|
||||
if (thread.getGrContext()) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
thread.cacheManager().trimMemory(TrimLevel::COMPLETE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ void RenderThread::initThreadLocals() {
|
||||
mEglManager = new EglManager();
|
||||
mRenderState = new RenderState(*this);
|
||||
mVkManager = VulkanManager::getInstance();
|
||||
mCacheManager = new CacheManager();
|
||||
mCacheManager = new CacheManager(*this);
|
||||
}
|
||||
|
||||
void RenderThread::setupFrameInterval() {
|
||||
@@ -453,6 +453,8 @@ bool RenderThread::threadLoop() {
|
||||
// next vsync (oops), so none of the callbacks are run.
|
||||
requestVsync();
|
||||
}
|
||||
|
||||
mCacheManager->onThreadIdle();
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -502,6 +504,11 @@ void RenderThread::preload() {
|
||||
HardwareBitmapUploader::initialize();
|
||||
}
|
||||
|
||||
void RenderThread::trimMemory(TrimLevel level) {
|
||||
ATRACE_CALL();
|
||||
cacheManager().trimMemory(level);
|
||||
}
|
||||
|
||||
} /* namespace renderthread */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
#ifndef RENDERTHREAD_H_
|
||||
#define RENDERTHREAD_H_
|
||||
|
||||
#include <surface_control_private.h>
|
||||
#include <GrDirectContext.h>
|
||||
#include <SkBitmap.h>
|
||||
#include <cutils/compiler.h>
|
||||
#include <private/android/choreographer.h>
|
||||
#include <surface_control_private.h>
|
||||
#include <thread/ThreadBase.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/Thread.h>
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "CacheManager.h"
|
||||
#include "MemoryPolicy.h"
|
||||
#include "ProfileDataContainer.h"
|
||||
#include "RenderTask.h"
|
||||
#include "TimeLord.h"
|
||||
@@ -172,6 +173,8 @@ public:
|
||||
return mASurfaceControlFunctions;
|
||||
}
|
||||
|
||||
void trimMemory(TrimLevel level);
|
||||
|
||||
/**
|
||||
* isCurrent provides a way to query, if the caller is running on
|
||||
* the render thread.
|
||||
|
||||
@@ -58,7 +58,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
|
||||
ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
|
||||
|
||||
// attempt to trim all memory while we still hold strong refs
|
||||
renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
|
||||
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
|
||||
|
||||
// free the surfaces
|
||||
@@ -75,11 +75,11 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
|
||||
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
|
||||
|
||||
// UI hidden and make sure only some got purged (unique should remain)
|
||||
renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
|
||||
renderThread.cacheManager().trimMemory(TrimLevel::UI_HIDDEN);
|
||||
ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
|
||||
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
|
||||
|
||||
// complete and make sure all get purged
|
||||
renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
|
||||
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user