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
AlphaMaskCache()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
~AlphaMaskCache()36 AlphaMaskCache::~AlphaMaskCache()
37 {
38 Clear();
39 }
40
41
42 /* static */ AlphaMaskCache*
Default()43 AlphaMaskCache::Default()
44 {
45 return &sDefaultInstance;
46 }
47
48
49 status_t
Put(ShapeAlphaMask * mask)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*
Get(const shape_data & shape,AlphaMask * previousMask,bool inverse)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
Clear()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
_FindUncachedPreviousMasks(AlphaMask * mask,bool reference)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
_PrintAndResetStatistics()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