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