blob: 208e7077d079e8b8483325cf1bcacf63e4ecec74 [file] [log] [blame]
Robert Phillipsc4039ea2018-03-01 11:36:45 -05001/*
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 "GrAtlasManager.h"
9
10#include "GrCaps.h"
11#include "GrGlyph.h"
12#include "GrGlyphCache.h"
13#include "GrProxyProvider.h"
14
Robert Phillipsaf4adef2018-03-07 11:59:37 -050015
16void GrRestrictedAtlasManager::ComputeAtlasLimits(const GrCaps* caps, float maxTextureBytes,
17 int* maxDim, int* minDim,
18 int* maxPlot, int* minPlot) {
19 SkASSERT(maxDim && minDim && maxPlot && minPlot);
20
21 // Calculate RGBA size. Must be between 512 x 256 and MaxTextureSize x MaxTextureSize / 2
22 int log2MaxTextureSize = SkPrevLog2(caps->maxTextureSize());
23 int log2MaxDim = 9;
24 for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) {
25 int maxDimTmp = 1 << log2MaxDim;
26 int minDimTmp = 1 << (log2MaxDim - 1);
27
28 if (maxDimTmp * minDimTmp * 4 >= maxTextureBytes) {
29 break;
30 }
31 }
32
33
34 int log2MinDim = log2MaxDim - 1;
35 *maxDim = 1 << log2MaxDim;
36 *minDim = 1 << log2MinDim;
37 // Plots are either 256 or 512.
38 *maxPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 2)));
39 *minPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 3)));
40}
41
Robert Phillipsc4039ea2018-03-01 11:36:45 -050042GrRestrictedAtlasManager::GrRestrictedAtlasManager(
43 sk_sp<const GrCaps> caps,
44 float maxTextureBytes,
45 GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)
46 : fCaps(std::move(caps))
47 , fAllowMultitexturing(allowMultitexturing) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -050048
Robert Phillipsaf4adef2018-03-07 11:59:37 -050049 int maxDim, minDim, maxPlot, minPlot;
50 ComputeAtlasLimits(fCaps.get(), maxTextureBytes, &maxDim, &minDim, &maxPlot, &minPlot);
Robert Phillipsc4039ea2018-03-01 11:36:45 -050051
52 // Setup default atlas configs. The A8 atlas uses maxDim for both width and height, as the A8
53 // format is already very compact.
54 fAtlasConfigs[kA8_GrMaskFormat].fWidth = maxDim;
55 fAtlasConfigs[kA8_GrMaskFormat].fHeight = maxDim;
56 fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = maxPlot;
57 fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = minPlot;
58
59 // A565 and ARGB use maxDim x minDim.
60 fAtlasConfigs[kA565_GrMaskFormat].fWidth = minDim;
61 fAtlasConfigs[kA565_GrMaskFormat].fHeight = maxDim;
62 fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = minPlot;
63 fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = minPlot;
64
65 fAtlasConfigs[kARGB_GrMaskFormat].fWidth = minDim;
66 fAtlasConfigs[kARGB_GrMaskFormat].fHeight = maxDim;
67 fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = minPlot;
68 fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = minPlot;
69
70 fGlyphSizeLimit = minPlot;
71}
72
73GrRestrictedAtlasManager::~GrRestrictedAtlasManager() {
74}
75
76static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format, const GrCaps& caps) {
77 switch (format) {
78 case kA8_GrMaskFormat:
79 return kAlpha_8_GrPixelConfig;
80 case kA565_GrMaskFormat:
81 return kRGB_565_GrPixelConfig;
82 case kARGB_GrMaskFormat:
83 return caps.srgbSupport() ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
84 default:
85 SkDEBUGFAIL("unsupported GrMaskFormat");
86 return kAlpha_8_GrPixelConfig;
87 }
88}
89
90//////////////////////////////////////////////////////////////////////////////////////////////////
91GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider, GrGlyphCache* glyphCache,
92 float maxTextureBytes,
93 GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)
94 : INHERITED(proxyProvider->refCaps(), maxTextureBytes, allowMultitexturing)
95 , fProxyProvider(proxyProvider)
96 , fGlyphCache(glyphCache) {
97}
98
99void GrAtlasManager::freeAll() {
100 for (int i = 0; i < kMaskFormatCount; ++i) {
101 fAtlases[i] = nullptr;
102 }
103}
104
105bool GrAtlasManager::hasGlyph(GrGlyph* glyph) {
106 SkASSERT(glyph);
107 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
108}
109
110// add to texture atlas that matches this format
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500111GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(
112 GrResourceProvider* resourceProvider,
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500113 GrGlyphCache* glyphCache,
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500114 GrTextStrike* strike, GrDrawOpAtlas::AtlasID* id,
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500115 GrDeferredUploadTarget* target, GrMaskFormat format,
116 int width, int height, const void* image, SkIPoint16* loc) {
117 glyphCache->setStrikeToPreserve(strike);
118 return this->getAtlas(format)->addToAtlas(resourceProvider, id, target, width, height,
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500119 image, loc);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500120}
121
122void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater,
123 GrGlyph* glyph,
124 GrDeferredUploadToken token) {
125 SkASSERT(glyph);
126 updater->add(glyph->fID);
127 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
128}
129
130#ifdef SK_DEBUG
131#include "GrContextPriv.h"
132#include "GrSurfaceProxy.h"
133#include "GrSurfaceContext.h"
134#include "GrTextureProxy.h"
135
136#include "SkBitmap.h"
137#include "SkImageEncoder.h"
138#include "SkStream.h"
139#include <stdio.h>
140
141/**
142 * Write the contents of the surface proxy to a PNG. Returns true if successful.
143 * @param filename Full path to desired file
144 */
145static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char* filename) {
146 if (!sProxy) {
147 return false;
148 }
149
150 SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(),
151 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
152 SkBitmap bm;
153 if (!bm.tryAllocPixels(ii)) {
154 return false;
155 }
156
157 sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
158 sk_ref_sp(sProxy)));
159 if (!sContext || !sContext->asTextureProxy()) {
160 return false;
161 }
162
163 bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), 0, 0);
164 if (!result) {
165 SkDebugf("------ failed to read pixels for %s\n", filename);
166 return false;
167 }
168
169 // remove any previous version of this file
170 remove(filename);
171
172 SkFILEWStream file(filename);
173 if (!file.isValid()) {
174 SkDebugf("------ failed to create file: %s\n", filename);
175 remove(filename); // remove any partial file
176 return false;
177 }
178
179 if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
180 SkDebugf("------ failed to encode %s\n", filename);
181 remove(filename); // remove any partial file
182 return false;
183 }
184
185 return true;
186}
187
188void GrAtlasManager::dump(GrContext* context) const {
189 static int gDumpCount = 0;
190 for (int i = 0; i < kMaskFormatCount; ++i) {
191 if (fAtlases[i]) {
192 const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies();
193 for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
194 SkASSERT(proxies[pageIdx]);
195 SkString filename;
196#ifdef SK_BUILD_FOR_ANDROID
197 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
198#else
199 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
200#endif
201
202 save_pixels(context, proxies[pageIdx].get(), filename.c_str());
203 }
204 }
205 }
206 ++gDumpCount;
207}
208#endif
209
210void GrAtlasManager::setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]) {
211 // Delete any old atlases.
212 // This should be safe to do as long as we are not in the middle of a flush.
213 for (int i = 0; i < kMaskFormatCount; i++) {
214 fAtlases[i] = nullptr;
215 }
216 memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs));
217}
218
219bool GrAtlasManager::initAtlas(GrMaskFormat format) {
220 int index = MaskFormatToAtlasIndex(format);
221 if (!fAtlases[index]) {
222 GrPixelConfig config = mask_format_to_pixel_config(format, *fCaps);
223 int width = fAtlasConfigs[index].fWidth;
224 int height = fAtlasConfigs[index].fHeight;
225 int numPlotsX = fAtlasConfigs[index].numPlotsX();
226 int numPlotsY = fAtlasConfigs[index].numPlotsY();
227
228 fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, config, width, height,
229 numPlotsX, numPlotsY, fAllowMultitexturing,
230 &GrGlyphCache::HandleEviction,
231 fGlyphCache);
232 if (!fAtlases[index]) {
233 return false;
234 }
235 }
236 return true;
237}