1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef GrAtlasManager_DEFINED 9 #define GrAtlasManager_DEFINED 10 11 #include "src/gpu/GrCaps.h" 12 #include "src/gpu/GrDrawOpAtlas.h" 13 #include "src/gpu/GrOnFlushResourceProvider.h" 14 #include "src/gpu/GrProxyProvider.h" 15 16 class GrGlyph; 17 class GrResourceProvider; 18 class SkGlyph; 19 class GrTextStrike; 20 21 ////////////////////////////////////////////////////////////////////////////////////////////////// 22 /** The GrAtlasManager manages the lifetime of and access to GrDrawOpAtlases. 23 * It is only available at flush and only via the GrOpFlushState. 24 * 25 * This implies that all of the advanced atlasManager functionality (i.e., 26 * adding glyphs to the atlas) are only available at flush time. 27 */ 28 class GrAtlasManager : public GrOnFlushCallbackObject, public GrDrawOpAtlas::GenerationCounter { 29 public: 30 GrAtlasManager(GrProxyProvider*, size_t maxTextureBytes, GrDrawOpAtlas::AllowMultitexturing); 31 ~GrAtlasManager() override; 32 33 // if getViews returns nullptr, the client must not try to use other functions on the 34 // GrStrikeCache which use the atlas. This function *must* be called first, before other 35 // functions which use the atlas. Note that we can have proxies available but none active 36 // (i.e., none instantiated). getViews(GrMaskFormat format,unsigned int * numActiveProxies)37 const GrSurfaceProxyView* getViews(GrMaskFormat format, unsigned int* numActiveProxies) { 38 format = this->resolveMaskFormat(format); 39 if (this->initAtlas(format)) { 40 *numActiveProxies = this->getAtlas(format)->numActivePages(); 41 return this->getAtlas(format)->getViews(); 42 } 43 *numActiveProxies = 0; 44 return nullptr; 45 } 46 47 void freeAll(); 48 49 bool hasGlyph(GrMaskFormat, GrGlyph*); 50 51 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE) incAtlasHitCount()52 void incAtlasHitCount() { fAtlasHitCount++; } incAtlasMissCount()53 void incAtlasMissCount() { fAtlasMissCount++; } atlasHitRate()54 float atlasHitRate() { 55 if (fAtlasHitCount + fAtlasMissCount == 0) { 56 return 0; 57 } 58 return (float)fAtlasHitCount / (fAtlasHitCount + fAtlasMissCount); 59 } resetHitCount()60 void resetHitCount() { 61 fAtlasHitCount = 0; 62 fAtlasMissCount = 0; 63 } 64 #endif 65 // If bilerpPadding == true then addGlyphToAtlas adds a 1 pixel border to the glyph before 66 // inserting it into the atlas. 67 GrDrawOpAtlas::ErrorCode addGlyphToAtlas(const SkGlyph&, 68 GrGlyph*, 69 int srcPadding, 70 GrResourceProvider*, 71 GrDeferredUploadTarget*, 72 bool bilerpPadding = false); 73 74 // To ensure the GrDrawOpAtlas does not evict the Glyph Mask from its texture backing store, 75 // the client must pass in the current op token along with the GrGlyph. 76 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas. 77 // For convenience, this function will also set the use token for the current glyph if required 78 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration 79 void addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater*, GrMaskFormat, GrGlyph*, 80 GrDeferredUploadToken); 81 setUseTokenBulk(const GrDrawOpAtlas::BulkUseTokenUpdater & updater,GrDeferredUploadToken token,GrMaskFormat format)82 void setUseTokenBulk(const GrDrawOpAtlas::BulkUseTokenUpdater& updater, 83 GrDeferredUploadToken token, 84 GrMaskFormat format) { 85 this->getAtlas(format)->setLastUseTokenBulk(updater, token); 86 } 87 88 // add to texture atlas that matches this format 89 GrDrawOpAtlas::ErrorCode addToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, GrMaskFormat, 90 int width, int height, const void* image, 91 GrDrawOpAtlas::AtlasLocator*); 92 93 // Some clients may wish to verify the integrity of the texture backing store of the 94 // GrDrawOpAtlas. The atlasGeneration returned below is a monotonically increasing number which 95 // changes every time something is removed from the texture backing store. atlasGeneration(GrMaskFormat format)96 uint64_t atlasGeneration(GrMaskFormat format) const { 97 return this->getAtlas(format)->atlasGeneration(); 98 } 99 100 // GrOnFlushCallbackObject overrides 101 preFlush(GrOnFlushResourceProvider * onFlushRP,SkSpan<const uint32_t>)102 void preFlush(GrOnFlushResourceProvider* onFlushRP, SkSpan<const uint32_t>) override { 103 for (int i = 0; i < kMaskFormatCount; ++i) { 104 if (fAtlases[i]) { 105 fAtlases[i]->instantiate(onFlushRP); 106 } 107 } 108 } 109 postFlush(GrDeferredUploadToken startTokenForNextFlush,SkSpan<const uint32_t>)110 void postFlush(GrDeferredUploadToken startTokenForNextFlush, SkSpan<const uint32_t>) override { 111 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE) 112 bool isRadicals = false; 113 static int count = 0; 114 count++; 115 if (count == 5) { 116 float hitRate = atlasHitRate(); 117 if (!(fabs(hitRate-0) <= 1.0e-6)) { 118 #ifdef SK_DEBUG_ATLAS_HIT_RATE 119 SkDebugf("----- last 5 flush AtlasHitRate = %{public}6.2f.", hitRate); 120 #endif 121 if (hitRate < 0.2) { 122 isRadicals = true; 123 } 124 } 125 resetHitCount(); 126 count = 0; 127 } 128 #endif 129 for (int i = 0; i < kMaskFormatCount; ++i) { 130 if (fAtlases[i]) { 131 #ifdef SK_ENABLE_SMALL_PAGE 132 fAtlases[i]->setRadicalsCompactFlag(isRadicals); 133 #endif 134 fAtlases[i]->compact(startTokenForNextFlush); 135 } 136 } 137 } 138 139 // The AtlasGlyph cache always survives freeGpuResources so we want it to remain in the active 140 // OnFlushCallbackObject list retainOnFreeGpuResources()141 bool retainOnFreeGpuResources() override { return true; } 142 143 /////////////////////////////////////////////////////////////////////////// 144 // Functions intended debug only 145 #ifdef SK_DEBUG 146 void dump(GrDirectContext*) const; 147 #endif 148 149 void setAtlasDimensionsToMinimum_ForTesting(); 150 void setMaxPages_TestingOnly(uint32_t maxPages); 151 152 private: 153 bool initAtlas(GrMaskFormat); 154 // Change an expected 565 mask format to 8888 if 565 is not supported (will happen when using 155 // Metal on macOS). The actual conversion of the data is handled in get_packed_glyph_image() in 156 // GrStrikeCache.cpp resolveMaskFormat(GrMaskFormat format)157 GrMaskFormat resolveMaskFormat(GrMaskFormat format) const { 158 if (kA565_GrMaskFormat == format && 159 !fProxyProvider->caps()->getDefaultBackendFormat(GrColorType::kBGR_565, 160 GrRenderable::kNo).isValid()) { 161 format = kARGB_GrMaskFormat; 162 } 163 return format; 164 } 165 166 // There is a 1:1 mapping between GrMaskFormats and atlas indices MaskFormatToAtlasIndex(GrMaskFormat format)167 static int MaskFormatToAtlasIndex(GrMaskFormat format) { return static_cast<int>(format); } AtlasIndexToMaskFormat(int idx)168 static GrMaskFormat AtlasIndexToMaskFormat(int idx) { return static_cast<GrMaskFormat>(idx); } 169 getAtlas(GrMaskFormat format)170 GrDrawOpAtlas* getAtlas(GrMaskFormat format) const { 171 format = this->resolveMaskFormat(format); 172 int atlasIndex = MaskFormatToAtlasIndex(format); 173 SkASSERT(fAtlases[atlasIndex]); 174 return fAtlases[atlasIndex].get(); 175 } 176 177 GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing; 178 std::unique_ptr<GrDrawOpAtlas> fAtlases[kMaskFormatCount]; 179 static_assert(kMaskFormatCount == 3); 180 GrProxyProvider* fProxyProvider; 181 sk_sp<const GrCaps> fCaps; 182 GrDrawOpAtlasConfig fAtlasConfig; 183 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE) 184 int fAtlasHitCount = 0; 185 int fAtlasMissCount = 0; 186 #endif 187 using INHERITED = GrOnFlushCallbackObject; 188 }; 189 190 #endif // GrAtlasManager_DEFINED 191