Bug 732917 - Enable double-buffering on Android. r=ajuma

Add support for double-buffering to ThebesLayerOGL, and enable it on Android.
Double-buffering should allow us to more easily implement progressive texture
upload.
This commit is contained in:
Chris Lord 2012-03-17 12:07:02 +00:00
parent 1bb6846373
commit 0c11c7ad0c
2 changed files with 144 additions and 0 deletions

View File

@ -952,6 +952,24 @@ ShadowThebesLayerOGL::ShadowThebesLayerOGL(LayerManagerOGL *aManager)
ShadowThebesLayerOGL::~ShadowThebesLayerOGL()
{}
bool
ShadowThebesLayerOGL::ShouldDoubleBuffer()
{
#ifdef ANDROID
/* Enable double-buffering on Android so that we don't block for as long
* when uploading textures. This is a work-around for the lack of an
* asynchronous texture upload facility.
*
* XXX When bug 730718 is fixed, we will likely want this only to apply for
* Adreno-equipped devices (which have broken sub-image texture updates,
* and no threaded texture upload capability).
*/
return true;
#else
return false;
#endif
}
void
ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
const nsIntRegion& aUpdatedRegion,
@ -960,6 +978,62 @@ ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
OptionalThebesBuffer* aReadOnlyFront,
nsIntRegion* aFrontUpdatedRegion)
{
// The double-buffer path is copied and adapted from BasicLayers.cpp
if (ShouldDoubleBuffer()) {
nsRefPtr<gfxASurface> newFrontBuffer =
ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
nsRefPtr<gfxASurface> currentFront =
ShadowLayerForwarder::OpenDescriptor(mFrontBufferDescriptor);
if (currentFront->GetSize() != newFrontBuffer->GetSize()) {
// Current front buffer is obsolete
DestroyFrontBuffer();
}
}
// This code relies on Swap() arriving *after* attribute mutations.
if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
*aNewBack = ThebesBuffer();
aNewBack->get_ThebesBuffer().buffer() = mFrontBufferDescriptor;
} else {
*aNewBack = null_t();
}
// We have to invalidate the pixels painted into the new buffer.
// They might overlap with our old pixels.
aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion);
nsRefPtr<gfxASurface> unused;
nsIntRect backRect;
nsIntPoint backRotation;
mFrontBuffer.Swap(
newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
getter_AddRefs(unused), &backRect, &backRotation);
if (aNewBack->type() != OptionalThebesBuffer::Tnull_t) {
aNewBack->get_ThebesBuffer().rect() = backRect;
aNewBack->get_ThebesBuffer().rotation() = backRotation;
}
mFrontBufferDescriptor = aNewFront.buffer();
// Upload new front-buffer to texture
if (!mDestroyed) {
if (!mBuffer) {
mBuffer = new ShadowBufferOGL(this);
}
nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(mFrontBufferDescriptor);
mBuffer->Upload(surf, aUpdatedRegion, aNewFront.rect(), aNewFront.rotation());
}
*aReadOnlyFront = aNewFront;
*aFrontUpdatedRegion = aUpdatedRegion;
return;
}
// Single-buffer path
if (!mDestroyed) {
if (!mBuffer) {
mBuffer = new ShadowBufferOGL(this);
@ -977,6 +1051,15 @@ ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
void
ShadowThebesLayerOGL::DestroyFrontBuffer()
{
if (ShouldDoubleBuffer()) {
mFrontBuffer.Clear();
mOldValidRegion.SetEmpty();
if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
mAllocator->DestroySharedSurface(&mFrontBufferDescriptor);
}
}
mBuffer = nsnull;
}

View File

@ -85,6 +85,50 @@ private:
nsRefPtr<Buffer> mBuffer;
};
class ShadowThebesLayerBufferOGL
{
public:
ShadowThebesLayerBufferOGL()
{
MOZ_COUNT_CTOR(ShadowThebesLayerBufferOGL);
}
~ShadowThebesLayerBufferOGL()
{
MOZ_COUNT_DTOR(ShadowThebesLayerBufferOGL);
}
void Swap(gfxASurface* aNewBuffer,
const nsIntRect& aNewRect, const nsIntPoint& aNewRotation,
gfxASurface** aOldBuffer,
nsIntRect* aOldRect, nsIntPoint* aOldRotation)
{
*aOldRect = mBufferRect;
*aOldRotation = mBufferRotation;
nsRefPtr<gfxASurface> oldBuffer = mBuffer;
mBufferRect = aNewRect;
mBufferRotation = aNewRotation;
mBuffer = aNewBuffer;
oldBuffer.forget(aOldBuffer);
}
/**
* Wipe out all retained contents. Call this when the entire
* buffer becomes invalid.
*/
void Clear()
{
mBuffer = nsnull;
mBufferRect.SetEmpty();
}
protected:
nsRefPtr<gfxASurface> mBuffer;
nsIntRect mBufferRect;
nsIntPoint mBufferRotation;
};
class ShadowThebesLayerOGL : public ShadowThebesLayer,
public LayerOGL
{
@ -92,6 +136,7 @@ public:
ShadowThebesLayerOGL(LayerManagerOGL *aManager);
virtual ~ShadowThebesLayerOGL();
virtual bool ShouldDoubleBuffer();
virtual void
Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
@ -100,6 +145,12 @@ public:
virtual void Disconnect();
virtual void SetValidRegion(const nsIntRegion& aRegion)
{
mOldValidRegion = mValidRegion;
ShadowThebesLayer::SetValidRegion(aRegion);
}
// LayerOGL impl
void Destroy();
Layer* GetLayer();
@ -110,6 +161,16 @@ public:
private:
nsRefPtr<ShadowBufferOGL> mBuffer;
// Following used for double-buffering
ShadowThebesLayerBufferOGL mFrontBuffer;
// Describes the gfxASurface we hand out to |mFrontBuffer|.
SurfaceDescriptor mFrontBufferDescriptor;
// When we receive an update from our remote partner, we stow away
// our previous parameters that described our previous front buffer.
// Then when we Swap() back/front buffers, we can return these
// parameters to our partner (adjusted as needed).
nsIntRegion mOldValidRegion;
};
} /* layers */