Bug 574239: Have ThebesLayerD3D9 retain its content even across changes to the visible region. r=jrmuizel

This commit is contained in:
Bas Schouten 2010-07-02 04:08:48 +02:00
parent 8442c7897c
commit 4e102a00dd
2 changed files with 103 additions and 34 deletions

View File

@ -54,6 +54,12 @@ ThebesLayerD3D9::~ThebesLayerD3D9()
mD3DManager->mThebesLayers.RemoveElement(this); mD3DManager->mThebesLayers.RemoveElement(this);
} }
/**
* Retention threshold - amount of pixels intersection required to enable
* layer content retention. This is a guesstimate. Profiling could be done to
* figure out the optimal threshold.
*/
#define RETENTION_THRESHOLD 16384
void void
ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion)
@ -61,55 +67,123 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion)
if (aRegion.IsEqual(mVisibleRegion)) { if (aRegion.IsEqual(mVisibleRegion)) {
return; return;
} }
nsIntRegion oldVisibleRegion = mVisibleRegion;
nsRefPtr<IDirect3DTexture9> oldTexture = mTexture;
ThebesLayer::SetVisibleRegion(aRegion); ThebesLayer::SetVisibleRegion(aRegion);
mInvalidatedRect = mVisibleRegion.GetBounds(); nsIntRect oldBounds = oldVisibleRegion.GetBounds();
device()->CreateTexture(mInvalidatedRect.width, mInvalidatedRect.height, 1, nsIntRect newBounds = mVisibleRegion.GetBounds();
device()->CreateTexture(newBounds.width, newBounds.height, 1,
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL);
// Old visible region will become the region that is covered by both the
// old and the new visible region.
oldVisibleRegion.And(oldVisibleRegion, mVisibleRegion);
// No point in retaining parts which were not valid.
oldVisibleRegion.And(oldVisibleRegion, mValidRegion);
nsIntRect largeRect = oldVisibleRegion.GetLargestRectangle();
// If we had no hardware texture before or have no retained area larger than
// the retention threshold, we're not retaining and are done here. If our
// texture creation failed this can mean a device reset is pending and we
// should silently ignore the failure. In the future when device failures
// are properly handled we should test for the type of failure and gracefully
// handle different failures. See bug 569081.
if (!oldTexture || !mTexture ||
largeRect.width * largeRect.height < RETENTION_THRESHOLD) {
mValidRegion.SetEmpty();
return;
}
nsRefPtr<IDirect3DSurface9> srcSurface, dstSurface;
oldTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
nsIntRegion retainedRegion;
nsIntRegionRectIterator iter(oldVisibleRegion);
const nsIntRect *r;
while ((r = iter.Next())) {
if (r->width * r->height > RETENTION_THRESHOLD) {
RECT oldRect, newRect;
// Calculate the retained rectangle's position on the old and the new
// surface.
oldRect.left = r->x - oldBounds.x;
oldRect.top = r->y - oldBounds.y;
oldRect.right = oldRect.left + r->width;
oldRect.bottom = oldRect.top + r->height;
newRect.left = r->x - newBounds.x;
newRect.top = r->y - newBounds.y;
newRect.right = newRect.left + r->width;
newRect.bottom = newRect.top + r->height;
// Copy data from our old texture to the new one
HRESULT hr = device()->
StretchRect(srcSurface, &oldRect, dstSurface, &newRect, D3DTEXF_NONE);
if (SUCCEEDED(hr)) {
retainedRegion.Or(retainedRegion, *r);
}
}
}
// Areas which were valid and were retained are still valid
mValidRegion.And(mValidRegion, retainedRegion);
} }
void void
ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion) ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion)
{ {
nsIntRegion invalidatedRegion; mValidRegion.Sub(mValidRegion, aRegion);
invalidatedRegion.Or(aRegion, mInvalidatedRect);
invalidatedRegion.And(invalidatedRegion, mVisibleRegion);
mInvalidatedRect = invalidatedRegion.GetBounds();
} }
void void
ThebesLayerD3D9::RenderLayer() ThebesLayerD3D9::RenderLayer()
{ {
if (mVisibleRegion.IsEmpty()) {
return;
}
nsIntRect visibleRect = mVisibleRegion.GetBounds(); nsIntRect visibleRect = mVisibleRegion.GetBounds();
if (!mTexture) { if (!mTexture) {
device()->CreateTexture(visibleRect.width, visibleRect.height, 1, device()->CreateTexture(visibleRect.width, visibleRect.height, 1,
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL);
mInvalidatedRect = visibleRect; mValidRegion.SetEmpty();
} }
if (!mInvalidatedRect.IsEmpty()) {
nsIntRegion region = mInvalidatedRect; if (!mValidRegion.IsEqual(mVisibleRegion)) {
nsIntRegion region;
region.Sub(mVisibleRegion, mValidRegion);
nsIntRect bounds = region.GetBounds();
gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32;; gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32;;
nsRefPtr<gfxASurface> destinationSurface; nsRefPtr<gfxASurface> destinationSurface;
nsRefPtr<gfxContext> context; nsRefPtr<gfxContext> context;
// XXX - Should optimize here to use IDirect3DSurface9::GetDC() for GDI
// rendering since we're always on windows. We may consider retaining a
// SYSTEMMEM texture texture the size of our DEFAULT texture and then use
// UpdateTexture and add dirty rects to update in a single call.
destinationSurface = destinationSurface =
gfxPlatform::GetPlatform()-> gfxPlatform::GetPlatform()->
CreateOffscreenSurface(gfxIntSize(mInvalidatedRect.width, CreateOffscreenSurface(gfxIntSize(bounds.width,
mInvalidatedRect.height), bounds.height),
imageFormat); imageFormat);
context = new gfxContext(destinationSurface); context = new gfxContext(destinationSurface);
context->Translate(gfxPoint(-mInvalidatedRect.x, -mInvalidatedRect.y)); context->Translate(gfxPoint(-bounds.x, -bounds.y));
LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
cbInfo.Callback(this, context, region, nsIntRegion(), cbInfo.CallbackData); cbInfo.Callback(this, context, region, nsIntRegion(), cbInfo.CallbackData);
nsRefPtr<IDirect3DTexture9> tmpTexture; nsRefPtr<IDirect3DTexture9> tmpTexture;
device()->CreateTexture(mInvalidatedRect.width, mInvalidatedRect.height, 1, device()->CreateTexture(bounds.width, bounds.height, 1,
0, D3DFMT_A8R8G8B8, 0, D3DFMT_A8R8G8B8,
D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL);
@ -118,8 +192,8 @@ ThebesLayerD3D9::RenderLayer()
nsRefPtr<gfxImageSurface> imgSurface = nsRefPtr<gfxImageSurface> imgSurface =
new gfxImageSurface((unsigned char *)r.pBits, new gfxImageSurface((unsigned char *)r.pBits,
gfxIntSize(mInvalidatedRect.width, gfxIntSize(bounds.width,
mInvalidatedRect.height), bounds.height),
r.Pitch, r.Pitch,
imageFormat); imageFormat);
@ -138,10 +212,20 @@ ThebesLayerD3D9::RenderLayer()
mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
POINT point; nsIntRegionRectIterator iter(region);
point.x = mInvalidatedRect.x - visibleRect.x; const nsIntRect *iterRect;
point.y = mInvalidatedRect.y - visibleRect.y; while ((iterRect = iter.Next())) {
device()->UpdateSurface(srcSurface, NULL, dstSurface, &point); RECT rect;
rect.left = iterRect->x - bounds.x;
rect.top = iterRect->y - bounds.y;
rect.right = rect.left + iterRect->width;
rect.bottom = rect.top + iterRect->height;
POINT point;
point.x = iterRect->x - visibleRect.x;
point.y = iterRect->y - visibleRect.y;
device()->UpdateSurface(srcSurface, &rect, dstSurface, &point);
}
mValidRegion = mVisibleRegion;
} }
float quadTransform[4][4]; float quadTransform[4][4];
@ -183,12 +267,6 @@ ThebesLayerD3D9::CleanResources()
mTexture = nsnull; mTexture = nsnull;
} }
const nsIntRect&
ThebesLayerD3D9::GetInvalidatedRect()
{
return mInvalidatedRect;
}
Layer* Layer*
ThebesLayerD3D9::GetLayer() ThebesLayerD3D9::GetLayer()
{ {

View File

@ -65,16 +65,7 @@ public:
virtual void RenderLayer(); virtual void RenderLayer();
virtual void CleanResources(); virtual void CleanResources();
/* ThebesLayerD3D9 */
nsIntRect GetVisibleRect() { return mVisibleRegion.GetBounds(); }
const nsIntRect &GetInvalidatedRect();
private: private:
/*
* Currently invalidated rectangular area.
*/
nsIntRect mInvalidatedRect;
/* /*
* D3D9 texture * D3D9 texture
*/ */