fune/gfx/layers/client/TextureClientPool.cpp
Margareta Eliza Balazs b1e7992b82 Backed out 8 changesets (bug 1265824) for bustage in /builds/worker/workspace/build/src/gfx/layers/opengl/CompositorOGL.cpp on a CLOSED TREE
Backed out changeset 1099d6f15f9f (bug 1265824)
Backed out changeset b5ba15b1a70f (bug 1265824)
Backed out changeset 51795de4adaf (bug 1265824)
Backed out changeset be68741ff4ce (bug 1265824)
Backed out changeset 4731dc56702d (bug 1265824)
Backed out changeset 984133e9614b (bug 1265824)
Backed out changeset efce316a4425 (bug 1265824)
Backed out changeset 367abce30668 (bug 1265824)
2018-07-19 09:33:28 +03:00

348 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 = NS_NewTimer();
mClearTimer = NS_NewTimer();
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.empty()) {
AllocateTextureClient();
}
if (mTextureClients.empty()) {
// 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->InitWithNamedFuncCallback(
ShrinkCallback,
this,
mShrinkTimeoutMsec,
nsITimer::TYPE_ONE_SHOT,
"layers::TextureClientPool::ResetTimers");
}
// Clear pool after a period of inactivity to reduce memory consumption
if (mClearTimeoutMsec) {
TCP_LOG("TexturePool %p scheduling a clear\n", this);
mClearTimer->InitWithNamedFuncCallback(
ClearCallback,
this,
mClearTimeoutMsec,
nsITimer::TYPE_ONE_SHOT,
"layers::TextureClientPool::ResetTimers");
}
}
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.empty()) {
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()->AsNonBlockingLock()->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