Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/gpu/graphite/text/TextAtlasManager.cpp
Line
Count
Source (jump to first uncovered line)
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
8
#include "src/gpu/graphite/text/TextAtlasManager.h"
9
10
#include "include/core/SkColorSpace.h"
11
#include "include/gpu/graphite/Recorder.h"
12
#include "src/base/SkAutoMalloc.h"
13
#include "src/core/SkDistanceFieldGen.h"
14
#include "src/core/SkMasks.h"
15
#include "src/gpu/graphite/AtlasProvider.h"
16
#include "src/gpu/graphite/DrawAtlas.h"
17
#include "src/gpu/graphite/RecorderPriv.h"
18
#include "src/gpu/graphite/TextureProxy.h"
19
#include "src/sksl/SkSLUtil.h"
20
#include "src/text/gpu/Glyph.h"
21
#include "src/text/gpu/GlyphVector.h"
22
#include "src/text/gpu/StrikeCache.h"
23
24
using Glyph = sktext::gpu::Glyph;
25
26
namespace skgpu::graphite {
27
28
TextAtlasManager::TextAtlasManager(Recorder* recorder)
29
        : fRecorder(recorder)
30
        , fSupportBilerpAtlas{recorder->priv().caps()->supportBilerpFromGlyphAtlas()}
31
        , fAtlasConfig{recorder->priv().caps()->maxTextureSize(),
32
0
                       recorder->priv().caps()->glyphCacheTextureMaximumBytes()} {
33
0
    if (!recorder->priv().caps()->allowMultipleAtlasTextures() ||
34
        // multitexturing supported only if range can represent the index + texcoords fully
35
0
        !(recorder->priv().caps()->shaderCaps()->fFloatIs32Bits ||
36
0
          recorder->priv().caps()->shaderCaps()->fIntegerSupport)) {
37
0
        fAllowMultitexturing = DrawAtlas::AllowMultitexturing::kNo;
38
0
    } else {
39
0
        fAllowMultitexturing = DrawAtlas::AllowMultitexturing::kYes;
40
0
    }
41
0
}
42
43
0
TextAtlasManager::~TextAtlasManager() = default;
44
45
0
void TextAtlasManager::freeAll() {
46
0
    for (int i = 0; i < kMaskFormatCount; ++i) {
47
0
        fAtlases[i] = nullptr;
48
0
    }
49
0
}
50
51
0
bool TextAtlasManager::hasGlyph(MaskFormat format, Glyph* glyph) {
52
0
    SkASSERT(glyph);
53
0
    return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
54
0
}
Unexecuted instantiation: skgpu::graphite::TextAtlasManager::hasGlyph(skgpu::MaskFormat, sktext::gpu::Glyph*)
Unexecuted instantiation: skgpu::graphite::TextAtlasManager::hasGlyph(skgpu::MaskFormat, sktext::gpu::Glyph*)
55
56
template <typename INT_TYPE>
57
static void expand_bits(INT_TYPE* dst,
58
                        const uint8_t* src,
59
                        int width,
60
                        int height,
61
                        int dstRowBytes,
62
0
                        int srcRowBytes) {
63
0
    for (int y = 0; y < height; ++y) {
64
0
        int rowWritesLeft = width;
65
0
        const uint8_t* s = src;
66
0
        INT_TYPE* d = dst;
67
0
        while (rowWritesLeft > 0) {
68
0
            unsigned mask = *s++;
69
0
            for (int x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) {
70
0
                *d++ = (mask & (1 << x)) ? (INT_TYPE)(~0UL) : 0;
71
0
            }
72
0
        }
73
0
        dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
74
0
        src += srcRowBytes;
75
0
    }
76
0
}
Unexecuted instantiation: TextAtlasManager.cpp:void skgpu::graphite::expand_bits<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int)
Unexecuted instantiation: TextAtlasManager.cpp:void skgpu::graphite::expand_bits<unsigned short>(unsigned short*, unsigned char const*, int, int, int, int)
77
78
static void get_packed_glyph_image(
79
0
        const SkGlyph& glyph, int dstRB, MaskFormat expectedMaskFormat, void* dst) {
80
0
    const int width = glyph.width();
81
0
    const int height = glyph.height();
82
0
    const void* src = glyph.image();
83
0
    SkASSERT(src != nullptr);
84
85
0
    MaskFormat maskFormat = Glyph::FormatFromSkGlyph(glyph.maskFormat());
86
0
    if (maskFormat == expectedMaskFormat) {
87
0
        int srcRB = glyph.rowBytes();
88
        // Notice this comparison is with the glyphs raw mask format, and not its MaskFormat.
89
0
        if (glyph.maskFormat() != SkMask::kBW_Format) {
90
0
            if (srcRB != dstRB) {
91
0
                const int bbp = MaskFormatBytesPerPixel(expectedMaskFormat);
92
0
                for (int y = 0; y < height; y++) {
93
0
                    memcpy(dst, src, width * bbp);
94
0
                    src = (const char*) src + srcRB;
95
0
                    dst = (char*) dst + dstRB;
96
0
                }
97
0
            } else {
98
0
                memcpy(dst, src, dstRB * height);
99
0
            }
100
0
        } else {
101
            // Handle 8-bit format by expanding the mask to the expected format.
102
0
            const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
103
0
            switch (expectedMaskFormat) {
104
0
                case MaskFormat::kA8: {
105
0
                    uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
106
0
                    expand_bits(bytes, bits, width, height, dstRB, srcRB);
107
0
                    break;
108
0
                }
109
0
                case MaskFormat::kA565: {
110
0
                    uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
111
0
                    expand_bits(rgb565, bits, width, height, dstRB, srcRB);
112
0
                    break;
113
0
                }
114
0
                default:
115
0
                    SK_ABORT("Invalid MaskFormat");
116
0
            }
117
0
        }
118
0
    } else if (maskFormat == MaskFormat::kA565 &&
119
0
               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
0
        static constexpr SkMasks masks{
124
0
                {0b1111'1000'0000'0000, 11, 5},  // Red
125
0
                {0b0000'0111'1110'0000,  5, 6},  // Green
126
0
                {0b0000'0000'0001'1111,  0, 5},  // Blue
127
0
                {0, 0, 0}                        // Alpha
128
0
        };
129
0
        constexpr int a565Bpp = MaskFormatBytesPerPixel(MaskFormat::kA565);
130
0
        constexpr int argbBpp = MaskFormatBytesPerPixel(MaskFormat::kARGB);
131
0
        constexpr bool kBGRAIsNative = kN32_SkColorType == kBGRA_8888_SkColorType;
132
0
        char* dstRow = (char*)dst;
133
0
        for (int y = 0; y < height; y++) {
134
0
            dst = dstRow;
135
0
            for (int x = 0; x < width; x++) {
136
0
                uint16_t color565 = 0;
137
0
                memcpy(&color565, src, a565Bpp);
138
0
                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
0
                if (kBGRAIsNative) {
142
0
                    color8888 = masks.getBlue(color565) |
143
0
                                (masks.getGreen(color565) << 8) |
144
0
                                (masks.getRed(color565) << 16) |
145
0
                                (0xFF << 24);
146
0
                } else {
147
0
                    color8888 = masks.getRed(color565) |
148
0
                                (masks.getGreen(color565) << 8) |
149
0
                                (masks.getBlue(color565) << 16) |
150
0
                                (0xFF << 24);
151
0
                }
152
0
                memcpy(dst, &color8888, argbBpp);
153
0
                src = (const char*)src + a565Bpp;
154
0
                dst = (      char*)dst + argbBpp;
155
0
            }
156
0
            dstRow += dstRB;
157
0
        }
158
0
    } else {
159
0
        SkUNREACHABLE;
160
0
    }
161
0
}
Unexecuted instantiation: TextAtlasManager.cpp:skgpu::graphite::get_packed_glyph_image(SkGlyph const&, int, skgpu::MaskFormat, void*)
Unexecuted instantiation: TextAtlasManager.cpp:skgpu::graphite::get_packed_glyph_image(SkGlyph const&, int, skgpu::MaskFormat, void*)
162
163
0
MaskFormat TextAtlasManager::resolveMaskFormat(MaskFormat format) const {
164
0
    if (MaskFormat::kA565 == format &&
165
0
        !fRecorder->priv().caps()->getDefaultSampledTextureInfo(kRGB_565_SkColorType,
166
0
                                                                /*mipmapped=*/Mipmapped::kNo,
167
0
                                                                Protected::kNo,
168
0
                                                                Renderable::kNo).isValid()) {
169
0
        format = MaskFormat::kARGB;
170
0
    }
171
0
    return format;
172
0
}
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.
176
DrawAtlas::ErrorCode TextAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
177
                                                       Glyph* glyph,
178
0
                                                       int srcPadding) {
179
0
#if !defined(SK_DISABLE_SDF_TEXT)
180
0
    SkASSERT(0 <= srcPadding && srcPadding <= SK_DistanceFieldInset);
181
#else
182
    SkASSERT(0 <= srcPadding);
183
#endif
184
185
0
    if (skGlyph.image() == nullptr) {
186
0
        return DrawAtlas::ErrorCode::kError;
187
0
    }
188
0
    SkASSERT(glyph != nullptr);
189
190
0
    MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
191
0
    MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
192
0
    int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
193
194
0
    int padding;
195
0
    switch (srcPadding) {
196
0
        case 0:
197
            // The direct mask/image case.
198
0
            padding = 0;
199
0
            if (fSupportBilerpAtlas) {
200
                // Force direct masks (glyph with no padding) to have padding.
201
0
                padding = 1;
202
0
                srcPadding = 1;
203
0
            }
204
0
            break;
205
0
        case 1:
206
            // The transformed mask/image case.
207
0
            padding = 1;
208
0
            break;
209
0
#if !defined(SK_DISABLE_SDF_TEXT)
210
0
        case SK_DistanceFieldInset:
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
0
            padding = 0;
216
0
            break;
217
0
#endif
218
0
        default:
219
            // The padding is not one of the know forms.
220
0
            return DrawAtlas::ErrorCode::kError;
221
0
    }
222
223
0
    const int width = skGlyph.width() + 2*padding;
224
0
    const int height = skGlyph.height() + 2*padding;
225
0
    int rowBytes = width * bytesPerPixel;
226
0
    size_t size = height * rowBytes;
227
228
    // Temporary storage for normalizing glyph image.
229
0
    SkAutoSMalloc<1024> storage(size);
230
0
    void* dataPtr = storage.get();
231
0
    if (padding > 0) {
232
0
        sk_bzero(dataPtr, size);
233
        // Advance in one row and one column.
234
0
        dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
235
0
    }
236
237
0
    get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
238
239
0
    DrawAtlas* atlas = this->getAtlas(expectedMaskFormat);
240
0
    auto errorCode = atlas->addToAtlas(fRecorder,
241
0
                                       width,
242
0
                                       height,
243
0
                                       storage.get(),
244
0
                                       &glyph->fAtlasLocator);
245
246
0
    if (errorCode == DrawAtlas::ErrorCode::kSucceeded) {
247
0
        glyph->fAtlasLocator.insetSrc(srcPadding);
248
0
    }
249
250
0
    return errorCode;
251
0
}
Unexecuted instantiation: skgpu::graphite::TextAtlasManager::addGlyphToAtlas(SkGlyph const&, sktext::gpu::Glyph*, int)
Unexecuted instantiation: skgpu::graphite::TextAtlasManager::addGlyphToAtlas(SkGlyph const&, sktext::gpu::Glyph*, int)
252
253
0
bool TextAtlasManager::recordUploads(DrawContext* dc) {
254
0
    for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
255
0
        if (fAtlases[i] && !fAtlases[i]->recordUploads(dc, fRecorder)) {
256
0
            return false;
257
0
        }
258
0
    }
259
260
0
    return true;
261
0
}
262
263
void TextAtlasManager::addGlyphToBulkAndSetUseToken(BulkUsePlotUpdater* updater,
264
                                                    MaskFormat format,
265
                                                    Glyph* glyph,
266
0
                                                    AtlasToken token) {
267
0
    SkASSERT(glyph);
268
0
    if (updater->add(glyph->fAtlasLocator)) {
269
0
        this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
270
0
    }
271
0
}
Unexecuted instantiation: skgpu::graphite::TextAtlasManager::addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater*, skgpu::MaskFormat, sktext::gpu::Glyph*, skgpu::AtlasToken)
Unexecuted instantiation: skgpu::graphite::TextAtlasManager::addGlyphToBulkAndSetUseToken(skgpu::BulkUsePlotUpdater*, skgpu::MaskFormat, sktext::gpu::Glyph*, skgpu::AtlasToken)
272
273
0
void TextAtlasManager::setAtlasDimensionsToMinimum_ForTesting() {
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
0
    for (int i = 0; i < skgpu::kMaskFormatCount; i++) {
277
0
        fAtlases[i] = nullptr;
278
0
    }
279
280
    // Set all the atlas sizes to 1x1 plot each.
281
0
    new (&fAtlasConfig) DrawAtlasConfig{2048, 0};
282
0
}
283
284
0
bool TextAtlasManager::initAtlas(MaskFormat format) {
285
0
    int index = MaskFormatToAtlasIndex(format);
286
0
    if (fAtlases[index] == nullptr) {
287
0
        SkColorType colorType = MaskFormatToColorType(format);
288
0
        SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
289
0
        SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
290
0
        fAtlases[index] = DrawAtlas::Make(colorType,
291
0
                                          SkColorTypeBytesPerPixel(colorType),
292
0
                                          atlasDimensions.width(), atlasDimensions.height(),
293
0
                                          plotDimensions.width(), plotDimensions.height(),
294
0
                                          /*generationCounter=*/this,
295
0
                                          fAllowMultitexturing,
296
0
                                          DrawAtlas::UseStorageTextures::kNo,
297
0
                                          /*evictor=*/nullptr,
298
0
                                          /*label=*/"TextAtlas");
299
0
        if (!fAtlases[index]) {
300
0
            return false;
301
0
        }
302
0
    }
303
0
    return true;
304
0
}
305
306
0
void TextAtlasManager::compact(bool forceCompact) {
307
0
    auto tokenTracker = fRecorder->priv().tokenTracker();
308
0
    for (int i = 0; i < kMaskFormatCount; ++i) {
309
0
        if (fAtlases[i]) {
310
0
            fAtlases[i]->compact(tokenTracker->nextFlushToken(), forceCompact);
311
0
        }
312
0
    }
313
0
}
314
315
}  // namespace skgpu::graphite
316
317
////////////////////////////////////////////////////////////////////////////////////////////////
318
319
namespace sktext::gpu {
320
321
using DrawAtlas = skgpu::graphite::DrawAtlas;
322
323
std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
324
                                                              int end,
325
                                                              skgpu::MaskFormat maskFormat,
326
                                                              int srcPadding,
327
0
                                                              skgpu::graphite::Recorder* recorder) {
328
0
    auto atlasManager = recorder->priv().atlasProvider()->textAtlasManager();
329
0
    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
0
    unsigned int numActiveProxies;
333
0
    const sk_sp<skgpu::graphite::TextureProxy>* proxies =
334
0
            atlasManager->getProxies(maskFormat, &numActiveProxies);
335
0
    if (!proxies) {
336
0
        SkDebugf("Could not allocate backing texture for atlas\n");
337
0
        return {false, 0};
338
0
    }
339
340
0
    uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
341
342
0
    this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
343
344
0
    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
0
        fBulkUseUpdater.reset();
348
349
0
        SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()};
350
351
        // Update the atlas information in the GrStrike.
352
0
        auto glyphs = fGlyphs.subspan(begin, end - begin);
353
0
        int glyphsPlacedInAtlas = 0;
354
0
        bool success = true;
355
0
        for (const Variant& variant : glyphs) {
356
0
            Glyph* gpuGlyph = variant.glyph;
357
0
            SkASSERT(gpuGlyph != nullptr);
358
359
0
            if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
360
0
                const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
361
0
                auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
362
0
                if (code != DrawAtlas::ErrorCode::kSucceeded) {
363
0
                    success = code != DrawAtlas::ErrorCode::kError;
364
0
                    break;
365
0
                }
366
0
            }
367
0
            atlasManager->addGlyphToBulkAndSetUseToken(
368
0
                    &fBulkUseUpdater, maskFormat, gpuGlyph,
369
0
                    tokenTracker->nextFlushToken());
370
0
            glyphsPlacedInAtlas++;
371
0
        }
372
373
        // Update atlas generation if there are no more glyphs to put in the atlas.
374
0
        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
0
            fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
378
0
        }
379
380
0
        return {success, glyphsPlacedInAtlas};
381
0
    } else {
382
        // The atlas hasn't changed, so our texture coordinates are still valid.
383
0
        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
0
            atlasManager->setUseTokenBulk(fBulkUseUpdater,
387
0
                                          tokenTracker->nextFlushToken(),
388
0
                                          maskFormat);
389
0
        }
390
0
        return {true, end - begin};
391
0
    }
392
0
}
Unexecuted instantiation: sktext::gpu::GlyphVector::regenerateAtlasForGraphite(int, int, skgpu::MaskFormat, int, skgpu::graphite::Recorder*)
Unexecuted instantiation: sktext::gpu::GlyphVector::regenerateAtlasForGraphite(int, int, skgpu::MaskFormat, int, skgpu::graphite::Recorder*)
393
394
}  // namespace sktext::gpu