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 16static uint32 sAlphaMaskGetCount = 0; 17#endif 18 19 20AlphaMaskCache AlphaMaskCache::sDefaultInstance; 21 22 23AlphaMaskCache::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 36AlphaMaskCache::~AlphaMaskCache() 37{ 38 Clear(); 39} 40 41 42/* static */ AlphaMaskCache* 43AlphaMaskCache::Default() 44{ 45 return &sDefaultInstance; 46} 47 48 49status_t 50AlphaMaskCache::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 104ShapeAlphaMask* 105AlphaMaskCache::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 129void 130AlphaMaskCache::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 149size_t 150AlphaMaskCache::_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 173void 174AlphaMaskCache::_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