1 /* 2 * Copyright 2015 Julian Harnath <julian.harnath@rwth-aachen.de> 3 * All rights reserved. Distributed under the terms of the MIT license. 4 */ 5 6 #include "AlphaMaskCache.h" 7 8 #include "AlphaMask.h" 9 #include "ShapePrivate.h" 10 11 #include <AutoLocker.h> 12 13 14 //#define PRINT_ALPHA_MASK_CACHE_STATISTICS 15 #ifdef PRINT_ALPHA_MASK_CACHE_STATISTICS 16 static uint32 sAlphaMaskGetCount = 0; 17 #endif 18 19 20 AlphaMaskCache AlphaMaskCache::sDefaultInstance; 21 22 23 AlphaMaskCache::AlphaMaskCache() 24 : 25 fLock("AlphaMask cache"), 26 fCurrentCacheBytes(0), 27 fTooLargeMaskCount(0), 28 fMasksReplacedCount(0), 29 fHitCount(0), 30 fMissCount(0), 31 fLowerMaskReferencedCount(0) 32 { 33 } 34 35 36 AlphaMaskCache::~AlphaMaskCache() 37 { 38 Clear(); 39 } 40 41 42 /* static */ AlphaMaskCache* 43 AlphaMaskCache::Default() 44 { 45 return &sDefaultInstance; 46 } 47 48 49 status_t 50 AlphaMaskCache::Put(ShapeAlphaMask* mask) 51 { 52 AutoLocker<BLocker> locker(fLock); 53 54 size_t maskStackSize = mask->BitmapSize(); 55 maskStackSize += _FindUncachedPreviousMasks(mask, true); 56 57 if (maskStackSize > kMaxCacheBytes) { 58 _FindUncachedPreviousMasks(mask, false); 59 fTooLargeMaskCount++; 60 return B_NO_MEMORY; 61 } 62 63 if (fCurrentCacheBytes + maskStackSize > kMaxCacheBytes) { 64 for (ShapeMaskSet::iterator it = fShapeMasks.begin(); 65 it != fShapeMasks.end();) { 66 67 if (atomic_get(&it->fMask->fNextMaskCount) > 0) { 68 it++; 69 continue; 70 } 71 72 size_t removedMaskStackSize = it->fMask->BitmapSize(); 73 removedMaskStackSize += _FindUncachedPreviousMasks(it->fMask, 74 false); 75 fCurrentCacheBytes -= removedMaskStackSize; 76 77 it->fMask->fInCache = false; 78 it->fMask->ReleaseReference(); 79 fMasksReplacedCount++; 80 fShapeMasks.erase(it++); 81 82 if (fCurrentCacheBytes + maskStackSize <= kMaxCacheBytes) 83 break; 84 } 85 } 86 87 if (fCurrentCacheBytes + maskStackSize > kMaxCacheBytes) { 88 _FindUncachedPreviousMasks(mask, false); 89 fTooLargeMaskCount++; 90 return B_NO_MEMORY; 91 } 92 93 fCurrentCacheBytes += maskStackSize; 94 95 ShapeMaskElement element(mask->fShape, mask, mask->fPreviousMask.Get(), 96 mask->fInverse); 97 fShapeMasks.insert(element); 98 mask->AcquireReference(); 99 mask->fInCache = true; 100 return B_OK; 101 } 102 103 104 ShapeAlphaMask* 105 AlphaMaskCache::Get(const shape_data& shape, AlphaMask* previousMask, 106 bool inverse) 107 { 108 AutoLocker<BLocker> locker(fLock); 109 110 #ifdef PRINT_ALPHA_MASK_CACHE_STATISTICS 111 if (sAlphaMaskGetCount++ > 200) { 112 _PrintAndResetStatistics(); 113 sAlphaMaskGetCount = 0; 114 } 115 #endif 116 117 ShapeMaskElement element(&shape, NULL, previousMask, inverse); 118 ShapeMaskSet::iterator it = fShapeMasks.find(element); 119 if (it == fShapeMasks.end()) { 120 fMissCount++; 121 return NULL; 122 } 123 fHitCount++; 124 it->fMask->AcquireReference(); 125 return it->fMask; 126 } 127 128 129 void 130 AlphaMaskCache::Clear() 131 { 132 AutoLocker<BLocker> locker(fLock); 133 134 for (ShapeMaskSet::iterator it = fShapeMasks.begin(); 135 it != fShapeMasks.end(); it++) { 136 it->fMask->fInCache = false; 137 it->fMask->fIndirectCacheReferences = 0; 138 it->fMask->ReleaseReference(); 139 } 140 fShapeMasks.clear(); 141 fTooLargeMaskCount = 0; 142 fMasksReplacedCount = 0; 143 fHitCount = 0; 144 fMissCount = 0; 145 fLowerMaskReferencedCount = 0; 146 } 147 148 149 size_t 150 AlphaMaskCache::_FindUncachedPreviousMasks(AlphaMask* mask, bool reference) 151 { 152 const int32 referenceModifier = reference ? 1 : -1; 153 size_t addedOrRemovedSize = 0; 154 155 for (AlphaMask* lowerMask = mask->fPreviousMask.Get(); lowerMask != NULL; 156 lowerMask = lowerMask->fPreviousMask.Get()) { 157 if (lowerMask->fInCache) 158 continue; 159 uint32 oldReferences = lowerMask->fIndirectCacheReferences; 160 lowerMask->fIndirectCacheReferences += referenceModifier; 161 if (lowerMask->fIndirectCacheReferences == 0 || oldReferences == 0) { 162 // We either newly referenced the mask for the first time, or 163 // released the last reference 164 addedOrRemovedSize += lowerMask->BitmapSize(); 165 fLowerMaskReferencedCount += referenceModifier; 166 } 167 } 168 169 return addedOrRemovedSize; 170 } 171 172 173 void 174 AlphaMaskCache::_PrintAndResetStatistics() 175 { 176 debug_printf("AlphaMaskCache statistics: size=%" B_PRIuSIZE " bytes=%" 177 B_PRIuSIZE " lower=%4" B_PRIu32 " total=%" B_PRIuSIZE " too_large=%4" 178 B_PRIu32 " replaced=%4" B_PRIu32 " hit=%4" B_PRIu32 " miss=%4" B_PRIu32 179 "\n", fShapeMasks.size(), fCurrentCacheBytes, fLowerMaskReferencedCount, 180 fShapeMasks.size() + fLowerMaskReferencedCount, fTooLargeMaskCount, 181 fMasksReplacedCount, fHitCount, fMissCount); 182 fTooLargeMaskCount = 0; 183 fMasksReplacedCount = 0; 184 fHitCount = 0; 185 fMissCount = 0; 186 } 187