b=979489; Implement SimpleTiledContentClient and friends; r=jrmuizel

This commit is contained in:
Vladimir Vukicevic 2014-03-10 14:34:57 -04:00
Родитель 241dfbe366
Коммит b7be770266
20 изменённых файлов: 1069 добавлений и 22 удалений

Просмотреть файл

@ -665,7 +665,8 @@ let settingsToObserve = {
prefName: 'dom.browser_frames.useAsyncPanZoom',
defaultValue: false
},
'layers.enable-tiles': false,
'layers.enable-tiles': true,
'layers.simple-tiles': false,
'layers.progressive-paint': false,
'layers.draw-tile-borders': false,
'layers.dump': false,

Просмотреть файл

@ -7,6 +7,7 @@
#define MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
#include "mozilla/RefPtr.h"
#include "mozilla/NullPtr.h"
namespace mozilla {
@ -15,7 +16,8 @@ class AtomicRefCountedWithFinalize
{
protected:
AtomicRefCountedWithFinalize()
: mRefCount(0)
: mRecycleCallback(nullptr)
, mRefCount(0)
{}
~AtomicRefCountedWithFinalize() {}
@ -28,17 +30,38 @@ class AtomicRefCountedWithFinalize
void Release() {
MOZ_ASSERT(mRefCount > 0);
if (0 == --mRefCount) {
int currCount = --mRefCount;
if (0 == currCount) {
// Recycle listeners must call ClearRecycleCallback
// before releasing their strong reference.
MOZ_ASSERT(mRecycleCallback == nullptr);
#ifdef DEBUG
mRefCount = detail::DEAD;
#endif
T* derived = static_cast<T*>(this);
derived->Finalize();
delete derived;
} else if (1 == currCount && mRecycleCallback) {
T* derived = static_cast<T*>(this);
mRecycleCallback(derived, mClosure);
}
}
typedef void (*RecycleCallback)(T* aObject, void* aClosure);
/**
* Set a callback responsible for recycling this object
* before it is finalized.
*/
void SetRecycleCallback(RecycleCallback aCallback, void* aClosure)
{
mRecycleCallback = aCallback;
mClosure = aClosure;
}
void ClearRecycleCallback() { SetRecycleCallback(nullptr, nullptr); }
private:
RecycleCallback mRecycleCallback;
void *mClosure;
Atomic<int> mRefCount;
};

Просмотреть файл

@ -55,6 +55,8 @@ const TextureFlags TEXTURE_ON_WHITE = 1 << 13;
const TextureFlags TEXTURE_ON_BLACK = 1 << 14;
// A texture host that supports tiling
const TextureFlags TEXTURE_TILE = 1 << 15;
// A texture should be recycled when no longer in used
const TextureFlags TEXTURE_RECYCLE = 1 << 16;
// Texture contents should be initialized
// from the previous texture.
const TextureFlags TEXTURE_COPY_PREVIOUS = 1 << 24;
@ -164,7 +166,9 @@ enum CompositableType
BUFFER_CONTENT_DIRECT, // thebes layer interface, double buffering
BUFFER_CONTENT_INC, // thebes layer interface, only sends incremental
// updates to a texture on the compositor side.
// somewhere in the middle
BUFFER_TILED, // tiled thebes layer
BUFFER_SIMPLE_TILED,
// the new compositable types
COMPOSITABLE_IMAGE, // image with single buffering
COMPOSITABLE_CONTENT_SINGLE, // thebes layer interface, single buffering

Просмотреть файл

@ -21,6 +21,7 @@
#include "mozilla/layers/PLayerChild.h" // for PLayerChild
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/TextureClientPool.h" // for TextureClientPool
#include "mozilla/layers/SimpleTextureClientPool.h" // for SimpleTextureClientPool
#include "nsAString.h"
#include "nsIWidget.h" // for nsIWidget
#include "nsTArray.h" // for AutoInfallibleTArray
@ -474,6 +475,21 @@ ClientLayerManager::GetTexturePool(SurfaceFormat aFormat)
return texturePoolMember->mTexturePool;
}
SimpleTextureClientPool*
ClientLayerManager::GetSimpleTileTexturePool(SurfaceFormat aFormat)
{
int index = (int) aFormat;
mSimpleTilePools.EnsureLengthAtLeast(index+1);
if (mSimpleTilePools[index].get() == nullptr) {
mSimpleTilePools[index] = new SimpleTextureClientPool(aFormat, IntSize(TILEDLAYERBUFFER_TILE_SIZE,
TILEDLAYERBUFFER_TILE_SIZE),
mForwarder);
}
return mSimpleTilePools[index];
}
void
ClientLayerManager::ClearCachedResources(Layer* aSubtree)
{

Просмотреть файл

@ -34,6 +34,7 @@ class CompositorChild;
class ImageLayer;
class PLayerChild;
class TextureClientPool;
class SimpleTextureClientPool;
class TextureClientPoolMember
: public LinkedListElement<TextureClientPoolMember> {
@ -108,6 +109,7 @@ public:
virtual void SetIsFirstPaint() MOZ_OVERRIDE;
TextureClientPool *GetTexturePool(gfx::SurfaceFormat aFormat);
SimpleTextureClientPool *GetSimpleTileTexturePool(gfx::SurfaceFormat aFormat);
// Drop cached resources and ask our shadow manager to do the same,
// if we have one.
@ -228,6 +230,9 @@ private:
RefPtr<ShadowLayerForwarder> mForwarder;
LinkedList<TextureClientPoolMember> mTexturePools;
// indexed by gfx::SurfaceFormat
nsTArray<RefPtr<SimpleTextureClientPool> > mSimpleTilePools;
};
class ClientLayer : public ShadowableLayer

Просмотреть файл

@ -5,6 +5,7 @@
#include "ClientThebesLayer.h"
#include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer
#include "SimpleTiledContentClient.h"
#include <stdint.h> // for uint32_t
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "client/ClientLayerManager.h" // for ClientLayerManager, etc
@ -176,10 +177,17 @@ ClientLayerManager::CreateThebesLayerWithHint(ThebesLayerCreationHint aHint)
(AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_OPENGL ||
AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D9 ||
AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D11)) {
nsRefPtr<ClientTiledThebesLayer> layer =
new ClientTiledThebesLayer(this);
CREATE_SHADOW(Thebes);
return layer.forget();
if (gfxPrefs::LayersUseSimpleTiles()) {
nsRefPtr<SimpleClientTiledThebesLayer> layer =
new SimpleClientTiledThebesLayer(this);
CREATE_SHADOW(Thebes);
return layer.forget();
} else {
nsRefPtr<ClientTiledThebesLayer> layer =
new ClientTiledThebesLayer(this);
CREATE_SHADOW(Thebes);
return layer.forget();
}
} else
{
nsRefPtr<ClientThebesLayer> layer =

Просмотреть файл

@ -0,0 +1,137 @@
/* -*- 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 "SimpleTextureClientPool.h"
#include "CompositableClient.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "gfxPrefs.h"
#include "nsComponentManagerUtils.h"
#if 0
#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
#else
#define RECYCLE_LOG(...) do { } while (0)
#endif
namespace mozilla {
namespace layers {
using gfx::SurfaceFormat;
/* static */ void
SimpleTextureClientPool::ShrinkCallback(nsITimer *aTimer, void *aClosure)
{
static_cast<SimpleTextureClientPool*>(aClosure)->ShrinkToMinimumSize();
}
/* static */ void
SimpleTextureClientPool::RecycleCallback(TextureClient* aClient, void* aClosure)
{
SimpleTextureClientPool* pool =
static_cast<SimpleTextureClientPool*>(aClosure);
aClient->ClearRecycleCallback();
pool->ReturnTextureClient(aClient);
}
/* static */ void
SimpleTextureClientPool::WaitForCompositorRecycleCallback(TextureClient* aClient, void* aClosure)
{
// This will grab a reference that will be released once the compositor
// acknowledges the remote recycle. Once it is received the object
// will be fully recycled.
aClient->WaitForCompositorRecycle();
aClient->SetRecycleCallback(SimpleTextureClientPool::RecycleCallback, aClosure);
}
SimpleTextureClientPool::SimpleTextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
ISurfaceAllocator *aAllocator)
: mFormat(aFormat)
, mSize(aSize)
, mSurfaceAllocator(aAllocator)
{
mTimer = do_CreateInstance("@mozilla.org/timer;1");
}
TemporaryRef<TextureClient>
SimpleTextureClientPool::GetTextureClient(bool aAutoRecycle)
{
// Try to fetch a client from the pool
RefPtr<TextureClient> textureClient;
if (mAvailableTextureClients.size()) {
textureClient = mAvailableTextureClients.top();
mAvailableTextureClients.pop();
RECYCLE_LOG("%s Skip allocate (%i left), returning %p\n", (mFormat == SurfaceFormat::B8G8R8A8?"poolA":"poolX"), mAvailableTextureClients.size(), textureClient.get());
} else {
// No unused clients in the pool, create one
if (gfxPrefs::ForceShmemTiles()) {
textureClient = TextureClient::CreateBufferTextureClient(mSurfaceAllocator, mFormat, TEXTURE_IMMEDIATE_UPLOAD | TEXTURE_RECYCLE);
} else {
textureClient = TextureClient::CreateTextureClientForDrawing(mSurfaceAllocator, mFormat, TEXTURE_FLAGS_DEFAULT | TEXTURE_RECYCLE);
}
if (!textureClient->AsTextureClientDrawTarget()->AllocateForSurface(mSize, ALLOC_DEFAULT)) {
NS_WARNING("TextureClient::AllocateForSurface failed!");
}
RECYCLE_LOG("%s Must allocate (0 left), returning %p\n", (mFormat == SurfaceFormat::B8G8R8A8?"poolA":"poolX"), textureClient.get());
}
if (aAutoRecycle) {
mOutstandingTextureClients.push_back(textureClient);
textureClient->SetRecycleCallback(SimpleTextureClientPool::WaitForCompositorRecycleCallback, this);
}
return textureClient;
}
void
SimpleTextureClientPool::ReturnTextureClient(TextureClient *aClient)
{
if (!aClient) {
return;
}
// If we haven't hit our max cached client limit, add this one
if (mAvailableTextureClients.size() < sMaxTextureClients) {
mAvailableTextureClients.push(aClient);
RECYCLE_LOG("%s recycled %p (have %d)\n", (mFormat == SurfaceFormat::B8G8R8A8?"poolA":"poolX"), aClient, mAvailableTextureClients.size());
} else {
RECYCLE_LOG("%s did not recycle %p (have %d)\n", (mFormat == SurfaceFormat::B8G8R8A8?"poolA":"poolX"), aClient, mAvailableTextureClients.size());
}
// Kick off the pool shrinking timer if there are still more unused texture
// clients than our desired minimum cache size.
if (mAvailableTextureClients.size() > sMinCacheSize) {
mTimer->InitWithFuncCallback(SimpleTextureClientPool::ShrinkCallback, this, sShrinkTimeout,
nsITimer::TYPE_ONE_SHOT);
}
mOutstandingTextureClients.remove(aClient);
}
void
SimpleTextureClientPool::ShrinkToMinimumSize()
{
RECYCLE_LOG("%s ShrinkToMinimumSize, removing %d clients", (mFormat == SurfaceFormat::B8G8R8A8?"poolA":"poolX"), mAvailableTextureClients.size() > sMinCacheSize ? mAvailableTextureClients.size() - sMinCacheSize : 0);
mTimer->Cancel();
while (mAvailableTextureClients.size() > sMinCacheSize) {
mAvailableTextureClients.pop();
}
}
void
SimpleTextureClientPool::Clear()
{
while (!mAvailableTextureClients.empty()) {
mAvailableTextureClients.pop();
}
}
}
}

Просмотреть файл

@ -0,0 +1,91 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_SIMPLETEXTURECLIENTPOOL_H
#define MOZILLA_GFX_SIMPLETEXTURECLIENTPOOL_H
#include "mozilla/gfx/Types.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/RefPtr.h"
#include "TextureClient.h"
#include "nsITimer.h"
#include <stack>
#include <list>
namespace mozilla {
namespace layers {
class ISurfaceAllocator;
class SimpleTextureClientPool : public RefCounted<SimpleTextureClientPool>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(SimpleTextureClientPool)
SimpleTextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
ISurfaceAllocator *aAllocator);
~SimpleTextureClientPool()
{
for (auto it = mOutstandingTextureClients.begin(); it != mOutstandingTextureClients.end(); ++it) {
(*it)->ClearRecycleCallback();
}
}
/**
* If a TextureClient is AutoRecycled, when the last reference is
* released this object will be automatically return to the pool as
* soon as the compositor informs us it is done with it.
*/
TemporaryRef<TextureClient> GetTextureClient(bool aAutoRecycle = false);
TemporaryRef<TextureClient> GetTextureClientWithAutoRecycle() { return GetTextureClient(true); }
void ReturnTextureClient(TextureClient *aClient);
void ShrinkToMinimumSize();
void Clear();
private:
// The time in milliseconds before the pool will be shrunk to the minimum
// size after returning a client.
static const uint32_t sShrinkTimeout = 3000;
// The minimum size of the pool (the number of tiles that will be kept after
// shrinking).
static const uint32_t sMinCacheSize = 16;
// This is the number of cached texture clients we don't want to exceed, even
// temporarily (pre-shrink)
static const uint32_t sMaxTextureClients = 50;
static void ShrinkCallback(nsITimer *aTimer, void *aClosure);
static void RecycleCallback(TextureClient* aClient, void* aClosure);
static void WaitForCompositorRecycleCallback(TextureClient* aClient, void* aClosure);
gfx::SurfaceFormat mFormat;
gfx::IntSize mSize;
// We use a std::stack and make sure to use it the following way:
// new (available to be used) elements are push()'d to the front
// requests are served from the front via pop()
// -- the thinking is that recently-used elements are most likely
// to be in any data cache, so we can get some wins there
// -- the converse though is that if there is some GPU locking going on
// the most recently used elements may also have the most contention;
// if we see that, then we should use push_back() to add new elements
// when we shrink this list, we use pop(), but should use pop_back() to
// nuke the oldest.
// We may need to switch to a std::deque
std::stack<RefPtr<TextureClient> > mAvailableTextureClients;
std::list<RefPtr<TextureClient> > mOutstandingTextureClients;
nsRefPtr<nsITimer> mTimer;
RefPtr<ISurfaceAllocator> mSurfaceAllocator;
};
}
}
#endif /* MOZILLA_GFX_SIMPLETEXTURECLIENTPOOL_H */

Просмотреть файл

@ -0,0 +1,472 @@
/* -*- 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 "mozilla/layers/SimpleTiledContentClient.h"
#include <math.h> // for ceil, ceilf, floor
#include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "Units.h" // for ScreenIntRect, CSSPoint, etc
#include "UnitTransforms.h" // for TransformTo
#include "ClientLayerManager.h" // for ClientLayerManager
#include "CompositorChild.h" // for CompositorChild
#include "gfxContext.h" // for gfxContext, etc
#include "gfxPlatform.h" // for gfxPlatform
#include "gfxRect.h" // for gfxRect
#include "mozilla/Attributes.h" // for MOZ_THIS_IN_INITIALIZER_LIST
#include "mozilla/MathAlgorithms.h" // for Abs
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/Rect.h" // for Rect
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
#include "SimpleTextureClientPool.h"
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
#include "nsSize.h" // for nsIntSize
#include "gfxReusableSharedImageSurfaceWrapper.h"
#include "nsMathUtils.h" // for NS_roundf
#include "gfx2DGlue.h"
#define ALOG(...) __android_log_print(ANDROID_LOG_INFO, "SimpleTiles", __VA_ARGS__)
using namespace mozilla::gfx;
namespace mozilla {
namespace layers {
void
SimpleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
mCallback = aCallback;
mCallbackData = aCallbackData;
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
long start = PR_IntervalNow();
#endif
// If this region is empty XMost() - 1 will give us a negative value.
NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n");
PROFILER_LABEL("SimpleTiledLayerBuffer", "PaintThebesUpdate");
Update(aNewValidRegion, aPaintRegion);
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (PR_IntervalNow() - start > 10) {
const nsIntRect bounds = aPaintRegion.GetBounds();
printf_stderr("Time to tile [%i, %i, %i, %i] -> %i\n", bounds.x, bounds.y, bounds.width, bounds.height, PR_IntervalNow() - start);
}
#endif
mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface();
mCallback = nullptr;
mCallbackData = nullptr;
}
SimpleTiledLayerTile
SimpleTiledLayerBuffer::ValidateTile(SimpleTiledLayerTile aTile,
const nsIntPoint& aTileOrigin,
const nsIntRegion& aDirtyRegion)
{
PROFILER_LABEL("SimpleTiledLayerBuffer", "ValidateTile");
static gfx::IntSize kTileSize(TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE);
gfx::SurfaceFormat tileFormat = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType());
// if this is true, we're using a separate buffer to do our drawing first
bool doBufferedDrawing = true;
bool fullPaint = false;
RefPtr<TextureClient> textureClient = mManager->GetSimpleTileTexturePool(tileFormat)->GetTextureClientWithAutoRecycle();
if (!textureClient) {
NS_WARNING("TextureClient allocation failed");
return SimpleTiledLayerTile();
}
if (!textureClient->Lock(OPEN_WRITE)) {
NS_WARNING("TextureClient lock failed");
return SimpleTiledLayerTile();
}
TextureClientSurface *textureClientSurf = textureClient->AsTextureClientSurface();
if (!textureClientSurf) {
doBufferedDrawing = false;
}
RefPtr<DrawTarget> drawTarget;
nsRefPtr<gfxImageSurface> clientAsImageSurface;
unsigned char *bufferData = nullptr;
// these are set/updated differently based on doBufferedDrawing
nsIntRect drawBounds;
nsIntRegion drawRegion;
nsIntRegion invalidateRegion;
if (doBufferedDrawing) {
// try to obtain the TextureClient as an ImageSurface, so that we can
// access the pixels directly
nsRefPtr<gfxASurface> asurf = textureClientSurf->GetAsSurface();
clientAsImageSurface = asurf ? asurf->GetAsImageSurface() : nullptr;
if (clientAsImageSurface) {
int32_t bufferStride = clientAsImageSurface->Stride();
if (!aTile.mCachedBuffer) {
aTile.mCachedBuffer = SharedBuffer::Create(clientAsImageSurface->GetDataSize());
fullPaint = true;
}
bufferData = (unsigned char*) aTile.mCachedBuffer->Data();
drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(bufferData,
kTileSize,
bufferStride,
tileFormat);
if (fullPaint) {
drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileLength(), GetScaledTileLength());
drawRegion = nsIntRegion(drawBounds);
} else {
drawBounds = aDirtyRegion.GetBounds();
drawRegion = nsIntRegion(drawBounds);
if (GetContentType() == gfxContentType::COLOR_ALPHA)
drawTarget->ClearRect(Rect(drawBounds.x - aTileOrigin.x, drawBounds.y - aTileOrigin.y,
drawBounds.width, drawBounds.height));
}
} else {
// failed to obtain the client as an ImageSurface
doBufferedDrawing = false;
}
}
// this might get set above if we couldn't extract out a buffer
if (!doBufferedDrawing) {
drawTarget = textureClient->AsTextureClientDrawTarget()->GetAsDrawTarget();
fullPaint = true;
drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileLength(), GetScaledTileLength());
drawRegion = nsIntRegion(drawBounds);
if (GetContentType() == gfxContentType::COLOR_ALPHA)
drawTarget->ClearRect(Rect(0, 0, drawBounds.width, drawBounds.height));
}
// do the drawing
RefPtr<gfxContext> ctxt = new gfxContext(drawTarget);
ctxt->Scale(mResolution, mResolution);
ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y));
mCallback(mThebesLayer, ctxt,
drawRegion,
fullPaint ? DrawRegionClip::CLIP_NONE : DrawRegionClip::DRAW_SNAPPED, // XXX DRAW or DRAW_SNAPPED?
invalidateRegion,
mCallbackData);
ctxt = nullptr;
drawTarget = nullptr;
if (doBufferedDrawing) {
memcpy(clientAsImageSurface->Data(), bufferData, clientAsImageSurface->GetDataSize());
clientAsImageSurface = nullptr;
bufferData = nullptr;
}
textureClient->Unlock();
if (!mCompositableClient->AddTextureClient(textureClient)) {
NS_WARNING("Failed to add tile TextureClient [simple]");
return SimpleTiledLayerTile();
}
// aTile.mCachedBuffer was set earlier
aTile.mTileBuffer = textureClient;
aTile.mManager = mManager;
aTile.mLastUpdate = TimeStamp::Now();
return aTile;
}
SurfaceDescriptorTiles
SimpleTiledLayerBuffer::GetSurfaceDescriptorTiles()
{
InfallibleTArray<TileDescriptor> tiles;
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
tiles.AppendElement(mRetainedTiles[i].GetTileDescriptor());
}
return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
tiles, mRetainedWidth, mRetainedHeight,
mResolution);
}
bool
SimpleTiledLayerBuffer::HasFormatChanged() const
{
return mThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque;
}
gfxContentType
SimpleTiledLayerBuffer::GetContentType() const
{
if (mThebesLayer->CanUseOpaqueSurface())
return gfxContentType::COLOR;
return gfxContentType::COLOR_ALPHA;
}
SimpleTiledContentClient::SimpleTiledContentClient(SimpleClientTiledThebesLayer* aThebesLayer,
ClientLayerManager* aManager)
: CompositableClient(aManager->AsShadowForwarder())
, mTiledBuffer(aThebesLayer, MOZ_THIS_IN_INITIALIZER_LIST(), aManager)
{
MOZ_COUNT_CTOR(SimpleTiledContentClient);
}
SimpleTiledContentClient::~SimpleTiledContentClient()
{
MOZ_COUNT_DTOR(SimpleTiledContentClient);
mTiledBuffer.Release();
}
void
SimpleTiledContentClient::UseTiledLayerBuffer()
{
mForwarder->UseTiledLayerBuffer(this, mTiledBuffer.GetSurfaceDescriptorTiles());
mTiledBuffer.ClearPaintedRegion();
}
SimpleClientTiledThebesLayer::SimpleClientTiledThebesLayer(ClientLayerManager* aManager)
: ThebesLayer(aManager,
static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()))
, mContentClient()
{
MOZ_COUNT_CTOR(SimpleClientTiledThebesLayer);
mPaintData.mLastScrollOffset = ScreenPoint(0, 0);
mPaintData.mFirstPaint = true;
}
SimpleClientTiledThebesLayer::~SimpleClientTiledThebesLayer()
{
MOZ_COUNT_DTOR(SimpleClientTiledThebesLayer);
}
void
SimpleClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ThebesLayerAttributes(GetValidRegion());
}
static LayoutDeviceRect
ApplyParentLayerToLayoutTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect)
{
return TransformTo<LayoutDevicePixel>(aTransform, aParentLayerRect);
}
void
SimpleClientTiledThebesLayer::BeginPaint()
{
if (ClientManager()->IsRepeatTransaction()) {
return;
}
mPaintData.mLowPrecisionPaintCount = 0;
mPaintData.mPaintFinished = false;
// Get the metrics of the nearest scroll container.
ContainerLayer* scrollParent = nullptr;
for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
const FrameMetrics& metrics = parent->GetFrameMetrics();
if (metrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) {
scrollParent = parent;
break;
}
}
if (!scrollParent) {
// XXX I don't think this can happen, but if it does, warn and set the
// composition bounds to empty so that progressive updates are disabled.
NS_WARNING("Tiled Thebes layer with no scrollable container parent");
mPaintData.mCompositionBounds.SetEmpty();
return;
}
const FrameMetrics& metrics = scrollParent->GetFrameMetrics();
// Calculate the transform required to convert screen space into transformed
// layout device space.
gfx::Matrix4x4 effectiveTransform = GetEffectiveTransform();
for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
if (parent->UseIntermediateSurface()) {
effectiveTransform = effectiveTransform * parent->GetEffectiveTransform();
}
}
gfx3DMatrix layoutToParentLayer;
gfx::To3DMatrix(effectiveTransform, layoutToParentLayer);
layoutToParentLayer.ScalePost(metrics.GetParentResolution().scale,
metrics.GetParentResolution().scale,
1.f);
mPaintData.mTransformParentLayerToLayout = layoutToParentLayer.Inverse();
// Compute the critical display port in layer space.
mPaintData.mLayoutCriticalDisplayPort.SetEmpty();
if (!metrics.mCriticalDisplayPort.IsEmpty()) {
// Convert the display port to screen space first so that we can transform
// it into layout device space.
const ParentLayerRect& criticalDisplayPort = metrics.mCriticalDisplayPort
* metrics.mDevPixelsPerCSSPixel
* metrics.GetParentResolution();
LayoutDeviceRect transformedCriticalDisplayPort =
ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout, criticalDisplayPort);
mPaintData.mLayoutCriticalDisplayPort =
LayoutDeviceIntRect::ToUntyped(RoundedOut(transformedCriticalDisplayPort));
}
// Calculate the frame resolution. Because this is Gecko-side, before any
// async transforms have occurred, we can use mZoom for this.
mPaintData.mResolution = metrics.mZoom;
// Calculate the scroll offset since the last transaction, and the
// composition bounds.
mPaintData.mCompositionBounds.SetEmpty();
mPaintData.mScrollOffset.MoveTo(0, 0);
Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer();
if (primaryScrollable) {
const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
mPaintData.mScrollOffset = metrics.mScrollOffset * metrics.mZoom;
mPaintData.mCompositionBounds =
ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout,
ParentLayerRect(metrics.mCompositionBounds));
}
}
void
SimpleClientTiledThebesLayer::EndPaint(bool aFinish)
{
if (!aFinish && !mPaintData.mPaintFinished) {
return;
}
mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
mPaintData.mPaintFinished = true;
mPaintData.mFirstPaint = false;
}
void
SimpleClientTiledThebesLayer::RenderLayer()
{
LayerManager::DrawThebesLayerCallback callback =
ClientManager()->GetThebesLayerCallback();
void *data = ClientManager()->GetThebesLayerCallbackData();
if (!callback) {
ClientManager()->SetTransactionIncomplete();
return;
}
// First time? Create a content client.
if (!mContentClient) {
mContentClient = new SimpleTiledContentClient(this, ClientManager());
mContentClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
MOZ_ASSERT(mContentClient->GetForwarder());
}
// If the format changed, nothing is valid
if (mContentClient->mTiledBuffer.HasFormatChanged()) {
mValidRegion = nsIntRegion();
}
nsIntRegion invalidRegion = mVisibleRegion;
invalidRegion.Sub(invalidRegion, mValidRegion);
if (invalidRegion.IsEmpty()) {
EndPaint(true);
return;
}
const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics();
nsIntRegion wantToPaintRegion = mVisibleRegion;
// Only paint the mask layer on the first transaction.
if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) {
ToClientLayer(GetMaskLayer())->RenderLayer();
}
// Fast path for no progressive updates, no low-precision updates and no
// critical display-port set, or no display-port set.
if (parentMetrics.mCriticalDisplayPort.IsEmpty() ||
parentMetrics.mDisplayPort.IsEmpty())
{
mValidRegion = wantToPaintRegion;
NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer");
mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
callback, data);
ClientManager()->Hold(this);
mContentClient->UseTiledLayerBuffer();
return;
}
// Calculate everything we need to perform the paint.
BeginPaint();
if (mPaintData.mPaintFinished) {
return;
}
// Make sure that tiles that fall outside of the visible region are
// discarded on the first update.
if (!ClientManager()->IsRepeatTransaction()) {
mValidRegion.And(mValidRegion, wantToPaintRegion);
if (!mPaintData.mLayoutCriticalDisplayPort.IsEmpty()) {
// Make sure that tiles that fall outside of the critical displayport are
// discarded on the first update.
mValidRegion.And(mValidRegion, mPaintData.mLayoutCriticalDisplayPort);
}
}
nsIntRegion lowPrecisionInvalidRegion;
if (!mPaintData.mLayoutCriticalDisplayPort.IsEmpty()) {
// Clip the invalid region to the critical display-port
invalidRegion.And(invalidRegion, mPaintData.mLayoutCriticalDisplayPort);
if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) {
EndPaint(true);
return;
}
}
if (!invalidRegion.IsEmpty()) {
mValidRegion = wantToPaintRegion;
if (!mPaintData.mLayoutCriticalDisplayPort.IsEmpty()) {
mValidRegion.And(mValidRegion, mPaintData.mLayoutCriticalDisplayPort);
}
mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
callback, data);
ClientManager()->Hold(this);
mContentClient->UseTiledLayerBuffer();
EndPaint(false);
return;
}
EndPaint(false);
}
}
}

Просмотреть файл

@ -0,0 +1,193 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_SIMPLETILEDCONTENTCLIENT_H
#define MOZILLA_GFX_SIMPLETILEDCONTENTCLIENT_H
// We include this header here so that we don't need to
// duplicate BasicTiledLayerPaintData
#include "TiledContentClient.h"
#include "SharedBuffer.h"
namespace mozilla {
namespace layers {
class ClientTiledThebesLayer;
class SimpleTiledLayerTile;
class SimpleTiledLayerBuffer;
class SimpleClientTiledThebesLayer;
class SimpleTiledLayerBuffer;
#define GFX_SIMP_TILEDLAYER_DEBUG_OVERLAY
struct SimpleTiledLayerTile
{
RefPtr<TextureClient> mTileBuffer;
RefPtr<ClientLayerManager> mManager;
nsRefPtr<SharedBuffer> mCachedBuffer;
TimeStamp mLastUpdate;
SimpleTiledLayerTile() { }
SimpleTiledLayerTile(ClientLayerManager *aManager, TextureClient *aBuffer)
: mTileBuffer(aBuffer)
, mManager(aManager)
{ }
bool operator== (const SimpleTiledLayerTile& o) const
{
return mTileBuffer == o.mTileBuffer;
}
bool operator!= (const SimpleTiledLayerTile& o) const
{
return mTileBuffer != o.mTileBuffer;
}
void SetLayerManager(ClientLayerManager *aManager)
{
mManager = aManager;
}
bool IsPlaceholderTile()
{
return mTileBuffer == nullptr;
}
TileDescriptor GetTileDescriptor()
{
if (mTileBuffer)
return TexturedTileDescriptor(nullptr, mTileBuffer->GetIPDLActor(), 0);
NS_NOTREACHED("Unhandled SimpleTiledLayerTile type");
return PlaceholderTileDescriptor();
}
void Release()
{
mTileBuffer = nullptr;
mCachedBuffer = nullptr;
}
};
class SimpleTiledLayerBuffer
: public TiledLayerBuffer<SimpleTiledLayerBuffer, SimpleTiledLayerTile>
{
friend class TiledLayerBuffer<SimpleTiledLayerBuffer, SimpleTiledLayerTile>;
public:
SimpleTiledLayerBuffer(SimpleClientTiledThebesLayer* aThebesLayer,
CompositableClient* aCompositableClient,
ClientLayerManager* aManager)
: mThebesLayer(aThebesLayer)
, mCompositableClient(aCompositableClient)
, mManager(aManager)
, mLastPaintOpaque(false)
{}
SimpleTiledLayerBuffer()
: mLastPaintOpaque(false)
{}
void PaintThebes(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData);
SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
void Release() {
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
mRetainedTiles[i].Release();
}
}
const CSSToScreenScale& GetFrameResolution() const { return mFrameResolution; }
void SetFrameResolution(const CSSToScreenScale& aResolution) { mFrameResolution = aResolution; }
bool HasFormatChanged() const;
private:
SimpleClientTiledThebesLayer* mThebesLayer;
CompositableClient* mCompositableClient;
ClientLayerManager* mManager;
LayerManager::DrawThebesLayerCallback mCallback;
void* mCallbackData;
CSSToScreenScale mFrameResolution;
bool mLastPaintOpaque;
gfxContentType GetContentType() const;
SimpleTiledLayerTile ValidateTile(SimpleTiledLayerTile aTile,
const nsIntPoint& aTileOrigin,
const nsIntRegion& aDirtyRect);
SimpleTiledLayerTile GetPlaceholderTile() const { return SimpleTiledLayerTile(); }
void ReleaseTile(SimpleTiledLayerTile aTile) { aTile.Release(); }
void SwapTiles(SimpleTiledLayerTile& aTileA, SimpleTiledLayerTile& aTileB) { std::swap(aTileA, aTileB); }
};
class SimpleTiledContentClient : public CompositableClient
{
friend class SimpleClientTiledThebesLayer;
public:
SimpleTiledContentClient(SimpleClientTiledThebesLayer* aThebesLayer,
ClientLayerManager* aManager);
~SimpleTiledContentClient();
virtual TextureInfo GetTextureInfo() const MOZ_OVERRIDE
{
return TextureInfo(BUFFER_SIMPLE_TILED);
}
void UseTiledLayerBuffer();
private:
SimpleTiledLayerBuffer mTiledBuffer;
};
class SimpleClientTiledThebesLayer : public ThebesLayer,
public ClientLayer
{
typedef ThebesLayer Base;
public:
SimpleClientTiledThebesLayer(ClientLayerManager* const aManager);
~SimpleClientTiledThebesLayer();
// Thebes Layer
virtual Layer* AsLayer() { return this; }
virtual void InvalidateRegion(const nsIntRegion& aRegion) {
mInvalidRegion.Or(mInvalidRegion, aRegion);
mValidRegion.Sub(mValidRegion, aRegion);
}
// Shadow methods
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void Disconnect() { ClientLayer::Disconnect(); }
virtual void RenderLayer();
protected:
ClientLayerManager* ClientManager() { return static_cast<ClientLayerManager*>(mManager); }
void BeginPaint();
void EndPaint(bool aFinish);
RefPtr<SimpleTiledContentClient> mContentClient;
BasicTiledLayerPaintData mPaintData;
};
} // mozilla
} // layers
#endif

Просмотреть файл

@ -52,6 +52,12 @@
# include "gfxSharedImageSurface.h"
#endif
#if 0
#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
#else
#define RECYCLE_LOG(...) do { } while (0)
#endif
using namespace mozilla::gl;
using namespace mozilla::gfx;
@ -90,6 +96,20 @@ public:
bool Recv__delete__() MOZ_OVERRIDE;
bool RecvCompositorRecycle()
{
RECYCLE_LOG("Receive recycle %p (%p)\n", mTextureClient, mWaitForRecycle.get());
mWaitForRecycle = nullptr;
return true;
}
void WaitForCompositorRecycle()
{
mWaitForRecycle = mTextureClient;
RECYCLE_LOG("Wait for recycle %p\n", mWaitForRecycle.get());
SendClientRecycle();
}
/**
* Only used during the deallocation phase iff we need synchronization between
* the client and host side for deallocation (that is, when the data is going
@ -128,6 +148,7 @@ private:
}
RefPtr<CompositableForwarder> mForwarder;
RefPtr<TextureClient> mWaitForRecycle;
TextureClientData* mTextureData;
TextureClient* mTextureClient;
bool mIPCOpen;
@ -138,6 +159,7 @@ private:
void
TextureChild::DeleteTextureData()
{
mWaitForRecycle = nullptr;
if (mTextureData) {
mTextureData->DeallocateSharedData(GetAllocator());
delete mTextureData;
@ -158,6 +180,7 @@ TextureChild::ActorDestroy(ActorDestroyReason why)
if (mTextureClient) {
mTextureClient->mActor = nullptr;
}
mWaitForRecycle = nullptr;
}
// static
@ -181,7 +204,13 @@ TextureClient::DestroyIPDLActor(PTextureChild* actor)
TextureClient*
TextureClient::AsTextureClient(PTextureChild* actor)
{
return actor? static_cast<TextureChild*>(actor)->mTextureClient : nullptr;
return actor ? static_cast<TextureChild*>(actor)->mTextureClient : nullptr;
}
void
TextureClient::WaitForCompositorRecycle()
{
mActor->WaitForCompositorRecycle();
}
bool

Просмотреть файл

@ -278,6 +278,15 @@ public:
*/
TextureFlags GetFlags() const { return mFlags; }
/**
* valid only for TEXTURE_RECYCLE TextureClient.
* When called this texture client will grab a strong reference and release
* it once the compositor notifies that it is done with the texture.
* NOTE: In this stage the texture client can no longer be used by the
* client in a transaction.
*/
void WaitForCompositorRecycle();
/**
* After being shared with the compositor side, an immutable texture is never
* modified, it can only be read. It is safe to not Lock/Unlock immutable

Просмотреть файл

@ -160,6 +160,7 @@ CompositableHost::Create(const TextureInfo& aTextureInfo)
result = new ContentHostIncremental(aTextureInfo);
break;
case BUFFER_TILED:
case BUFFER_SIMPLE_TILED:
result = new TiledContentHost(aTextureInfo);
break;
case COMPOSITABLE_IMAGE:

Просмотреть файл

@ -22,8 +22,15 @@
#include "nsAutoPtr.h" // for nsRefPtr
#include "nsPrintfCString.h" // for nsPrintfCString
#include "mozilla/layers/PTextureParent.h"
#include "mozilla/unused.h"
#include <limits>
#if 0
#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
#else
#define RECYCLE_LOG(...) do { } while (0)
#endif
struct nsIntPoint;
namespace mozilla {
@ -43,6 +50,9 @@ public:
bool Init(const SurfaceDescriptor& aSharedData,
const TextureFlags& aFlags);
void CompositorRecycle();
virtual bool RecvClientRecycle() MOZ_OVERRIDE;
virtual bool RecvRemoveTexture() MOZ_OVERRIDE;
virtual bool RecvRemoveTextureSync() MOZ_OVERRIDE;
@ -52,6 +62,7 @@ public:
void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
ISurfaceAllocator* mAllocator;
RefPtr<TextureHost> mWaitForClientRecycle;
RefPtr<TextureHost> mTextureHost;
};
@ -728,7 +739,37 @@ TextureParent::TextureParent(ISurfaceAllocator* aAllocator)
TextureParent::~TextureParent()
{
MOZ_COUNT_DTOR(TextureParent);
mTextureHost = nullptr;
if (mTextureHost) {
mTextureHost->ClearRecycleCallback();
}
}
static void RecycleCallback(TextureHost* textureHost, void* aClosure) {
TextureParent* tp = reinterpret_cast<TextureParent*>(aClosure);
tp->CompositorRecycle();
}
void
TextureParent::CompositorRecycle()
{
mTextureHost->ClearRecycleCallback();
mozilla::unused << SendCompositorRecycle();
// Don't forget to prepare for the next reycle
mWaitForClientRecycle = mTextureHost;
}
bool
TextureParent::RecvClientRecycle()
{
// This will allow the RecycleCallback to be called once the compositor
// releases any external references to TextureHost.
mTextureHost->SetRecycleCallback(RecycleCallback, this);
if (!mWaitForClientRecycle) {
RECYCLE_LOG("Not a recycable tile");
}
mWaitForClientRecycle = nullptr;
return true;
}
bool
@ -738,7 +779,14 @@ TextureParent::Init(const SurfaceDescriptor& aSharedData,
mTextureHost = TextureHost::Create(aSharedData,
mAllocator,
aFlags);
mTextureHost->mActor = this;
if (mTextureHost) {
mTextureHost->mActor = this;
if (aFlags & TEXTURE_RECYCLE) {
mWaitForClientRecycle = mTextureHost;
RECYCLE_LOG("Setup recycling for tile %p\n", this);
}
}
return !!mTextureHost;
}
@ -773,6 +821,10 @@ TextureParent::ActorDestroy(ActorDestroyReason why)
NS_RUNTIMEABORT("FailedConstructor isn't possible in PTexture");
}
if (mTextureHost->GetFlags() & TEXTURE_RECYCLE) {
RECYCLE_LOG("clear recycling for tile %p\n", this);
mTextureHost->ClearRecycleCallback();
}
if (mTextureHost->GetFlags() & TEXTURE_DEALLOCATE_CLIENT) {
mTextureHost->ForgetSharedData();
}

Просмотреть файл

@ -275,7 +275,6 @@ class TextureHost
void Finalize();
friend class AtomicRefCountedWithFinalize<TextureHost>;
public:
TextureHost(TextureFlags aFlags);

Просмотреть файл

@ -34,6 +34,7 @@ TiledLayerBufferComposite::TiledLayerBufferComposite()
TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
const SurfaceDescriptorTiles& aDescriptor,
const nsIntRegion& aOldPaintedRegion)
: mFrameResolution(1.0)
{
mUninitialized = false;
mHasDoubleBufferedTiles = false;
@ -61,13 +62,13 @@ TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocat
sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_Shmem());
} else {
sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
// The corresponding AddRef is in TiledClient::GetTileDescriptor
sharedLock->Release();
}
MOZ_ASSERT(sharedLock);
if (sharedLock) {
mRetainedTiles.AppendElement(TileHost(sharedLock, texture));
if (sharedLock) {
// The corresponding AddRef is in TiledClient::GetTileDescriptor
sharedLock->Release();
}
}
mRetainedTiles.AppendElement(TileHost(sharedLock, texture));
break;
}
default:

Просмотреть файл

@ -88,9 +88,6 @@ public:
bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
void ReadUnlock() {
// Warn if we have a texture host, but no corresponding lock.
NS_WARN_IF_FALSE(mTextureHost == nullptr || mSharedLock != nullptr,
"ReadUnlock with no gfxSharedReadLock");
if (mSharedLock) {
mSharedLock->ReadUnlock();
}
@ -263,8 +260,8 @@ private:
TiledLayerBufferComposite mLowPrecisionTiledBuffer;
TiledLayerBufferComposite mOldTiledBuffer;
TiledLayerBufferComposite mOldLowPrecisionTiledBuffer;
bool mPendingUpload : 1;
bool mPendingLowPrecisionUpload : 1;
bool mPendingUpload;
bool mPendingLowPrecisionUpload;
};
}

Просмотреть файл

@ -25,8 +25,12 @@ sync protocol PTexture {
child:
async __delete__();
async CompositorRecycle();
parent:
async ClientRecycle();
/**
* Asynchronously tell the Compositor side to remove the texture.
*/

Просмотреть файл

@ -105,6 +105,8 @@ EXPORTS.mozilla.layers += [
'client/CompositableClient.h',
'client/ContentClient.h',
'client/ImageClient.h',
'client/SimpleTextureClientPool.h',
'client/SimpleTiledContentClient.h',
'client/TextureClient.h',
'client/TextureClientPool.h',
'client/TiledContentClient.h',
@ -234,6 +236,8 @@ UNIFIED_SOURCES += [
'client/CompositableClient.cpp',
'client/ContentClient.cpp',
'client/ImageClient.cpp',
'client/SimpleTextureClientPool.cpp',
'client/SimpleTiledContentClient.cpp',
'client/TextureClient.cpp',
'client/TextureClientPool.cpp',
'client/TiledContentClient.cpp',

Просмотреть файл

@ -156,6 +156,7 @@ private:
DECL_GFX_PREF(Live, "layers.draw-tile-borders", DrawTileBorders, bool, false);
DECL_GFX_PREF(Once, "layers.dump", LayersDump, bool, false);
DECL_GFX_PREF(Once, "layers.enable-tiles", LayersTilesEnabled, bool, false);
DECL_GFX_PREF(Once, "layers.simple-tiles", LayersUseSimpleTiles, bool, false);
DECL_GFX_PREF(Once, "layers.force-per-tile-drawing", PerTileDrawing, bool, false);
DECL_GFX_PREF(Once, "layers.overzealous-gralloc-unlocking", OverzealousGrallocUnlocking, bool, false);
DECL_GFX_PREF(Once, "layers.force-shmem-tiles", ForceShmemTiles, bool, false);