1 /*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/text/GrAtlasManager.h"
9
10 #include "src/gpu/GrGlyph.h"
11 #include "src/gpu/text/GrStrikeCache.h"
12
GrAtlasManager(GrProxyProvider * proxyProvider,GrStrikeCache * glyphCache,size_t maxTextureBytes,GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,int plotOldThreshold)13 GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider, GrStrikeCache* glyphCache,
14 size_t maxTextureBytes,
15 GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,
16 int plotOldThreshold)
17 : fAllowMultitexturing{allowMultitexturing}
18 , fProxyProvider{proxyProvider}
19 , fCaps{fProxyProvider->refCaps()}
20 , fGlyphCache{glyphCache}
21 , fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes}
22 , fPlotOldThreshold(plotOldThreshold)
23 , fAtlasHitCount(0)
24 , fAtlasMissCount(0) { }
25
26 GrAtlasManager::~GrAtlasManager() = default;
27
mask_format_to_gr_color_type(GrMaskFormat format)28 static GrColorType mask_format_to_gr_color_type(GrMaskFormat format) {
29 switch (format) {
30 case kA8_GrMaskFormat:
31 return GrColorType::kAlpha_8;
32 case kA565_GrMaskFormat:
33 return GrColorType::kBGR_565;
34 case kARGB_GrMaskFormat:
35 return GrColorType::kRGBA_8888;
36 default:
37 SkDEBUGFAIL("unsupported GrMaskFormat");
38 return GrColorType::kAlpha_8;
39 }
40 }
41
freeAll()42 void GrAtlasManager::freeAll() {
43 for (int i = 0; i < kMaskFormatCount; ++i) {
44 fAtlases[i] = nullptr;
45 }
46 }
47
hasGlyph(GrGlyph * glyph)48 bool GrAtlasManager::hasGlyph(GrGlyph* glyph) {
49 SkASSERT(glyph);
50 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
51 }
52
53 // add to texture atlas that matches this format
addToAtlas(GrResourceProvider * resourceProvider,GrStrikeCache * glyphCache,GrTextStrike * strike,GrDrawOpAtlas::AtlasID * id,GrDeferredUploadTarget * target,GrMaskFormat format,int width,int height,const void * image,SkIPoint16 * loc)54 GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(
55 GrResourceProvider* resourceProvider,
56 GrStrikeCache* glyphCache,
57 GrTextStrike* strike, GrDrawOpAtlas::AtlasID* id,
58 GrDeferredUploadTarget* target, GrMaskFormat format,
59 int width, int height, const void* image, SkIPoint16* loc) {
60 glyphCache->setStrikeToPreserve(strike);
61 return this->getAtlas(format)->addToAtlas(resourceProvider, id, target, width, height,
62 image, loc);
63 }
64
addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater * updater,GrGlyph * glyph,GrDeferredUploadToken token)65 void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater,
66 GrGlyph* glyph,
67 GrDeferredUploadToken token) {
68 SkASSERT(glyph);
69 if (updater->add(glyph->fID)) {
70 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
71 }
72 }
73
postFlush(GrDeferredUploadToken startTokenForNextFlush,const uint32_t * opListIDs,int numOpListIDs)74 void GrAtlasManager::postFlush(GrDeferredUploadToken startTokenForNextFlush,
75 const uint32_t* opListIDs, int numOpListIDs) {
76 bool isRadicals = false;
77 static int count = 0;
78 count++;
79 if (count == 5) {
80 float hitRate = atlasHitRate();
81 if (!(fabs(hitRate-0) <= 1.0e-6)) {
82 #ifdef SK_DEGUB_ATLAS_HIT_RATE
83 SkDebugf("----- last 5 flush AtlasHitRate = %{public}6.2f.", hitRate);
84 #endif
85 #ifdef SK_ENABLE_SMALL_PAGE
86 if (hitRate < 0.2) {
87 isRadicals = true;
88 }
89 #endif
90 }
91 resetHitCount();
92 count = 0;
93 }
94
95 for (int i = 0; i < kMaskFormatCount; ++i) {
96 if (fAtlases[i]) {
97 fAtlases[i]->compact(startTokenForNextFlush, isRadicals);
98 }
99 }
100 }
101
102 #ifdef SK_DUMP_ATLAS_IMAGE
103 #include "src/gpu/GrContextPriv.h"
104 #include "src/gpu/GrSurfaceContext.h"
105 #include "src/gpu/GrSurfaceProxy.h"
106 #include "src/gpu/GrTextureProxy.h"
107
108 #include "include/core/SkBitmap.h"
109 #include "include/core/SkImageEncoder.h"
110 #include "include/core/SkStream.h"
111 #include <stdio.h>
112
113 /**
114 * Write the contents of the surface proxy to a PNG. Returns true if successful.
115 * @param filename Full path to desired file
116 */
save_pixels(GrContext * context,GrSurfaceProxy * sProxy,GrColorType colorType,const char * filename)117 static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, GrColorType colorType,
118 const char* filename) {
119 if (!sProxy) {
120 return false;
121 }
122
123 SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(),
124 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
125 SkBitmap bm;
126 if (!bm.tryAllocPixels(ii)) {
127 return false;
128 }
129
130 sk_sp<GrSurfaceContext> sContext(context->priv().makeWrappedSurfaceContext(
131 sk_ref_sp(sProxy), colorType, kUnknown_SkAlphaType));
132 if (!sContext || !sContext->asTextureProxy()) {
133 return false;
134 }
135
136 bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), {0, 0});
137 if (!result) {
138 SkDebugf("------ failed to read pixels for %s\n", filename);
139 return false;
140 }
141
142 // remove any previous version of this file
143 remove(filename);
144
145 SkFILEWStream file(filename);
146 if (!file.isValid()) {
147 SkDebugf("------ failed to create file: %s\n", filename);
148 remove(filename); // remove any partial file
149 return false;
150 }
151
152 if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
153 SkDebugf("------ failed to encode %s\n", filename);
154 remove(filename); // remove any partial file
155 return false;
156 }
157
158 return true;
159 }
160
dump(GrContext * context) const161 void GrAtlasManager::dump(GrContext* context) const {
162 static int gDumpCount = 0;
163 for (int i = 0; i < kMaskFormatCount; ++i) {
164 if (fAtlases[i]) {
165 const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies();
166 for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
167 SkASSERT(proxies[pageIdx]);
168 SkString filename;
169 #ifdef SK_BUILD_FOR_ANDROID
170 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
171 #else
172 filename.printf("/data/storage/el2/base/cache/fontcache_%d_%d_%d.png", gDumpCount, i, pageIdx);
173 #endif
174 auto ct = mask_format_to_gr_color_type(AtlasIndexToMaskFormat(i));
175 save_pixels(context, proxies[pageIdx].get(), ct, filename.c_str());
176 }
177 }
178 }
179 ++gDumpCount;
180 }
181 #endif
182
setAtlasSizesToMinimum_ForTesting()183 void GrAtlasManager::setAtlasSizesToMinimum_ForTesting() {
184 // Delete any old atlases.
185 // This should be safe to do as long as we are not in the middle of a flush.
186 for (int i = 0; i < kMaskFormatCount; i++) {
187 fAtlases[i] = nullptr;
188 }
189
190 // Set all the atlas sizes to 1x1 plot each.
191 new (&fAtlasConfig) GrDrawOpAtlasConfig{};
192 }
193
initAtlas(GrMaskFormat format)194 bool GrAtlasManager::initAtlas(GrMaskFormat format) {
195 int index = MaskFormatToAtlasIndex(format);
196 if (fAtlases[index] == nullptr) {
197 GrColorType grColorType = mask_format_to_gr_color_type(format);
198 int pageNum = 4; // The maximum number of texture pages in the original skia code is 4
199 #ifdef SK_ENABLE_SMALL_PAGE
200 if (format == kA8_GrMaskFormat && fAtlasConfig.getARGBDimensions().width() > 512) {
201 // reset fAtlasConfig to suit small page.
202 pageNum = fAtlasConfig.resetAsSmallPage();
203 }
204 #endif
205 SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
206 SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
207
208 const GrBackendFormat format = fCaps->getDefaultBackendFormat(grColorType,
209 GrRenderable::kNo);
210
211 fAtlases[index] = GrDrawOpAtlas::Make(
212 fProxyProvider, format, grColorType,
213 atlasDimensions.width(), atlasDimensions.height(),
214 plotDimensions.width(), plotDimensions.height(),
215 fAllowMultitexturing, pageNum, fPlotOldThreshold, &GrStrikeCache::HandleEviction, fGlyphCache);
216 if (!fAtlases[index]) {
217 return false;
218 }
219 }
220 return true;
221 }
222