diff options
Diffstat (limited to 'gfx/layers/client/TextureClientPool.cpp')
-rw-r--r-- | gfx/layers/client/TextureClientPool.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/gfx/layers/client/TextureClientPool.cpp b/gfx/layers/client/TextureClientPool.cpp new file mode 100644 index 000000000..c556a791e --- /dev/null +++ b/gfx/layers/client/TextureClientPool.cpp @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TextureClientPool.h" +#include "CompositableClient.h" +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/TextureForwarder.h" +#include "mozilla/layers/TiledContentClient.h" + +#include "gfxPrefs.h" + +#include "nsComponentManagerUtils.h" + +#define TCP_LOG(...) +//#define TCP_LOG(...) printf_stderr(__VA_ARGS__); + +namespace mozilla { +namespace layers { + +// We want to shrink to our maximum size of N unused tiles +// after a timeout to allow for short-term budget requirements +static void +ShrinkCallback(nsITimer *aTimer, void *aClosure) +{ + static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize(); +} + +// After a certain amount of inactivity, let's clear the pool so that +// we don't hold onto tiles needlessly. In general, allocations are +// cheap enough that re-allocating isn't an issue unless we're allocating +// at an inopportune time (e.g. mid-animation). +static void +ClearCallback(nsITimer *aTimer, void *aClosure) +{ + static_cast<TextureClientPool*>(aClosure)->Clear(); +} + +TextureClientPool::TextureClientPool(LayersBackend aLayersBackend, + int32_t aMaxTextureSize, + gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + TextureFlags aFlags, + uint32_t aShrinkTimeoutMsec, + uint32_t aClearTimeoutMsec, + uint32_t aInitialPoolSize, + uint32_t aPoolUnusedSize, + TextureForwarder* aAllocator) + : mBackend(aLayersBackend) + , mMaxTextureSize(aMaxTextureSize) + , mFormat(aFormat) + , mSize(aSize) + , mFlags(aFlags) + , mShrinkTimeoutMsec(aShrinkTimeoutMsec) + , mClearTimeoutMsec(aClearTimeoutMsec) + , mInitialPoolSize(aInitialPoolSize) + , mPoolUnusedSize(aPoolUnusedSize) + , mOutstandingClients(0) + , mSurfaceAllocator(aAllocator) + , mDestroyed(false) +{ + TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n", + this, mInitialPoolSize); + mShrinkTimer = do_CreateInstance("@mozilla.org/timer;1"); + mClearTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (aFormat == gfx::SurfaceFormat::UNKNOWN) { + gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format"; + } +} + +TextureClientPool::~TextureClientPool() +{ + mShrinkTimer->Cancel(); + mClearTimer->Cancel(); +} + +#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL +static bool TestClientPool(const char* what, + TextureClient* aClient, + TextureClientPool* aPool) +{ + if (!aClient || !aPool) { + return false; + } + + TextureClientPool* actual = aClient->mPoolTracker; + bool ok = (actual == aPool); + if (ok) { + ok = (aClient->GetFormat() == aPool->GetFormat()); + } + + if (!ok) { + if (actual) { + gfxCriticalError() << "Pool error(" << what << "): " + << aPool << "-" << aPool->GetFormat() << ", " + << actual << "-" << actual->GetFormat() << ", " + << aClient->GetFormat(); + MOZ_CRASH("GFX: Crashing with actual"); + } else { + gfxCriticalError() << "Pool error(" << what << "): " + << aPool << "-" << aPool->GetFormat() << ", nullptr, " + << aClient->GetFormat(); + MOZ_CRASH("GFX: Crashing without actual"); + } + } + return ok; +} +#endif + +already_AddRefed<TextureClient> +TextureClientPool::GetTextureClient() +{ + // Try to fetch a client from the pool + RefPtr<TextureClient> textureClient; + + // We initially allocate mInitialPoolSize for our pool. If we run + // out of TextureClients, we allocate additional TextureClients to try and keep around + // mPoolUnusedSize + if (!mTextureClients.size()) { + AllocateTextureClient(); + } + + if (!mTextureClients.size()) { + // All our allocations failed, return nullptr + return nullptr; + } + + mOutstandingClients++; + textureClient = mTextureClients.top(); + mTextureClients.pop(); +#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL + if (textureClient) { + textureClient->mPoolTracker = this; + } + DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this); + MOZ_ASSERT(ok); +#endif + TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n", + this, textureClient.get(), mTextureClients.size(), mOutstandingClients); + + return textureClient.forget(); +} + +void +TextureClientPool::AllocateTextureClient() +{ + TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n", + this, mOutstandingClients); + + RefPtr<TextureClient> newClient; + if (gfxPrefs::ForceShmemTiles()) { + // gfx::BackendType::NONE means use the content backend + newClient = + TextureClient::CreateForRawBufferAccess(mSurfaceAllocator, + mFormat, mSize, + gfx::BackendType::NONE, + mBackend, + mFlags, ALLOC_DEFAULT); + } else { + newClient = + TextureClient::CreateForDrawing(mSurfaceAllocator, + mFormat, mSize, + mBackend, + mMaxTextureSize, + BackendSelector::Content, + mFlags); + } + + if (newClient) { + mTextureClients.push(newClient); + } +} + +void +TextureClientPool::ResetTimers() +{ + // Shrink down if we're beyond our maximum size + if (mShrinkTimeoutMsec && + mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) { + TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this); + mShrinkTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec, + nsITimer::TYPE_ONE_SHOT); + } + + // Clear pool after a period of inactivity to reduce memory consumption + if (mClearTimeoutMsec) { + TCP_LOG("TexturePool %p scheduling a clear\n", this); + mClearTimer->InitWithFuncCallback(ClearCallback, this, mClearTimeoutMsec, + nsITimer::TYPE_ONE_SHOT); + } +} + +void +TextureClientPool::ReturnTextureClient(TextureClient *aClient) +{ + if (!aClient || mDestroyed) { + return; + } +#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL + DebugOnly<bool> ok = TestClientPool("return", aClient, this); + MOZ_ASSERT(ok); +#endif + // Add the client to the pool: + MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size()); + mOutstandingClients--; + mTextureClients.push(aClient); + TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n", + this, aClient, mTextureClients.size(), mOutstandingClients); + + ResetTimers(); +} + +void +TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient) +{ + if (!aClient || mDestroyed) { + return; + } + MOZ_ASSERT(aClient->GetReadLock()); +#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL + DebugOnly<bool> ok = TestClientPool("defer", aClient, this); + MOZ_ASSERT(ok); +#endif + mTextureClientsDeferred.push_back(aClient); + TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n", + this, aClient, mTextureClientsDeferred.size(), mOutstandingClients); + + ResetTimers(); +} + +void +TextureClientPool::ShrinkToMaximumSize() +{ + // We're over our desired maximum size, immediately shrink down to the + // maximum. + // + // We cull from the deferred TextureClients first, as we can't reuse those + // until they get returned. + uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size(); + + // If we have > mInitialPoolSize outstanding, then we want to keep around + // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize + // outstanding, then keep around the entire initial pool size. + uint32_t targetUnusedClients; + if (mOutstandingClients > mInitialPoolSize) { + targetUnusedClients = mPoolUnusedSize; + } else { + targetUnusedClients = mInitialPoolSize; + } + + TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n", + this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients); + + while (totalUnusedTextureClients > targetUnusedClients) { + if (mTextureClientsDeferred.size()) { + mOutstandingClients--; + TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n", + this, mTextureClientsDeferred.front().get(), + mTextureClientsDeferred.size() - 1); + mTextureClientsDeferred.pop_front(); + } else { + TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n", + this, mTextureClients.top().get(), mTextureClients.size() - 1); + mTextureClients.pop(); + } + totalUnusedTextureClients--; + } +} + +void +TextureClientPool::ReturnDeferredClients() +{ + if (mTextureClientsDeferred.empty()) { + return; + } + + TCP_LOG("TexturePool %p returning %u deferred clients to pool\n", + this, mTextureClientsDeferred.size()); + + ReturnUnlockedClients(); + ShrinkToMaximumSize(); +} + +void +TextureClientPool::ReturnUnlockedClients() +{ + for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) { + MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1); + // Last count is held by the lock itself. + if (!(*it)->IsReadLocked()) { + mTextureClients.push(*it); + it = mTextureClientsDeferred.erase(it); + + MOZ_ASSERT(mOutstandingClients > 0); + mOutstandingClients--; + } else { + it++; + } + } +} + +void +TextureClientPool::ReportClientLost() +{ + MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size()); + mOutstandingClients--; + TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n", + this, mOutstandingClients); +} + +void +TextureClientPool::Clear() +{ + TCP_LOG("TexturePool %p getting cleared\n", this); + while (!mTextureClients.empty()) { + TCP_LOG("TexturePool %p releasing client %p\n", + this, mTextureClients.top().get()); + mTextureClients.pop(); + } + while (!mTextureClientsDeferred.empty()) { + MOZ_ASSERT(mOutstandingClients > 0); + mOutstandingClients--; + TCP_LOG("TexturePool %p releasing deferred client %p\n", + this, mTextureClientsDeferred.front().get()); + mTextureClientsDeferred.pop_front(); + } +} + +void TextureClientPool::Destroy() +{ + Clear(); + mDestroyed = true; + mInitialPoolSize = 0; + mPoolUnusedSize = 0; +} + +} // namespace layers +} // namespace mozilla |