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