• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
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/graphite/text/TextAtlasManager.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/gpu/graphite/Recorder.h"
12 #include "src/base/SkAutoMalloc.h"
13 #include "src/core/SkDistanceFieldGen.h"
14 #include "src/core/SkMasks.h"
15 #include "src/gpu/graphite/AtlasProvider.h"
16 #include "src/gpu/graphite/DrawAtlas.h"
17 #include "src/gpu/graphite/RecorderPriv.h"
18 #include "src/gpu/graphite/TextureProxy.h"
19 #include "src/sksl/SkSLUtil.h"
20 #include "src/text/gpu/Glyph.h"
21 #include "src/text/gpu/GlyphVector.h"
22 #include "src/text/gpu/StrikeCache.h"
23 
24 using Glyph = sktext::gpu::Glyph;
25 
26 namespace skgpu::graphite {
27 
TextAtlasManager(Recorder * recorder)28 TextAtlasManager::TextAtlasManager(Recorder* recorder)
29         : fRecorder(recorder)
30         , fSupportBilerpAtlas{recorder->priv().caps()->supportBilerpFromGlyphAtlas()}
31         , fAtlasConfig{recorder->priv().caps()->maxTextureSize(),
32                        recorder->priv().caps()->glyphCacheTextureMaximumBytes()} {
33     if (!recorder->priv().caps()->allowMultipleAtlasTextures() ||
34         // multitexturing supported only if range can represent the index + texcoords fully
35         !(recorder->priv().caps()->shaderCaps()->fFloatIs32Bits ||
36           recorder->priv().caps()->shaderCaps()->fIntegerSupport)) {
37         fAllowMultitexturing = DrawAtlas::AllowMultitexturing::kNo;
38     } else {
39         fAllowMultitexturing = DrawAtlas::AllowMultitexturing::kYes;
40     }
41 }
42 
43 TextAtlasManager::~TextAtlasManager() = default;
44 
freeGpuResources()45 void TextAtlasManager::freeGpuResources() {
46     auto tokenTracker = fRecorder->priv().tokenTracker();
47     for (int i = 0; i < kMaskFormatCount; ++i) {
48         if (fAtlases[i]) {
49             fAtlases[i]->freeGpuResources(tokenTracker->nextFlushToken());
50         }
51     }
52 }
53 
hasGlyph(MaskFormat format,Glyph * glyph)54 bool TextAtlasManager::hasGlyph(MaskFormat format, Glyph* glyph) {
55     SkASSERT(glyph);
56     return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
57 }
58 
59 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)60 static void expand_bits(INT_TYPE* dst,
61                         const uint8_t* src,
62                         int width,
63                         int height,
64                         int dstRowBytes,
65                         int srcRowBytes) {
66     for (int y = 0; y < height; ++y) {
67         int rowWritesLeft = width;
68         const uint8_t* s = src;
69         INT_TYPE* d = dst;
70         while (rowWritesLeft > 0) {
71             unsigned mask = *s++;
72             for (int x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) {
73                 *d++ = (mask & (1 << x)) ? (INT_TYPE)(~0UL) : 0;
74             }
75         }
76         dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
77         src += srcRowBytes;
78     }
79 }
80 
get_packed_glyph_image(const SkGlyph & glyph,int dstRB,MaskFormat expectedMaskFormat,void * dst)81 static void get_packed_glyph_image(
82         const SkGlyph& glyph, int dstRB, MaskFormat expectedMaskFormat, void* dst) {
83     const int width = glyph.width();
84     const int height = glyph.height();
85     const void* src = glyph.image();
86     SkASSERT(src != nullptr);
87 
88     MaskFormat maskFormat = Glyph::FormatFromSkGlyph(glyph.maskFormat());
89     if (maskFormat == expectedMaskFormat) {
90         int srcRB = glyph.rowBytes();
91         // Notice this comparison is with the glyphs raw mask format, and not its MaskFormat.
92         if (glyph.maskFormat() != SkMask::kBW_Format) {
93             if (srcRB != dstRB) {
94                 const int bbp = MaskFormatBytesPerPixel(expectedMaskFormat);
95                 for (int y = 0; y < height; y++) {
96                     memcpy(dst, src, width * bbp);
97                     src = (const char*) src + srcRB;
98                     dst = (char*) dst + dstRB;
99                 }
100             } else {
101                 memcpy(dst, src, dstRB * height);
102             }
103         } else {
104             // Handle 8-bit format by expanding the mask to the expected format.
105             const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
106             switch (expectedMaskFormat) {
107                 case MaskFormat::kA8: {
108                     uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
109                     expand_bits(bytes, bits, width, height, dstRB, srcRB);
110                     break;
111                 }
112                 case MaskFormat::kA565: {
113                     uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
114                     expand_bits(rgb565, bits, width, height, dstRB, srcRB);
115                     break;
116                 }
117                 default:
118                     SK_ABORT("Invalid MaskFormat");
119             }
120         }
121     } else if (maskFormat == MaskFormat::kA565 &&
122                expectedMaskFormat == MaskFormat::kARGB) {
123         // Convert if the glyph uses a 565 mask format since it is using LCD text rendering
124         // but the expected format is 8888 (will happen on Intel MacOS with Metal since that
125         // combination does not support 565).
126         static constexpr SkMasks masks{
127                 {0b1111'1000'0000'0000, 11, 5},  // Red
128                 {0b0000'0111'1110'0000,  5, 6},  // Green
129                 {0b0000'0000'0001'1111,  0, 5},  // Blue
130                 {0, 0, 0}                        // Alpha
131         };
132         constexpr int a565Bpp = MaskFormatBytesPerPixel(MaskFormat::kA565);
133         constexpr int argbBpp = MaskFormatBytesPerPixel(MaskFormat::kARGB);
134         constexpr bool kBGRAIsNative = kN32_SkColorType == kBGRA_8888_SkColorType;
135         char* dstRow = (char*)dst;
136         for (int y = 0; y < height; y++) {
137             dst = dstRow;
138             for (int x = 0; x < width; x++) {
139                 uint16_t color565 = 0;
140                 memcpy(&color565, src, a565Bpp);
141                 uint32_t color8888;
142                 // On Windows (and possibly others), font data is stored as BGR.
143                 // So we need to swizzle the data to reflect that.
144                 if (kBGRAIsNative) {
145                     color8888 = masks.getBlue(color565) |
146                                 (masks.getGreen(color565) << 8) |
147                                 (masks.getRed(color565) << 16) |
148                                 (0xFF << 24);
149                 } else {
150                     color8888 = masks.getRed(color565) |
151                                 (masks.getGreen(color565) << 8) |
152                                 (masks.getBlue(color565) << 16) |
153                                 (0xFF << 24);
154                 }
155                 memcpy(dst, &color8888, argbBpp);
156                 src = (const char*)src + a565Bpp;
157                 dst = (      char*)dst + argbBpp;
158             }
159             dstRow += dstRB;
160         }
161     } else {
162         SkUNREACHABLE;
163     }
164 }
165 
resolveMaskFormat(MaskFormat format) const166 MaskFormat TextAtlasManager::resolveMaskFormat(MaskFormat format) const {
167     if (MaskFormat::kA565 == format &&
168         !fRecorder->priv().caps()->getDefaultSampledTextureInfo(kRGB_565_SkColorType,
169                                                                 /*mipmapped=*/Mipmapped::kNo,
170                                                                 Protected::kNo,
171                                                                 Renderable::kNo).isValid()) {
172         format = MaskFormat::kARGB;
173     }
174     return format;
175 }
176 
177 // Returns kSucceeded if glyph successfully added to texture atlas, kTryAgain if a RenderPassTask
178 // needs to be snapped before adding the glyph, and kError if it can't be added at all.
addGlyphToAtlas(const SkGlyph & skGlyph,Glyph * glyph,int srcPadding)179 DrawAtlas::ErrorCode TextAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
180                                                        Glyph* glyph,
181                                                        int srcPadding) {
182 #if !defined(SK_DISABLE_SDF_TEXT)
183     SkASSERT(0 <= srcPadding && srcPadding <= SK_DistanceFieldInset);
184 #else
185     SkASSERT(0 <= srcPadding);
186 #endif
187 
188     if (skGlyph.image() == nullptr) {
189         return DrawAtlas::ErrorCode::kError;
190     }
191     SkASSERT(glyph != nullptr);
192 
193     MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
194     MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
195     int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
196 
197     int padding;
198     switch (srcPadding) {
199         case 0:
200             // The direct mask/image case.
201             padding = 0;
202             if (fSupportBilerpAtlas) {
203                 // Force direct masks (glyph with no padding) to have padding.
204                 padding = 1;
205                 srcPadding = 1;
206             }
207             break;
208         case 1:
209             // The transformed mask/image case.
210             padding = 1;
211             break;
212 #if !defined(SK_DISABLE_SDF_TEXT)
213         case SK_DistanceFieldInset:
214             // The SDFT case.
215             // If the srcPadding == SK_DistanceFieldInset (SDFT case) then the padding is built
216             // into the image on the glyph; no extra padding needed.
217             // TODO: can the SDFT glyph image in the cache be reduced by the padding?
218             padding = 0;
219             break;
220 #endif
221         default:
222             // The padding is not one of the know forms.
223             return DrawAtlas::ErrorCode::kError;
224     }
225 
226     const int width = skGlyph.width() + 2*padding;
227     const int height = skGlyph.height() + 2*padding;
228     int rowBytes = width * bytesPerPixel;
229     size_t size = height * rowBytes;
230 
231     // Temporary storage for normalizing glyph image.
232     SkAutoSMalloc<1024> storage(size);
233     void* dataPtr = storage.get();
234     if (padding > 0) {
235         sk_bzero(dataPtr, size);
236         // Advance in one row and one column.
237         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
238     }
239 
240     get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
241 
242     DrawAtlas* atlas = this->getAtlas(expectedMaskFormat);
243     auto errorCode = atlas->addToAtlas(fRecorder,
244                                        width,
245                                        height,
246                                        storage.get(),
247                                        &glyph->fAtlasLocator);
248 
249     if (errorCode == DrawAtlas::ErrorCode::kSucceeded) {
250         glyph->fAtlasLocator.insetSrc(srcPadding);
251     }
252 
253     return errorCode;
254 }
255 
recordUploads(DrawContext * dc)256 bool TextAtlasManager::recordUploads(DrawContext* dc) {
257     for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
258         if (fAtlases[i] && !fAtlases[i]->recordUploads(dc, fRecorder)) {
259             return false;
260         }
261     }
262 
263     return true;
264 }
265 
addGlyphToBulkAndSetUseToken(BulkUsePlotUpdater * updater,MaskFormat format,Glyph * glyph,AtlasToken token)266 void TextAtlasManager::addGlyphToBulkAndSetUseToken(BulkUsePlotUpdater* updater,
267                                                     MaskFormat format,
268                                                     Glyph* glyph,
269                                                     AtlasToken token) {
270     SkASSERT(glyph);
271     if (updater->add(glyph->fAtlasLocator)) {
272         this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
273     }
274 }
275 
setAtlasDimensionsToMinimum_ForTesting()276 void TextAtlasManager::setAtlasDimensionsToMinimum_ForTesting() {
277     // Delete any old atlases.
278     // This should be safe to do as long as we are not in the middle of a flush.
279     for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
280         fAtlases[i] = nullptr;
281     }
282 
283     // Set all the atlas sizes to 1x1 plot each.
284     new (&fAtlasConfig) DrawAtlasConfig{2048, 0};
285 }
286 
initAtlas(MaskFormat format)287 bool TextAtlasManager::initAtlas(MaskFormat format) {
288     int index = MaskFormatToAtlasIndex(format);
289     if (fAtlases[index] == nullptr) {
290         SkColorType colorType = MaskFormatToColorType(format);
291         SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
292         SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
293         fAtlases[index] = DrawAtlas::Make(colorType,
294                                           SkColorTypeBytesPerPixel(colorType),
295                                           atlasDimensions.width(), atlasDimensions.height(),
296                                           plotDimensions.width(), plotDimensions.height(),
297                                           /*generationCounter=*/this,
298                                           fAllowMultitexturing,
299                                           DrawAtlas::UseStorageTextures::kNo,
300                                           /*evictor=*/nullptr,
301                                           /*label=*/"TextAtlas");
302         if (!fAtlases[index]) {
303             return false;
304         }
305     }
306     return true;
307 }
308 
compact()309 void TextAtlasManager::compact() {
310     auto tokenTracker = fRecorder->priv().tokenTracker();
311     for (int i = 0; i < kMaskFormatCount; ++i) {
312         if (fAtlases[i]) {
313             fAtlases[i]->compact(tokenTracker->nextFlushToken());
314         }
315     }
316 }
317 
318 }  // namespace skgpu::graphite
319 
320 ////////////////////////////////////////////////////////////////////////////////////////////////
321 
322 namespace sktext::gpu {
323 
324 using DrawAtlas = skgpu::graphite::DrawAtlas;
325 
regenerateAtlasForGraphite(int begin,int end,skgpu::MaskFormat maskFormat,int srcPadding,skgpu::graphite::Recorder * recorder)326 std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
327                                                               int end,
328                                                               skgpu::MaskFormat maskFormat,
329                                                               int srcPadding,
330                                                               skgpu::graphite::Recorder* recorder) {
331     auto atlasManager = recorder->priv().atlasProvider()->textAtlasManager();
332     auto tokenTracker = recorder->priv().tokenTracker();
333 
334     // TODO: this is not a great place for this -- need a better way to init atlases when needed
335     unsigned int numActiveProxies;
336     const sk_sp<skgpu::graphite::TextureProxy>* proxies =
337             atlasManager->getProxies(maskFormat, &numActiveProxies);
338     if (!proxies) {
339         SkDebugf("Could not allocate backing texture for atlas\n");
340         return {false, 0};
341     }
342 
343     uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
344 
345     this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
346 
347     if (fAtlasGeneration != currentAtlasGen) {
348         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
349         // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
350         fBulkUseUpdater.reset();
351 
352         SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()};
353 
354         // Update the atlas information in the GrStrike.
355         auto glyphs = fGlyphs.subspan(begin, end - begin);
356         int glyphsPlacedInAtlas = 0;
357         bool success = true;
358         for (const Variant& variant : glyphs) {
359             Glyph* gpuGlyph = variant.glyph;
360             SkASSERT(gpuGlyph != nullptr);
361 
362             if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
363                 const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
364                 auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
365                 if (code != DrawAtlas::ErrorCode::kSucceeded) {
366                     success = code != DrawAtlas::ErrorCode::kError;
367                     break;
368                 }
369             }
370             atlasManager->addGlyphToBulkAndSetUseToken(
371                     &fBulkUseUpdater, maskFormat, gpuGlyph,
372                     tokenTracker->nextFlushToken());
373             glyphsPlacedInAtlas++;
374         }
375 
376         // Update atlas generation if there are no more glyphs to put in the atlas.
377         if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
378             // Need to get the freshest value of the atlas' generation because
379             // updateTextureCoordinates may have changed it.
380             fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
381         }
382 
383         return {success, glyphsPlacedInAtlas};
384     } else {
385         // The atlas hasn't changed, so our texture coordinates are still valid.
386         if (end == SkCount(fGlyphs)) {
387             // The atlas hasn't changed and the texture coordinates are all still valid. Update
388             // all the plots used to the new use token.
389             atlasManager->setUseTokenBulk(fBulkUseUpdater,
390                                           tokenTracker->nextFlushToken(),
391                                           maskFormat);
392         }
393         return {true, end - begin};
394     }
395 }
396 
397 }  // namespace sktext::gpu
398