| /* | |
| * Copyright 2025 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/graphite/text/GlyphData.h" | |
| #include "include/private/SkAssert.h" | |
| #include "src/core/SkGlyph.h" | |
| #include "src/core/SkStrike.h" | |
| #include "src/core/SkZip.h" | |
| #include "src/gpu/graphite/AtlasProvider.h" | |
| #include "src/gpu/graphite/DrawWriter.h" | |
| #include "src/gpu/graphite/RecorderPriv.h" | |
| #include "src/gpu/graphite/text/TextAtlasManager.h" | |
| #include "src/gpu/graphite/text/TextStrike.h" | |
| #include "src/text/gpu/GlyphVector.h" | |
| #include "src/text/gpu/StrikeCache.h" | |
| #include "src/text/gpu/SubRunAllocator.h" | |
| #include "src/text/gpu/VertexFiller.h" | |
| using MaskFormat = skgpu::MaskFormat; | |
| namespace skgpu::graphite { | |
| GlyphData::GlyphData(sk_sp<TextStrike> strike, | |
| Recorder* recorder, | |
| sktext::gpu::RendererData renderData) | |
| : fTextStrike{std::move(strike)} | |
| , fRenderData(recorder->priv().getOrCreateAtlasProvider() | |
| ->textAtlasManager() | |
| ->resolveRendererData(renderData)) {} | |
| GlyphData::~GlyphData() = default; | |
| Glyph GlyphData::makeGlyphFromID(SkPackedGlyphID id) { | |
| sktext::gpu::PackedGPUGlyphID gpuID{id, | |
| fRenderData.maskFormat, | |
| fRenderData.srcPadding, | |
| fRenderData.isSDF}; | |
| return Glyph{fTextStrike->getGlyph(gpuID)}; | |
| } | |
| std::tuple<bool, int> GlyphData::regenerateAtlas(int begin, | |
| int end, | |
| sktext::gpu::GlyphVector& glyphVector, | |
| Recorder* recorder) { | |
| SkASSERT(glyphVector.hasBackendData()); | |
| auto atlasManager = recorder->priv().getOrCreateAtlasProvider()->textAtlasManager(); | |
| auto tokenTracker = recorder->priv().tokenTracker(); | |
| // TODO: this is not a great place for this -- need a better way to init atlases when needed | |
| unsigned int numActiveProxies; | |
| const sk_sp<TextureProxy>* proxies = atlasManager->getProxies(fRenderData.maskFormat, | |
| &numActiveProxies); | |
| if (!proxies) { | |
| SkDebugf("Could not allocate backing texture for atlas\n"); | |
| return {false, 0}; | |
| } | |
| uint64_t currentAtlasGen = atlasManager->atlasGeneration(fRenderData.maskFormat); | |
| if (fAtlasGeneration != currentAtlasGen) { | |
| // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration | |
| // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls. Always | |
| // reset the update, even when begin > 0. When begin > 0, this subrun was split across | |
| // flushes and previously used glyphs in [0, begin) have an older token. If those glyphs are | |
| // not used in [begin, end], they shouldn't be part of the next bulk update (and because | |
| // we won't have updated the whole subrun in one go, we won't set `fAtlasGeneration` to take | |
| // the fast path on reuse). If we do reuse the glyphs, we need the bulk update to have been | |
| // reset so that the call to addGlyphToBulkAndSetUseToken() sees the first use with the new | |
| // token and updates the atlas locator. | |
| fBulkUseUpdater.reset(); | |
| SkBulkGlyphMetricsAndImages metricsAndImages{fTextStrike->strikeSpec()}; | |
| // Update the atlas information in the GrStrike. | |
| int glyphsPlacedInAtlas = 0; | |
| bool success = true; | |
| SkSpan<const Glyph> glyphs = glyphVector.accessBackendGlyphs<Glyph>(); | |
| for (int i = begin; i < end; i++) { | |
| const Glyph& glyph = glyphs[i]; | |
| SkASSERT(glyph.entry().fKey.maskFormat() == fRenderData.maskFormat); | |
| SkASSERT(glyph.entry().fKey.padding() == fRenderData.srcPadding); | |
| SkASSERT(glyph.entry().fKey.isSDF() == fRenderData.isSDF); | |
| if (!atlasManager->hasGlyph(glyph.entry())) { | |
| const SkGlyph& skGlyph = *metricsAndImages.glyph(glyph.packedID()); | |
| auto code = atlasManager->addGlyphToAtlas(skGlyph, &glyph.entry()); | |
| if (code != DrawAtlas::ErrorCode::kSucceeded) { | |
| success = code != DrawAtlas::ErrorCode::kError; | |
| break; | |
| } | |
| } | |
| atlasManager->addGlyphToBulkAndSetUseToken(&fBulkUseUpdater, | |
| glyph.entry(), | |
| tokenTracker->nextFlushToken()); | |
| glyphsPlacedInAtlas++; | |
| } | |
| // Update atlas generation if there are no more glyphs to put in the atlas. We can only do | |
| // this if we successfully checked/added the entire glyph vector in one pass. Otherwise, on | |
| // a partial update, some of the previous glyphs of the subrun that were already drawn could | |
| // have been evicted to make room for these remaining glyphs. | |
| if (success && begin == 0 && glyphsPlacedInAtlas == glyphVector.glyphCount()) { | |
| // Need to get the freshest value of the atlas' generation because | |
| // updateTextureCoordinates may have changed it. | |
| fAtlasGeneration = atlasManager->atlasGeneration(fRenderData.maskFormat); | |
| } | |
| return {success, glyphsPlacedInAtlas}; | |
| } else { | |
| // The atlas hasn't changed, so our texture coordinates are still valid. | |
| if (end == glyphVector.glyphCount()) { | |
| // The atlas hasn't changed and the texture coordinates are all still valid. Update | |
| // all the plots used to the new use token. | |
| atlasManager->setUseTokenBulk( | |
| fBulkUseUpdater, tokenTracker->nextFlushToken(), fRenderData.maskFormat); | |
| } | |
| return {true, end - begin}; | |
| } | |
| } | |
| namespace { | |
| struct AtlasPt { | |
| uint16_t u; | |
| uint16_t v; | |
| }; | |
| } | |
| void GlyphData::fillInstanceData(const sktext::gpu::VertexFiller& vf, | |
| SkSpan<const Glyph> glyphs, | |
| DrawWriter* dw, | |
| int offset, | |
| int count, | |
| unsigned short flags, | |
| uint32_t ssboIndex, | |
| SkScalar depth) const { | |
| auto quadData = [&]() { | |
| return SkMakeZip(glyphs.subspan(offset, count), vf.topLefts().subspan(offset, count)); | |
| }; | |
| DrawWriter::Instances instances{*dw, {}, {}, 4}; | |
| instances.reserve(count); | |
| // Need to send width, height, uvPos, xyPos, and strikeToSourceScale | |
| // pre-transform coords = (s*w*b_x + t_x, s*h*b_y + t_y) | |
| // where (b_x, b_y) are the vertexID coords | |
| for (auto [glyph, leftTop] : quadData()) { | |
| auto [al, at, ar, ab] = glyph.entry().fAtlasLocator.getUVs(); | |
| instances.append(1) << AtlasPt{uint16_t(ar-al), uint16_t(ab-at)} | |
| << AtlasPt{uint16_t(al & 0x1fff), at} | |
| << leftTop << /*index=*/uint16_t(al >> 13) << flags | |
| << 1.0f | |
| << depth << ssboIndex; | |
| } | |
| } | |
| } // namespace skgpu::graphite |