diff options
Diffstat (limited to 'src/gpu/text')
-rw-r--r-- | src/gpu/text/GrAtlasGlyphCache.h | 270 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasManager.cpp | 222 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasManager.h | 155 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextBlob.cpp | 21 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextBlob.h | 24 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp | 29 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextContext.cpp | 121 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextContext.h | 27 | ||||
-rw-r--r-- | src/gpu/text/GrGlyphCache.cpp (renamed from src/gpu/text/GrAtlasGlyphCache.cpp) | 191 | ||||
-rw-r--r-- | src/gpu/text/GrGlyphCache.h | 148 | ||||
-rw-r--r-- | src/gpu/text/GrTextUtils.h | 2 |
11 files changed, 661 insertions, 549 deletions
diff --git a/src/gpu/text/GrAtlasGlyphCache.h b/src/gpu/text/GrAtlasGlyphCache.h deleted file mode 100644 index 84b7d1b249..0000000000 --- a/src/gpu/text/GrAtlasGlyphCache.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef GrAtlasGlyphCache_DEFINED -#define GrAtlasGlyphCache_DEFINED - -#include "GrCaps.h" -#include "GrDrawOpAtlas.h" -#include "GrGlyph.h" -#include "GrOnFlushResourceProvider.h" -#include "SkArenaAlloc.h" -#include "SkGlyphCache.h" -#include "SkTDynamicHash.h" - -class GrAtlasGlyphCache; -class GrGpu; - -/** - * The GrAtlasTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory - * is indexed by a PackedID and SkGlyphCache. The SkGlyphCache is what actually creates the mask. - * The GrAtlasTextStrike may outlive the generating SkGlyphCache. However, it retains a copy - * of it's SkDescriptor as a key to access (or regenerate) the SkGlyphCache. GrAtlasTextStrike are - * created by and owned by a GrAtlasGlyphCache. - */ -class GrAtlasTextStrike : public SkNVRefCnt<GrAtlasTextStrike> { -public: - GrAtlasTextStrike(const SkDescriptor& fontScalerKey); - ~GrAtlasTextStrike(); - - inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed, - SkGlyphCache* cache) { - GrGlyph* glyph = fCache.find(packed); - if (nullptr == glyph) { - glyph = this->generateGlyph(skGlyph, packed, cache); - } - return glyph; - } - - // This variant of the above function is called by GrAtlasTextOp. At this point, it is possible - // that the maskformat of the glyph differs from what we expect. In these cases we will just - // draw a clear square. - // skbug:4143 crbug:510931 - inline GrGlyph* getGlyph(GrGlyph::PackedID packed, - GrMaskFormat expectedMaskFormat, - SkGlyphCache* cache) { - GrGlyph* glyph = fCache.find(packed); - if (nullptr == glyph) { - // We could return this to the caller, but in practice it adds code complexity for - // potentially little benefit(ie, if the glyph is not in our font cache, then its not - // in the atlas and we're going to be doing a texture upload anyways). - const SkGlyph& skGlyph = GrToSkGlyph(cache, packed); - glyph = this->generateGlyph(skGlyph, packed, cache); - glyph->fMaskFormat = expectedMaskFormat; - } - return glyph; - } - - // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's - // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never - // happen. - // TODO we can handle some of these cases if we really want to, but the long term solution is to - // get the actual glyph image itself when we get the glyph metrics. - bool addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, GrAtlasGlyphCache*, GrGlyph*, - SkGlyphCache*, GrMaskFormat expectedMaskFormat); - - // testing - int countGlyphs() const { return fCache.count(); } - - // remove any references to this plot - void removeID(GrDrawOpAtlas::AtlasID); - - // If a TextStrike is abandoned by the cache, then the caller must get a new strike - bool isAbandoned() const { return fIsAbandoned; } - - static const SkDescriptor& GetKey(const GrAtlasTextStrike& ts) { - return *ts.fFontScalerKey.getDesc(); - } - - static uint32_t Hash(const SkDescriptor& desc) { return desc.getChecksum(); } - -private: - SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache; - SkAutoDescriptor fFontScalerKey; - SkArenaAlloc fPool{512}; - - int fAtlasedGlyphs; - bool fIsAbandoned; - - static const SkGlyph& GrToSkGlyph(SkGlyphCache* cache, GrGlyph::PackedID id) { - return cache->getGlyphIDMetrics(GrGlyph::UnpackID(id), - GrGlyph::UnpackFixedX(id), - GrGlyph::UnpackFixedY(id)); - } - - GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, SkGlyphCache*); - - friend class GrAtlasGlyphCache; -}; - -/** - * GrAtlasGlyphCache manages strikes which are indexed by a SkGlyphCache. These strikes can then be - * used to generate individual Glyph Masks. The GrAtlasGlyphCache also manages GrDrawOpAtlases, - * though this is more or less transparent to the client(aside from atlasGeneration, described - * below). - */ -class GrAtlasGlyphCache : public GrOnFlushCallbackObject { -public: - GrAtlasGlyphCache(GrProxyProvider*, float maxTextureBytes, - GrDrawOpAtlas::AllowMultitexturing); - ~GrAtlasGlyphCache() override; - // The user of the cache may hold a long-lived ref to the returned strike. However, actions by - // another client of the cache may cause the strike to be purged while it is still reffed. - // Therefore, the caller must check GrAtlasTextStrike::isAbandoned() if there are other - // interactions with the cache since the strike was received. - inline GrAtlasTextStrike* getStrike(const SkGlyphCache* cache) { - GrAtlasTextStrike* strike = fCache.find(cache->getDescriptor()); - if (nullptr == strike) { - strike = this->generateStrike(cache); - } - return strike; - } - - void freeAll(); - - // if getProxies returns nullptr, the client must not try to use other functions on the - // GrAtlasGlyphCache which use the atlas. This function *must* be called first, before other - // functions which use the atlas. - const sk_sp<GrTextureProxy>* getProxies(GrMaskFormat format, unsigned int* numProxies) { - SkASSERT(numProxies); - - if (this->initAtlas(format)) { - *numProxies = this->getAtlas(format)->numActivePages(); - return this->getAtlas(format)->getProxies(); - } - *numProxies = 0; - return nullptr; - } - - SkScalar getGlyphSizeLimit() const { return fGlyphSizeLimit; } - - bool hasGlyph(GrGlyph* glyph) { - SkASSERT(glyph); - return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID); - } - - // To ensure the GrDrawOpAtlas does not evict the Glyph Mask from its texture backing store, - // the client must pass in the current op token along with the GrGlyph. - // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas. - // For convenience, this function will also set the use token for the current glyph if required - // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration - void addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater, GrGlyph* glyph, - GrDeferredUploadToken token) { - SkASSERT(glyph); - updater->add(glyph->fID); - this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token); - } - - void setUseTokenBulk(const GrDrawOpAtlas::BulkUseTokenUpdater& updater, - GrDeferredUploadToken token, - GrMaskFormat format) { - this->getAtlas(format)->setLastUseTokenBulk(updater, token); - } - - // add to texture atlas that matches this format - bool addToAtlas(GrResourceProvider* resourceProvider, GrAtlasTextStrike* strike, - GrDrawOpAtlas::AtlasID* id, - GrDeferredUploadTarget* target, GrMaskFormat format, int width, int height, - const void* image, SkIPoint16* loc) { - fPreserveStrike = strike; - return this->getAtlas(format)->addToAtlas(resourceProvider, id, target, - width, height, image, loc); - } - - // Some clients may wish to verify the integrity of the texture backing store of the - // GrDrawOpAtlas. The atlasGeneration returned below is a monotonically increasing number which - // changes every time something is removed from the texture backing store. - uint64_t atlasGeneration(GrMaskFormat format) const { - return this->getAtlas(format)->atlasGeneration(); - } - - // GrOnFlushCallbackObject overrides - - void preFlush(GrOnFlushResourceProvider* onFlushResourceProvider, const uint32_t*, int, - SkTArray<sk_sp<GrRenderTargetContext>>*) override { - for (int i = 0; i < kMaskFormatCount; ++i) { - if (fAtlases[i]) { - fAtlases[i]->instantiate(onFlushResourceProvider); - } - } - } - - void postFlush(GrDeferredUploadToken startTokenForNextFlush, const uint32_t*, int) override { - for (int i = 0; i < kMaskFormatCount; ++i) { - if (fAtlases[i]) { - fAtlases[i]->compact(startTokenForNextFlush); - } - } - } - - // The AtlasGlyph cache always survives freeGpuResources so we want it to remain in the active - // OnFlushCallbackObject list - bool retainOnFreeGpuResources() override { return true; } - - /////////////////////////////////////////////////////////////////////////// - // Functions intended debug only -#ifdef SK_DEBUG - void dump(GrContext*) const; -#endif - - void setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]); - -private: - static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format, const GrCaps& caps) { - switch (format) { - case kA8_GrMaskFormat: - return kAlpha_8_GrPixelConfig; - case kA565_GrMaskFormat: - return kRGB_565_GrPixelConfig; - case kARGB_GrMaskFormat: - return caps.srgbSupport() ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig; - default: - SkDEBUGFAIL("unsupported GrMaskFormat"); - return kAlpha_8_GrPixelConfig; - } - } - - // There is a 1:1 mapping between GrMaskFormats and atlas indices - static int MaskFormatToAtlasIndex(GrMaskFormat format) { - static const int sAtlasIndices[] = { - kA8_GrMaskFormat, - kA565_GrMaskFormat, - kARGB_GrMaskFormat, - }; - static_assert(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, "array_size_mismatch"); - - SkASSERT(sAtlasIndices[format] < kMaskFormatCount); - return sAtlasIndices[format]; - } - - bool initAtlas(GrMaskFormat); - - GrAtlasTextStrike* generateStrike(const SkGlyphCache* cache) { - GrAtlasTextStrike* strike = new GrAtlasTextStrike(cache->getDescriptor()); - fCache.add(strike); - return strike; - } - - GrDrawOpAtlas* getAtlas(GrMaskFormat format) const { - int atlasIndex = MaskFormatToAtlasIndex(format); - SkASSERT(fAtlases[atlasIndex]); - return fAtlases[atlasIndex].get(); - } - - static void HandleEviction(GrDrawOpAtlas::AtlasID, void*); - - using StrikeHash = SkTDynamicHash<GrAtlasTextStrike, SkDescriptor>; - GrProxyProvider* fProxyProvider; - StrikeHash fCache; - GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing; - std::unique_ptr<GrDrawOpAtlas> fAtlases[kMaskFormatCount]; - GrAtlasTextStrike* fPreserveStrike; - GrDrawOpAtlasConfig fAtlasConfigs[kMaskFormatCount]; - SkScalar fGlyphSizeLimit; -}; - -#endif diff --git a/src/gpu/text/GrAtlasManager.cpp b/src/gpu/text/GrAtlasManager.cpp new file mode 100644 index 0000000000..6e227a94a2 --- /dev/null +++ b/src/gpu/text/GrAtlasManager.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrAtlasManager.h" + +#include "GrCaps.h" +#include "GrGlyph.h" +#include "GrGlyphCache.h" +#include "GrProxyProvider.h" + +GrRestrictedAtlasManager::GrRestrictedAtlasManager( + sk_sp<const GrCaps> caps, + float maxTextureBytes, + GrDrawOpAtlas::AllowMultitexturing allowMultitexturing) + : fCaps(std::move(caps)) + , fAllowMultitexturing(allowMultitexturing) { + // Calculate RGBA size. Must be between 512 x 256 and MaxTextureSize x MaxTextureSize / 2 + int log2MaxTextureSize = SkPrevLog2(fCaps->maxTextureSize()); + int log2MaxDim = 9; + for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) { + int maxDim = 1 << log2MaxDim; + int minDim = 1 << (log2MaxDim - 1); + + if (maxDim * minDim * 4 >= maxTextureBytes) break; + } + + int log2MinDim = log2MaxDim - 1; + int maxDim = 1 << log2MaxDim; + int minDim = 1 << log2MinDim; + // Plots are either 256 or 512. + int maxPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 2))); + int minPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 3))); + + // Setup default atlas configs. The A8 atlas uses maxDim for both width and height, as the A8 + // format is already very compact. + fAtlasConfigs[kA8_GrMaskFormat].fWidth = maxDim; + fAtlasConfigs[kA8_GrMaskFormat].fHeight = maxDim; + fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = maxPlot; + fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = minPlot; + + // A565 and ARGB use maxDim x minDim. + fAtlasConfigs[kA565_GrMaskFormat].fWidth = minDim; + fAtlasConfigs[kA565_GrMaskFormat].fHeight = maxDim; + fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = minPlot; + fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = minPlot; + + fAtlasConfigs[kARGB_GrMaskFormat].fWidth = minDim; + fAtlasConfigs[kARGB_GrMaskFormat].fHeight = maxDim; + fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = minPlot; + fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = minPlot; + + fGlyphSizeLimit = minPlot; +} + +GrRestrictedAtlasManager::~GrRestrictedAtlasManager() { +} + +static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format, const GrCaps& caps) { + switch (format) { + case kA8_GrMaskFormat: + return kAlpha_8_GrPixelConfig; + case kA565_GrMaskFormat: + return kRGB_565_GrPixelConfig; + case kARGB_GrMaskFormat: + return caps.srgbSupport() ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig; + default: + SkDEBUGFAIL("unsupported GrMaskFormat"); + return kAlpha_8_GrPixelConfig; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider, GrGlyphCache* glyphCache, + float maxTextureBytes, + GrDrawOpAtlas::AllowMultitexturing allowMultitexturing) + : INHERITED(proxyProvider->refCaps(), maxTextureBytes, allowMultitexturing) + , fProxyProvider(proxyProvider) + , fGlyphCache(glyphCache) { +} + +void GrAtlasManager::freeAll() { + for (int i = 0; i < kMaskFormatCount; ++i) { + fAtlases[i] = nullptr; + } +} + +bool GrAtlasManager::hasGlyph(GrGlyph* glyph) { + SkASSERT(glyph); + return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID); +} + +// add to texture atlas that matches this format +bool GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider, + GrGlyphCache* glyphCache, + GrAtlasTextStrike* strike, GrDrawOpAtlas::AtlasID* id, + GrDeferredUploadTarget* target, GrMaskFormat format, + int width, int height, const void* image, SkIPoint16* loc) { + glyphCache->setStrikeToPreserve(strike); + return this->getAtlas(format)->addToAtlas(resourceProvider, id, target, width, height, + image, loc); +} + +void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater, + GrGlyph* glyph, + GrDeferredUploadToken token) { + SkASSERT(glyph); + updater->add(glyph->fID); + this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token); +} + +#ifdef SK_DEBUG +#include "GrContextPriv.h" +#include "GrSurfaceProxy.h" +#include "GrSurfaceContext.h" +#include "GrTextureProxy.h" + +#include "SkBitmap.h" +#include "SkImageEncoder.h" +#include "SkStream.h" +#include <stdio.h> + +/** + * Write the contents of the surface proxy to a PNG. Returns true if successful. + * @param filename Full path to desired file + */ +static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char* filename) { + if (!sProxy) { + return false; + } + + SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + SkBitmap bm; + if (!bm.tryAllocPixels(ii)) { + return false; + } + + sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext( + sk_ref_sp(sProxy))); + if (!sContext || !sContext->asTextureProxy()) { + return false; + } + + bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), 0, 0); + if (!result) { + SkDebugf("------ failed to read pixels for %s\n", filename); + return false; + } + + // remove any previous version of this file + remove(filename); + + SkFILEWStream file(filename); + if (!file.isValid()) { + SkDebugf("------ failed to create file: %s\n", filename); + remove(filename); // remove any partial file + return false; + } + + if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) { + SkDebugf("------ failed to encode %s\n", filename); + remove(filename); // remove any partial file + return false; + } + + return true; +} + +void GrAtlasManager::dump(GrContext* context) const { + static int gDumpCount = 0; + for (int i = 0; i < kMaskFormatCount; ++i) { + if (fAtlases[i]) { + const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies(); + for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) { + SkASSERT(proxies[pageIdx]); + SkString filename; +#ifdef SK_BUILD_FOR_ANDROID + filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx); +#else + filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx); +#endif + + save_pixels(context, proxies[pageIdx].get(), filename.c_str()); + } + } + } + ++gDumpCount; +} +#endif + +void GrAtlasManager::setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]) { + // Delete any old atlases. + // This should be safe to do as long as we are not in the middle of a flush. + for (int i = 0; i < kMaskFormatCount; i++) { + fAtlases[i] = nullptr; + } + memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs)); +} + +bool GrAtlasManager::initAtlas(GrMaskFormat format) { + int index = MaskFormatToAtlasIndex(format); + if (!fAtlases[index]) { + GrPixelConfig config = mask_format_to_pixel_config(format, *fCaps); + int width = fAtlasConfigs[index].fWidth; + int height = fAtlasConfigs[index].fHeight; + int numPlotsX = fAtlasConfigs[index].numPlotsX(); + int numPlotsY = fAtlasConfigs[index].numPlotsY(); + + fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, config, width, height, + numPlotsX, numPlotsY, fAllowMultitexturing, + &GrGlyphCache::HandleEviction, + fGlyphCache); + if (!fAtlases[index]) { + return false; + } + } + return true; +} diff --git a/src/gpu/text/GrAtlasManager.h b/src/gpu/text/GrAtlasManager.h new file mode 100644 index 0000000000..4629cb173b --- /dev/null +++ b/src/gpu/text/GrAtlasManager.h @@ -0,0 +1,155 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrAtlasManager_DEFINED +#define GrAtlasManager_DEFINED + +#include "GrDrawOpAtlas.h" +#include "GrOnFlushResourceProvider.h" + +class GrAtlasGlypCache; +class GrAtlasTextStrike; +struct GrGlyph; + + /** The GrAtlasManager classes manage the lifetime of and access to GrDrawOpAtlases. + * The restricted version is available at op creation time and only allows basic access + * to the proxies (so the created ops can reference them). The full GrAtlasManager class + * is only available at flush time and only via the GrOpFlushState. + * + * This organization implies that all of the advanced atlasManager functionality (i.e., + * adding glyphs to the atlas) are only available at flush time. + */ +class GrRestrictedAtlasManager : public GrOnFlushCallbackObject { +public: + GrRestrictedAtlasManager(sk_sp<const GrCaps>, float maxTextureBytes, + GrDrawOpAtlas::AllowMultitexturing); + ~GrRestrictedAtlasManager() override; + + // if getProxies returns nullptr, the client must not try to use other functions on the + // GrGlyphCache which use the atlas. This function *must* be called first, before other + // functions which use the atlas. + const sk_sp<GrTextureProxy>* getProxies(GrMaskFormat format, unsigned int* numProxies) { + if (this->initAtlas(format)) { + *numProxies = this->getAtlas(format)->numActivePages(); + return this->getAtlas(format)->getProxies(); + } + *numProxies = 0; + return nullptr; + } + + SkScalar getGlyphSizeLimit() const { return fGlyphSizeLimit; } + +protected: + // There is a 1:1 mapping between GrMaskFormats and atlas indices + static int MaskFormatToAtlasIndex(GrMaskFormat format) { + static const int sAtlasIndices[] = { + kA8_GrMaskFormat, + kA565_GrMaskFormat, + kARGB_GrMaskFormat, + }; + static_assert(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, "array_size_mismatch"); + + SkASSERT(sAtlasIndices[format] < kMaskFormatCount); + return sAtlasIndices[format]; + } + + GrDrawOpAtlas* getAtlas(GrMaskFormat format) const { + int atlasIndex = MaskFormatToAtlasIndex(format); + SkASSERT(fAtlases[atlasIndex]); + return fAtlases[atlasIndex].get(); + } + + sk_sp<const GrCaps> fCaps; + GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing; + std::unique_ptr<GrDrawOpAtlas> fAtlases[kMaskFormatCount]; + GrDrawOpAtlasConfig fAtlasConfigs[kMaskFormatCount]; + SkScalar fGlyphSizeLimit; + +private: + virtual bool initAtlas(GrMaskFormat) = 0; + + typedef GrOnFlushCallbackObject INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////// +class GrAtlasManager : public GrRestrictedAtlasManager { +public: + GrAtlasManager(GrProxyProvider*, GrGlyphCache*, + float maxTextureBytes, GrDrawOpAtlas::AllowMultitexturing); + + void freeAll(); + + bool hasGlyph(GrGlyph* glyph); + + // To ensure the GrDrawOpAtlas does not evict the Glyph Mask from its texture backing store, + // the client must pass in the current op token along with the GrGlyph. + // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas. + // For convenience, this function will also set the use token for the current glyph if required + // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration + void addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater*, GrGlyph*, + GrDeferredUploadToken); + + void setUseTokenBulk(const GrDrawOpAtlas::BulkUseTokenUpdater& updater, + GrDeferredUploadToken token, + GrMaskFormat format) { + this->getAtlas(format)->setLastUseTokenBulk(updater, token); + } + + // add to texture atlas that matches this format + bool addToAtlas(GrResourceProvider*, GrGlyphCache*, GrAtlasTextStrike*, + GrDrawOpAtlas::AtlasID*, GrDeferredUploadTarget*, GrMaskFormat, + int width, int height, const void* image, SkIPoint16* loc); + + // Some clients may wish to verify the integrity of the texture backing store of the + // GrDrawOpAtlas. The atlasGeneration returned below is a monotonically increasing number which + // changes every time something is removed from the texture backing store. + uint64_t atlasGeneration(GrMaskFormat format) const { + return this->getAtlas(format)->atlasGeneration(); + } + + // GrOnFlushCallbackObject overrides + + void preFlush(GrOnFlushResourceProvider* onFlushResourceProvider, const uint32_t*, int, + SkTArray<sk_sp<GrRenderTargetContext>>*) override { + for (int i = 0; i < kMaskFormatCount; ++i) { + if (fAtlases[i]) { + fAtlases[i]->instantiate(onFlushResourceProvider); + } + } + } + + void postFlush(GrDeferredUploadToken startTokenForNextFlush, + const uint32_t* opListIDs, int numOpListIDs) override { + for (int i = 0; i < kMaskFormatCount; ++i) { + if (fAtlases[i]) { + fAtlases[i]->compact(startTokenForNextFlush); + } + } + } + + // The AtlasGlyph cache always survives freeGpuResources so we want it to remain in the active + // OnFlushCallbackObject list + bool retainOnFreeGpuResources() override { return true; } + + /////////////////////////////////////////////////////////////////////////// + // Functions intended debug only +#ifdef SK_DEBUG + void dump(GrContext* context) const; +#endif + + void setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]); + +private: + bool initAtlas(GrMaskFormat) override; + + GrProxyProvider* fProxyProvider; + GrGlyphCache* fGlyphCache; + + typedef GrRestrictedAtlasManager INHERITED; +}; + +#endif // GrAtlasManager_DEFINED diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp index 90bcf5a224..35e783019f 100644 --- a/src/gpu/text/GrAtlasTextBlob.cpp +++ b/src/gpu/text/GrAtlasTextBlob.cpp @@ -250,8 +250,8 @@ inline std::unique_ptr<GrAtlasTextOp> GrAtlasTextBlob::makeOp( const Run::SubRunInfo& info, int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect, const GrTextUtils::Paint& paint, const SkSurfaceProps& props, - const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache, - GrTextUtils::Target* target) { + const GrDistanceFieldAdjustTable* distanceAdjustTable, + GrRestrictedAtlasManager* restrictedAtlasManager, GrTextUtils::Target* target) { GrMaskFormat format = info.maskFormat(); GrPaint grPaint; @@ -260,11 +260,12 @@ inline std::unique_ptr<GrAtlasTextOp> GrAtlasTextBlob::makeOp( if (info.drawAsDistanceFields()) { bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry()); op = GrAtlasTextOp::MakeDistanceField( - std::move(grPaint), glyphCount, cache, distanceAdjustTable, + std::move(grPaint), glyphCount, restrictedAtlasManager, distanceAdjustTable, target->colorSpaceInfo().isGammaCorrect(), paint.luminanceColor(), info.hasUseLCDText(), useBGR, info.isAntiAliased()); } else { - op = GrAtlasTextOp::MakeBitmap(std::move(grPaint), format, glyphCount, cache); + op = GrAtlasTextOp::MakeBitmap(std::move(grPaint), format, + glyphCount, restrictedAtlasManager); } GrAtlasTextOp::Geometry& geometry = op->geometry(); geometry.fViewMatrix = viewMatrix; @@ -300,8 +301,8 @@ static void calculate_translation(bool applyVM, } } -void GrAtlasTextBlob::flush(GrAtlasGlyphCache* atlasGlyphCache, GrTextUtils::Target* target, - const SkSurfaceProps& props, +void GrAtlasTextBlob::flush(GrRestrictedAtlasManager* restrictedAtlasManager, + GrTextUtils::Target* target, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, const GrTextUtils::Paint& paint, const GrClip& clip, const SkMatrix& viewMatrix, const SkIRect& clipBounds, @@ -376,7 +377,7 @@ void GrAtlasTextBlob::flush(GrAtlasGlyphCache* atlasGlyphCache, GrTextUtils::Tar if (submitOp) { auto op = this->makeOp(info, glyphCount, runIndex, subRun, viewMatrix, x, y, clipRect, std::move(paint), props, distanceAdjustTable, - atlasGlyphCache, target); + restrictedAtlasManager, target); if (op) { if (skipClip) { target->addDrawOp(GrNoClip(), std::move(op)); @@ -394,12 +395,12 @@ void GrAtlasTextBlob::flush(GrAtlasGlyphCache* atlasGlyphCache, GrTextUtils::Tar std::unique_ptr<GrDrawOp> GrAtlasTextBlob::test_makeOp( int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint, const SkSurfaceProps& props, - const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache, - GrTextUtils::Target* target) { + const GrDistanceFieldAdjustTable* distanceAdjustTable, + GrRestrictedAtlasManager* restrictedAtlasManager, GrTextUtils::Target* target) { const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun]; SkIRect emptyRect = SkIRect::MakeEmpty(); return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, emptyRect, paint, props, - distanceAdjustTable, cache, target); + distanceAdjustTable, restrictedAtlasManager, target); } void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) { diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h index 0fae3cd8c7..9f91cae957 100644 --- a/src/gpu/text/GrAtlasTextBlob.h +++ b/src/gpu/text/GrAtlasTextBlob.h @@ -8,9 +8,9 @@ #ifndef GrAtlasTextBlob_DEFINED #define GrAtlasTextBlob_DEFINED -#include "GrAtlasGlyphCache.h" #include "GrColor.h" #include "GrDrawOpAtlas.h" +#include "GrGlyphCache.h" #include "GrMemoryPool.h" #include "GrTextUtils.h" #include "SkDescriptor.h" @@ -22,8 +22,13 @@ #include "SkSurfaceProps.h" #include "SkTInternalLList.h" +class GrAtlasManager; struct GrDistanceFieldAdjustTable; +struct GrGlyph; +class GrGlyphCache; class GrMemoryPool; +class GrRestrictedAtlasManager; + class SkDrawFilter; class SkTextBlob; class SkTextBlobRunIterator; @@ -201,7 +206,7 @@ public: bool mustRegenerate(const GrTextUtils::Paint&, const SkMaskFilterBase::BlurRec& blurRec, const SkMatrix& viewMatrix, SkScalar x, SkScalar y); - void flush(GrAtlasGlyphCache*, GrTextUtils::Target*, const SkSurfaceProps& props, + void flush(GrRestrictedAtlasManager*, GrTextUtils::Target*, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, const GrTextUtils::Paint& paint, const GrClip& clip, const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x, @@ -276,8 +281,8 @@ public: std::unique_ptr<GrDrawOp> test_makeOp(int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint&, const SkSurfaceProps&, - const GrDistanceFieldAdjustTable*, GrAtlasGlyphCache*, - GrTextUtils::Target*); + const GrDistanceFieldAdjustTable*, + GrRestrictedAtlasManager*, GrTextUtils::Target*); private: GrAtlasTextBlob() @@ -506,9 +511,8 @@ private: inline std::unique_ptr<GrAtlasTextOp> makeOp( const Run::SubRunInfo& info, int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect, - const GrTextUtils::Paint& paint, const SkSurfaceProps& props, - const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache, - GrTextUtils::Target*); + const GrTextUtils::Paint&, const SkSurfaceProps&, + const GrDistanceFieldAdjustTable*, GrRestrictedAtlasManager* , GrTextUtils::Target*); struct StrokeInfo { SkScalar fFrameWidth; @@ -562,7 +566,8 @@ public: */ VertexRegenerator(GrResourceProvider*, GrAtlasTextBlob*, int runIdx, int subRunIdx, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color, - GrDeferredUploadTarget*, GrAtlasGlyphCache*, SkAutoGlyphCache*); + GrDeferredUploadTarget*, GrGlyphCache*, GrAtlasManager*, + SkAutoGlyphCache*); struct Result { /** @@ -593,7 +598,8 @@ private: const SkMatrix& fViewMatrix; GrAtlasTextBlob* fBlob; GrDeferredUploadTarget* fUploadTarget; - GrAtlasGlyphCache* fGlyphCache; + GrGlyphCache* fGlyphCache; + GrAtlasManager* fFullAtlasManager; SkAutoGlyphCache* fLazyCache; Run* fRun; Run::SubRunInfo* fSubRun; diff --git a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp index 500ae31703..54001c2628 100644 --- a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp +++ b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "GrAtlasManager.h" #include "GrAtlasTextBlob.h" #include "GrTextUtils.h" #include "SkDistanceFieldGen.h" @@ -193,13 +194,14 @@ inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStri Regenerator::VertexRegenerator(GrResourceProvider* resourceProvider, GrAtlasTextBlob* blob, int runIdx, int subRunIdx, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color, - GrDeferredUploadTarget* uploadTarget, GrAtlasGlyphCache* glyphCache, - SkAutoGlyphCache* lazyCache) + GrDeferredUploadTarget* uploadTarget, GrGlyphCache* glyphCache, + GrAtlasManager* fullAtlasManager, SkAutoGlyphCache* lazyCache) : fResourceProvider(resourceProvider) , fViewMatrix(viewMatrix) , fBlob(blob) , fUploadTarget(uploadTarget) , fGlyphCache(glyphCache) + , fFullAtlasManager(fullAtlasManager) , fLazyCache(lazyCache) , fRun(&blob->fRuns[runIdx]) , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx]) @@ -207,7 +209,7 @@ Regenerator::VertexRegenerator(GrResourceProvider* resourceProvider, GrAtlasText // Compute translation if any fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY); - // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for + // Because the GrGlyphCache may evict the strike a blob depends on using for // generating its texture coords, we have to track whether or not the strike has // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is // otherwise we have to get the new strike, and use that to get the correct glyphs. @@ -275,16 +277,17 @@ Regenerator::Result Regenerator::doRegen() { glyph = fBlob->fGlyphs[glyphOffset]; SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat()); - if (!fGlyphCache->hasGlyph(glyph) && - !strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache, glyph, + if (!fFullAtlasManager->hasGlyph(glyph) && + !strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache, + fFullAtlasManager, glyph, fLazyCache->get(), fSubRun->maskFormat())) { fBrokenRun = glyphIdx > 0; result.fFinished = false; return result; } auto tokenTracker = fUploadTarget->tokenTracker(); - fGlyphCache->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph, - tokenTracker->nextDrawToken()); + fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph, + tokenTracker->nextDrawToken()); } regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride, @@ -302,14 +305,14 @@ Regenerator::Result Regenerator::doRegen() { fSubRun->setStrike(strike); } fSubRun->setAtlasGeneration(fBrokenRun - ? GrDrawOpAtlas::kInvalidAtlasGeneration - : fGlyphCache->atlasGeneration(fSubRun->maskFormat())); + ? GrDrawOpAtlas::kInvalidAtlasGeneration + : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat())); } return result; } Regenerator::Result Regenerator::regenerate() { - uint64_t currentAtlasGen = fGlyphCache->atlasGeneration(fSubRun->maskFormat()); + uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()); // If regenerate() is called multiple times then the atlas gen may have changed. So we check // this each time. if (fSubRun->atlasGeneration() != currentAtlasGen) { @@ -352,9 +355,9 @@ Regenerator::Result Regenerator::regenerate() { // set use tokens for all of the glyphs in our subrun. This is only valid if we // have a valid atlas generation - fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(), - fUploadTarget->tokenTracker()->nextDrawToken(), - fSubRun->maskFormat()); + fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(), + fUploadTarget->tokenTracker()->nextDrawToken(), + fSubRun->maskFormat()); return result; } } diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp index 06282e103e..aadc2dc0c1 100644 --- a/src/gpu/text/GrAtlasTextContext.cpp +++ b/src/gpu/text/GrAtlasTextContext.cpp @@ -118,7 +118,8 @@ void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* t drawFilter); SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo()); - auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache(); + auto glyphCache = context->contextPriv().getGlyphCache(); + auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager(); GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache(); if (canCache) { @@ -151,7 +152,7 @@ void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* t // but we'd have to clear the subrun information textBlobCache->remove(cacheBlob.get()); cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint); - this->regenerateTextBlob(cacheBlob.get(), atlasGlyphCache, + this->regenerateTextBlob(cacheBlob.get(), glyphCache, *context->caps()->shaderCaps(), paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter); } else { @@ -163,7 +164,7 @@ void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* t GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob); sk_sp<GrAtlasTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount)); sanityBlob->setupKey(key, blurRec, skPaint); - this->regenerateTextBlob(sanityBlob.get(), atlasGlyphCache, + this->regenerateTextBlob(sanityBlob.get(), glyphCache, *context->caps()->shaderCaps(), paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter); GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob); @@ -175,17 +176,17 @@ void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* t } else { cacheBlob = textBlobCache->makeBlob(blob); } - this->regenerateTextBlob(cacheBlob.get(), atlasGlyphCache, + this->regenerateTextBlob(cacheBlob.get(), glyphCache, *context->caps()->shaderCaps(), paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter); } - cacheBlob->flush(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint, + cacheBlob->flush(restrictedAtlasManager, target, props, fDistanceAdjustTable.get(), paint, clip, viewMatrix, clipBounds, x, y); } void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, - GrAtlasGlyphCache* fontCache, + GrGlyphCache* glyphCache, const GrShaderCaps& shaderCaps, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, @@ -211,21 +212,21 @@ void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, if (this->canDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) { switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: { - this->drawDFText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + this->drawDFText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y()); break; } case SkTextBlob::kHorizontal_Positioning: { SkPoint dfOffset = SkPoint::Make(x, y + offset.y()); - this->drawDFPosText(cacheBlob, run, fontCache, props, runPaint, + this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, dfOffset); break; } case SkTextBlob::kFull_Positioning: { SkPoint dfOffset = SkPoint::Make(x, y); - this->drawDFPosText(cacheBlob, run, fontCache, props, runPaint, + this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, dfOffset); break; @@ -234,17 +235,17 @@ void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, } else { switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: - DrawBmpText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + DrawBmpText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y()); break; case SkTextBlob::kHorizontal_Positioning: - DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y())); break; case SkTextBlob::kFull_Positioning: - DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, SkPoint::Make(x, y)); break; @@ -255,7 +256,7 @@ void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, inline sk_sp<GrAtlasTextBlob> GrAtlasTextContext::makeDrawTextBlob(GrTextBlobCache* blobCache, - GrAtlasGlyphCache* fontCache, + GrGlyphCache* glyphCache, const GrShaderCaps& shaderCaps, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, @@ -272,10 +273,10 @@ GrAtlasTextContext::makeDrawTextBlob(GrTextBlobCache* blobCache, blob->setRunPaintFlags(0, paint.skPaint().getFlags()); if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) { - this->drawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, + this->drawDFText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix, text, byteLength, x, y); } else { - DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text, + DrawBmpText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix, text, byteLength, x, y); } return blob; @@ -283,7 +284,7 @@ GrAtlasTextContext::makeDrawTextBlob(GrTextBlobCache* blobCache, inline sk_sp<GrAtlasTextBlob> GrAtlasTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache, - GrAtlasGlyphCache* fontCache, + GrGlyphCache* glyphCache, const GrShaderCaps& shaderCaps, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, @@ -302,11 +303,11 @@ GrAtlasTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache, blob->setRunPaintFlags(0, paint.skPaint().getFlags()); if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) { - this->drawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, + this->drawDFPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset); } else { - DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text, - byteLength, pos, scalarsPerPosition, offset); + DrawBmpPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix, + text, byteLength, pos, scalarsPerPosition, offset); } return blob; } @@ -320,17 +321,18 @@ void GrAtlasTextContext::drawText(GrContext* context, GrTextUtils::Target* targe return; } - auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache(); + auto glyphCache = context->contextPriv().getGlyphCache(); + auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager(); auto textBlobCache = context->contextPriv().getTextBlobCache(); GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo()); sk_sp<GrAtlasTextBlob> blob( - this->makeDrawTextBlob(textBlobCache, atlasGlyphCache, + this->makeDrawTextBlob(textBlobCache, glyphCache, *context->caps()->shaderCaps(), paint, ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text, byteLength, x, y)); if (blob) { - blob->flush(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint, + blob->flush(restrictedAtlasManager, target, props, fDistanceAdjustTable.get(), paint, clip, viewMatrix, regionClipBounds, x, y); } } @@ -346,22 +348,23 @@ void GrAtlasTextContext::drawPosText(GrContext* context, GrTextUtils::Target* ta return; } - auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache(); + auto glyphCache = context->contextPriv().getGlyphCache(); + auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager(); auto textBlobCache = context->contextPriv().getTextBlobCache(); sk_sp<GrAtlasTextBlob> blob(this->makeDrawPosTextBlob( - textBlobCache, atlasGlyphCache, + textBlobCache, glyphCache, *context->caps()->shaderCaps(), paint, ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text, byteLength, pos, scalarsPerPosition, offset)); if (blob) { - blob->flush(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint, + blob->flush(restrictedAtlasManager, target, props, fDistanceAdjustTable.get(), paint, clip, viewMatrix, regionClipBounds, offset.fX, offset.fY); } } void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], @@ -377,7 +380,7 @@ void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, blob->setHasBitmap(); if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) { - DrawBmpTextAsPaths(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, + DrawBmpTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags, viewMatrix, text, byteLength, x, y); return; } @@ -387,7 +390,7 @@ void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, viewMatrix, paint.skPaint().getTextAlign(), cache, [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { position += rounding; - BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, + BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, SkScalarFloorToScalar(position.fX), SkScalarFloorToScalar(position.fY), paint.filteredPremulColor(), cache, @@ -398,7 +401,7 @@ void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, } void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, @@ -416,7 +419,7 @@ void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, blob->setHasBitmap(); if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) { - DrawBmpPosTextAsPaths(blob, runIndex, fontCache, props, paint, scalerContextFlags, + DrawBmpPosTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset); return; } @@ -429,7 +432,7 @@ void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, scalarsPerPosition, paint.skPaint().getTextAlign(), cache, [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { position += rounding; - BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, + BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, SkScalarFloorToScalar(position.fX), SkScalarFloorToScalar(position.fY), paint.filteredPremulColor(), cache, SK_Scalar1); @@ -439,7 +442,7 @@ void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, } void GrAtlasTextContext::DrawBmpTextAsPaths(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& origPaint, SkScalerContextFlags scalerContextFlags, @@ -457,7 +460,7 @@ void GrAtlasTextContext::DrawBmpTextAsPaths(GrAtlasTextBlob* blob, int runIndex, GrTextUtils::PathTextIter iter(text, byteLength, pathPaint, true); FallbackTextHelper fallbackTextHelper(viewMatrix, pathPaint.getTextSize(), - fontCache->getGlyphSizeLimit(), + glyphCache->getGlyphSizeLimit(), iter.getPathScale()); const SkGlyph* iterGlyph; @@ -474,11 +477,11 @@ void GrAtlasTextContext::DrawBmpTextAsPaths(GrAtlasTextBlob* blob, int runIndex, lastText = iter.getText(); } - fallbackTextHelper.drawText(blob, runIndex, fontCache, props, origPaint, scalerContextFlags); + fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags); } void GrAtlasTextContext::DrawBmpPosTextAsPaths(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& origPaint, SkScalerContextFlags scalerContextFlags, @@ -497,7 +500,7 @@ void GrAtlasTextContext::DrawBmpPosTextAsPaths(GrAtlasTextBlob* blob, int runInd SkPaint pathPaint(origPaint); SkScalar matrixScale = pathPaint.setupForAsPaths(); FallbackTextHelper fallbackTextHelper(viewMatrix, pathPaint.getTextSize(), matrixScale, - fontCache->getGlyphSizeLimit()); + glyphCache->getGlyphSizeLimit()); // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache. pathPaint.setStyle(SkPaint::kFill_Style); @@ -534,23 +537,23 @@ void GrAtlasTextContext::DrawBmpPosTextAsPaths(GrAtlasTextBlob* blob, int runInd pos += scalarsPerPosition; } - fallbackTextHelper.drawText(blob, runIndex, fontCache, props, origPaint, scalerContextFlags); + fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags); } void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, GrAtlasTextStrike** strike, + GrGlyphCache* grGlyphCache, GrAtlasTextStrike** strike, const SkGlyph& skGlyph, SkScalar sx, SkScalar sy, - GrColor color, SkGlyphCache* glyphCache, + GrColor color, SkGlyphCache* skGlyphCache, SkScalar textRatio) { if (!*strike) { - *strike = fontCache->getStrike(glyphCache); + *strike = grGlyphCache->getStrike(skGlyphCache); } GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), skGlyph.getSubXFixed(), skGlyph.getSubYFixed(), GrGlyph::kCoverage_MaskStyle); - GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache); + GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache); if (!glyph) { return; } @@ -570,7 +573,7 @@ void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex, SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height); - blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx, sy, + blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy, textRatio, true); } @@ -670,7 +673,7 @@ void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob, } void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], @@ -740,12 +743,12 @@ void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex, y -= alignY; SkPoint offset = SkPoint::Make(x, y); - this->drawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, + this->drawDFPosText(blob, runIndex, glyphCache, props, paint, scalerContextFlags, viewMatrix, text, byteLength, positions.begin(), 2, offset); } void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], @@ -771,7 +774,7 @@ void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex, FallbackTextHelper fallbackTextHelper(viewMatrix, paint.skPaint().getTextSize(), - fontCache->getGlyphSizeLimit(), + glyphCache->getGlyphSizeLimit(), textRatio); GrAtlasTextStrike* currStrike = nullptr; @@ -800,7 +803,7 @@ void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex, SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio; if (glyph.fMaskFormat != SkMask::kARGB32_Format) { - DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, glyphPos.fX, + DfAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos.fX, glyphPos.fY, paint.filteredPremulColor(), cache, textRatio); } else { // can't append color glyph to SDF batch, send to fallback @@ -812,25 +815,24 @@ void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex, SkGlyphCache::AttachCache(cache); - fallbackTextHelper.drawText(blob, runIndex, fontCache, props, paint, - scalerContextFlags); + fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, paint, scalerContextFlags); } // TODO: merge with BmpAppendGlyph void GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* cache, GrAtlasTextStrike** strike, + GrGlyphCache* grGlyphCache, GrAtlasTextStrike** strike, const SkGlyph& skGlyph, SkScalar sx, SkScalar sy, - GrColor color, SkGlyphCache* glyphCache, + GrColor color, SkGlyphCache* skGlyphCache, SkScalar textRatio) { if (!*strike) { - *strike = cache->getStrike(glyphCache); + *strike = grGlyphCache->getStrike(skGlyphCache); } GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), skGlyph.getSubXFixed(), skGlyph.getSubYFixed(), GrGlyph::kDistance_MaskStyle); - GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache); + GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache); if (!glyph) { return; } @@ -846,7 +848,7 @@ void GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, height *= textRatio; SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height); - blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx, sy, + blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy, textRatio, false); } @@ -871,7 +873,7 @@ void GrAtlasTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, in } void GrAtlasTextContext::FallbackTextHelper::drawText(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, + GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags) { @@ -909,7 +911,7 @@ void GrAtlasTextContext::FallbackTextHelper::drawText(GrAtlasTextBlob* blob, int SkPoint* glyphPos = fFallbackPos.begin(); while (text < stop) { const SkGlyph& glyph = glyphCacheProc(cache, &text); - GrAtlasTextContext::BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, + GrAtlasTextContext::BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos->fX, glyphPos->fY, textColor, cache, textRatio); glyphPos++; @@ -962,18 +964,19 @@ GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) { SkScalar x = SkIntToScalar(xInt); SkScalar y = SkIntToScalar(yInt); - auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache(); + auto glyphCache = context->contextPriv().getGlyphCache(); + auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager(); // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to // test the text op with this unit test, that is okay. sk_sp<GrAtlasTextBlob> blob(gTextContext->makeDrawTextBlob( - context->contextPriv().getTextBlobCache(), atlasGlyphCache, + context->contextPriv().getTextBlobCache(), glyphCache, *context->caps()->shaderCaps(), utilsPaint, GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, gSurfaceProps, text, static_cast<size_t>(textLen), x, y)); return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, gSurfaceProps, - gTextContext->dfAdjustTable(), atlasGlyphCache, + gTextContext->dfAdjustTable(), restrictedAtlasManager, rtc->textTarget()); } diff --git a/src/gpu/text/GrAtlasTextContext.h b/src/gpu/text/GrAtlasTextContext.h index 207dd67206..37b7de5357 100644 --- a/src/gpu/text/GrAtlasTextContext.h +++ b/src/gpu/text/GrAtlasTextContext.h @@ -74,9 +74,8 @@ private: } void appendText(const SkGlyph& glyph, int count, const char* text, SkPoint glyphPos); - void drawText(GrAtlasTextBlob* blob, int runIndex, - GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, - const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags); + void drawText(GrAtlasTextBlob* blob, int runIndex, GrGlyphCache*, const SkSurfaceProps&, + const GrTextUtils::Paint&, SkScalerContextFlags); private: SkTDArray<char> fFallbackTxt; @@ -96,7 +95,7 @@ private: // Determines if we need to use fake gamma (and contrast boost): static SkScalerContextFlags ComputeScalerContextFlags(const GrColorSpaceInfo&); void regenerateTextBlob(GrAtlasTextBlob* bmp, - GrAtlasGlyphCache*, + GrGlyphCache*, const GrShaderCaps&, const GrTextUtils::Paint&, SkScalerContextFlags scalerContextFlags, @@ -107,7 +106,7 @@ private: static bool HasLCD(const SkTextBlob*); - sk_sp<GrAtlasTextBlob> makeDrawTextBlob(GrTextBlobCache*, GrAtlasGlyphCache*, + sk_sp<GrAtlasTextBlob> makeDrawTextBlob(GrTextBlobCache*, GrGlyphCache*, const GrShaderCaps&, const GrTextUtils::Paint&, SkScalerContextFlags scalerContextFlags, @@ -116,7 +115,7 @@ private: const char text[], size_t byteLength, SkScalar x, SkScalar y) const; - sk_sp<GrAtlasTextBlob> makeDrawPosTextBlob(GrTextBlobCache*, GrAtlasGlyphCache*, + sk_sp<GrAtlasTextBlob> makeDrawPosTextBlob(GrTextBlobCache*, GrGlyphCache*, const GrShaderCaps&, const GrTextUtils::Paint&, SkScalerContextFlags scalerContextFlags, @@ -128,24 +127,24 @@ private: const SkPoint& offset) const; // Functions for appending BMP text to GrAtlasTextBlob - static void DrawBmpText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*, + static void DrawBmpText(GrAtlasTextBlob*, int runIndex, GrGlyphCache*, const SkSurfaceProps&, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y); - static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*, + static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex, GrGlyphCache*, const SkSurfaceProps&, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset); - static void DrawBmpTextAsPaths(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*, + static void DrawBmpTextAsPaths(GrAtlasTextBlob*, int runIndex, GrGlyphCache*, const SkSurfaceProps&, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y); - static void DrawBmpPosTextAsPaths(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*, + static void DrawBmpPosTextAsPaths(GrAtlasTextBlob*, int runIndex, GrGlyphCache*, const SkSurfaceProps&, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, @@ -157,12 +156,12 @@ private: bool canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix, const SkSurfaceProps& props, const GrShaderCaps& caps) const; - void drawDFText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*, const SkSurfaceProps&, + void drawDFText(GrAtlasTextBlob* blob, int runIndex, GrGlyphCache*, const SkSurfaceProps&, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y) const; - void drawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*, + void drawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrGlyphCache*, const SkSurfaceProps&, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const char text[], @@ -174,11 +173,11 @@ private: SkScalar* textRatio, const SkMatrix& viewMatrix) const; - static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*, + static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrGlyphCache*, GrAtlasTextStrike**, const SkGlyph&, SkScalar sx, SkScalar sy, GrColor color, SkGlyphCache*, SkScalar textRatio); - static void DfAppendGlyph(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*, + static void DfAppendGlyph(GrAtlasTextBlob*, int runIndex, GrGlyphCache*, GrAtlasTextStrike**, const SkGlyph&, SkScalar sx, SkScalar sy, GrColor color, SkGlyphCache* cache, SkScalar textRatio); diff --git a/src/gpu/text/GrAtlasGlyphCache.cpp b/src/gpu/text/GrGlyphCache.cpp index f7b1e1269a..d4440897bc 100644 --- a/src/gpu/text/GrAtlasGlyphCache.cpp +++ b/src/gpu/text/GrGlyphCache.cpp @@ -5,81 +5,19 @@ * found in the LICENSE file. */ -#include "GrAtlasGlyphCache.h" -#include "GrContext.h" +#include "GrAtlasManager.h" #include "GrDistanceFieldGenFromVector.h" -#include "GrGpu.h" -#include "GrProxyProvider.h" -#include "GrRectanizer.h" +#include "GrGlyphCache.h" #include "SkAutoMalloc.h" #include "SkDistanceFieldGen.h" -#include "SkMathPriv.h" -#include "SkString.h" - -bool GrAtlasGlyphCache::initAtlas(GrMaskFormat format) { - int index = MaskFormatToAtlasIndex(format); - if (!fAtlases[index]) { - GrPixelConfig config = MaskFormatToPixelConfig(format, *fProxyProvider->caps()); - int width = fAtlasConfigs[index].fWidth; - int height = fAtlasConfigs[index].fHeight; - int numPlotsX = fAtlasConfigs[index].numPlotsX(); - int numPlotsY = fAtlasConfigs[index].numPlotsY(); - - fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, config, width, height, - numPlotsX, numPlotsY, fAllowMultitexturing, - &GrAtlasGlyphCache::HandleEviction, (void*)this); - if (!fAtlases[index]) { - return false; - } - } - return true; -} -GrAtlasGlyphCache::GrAtlasGlyphCache(GrProxyProvider* proxyProvider, float maxTextureBytes, - GrDrawOpAtlas::AllowMultitexturing allowMultitexturing) - : fProxyProvider(proxyProvider) - , fAllowMultitexturing(allowMultitexturing) - , fPreserveStrike(nullptr) { - // Calculate RGBA size. Must be between 512 x 256 and MaxTextureSize x MaxTextureSize / 2 - int log2MaxTextureSize = SkPrevLog2(fProxyProvider->caps()->maxTextureSize()); - int log2MaxDim = 9; - for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) { - int maxDim = 1 << log2MaxDim; - int minDim = 1 << (log2MaxDim - 1); - - if (maxDim * minDim * 4 >= maxTextureBytes) break; - } - - int log2MinDim = log2MaxDim - 1; - int maxDim = 1 << log2MaxDim; - int minDim = 1 << log2MinDim; - // Plots are either 256 or 512. - int maxPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 2))); - int minPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 3))); - - // Setup default atlas configs. The A8 atlas uses maxDim for both width and height, as the A8 - // format is already very compact. - fAtlasConfigs[kA8_GrMaskFormat].fWidth = maxDim; - fAtlasConfigs[kA8_GrMaskFormat].fHeight = maxDim; - fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = maxPlot; - fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = minPlot; - - // A565 and ARGB use maxDim x minDim. - fAtlasConfigs[kA565_GrMaskFormat].fWidth = minDim; - fAtlasConfigs[kA565_GrMaskFormat].fHeight = maxDim; - fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = minPlot; - fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = minPlot; - - fAtlasConfigs[kARGB_GrMaskFormat].fWidth = minDim; - fAtlasConfigs[kARGB_GrMaskFormat].fHeight = maxDim; - fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = minPlot; - fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = minPlot; - - fGlyphSizeLimit = minPlot; +GrGlyphCache::GrGlyphCache() + : fPreserveStrike(nullptr) + , fGlyphSizeLimit(0) { } -GrAtlasGlyphCache::~GrAtlasGlyphCache() { +GrGlyphCache::~GrGlyphCache() { StrikeHash::Iter iter(&fCache); while (!iter.done()) { (*iter).fIsAbandoned = true; @@ -88,7 +26,7 @@ GrAtlasGlyphCache::~GrAtlasGlyphCache() { } } -void GrAtlasGlyphCache::freeAll() { +void GrGlyphCache::freeAll() { StrikeHash::Iter iter(&fCache); while (!iter.done()) { (*iter).fIsAbandoned = true; @@ -96,120 +34,26 @@ void GrAtlasGlyphCache::freeAll() { ++iter; } fCache.rewind(); - for (int i = 0; i < kMaskFormatCount; ++i) { - fAtlases[i] = nullptr; - } } -void GrAtlasGlyphCache::HandleEviction(GrDrawOpAtlas::AtlasID id, void* ptr) { - GrAtlasGlyphCache* fontCache = reinterpret_cast<GrAtlasGlyphCache*>(ptr); +void GrGlyphCache::HandleEviction(GrDrawOpAtlas::AtlasID id, void* ptr) { + GrGlyphCache* glyphCache = reinterpret_cast<GrGlyphCache*>(ptr); - StrikeHash::Iter iter(&fontCache->fCache); + StrikeHash::Iter iter(&glyphCache->fCache); for (; !iter.done(); ++iter) { GrAtlasTextStrike* strike = &*iter; strike->removeID(id); // clear out any empty strikes. We will preserve the strike whose call to addToAtlas // triggered the eviction - if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) { - fontCache->fCache.remove(GrAtlasTextStrike::GetKey(*strike)); + if (strike != glyphCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) { + glyphCache->fCache.remove(GrAtlasTextStrike::GetKey(*strike)); strike->fIsAbandoned = true; strike->unref(); } } } -#ifdef SK_DEBUG -#include "GrContextPriv.h" -#include "GrSurfaceProxy.h" -#include "GrSurfaceContext.h" -#include "GrTextureProxy.h" - -#include "SkBitmap.h" -#include "SkImageEncoder.h" -#include "SkStream.h" -#include <stdio.h> - -/** - * Write the contents of the surface proxy to a PNG. Returns true if successful. - * @param filename Full path to desired file - */ -static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char* filename) { - if (!sProxy) { - return false; - } - - SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); - SkBitmap bm; - if (!bm.tryAllocPixels(ii)) { - return false; - } - - sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext( - sk_ref_sp(sProxy))); - if (!sContext || !sContext->asTextureProxy()) { - return false; - } - - bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), 0, 0); - if (!result) { - SkDebugf("------ failed to read pixels for %s\n", filename); - return false; - } - - // remove any previous version of this file - remove(filename); - - SkFILEWStream file(filename); - if (!file.isValid()) { - SkDebugf("------ failed to create file: %s\n", filename); - remove(filename); // remove any partial file - return false; - } - - if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) { - SkDebugf("------ failed to encode %s\n", filename); - remove(filename); // remove any partial file - return false; - } - - return true; -} - -void GrAtlasGlyphCache::dump(GrContext* context) const { - static int gDumpCount = 0; - for (int i = 0; i < kMaskFormatCount; ++i) { - if (fAtlases[i]) { - const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies(); - for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) { - SkASSERT(proxies[pageIdx]); - SkString filename; -#ifdef SK_BUILD_FOR_ANDROID - filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx); -#else - filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx); -#endif - - save_pixels(context, proxies[pageIdx].get(), filename.c_str()); - } - } - } - ++gDumpCount; -} -#endif - -void GrAtlasGlyphCache::setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]) { - // Delete any old atlases. - // This should be safe to do as long as we are not in the middle of a flush. - for (int i = 0; i < kMaskFormatCount; i++) { - fAtlases[i] = nullptr; - } - memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs)); -} - -/////////////////////////////////////////////////////////////////////////////// - static inline GrMaskFormat get_packed_glyph_mask_format(const SkGlyph& glyph) { SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); switch (format) { @@ -450,7 +294,8 @@ void GrAtlasTextStrike::removeID(GrDrawOpAtlas::AtlasID id) { bool GrAtlasTextStrike::addGlyphToAtlas(GrResourceProvider* resourceProvider, GrDeferredUploadTarget* target, - GrAtlasGlyphCache* atlasGlyphCache, + GrGlyphCache* glyphCache, + GrAtlasManager* fullAtlasManager, GrGlyph* glyph, SkGlyphCache* cache, GrMaskFormat expectedMaskFormat) { @@ -477,10 +322,10 @@ bool GrAtlasTextStrike::addGlyphToAtlas(GrResourceProvider* resourceProvider, } } - bool success = atlasGlyphCache->addToAtlas(resourceProvider, this, &glyph->fID, target, - expectedMaskFormat, - glyph->width(), glyph->height(), - storage.get(), &glyph->fAtlasLocation); + bool success = fullAtlasManager->addToAtlas(resourceProvider, glyphCache, this, + &glyph->fID, target, expectedMaskFormat, + glyph->width(), glyph->height(), + storage.get(), &glyph->fAtlasLocation); if (success) { SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != glyph->fID); fAtlasedGlyphs++; diff --git a/src/gpu/text/GrGlyphCache.h b/src/gpu/text/GrGlyphCache.h new file mode 100644 index 0000000000..169e4b30a5 --- /dev/null +++ b/src/gpu/text/GrGlyphCache.h @@ -0,0 +1,148 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrAtlasGlyphCache_DEFINED +#define GrAtlasGlyphCache_DEFINED + +#include "GrDrawOpAtlas.h" +#include "GrGlyph.h" +#include "SkArenaAlloc.h" +#include "SkGlyphCache.h" +#include "SkTDynamicHash.h" + +class GrGlyphCache; +class GrAtlasManager; +class GrGpu; + +/** + * The GrAtlasTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory + * is indexed by a PackedID and SkGlyphCache. The SkGlyphCache is what actually creates the mask. + * The GrAtlasTextStrike may outlive the generating SkGlyphCache. However, it retains a copy + * of it's SkDescriptor as a key to access (or regenerate) the SkGlyphCache. GrAtlasTextStrike are + * created by and owned by a GrGlyphCache. + */ +class GrAtlasTextStrike : public SkNVRefCnt<GrAtlasTextStrike> { +public: + GrAtlasTextStrike(const SkDescriptor& fontScalerKey); + ~GrAtlasTextStrike(); + + inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed, + SkGlyphCache* cache) { + GrGlyph* glyph = fCache.find(packed); + if (nullptr == glyph) { + glyph = this->generateGlyph(skGlyph, packed, cache); + } + return glyph; + } + + // This variant of the above function is called by GrAtlasTextOp. At this point, it is possible + // that the maskformat of the glyph differs from what we expect. In these cases we will just + // draw a clear square. + // skbug:4143 crbug:510931 + inline GrGlyph* getGlyph(GrGlyph::PackedID packed, + GrMaskFormat expectedMaskFormat, + SkGlyphCache* cache) { + GrGlyph* glyph = fCache.find(packed); + if (nullptr == glyph) { + // We could return this to the caller, but in practice it adds code complexity for + // potentially little benefit(ie, if the glyph is not in our font cache, then its not + // in the atlas and we're going to be doing a texture upload anyways). + const SkGlyph& skGlyph = GrToSkGlyph(cache, packed); + glyph = this->generateGlyph(skGlyph, packed, cache); + glyph->fMaskFormat = expectedMaskFormat; + } + return glyph; + } + + // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's + // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never + // happen. + // TODO we can handle some of these cases if we really want to, but the long term solution is to + // get the actual glyph image itself when we get the glyph metrics. + bool addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, GrGlyphCache*, + GrAtlasManager*, GrGlyph*, + SkGlyphCache*, GrMaskFormat expectedMaskFormat); + + // testing + int countGlyphs() const { return fCache.count(); } + + // remove any references to this plot + void removeID(GrDrawOpAtlas::AtlasID); + + // If a TextStrike is abandoned by the cache, then the caller must get a new strike + bool isAbandoned() const { return fIsAbandoned; } + + static const SkDescriptor& GetKey(const GrAtlasTextStrike& ts) { + return *ts.fFontScalerKey.getDesc(); + } + + static uint32_t Hash(const SkDescriptor& desc) { return desc.getChecksum(); } + +private: + SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache; + SkAutoDescriptor fFontScalerKey; + SkArenaAlloc fPool{512}; + + int fAtlasedGlyphs; + bool fIsAbandoned; + + static const SkGlyph& GrToSkGlyph(SkGlyphCache* cache, GrGlyph::PackedID id) { + return cache->getGlyphIDMetrics(GrGlyph::UnpackID(id), + GrGlyph::UnpackFixedX(id), + GrGlyph::UnpackFixedY(id)); + } + + GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, SkGlyphCache*); + + friend class GrGlyphCache; +}; + +/** + * GrGlyphCache manages strikes which are indexed by a SkGlyphCache. These strikes can then be + * used to generate individual Glyph Masks. + */ +class GrGlyphCache { +public: + GrGlyphCache(); + ~GrGlyphCache(); + + void setGlyphSizeLimit(SkScalar sizeLimit) { fGlyphSizeLimit = sizeLimit; } + SkScalar getGlyphSizeLimit() const { return fGlyphSizeLimit; } + + void setStrikeToPreserve(GrAtlasTextStrike* strike) { fPreserveStrike = strike; } + + // The user of the cache may hold a long-lived ref to the returned strike. However, actions by + // another client of the cache may cause the strike to be purged while it is still reffed. + // Therefore, the caller must check GrAtlasTextStrike::isAbandoned() if there are other + // interactions with the cache since the strike was received. + inline GrAtlasTextStrike* getStrike(const SkGlyphCache* cache) { + GrAtlasTextStrike* strike = fCache.find(cache->getDescriptor()); + if (nullptr == strike) { + strike = this->generateStrike(cache); + } + return strike; + } + + void freeAll(); + + static void HandleEviction(GrDrawOpAtlas::AtlasID, void*); + +private: + GrAtlasTextStrike* generateStrike(const SkGlyphCache* cache) { + GrAtlasTextStrike* strike = new GrAtlasTextStrike(cache->getDescriptor()); + fCache.add(strike); + return strike; + } + + using StrikeHash = SkTDynamicHash<GrAtlasTextStrike, SkDescriptor>; + + StrikeHash fCache; + GrAtlasTextStrike* fPreserveStrike; + SkScalar fGlyphSizeLimit; +}; + +#endif diff --git a/src/gpu/text/GrTextUtils.h b/src/gpu/text/GrTextUtils.h index 5fe38fe773..69421c2474 100644 --- a/src/gpu/text/GrTextUtils.h +++ b/src/gpu/text/GrTextUtils.h @@ -16,13 +16,13 @@ #include "SkTextToPathIter.h" #include "SkTLazy.h" -class GrAtlasGlyphCache; class GrAtlasTextBlob; class GrAtlasTextOp; class GrAtlasTextStrike; class GrClip; class GrColorSpaceXform; class GrContext; +class GrGlyphCache; class GrPaint; class GrShaderCaps; class SkColorSpace; |