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