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