Bug 584494: Avoid creating intermediate surfaces in D3D9 layers. r=roc a=blocking-betaN
authorBas Schouten <bschouten@mozilla.com>
Thu, 21 Oct 2010 22:41:04 +0200
changeset 56334 73f8c0079bc185475435f2d27d2650f461f1ffec
parent 56333 39c2e0d65ad14bf10276596497d55b56f58ecca7
child 56335 f4a06cca9fb95118a0565c5b58d2ee1f24f6b0f5
push id16487
push userbschouten@mozilla.com
push dateThu, 21 Oct 2010 20:42:12 +0000
treeherdermozilla-central@f4a06cca9fb9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, blocking-betaN
bugs584494
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 584494: Avoid creating intermediate surfaces in D3D9 layers. r=roc a=blocking-betaN
gfx/layers/d3d9/CanvasLayerD3D9.cpp
gfx/layers/d3d9/CanvasLayerD3D9.h
gfx/layers/d3d9/ColorLayerD3D9.cpp
gfx/layers/d3d9/ColorLayerD3D9.h
gfx/layers/d3d9/ContainerLayerD3D9.cpp
gfx/layers/d3d9/ContainerLayerD3D9.h
gfx/layers/d3d9/ImageLayerD3D9.cpp
gfx/layers/d3d9/ImageLayerD3D9.h
gfx/layers/d3d9/LayerManagerD3D9.cpp
gfx/layers/d3d9/LayerManagerD3D9.h
gfx/layers/d3d9/ThebesLayerD3D9.cpp
gfx/layers/d3d9/ThebesLayerD3D9.h
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -222,17 +222,17 @@ CanvasLayerD3D9::Updated(const nsIntRect
 
 Layer*
 CanvasLayerD3D9::GetLayer()
 {
   return this;
 }
 
 void
-CanvasLayerD3D9::RenderLayer()
+CanvasLayerD3D9::RenderLayer(float aOpacity, const gfx3DMatrix &aTransform)
 {
   if (!mTexture) {
     Updated(mBounds);
   }
 
   /*
    * We flip the Y axis here, note we can only do this because we are in 
    * CULL_NONE mode!
@@ -241,17 +241,18 @@ CanvasLayerD3D9::RenderLayer()
   ShaderConstantRect quad(0, 0, mBounds.width, mBounds.height);
   if (mNeedsYFlip) {
     quad.mHeight = (float)-mBounds.height;
     quad.mY = (float)mBounds.height;
   }
 
   device()->SetVertexShaderConstantF(CBvLayerQuad, quad, 1);
 
-  device()->SetVertexShaderConstantF(CBmLayerTransform, &mTransform._11, 4);
+  gfx3DMatrix transform = mTransform * aTransform;
+  device()->SetVertexShaderConstantF(CBmLayerTransform, &transform._11, 4);
 
   float opacity[4];
   /*
    * We always upload a 4 component float, but the shader will use only the
    * first component since it's declared as a 'float'.
    */
   opacity[0] = GetOpacity();
   device()->SetPixelShaderConstantF(CBfLayerOpacity, opacity, 1);
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -65,17 +65,17 @@ public:
   ~CanvasLayerD3D9();
 
   // CanvasLayer implementation
   virtual void Initialize(const Data& aData);
   virtual void Updated(const nsIntRect& aRect);
 
   // LayerD3D9 implementation
   virtual Layer* GetLayer();
-  virtual void RenderLayer();
+  virtual void RenderLayer(float aOpacity, const gfx3DMatrix &aTransform);
   virtual void CleanResources();
   virtual void LayerManagerDestroyed();
 
   void CreateTexture();
 
 protected:
   typedef mozilla::gl::GLContext GLContext;
 
--- a/gfx/layers/d3d9/ColorLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ColorLayerD3D9.cpp
@@ -43,39 +43,40 @@ namespace layers {
 
 Layer*
 ColorLayerD3D9::GetLayer()
 {
   return this;
 }
 
 void
-ColorLayerD3D9::RenderLayer()
+ColorLayerD3D9::RenderLayer(float aOpacity, const gfx3DMatrix &aTransform)
 {
   // XXX we might be able to improve performance by using
   // IDirect3DDevice9::Clear
 
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
   device()->SetVertexShaderConstantF(
     CBvLayerQuad,
     ShaderConstantRect(visibleRect.x,
                        visibleRect.y,
                        visibleRect.width,
                        visibleRect.height),
     1);
 
-  device()->SetVertexShaderConstantF(CBmLayerTransform, &mTransform._11, 4);
+  gfx3DMatrix transform = mTransform * aTransform;
+  device()->SetVertexShaderConstantF(CBmLayerTransform, &transform._11, 4);
 
   float color[4];
   // color is premultiplied, so we need to adjust all channels
-  color[0] = (float)(mColor.r * GetOpacity());
-  color[1] = (float)(mColor.g * GetOpacity());
-  color[2] = (float)(mColor.b * GetOpacity());
-  color[3] = (float)(mColor.a * GetOpacity());
+  color[0] = (float)(mColor.r * GetOpacity() * aOpacity);
+  color[1] = (float)(mColor.g * GetOpacity() * aOpacity);
+  color[2] = (float)(mColor.b * GetOpacity() * aOpacity);
+  color[3] = (float)(mColor.a * GetOpacity() * aOpacity);
 
   device()->SetPixelShaderConstantF(0, color, 1);
 
   mD3DManager->SetShaderMode(DeviceManagerD3D9::SOLIDCOLORLAYER);
 
   device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
 }
 
--- a/gfx/layers/d3d9/ColorLayerD3D9.h
+++ b/gfx/layers/d3d9/ColorLayerD3D9.h
@@ -53,15 +53,15 @@ public:
     , LayerD3D9(aManager)
   {
     mImplData = static_cast<LayerD3D9*>(this);
   }
 
   // LayerD3D9 Implementation
   virtual Layer* GetLayer();
 
-  virtual void RenderLayer();
+  virtual void RenderLayer(float aOpacity, const gfx3DMatrix &aTransform);
 };
 
 } /* layers */
 } /* mozilla */
 
 #endif /* GFX_COLORLAYERD3D9_H */
--- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp
@@ -130,32 +130,33 @@ ContainerLayerD3D9::GetFirstChildD3D9()
 {
   if (!mFirstChild) {
     return nsnull;
   }
   return static_cast<LayerD3D9*>(mFirstChild->ImplData());
 }
 
 void
-ContainerLayerD3D9::RenderLayer()
+ContainerLayerD3D9::RenderLayer(float aOpacity, const gfx3DMatrix &aTransform)
 {
-  float opacity = GetOpacity();
+  float opacity = GetOpacity() * aOpacity;
   nsRefPtr<IDirect3DSurface9> previousRenderTarget;
   nsRefPtr<IDirect3DTexture9> renderTexture;
   float previousRenderTargetOffset[4];
   RECT oldClipRect;
   float renderTargetOffset[] = { 0, 0, 0, 0 };
   float oldViewMatrix[4][4];
 
+  gfx3DMatrix transform = mTransform * aTransform;
+
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
-  PRBool useIntermediate = (opacity != 1.0 || !mTransform.IsIdentity());
+  PRBool useIntermediate = ShouldUseIntermediate(opacity, transform);
 
   if (useIntermediate) {
     device()->GetRenderTarget(0, getter_AddRefs(previousRenderTarget));
-    device()->GetScissorRect(&oldClipRect);
     device()->CreateTexture(visibleRect.width, visibleRect.height, 1,
                             D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
                             D3DPOOL_DEFAULT, getter_AddRefs(renderTexture),
                             NULL);
     nsRefPtr<IDirect3DSurface9> renderSurface;
     renderTexture->GetSurfaceLevel(0, getter_AddRefs(renderSurface));
     device()->SetRenderTarget(0, renderSurface);
     device()->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0);
@@ -179,68 +180,83 @@ ContainerLayerD3D9::RenderLayer()
   }
 
   /*
    * Render this container's contents.
    */
   LayerD3D9 *layerToRender = GetFirstChildD3D9();
   while (layerToRender) {
     const nsIntRect *clipRect = layerToRender->GetLayer()->GetClipRect();
-    RECT r;
-    if (clipRect) {
-      r.left = (LONG)(clipRect->x - renderTargetOffset[0]);
-      r.top = (LONG)(clipRect->y - renderTargetOffset[1]);
-      r.right = (LONG)(clipRect->x - renderTargetOffset[0] + clipRect->width);
-      r.bottom = (LONG)(clipRect->y - renderTargetOffset[1] + clipRect->height);
-    } else {
-      if (useIntermediate) {
+    if (clipRect || useIntermediate) {
+      RECT r;
+      device()->GetScissorRect(&oldClipRect);
+      if (clipRect) {
+        r.left = (LONG)(clipRect->x - renderTargetOffset[0]);
+        r.top = (LONG)(clipRect->y - renderTargetOffset[1]);
+        r.right = (LONG)(clipRect->x - renderTargetOffset[0] + clipRect->width);
+        r.bottom = (LONG)(clipRect->y - renderTargetOffset[1] + clipRect->height);
+      } else {
         r.left = 0;
         r.top = 0;
+        r.right = visibleRect.width;
+        r.bottom = visibleRect.height;
+      }
+
+      nsRefPtr<IDirect3DSurface9> renderSurface;
+      device()->GetRenderTarget(0, getter_AddRefs(renderSurface));
+
+      D3DSURFACE_DESC desc;
+      renderSurface->GetDesc(&desc);
+
+      if (!useIntermediate) {
+        // Intersect with current clip rect.
+        r.left = NS_MAX<PRInt32>(oldClipRect.left, r.left);
+        r.right = NS_MIN<PRInt32>(oldClipRect.right, r.right);
+        r.top = NS_MAX<PRInt32>(oldClipRect.top, r.top);
+        r.bottom = NS_MAX<PRInt32>(oldClipRect.bottom, r.bottom);
       } else {
-        r.left = visibleRect.x;
-        r.top = visibleRect.y;
+        // > 0 is implied during the intersection when useIntermediate == true;
+        r.left = NS_MAX<LONG>(0, r.left);
+        r.top = NS_MAX<LONG>(0, r.top);
       }
-      r.right = r.left + visibleRect.width;
-      r.bottom = r.top + visibleRect.height;
+      r.bottom = NS_MIN<LONG>(r.bottom, desc.Height);
+      r.right = NS_MIN<LONG>(r.right, desc.Width);
+
+      device()->SetScissorRect(&r);
     }
 
-    nsRefPtr<IDirect3DSurface9> renderSurface;
-    device()->GetRenderTarget(0, getter_AddRefs(renderSurface));
-
-    D3DSURFACE_DESC desc;
-    renderSurface->GetDesc(&desc);
+    if (!useIntermediate) {
+      layerToRender->RenderLayer(opacity, transform);
+    } else {
+      layerToRender->RenderLayer(1.0, gfx3DMatrix());
+    }
 
-    r.left = NS_MAX<LONG>(0, r.left);
-    r.top = NS_MAX<LONG>(0, r.top);
-    r.bottom = NS_MIN<LONG>(r.bottom, desc.Height);
-    r.right = NS_MIN<LONG>(r.right, desc.Width);
+    if (clipRect || useIntermediate) {
+      device()->SetScissorRect(&oldClipRect);
+    }
 
-    device()->SetScissorRect(&r);
-
-    layerToRender->RenderLayer();
     Layer *nextSibling = layerToRender->GetLayer()->GetNextSibling();
     layerToRender = nextSibling ? static_cast<LayerD3D9*>(nextSibling->
                                                           ImplData())
                                 : nsnull;
   }
 
   if (useIntermediate) {
     device()->SetRenderTarget(0, previousRenderTarget);
-    device()->SetScissorRect(&oldClipRect);
     device()->SetVertexShaderConstantF(CBvRenderTargetOffset, previousRenderTargetOffset, 1);
     device()->SetVertexShaderConstantF(CBmProjection, &oldViewMatrix[0][0], 4);
 
     device()->SetVertexShaderConstantF(CBvLayerQuad,
                                        ShaderConstantRect(visibleRect.x,
                                                           visibleRect.y,
                                                           visibleRect.width,
                                                           visibleRect.height),
                                        1);
 
-    device()->SetVertexShaderConstantF(CBmLayerTransform, &mTransform._11, 4);
+    device()->SetVertexShaderConstantF(CBmLayerTransform, &transform._11, 4);
 
     float opacityVector[4];
     /*
      * We always upload a 4 component float, but the shader will use only the
      * first component since it's declared as a 'float'.
      */
     opacityVector[0] = opacity;
     device()->SetPixelShaderConstantF(CBfLayerOpacity, opacityVector, 1);
@@ -256,10 +272,37 @@ void
 ContainerLayerD3D9::LayerManagerDestroyed()
 {
   while (mFirstChild) {
     GetFirstChildD3D9()->LayerManagerDestroyed();
     RemoveChild(mFirstChild);
   }
 }
 
+bool
+ContainerLayerD3D9::ShouldUseIntermediate(float aOpacity,
+                                          const gfx3DMatrix &aMatrix)
+{
+  if (aOpacity == 1.0f && aMatrix.IsIdentity()) {
+    return false;
+  }
+
+  Layer *firstChild = GetFirstChild();
+
+  if (!firstChild || (!firstChild->GetNextSibling() &&
+      !firstChild->GetClipRect())) {
+    // If we forward our transform to a child without using an intermediate,
+    // we need to be sure that child does not have a clip rect, since its clip
+    // rect would be applied after our transform.
+    return false;
+  }
+
+  if (aMatrix.IsIdentity() && (!firstChild || !firstChild->GetNextSibling())) {
+    // If there's no transforms applied and a single child, opacity can always
+    // be forwarded to our only child.
+    return false;
+  }
+
+  return true;
+}
+
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/d3d9/ContainerLayerD3D9.h
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.h
@@ -60,17 +60,21 @@ public:
 
   /* LayerD3D9 implementation */
   Layer* GetLayer();
 
   LayerD3D9* GetFirstChildD3D9();
 
   PRBool IsEmpty();
 
-  void RenderLayer();
+  void RenderLayer(float aOpacity, const gfx3DMatrix &aTransform);
 
   virtual void LayerManagerDestroyed();
+
+private:
+  bool ShouldUseIntermediate(float aOpacity,
+                             const gfx3DMatrix &aMatrix);
 };
 
 } /* layers */
 } /* mozilla */
 
 #endif /* GFX_CONTAINERLAYERD3D9_H */
--- a/gfx/layers/d3d9/ImageLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp
@@ -143,17 +143,17 @@ ImageContainerD3D9::SetLayerManager(Laye
 
 Layer*
 ImageLayerD3D9::GetLayer()
 {
   return this;
 }
 
 void
-ImageLayerD3D9::RenderLayer()
+ImageLayerD3D9::RenderLayer(float aOpacity, const gfx3DMatrix &aTransform)
 {
   if (!GetContainer()) {
     return;
   }
 
   nsRefPtr<Image> image = GetContainer()->GetCurrentImage();
 
   if (image->GetFormat() == Image::PLANAR_YCBCR) {
@@ -167,24 +167,25 @@ ImageLayerD3D9::RenderLayer()
 
     device()->SetVertexShaderConstantF(CBvLayerQuad,
                                        ShaderConstantRect(0,
                                                           0,
                                                           yuvImage->mSize.width,
                                                           yuvImage->mSize.height),
                                        1);
 
-    device()->SetVertexShaderConstantF(CBmLayerTransform, &mTransform._11, 4);
+    gfx3DMatrix transform = mTransform * aTransform;
+    device()->SetVertexShaderConstantF(CBmLayerTransform, &transform._11, 4);
 
     float opacity[4];
     /*
      * We always upload a 4 component float, but the shader will
      * only use the the first component since it's declared as a 'float'.
      */
-    opacity[0] = GetOpacity();
+    opacity[0] = GetOpacity() * aOpacity;
     device()->SetPixelShaderConstantF(CBfLayerOpacity, opacity, 1);
 
     mD3DManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER);
 
     /* 
      * Send 3d control data and metadata 
      */ 
     if (mD3DManager->Is3DEnabled() && mD3DManager->GetNv3DVUtils()) { 
@@ -215,24 +216,25 @@ ImageLayerD3D9::RenderLayer()
 
     device()->SetVertexShaderConstantF(CBvLayerQuad,
                                        ShaderConstantRect(0,
                                                           0,
                                                           cairoImage->mSize.width,
                                                           cairoImage->mSize.height),
                                        1);
 
-    device()->SetVertexShaderConstantF(CBmLayerTransform, &mTransform._11, 4);
+    gfx3DMatrix transform = mTransform * aTransform;
+    device()->SetVertexShaderConstantF(CBmLayerTransform, &transform._11, 4);
 
     float opacity[4];
     /*
      * We always upload a 4 component float, but the shader will
      * only use the the first component since it's declared as a 'float'.
      */
-    opacity[0] = GetOpacity();
+    opacity[0] = GetOpacity() * aOpacity;
     device()->SetPixelShaderConstantF(CBfLayerOpacity, opacity, 1);
 
     mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
 
     device()->SetTexture(0, cairoImage->mTexture);
     device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
   }
 }
--- a/gfx/layers/d3d9/ImageLayerD3D9.h
+++ b/gfx/layers/d3d9/ImageLayerD3D9.h
@@ -82,17 +82,17 @@ public:
     , LayerD3D9(aManager)
   {
     mImplData = static_cast<LayerD3D9*>(this);
   }
 
   // LayerD3D9 Implementation
   virtual Layer* GetLayer();
 
-  virtual void RenderLayer();
+  virtual void RenderLayer(float aOpacity, const gfx3DMatrix &aTransform);
 };
 
 class THEBES_API ImageD3D9
 {
 public:
   virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
 };
 
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -293,17 +293,17 @@ LayerManagerD3D9::Render()
       r.bottom = (LONG)(clipRect->y + clipRect->height);
     } else {
       r.left = r.top = 0;
       r.right = rect.width;
       r.bottom = rect.height;
     }
     device()->SetScissorRect(&r);
 
-    static_cast<LayerD3D9*>(mRoot->ImplData())->RenderLayer();
+    static_cast<LayerD3D9*>(mRoot->ImplData())->RenderLayer(1.0, gfx3DMatrix());
   }
 
   device()->EndScene();
 
   if (!mTarget) {
     const nsIntRect *r;
     for (nsIntRegionRectIterator iter(mClippingRegion);
          (r = iter.Next()) != nsnull;) {
--- a/gfx/layers/d3d9/LayerManagerD3D9.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9.h
@@ -246,17 +246,17 @@ public:
   LayerD3D9(LayerManagerD3D9 *aManager);
 
   virtual LayerD3D9 *GetFirstChildD3D9() { return nsnull; }
 
   void SetFirstChild(LayerD3D9 *aParent);
 
   virtual Layer* GetLayer() = 0;
 
-  virtual void RenderLayer() = 0;
+  virtual void RenderLayer(float aOpacity, const gfx3DMatrix &aTransform) = 0;
 
   /* This function may be used on device resets to clear all VRAM resources
    * that a layer might be using.
    */
   virtual void CleanResources() {}
 
   IDirect3DDevice9 *device() const { return mD3DManager->device(); }
 
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -167,17 +167,17 @@ ThebesLayerD3D9::SetVisibleRegion(const 
 
 void
 ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion)
 {
   mValidRegion.Sub(mValidRegion, aRegion);
 }
 
 void
-ThebesLayerD3D9::RenderLayer()
+ThebesLayerD3D9::RenderLayer(float aOpacity, const gfx3DMatrix &aTransform)
 {
   if (mVisibleRegion.IsEmpty()) {
     return;
   }
 
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
   // We differentiate between these formats since D3D9 will only allow us to
@@ -220,24 +220,25 @@ ThebesLayerD3D9::RenderLayer()
     nsIntRegion region;
     region.Sub(mVisibleRegion, mValidRegion);
 
     DrawRegion(region);
 
     mValidRegion = mVisibleRegion;
   }
 
-  device()->SetVertexShaderConstantF(CBmLayerTransform, &mTransform._11, 4);
+  gfx3DMatrix transform = mTransform * aTransform;
+  device()->SetVertexShaderConstantF(CBmLayerTransform, &transform._11, 4);
 
   float opacity[4];
   /*
    * We always upload a 4 component float, but the shader will use only the
    * first component since it's declared as a 'float'.
    */
-  opacity[0] = GetOpacity();
+  opacity[0] = GetOpacity() * aOpacity;
   device()->SetPixelShaderConstantF(0, opacity, 1);
 
 #ifdef CAIRO_HAS_D2D_SURFACE
   if (mD2DSurface && CanUseOpaqueSurface()) {
     mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER);
   } else
 #endif
   mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
--- a/gfx/layers/d3d9/ThebesLayerD3D9.h
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.h
@@ -56,17 +56,17 @@ public:
   void SetVisibleRegion(const nsIntRegion& aRegion);
 
   /* ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
   /* LayerD3D9 implementation */
   Layer* GetLayer();
   virtual PRBool IsEmpty();
-  virtual void RenderLayer();
+  virtual void RenderLayer(float aOpacity, const gfx3DMatrix &aTransform);
   virtual void CleanResources();
   virtual void LayerManagerDestroyed();
 
 private:
   /*
    * D3D9 texture
    */
   nsRefPtr<IDirect3DTexture9> mTexture;