xref: /haiku/src/servers/app/drawing/AlphaMaskCache.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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