• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "rs_skia_memory_tracer.h"
17 #include <numeric>
18 namespace OHOS::Rosen {
19 constexpr uint32_t MEMUNIT_RATE = 1024;
20 static const std::unordered_map<RSTagTracker::SOURCETYPE, std::string> sourceToStringMap = {
21     {RSTagTracker::SOURCE_OTHER, "source_other"},
22     {RSTagTracker::SOURCE_RSCUSTOMMODIFIERDRAWABLE, "source_rscustommodifierdrawable"},
23     {RSTagTracker::SOURCE_RSBEGINBLENDERDRAWABLE, "source_rsbeginblenderdrawable"},
24     {RSTagTracker::SOURCE_RSSHADOWDRAWABLE, "source_rsshadowdrawable"},
25     {RSTagTracker::SOURCE_RSBACKGROUNDIMAGEDRAWABLE, "source_rsbackgroundimagedrawable"},
26     {RSTagTracker::SOURCE_RSBACKGROUNDEFFECTDRAWABLE, "source_rsbackgroundeffectdrawable"},
27     {RSTagTracker::SOURCE_RSUSEEFFECTDRAWABLE, "source_rsuseeffectdrawable"},
28     {RSTagTracker::SOURCE_RSDYNAMICLIGHTUPDRAWABLE, "source_rsdynamiclightupdrawable"},
29     {RSTagTracker::SOURCE_RSBINARIZATIONDRAWABLE, "source_rsbinarizationdrawable"},
30     {RSTagTracker::SOURCE_RSCOLORFILTERDRAWABLE, "source_rscolorfilterdrawable"},
31     {RSTagTracker::SOURCE_RSLIGHTUPEFFECTDRAWABLE, "source_rslightupeffectdrawable"},
32     {RSTagTracker::SOURCE_RSDYNAMICDIMDRAWABLE, "source_rsdynamicdimdrawable"},
33     {RSTagTracker::SOURCE_RSFOREGROUNDFILTERDRAWABLE, "source_rsforegroundfilterdrawable"},
34     {RSTagTracker::SOURCE_RSFOREGROUNDFILTERRESTOREDRAWABLE, "source_rsforegroundfilterrestoredrawable"},
35     {RSTagTracker::SOURCE_RSPIXELSTRETCHDRAWABLE, "source_rspixelstretchdrawable"},
36     {RSTagTracker::SOURCE_RSPOINTLIGHTDRAWABLE, "source_rspointlightdrawable"},
37     {RSTagTracker::SOURCE_RSPROPERTYDRAWABLE, "source_rspropertydrawable"},
38     {RSTagTracker::SOURCE_RSFILTERDRAWABLE, "source_rsfilterdrawable"},
39     {RSTagTracker::SOURCE_FINISHOFFSCREENRENDER, "source_finishoffscreenrender"},
40     {RSTagTracker::SOURCE_DRAWSELFDRAWINGNODEBUFFER, "source_drawselfdrawingnodebuffer"},
41     {RSTagTracker::SOURCE_ONCAPTURE, "source_oncapture"},
42     {RSTagTracker::SOURCE_INITCACHEDSURFACE, "source_initcachedsurface"},
43     {RSTagTracker::SOURCE_DRAWRENDERCONTENT, "source_drawrendercontent"},
44     {RSTagTracker::SOURCE_FILTERCACHEENABLEVMA, "source_filtercacheenablevma"},
45 };
46 
SkiaMemoryTracer(const std::vector<ResourcePair> & resourceMap,bool itemizeType)47 SkiaMemoryTracer::SkiaMemoryTracer(const std::vector<ResourcePair>& resourceMap, bool itemizeType)
48     : resourceMap_(resourceMap), itemizeType_(itemizeType), totalSize_("bytes", 0),
49       purgeableSize_("bytes", 0), externalTextureSize_("bytes", 0)
50 {}
51 
SkiaMemoryTracer(const char * categoryKey,bool itemizeType)52 SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
53     : categoryKey_(categoryKey), itemizeType_(itemizeType), totalSize_("bytes", 0),
54       purgeableSize_("bytes", 0), externalTextureSize_("bytes", 0)
55 {}
56 
SourceType2String(RSTagTracker::SOURCETYPE type)57 std::string SkiaMemoryTracer::SourceType2String(RSTagTracker::SOURCETYPE type)
58 {
59     auto it = sourceToStringMap.find(type);
60     if (it != sourceToStringMap.end()) {
61         return it->second;
62     }
63     return "";
64 }
65 
MapName(const char * resourceName)66 const char* SkiaMemoryTracer::MapName(const char* resourceName)
67 {
68     for (auto& resource : resourceMap_) {
69         if (SkStrContains(resourceName, resource.first)) {
70             return resource.second;
71         }
72     }
73     return nullptr;
74 }
75 
ProcessElement()76 void SkiaMemoryTracer::ProcessElement()
77 {
78     if (!currentElement_.empty()) {
79         // Only count elements that contain "size", other values just provide metadata.
80         auto sizeResult = currentValues_.find("size");
81         if (sizeResult != currentValues_.end()) {
82             totalSize_.value += sizeResult->second.value;
83             totalSize_.count++;
84         } else {
85             currentElement_.clear();
86             currentValues_.clear();
87             return;
88         }
89 
90         // find the purgeable size if one exists
91         auto purgeableResult = currentValues_.find("purgeable_size");
92         if (purgeableResult != currentValues_.end()) {
93             purgeableSize_.value += purgeableResult->second.value;
94             purgeableSize_.count++;
95         }
96 
97         // find the type if one exists
98         std::string type;
99         auto typeResult = currentValues_.find("type");
100         if (typeResult != currentValues_.end()) {
101             type = typeResult->second.units.c_str();
102         } else if (itemizeType_) {
103             type = "Other";
104         } else {
105             type = "";
106         }
107 
108         // compute the type if we are itemizing or use the default "size" if we are not
109         std::string key = (itemizeType_) ? type : sizeResult->first;
110 
111         // compute the external texture size if one exists
112         std::string externalPrefix = "External";
113         if (key.size() >= externalPrefix.size() && key.compare(0, externalPrefix.size(), externalPrefix) == 0) {
114             externalTextureSize_.value += sizeResult->second.value;
115             externalTextureSize_.count++;
116         }
117 
118         // compute the top level element name using either the map or category key
119         const char* resourceName = MapName(currentElement_.c_str());
120         if (categoryKey_ != nullptr) {
121             // find the category if one exists
122             auto categoryResult = currentValues_.find(categoryKey_);
123             if (categoryResult != currentValues_.end()) {
124                 resourceName = categoryResult->second.units.c_str();
125             }
126         }
127 
128         // if we don't have a resource name then we don't know how to label the
129         // data and should abort.
130         if (resourceName == nullptr) {
131             resourceName = currentElement_.c_str();
132         }
133 
134         auto result = results_.find(resourceName);
135         if (result == results_.end()) {
136             std::string strResourceName = resourceName;
137             TraceValue sizeValue = sizeResult->second;
138             std::unordered_map<std::string, TraceValue> typeItem{{key, sizeValue}};
139             results_.insert({ strResourceName, typeItem });
140         } else {
141             auto& resourceValues = result->second;
142             typeResult = resourceValues.find(key);
143             if (typeResult == resourceValues.end()) {
144                 resourceValues.insert({ key, sizeResult->second });
145             } else {
146                 typeResult->second.value += sizeResult->second.value;
147                 typeResult->second.count++;
148             }
149         }
150 
151         // find the source if one exists
152         std::string sourceType;
153         auto sourceIdResult = currentValues_.find("source");
154         if (sourceIdResult != currentValues_.end()) {
155             sourceType = SourceType2String(
156                 static_cast<RSTagTracker::SOURCETYPE>(static_cast<uint32_t>(sourceIdResult->second.value)));
157         } else if (itemizeType_) {
158             sourceType = "source_other";
159         } else {
160             sourceType = "";
161         }
162 
163         std::string strResourceName = resourceName; // current tag
164         TraceValue sizeValue = sizeResult->second; // current size info
165         auto sourceResult = sourceTagResults_.find(sourceType);
166         if (sourceResult == sourceTagResults_.end()) { // 1. add new source item
167             std::unordered_map<std::string, TraceValue> typeItem{{key, sizeValue}};
168             std::unordered_map<std::string,
169                 std::unordered_map<std::string, TraceValue>> tagItem{{strResourceName, typeItem}};
170             sourceTagResults_.insert({sourceType, tagItem});
171         } else { // 2.update source item
172             auto& sourceValues = sourceResult->second;
173             auto tagResult = sourceValues.find(strResourceName);
174             if (tagResult == sourceValues.end()) { // 2.1 add new tag item in current source
175                 std::unordered_map<std::string, TraceValue> typeItem{{key, sizeValue}};
176                 sourceResult->second.insert({strResourceName, typeItem});
177             } else { // 2.2 update tag item in current source
178                 auto& typeValues = tagResult->second;
179                 typeResult = typeValues.find(key);
180                 if (typeResult == typeValues.end()) { // 2.2.1 add new type item in current tag
181                     typeValues.insert({key, sizeValue});
182                 } else { // 2.2.2 update type item in current tag
183                     typeResult->second.value += sizeValue.value;
184                     typeResult->second.count++;
185                 }
186             }
187         }
188     }
189 
190     currentElement_.clear();
191     currentValues_.clear();
192 }
193 
dumpNumericValue(const char * dumpName,const char * valueName,const char * units,uint64_t value)194 void SkiaMemoryTracer::dumpNumericValue(const char* dumpName, const char* valueName, const char* units, uint64_t value)
195 {
196     if (currentElement_ != dumpName) {
197         ProcessElement();
198         currentElement_ = dumpName;
199     }
200     currentValues_.insert({ valueName, { units, value } });
201 }
202 
LogOutput(DfxString & log)203 void SkiaMemoryTracer::LogOutput(DfxString& log)
204 {
205     // process any remaining elements
206     ProcessElement();
207 
208     for (const auto& sourceItem: sourceTagResults_) {
209         if (itemizeType_) {
210             log.AppendFormat("  %s:\n", sourceItem.first.c_str()); // source_xx
211             for (const auto& tagItem: sourceItem.second) {
212                 log.AppendFormat("    %s:\n", tagItem.first.c_str()); // skia/sk_glyph_cache
213                 for (const auto& typeItem: tagItem.second) {
214                     TraceValue traceValue = ConvertUnits(typeItem.second);
215                     const char* entry = (traceValue.count > 1) ? "entries" : "entry";
216                     log.AppendFormat("      %s: %.2f %s (%d %s)\n", typeItem.first.c_str(), traceValue.value,
217                         traceValue.units.c_str(), traceValue.count, entry);
218                 }
219             }
220         } else {
221             for (const auto& tagItem: sourceItem.second) {
222                 auto result = tagItem.second.find("size"); // Logically not empty
223                 TraceValue traceValue = ConvertUnits(result->second);
224                 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
225                 log.AppendFormat("  %s: %.2f %s (%d %s)\n", tagItem.first.c_str(), traceValue.value,
226                     traceValue.units.c_str(), traceValue.count, entry);
227             }
228         }
229     }
230 }
231 
GetGLMemorySize()232 float SkiaMemoryTracer::GetGLMemorySize()
233 {
234     ProcessElement();
235     // exclude scratch memory
236     // cause scratch memory is generated by animation and effect. which is not response by app
237     size_t totalGpuSizeApp = 0;
238     for (const auto& namedItem : results_) {
239         if (namedItem.first == "Scratch" || namedItem.first == "skia/gr_text_blob_cache") {
240             continue;
241         }
242         totalGpuSizeApp = std::accumulate(namedItem.second.begin(), namedItem.second.end(), totalGpuSizeApp,
243             [](int total, const auto& typedValue) { return total + typedValue.second.value; });
244     }
245     return totalGpuSizeApp;
246 }
247 
GetGpuMemorySizeInMB()248 float SkiaMemoryTracer::GetGpuMemorySizeInMB()
249 {
250     ProcessElement();
251     return ConvertToMB(totalSize_);
252 }
253 
LogTotals(DfxString & log)254 void SkiaMemoryTracer::LogTotals(DfxString& log)
255 {
256     TraceValue total = ConvertUnits(totalSize_);
257     TraceValue purgeable = ConvertUnits(purgeableSize_);
258     TraceValue externalTexture = ConvertUnits(externalTextureSize_);
259     log.AppendFormat("    %.0f bytes, %.2f %s (%.2f %s is purgeable, %.2f %s is external)\n",
260         totalSize_.value, total.value, total.units.c_str(), purgeable.value, purgeable.units.c_str(),
261         externalTexture.value, externalTexture.units.c_str());
262 }
263 
ConvertUnits(const TraceValue & value)264 SkiaMemoryTracer::TraceValue SkiaMemoryTracer::ConvertUnits(const TraceValue& value)
265 {
266     TraceValue output(value);
267     if (output.units == SkString("bytes") && output.value >= MEMUNIT_RATE) {
268         output.value = output.value / MEMUNIT_RATE;
269         output.units = "KB";
270     }
271     if (output.units == SkString("KB") && output.value >= MEMUNIT_RATE) {
272         output.value = output.value / MEMUNIT_RATE;
273         output.units = "MB";
274     }
275     return output;
276 }
277 
ConvertToMB(const TraceValue & value)278 float SkiaMemoryTracer::ConvertToMB(const TraceValue& value)
279 {
280     if (value.units == SkString("bytes")) {
281         return value.value / MEMUNIT_RATE / MEMUNIT_RATE;
282     }
283     if (value.units == SkString("KB")) {
284         return value.value / MEMUNIT_RATE;
285     }
286     return value.value;
287 }
288 
289 } // namespace OHOS::Rosen