Bug 870211 - Add ContentHostIncremental. r=nical

This commit is contained in:
Matt Woodrow 2013-05-16 15:45:43 +12:00
parent f1ba51d39d
commit 5f4fec3687
5 changed files with 406 additions and 1 deletions

View File

@ -65,6 +65,8 @@ enum CompositableType
BUFFER_BRIDGE, // image bridge protocol
BUFFER_CONTENT, // thebes layer interface, single buffering
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.
BUFFER_TILED, // tiled thebes layer
BUFFER_COUNT
};
@ -76,7 +78,9 @@ enum TextureHostFlags
{
TEXTURE_HOST_DEFAULT = 0, // The default texture host for the given
// SurfaceDescriptor
TEXTURE_HOST_TILED = 1 << 0 // A texture host that supports tiling
TEXTURE_HOST_TILED = 1 << 0, // A texture host that supports tiling
TEXTURE_HOST_COPY_PREVIOUS = 1 << 1 // Texture contents should be initialized
// from the previous texture.
};
/**

View File

@ -68,6 +68,9 @@ CompositableHost::Create(const TextureInfo& aTextureInfo)
case BUFFER_CONTENT_DIRECT:
result = new ContentHostDoubleBuffered(aTextureInfo);
return result;
case BUFFER_CONTENT_INC:
result = new ContentHostIncremental(aTextureInfo);
return result;
default:
MOZ_NOT_REACHED("Unknown CompositableType");
return nullptr;

View File

@ -99,6 +99,28 @@ public:
MOZ_ASSERT(false, "should be implemented or not used");
}
/**
* Update the content host using a surface that only contains the updated
* region.
*
* Takes ownership of aSurface, and is responsible for freeing it.
*
* @param aTextureId Texture to update.
* @param aSurface Surface containing the update area. Its contents are relative
* to aUpdated.TopLeft()
* @param aUpdated Area of the content host to update.
* @param aBufferRect New area covered by the content host.
* @param aBufferRotation New buffer rotation.
*/
virtual void UpdateIncremental(TextureIdentifier aTextureId,
SurfaceDescriptor& aSurface,
const nsIntRegion& aUpdated,
const nsIntRect& aBufferRect,
const nsIntPoint& aBufferRotation)
{
MOZ_ASSERT(false, "should be implemented or not used");
}
/**
* Ensure that a suitable texture host exists in this compositable. The
* compositable host may or may not create a new texture host. If a texture
@ -119,6 +141,22 @@ public:
ISurfaceAllocator* aAllocator,
const TextureInfo& aTextureInfo) = 0;
/**
* Ensure that a suitable texture host exists in this compsitable.
*
* Only used with ContentHostIncremental.
*
* No SurfaceDescriptor or TextureIdentifier is provider as we
* don't have a single surface for the texture contents, and we
* need to allocate our own one to be updated later.
*/
virtual void EnsureTextureHost(ISurfaceAllocator* aAllocator,
const TextureInfo& aTextureInfo,
const nsIntRect& aBufferRect)
{
MOZ_ASSERT(false, "should be implemented or not used");
}
virtual TextureHost* GetTextureHost() { return nullptr; }
virtual LayerRenderState GetRenderState() = 0;

View File

@ -6,6 +6,7 @@
#include "mozilla/layers/ContentHost.h"
#include "mozilla/layers/Effects.h"
#include "nsPrintfCString.h"
#include "gfx2DGlue.h"
namespace mozilla {
using namespace gfx;
@ -432,6 +433,220 @@ ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData,
mValidRegionForNextBackBuffer = aOldValidRegionBack;
}
void
ContentHostIncremental::EnsureTextureHost(ISurfaceAllocator* aAllocator,
const TextureInfo& aTextureInfo,
const nsIntRect& aBufferRect)
{
mUpdateList.AppendElement(new TextureCreationRequest(aTextureInfo,
aBufferRect));
mDeAllocator = aAllocator;
}
void
ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId,
SurfaceDescriptor& aSurface,
const nsIntRegion& aUpdated,
const nsIntRect& aBufferRect,
const nsIntPoint& aBufferRotation)
{
mUpdateList.AppendElement(new TextureUpdateRequest(aTextureId,
aSurface,
aUpdated,
aBufferRect,
aBufferRotation));
}
void
ContentHostIncremental::ProcessTextureUpdates()
{
for (uint32_t i = 0; i < mUpdateList.Length(); i++) {
mUpdateList[i]->Execute(this);
}
mUpdateList.Clear();
}
void
ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost)
{
RefPtr<TextureHost> newHost =
TextureHost::CreateTextureHost(SurfaceDescriptor::TShmem,
mTextureInfo.mTextureHostFlags,
mTextureInfo.mTextureFlags);
Compositor* compositor = aHost->GetCompositor();
if (compositor) {
newHost->SetCompositor(compositor);
}
RefPtr<TextureHost> newHostOnWhite;
if (mTextureInfo.mTextureFlags & ComponentAlpha) {
newHostOnWhite =
TextureHost::CreateTextureHost(SurfaceDescriptor::TShmem,
mTextureInfo.mTextureHostFlags,
mTextureInfo.mTextureFlags);
Compositor* compositor = aHost->GetCompositor();
if (compositor) {
newHostOnWhite->SetCompositor(compositor);
}
}
if (mTextureInfo.mTextureHostFlags & TEXTURE_HOST_COPY_PREVIOUS) {
nsIntRect bufferRect = aHost->mBufferRect;
nsIntPoint bufferRotation = aHost->mBufferRotation;
nsIntRect overlap;
// The buffer looks like:
// ______
// |1 |2 | Where the center point is offset by mBufferRotation from the top-left corner.
// |___|__|
// |3 |4 |
// |___|__|
//
// This is drawn to the screen as:
// ______
// |4 |3 | Where the center point is { width - mBufferRotation.x, height - mBufferRotation.y } from
// |___|__| from the top left corner - rotationPoint.
// |2 |1 |
// |___|__|
//
// The basic idea below is to take all quadrant rectangles from the src and transform them into rectangles
// in the destination. Unfortunately, it seems it is overly complex and could perhaps be simplified.
nsIntRect srcBufferSpaceBottomRight(bufferRotation.x, bufferRotation.y, bufferRect.width - bufferRotation.x, bufferRect.height - bufferRotation.y);
nsIntRect srcBufferSpaceTopRight(bufferRotation.x, 0, bufferRect.width - bufferRotation.x, bufferRotation.y);
nsIntRect srcBufferSpaceTopLeft(0, 0, bufferRotation.x, bufferRotation.y);
nsIntRect srcBufferSpaceBottomLeft(0, bufferRotation.y, bufferRotation.x, bufferRect.height - bufferRotation.y);
overlap.IntersectRect(bufferRect, mBufferRect);
nsIntRect srcRect(overlap), dstRect(overlap);
srcRect.MoveBy(- bufferRect.TopLeft() + bufferRotation);
nsIntRect srcRectDrawTopRight(srcRect);
nsIntRect srcRectDrawTopLeft(srcRect);
nsIntRect srcRectDrawBottomLeft(srcRect);
// transform into the different quadrants
srcRectDrawTopRight .MoveBy(-nsIntPoint(0, bufferRect.height));
srcRectDrawTopLeft .MoveBy(-nsIntPoint(bufferRect.width, bufferRect.height));
srcRectDrawBottomLeft.MoveBy(-nsIntPoint(bufferRect.width, 0));
// Intersect with the quadrant
srcRect = srcRect .Intersect(srcBufferSpaceBottomRight);
srcRectDrawTopRight = srcRectDrawTopRight .Intersect(srcBufferSpaceTopRight);
srcRectDrawTopLeft = srcRectDrawTopLeft .Intersect(srcBufferSpaceTopLeft);
srcRectDrawBottomLeft = srcRectDrawBottomLeft.Intersect(srcBufferSpaceBottomLeft);
dstRect = srcRect;
nsIntRect dstRectDrawTopRight(srcRectDrawTopRight);
nsIntRect dstRectDrawTopLeft(srcRectDrawTopLeft);
nsIntRect dstRectDrawBottomLeft(srcRectDrawBottomLeft);
// transform back to src buffer space
dstRect .MoveBy(-bufferRotation);
dstRectDrawTopRight .MoveBy(-bufferRotation + nsIntPoint(0, bufferRect.height));
dstRectDrawTopLeft .MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, bufferRect.height));
dstRectDrawBottomLeft.MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, 0));
// transform back to draw coordinates
dstRect .MoveBy(bufferRect.TopLeft());
dstRectDrawTopRight .MoveBy(bufferRect.TopLeft());
dstRectDrawTopLeft .MoveBy(bufferRect.TopLeft());
dstRectDrawBottomLeft.MoveBy(bufferRect.TopLeft());
// transform to destBuffer space
dstRect .MoveBy(-mBufferRect.TopLeft());
dstRectDrawTopRight .MoveBy(-mBufferRect.TopLeft());
dstRectDrawTopLeft .MoveBy(-mBufferRect.TopLeft());
dstRectDrawBottomLeft.MoveBy(-mBufferRect.TopLeft());
newHost->EnsureBuffer(mBufferRect.Size(),
ContentForFormat(aHost->mTextureHost->GetFormat()));
aHost->mTextureHost->CopyTo(srcRect, newHost, dstRect);
if (bufferRotation != nsIntPoint(0, 0)) {
// Draw the remaining quadrants. We call BlitTextureImage 3 extra
// times instead of doing a single draw call because supporting that
// with a tiled source is quite tricky.
if (!srcRectDrawTopRight.IsEmpty())
aHost->mTextureHost->CopyTo(srcRectDrawTopRight,
newHost, dstRectDrawTopRight);
if (!srcRectDrawTopLeft.IsEmpty())
aHost->mTextureHost->CopyTo(srcRectDrawTopLeft,
newHost, dstRectDrawTopLeft);
if (!srcRectDrawBottomLeft.IsEmpty())
aHost->mTextureHost->CopyTo(srcRectDrawBottomLeft,
newHost, dstRectDrawBottomLeft);
}
if (newHostOnWhite) {
newHostOnWhite->EnsureBuffer(mBufferRect.Size(),
ContentForFormat(aHost->mTextureHostOnWhite->GetFormat()));
aHost->mTextureHostOnWhite->CopyTo(srcRect, newHostOnWhite, dstRect);
if (bufferRotation != nsIntPoint(0, 0)) {
// draw the remaining quadrants
if (!srcRectDrawTopRight.IsEmpty())
aHost->mTextureHostOnWhite->CopyTo(srcRectDrawTopRight,
newHostOnWhite, dstRectDrawTopRight);
if (!srcRectDrawTopLeft.IsEmpty())
aHost->mTextureHostOnWhite->CopyTo(srcRectDrawTopLeft,
newHostOnWhite, dstRectDrawTopLeft);
if (!srcRectDrawBottomLeft.IsEmpty())
aHost->mTextureHostOnWhite->CopyTo(srcRectDrawBottomLeft,
newHostOnWhite, dstRectDrawBottomLeft);
}
}
}
aHost->mTextureHost = newHost;
aHost->mTextureHostOnWhite = newHostOnWhite;
aHost->mBufferRect = mBufferRect;
aHost->mBufferRotation = nsIntPoint();
}
nsIntRect
ContentHostIncremental::TextureUpdateRequest::GetQuadrantRectangle(XSide aXSide,
YSide aYSide) const
{
// quadrantTranslation is the amount we translate the top-left
// of the quadrant by to get coordinates relative to the layer
nsIntPoint quadrantTranslation = -mBufferRotation;
quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
return mBufferRect + quadrantTranslation;
}
void
ContentHostIncremental::TextureUpdateRequest::Execute(ContentHostIncremental* aHost)
{
nsIntRect drawBounds = mUpdated.GetBounds();
aHost->mBufferRect = mBufferRect;
aHost->mBufferRotation = mBufferRotation;
// Figure out which quadrant to draw in
int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT;
YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP;
nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants");
mUpdated.MoveBy(-nsIntPoint(quadrantRect.x, quadrantRect.y));
nsIntPoint offset = -mUpdated.GetBounds().TopLeft();
if (mTextureId == TextureFront) {
aHost->mTextureHost->Update(mDescriptor, &mUpdated, &offset);
} else {
aHost->mTextureHostOnWhite->Update(mDescriptor, &mUpdated, &offset);
}
//TODO: Recycle these?
aHost->mDeAllocator->DestroySharedSurface(&mDescriptor);
}
#ifdef MOZ_LAYERS_HAVE_LOG
void
ContentHostSingleBuffered::PrintInfo(nsACString& aTo, const char* aPrefix)

View File

@ -199,6 +199,151 @@ public:
#endif
};
/**
* Maintains a host-side only texture, and gets provided with
* surfaces that only cover the changed pixels during an update.
*
* Takes ownership of the passed in update surfaces, and must
* free them once texture upload is complete.
*
* Delays texture uploads until the next composite to
* avoid blocking the main thread.
*/
class ContentHostIncremental : public ContentHostBase
{
public:
ContentHostIncremental(const TextureInfo& aTextureInfo)
: ContentHostBase(aTextureInfo)
, mDeAllocator(nullptr)
{}
virtual CompositableType GetType() { return BUFFER_CONTENT; }
virtual void EnsureTextureHost(ISurfaceAllocator* aAllocator,
const TextureInfo& aTextureInfo,
const nsIntRect& aBufferRect) MOZ_OVERRIDE;
virtual void EnsureTextureHost(TextureIdentifier aTextureId,
const SurfaceDescriptor& aSurface,
ISurfaceAllocator* aAllocator,
const TextureInfo& aTextureInfo)
{
NS_RUNTIMEABORT("Shouldn't call this");
}
virtual void UpdateIncremental(TextureIdentifier aTextureId,
SurfaceDescriptor& aSurface,
const nsIntRegion& aUpdated,
const nsIntRect& aBufferRect,
const nsIntPoint& aBufferRotation) MOZ_OVERRIDE;
virtual void UpdateThebes(const ThebesBufferData& aData,
const nsIntRegion& aUpdated,
const nsIntRegion& aOldValidRegionBack,
nsIntRegion* aUpdatedRegionBack)
{
NS_RUNTIMEABORT("Shouldn't call this");
}
virtual void Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Point& aOffset,
const gfx::Filter& aFilter,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion = nullptr,
TiledLayerProperties* aLayerProperties = nullptr)
{
ProcessTextureUpdates();
ContentHostBase::Composite(aEffectChain, aOpacity,
aTransform, aOffset, aFilter,
aClipRect, aVisibleRegion,
aLayerProperties);
}
virtual void DestroyTextures()
{
mTextureHost = nullptr;
mTextureHostOnWhite = nullptr;
mUpdateList.Clear();
}
private:
void ProcessTextureUpdates();
class Request
{
public:
Request()
{
MOZ_COUNT_CTOR(ContentHostIncremental::Request);
}
virtual ~Request()
{
MOZ_COUNT_DTOR(ContentHostIncremental::Request);
}
virtual void Execute(ContentHostIncremental *aHost) = 0;
};
class TextureCreationRequest : public Request
{
public:
TextureCreationRequest(const TextureInfo& aTextureInfo,
const nsIntRect& aBufferRect)
: mTextureInfo(aTextureInfo)
, mBufferRect(aBufferRect)
{}
virtual void Execute(ContentHostIncremental *aHost);
private:
TextureInfo mTextureInfo;
nsIntRect mBufferRect;
};
class TextureUpdateRequest : public Request
{
public:
TextureUpdateRequest(TextureIdentifier aTextureId,
SurfaceDescriptor& aDescriptor,
const nsIntRegion& aUpdated,
const nsIntRect& aBufferRect,
const nsIntPoint& aBufferRotation)
: mTextureId(aTextureId)
, mDescriptor(aDescriptor)
, mUpdated(aUpdated)
, mBufferRect(aBufferRect)
, mBufferRotation(aBufferRotation)
{}
virtual void Execute(ContentHostIncremental *aHost);
private:
enum XSide {
LEFT, RIGHT
};
enum YSide {
TOP, BOTTOM
};
nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
TextureIdentifier mTextureId;
SurfaceDescriptor mDescriptor;
nsIntRegion mUpdated;
nsIntRect mBufferRect;
nsIntPoint mBufferRotation;
};
nsTArray<nsAutoPtr<Request> > mUpdateList;
ISurfaceAllocator* mDeAllocator;
};
}
}