Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 char* dstRow = (char*)dst;
132 for (int y = 0; y < height; y++) {
133 dst = dstRow;
134 for (int x = 0; x < width; x++) {
135 uint16_t color565 = 0;
136 memcpy(&color565, src, a565Bpp);
137 // TODO: create Graphite version of GrColorPackRGBA?
138 uint32_t colorRGBA = masks.getRed(color565) |
139 (masks.getGreen(color565) << 8) |
140 (masks.getBlue(color565) << 16) |
141 (0xFF << 24);
142 memcpy(dst, &colorRGBA, argbBpp);
143 src = (const char*)src + a565Bpp;
144 dst = ( char*)dst + argbBpp;
145 }
146 dstRow += dstRB;
147 }
148 } else {
150 }
151}
152
153MaskFormat TextAtlasManager::resolveMaskFormat(MaskFormat format) const {
154 if (MaskFormat::kA565 == format &&
156 /*mipmapped=*/Mipmapped::kNo,
157 Protected::kNo,
158 Renderable::kNo).isValid()) {
159 format = MaskFormat::kARGB;
160 }
161 return format;
162}
163
164// Returns kSucceeded if glyph successfully added to texture atlas, kTryAgain if a RenderPassTask
165// needs to be snapped before adding the glyph, and kError if it can't be added at all.
167 Glyph* glyph,
168 int srcPadding) {
169#if !defined(SK_DISABLE_SDF_TEXT)
170 SkASSERT(0 <= srcPadding && srcPadding <= SK_DistanceFieldInset);
171#else
172 SkASSERT(0 <= srcPadding);
173#endif
174
175 if (skGlyph.image() == nullptr) {
177 }
178 SkASSERT(glyph != nullptr);
179
180 MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
181 MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
182 int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
183
184 int padding;
185 switch (srcPadding) {
186 case 0:
187 // The direct mask/image case.
188 padding = 0;
189 if (fSupportBilerpAtlas) {
190 // Force direct masks (glyph with no padding) to have padding.
191 padding = 1;
192 srcPadding = 1;
193 }
194 break;
195 case 1:
196 // The transformed mask/image case.
197 padding = 1;
198 break;
199#if !defined(SK_DISABLE_SDF_TEXT)
201 // The SDFT case.
202 // If the srcPadding == SK_DistanceFieldInset (SDFT case) then the padding is built
203 // into the image on the glyph; no extra padding needed.
204 // TODO: can the SDFT glyph image in the cache be reduced by the padding?
205 padding = 0;
206 break;
207#endif
208 default:
209 // The padding is not one of the know forms.
211 }
212
213 const int width = skGlyph.width() + 2*padding;
214 const int height = skGlyph.height() + 2*padding;
215 int rowBytes = width * bytesPerPixel;
216 size_t size = height * rowBytes;
217
218 // Temporary storage for normalizing glyph image.
219 SkAutoSMalloc<1024> storage(size);
220 void* dataPtr = storage.get();
221 if (padding > 0) {
222 sk_bzero(dataPtr, size);
223 // Advance in one row and one column.
224 dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
225 }
226
227 get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
228
229 DrawAtlas* atlas = this->getAtlas(expectedMaskFormat);
230 auto errorCode = atlas->addToAtlas(fRecorder,
231 width,
232 height,
233 storage.get(),
234 &glyph->fAtlasLocator);
235
236 if (errorCode == DrawAtlas::ErrorCode::kSucceeded) {
237 glyph->fAtlasLocator.insetSrc(srcPadding);
238 }
239
240 return errorCode;
241}
242
244 for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
245 if (fAtlases[i] && !fAtlases[i]->recordUploads(dc, fRecorder)) {
246 return false;
247 }
248 }
249
250 return true;
251}
252
255 Glyph* glyph,
256 AtlasToken token) {
257 SkASSERT(glyph);
258 if (updater->add(glyph->fAtlasLocator)) {
259 this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
260 }
261}
262
264 // Delete any old atlases.
265 // This should be safe to do as long as we are not in the middle of a flush.
266 for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
267 fAtlases[i] = nullptr;
268 }
269
270 // Set all the atlas sizes to 1x1 plot each.
271 new (&fAtlasConfig) DrawAtlasConfig{2048, 0};
272}
273
274bool TextAtlasManager::initAtlas(MaskFormat format) {
275 int index = MaskFormatToAtlasIndex(format);
276 if (fAtlases[index] == nullptr) {
278 SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
279 SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
280 fAtlases[index] = DrawAtlas::Make(colorType,
282 atlasDimensions.width(), atlasDimensions.height(),
283 plotDimensions.width(), plotDimensions.height(),
284 this,
285 fAllowMultitexturing,
286 nullptr,
287 /*label=*/"TextAtlas");
288 if (!fAtlases[index]) {
289 return false;
290 }
291 }
292 return true;
293}
294
296 auto tokenTracker = fRecorder->priv().tokenTracker();
297 for (int i = 0; i < kMaskFormatCount; ++i) {
298 if (fAtlases[i]) {
299 fAtlases[i]->compact(tokenTracker->nextFlushToken());
300 }
301 }
302}
303
304} // namespace skgpu::graphite
305
306////////////////////////////////////////////////////////////////////////////////////////////////
307
308namespace sktext::gpu {
309
311
312std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
313 int end,
314 skgpu::MaskFormat maskFormat,
315 int srcPadding,
316 skgpu::graphite::Recorder* recorder) {
317 auto atlasManager = recorder->priv().atlasProvider()->textAtlasManager();
318 auto tokenTracker = recorder->priv().tokenTracker();
319
320 // TODO: this is not a great place for this -- need a better way to init atlases when needed
321 unsigned int numActiveProxies;
323 atlasManager->getProxies(maskFormat, &numActiveProxies);
324 if (!proxies) {
325 SkDebugf("Could not allocate backing texture for atlas\n");
326 return {false, 0};
327 }
328
329 uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
330
331 this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
332
333 if (fAtlasGeneration != currentAtlasGen) {
334 // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
335 // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
336 fBulkUseUpdater.reset();
337
338 SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()};
339
340 // Update the atlas information in the GrStrike.
341 auto glyphs = fGlyphs.subspan(begin, end - begin);
342 int glyphsPlacedInAtlas = 0;
343 bool success = true;
344 for (const Variant& variant : glyphs) {
345 Glyph* gpuGlyph = variant.glyph;
346 SkASSERT(gpuGlyph != nullptr);
347
348 if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
349 const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
350 auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
351 if (code != DrawAtlas::ErrorCode::kSucceeded) {
352 success = code != DrawAtlas::ErrorCode::kError;
353 break;
354 }
355 }
356 atlasManager->addGlyphToBulkAndSetUseToken(
357 &fBulkUseUpdater, maskFormat, gpuGlyph,
358 tokenTracker->nextFlushToken());
359 glyphsPlacedInAtlas++;
360 }
361
362 // Update atlas generation if there are no more glyphs to put in the atlas.
363 if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
364 // Need to get the freshest value of the atlas' generation because
365 // updateTextureCoordinates may have changed it.
366 fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
367 }
368
369 return {success, glyphsPlacedInAtlas};
370 } else {
371 // The atlas hasn't changed, so our texture coordinates are still valid.
372 if (end == SkCount(fGlyphs)) {
373 // The atlas hasn't changed and the texture coordinates are all still valid. Update
374 // all the plots used to the new use token.
375 atlasManager->setUseTokenBulk(fBulkUseUpdater,
376 tokenTracker->nextFlushToken(),
377 maskFormat);
378 }
379 return {true, end - begin};
380 }
381}
382
383} // namespace sktext::gpu
uint16_t glyphs[5]
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
@ 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)
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
constexpr int SkCount(const Container &c)
Definition SkTLogic.h:54
void * get() const
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
bool allowMultipleAtlasTextures() const
Definition Caps.h:277
virtual TextureInfo getDefaultSampledTextureInfo(SkColorType, Mipmapped mipmapped, Protected, Renderable) const =0
const SkSL::ShaderCaps * shaderCaps() const
Definition Caps.h:74
SkISize plotDimensions(MaskFormat type) const
SkISize atlasDimensions(MaskFormat type) const
void setLastUseToken(const AtlasLocator &atlasLocator, AtlasToken token)
Definition DrawAtlas.h:135
bool hasID(const PlotLocator &plotLocator)
Definition DrawAtlas.h:122
static std::unique_ptr< DrawAtlas > Make(SkColorType ct, size_t bpp, int width, int height, int plotWidth, int plotHeight, AtlasGenerationCounter *generationCounter, AllowMultitexturing allowMultitexturing, PlotEvictionCallback *evictor, std::string_view label)
Definition DrawAtlas.cpp:52
TokenTracker * tokenTracker()
sktext::gpu::StrikeCache * strikeCache()
AtlasProvider * atlasProvider()
const Caps * caps() const
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
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
int32_t height
int32_t width
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