Merge "Implement CacheManager for the Skia pipelines."
This commit is contained in:
committed by
Android (Google) Code Review
commit
12741cec2d
@@ -141,6 +141,7 @@ cc_defaults {
|
||||
"renderstate/Scissor.cpp",
|
||||
"renderstate/Stencil.cpp",
|
||||
"renderstate/TextureState.cpp",
|
||||
"renderthread/CacheManager.cpp",
|
||||
"renderthread/CanvasContext.cpp",
|
||||
"renderthread/OpenGLPipeline.cpp",
|
||||
"renderthread/DrawFrameTask.cpp",
|
||||
@@ -300,6 +301,7 @@ cc_test {
|
||||
"tests/unit/BakedOpRendererTests.cpp",
|
||||
"tests/unit/BakedOpStateTests.cpp",
|
||||
"tests/unit/BitmapTests.cpp",
|
||||
"tests/unit/CacheManagerTests.cpp",
|
||||
"tests/unit/CanvasContextTests.cpp",
|
||||
"tests/unit/CanvasStateTests.cpp",
|
||||
"tests/unit/ClipAreaTests.cpp",
|
||||
|
||||
@@ -50,8 +50,7 @@ TaskManager* SkiaPipeline::getTaskManager() {
|
||||
}
|
||||
|
||||
void SkiaPipeline::onDestroyHardwareResources() {
|
||||
// No need to flush the caches here. There is a timer
|
||||
// which will flush temporary resources over time.
|
||||
mRenderThread.cacheManager().trimStaleResources();
|
||||
}
|
||||
|
||||
bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
|
||||
|
||||
@@ -45,6 +45,7 @@ class Layer;
|
||||
class DeferredLayerUpdater;
|
||||
|
||||
namespace renderthread {
|
||||
class CacheManager;
|
||||
class CanvasContext;
|
||||
class RenderThread;
|
||||
}
|
||||
@@ -55,6 +56,7 @@ class RenderState {
|
||||
PREVENT_COPY_AND_ASSIGN(RenderState);
|
||||
friend class renderthread::RenderThread;
|
||||
friend class Caches;
|
||||
friend class renderthread::CacheManager;
|
||||
public:
|
||||
void onGLContextCreated();
|
||||
void onGLContextDestroyed();
|
||||
|
||||
187
libs/hwui/renderthread/CacheManager.cpp
Normal file
187
libs/hwui/renderthread/CacheManager.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 "CacheManager.h"
|
||||
|
||||
#include "Layer.h"
|
||||
#include "RenderThread.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
|
||||
#include <gui/Surface.h>
|
||||
#include <GrContextOptions.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)
|
||||
|
||||
// for super large fonts we will draw them as paths so no need to keep linearly
|
||||
// increasing the font cache size.
|
||||
#define FONT_CACHE_MIN_MB (0.5f)
|
||||
#define FONT_CACHE_MAX_MB (4.0f)
|
||||
|
||||
CacheManager::CacheManager(const DisplayInfo& display)
|
||||
: mMaxSurfaceArea(display.w * display.h) {
|
||||
mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
|
||||
}
|
||||
|
||||
void CacheManager::reset(GrContext* context) {
|
||||
if (context != mGrContext.get()) {
|
||||
destroy();
|
||||
}
|
||||
|
||||
if (context) {
|
||||
mGrContext = sk_ref_sp(context);
|
||||
mGrContext->getResourceCacheLimits(&mMaxResources, nullptr);
|
||||
updateContextCacheSizes();
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::destroy() {
|
||||
// cleanup any caches here as the GrContext is about to go away...
|
||||
mGrContext.reset(nullptr);
|
||||
mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
|
||||
}
|
||||
|
||||
void CacheManager::updateContextCacheSizes() {
|
||||
mMaxResourceBytes = mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER;
|
||||
mBackgroundResourceBytes = mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE;
|
||||
|
||||
mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
|
||||
}
|
||||
|
||||
void CacheManager::configureContext(GrContextOptions* contextOptions) {
|
||||
contextOptions->fAllowPathMaskCaching = true;
|
||||
|
||||
float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f;
|
||||
float fontCacheMB = 0;
|
||||
float decimalVal = std::modf(screenMP, &fontCacheMB);
|
||||
|
||||
// This is a basic heuristic to size the cache to a multiple of 512 KB
|
||||
if (decimalVal > 0.8f) {
|
||||
fontCacheMB += 1.0f;
|
||||
} else if (decimalVal > 0.5f) {
|
||||
fontCacheMB += 0.5f;
|
||||
}
|
||||
|
||||
// set limits on min/max size of the cache
|
||||
fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB));
|
||||
|
||||
// We must currently set the size of the text cache based on the size of the
|
||||
// display even though we like to be dynamicallysizing it to the size of the window.
|
||||
// Skia's implementation doesn't provide a mechanism to resize the font cache due to
|
||||
// the potential cost of recreating the glyphs.
|
||||
contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024;
|
||||
}
|
||||
|
||||
void CacheManager::trimMemory(TrimMemoryMode mode) {
|
||||
if (!mGrContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGrContext->flush();
|
||||
|
||||
switch (mode) {
|
||||
case TrimMemoryMode::Complete:
|
||||
mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
|
||||
mGrContext->freeGpuResources();
|
||||
break;
|
||||
case TrimMemoryMode::UiHidden:
|
||||
mGrContext->purgeUnlockedResources(mMaxResourceBytes - mBackgroundResourceBytes, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CacheManager::trimStaleResources() {
|
||||
if (!mGrContext) {
|
||||
return;
|
||||
}
|
||||
mGrContext->flush();
|
||||
mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
|
||||
}
|
||||
|
||||
VectorDrawableAtlas* CacheManager::acquireVectorDrawableAtlas() {
|
||||
LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr);
|
||||
LOG_ALWAYS_FATAL_IF(mGrContext == nullptr);
|
||||
|
||||
/**
|
||||
* TODO LIST:
|
||||
* 1) compute the atlas based on the surfaceArea surface
|
||||
* 2) identify a way to reuse cache entries
|
||||
* 3) add ability to repack the cache?
|
||||
* 4) define memory conditions where we clear the cache (e.g. surface->reset())
|
||||
*/
|
||||
|
||||
return mVectorDrawableAtlas.release();
|
||||
}
|
||||
void CacheManager::releaseVectorDrawableAtlas(VectorDrawableAtlas* atlas) {
|
||||
LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() != nullptr);
|
||||
mVectorDrawableAtlas.reset(atlas);
|
||||
mVectorDrawableAtlas->isNewAtlas = false;
|
||||
}
|
||||
|
||||
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
|
||||
if (!mGrContext) {
|
||||
log.appendFormat("No valid cache instance.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t bytesCached;
|
||||
mGrContext->getResourceCacheUsage(nullptr, &bytesCached);
|
||||
|
||||
log.appendFormat("Caches:\n");
|
||||
log.appendFormat(" Current / Maximum\n");
|
||||
log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n",
|
||||
0.0f, 0.0f, (size_t)0);
|
||||
|
||||
if (renderState) {
|
||||
if (renderState->mActiveLayers.size() > 0) {
|
||||
log.appendFormat(" Layer Info:\n");
|
||||
}
|
||||
|
||||
size_t layerMemoryTotal = 0;
|
||||
for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin();
|
||||
it != renderState->mActiveLayers.end(); it++) {
|
||||
const Layer* layer = *it;
|
||||
const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer";
|
||||
log.appendFormat(" %s size %dx%d\n", layerType,
|
||||
layer->getWidth(), layer->getHeight());
|
||||
layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4;
|
||||
}
|
||||
log.appendFormat(" Layers Total %6.2f kB (numLayers = %zu)\n",
|
||||
layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size());
|
||||
}
|
||||
|
||||
|
||||
log.appendFormat("Total memory usage:\n");
|
||||
log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n",
|
||||
bytesCached, bytesCached / 1024.0f / 1024.0f,
|
||||
mGrContext->getResourceCachePurgeableBytes() / 1024.0f / 1024.0f);
|
||||
|
||||
|
||||
}
|
||||
|
||||
} /* namespace renderthread */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
92
libs/hwui/renderthread/CacheManager.h
Normal file
92
libs/hwui/renderthread/CacheManager.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef CACHEMANAGER_H
|
||||
#define CACHEMANAGER_H
|
||||
|
||||
#include <GrContext.h>
|
||||
#include <SkSurface.h>
|
||||
#include <ui/DisplayInfo.h>
|
||||
#include <utils/String8.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
|
||||
class Surface;
|
||||
|
||||
namespace uirenderer {
|
||||
|
||||
class RenderState;
|
||||
|
||||
namespace renderthread {
|
||||
|
||||
class IRenderPipeline;
|
||||
class RenderThread;
|
||||
|
||||
struct VectorDrawableAtlas {
|
||||
sk_sp<SkSurface> surface;
|
||||
bool isNewAtlas = true;
|
||||
};
|
||||
|
||||
class CacheManager {
|
||||
public:
|
||||
enum class TrimMemoryMode {
|
||||
Complete,
|
||||
UiHidden
|
||||
};
|
||||
|
||||
void configureContext(GrContextOptions* context);
|
||||
void trimMemory(TrimMemoryMode mode);
|
||||
void trimStaleResources();
|
||||
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
|
||||
|
||||
VectorDrawableAtlas* acquireVectorDrawableAtlas();
|
||||
void releaseVectorDrawableAtlas(VectorDrawableAtlas*);
|
||||
|
||||
size_t getCacheSize() const { return mMaxResourceBytes; }
|
||||
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
|
||||
|
||||
private:
|
||||
friend class RenderThread;
|
||||
|
||||
CacheManager(const DisplayInfo& display);
|
||||
|
||||
|
||||
void reset(GrContext* grContext);
|
||||
void destroy();
|
||||
void updateContextCacheSizes();
|
||||
|
||||
const size_t mMaxSurfaceArea;
|
||||
sk_sp<GrContext> mGrContext;
|
||||
|
||||
int mMaxResources = 0;
|
||||
size_t mMaxResourceBytes = 0;
|
||||
size_t mBackgroundResourceBytes = 0;
|
||||
|
||||
struct PipelineProps {
|
||||
const void* pipelineKey = nullptr;
|
||||
size_t surfaceArea = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<VectorDrawableAtlas> mVectorDrawableAtlas;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
#endif /* CACHEMANAGER_H */
|
||||
|
||||
@@ -585,15 +585,37 @@ void CanvasContext::destroyHardwareResources() {
|
||||
}
|
||||
|
||||
void CanvasContext::trimMemory(RenderThread& thread, int level) {
|
||||
// No context means nothing to free
|
||||
if (!thread.eglManager().hasEglContext()) return;
|
||||
|
||||
ATRACE_CALL();
|
||||
if (level >= TRIM_MEMORY_COMPLETE) {
|
||||
thread.renderState().flush(Caches::FlushMode::Full);
|
||||
thread.eglManager().destroy();
|
||||
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
|
||||
thread.renderState().flush(Caches::FlushMode::Moderate);
|
||||
auto renderType = Properties::getRenderPipelineType();
|
||||
switch (renderType) {
|
||||
case RenderPipelineType::OpenGL: {
|
||||
// No context means nothing to free
|
||||
if (!thread.eglManager().hasEglContext()) return;
|
||||
ATRACE_CALL();
|
||||
if (level >= TRIM_MEMORY_COMPLETE) {
|
||||
thread.renderState().flush(Caches::FlushMode::Full);
|
||||
thread.eglManager().destroy();
|
||||
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
|
||||
thread.renderState().flush(Caches::FlushMode::Moderate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RenderPipelineType::SkiaGL:
|
||||
case RenderPipelineType::SkiaVulkan: {
|
||||
// No context means nothing to free
|
||||
if (!thread.getGrContext()) return;
|
||||
ATRACE_CALL();
|
||||
if (level >= TRIM_MEMORY_COMPLETE) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
thread.eglManager().destroy();
|
||||
thread.vulkanManager().destroy();
|
||||
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ void EglManager::initialize() {
|
||||
|
||||
GrContextOptions options;
|
||||
options.fGpuPathRenderers &= ~GrContextOptions::GpuPathRenderers::kDistanceField;
|
||||
options.fAllowPathMaskCaching = true;
|
||||
mRenderThread.cacheManager().configureContext(&options);
|
||||
mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
|
||||
(GrBackendContext)glInterface.get(), options));
|
||||
}
|
||||
|
||||
@@ -469,18 +469,7 @@ uint32_t RenderProxy::frameTimePercentile(int p) {
|
||||
}
|
||||
|
||||
CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
|
||||
args->thread->jankTracker().dump(args->fd);
|
||||
|
||||
FILE *file = fdopen(args->fd, "a");
|
||||
if (Caches::hasInstance()) {
|
||||
String8 cachesLog;
|
||||
Caches::getInstance().dumpMemoryUsage(cachesLog);
|
||||
fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
|
||||
} else {
|
||||
fprintf(file, "\nNo caches instance.\n");
|
||||
}
|
||||
fprintf(file, "\nPipeline=FrameBuilder\n");
|
||||
fflush(file);
|
||||
args->thread->dumpGraphicsMemory(args->fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,6 +202,45 @@ void RenderThread::initThreadLocals() {
|
||||
mRenderState = new RenderState(*this);
|
||||
mJankTracker = new JankTracker(mDisplayInfo);
|
||||
mVkManager = new VulkanManager(*this);
|
||||
mCacheManager = new CacheManager(mDisplayInfo);
|
||||
}
|
||||
|
||||
void RenderThread::dumpGraphicsMemory(int fd) {
|
||||
jankTracker().dump(fd);
|
||||
|
||||
String8 cachesOutput;
|
||||
String8 pipeline;
|
||||
auto renderType = Properties::getRenderPipelineType();
|
||||
switch (renderType) {
|
||||
case RenderPipelineType::OpenGL: {
|
||||
if (Caches::hasInstance()) {
|
||||
cachesOutput.appendFormat("Caches:\n");
|
||||
Caches::getInstance().dumpMemoryUsage(cachesOutput);
|
||||
} else {
|
||||
cachesOutput.appendFormat("No caches instance.");
|
||||
}
|
||||
pipeline.appendFormat("FrameBuilder");
|
||||
break;
|
||||
}
|
||||
case RenderPipelineType::SkiaGL: {
|
||||
mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
|
||||
pipeline.appendFormat("Skia (OpenGL)");
|
||||
break;
|
||||
}
|
||||
case RenderPipelineType::SkiaVulkan: {
|
||||
mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
|
||||
pipeline.appendFormat("Skia (Vulkan)");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
}
|
||||
|
||||
FILE *file = fdopen(fd, "a");
|
||||
fprintf(file, "\n%s\n", cachesOutput.string());
|
||||
fprintf(file, "\nPipeline=%s\n", pipeline.string());
|
||||
fflush(file);
|
||||
}
|
||||
|
||||
Readback& RenderThread::readback() {
|
||||
@@ -228,6 +267,14 @@ Readback& RenderThread::readback() {
|
||||
return *mReadback;
|
||||
}
|
||||
|
||||
void RenderThread::setGrContext(GrContext* context) {
|
||||
mCacheManager->reset(context);
|
||||
if (mGrContext.get()) {
|
||||
mGrContext->releaseResourcesAndAbandonContext();
|
||||
}
|
||||
mGrContext.reset(context);
|
||||
}
|
||||
|
||||
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
|
||||
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
|
||||
ALOGE("Display event receiver pipe was closed or an error occurred. "
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "RenderTask.h"
|
||||
|
||||
#include "../JankTracker.h"
|
||||
#include "CacheManager.h"
|
||||
#include "TimeLord.h"
|
||||
|
||||
#include <GrContext.h>
|
||||
@@ -102,11 +103,13 @@ public:
|
||||
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
|
||||
|
||||
GrContext* getGrContext() const { return mGrContext.get(); }
|
||||
void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); }
|
||||
void setGrContext(GrContext* cxt);
|
||||
|
||||
CacheManager& cacheManager() { return *mCacheManager; }
|
||||
VulkanManager& vulkanManager() { return *mVkManager; }
|
||||
|
||||
sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
|
||||
void dumpGraphicsMemory(int fd);
|
||||
|
||||
protected:
|
||||
virtual bool threadLoop() override;
|
||||
@@ -161,6 +164,7 @@ private:
|
||||
Readback* mReadback = nullptr;
|
||||
|
||||
sk_sp<GrContext> mGrContext;
|
||||
CacheManager* mCacheManager;
|
||||
VulkanManager* mVkManager;
|
||||
};
|
||||
|
||||
|
||||
75
libs/hwui/tests/unit/CacheManagerTests.cpp
Normal file
75
libs/hwui/tests/unit/CacheManagerTests.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 <gtest/gtest.h>
|
||||
|
||||
#include "renderthread/CacheManager.h"
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "tests/common/TestUtils.h"
|
||||
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
using namespace android::uirenderer::renderthread;
|
||||
|
||||
static size_t getCacheUsage(GrContext* grContext) {
|
||||
size_t cacheUsage;
|
||||
grContext->getResourceCacheUsage(nullptr, &cacheUsage);
|
||||
return cacheUsage;
|
||||
}
|
||||
|
||||
RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
|
||||
DisplayInfo displayInfo = renderThread.mainDisplayInfo();
|
||||
GrContext* grContext = renderThread.getGrContext();
|
||||
ASSERT_TRUE(grContext != nullptr);
|
||||
|
||||
// create pairs of offscreen render targets and images until we exceed the backgroundCacheSizeLimit
|
||||
std::vector<sk_sp<SkSurface>> surfaces;
|
||||
|
||||
while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) {
|
||||
SkImageInfo info = SkImageInfo::MakeA8(displayInfo.w, displayInfo.h);
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info);
|
||||
surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT);
|
||||
|
||||
grContext->flush();
|
||||
|
||||
surfaces.push_back(surface);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(1 < surfaces.size());
|
||||
|
||||
// attempt to trim all memory while we still hold strong refs
|
||||
renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
|
||||
|
||||
// free the surfaces
|
||||
for (size_t i = 0; i < surfaces.size(); i++) {
|
||||
ASSERT_TRUE(surfaces[i]->unique());
|
||||
surfaces[i].reset();
|
||||
}
|
||||
|
||||
// verify that we have enough purgeable bytes
|
||||
const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes();
|
||||
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
|
||||
|
||||
// UI hidden and make sure only some got purged
|
||||
renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
|
||||
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);
|
||||
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
|
||||
}
|
||||
Reference in New Issue
Block a user