/src/mozilla-central/gfx/layers/client/TextureClientPool.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "TextureClientPool.h" |
8 | | #include "CompositableClient.h" |
9 | | #include "mozilla/layers/CompositableForwarder.h" |
10 | | #include "mozilla/layers/TextureForwarder.h" |
11 | | #include "mozilla/layers/TiledContentClient.h" |
12 | | |
13 | | #include "gfxPrefs.h" |
14 | | |
15 | | #include "nsComponentManagerUtils.h" |
16 | | |
17 | | #define TCP_LOG(...) |
18 | | //#define TCP_LOG(...) printf_stderr(__VA_ARGS__); |
19 | | |
20 | | namespace mozilla { |
21 | | namespace layers { |
22 | | |
23 | | // We want to shrink to our maximum size of N unused tiles |
24 | | // after a timeout to allow for short-term budget requirements |
25 | | static void |
26 | | ShrinkCallback(nsITimer *aTimer, void *aClosure) |
27 | 0 | { |
28 | 0 | static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize(); |
29 | 0 | } |
30 | | |
31 | | // After a certain amount of inactivity, let's clear the pool so that |
32 | | // we don't hold onto tiles needlessly. In general, allocations are |
33 | | // cheap enough that re-allocating isn't an issue unless we're allocating |
34 | | // at an inopportune time (e.g. mid-animation). |
35 | | static void |
36 | | ClearCallback(nsITimer *aTimer, void *aClosure) |
37 | 0 | { |
38 | 0 | static_cast<TextureClientPool*>(aClosure)->Clear(); |
39 | 0 | } |
40 | | |
41 | | TextureClientPool::TextureClientPool(LayersBackend aLayersBackend, |
42 | | bool aSupportsTextureDirectMapping, |
43 | | int32_t aMaxTextureSize, |
44 | | gfx::SurfaceFormat aFormat, |
45 | | gfx::IntSize aSize, |
46 | | TextureFlags aFlags, |
47 | | uint32_t aShrinkTimeoutMsec, |
48 | | uint32_t aClearTimeoutMsec, |
49 | | uint32_t aInitialPoolSize, |
50 | | uint32_t aPoolUnusedSize, |
51 | | TextureForwarder* aAllocator) |
52 | | : mBackend(aLayersBackend) |
53 | | , mMaxTextureSize(aMaxTextureSize) |
54 | | , mFormat(aFormat) |
55 | | , mSize(aSize) |
56 | | , mFlags(aFlags) |
57 | | , mShrinkTimeoutMsec(aShrinkTimeoutMsec) |
58 | | , mClearTimeoutMsec(aClearTimeoutMsec) |
59 | | , mInitialPoolSize(aInitialPoolSize) |
60 | | , mPoolUnusedSize(aPoolUnusedSize) |
61 | | , mOutstandingClients(0) |
62 | | , mSurfaceAllocator(aAllocator) |
63 | | , mDestroyed(false) |
64 | | , mSupportsTextureDirectMapping(aSupportsTextureDirectMapping) |
65 | 0 | { |
66 | 0 | TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n", |
67 | 0 | this, mInitialPoolSize); |
68 | 0 | mShrinkTimer = NS_NewTimer(); |
69 | 0 | mClearTimer = NS_NewTimer(); |
70 | 0 | if (aFormat == gfx::SurfaceFormat::UNKNOWN) { |
71 | 0 | gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format"; |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | TextureClientPool::~TextureClientPool() |
76 | 0 | { |
77 | 0 | mShrinkTimer->Cancel(); |
78 | 0 | mClearTimer->Cancel(); |
79 | 0 | } |
80 | | |
81 | | #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL |
82 | | static bool TestClientPool(const char* what, |
83 | | TextureClient* aClient, |
84 | | TextureClientPool* aPool) |
85 | | { |
86 | | if (!aClient || !aPool) { |
87 | | return false; |
88 | | } |
89 | | |
90 | | TextureClientPool* actual = aClient->mPoolTracker; |
91 | | bool ok = (actual == aPool); |
92 | | if (ok) { |
93 | | ok = (aClient->GetFormat() == aPool->GetFormat()); |
94 | | } |
95 | | |
96 | | if (!ok) { |
97 | | if (actual) { |
98 | | gfxCriticalError() << "Pool error(" << what << "): " |
99 | | << aPool << "-" << aPool->GetFormat() << ", " |
100 | | << actual << "-" << actual->GetFormat() << ", " |
101 | | << aClient->GetFormat(); |
102 | | MOZ_CRASH("GFX: Crashing with actual"); |
103 | | } else { |
104 | | gfxCriticalError() << "Pool error(" << what << "): " |
105 | | << aPool << "-" << aPool->GetFormat() << ", nullptr, " |
106 | | << aClient->GetFormat(); |
107 | | MOZ_CRASH("GFX: Crashing without actual"); |
108 | | } |
109 | | } |
110 | | return ok; |
111 | | } |
112 | | #endif |
113 | | |
114 | | already_AddRefed<TextureClient> |
115 | | TextureClientPool::GetTextureClient() |
116 | 0 | { |
117 | 0 | // Try to fetch a client from the pool |
118 | 0 | RefPtr<TextureClient> textureClient; |
119 | 0 |
|
120 | 0 | // We initially allocate mInitialPoolSize for our pool. If we run |
121 | 0 | // out of TextureClients, we allocate additional TextureClients to try and keep around |
122 | 0 | // mPoolUnusedSize |
123 | 0 | if (mTextureClients.empty()) { |
124 | 0 | AllocateTextureClient(); |
125 | 0 | } |
126 | 0 |
|
127 | 0 | if (mTextureClients.empty()) { |
128 | 0 | // All our allocations failed, return nullptr |
129 | 0 | return nullptr; |
130 | 0 | } |
131 | 0 | |
132 | 0 | mOutstandingClients++; |
133 | 0 | textureClient = mTextureClients.top(); |
134 | 0 | mTextureClients.pop(); |
135 | | #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL |
136 | | if (textureClient) { |
137 | | textureClient->mPoolTracker = this; |
138 | | } |
139 | | DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this); |
140 | | MOZ_ASSERT(ok); |
141 | | #endif |
142 | | TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n", |
143 | 0 | this, textureClient.get(), mTextureClients.size(), mOutstandingClients); |
144 | 0 |
|
145 | 0 | return textureClient.forget(); |
146 | 0 | } |
147 | | |
148 | | void |
149 | | TextureClientPool::AllocateTextureClient() |
150 | 0 | { |
151 | 0 | TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n", |
152 | 0 | this, mOutstandingClients); |
153 | 0 |
|
154 | 0 | TextureAllocationFlags allocFlags = ALLOC_DEFAULT; |
155 | 0 |
|
156 | 0 | if (mSupportsTextureDirectMapping && std::max(mSize.width, mSize.height) <= mMaxTextureSize) { |
157 | 0 | allocFlags = TextureAllocationFlags(allocFlags | ALLOC_ALLOW_DIRECT_MAPPING); |
158 | 0 | } |
159 | 0 |
|
160 | 0 | RefPtr<TextureClient> newClient; |
161 | 0 | if (gfxPrefs::ForceShmemTiles()) { |
162 | 0 | // gfx::BackendType::NONE means use the content backend |
163 | 0 | newClient = |
164 | 0 | TextureClient::CreateForRawBufferAccess(mSurfaceAllocator, |
165 | 0 | mFormat, mSize, |
166 | 0 | gfx::BackendType::NONE, |
167 | 0 | mBackend, |
168 | 0 | mFlags, allocFlags); |
169 | 0 | } else { |
170 | 0 | newClient = |
171 | 0 | TextureClient::CreateForDrawing(mSurfaceAllocator, |
172 | 0 | mFormat, mSize, |
173 | 0 | mBackend, |
174 | 0 | mMaxTextureSize, |
175 | 0 | BackendSelector::Content, |
176 | 0 | mFlags, allocFlags); |
177 | 0 | } |
178 | 0 |
|
179 | 0 | if (newClient) { |
180 | 0 | mTextureClients.push(newClient); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | void |
185 | | TextureClientPool::ResetTimers() |
186 | 0 | { |
187 | 0 | // Shrink down if we're beyond our maximum size |
188 | 0 | if (mShrinkTimeoutMsec && |
189 | 0 | mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) { |
190 | 0 | TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this); |
191 | 0 | mShrinkTimer->InitWithNamedFuncCallback( |
192 | 0 | ShrinkCallback, |
193 | 0 | this, |
194 | 0 | mShrinkTimeoutMsec, |
195 | 0 | nsITimer::TYPE_ONE_SHOT, |
196 | 0 | "layers::TextureClientPool::ResetTimers"); |
197 | 0 | } |
198 | 0 |
|
199 | 0 | // Clear pool after a period of inactivity to reduce memory consumption |
200 | 0 | if (mClearTimeoutMsec) { |
201 | 0 | TCP_LOG("TexturePool %p scheduling a clear\n", this); |
202 | 0 | mClearTimer->InitWithNamedFuncCallback( |
203 | 0 | ClearCallback, |
204 | 0 | this, |
205 | 0 | mClearTimeoutMsec, |
206 | 0 | nsITimer::TYPE_ONE_SHOT, |
207 | 0 | "layers::TextureClientPool::ResetTimers"); |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | void |
212 | | TextureClientPool::ReturnTextureClient(TextureClient *aClient) |
213 | 0 | { |
214 | 0 | if (!aClient || mDestroyed) { |
215 | 0 | return; |
216 | 0 | } |
217 | | #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL |
218 | | DebugOnly<bool> ok = TestClientPool("return", aClient, this); |
219 | | MOZ_ASSERT(ok); |
220 | | #endif |
221 | | // Add the client to the pool: |
222 | 0 | MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size()); |
223 | 0 | mOutstandingClients--; |
224 | 0 | mTextureClients.push(aClient); |
225 | 0 | TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n", |
226 | 0 | this, aClient, mTextureClients.size(), mOutstandingClients); |
227 | 0 |
|
228 | 0 | ResetTimers(); |
229 | 0 | } |
230 | | |
231 | | void |
232 | | TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient) |
233 | 0 | { |
234 | 0 | if (!aClient || mDestroyed) { |
235 | 0 | return; |
236 | 0 | } |
237 | 0 | MOZ_ASSERT(aClient->GetReadLock()); |
238 | | #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL |
239 | | DebugOnly<bool> ok = TestClientPool("defer", aClient, this); |
240 | | MOZ_ASSERT(ok); |
241 | | #endif |
242 | | mTextureClientsDeferred.push_back(aClient); |
243 | 0 | TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n", |
244 | 0 | this, aClient, mTextureClientsDeferred.size(), mOutstandingClients); |
245 | 0 |
|
246 | 0 | ResetTimers(); |
247 | 0 | } |
248 | | |
249 | | void |
250 | | TextureClientPool::ShrinkToMaximumSize() |
251 | 0 | { |
252 | 0 | // We're over our desired maximum size, immediately shrink down to the |
253 | 0 | // maximum. |
254 | 0 | // |
255 | 0 | // We cull from the deferred TextureClients first, as we can't reuse those |
256 | 0 | // until they get returned. |
257 | 0 | uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size(); |
258 | 0 |
|
259 | 0 | // If we have > mInitialPoolSize outstanding, then we want to keep around |
260 | 0 | // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize |
261 | 0 | // outstanding, then keep around the entire initial pool size. |
262 | 0 | uint32_t targetUnusedClients; |
263 | 0 | if (mOutstandingClients > mInitialPoolSize) { |
264 | 0 | targetUnusedClients = mPoolUnusedSize; |
265 | 0 | } else { |
266 | 0 | targetUnusedClients = mInitialPoolSize; |
267 | 0 | } |
268 | 0 |
|
269 | 0 | TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n", |
270 | 0 | this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients); |
271 | 0 |
|
272 | 0 | while (totalUnusedTextureClients > targetUnusedClients) { |
273 | 0 | if (!mTextureClientsDeferred.empty()) { |
274 | 0 | mOutstandingClients--; |
275 | 0 | TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n", |
276 | 0 | this, mTextureClientsDeferred.front().get(), |
277 | 0 | mTextureClientsDeferred.size() - 1); |
278 | 0 | mTextureClientsDeferred.pop_front(); |
279 | 0 | } else { |
280 | 0 | TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n", |
281 | 0 | this, mTextureClients.top().get(), mTextureClients.size() - 1); |
282 | 0 | mTextureClients.pop(); |
283 | 0 | } |
284 | 0 | totalUnusedTextureClients--; |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | | void |
289 | | TextureClientPool::ReturnDeferredClients() |
290 | 0 | { |
291 | 0 | if (mTextureClientsDeferred.empty()) { |
292 | 0 | return; |
293 | 0 | } |
294 | 0 | |
295 | 0 | TCP_LOG("TexturePool %p returning %u deferred clients to pool\n", |
296 | 0 | this, mTextureClientsDeferred.size()); |
297 | 0 |
|
298 | 0 | ReturnUnlockedClients(); |
299 | 0 | ShrinkToMaximumSize(); |
300 | 0 | } |
301 | | |
302 | | void |
303 | | TextureClientPool::ReturnUnlockedClients() |
304 | 0 | { |
305 | 0 | for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) { |
306 | 0 | MOZ_ASSERT((*it)->GetReadLock()->AsNonBlockingLock()->GetReadCount() >= 1); |
307 | 0 | // Last count is held by the lock itself. |
308 | 0 | if (!(*it)->IsReadLocked()) { |
309 | 0 | mTextureClients.push(*it); |
310 | 0 | it = mTextureClientsDeferred.erase(it); |
311 | 0 |
|
312 | 0 | MOZ_ASSERT(mOutstandingClients > 0); |
313 | 0 | mOutstandingClients--; |
314 | 0 | } else { |
315 | 0 | it++; |
316 | 0 | } |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | void |
321 | | TextureClientPool::ReportClientLost() |
322 | 0 | { |
323 | 0 | MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size()); |
324 | 0 | mOutstandingClients--; |
325 | 0 | TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n", |
326 | 0 | this, mOutstandingClients); |
327 | 0 | } |
328 | | |
329 | | void |
330 | | TextureClientPool::Clear() |
331 | 0 | { |
332 | 0 | TCP_LOG("TexturePool %p getting cleared\n", this); |
333 | 0 | while (!mTextureClients.empty()) { |
334 | 0 | TCP_LOG("TexturePool %p releasing client %p\n", |
335 | 0 | this, mTextureClients.top().get()); |
336 | 0 | mTextureClients.pop(); |
337 | 0 | } |
338 | 0 | while (!mTextureClientsDeferred.empty()) { |
339 | 0 | MOZ_ASSERT(mOutstandingClients > 0); |
340 | 0 | mOutstandingClients--; |
341 | 0 | TCP_LOG("TexturePool %p releasing deferred client %p\n", |
342 | 0 | this, mTextureClientsDeferred.front().get()); |
343 | 0 | mTextureClientsDeferred.pop_front(); |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | void TextureClientPool::Destroy() |
348 | 0 | { |
349 | 0 | Clear(); |
350 | 0 | mDestroyed = true; |
351 | 0 | mInitialPoolSize = 0; |
352 | 0 | mPoolUnusedSize = 0; |
353 | 0 | } |
354 | | |
355 | | } // namespace layers |
356 | | } // namespace mozilla |