Bug 1167235 - Part 3 - Use TextureReadLock to optimize canvas copy-on-writes. r=Bas
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -85,24 +85,26 @@ PersistentBufferProviderShared::Create(g
{
if (!aFwd || !aFwd->IPCOpen()) {
return nullptr;
}
RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
aFwd, aFormat, aSize,
BackendSelector::Canvas,
- TextureFlags::IMMEDIATE_UPLOAD,
+ TextureFlags::DEFAULT,
TextureAllocationFlags::ALLOC_DEFAULT
);
if (!texture) {
return nullptr;
}
+ texture->EnableReadLock();
+
RefPtr<PersistentBufferProviderShared> provider =
new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
return provider.forget();
}
PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
CompositableForwarder* aFwd,
@@ -115,51 +117,79 @@ PersistentBufferProviderShared::Persiste
{
MOZ_COUNT_CTOR(PersistentBufferProviderShared);
}
PersistentBufferProviderShared::~PersistentBufferProviderShared()
{
MOZ_COUNT_DTOR(PersistentBufferProviderShared);
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().RemoveObject(this);
+ }
+
mDrawTarget = nullptr;
if (mBack && mBack->IsLocked()) {
mBack->Unlock();
}
if (mFront && mFront->IsLocked()) {
mFront->Unlock();
}
+ if (mBuffer && mBuffer->IsLocked()) {
+ mBuffer->Unlock();
+ }
}
already_AddRefed<gfx::DrawTarget>
PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
{
if (!mFwd->IPCOpen()) {
return nullptr;
}
+ MOZ_ASSERT(!mSnapshot);
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().MarkUsed(this);
+ } else {
+ mFwd->GetActiveResourceTracker().AddObject(this);
+ }
+
if (!mDrawTarget) {
- if (!mBack) {
- mBack = TextureClient::CreateForDrawing(
- mFwd, mFormat, mSize,
- BackendSelector::Canvas,
- TextureFlags::IMMEDIATE_UPLOAD,
- TextureAllocationFlags::ALLOC_DEFAULT
- );
+ bool changedBackBuffer = false;
+ if (!mBack || mBack->IsReadLocked()) {
+ if (mBuffer && !mBuffer->IsReadLocked()) {
+ mBack.swap(mBuffer);
+ } else {
+ mBack = TextureClient::CreateForDrawing(
+ mFwd, mFormat, mSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+ if (mBack) {
+ mBack->EnableReadLock();
+ }
+ }
+ changedBackBuffer = true;
+ } else {
+ // Fast path, our front buffer is already writable because the texture upload
+ // has completed on the compositor side.
+ if (mBack->HasIntermediateBuffer()) {
+ // No need to keep an extra buffer around
+ mBuffer = nullptr;
+ }
}
- if (!mBack) {
+ if (!mBack || !mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
return nullptr;
}
- if (!mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
- return nullptr;
- }
- if (!aPersistedRect.IsEmpty() && mFront
- && mFront->Lock(OpenMode::OPEN_READ)) {
+ if (changedBackBuffer && !aPersistedRect.IsEmpty()
+ && mFront && mFront->Lock(OpenMode::OPEN_READ)) {
DebugOnly<bool> success = mFront->CopyToTextureClient(mBack, &aPersistedRect, nullptr);
MOZ_ASSERT(success);
mFront->Unlock();
}
mDrawTarget = mBack->BorrowDrawTarget();
@@ -172,31 +202,39 @@ PersistentBufferProviderShared::BorrowDr
return dt.forget();
}
bool
PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
{
RefPtr<gfx::DrawTarget> dt(aDT);
MOZ_ASSERT(mDrawTarget == dt);
+ MOZ_ASSERT(!mSnapshot);
mDrawTarget = nullptr;
dt = nullptr;
mBack->Unlock();
+ if (!mBuffer && mFront && !mFront->IsLocked()) {
+ mBuffer.swap(mFront);
+ }
+
mFront = mBack;
- mBack = nullptr;
return true;
}
already_AddRefed<gfx::SourceSurface>
PersistentBufferProviderShared::BorrowSnapshot()
{
+ // TODO[nical] currently we can't snapshot while drawing, looks like it does
+ // the job but I am not sure whether we want to be able to do that.
+ MOZ_ASSERT(!mDrawTarget);
+
if (!mFront || mFront->IsLocked()) {
MOZ_ASSERT(false);
return nullptr;
}
if (!mFront->Lock(OpenMode::OPEN_READ)) {
return nullptr;
}
@@ -222,10 +260,21 @@ PersistentBufferProviderShared::ReturnSn
mSnapshot = nullptr;
snapshot = nullptr;
mDrawTarget = nullptr;
mFront->Unlock();
}
+void
+PersistentBufferProviderShared::NotifyInactive()
+{
+ if (mBuffer && mBuffer->IsLocked()) {
+ // mBuffer should never be locked
+ MOZ_ASSERT(false);
+ mBuffer->Unlock();
+ }
+ mBuffer = nullptr;
+}
+
} // namespace layers
} // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -91,16 +91,17 @@ private:
};
/**
* Provides access to a buffer which can be sent to the compositor without
* requiring a copy.
*/
class PersistentBufferProviderShared : public PersistentBufferProvider
+ , public ActiveResource
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderShared, override)
static already_AddRefed<PersistentBufferProviderShared>
Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
CompositableForwarder* aFwd);
@@ -113,28 +114,34 @@ public:
virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
TextureClient* GetTextureClient() override {
return mFront;
}
+ virtual void NotifyInactive() override;
+
protected:
PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
CompositableForwarder* aFwd,
RefPtr<TextureClient>& aTexture);
~PersistentBufferProviderShared();
gfx::IntSize mSize;
gfx::SurfaceFormat mFormat;
RefPtr<CompositableForwarder> mFwd;
+ // The texture presented to the compositor.
RefPtr<TextureClient> mFront;
+ // The texture that the canvas uses.
RefPtr<TextureClient> mBack;
+ // An extra texture we keep around temporarily to avoid allocating.
+ RefPtr<TextureClient> mBuffer;
RefPtr<gfx::DrawTarget> mDrawTarget;
RefPtr<gfx::SourceSurface > mSnapshot;
};
struct AutoReturnSnapshot
{
PersistentBufferProvider* mBufferProvider;
RefPtr<gfx::SourceSurface>* mSnapshot;
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -106,17 +106,16 @@ public:
}
virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
virtual void UpdateFromTexture(TextureClient* aBuffer) override;
virtual bool AddTextureClient(TextureClient* aTexture) override
{
- MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
return CanvasClient::AddTextureClient(aTexture);
}
virtual void OnDetach() override
{
mBackBuffer = mFrontBuffer = nullptr;
}
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -13,47 +13,79 @@
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
#include "mozilla/layers/LayersTypes.h" // for LayersBackend
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
#include "nsRegion.h" // for nsIntRegion
#include "mozilla/gfx/Rect.h"
+#include "nsExpirationTracker.h"
#include "nsHashKeys.h"
#include "nsTHashtable.h"
namespace mozilla {
namespace layers {
class CompositableClient;
class AsyncTransactionTracker;
class ImageContainer;
struct TextureFactoryIdentifier;
class SurfaceDescriptor;
class SurfaceDescriptorTiles;
class ThebesBufferData;
class PTextureChild;
/**
+ * See ActiveResourceTracker below.
+ */
+class ActiveResource
+{
+public:
+ virtual void NotifyInactive() = 0;
+ nsExpirationState* GetExpirationState() { return &mExpirationState; }
+ bool IsActivityTracked() { return mExpirationState.IsTracked(); }
+private:
+ nsExpirationState mExpirationState;
+};
+
+/**
+ * A convenience class on top of nsExpirationTracker
+ */
+class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3>
+{
+public:
+ ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName)
+ : nsExpirationTracker(aExpirationCycle, aName)
+ {}
+
+ virtual void NotifyExpired(ActiveResource* aResource) override
+ {
+ RemoveObject(aResource);
+ aResource->NotifyInactive();
+ }
+};
+
+/**
* A transaction is a set of changes that happenned on the content side, that
* should be sent to the compositor side.
* CompositableForwarder is an interface to manage a transaction of
* compositable objetcs.
*
* ShadowLayerForwarder is an example of a CompositableForwarder (that can
* additionally forward modifications of the Layer tree).
* ImageBridgeChild is another CompositableForwarder.
*/
class CompositableForwarder : public TextureForwarder
{
public:
CompositableForwarder(const char* aName)
: TextureForwarder(aName)
+ , mActiveResourceTracker(1000, "CompositableForwarder")
, mSerial(++sSerialCounter)
{}
/**
* Setup the IPDL actor for aCompositable to be part of layers
* transactions.
*/
virtual void Connect(CompositableClient* aCompositable,
@@ -164,23 +196,27 @@ public:
return mTextureFactoryIdentifier.mSupportsPartialUploads;
}
const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
{
return mTextureFactoryIdentifier;
}
+ ActiveResourceTracker& GetActiveResourceTracker() { return mActiveResourceTracker; }
+
protected:
TextureFactoryIdentifier mTextureFactoryIdentifier;
nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
RefPtr<SyncObject> mSyncObject;
+ ActiveResourceTracker mActiveResourceTracker;
+
const int32_t mSerial;
static mozilla::Atomic<int32_t> sSerialCounter;
};
} // namespace layers
} // namespace mozilla
#endif