• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/ganesh/text/GrAtlasManager.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkEncodedImageFormat.h"
12 #include "src/base/SkAutoMalloc.h"
13 #include "src/codec/SkMasks.h"
14 #include "src/core/SkDistanceFieldGen.h"
15 #include "src/gpu/ganesh/GrImageInfo.h"
16 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
17 #include "src/text/gpu/Glyph.h"
18 #include "src/text/gpu/GlyphVector.h"
19 #include "src/text/gpu/StrikeCache.h"
20 
21 using Glyph = sktext::gpu::Glyph;
22 using MaskFormat = skgpu::MaskFormat;
23 
GrAtlasManager(GrProxyProvider * proxyProvider,size_t maxTextureBytes,GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,bool supportBilerpAtlas)24 GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider,
25                                size_t maxTextureBytes,
26                                GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,
27                                bool supportBilerpAtlas)
28             : fAllowMultitexturing{allowMultitexturing}
29             , fSupportBilerpAtlas{supportBilerpAtlas}
30             , fProxyProvider{proxyProvider}
31             , fCaps{fProxyProvider->refCaps()}
32             , fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { }
33 
34 GrAtlasManager::~GrAtlasManager() = default;
35 
freeAll()36 void GrAtlasManager::freeAll() {
37     for (int i = 0; i < skgpu::kMaskFormatCount; ++i) {
38         fAtlases[i] = nullptr;
39     }
40 }
41 
hasGlyph(MaskFormat format,Glyph * glyph)42 bool GrAtlasManager::hasGlyph(MaskFormat format, Glyph* glyph) {
43     SkASSERT(glyph);
44     return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
45 }
46 
47 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)48 static void expand_bits(INT_TYPE* dst,
49                         const uint8_t* src,
50                         int width,
51                         int height,
52                         int dstRowBytes,
53                         int srcRowBytes) {
54     for (int y = 0; y < height; ++y) {
55         int rowWritesLeft = width;
56         const uint8_t* s = src;
57         INT_TYPE* d = dst;
58         while (rowWritesLeft > 0) {
59             unsigned mask = *s++;
60             for (int x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) {
61                 *d++ = (mask & (1 << x)) ? (INT_TYPE)(~0UL) : 0;
62             }
63         }
64         dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
65         src += srcRowBytes;
66     }
67 }
68 
get_packed_glyph_image(const SkGlyph & glyph,int dstRB,MaskFormat expectedMaskFormat,void * dst)69 static void get_packed_glyph_image(
70         const SkGlyph& glyph, int dstRB, MaskFormat expectedMaskFormat, void* dst) {
71     const int width = glyph.width();
72     const int height = glyph.height();
73     const void* src = glyph.image();
74     SkASSERT(src != nullptr);
75 
76     MaskFormat maskFormat = Glyph::FormatFromSkGlyph(glyph.maskFormat());
77     if (maskFormat == expectedMaskFormat) {
78         int srcRB = glyph.rowBytes();
79         // Notice this comparison is with the glyphs raw mask format, and not its MaskFormat.
80         if (glyph.maskFormat() != SkMask::kBW_Format) {
81             if (srcRB != dstRB) {
82                 const int bbp = MaskFormatBytesPerPixel(expectedMaskFormat);
83                 for (int y = 0; y < height; y++) {
84                     memcpy(dst, src, width * bbp);
85                     src = (const char*) src + srcRB;
86                     dst = (char*) dst + dstRB;
87                 }
88             } else {
89                 memcpy(dst, src, dstRB * height);
90             }
91         } else {
92             // Handle 8-bit format by expanding the mask to the expected format.
93             const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
94             switch (expectedMaskFormat) {
95                 case MaskFormat::kA8: {
96                     uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
97                     expand_bits(bytes, bits, width, height, dstRB, srcRB);
98                     break;
99                 }
100                 case MaskFormat::kA565: {
101                     uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
102                     expand_bits(rgb565, bits, width, height, dstRB, srcRB);
103                     break;
104                 }
105                 default:
106                     SK_ABORT("Invalid MaskFormat");
107             }
108         }
109     } else if (maskFormat == MaskFormat::kA565 &&
110                expectedMaskFormat == MaskFormat::kARGB) {
111         // Convert if the glyph uses a 565 mask format since it is using LCD text rendering
112         // but the expected format is 8888 (will happen on macOS with Metal since that
113         // combination does not support 565).
114         static constexpr SkMasks masks{
115                 {0b1111'1000'0000'0000, 11, 5},  // Red
116                 {0b0000'0111'1110'0000,  5, 6},  // Green
117                 {0b0000'0000'0001'1111,  0, 5},  // Blue
118                 {0, 0, 0}                        // Alpha
119         };
120         constexpr int a565Bpp = MaskFormatBytesPerPixel(MaskFormat::kA565);
121         constexpr int argbBpp = MaskFormatBytesPerPixel(MaskFormat::kARGB);
122         char* dstRow = (char*)dst;
123         for (int y = 0; y < height; y++) {
124             dst = dstRow;
125             for (int x = 0; x < width; x++) {
126                 uint16_t color565 = 0;
127                 memcpy(&color565, src, a565Bpp);
128                 uint32_t colorRGBA = GrColorPackRGBA(masks.getRed(color565),
129                                                      masks.getGreen(color565),
130                                                      masks.getBlue(color565),
131                                                      0xFF);
132                 memcpy(dst, &colorRGBA, argbBpp);
133                 src = (char*)src + a565Bpp;
134                 dst = (char*)dst + argbBpp;
135             }
136             dstRow += dstRB;
137         }
138     } else {
139         SkUNREACHABLE;
140     }
141 }
142 
143 // returns true if glyph successfully added to texture atlas, false otherwise.
addGlyphToAtlas(const SkGlyph & skGlyph,Glyph * glyph,int srcPadding,GrResourceProvider * resourceProvider,GrDeferredUploadTarget * uploadTarget)144 GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
145                                                          Glyph* glyph,
146                                                          int srcPadding,
147                                                          GrResourceProvider* resourceProvider,
148                                                          GrDeferredUploadTarget* uploadTarget) {
149 #if !defined(SK_DISABLE_SDF_TEXT)
150     SkASSERT(0 <= srcPadding && srcPadding <= SK_DistanceFieldInset);
151 #else
152     SkASSERT(0 <= srcPadding);
153 #endif
154 
155     if (skGlyph.image() == nullptr) {
156         return GrDrawOpAtlas::ErrorCode::kError;
157     }
158     SkASSERT(glyph != nullptr);
159 
160     MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
161     MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
162     int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
163 
164     int padding;
165     switch (srcPadding) {
166         case 0:
167             // The direct mask/image case.
168             padding = 0;
169             if (fSupportBilerpAtlas) {
170                 // Force direct masks (glyph with no padding) to have padding.
171                 padding = 1;
172                 srcPadding = 1;
173             }
174             break;
175         case 1:
176             // The transformed mask/image case.
177             padding = 1;
178             break;
179 #if !defined(SK_DISABLE_SDF_TEXT)
180         case SK_DistanceFieldInset:
181             // The SDFT case.
182             // If the srcPadding == SK_DistanceFieldInset (SDFT case) then the padding is built
183             // into the image on the glyph; no extra padding needed.
184             // TODO: can the SDFT glyph image in the cache be reduced by the padding?
185             padding = 0;
186             break;
187 #endif
188         default:
189             // The padding is not one of the know forms.
190             return GrDrawOpAtlas::ErrorCode::kError;
191     }
192 
193     const int width = skGlyph.width() + 2*padding;
194     const int height = skGlyph.height() + 2*padding;
195     int rowBytes = width * bytesPerPixel;
196     size_t size = height * rowBytes;
197 
198     // Temporary storage for normalizing glyph image.
199     SkAutoSMalloc<1024> storage(size);
200     void* dataPtr = storage.get();
201     if (padding > 0) {
202         sk_bzero(dataPtr, size);
203         // Advance in one row and one column.
204         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
205     }
206 
207     get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
208 
209     auto errorCode = this->addToAtlas(resourceProvider,
210                                       uploadTarget,
211                                       expectedMaskFormat,
212                                       width,
213                                       height,
214                                       storage.get(),
215                                       &glyph->fAtlasLocator);
216 
217     if (errorCode == GrDrawOpAtlas::ErrorCode::kSucceeded) {
218         glyph->fAtlasLocator.insetSrc(srcPadding);
219     }
220 
221     return errorCode;
222 }
223 
224 // add to texture atlas that matches this format
addToAtlas(GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,MaskFormat format,int width,int height,const void * image,skgpu::AtlasLocator * atlasLocator)225 GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider,
226                                                     GrDeferredUploadTarget* target,
227                                                     MaskFormat format,
228                                                     int width, int height, const void* image,
229                                                     skgpu::AtlasLocator* atlasLocator) {
230     return this->getAtlas(format)->addToAtlas(resourceProvider, target, width, height, image,
231                                               atlasLocator);
232 }
233 
addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater * updater,MaskFormat format,Glyph * glyph,skgpu::AtlasToken token)234 void GrAtlasManager::addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater* updater,
235                                                   MaskFormat format, Glyph* glyph,
236                                                   skgpu::AtlasToken token) {
237     SkASSERT(glyph);
238     if (updater->add(glyph->fAtlasLocator)) {
239         this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
240     }
241 }
242 
243 #ifdef SK_DEBUG
244 #include "include/gpu/GrDirectContext.h"
245 #include "src/gpu/ganesh/GrDirectContextPriv.h"
246 #include "src/gpu/ganesh/GrSurfaceProxy.h"
247 #include "src/gpu/ganesh/GrTextureProxy.h"
248 #include "src/gpu/ganesh/SurfaceContext.h"
249 
250 #include "include/core/SkBitmap.h"
251 #include "include/core/SkImageEncoder.h"
252 #include "include/core/SkStream.h"
253 #include <stdio.h>
254 
255 /**
256   * Write the contents of the surface proxy to a PNG. Returns true if successful.
257   * @param filename      Full path to desired file
258   */
save_pixels(GrDirectContext * dContext,GrSurfaceProxyView view,GrColorType colorType,const char * filename)259 static bool save_pixels(GrDirectContext* dContext, GrSurfaceProxyView view, GrColorType colorType,
260                         const char* filename) {
261     if (!view.proxy()) {
262         return false;
263     }
264 
265     auto ii = SkImageInfo::Make(view.proxy()->dimensions(), kRGBA_8888_SkColorType,
266                                 kPremul_SkAlphaType);
267     SkBitmap bm;
268     if (!bm.tryAllocPixels(ii)) {
269         return false;
270     }
271 
272     auto sContext = dContext->priv().makeSC(std::move(view),
273                                             {colorType, kUnknown_SkAlphaType, nullptr});
274     if (!sContext || !sContext->asTextureProxy()) {
275         return false;
276     }
277 
278     bool result = sContext->readPixels(dContext, bm.pixmap(), {0, 0});
279     if (!result) {
280         SkDebugf("------ failed to read pixels for %s\n", filename);
281         return false;
282     }
283 
284     // remove any previous version of this file
285     remove(filename);
286 
287     SkFILEWStream file(filename);
288     if (!file.isValid()) {
289         SkDebugf("------ failed to create file: %s\n", filename);
290         remove(filename);   // remove any partial file
291         return false;
292     }
293 
294     if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
295         SkDebugf("------ failed to encode %s\n", filename);
296         remove(filename);   // remove any partial file
297         return false;
298     }
299 
300     return true;
301 }
302 
dump(GrDirectContext * context) const303 void GrAtlasManager::dump(GrDirectContext* context) const {
304     static int gDumpCount = 0;
305     for (int i = 0; i < skgpu::kMaskFormatCount; ++i) {
306         if (fAtlases[i]) {
307             const GrSurfaceProxyView* views = fAtlases[i]->getViews();
308             for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
309                 SkASSERT(views[pageIdx].proxy());
310                 SkString filename;
311 #ifdef SK_BUILD_FOR_ANDROID
312                 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
313 #else
314                 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
315 #endif
316                 SkColorType ct = MaskFormatToColorType(AtlasIndexToMaskFormat(i));
317                 save_pixels(context, views[pageIdx], SkColorTypeToGrColorType(ct),
318                             filename.c_str());
319             }
320         }
321     }
322     ++gDumpCount;
323 }
324 #endif
325 
setAtlasDimensionsToMinimum_ForTesting()326 void GrAtlasManager::setAtlasDimensionsToMinimum_ForTesting() {
327     // Delete any old atlases.
328     // This should be safe to do as long as we are not in the middle of a flush.
329     for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
330         fAtlases[i] = nullptr;
331     }
332 
333     // Set all the atlas sizes to 1x1 plot each.
334     new (&fAtlasConfig) GrDrawOpAtlasConfig{};
335 }
336 
initAtlas(MaskFormat format)337 bool GrAtlasManager::initAtlas(MaskFormat format) {
338     int index = MaskFormatToAtlasIndex(format);
339     if (fAtlases[index] == nullptr) {
340         SkColorType colorType = MaskFormatToColorType(format);
341         GrColorType grColorType = SkColorTypeToGrColorType(colorType);
342         SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
343         SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
344 
345         const GrBackendFormat backendFormat =
346                 fCaps->getDefaultBackendFormat(grColorType, GrRenderable::kNo);
347 
348         fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, backendFormat,
349                                               GrColorTypeToSkColorType(grColorType),
350                                               GrColorTypeBytesPerPixel(grColorType),
351                                               atlasDimensions.width(), atlasDimensions.height(),
352                                               plotDimensions.width(), plotDimensions.height(),
353                                               this,
354                                               fAllowMultitexturing,
355                                               nullptr,
356                                               /*label=*/"TextAtlas");
357         if (!fAtlases[index]) {
358             return false;
359         }
360     }
361     return true;
362 }
363 
364 ////////////////////////////////////////////////////////////////////////////////////////////////
365 
366 namespace sktext::gpu {
367 
regenerateAtlas(int begin,int end,MaskFormat maskFormat,int srcPadding,GrMeshDrawTarget * target)368 std::tuple<bool, int> GlyphVector::regenerateAtlas(int begin, int end,
369                                                    MaskFormat maskFormat,
370                                                    int srcPadding,
371                                                    GrMeshDrawTarget* target) {
372     GrAtlasManager* atlasManager = target->atlasManager();
373     GrDeferredUploadTarget* uploadTarget = target->deferredUploadTarget();
374 
375     uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
376 
377     this->packedGlyphIDToGlyph(target->strikeCache());
378 
379     if (fAtlasGeneration != currentAtlasGen) {
380         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
381         // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
382         fBulkUseUpdater.reset();
383 
384         SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()};
385 
386         // Update the atlas information in the GrStrike.
387         auto tokenTracker = uploadTarget->tokenTracker();
388         auto glyphs = fGlyphs.subspan(begin, end - begin);
389         int glyphsPlacedInAtlas = 0;
390         bool success = true;
391         for (const Variant& variant : glyphs) {
392             Glyph* gpuGlyph = variant.glyph;
393             SkASSERT(gpuGlyph != nullptr);
394 
395             if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
396                 const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
397                 auto code = atlasManager->addGlyphToAtlas(
398                         skGlyph, gpuGlyph, srcPadding, target->resourceProvider(), uploadTarget);
399                 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
400                     success = code != GrDrawOpAtlas::ErrorCode::kError;
401                     break;
402                 }
403             }
404             atlasManager->addGlyphToBulkAndSetUseToken(
405                     &fBulkUseUpdater, maskFormat, gpuGlyph,
406                     tokenTracker->nextDrawToken());
407             glyphsPlacedInAtlas++;
408         }
409 
410         // Update atlas generation if there are no more glyphs to put in the atlas.
411         if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
412             // Need to get the freshest value of the atlas' generation because
413             // updateTextureCoordinates may have changed it.
414             fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
415         }
416 
417         return {success, glyphsPlacedInAtlas};
418     } else {
419         // The atlas hasn't changed, so our texture coordinates are still valid.
420         if (end == SkCount(fGlyphs)) {
421             // The atlas hasn't changed and the texture coordinates are all still valid. Update
422             // all the plots used to the new use token.
423             atlasManager->setUseTokenBulk(fBulkUseUpdater,
424                                           uploadTarget->tokenTracker()->nextDrawToken(),
425                                           maskFormat);
426         }
427         return {true, end - begin};
428     }
429 }
430 
431 }  // namespace sktext::gpu
432