Flutter Engine
The Flutter Engine
GrAtlasManager.cpp
Go to the documentation of this file.
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
9
11#include "include/core/SkSize.h"
12#include "include/core/SkSpan.h"
17#include "src/core/SkGlyph.h"
18#include "src/core/SkMask.h"
19#include "src/core/SkMasks.h"
24#include "src/text/gpu/Glyph.h"
27
28#include <cstring>
29#include <tuple>
30
33
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
45
47 for (int i = 0; i < skgpu::kMaskFormatCount; ++i) {
48 fAtlases[i] = nullptr;
49 }
50}
51
53 SkASSERT(glyph);
54 return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
55}
56
57template <typename INT_TYPE>
58static 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
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
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 {
161 }
162}
163
164// returns true if glyph successfully added to texture atlas, false otherwise.
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) {
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)
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.
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
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
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
264bool GrAtlasManager::initAtlas(MaskFormat format) {
265 int index = MaskFormatToAtlasIndex(format);
266 if (fAtlases[index] == nullptr) {
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
293namespace sktext::gpu {
294
295std::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);
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
sk_bzero(glyphs, sizeof(glyphs))
sktext::gpu::Glyph Glyph
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)
static GrColor GrColorPackRGBA(unsigned r, unsigned g, unsigned b, unsigned a)
Definition: GrColor.h:46
static constexpr size_t GrColorTypeBytesPerPixel(GrColorType ct)
Definition: GrTypesPriv.h:896
static constexpr SkColorType GrColorTypeToSkColorType(GrColorType ct)
Definition: GrTypesPriv.h:589
GrColorType
Definition: GrTypesPriv.h:540
static constexpr GrColorType SkColorTypeToGrColorType(SkColorType ct)
Definition: GrTypesPriv.h:629
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
#define SK_DistanceFieldInset
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
constexpr int SkCount(const Container &c)
Definition: SkTLogic.h:54
void addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater *, skgpu::MaskFormat, sktext::gpu::Glyph *, skgpu::AtlasToken)
~GrAtlasManager() override
void setUseTokenBulk(const skgpu::BulkUsePlotUpdater &updater, skgpu::AtlasToken token, skgpu::MaskFormat format)
uint64_t atlasGeneration(skgpu::MaskFormat format) const
bool hasGlyph(skgpu::MaskFormat, sktext::gpu::Glyph *)
GrDrawOpAtlas::ErrorCode addGlyphToAtlas(const SkGlyph &, sktext::gpu::Glyph *, int srcPadding, GrResourceProvider *, GrDeferredUploadTarget *)
GrAtlasManager(GrProxyProvider *, size_t maxTextureBytes, GrDrawOpAtlas::AllowMultitexturing, bool supportBilerpAtlas)
GrDrawOpAtlas::ErrorCode addToAtlas(GrResourceProvider *, GrDeferredUploadTarget *, skgpu::MaskFormat, int width, int height, const void *image, skgpu::AtlasLocator *)
GrBackendFormat getDefaultBackendFormat(GrColorType, GrRenderable) const
Definition: GrCaps.cpp:400
virtual const skgpu::TokenTracker * tokenTracker()=0
SkISize atlasDimensions(skgpu::MaskFormat type) const
SkISize plotDimensions(skgpu::MaskFormat type) const
static std::unique_ptr< GrDrawOpAtlas > Make(GrProxyProvider *proxyProvider, const GrBackendFormat &format, SkColorType ct, size_t bpp, int width, int height, int plotWidth, int plotHeight, skgpu::AtlasGenerationCounter *generationCounter, AllowMultitexturing allowMultitexturing, skgpu::PlotEvictionCallback *evictor, std::string_view label)
void setLastUseToken(const skgpu::AtlasLocator &atlasLocator, skgpu::AtlasToken token)
bool hasID(const skgpu::PlotLocator &plotLocator)
ErrorCode addToAtlas(GrResourceProvider *, GrDeferredUploadTarget *, int width, int height, const void *image, skgpu::AtlasLocator *)
virtual sktext::gpu::StrikeCache * strikeCache() const =0
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
AtlasToken nextDrawToken() const
Definition: AtlasTypes.h:214
SkSpan< const Glyph * > glyphs() const
Definition: GlyphVector.cpp:96
void packedGlyphIDToGlyph(StrikeCache *cache)
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
uint32_t uint32_t * format
uint32_t * target
double y
double x
sk_sp< const SkImage > image
Definition: SkRecords.h:269
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
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
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