xref: /aosp_15_r20/external/skia/src/gpu/ganesh/text/GrAtlasManager.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkColorType.h"
11 #include "include/core/SkSize.h"
12 #include "include/core/SkSpan.h"
13 #include "include/private/base/SkMalloc.h"
14 #include "include/private/base/SkTLogic.h"
15 #include "src/base/SkAutoMalloc.h"
16 #include "src/core/SkDistanceFieldGen.h"
17 #include "src/core/SkGlyph.h"
18 #include "src/core/SkMask.h"
19 #include "src/core/SkMasks.h"
20 #include "src/core/SkStrikeSpec.h"
21 #include "src/gpu/ganesh/GrColor.h"
22 #include "src/gpu/ganesh/GrDeferredUpload.h"
23 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
24 #include "src/text/gpu/Glyph.h"
25 #include "src/text/gpu/GlyphVector.h"
26 #include "src/text/gpu/StrikeCache.h"
27 
28 #include <cstring>
29 #include <tuple>
30 
31 using Glyph = sktext::gpu::Glyph;
32 using MaskFormat = skgpu::MaskFormat;
33 
GrAtlasManager(GrProxyProvider * proxyProvider,size_t maxTextureBytes,GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,bool supportBilerpAtlas)34 GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider,
35                                size_t maxTextureBytes,
36                                GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,
37                                bool supportBilerpAtlas)
38             : fAllowMultitexturing{allowMultitexturing}
39             , fSupportBilerpAtlas{supportBilerpAtlas}
40             , fProxyProvider{proxyProvider}
41             , fCaps{fProxyProvider->refCaps()}
42             , fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { }
43 
44 GrAtlasManager::~GrAtlasManager() = default;
45 
freeAll()46 void GrAtlasManager::freeAll() {
47     for (int i = 0; i < skgpu::kMaskFormatCount; ++i) {
48         fAtlases[i] = nullptr;
49     }
50 }
51 
hasGlyph(MaskFormat format,Glyph * glyph)52 bool GrAtlasManager::hasGlyph(MaskFormat format, Glyph* glyph) {
53     SkASSERT(glyph);
54     return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
55 }
56 
57 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)58 static void expand_bits(INT_TYPE* dst,
59                         const uint8_t* src,
60                         int width,
61                         int height,
62                         int dstRowBytes,
63                         int srcRowBytes) {
64     for (int y = 0; y < height; ++y) {
65         int rowWritesLeft = width;
66         const uint8_t* s = src;
67         INT_TYPE* d = dst;
68         while (rowWritesLeft > 0) {
69             unsigned mask = *s++;
70             for (int x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) {
71                 *d++ = (mask & (1 << x)) ? (INT_TYPE)(~0UL) : 0;
72             }
73         }
74         dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
75         src += srcRowBytes;
76     }
77 }
78 
get_packed_glyph_image(const SkGlyph & glyph,int dstRB,MaskFormat expectedMaskFormat,void * dst)79 static void get_packed_glyph_image(
80         const SkGlyph& glyph, int dstRB, MaskFormat expectedMaskFormat, void* dst) {
81     const int width = glyph.width();
82     const int height = glyph.height();
83     const void* src = glyph.image();
84     SkASSERT(src != nullptr);
85 
86     MaskFormat maskFormat = Glyph::FormatFromSkGlyph(glyph.maskFormat());
87     if (maskFormat == expectedMaskFormat) {
88         int srcRB = glyph.rowBytes();
89         // Notice this comparison is with the glyphs raw mask format, and not its MaskFormat.
90         if (glyph.maskFormat() != SkMask::kBW_Format) {
91             if (srcRB != dstRB) {
92                 const int bbp = MaskFormatBytesPerPixel(expectedMaskFormat);
93                 for (int y = 0; y < height; y++) {
94                     memcpy(dst, src, width * bbp);
95                     src = (const char*) src + srcRB;
96                     dst = (char*) dst + dstRB;
97                 }
98             } else {
99                 memcpy(dst, src, dstRB * height);
100             }
101         } else {
102             // Handle 8-bit format by expanding the mask to the expected format.
103             const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
104             switch (expectedMaskFormat) {
105                 case MaskFormat::kA8: {
106                     uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
107                     expand_bits(bytes, bits, width, height, dstRB, srcRB);
108                     break;
109                 }
110                 case MaskFormat::kA565: {
111                     uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
112                     expand_bits(rgb565, bits, width, height, dstRB, srcRB);
113                     break;
114                 }
115                 default:
116                     SK_ABORT("Invalid MaskFormat");
117             }
118         }
119     } else if (maskFormat == MaskFormat::kA565 &&
120                expectedMaskFormat == MaskFormat::kARGB) {
121         // Convert if the glyph uses a 565 mask format since it is using LCD text rendering
122         // but the expected format is 8888 (will happen on macOS with Metal since that
123         // combination does not support 565).
124         static constexpr SkMasks masks{
125                 {0b1111'1000'0000'0000, 11, 5},  // Red
126                 {0b0000'0111'1110'0000,  5, 6},  // Green
127                 {0b0000'0000'0001'1111,  0, 5},  // Blue
128                 {0, 0, 0}                        // Alpha
129         };
130         constexpr int a565Bpp = MaskFormatBytesPerPixel(MaskFormat::kA565);
131         constexpr int argbBpp = MaskFormatBytesPerPixel(MaskFormat::kARGB);
132         constexpr bool kBGRAIsNative = kN32_SkColorType == kBGRA_8888_SkColorType;
133         char* dstRow = (char*)dst;
134         for (int y = 0; y < height; y++) {
135             dst = dstRow;
136             for (int x = 0; x < width; x++) {
137                 uint16_t color565 = 0;
138                 memcpy(&color565, src, a565Bpp);
139                 uint32_t color8888;
140                 // On Windows (and possibly others), font data is stored as BGR.
141                 // So we need to swizzle the data to reflect that.
142                 if (kBGRAIsNative) {
143                     color8888 = GrColorPackRGBA(masks.getBlue(color565),
144                                                 masks.getGreen(color565),
145                                                 masks.getRed(color565),
146                                                 0xFF);
147                 } else {
148                     color8888 = GrColorPackRGBA(masks.getRed(color565),
149                                                 masks.getGreen(color565),
150                                                 masks.getBlue(color565),
151                                                 0xFF);
152                 }
153                 memcpy(dst, &color8888, argbBpp);
154                 src = (const char*)src + a565Bpp;
155                 dst = (char*)dst + argbBpp;
156             }
157             dstRow += dstRB;
158         }
159     } else {
160         SkUNREACHABLE;
161     }
162 }
163 
164 // returns true if glyph successfully added to texture atlas, false otherwise.
addGlyphToAtlas(const SkGlyph & skGlyph,Glyph * glyph,int srcPadding,GrResourceProvider * resourceProvider,GrDeferredUploadTarget * uploadTarget)165 GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
166                                                          Glyph* glyph,
167                                                          int srcPadding,
168                                                          GrResourceProvider* resourceProvider,
169                                                          GrDeferredUploadTarget* uploadTarget) {
170 #if !defined(SK_DISABLE_SDF_TEXT)
171     SkASSERT(0 <= srcPadding && srcPadding <= SK_DistanceFieldInset);
172 #else
173     SkASSERT(0 <= srcPadding);
174 #endif
175 
176     if (skGlyph.image() == nullptr) {
177         return GrDrawOpAtlas::ErrorCode::kError;
178     }
179     SkASSERT(glyph != nullptr);
180 
181     MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
182     MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
183     int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
184 
185     int padding;
186     switch (srcPadding) {
187         case 0:
188             // The direct mask/image case.
189             padding = 0;
190             if (fSupportBilerpAtlas) {
191                 // Force direct masks (glyph with no padding) to have padding.
192                 padding = 1;
193                 srcPadding = 1;
194             }
195             break;
196         case 1:
197             // The transformed mask/image case.
198             padding = 1;
199             break;
200 #if !defined(SK_DISABLE_SDF_TEXT)
201         case SK_DistanceFieldInset:
202             // The SDFT case.
203             // If the srcPadding == SK_DistanceFieldInset (SDFT case) then the padding is built
204             // into the image on the glyph; no extra padding needed.
205             // TODO: can the SDFT glyph image in the cache be reduced by the padding?
206             padding = 0;
207             break;
208 #endif
209         default:
210             // The padding is not one of the know forms.
211             return GrDrawOpAtlas::ErrorCode::kError;
212     }
213 
214     const int width = skGlyph.width() + 2*padding;
215     const int height = skGlyph.height() + 2*padding;
216     int rowBytes = width * bytesPerPixel;
217     size_t size = height * rowBytes;
218 
219     // Temporary storage for normalizing glyph image.
220     SkAutoSMalloc<1024> storage(size);
221     void* dataPtr = storage.get();
222     if (padding > 0) {
223         sk_bzero(dataPtr, size);
224         // Advance in one row and one column.
225         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
226     }
227 
228     get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
229 
230     auto errorCode = this->addToAtlas(resourceProvider,
231                                       uploadTarget,
232                                       expectedMaskFormat,
233                                       width,
234                                       height,
235                                       storage.get(),
236                                       &glyph->fAtlasLocator);
237 
238     if (errorCode == GrDrawOpAtlas::ErrorCode::kSucceeded) {
239         glyph->fAtlasLocator.insetSrc(srcPadding);
240     }
241 
242     return errorCode;
243 }
244 
245 // 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)246 GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider,
247                                                     GrDeferredUploadTarget* target,
248                                                     MaskFormat format,
249                                                     int width, int height, const void* image,
250                                                     skgpu::AtlasLocator* atlasLocator) {
251     return this->getAtlas(format)->addToAtlas(resourceProvider, target, width, height, image,
252                                               atlasLocator);
253 }
254 
addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater * updater,MaskFormat format,Glyph * glyph,skgpu::AtlasToken token)255 void GrAtlasManager::addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater* updater,
256                                                   MaskFormat format, Glyph* glyph,
257                                                   skgpu::AtlasToken token) {
258     SkASSERT(glyph);
259     if (updater->add(glyph->fAtlasLocator)) {
260         this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
261     }
262 }
263 
initAtlas(MaskFormat format)264 bool GrAtlasManager::initAtlas(MaskFormat format) {
265     int index = MaskFormatToAtlasIndex(format);
266     if (fAtlases[index] == nullptr) {
267         SkColorType colorType = MaskFormatToColorType(format);
268         GrColorType grColorType = SkColorTypeToGrColorType(colorType);
269         SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
270         SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
271 
272         const GrBackendFormat backendFormat =
273                 fCaps->getDefaultBackendFormat(grColorType, GrRenderable::kNo);
274 
275         fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, backendFormat,
276                                               GrColorTypeToSkColorType(grColorType),
277                                               GrColorTypeBytesPerPixel(grColorType),
278                                               atlasDimensions.width(), atlasDimensions.height(),
279                                               plotDimensions.width(), plotDimensions.height(),
280                                               this,
281                                               fAllowMultitexturing,
282                                               nullptr,
283                                               /*label=*/"TextAtlas");
284         if (!fAtlases[index]) {
285             return false;
286         }
287     }
288     return true;
289 }
290 
291 ////////////////////////////////////////////////////////////////////////////////////////////////
292 
293 namespace sktext::gpu {
294 
regenerateAtlasForGanesh(int begin,int end,MaskFormat maskFormat,int srcPadding,GrMeshDrawTarget * target)295 std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
296         int begin, int end, MaskFormat maskFormat, int srcPadding, GrMeshDrawTarget* target) {
297     GrAtlasManager* atlasManager = target->atlasManager();
298     GrDeferredUploadTarget* uploadTarget = target->deferredUploadTarget();
299 
300     uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
301 
302     this->packedGlyphIDToGlyph(target->strikeCache());
303 
304     if (fAtlasGeneration != currentAtlasGen) {
305         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
306         // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
307         fBulkUseUpdater.reset();
308 
309         SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()};
310 
311         // Update the atlas information in the GrStrike.
312         auto tokenTracker = uploadTarget->tokenTracker();
313         auto glyphs = fGlyphs.subspan(begin, end - begin);
314         int glyphsPlacedInAtlas = 0;
315         bool success = true;
316         for (const Variant& variant : glyphs) {
317             Glyph* gpuGlyph = variant.glyph;
318             SkASSERT(gpuGlyph != nullptr);
319 
320             if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
321                 const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
322                 auto code = atlasManager->addGlyphToAtlas(
323                         skGlyph, gpuGlyph, srcPadding, target->resourceProvider(), uploadTarget);
324                 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
325                     success = code != GrDrawOpAtlas::ErrorCode::kError;
326                     break;
327                 }
328             }
329             atlasManager->addGlyphToBulkAndSetUseToken(
330                     &fBulkUseUpdater, maskFormat, gpuGlyph,
331                     tokenTracker->nextDrawToken());
332             glyphsPlacedInAtlas++;
333         }
334 
335         // Update atlas generation if there are no more glyphs to put in the atlas.
336         if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
337             // Need to get the freshest value of the atlas' generation because
338             // updateTextureCoordinates may have changed it.
339             fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
340         }
341 
342         return {success, glyphsPlacedInAtlas};
343     } else {
344         // The atlas hasn't changed, so our texture coordinates are still valid.
345         if (end == SkCount(fGlyphs)) {
346             // The atlas hasn't changed and the texture coordinates are all still valid. Update
347             // all the plots used to the new use token.
348             atlasManager->setUseTokenBulk(fBulkUseUpdater,
349                                           uploadTarget->tokenTracker()->nextDrawToken(),
350                                           maskFormat);
351         }
352         return {true, end - begin};
353     }
354 }
355 
356 }  // namespace sktext::gpu
357