Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
TextAtlasManager.cpp
Go to the documentation of this file.
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
9
14#include "src/core/SkMasks.h"
19#include "src/sksl/SkSLUtil.h"
20#include "src/text/gpu/Glyph.h"
23
25
26namespace skgpu::graphite {
27
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
44
46 for (int i = 0; i < kMaskFormatCount; ++i) {
47 fAtlases[i] = nullptr;
48 }
49}
50
52 SkASSERT(glyph);
53 return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
54}
55
56template <typename INT_TYPE>
57static void expand_bits(INT_TYPE* dst,
58 const uint8_t* src,
59 int width,
60 int height,
61 int dstRowBytes,
62 int srcRowBytes) {
63 for (int y = 0; y < height; ++y) {
64 int rowWritesLeft = width;
65 const uint8_t* s = src;
66 INT_TYPE* d = dst;
67 while (rowWritesLeft > 0) {
68 unsigned mask = *s++;
69 for (int x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) {
70 *d++ = (mask & (1 << x)) ? (INT_TYPE)(~0UL) : 0;
71 }
72 }
73 dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
74 src += srcRowBytes;
75 }
76}
77
79 const SkGlyph& glyph, int dstRB, MaskFormat expectedMaskFormat, void* dst) {
80 const int width = glyph.width();
81 const int height = glyph.height();
82 const void* src = glyph.image();
83 SkASSERT(src != nullptr);
84
86 if (maskFormat == expectedMaskFormat) {
87 int srcRB = glyph.rowBytes();
88 // Notice this comparison is with the glyphs raw mask format, and not its MaskFormat.
89 if (glyph.maskFormat() != SkMask::kBW_Format) {
90 if (srcRB != dstRB) {
91 const int bbp = MaskFormatBytesPerPixel(expectedMaskFormat);
92 for (int y = 0; y < height; y++) {
93 memcpy(dst, src, width * bbp);
94 src = (const char*) src + srcRB;
95 dst = (char*) dst + dstRB;
96 }
97 } else {
98 memcpy(dst, src, dstRB * height);
99 }
100 } else {
101 // Handle 8-bit format by expanding the mask to the expected format.
102 const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
103 switch (expectedMaskFormat) {
104 case MaskFormat::kA8: {
105 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
106 expand_bits(bytes, bits, width, height, dstRB, srcRB);
107 break;
108 }
109 case MaskFormat::kA565: {
110 uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
111 expand_bits(rgb565, bits, width, height, dstRB, srcRB);
112 break;
113 }
114 default:
115 SK_ABORT("Invalid MaskFormat");
116 }
117 }
118 } else if (maskFormat == MaskFormat::kA565 &&
119 expectedMaskFormat == MaskFormat::kARGB) {
120 // Convert if the glyph uses a 565 mask format since it is using LCD text rendering
121 // but the expected format is 8888 (will happen on Intel MacOS with Metal since that
122 // combination does not support 565).
123 static constexpr SkMasks masks{
124 {0b1111'1000'0000'0000, 11, 5}, // Red
125 {0b0000'0111'1110'0000, 5, 6}, // Green
126 {0b0000'0000'0001'1111, 0, 5}, // Blue
127 {0, 0, 0} // Alpha
128 };
129 constexpr int a565Bpp = MaskFormatBytesPerPixel(MaskFormat::kA565);
130 constexpr int argbBpp = MaskFormatBytesPerPixel(MaskFormat::kARGB);
131 constexpr bool kBGRAIsNative = kN32_SkColorType == kBGRA_8888_SkColorType;
132 char* dstRow = (char*)dst;
133 for (int y = 0; y < height; y++) {
134 dst = dstRow;
135 for (int x = 0; x < width; x++) {
136 uint16_t color565 = 0;
137 memcpy(&color565, src, a565Bpp);
138 uint32_t color8888;
139 // On Windows (and possibly others), font data is stored as BGR.
140 // So we need to swizzle the data to reflect that.
141 if (kBGRAIsNative) {
142 color8888 = masks.getBlue(color565) |
143 (masks.getGreen(color565) << 8) |
144 (masks.getRed(color565) << 16) |
145 (0xFF << 24);
146 } else {
147 color8888 = masks.getRed(color565) |
148 (masks.getGreen(color565) << 8) |
149 (masks.getBlue(color565) << 16) |
150 (0xFF << 24);
151 }
152 memcpy(dst, &color8888, argbBpp);
153 src = (const char*)src + a565Bpp;
154 dst = ( char*)dst + argbBpp;
155 }
156 dstRow += dstRB;
157 }
158 } else {
160 }
161}
162
163MaskFormat TextAtlasManager::resolveMaskFormat(MaskFormat format) const {
164 if (MaskFormat::kA565 == format &&
166 /*mipmapped=*/Mipmapped::kNo,
170 }
171 return format;
172}
173
174// Returns kSucceeded if glyph successfully added to texture atlas, kTryAgain if a RenderPassTask
175// needs to be snapped before adding the glyph, and kError if it can't be added at all.
177 Glyph* glyph,
178 int srcPadding) {
179#if !defined(SK_DISABLE_SDF_TEXT)
180 SkASSERT(0 <= srcPadding && srcPadding <= SK_DistanceFieldInset);
181#else
182 SkASSERT(0 <= srcPadding);
183#endif
184
185 if (skGlyph.image() == nullptr) {
187 }
188 SkASSERT(glyph != nullptr);
189
190 MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
191 MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
192 int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
193
194 int padding;
195 switch (srcPadding) {
196 case 0:
197 // The direct mask/image case.
198 padding = 0;
199 if (fSupportBilerpAtlas) {
200 // Force direct masks (glyph with no padding) to have padding.
201 padding = 1;
202 srcPadding = 1;
203 }
204 break;
205 case 1:
206 // The transformed mask/image case.
207 padding = 1;
208 break;
209#if !defined(SK_DISABLE_SDF_TEXT)
211 // The SDFT case.
212 // If the srcPadding == SK_DistanceFieldInset (SDFT case) then the padding is built
213 // into the image on the glyph; no extra padding needed.
214 // TODO: can the SDFT glyph image in the cache be reduced by the padding?
215 padding = 0;
216 break;
217#endif
218 default:
219 // The padding is not one of the know forms.
221 }
222
223 const int width = skGlyph.width() + 2*padding;
224 const int height = skGlyph.height() + 2*padding;
225 int rowBytes = width * bytesPerPixel;
226 size_t size = height * rowBytes;
227
228 // Temporary storage for normalizing glyph image.
229 SkAutoSMalloc<1024> storage(size);
230 void* dataPtr = storage.get();
231 if (padding > 0) {
232 sk_bzero(dataPtr, size);
233 // Advance in one row and one column.
234 dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
235 }
236
237 get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
238
239 DrawAtlas* atlas = this->getAtlas(expectedMaskFormat);
240 auto errorCode = atlas->addToAtlas(fRecorder,
241 width,
242 height,
243 storage.get(),
244 &glyph->fAtlasLocator);
245
246 if (errorCode == DrawAtlas::ErrorCode::kSucceeded) {
247 glyph->fAtlasLocator.insetSrc(srcPadding);
248 }
249
250 return errorCode;
251}
252
254 for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
255 if (fAtlases[i] && !fAtlases[i]->recordUploads(dc, fRecorder)) {
256 return false;
257 }
258 }
259
260 return true;
261}
262
265 Glyph* glyph,
266 AtlasToken token) {
267 SkASSERT(glyph);
268 if (updater->add(glyph->fAtlasLocator)) {
269 this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
270 }
271}
272
274 // Delete any old atlases.
275 // This should be safe to do as long as we are not in the middle of a flush.
276 for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
277 fAtlases[i] = nullptr;
278 }
279
280 // Set all the atlas sizes to 1x1 plot each.
281 new (&fAtlasConfig) DrawAtlasConfig{2048, 0};
282}
283
284bool TextAtlasManager::initAtlas(MaskFormat format) {
285 int index = MaskFormatToAtlasIndex(format);
286 if (fAtlases[index] == nullptr) {
288 SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
289 SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
290 fAtlases[index] = DrawAtlas::Make(colorType,
292 atlasDimensions.width(), atlasDimensions.height(),
293 plotDimensions.width(), plotDimensions.height(),
294 /*generationCounter=*/this,
295 fAllowMultitexturing,
297 /*evictor=*/nullptr,
298 /*label=*/"TextAtlas");
299 if (!fAtlases[index]) {
300 return false;
301 }
302 }
303 return true;
304}
305
307 auto tokenTracker = fRecorder->priv().tokenTracker();
308 for (int i = 0; i < kMaskFormatCount; ++i) {
309 if (fAtlases[i]) {
310 fAtlases[i]->compact(tokenTracker->nextFlushToken());
311 }
312 }
313}
314
315} // namespace skgpu::graphite
316
317////////////////////////////////////////////////////////////////////////////////////////////////
318
319namespace sktext::gpu {
320
322
323std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
324 int end,
325 skgpu::MaskFormat maskFormat,
326 int srcPadding,
327 skgpu::graphite::Recorder* recorder) {
328 auto atlasManager = recorder->priv().atlasProvider()->textAtlasManager();
329 auto tokenTracker = recorder->priv().tokenTracker();
330
331 // TODO: this is not a great place for this -- need a better way to init atlases when needed
332 unsigned int numActiveProxies;
334 atlasManager->getProxies(maskFormat, &numActiveProxies);
335 if (!proxies) {
336 SkDebugf("Could not allocate backing texture for atlas\n");
337 return {false, 0};
338 }
339
340 uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
341
342 this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
343
344 if (fAtlasGeneration != currentAtlasGen) {
345 // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
346 // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
347 fBulkUseUpdater.reset();
348
349 SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()};
350
351 // Update the atlas information in the GrStrike.
352 auto glyphs = fGlyphs.subspan(begin, end - begin);
353 int glyphsPlacedInAtlas = 0;
354 bool success = true;
355 for (const Variant& variant : glyphs) {
356 Glyph* gpuGlyph = variant.glyph;
357 SkASSERT(gpuGlyph != nullptr);
358
359 if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
360 const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
361 auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
362 if (code != DrawAtlas::ErrorCode::kSucceeded) {
364 break;
365 }
366 }
367 atlasManager->addGlyphToBulkAndSetUseToken(
368 &fBulkUseUpdater, maskFormat, gpuGlyph,
369 tokenTracker->nextFlushToken());
370 glyphsPlacedInAtlas++;
371 }
372
373 // Update atlas generation if there are no more glyphs to put in the atlas.
374 if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
375 // Need to get the freshest value of the atlas' generation because
376 // updateTextureCoordinates may have changed it.
377 fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
378 }
379
380 return {success, glyphsPlacedInAtlas};
381 } else {
382 // The atlas hasn't changed, so our texture coordinates are still valid.
383 if (end == SkCount(fGlyphs)) {
384 // The atlas hasn't changed and the texture coordinates are all still valid. Update
385 // all the plots used to the new use token.
386 atlasManager->setUseTokenBulk(fBulkUseUpdater,
387 tokenTracker->nextFlushToken(),
388 maskFormat);
389 }
390 return {true, end - begin};
391 }
392}
393
394} // namespace sktext::gpu
sk_bzero(glyphs, sizeof(glyphs))
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
static const uint16_t rgb565[kNumPixels]
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkColorType
Definition: SkColorType.h:19
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition: SkColorType.h:22
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SK_DistanceFieldInset
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
SK_API int SkColorTypeBytesPerPixel(SkColorType ct)
Definition: SkImageInfo.cpp:16
constexpr int SkCount(const Container &c)
Definition: SkTLogic.h:54
sktext::gpu::Glyph Glyph
void * get() const
Definition: SkAutoMalloc.h:126
size_t rowBytes() const
Definition: SkGlyph.cpp:233
SkMask::Format maskFormat() const
Definition: SkGlyph.h:500
int height() const
Definition: SkGlyph.h:513
int width() const
Definition: SkGlyph.h:512
const void * image() const
Definition: SkGlyph.h:465
void insetSrc(int padding)
Definition: AtlasTypes.h:327
PlotLocator plotLocator() const
Definition: AtlasTypes.h:301
bool add(const skgpu::AtlasLocator &atlasLocator)
Definition: AtlasTypes.h:387
TextAtlasManager * textAtlasManager() const
Definition: AtlasProvider.h:53
bool allowMultipleAtlasTextures() const
Definition: Caps.h:284
virtual TextureInfo getDefaultSampledTextureInfo(SkColorType, Mipmapped mipmapped, Protected, Renderable) const =0
const SkSL::ShaderCaps * shaderCaps() const
Definition: Caps.h:75
SkISize plotDimensions(MaskFormat type) const
Definition: DrawAtlas.cpp:567
SkISize atlasDimensions(MaskFormat type) const
Definition: DrawAtlas.cpp:557
void setLastUseToken(const AtlasLocator &atlasLocator, AtlasToken token)
Definition: DrawAtlas.h:141
bool hasID(const PlotLocator &plotLocator)
Definition: DrawAtlas.h:128
static std::unique_ptr< DrawAtlas > Make(SkColorType ct, size_t bpp, int width, int height, int plotWidth, int plotHeight, AtlasGenerationCounter *generationCounter, AllowMultitexturing allowMultitexturing, UseStorageTextures useStorageTextures, PlotEvictionCallback *evictor, std::string_view label)
Definition: DrawAtlas.cpp:52
TokenTracker * tokenTracker()
Definition: RecorderPriv.h:62
sktext::gpu::StrikeCache * strikeCache()
Definition: RecorderPriv.h:63
AtlasProvider * atlasProvider()
Definition: RecorderPriv.h:61
const Caps * caps() const
Definition: RecorderPriv.h:31
DrawAtlas::ErrorCode addGlyphToAtlas(const SkGlyph &, sktext::gpu::Glyph *, int srcPadding)
void addGlyphToBulkAndSetUseToken(BulkUsePlotUpdater *, MaskFormat, sktext::gpu::Glyph *, AtlasToken)
bool hasGlyph(MaskFormat, sktext::gpu::Glyph *)
skgpu::AtlasLocator fAtlasLocator
Definition: Glyph.h:40
static skgpu::MaskFormat FormatFromSkGlyph(SkMask::Format format)
Definition: Glyph.h:19
const SkPackedGlyphID fPackedID
Definition: Glyph.h:39
static const char * begin(const StringSlice &s)
Definition: editor.cpp:252
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
struct MyStruct s
glong glong end
FlPixelBufferTexturePrivate * priv
uint32_t uint32_t * format
double y
double x
sk_sp< const SkImage > atlas
Definition: SkRecords.h:331
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
static void get_packed_glyph_image(const SkGlyph &glyph, int dstRB, MaskFormat expectedMaskFormat, void *dst)
static void expand_bits(INT_TYPE *dst, const uint8_t *src, int width, int height, int dstRowBytes, int srcRowBytes)
constexpr int MaskFormatBytesPerPixel(MaskFormat format)
Definition: AtlasTypes.h:110
static constexpr SkColorType MaskFormatToColorType(MaskFormat format)
Definition: AtlasTypes.h:122
static const int kMaskFormatCount
Definition: AtlasTypes.h:105
MaskFormat
Definition: AtlasTypes.h:98
@ kA565
2-bytes per pixel, RGB represent 3-channel LCD coverage
@ kA8
1-byte per pixel
@ kARGB
4-bytes per pixel, color format
skgpu::graphite::DrawAtlas DrawAtlas
int32_t height
int32_t width
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition: SkMask.h:27
bool fIntegerSupport
Definition: SkSLUtil.h:89
bool fFloatIs32Bits
Definition: SkSLUtil.h:100