1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "CacheManager.h"
18 
19 #include <GrContextOptions.h>
20 #include <GrTypes.h>
21 #include <SkExecutor.h>
22 #include <SkGraphics.h>
23 #include <math.h>
24 #include <utils/Trace.h>
25 
26 #include <set>
27 
28 #include "CanvasContext.h"
29 #include "DeviceInfo.h"
30 #include "Layer.h"
31 #include "Properties.h"
32 #include "RenderThread.h"
33 #include "VulkanManager.h"
34 #include "pipeline/skia/ATraceMemoryDump.h"
35 #include "pipeline/skia/ShaderCache.h"
36 #include "pipeline/skia/SkiaMemoryTracer.h"
37 #include "renderstate/RenderState.h"
38 #include "thread/CommonPool.h"
39 
40 namespace android {
41 namespace uirenderer {
42 namespace renderthread {
43 
CacheManager(RenderThread & thread)44 CacheManager::CacheManager(RenderThread& thread)
45         : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {
46     mMaxSurfaceArea = static_cast<size_t>((DeviceInfo::getWidth() * DeviceInfo::getHeight()) *
47                                           mMemoryPolicy.initialMaxSurfaceAreaScale);
48     setupCacheLimits();
49 }
50 
countLeadingZeros(uint32_t mask)51 static inline int countLeadingZeros(uint32_t mask) {
52     // __builtin_clz(0) is undefined, so we have to detect that case.
53     return mask ? __builtin_clz(mask) : 32;
54 }
55 
56 // Return the smallest power-of-2 >= n.
nextPowerOfTwo(uint32_t n)57 static inline uint32_t nextPowerOfTwo(uint32_t n) {
58     return n ? (1 << (32 - countLeadingZeros(n - 1))) : 1;
59 }
60 
setupCacheLimits()61 void CacheManager::setupCacheLimits() {
62     mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
63     mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
64     // This sets the maximum size for a single texture atlas in the GPU font cache. If
65     // necessary, the cache can allocate additional textures that are counted against the
66     // total cache limits provided to Skia.
67     mMaxGpuFontAtlasBytes = nextPowerOfTwo(mMaxSurfaceArea);
68     // This sets the maximum size of the CPU font cache to be at least the same size as the
69     // total number of GPU font caches (i.e. 4 separate GPU atlases).
70     mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
71     mBackgroundCpuFontCacheBytes = mMaxCpuFontCacheBytes * mMemoryPolicy.backgroundRetentionPercent;
72 
73     SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
74     if (mGrContext) {
75         mGrContext->setResourceCacheLimit(mMaxResourceBytes);
76     }
77 }
78 
reset(sk_sp<GrDirectContext> context)79 void CacheManager::reset(sk_sp<GrDirectContext> context) {
80     if (context != mGrContext) {
81         destroy();
82     }
83 
84     if (context) {
85         mGrContext = std::move(context);
86         mGrContext->setResourceCacheLimit(mMaxResourceBytes);
87         mLastDeferredCleanup = systemTime(CLOCK_MONOTONIC);
88     }
89 }
90 
destroy()91 void CacheManager::destroy() {
92     // cleanup any caches here as the GrContext is about to go away...
93     mGrContext.reset(nullptr);
94 }
95 
96 class CommonPoolExecutor : public SkExecutor {
97 public:
add(std::function<void (void)> func)98     virtual void add(std::function<void(void)> func) override { CommonPool::post(std::move(func)); }
99 };
100 
101 static CommonPoolExecutor sDefaultExecutor;
102 
configureContext(GrContextOptions * contextOptions,const void * identity,ssize_t size)103 void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity,
104                                     ssize_t size) {
105     contextOptions->fAllowPathMaskCaching = true;
106     contextOptions->fGlyphCacheTextureMaximumBytes = mMaxGpuFontAtlasBytes;
107     contextOptions->fExecutor = &sDefaultExecutor;
108 
109     auto& cache = skiapipeline::ShaderCache::get();
110     cache.initShaderDiskCache(identity, size);
111     contextOptions->fPersistentCache = &cache;
112 }
113 
toSkiaEnum(bool scratchOnly)114 static GrPurgeResourceOptions toSkiaEnum(bool scratchOnly) {
115     return scratchOnly ? GrPurgeResourceOptions::kScratchResourcesOnly :
116                          GrPurgeResourceOptions::kAllResources;
117 }
118 
trimMemory(TrimLevel mode)119 void CacheManager::trimMemory(TrimLevel mode) {
120     if (!mGrContext) {
121         return;
122     }
123 
124     // flush and submit all work to the gpu and wait for it to finish
125     mGrContext->flushAndSubmit(GrSyncCpu::kYes);
126 
127     if (mode >= TrimLevel::BACKGROUND) {
128         mGrContext->freeGpuResources();
129         SkGraphics::PurgeAllCaches();
130         mRenderThread.destroyRenderingContext();
131     } else if (mode == TrimLevel::UI_HIDDEN) {
132         // Here we purge all the unlocked scratch resources and then toggle the resources cache
133         // limits between the background and max amounts. This causes the unlocked resources
134         // that have persistent data to be purged in LRU order.
135         mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
136         SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
137         mGrContext->purgeUnlockedResources(toSkiaEnum(mMemoryPolicy.purgeScratchOnly));
138         mGrContext->setResourceCacheLimit(mMaxResourceBytes);
139         SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
140     }
141 }
142 
trimCaches(CacheTrimLevel mode)143 void CacheManager::trimCaches(CacheTrimLevel mode) {
144     switch (mode) {
145         case CacheTrimLevel::FONT_CACHE:
146             SkGraphics::PurgeFontCache();
147             break;
148         case CacheTrimLevel::RESOURCE_CACHE:
149             SkGraphics::PurgeResourceCache();
150             break;
151         case CacheTrimLevel::ALL_CACHES:
152             SkGraphics::PurgeAllCaches();
153             if (mGrContext) {
154                 mGrContext->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
155             }
156             break;
157         default:
158             break;
159     }
160 }
161 
trimStaleResources()162 void CacheManager::trimStaleResources() {
163     if (!mGrContext) {
164         return;
165     }
166     mGrContext->flushAndSubmit();
167     mGrContext->performDeferredCleanup(std::chrono::seconds(30),
168                                        GrPurgeResourceOptions::kAllResources);
169 }
170 
getMemoryUsage(size_t * cpuUsage,size_t * gpuUsage)171 void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
172     *cpuUsage = 0;
173     *gpuUsage = 0;
174     if (!mGrContext) {
175         return;
176     }
177 
178     skiapipeline::SkiaMemoryTracer cpuTracer("category", true);
179     SkGraphics::DumpMemoryStatistics(&cpuTracer);
180     *cpuUsage += cpuTracer.total();
181 
182     skiapipeline::SkiaMemoryTracer gpuTracer("category", true);
183     mGrContext->dumpMemoryStatistics(&gpuTracer);
184     *gpuUsage += gpuTracer.total();
185 }
186 
dumpMemoryUsage(String8 & log,const RenderState * renderState)187 void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
188     log.appendFormat(R"(Memory policy:
189   Max surface area: %zu
190   Max resource usage: %.2fMB (x%.0f)
191   Background retention: %.0f%% (altUiHidden = %s)
192 )",
193                      mMaxSurfaceArea, mMaxResourceBytes / 1000000.f,
194                      mMemoryPolicy.surfaceSizeMultiplier,
195                      mMemoryPolicy.backgroundRetentionPercent * 100.0f,
196                      mMemoryPolicy.useAlternativeUiHidden ? "true" : "false");
197     if (Properties::isSystemOrPersistent) {
198         log.appendFormat("  IsSystemOrPersistent\n");
199     }
200     log.appendFormat("  GPU Context timeout: %" PRIu64 "\n", ns2s(mMemoryPolicy.contextTimeout));
201     size_t stoppedContexts = 0;
202     for (auto context : mCanvasContexts) {
203         if (context->isStopped()) stoppedContexts++;
204     }
205     log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts);
206 
207     auto vkInstance = VulkanManager::peekInstance();
208     if (!mGrContext) {
209         if (!vkInstance) {
210             log.appendFormat("No GPU context.\n");
211         } else {
212             log.appendFormat("No GrContext; however %d remaining Vulkan refs",
213                              vkInstance->getStrongCount() - 1);
214         }
215         return;
216     }
217     std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
218             {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
219             {"skia/sk_resource_cache/rrect-blur_", "Masks"},
220             {"skia/sk_resource_cache/rects-blur_", "Masks"},
221             {"skia/sk_resource_cache/tessellated", "Shadows"},
222             {"skia/sk_glyph_cache", "Glyph Cache"},
223     };
224     skiapipeline::SkiaMemoryTracer cpuTracer(cpuResourceMap, false);
225     SkGraphics::DumpMemoryStatistics(&cpuTracer);
226     if (cpuTracer.hasOutput()) {
227         log.appendFormat("CPU Caches:\n");
228         cpuTracer.logOutput(log);
229         log.appendFormat("  Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed());
230         log.appendFormat("Total CPU memory usage:\n");
231         cpuTracer.logTotals(log);
232     }
233 
234     skiapipeline::SkiaMemoryTracer gpuTracer("category", true);
235     mGrContext->dumpMemoryStatistics(&gpuTracer);
236     if (gpuTracer.hasOutput()) {
237         log.appendFormat("GPU Caches:\n");
238         gpuTracer.logOutput(log);
239     }
240 
241     if (renderState && renderState->mActiveLayers.size() > 0) {
242         log.appendFormat("Layer Info:\n");
243 
244         const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL
245                                         ? "GlLayer"
246                                         : "VkLayer";
247         size_t layerMemoryTotal = 0;
248         for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin();
249              it != renderState->mActiveLayers.end(); it++) {
250             const Layer* layer = *it;
251             log.appendFormat("    %s size %dx%d\n", layerType, layer->getWidth(),
252                              layer->getHeight());
253             layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4;
254         }
255         log.appendFormat("  Layers Total         %6.2f KB (numLayers = %zu)\n",
256                          layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size());
257     }
258 
259     log.appendFormat("Total GPU memory usage:\n");
260     gpuTracer.logTotals(log);
261 }
262 
onFrameCompleted()263 void CacheManager::onFrameCompleted() {
264     cancelDestroyContext();
265     mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
266     if (ATRACE_ENABLED()) {
267         ATRACE_NAME("dumpingMemoryStatistics");
268         static skiapipeline::ATraceMemoryDump tracer;
269         tracer.startFrame();
270         SkGraphics::DumpMemoryStatistics(&tracer);
271         if (mGrContext && Properties::debugTraceGpuResourceCategories) {
272             mGrContext->dumpMemoryStatistics(&tracer);
273         }
274         tracer.logTraces(Properties::debugTraceGpuResourceCategories, mGrContext.get());
275     }
276 }
277 
onThreadIdle()278 void CacheManager::onThreadIdle() {
279     if (!mGrContext || mFrameCompletions.size() == 0) return;
280 
281     const nsecs_t now = systemTime(CLOCK_MONOTONIC);
282     // Rate limiting
283     if ((now - mLastDeferredCleanup) > 25_ms) {
284         mLastDeferredCleanup = now;
285         const nsecs_t frameCompleteNanos = mFrameCompletions[0];
286         const nsecs_t frameDiffNanos = now - frameCompleteNanos;
287         const nsecs_t cleanupMillis =
288                 ns2ms(std::clamp(frameDiffNanos, mMemoryPolicy.minimumResourceRetention,
289                                  mMemoryPolicy.maximumResourceRetention));
290         mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
291                                            toSkiaEnum(mMemoryPolicy.purgeScratchOnly));
292     }
293 }
294 
scheduleDestroyContext()295 void CacheManager::scheduleDestroyContext() {
296     if (mMemoryPolicy.contextTimeout > 0) {
297         mRenderThread.queue().postDelayed(mMemoryPolicy.contextTimeout,
298                                           [this, genId = mGenerationId] {
299                                               if (mGenerationId != genId) return;
300                                               // GenID should have already stopped this, but just in
301                                               // case
302                                               if (!areAllContextsStopped()) return;
303                                               mRenderThread.destroyRenderingContext();
304                                           });
305     }
306 }
307 
cancelDestroyContext()308 void CacheManager::cancelDestroyContext() {
309     if (mIsDestructionPending) {
310         mIsDestructionPending = false;
311         mGenerationId++;
312     }
313 }
314 
areAllContextsStopped()315 bool CacheManager::areAllContextsStopped() {
316     for (auto context : mCanvasContexts) {
317         if (!context->isStopped()) return false;
318     }
319     return true;
320 }
321 
checkUiHidden()322 void CacheManager::checkUiHidden() {
323     if (!mGrContext) return;
324 
325     if (mMemoryPolicy.useAlternativeUiHidden && areAllContextsStopped()) {
326         trimMemory(TrimLevel::UI_HIDDEN);
327     }
328 }
329 
registerCanvasContext(CanvasContext * context)330 void CacheManager::registerCanvasContext(CanvasContext* context) {
331     mCanvasContexts.push_back(context);
332     cancelDestroyContext();
333 }
334 
unregisterCanvasContext(CanvasContext * context)335 void CacheManager::unregisterCanvasContext(CanvasContext* context) {
336     std::erase(mCanvasContexts, context);
337     checkUiHidden();
338     if (mCanvasContexts.empty()) {
339         scheduleDestroyContext();
340     }
341 }
342 
onContextStopped(CanvasContext * context)343 void CacheManager::onContextStopped(CanvasContext* context) {
344     checkUiHidden();
345     if (mMemoryPolicy.releaseContextOnStoppedOnly && areAllContextsStopped()) {
346         scheduleDestroyContext();
347     }
348 }
349 
notifyNextFrameSize(int width,int height)350 void CacheManager::notifyNextFrameSize(int width, int height) {
351     int frameArea = width * height;
352     if (frameArea > mMaxSurfaceArea) {
353         mMaxSurfaceArea = frameArea;
354         setupCacheLimits();
355     }
356 }
357 
358 } /* namespace renderthread */
359 } /* namespace uirenderer */
360 } /* namespace android */
361