blob: fe1e34a0684c6d74acba7ebd32c980075ac847a3 [file] [log] [blame]
/*
* 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/base/SkAssert.h"
#include "src/base/SkZip.h"
#include "src/core/SkGlyph.h"
#include "src/core/SkStrike.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) : fTextStrike{std::move(strike)} {}
GlyphData::~GlyphData() = default;
Glyph GlyphData::makeGlyphFromID(SkPackedGlyphID id) { return Glyph{fTextStrike->getGlyph(id)}; }
std::tuple<bool, int> GlyphData::regenerateAtlas(int begin,
int end,
sktext::gpu::GlyphVector& glyphVector,
MaskFormat maskFormat,
int srcPadding,
Recorder* recorder) {
SkASSERT(glyphVector.hasBackendData());
auto atlasManager = recorder->priv().atlasProvider()->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(maskFormat, &numActiveProxies);
if (!proxies) {
SkDebugf("Could not allocate backing texture for atlas\n");
return {false, 0};
}
uint64_t currentAtlasGen = atlasManager->atlasGeneration(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.
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++) {
if (!atlasManager->hasGlyph(maskFormat, glyphs[i].entry())) {
const SkGlyph& skGlyph = *metricsAndImages.glyph(glyphs[i].packedID());
auto code = atlasManager->addGlyphToAtlas(skGlyph, &glyphs[i].entry(), srcPadding);
if (code != DrawAtlas::ErrorCode::kSucceeded) {
success = code != DrawAtlas::ErrorCode::kError;
break;
}
}
atlasManager->addGlyphToBulkAndSetUseToken(&fBulkUseUpdater,
maskFormat,
glyphs[i].entry(),
tokenTracker->nextFlushToken());
glyphsPlacedInAtlas++;
}
// Update atlas generation if there are no more glyphs to put in the atlas.
if (success && begin + glyphsPlacedInAtlas == glyphVector.glyphCount()) {
// Need to get the freshest value of the atlas' generation because
// updateTextureCoordinates may have changed it.
fAtlasGeneration = atlasManager->atlasGeneration(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(), 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