From bfb12d86f0b3f09506c0fc7cb8b074ea181f97ac Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 1 Apr 2011 10:33:46 +1300 Subject: [PATCH] Bug 635373. If we need to resample a ThebesLayer texture, make sure we make all the pixels of the texture valid in case they get sampled. r=bas --- gfx/layers/d3d10/ThebesLayerD3D10.cpp | 95 ++++++++++++++++----------- gfx/layers/d3d10/ThebesLayerD3D10.h | 7 +- gfx/layers/d3d9/ThebesLayerD3D9.cpp | 27 +++++++- 3 files changed, 86 insertions(+), 43 deletions(-) diff --git a/gfx/layers/d3d10/ThebesLayerD3D10.cpp b/gfx/layers/d3d10/ThebesLayerD3D10.cpp index 5c2ddd67aa9..d19f980601a 100644 --- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp +++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp @@ -54,6 +54,7 @@ namespace layers { ThebesLayerD3D10::ThebesLayerD3D10(LayerManagerD3D10 *aManager) : ThebesLayer(aManager, NULL) , LayerD3D10(aManager) + , mCurrentSurfaceMode(SURFACE_OPAQUE) { mImplData = static_cast(this); } @@ -119,20 +120,24 @@ ThebesLayerD3D10::RenderLayer() return; } - nsIntRect visibleRect = mVisibleRegion.GetBounds(); - SetEffectTransformAndOpacity(); ID3D10EffectTechnique *technique; - if (mTextureOnWhite) { + switch (mCurrentSurfaceMode) { + case SURFACE_COMPONENT_ALPHA: technique = effect()->GetTechniqueByName("RenderComponentAlphaLayer"); - } else if (CanUseOpaqueSurface()) { + break; + case SURFACE_OPAQUE: technique = effect()->GetTechniqueByName("RenderRGBLayerPremul"); - } else { + break; + case SURFACE_SINGLE_CHANNEL_ALPHA: technique = effect()->GetTechniqueByName("RenderRGBALayerPremul"); + break; + default: + NS_ERROR("Unknown mode"); + return; } - nsIntRegionRectIterator iter(mVisibleRegion); const nsIntRect *iterRect; @@ -154,10 +159,10 @@ ThebesLayerD3D10::RenderLayer() effect()->GetVariableByName("vTextureCoords")->AsVector()->SetFloatVector( ShaderConstantRectD3D10( - (float)(iterRect->x - visibleRect.x) / (float)visibleRect.width, - (float)(iterRect->y - visibleRect.y) / (float)visibleRect.height, - (float)iterRect->width / (float)visibleRect.width, - (float)iterRect->height / (float)visibleRect.height) + (float)(iterRect->x - mTextureRect.x) / (float)mTextureRect.width, + (float)(iterRect->y - mTextureRect.y) / (float)mTextureRect.height, + (float)iterRect->width / (float)mTextureRect.width, + (float)iterRect->height / (float)mTextureRect.height) ); technique->GetPassByIndex(0)->Apply(0); @@ -176,11 +181,34 @@ ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) return; } + nsIntRect newTextureRect = mVisibleRegion.GetBounds(); + SurfaceMode mode = GetSurfaceMode(); if (mode == SURFACE_COMPONENT_ALPHA && (!mParent || !mParent->SupportsComponentAlphaChildren())) { mode = SURFACE_SINGLE_CHANNEL_ALPHA; } + // If we have a transform that requires resampling of our texture, then + // we need to make sure we don't sample pixels that haven't been drawn. + // We clamp sample coordinates to the texture rect, but when the visible region + // doesn't fill the entire texture rect we need to make sure we draw all the + // pixels in the texture rect anyway in case they get sampled. + nsIntRegion neededRegion = mVisibleRegion; + if (neededRegion.GetBounds() != newTextureRect || + neededRegion.GetNumRects() > 1) { + gfxMatrix transform2d; + if (!GetEffectiveTransform().Is2D(&transform2d) || + transform2d.HasNonIntegerTranslation()) { + neededRegion = newTextureRect; + if (mode == SURFACE_OPAQUE) { + // We're going to paint outside the visible region, but layout hasn't + // promised that it will paint opaquely there, so we'll have to + // treat this layer as transparent. + mode = SURFACE_SINGLE_CHANNEL_ALPHA; + } + } + } + mCurrentSurfaceMode = mode; VerifyContentType(mode); @@ -199,27 +227,22 @@ ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion); } - nsIntRect visibleRect = mVisibleRegion.GetBounds(); - if (mTexture) { - if (!mTextureRegion.IsEqual(mVisibleRegion)) { + if (mTextureRect != newTextureRect) { nsRefPtr oldTexture = mTexture; mTexture = nsnull; nsRefPtr oldTextureOnWhite = mTextureOnWhite; mTextureOnWhite = nsnull; - nsIntRegion retainRegion = mTextureRegion; - nsIntRect oldBounds = mTextureRegion.GetBounds(); - nsIntRect newBounds = mVisibleRegion.GetBounds(); - - CreateNewTextures(gfxIntSize(newBounds.width, newBounds.height), mode); - + nsIntRegion retainRegion = mTextureRect; // Old visible region will become the region that is covered by both the // old and the new visible region. retainRegion.And(retainRegion, mVisibleRegion); // No point in retaining parts which were not valid. retainRegion.And(retainRegion, mValidRegion); + CreateNewTextures(gfxIntSize(newTextureRect.width, newTextureRect.height), mode); + nsIntRect largeRect = retainRegion.GetLargestRectangle(); // If we had no hardware texture before, have no retained area larger than @@ -234,47 +257,41 @@ ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) ResolutionChanged(xres, yres)) { mValidRegion.SetEmpty(); } else { - CopyRegion(oldTexture, oldBounds.TopLeft(), - mTexture, newBounds.TopLeft(), + CopyRegion(oldTexture, mTextureRect.TopLeft(), + mTexture, newTextureRect.TopLeft(), retainRegion, &mValidRegion, xres, yres); if (oldTextureOnWhite) { - CopyRegion(oldTextureOnWhite, oldBounds.TopLeft(), - mTextureOnWhite, newBounds.TopLeft(), + CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), + mTextureOnWhite, newTextureRect.TopLeft(), retainRegion, &mValidRegion, xres, yres); } } } } - mTextureRegion = mVisibleRegion; + mTextureRect = newTextureRect; if (!mTexture || (mode == SURFACE_COMPONENT_ALPHA && !mTextureOnWhite)) { - CreateNewTextures(gfxIntSize(visibleRect.width, visibleRect.height), mode); + CreateNewTextures(gfxIntSize(newTextureRect.width, newTextureRect.height), mode); mValidRegion.SetEmpty(); } - if (!mValidRegion.IsEqual(mVisibleRegion)) { + nsIntRegion drawRegion; + drawRegion.Sub(neededRegion, mValidRegion); + + if (!drawRegion.IsEmpty()) { LayerManagerD3D10::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); if (!cbInfo.Callback) { NS_ERROR("D3D10 should never need to update ThebesLayers in an empty transaction"); return; } - /* We use the bounds of the visible region because we draw the bounds of - * this region when we draw this entire texture. We have to make sure that - * the areas that aren't filled with content get their background drawn. - * This is an issue for opaque surfaces, which otherwise won't get their - * background painted. - */ - nsIntRegion region; - region.Sub(mVisibleRegion, mValidRegion); - - DrawRegion(region, mode); + DrawRegion(drawRegion, mode); if (readbackUpdates.Length() > 0) { CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - visibleRect.width, visibleRect.height, + newTextureRect.width, newTextureRect.height, 1, 1, 0, D3D10_USAGE_STAGING, D3D10_CPU_ACCESS_READ); @@ -285,11 +302,11 @@ ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) for (int i = 0; i < readbackUpdates.Length(); i++) { mD3DManager->readbackManager()->PostTask(readbackTexture, &readbackUpdates[i], - gfxPoint(visibleRect.x, visibleRect.y)); + gfxPoint(newTextureRect.x, newTextureRect.y)); } } - mValidRegion = mVisibleRegion; + mValidRegion = neededRegion; } } diff --git a/gfx/layers/d3d10/ThebesLayerD3D10.h b/gfx/layers/d3d10/ThebesLayerD3D10.h index 7fa2d6ea646..c101c04f4a0 100644 --- a/gfx/layers/d3d10/ThebesLayerD3D10.h +++ b/gfx/layers/d3d10/ThebesLayerD3D10.h @@ -75,8 +75,11 @@ private: /* Shader resource view for our render-on-white texture */ nsRefPtr mSRViewOnWhite; - /* Visible region used when we drew the contents of the textures */ - nsIntRegion mTextureRegion; + /* Area of layer currently stored in texture(s) */ + nsIntRect mTextureRect; + + /* Last surface mode set in Validate() */ + SurfaceMode mCurrentSurfaceMode; /* Checks if our D2D surface has the right content type */ void VerifyContentType(SurfaceMode aMode); diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index 4f189d560bf..7e35e07de36 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -208,11 +208,34 @@ ThebesLayerD3D9::RenderThebesLayer(ReadbackProcessor* aReadback) return; } + nsIntRect newTextureRect = mVisibleRegion.GetBounds(); + SurfaceMode mode = GetSurfaceMode(); if (mode == SURFACE_COMPONENT_ALPHA && (!mParent || !mParent->SupportsComponentAlphaChildren())) { mode = SURFACE_SINGLE_CHANNEL_ALPHA; } + // If we have a transform that requires resampling of our texture, then + // we need to make sure we don't sample pixels that haven't been drawn. + // We clamp sample coordinates to the texture rect, but when the visible region + // doesn't fill the entire texture rect we need to make sure we draw all the + // pixels in the texture rect anyway in case they get sampled. + nsIntRegion neededRegion = mVisibleRegion; + if (neededRegion.GetBounds() != newTextureRect || + neededRegion.GetNumRects() > 1) { + gfxMatrix transform2d; + if (!GetEffectiveTransform().Is2D(&transform2d) || + transform2d.HasNonIntegerTranslation()) { + neededRegion = newTextureRect; + if (mode == SURFACE_OPAQUE) { + // We're going to paint outside the visible region, but layout hasn't + // promised that it will paint opaquely there, so we'll have to + // treat this layer as transparent. + mode = SURFACE_SINGLE_CHANNEL_ALPHA; + } + } + } + VerifyContentType(mode); UpdateTextures(mode); if (!HaveTextures(mode)) { @@ -232,7 +255,7 @@ ThebesLayerD3D9::RenderThebesLayer(ReadbackProcessor* aReadback) // Then the readback areas we need can be copied out of the temporary // destinationSurface in DrawRegion. nsIntRegion drawRegion; - drawRegion.Sub(mVisibleRegion, mValidRegion); + drawRegion.Sub(neededRegion, mValidRegion); drawRegion.Or(drawRegion, readbackRegion); // NS_ASSERTION(mVisibleRegion.Contains(region), "Bad readback region!"); @@ -245,7 +268,7 @@ ThebesLayerD3D9::RenderThebesLayer(ReadbackProcessor* aReadback) DrawRegion(drawRegion, mode, readbackUpdates); - mValidRegion = mVisibleRegion; + mValidRegion = neededRegion; } SetShaderTransformAndOpacity();