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