Bug 1274673 - Use binary space partitioning for sorting/drawing layers - Part 3: Use BSPTree for layer sorting draft
authorMiko Mynttinen <mikokm@gmail.com>
Tue, 06 Dec 2016 13:39:01 -1000
changeset 448579 ecc158cd61b6aea2f854fbdc59707b1bcaf265c4
parent 447647 18ccb6d417b73dbf7e72054a03cbaf4d90d05273
child 539310 86118a9caffbcf2dcf29f520776ee0633314cb7a
push id38365
push userbmo:mikokm@gmail.com
push dateSun, 11 Dec 2016 01:12:23 +0000
bugs1274673
milestone53.0a1
Bug 1274673 - Use binary space partitioning for sorting/drawing layers - Part 3: Use BSPTree for layer sorting MozReview-Commit-ID: 3Hy4IRDFgaP
gfx/layers/Compositor.cpp
gfx/layers/Compositor.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicLayerManager.cpp
gfx/layers/client/ClientContainerLayer.h
gfx/layers/composite/CanvasLayerComposite.cpp
gfx/layers/composite/CanvasLayerComposite.h
gfx/layers/composite/ColorLayerComposite.cpp
gfx/layers/composite/ColorLayerComposite.h
gfx/layers/composite/CompositableHost.h
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/composite/ContainerLayerComposite.h
gfx/layers/composite/ContentHost.cpp
gfx/layers/composite/ContentHost.h
gfx/layers/composite/ImageHost.cpp
gfx/layers/composite/ImageHost.h
gfx/layers/composite/ImageLayerComposite.cpp
gfx/layers/composite/ImageLayerComposite.h
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/composite/PaintedLayerComposite.cpp
gfx/layers/composite/PaintedLayerComposite.h
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/composite/TiledContentHost.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
gfx/thebes/gfxPrefs.h
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -265,17 +265,17 @@ void
 Compositor::DrawGeometry(const gfx::Rect& aRect,
                          const gfx::IntRect& aClipRect,
                          const EffectChain& aEffectChain,
                          gfx::Float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Rect& aVisibleRect,
                          const Maybe<gfx::Polygon>& aGeometry)
 {
-  if (!aGeometry) {
+  if (!aGeometry || !SupportsLayerGeometry()) {
     DrawQuad(aRect, aClipRect, aEffectChain,
              aOpacity, aTransform, aVisibleRect);
     return;
   }
 
   // Cull invisible polygons.
   if (aRect.Intersect(aGeometry->BoundingBox()).IsEmpty()) {
     return;
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -354,16 +354,21 @@ public:
                             const EffectChain& aEffectChain,
                             gfx::Float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Rect& aVisibleRect)
   {
     MOZ_CRASH("Compositor::DrawTriangle is not implemented for the current platform!");
   }
 
+  virtual bool SupportsLayerGeometry() const
+  {
+    return false;
+  }
+
   /**
    * Draw an unfilled solid color rect. Typically used for debugging overlays.
    */
   void SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& color,
                 const gfx::IntRect& aClipRect = gfx::IntRect(),
                 const gfx::Matrix4x4& aTransform = gfx::Matrix4x4(),
                 int aStrokeWidth = 1);
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -23,17 +23,19 @@
 #include "gfx2DGlue.h"
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/Telemetry.h"          // for Accumulate
 #include "mozilla/ToString.h"
 #include "mozilla/dom/Animation.h"      // for ComputedTimingFunction
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/BSPTree.h"     // for BSPTree
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayerAnimationUtils.h"  // for TimingFunctionToComputedTimingFunction
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "mozilla/layers/LayersTypes.h"  // for TextureDumpMode
@@ -42,16 +44,19 @@
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsStyleStruct.h"              // for nsTimingFunction, etc
 #include "protobuf/LayerScopePacket.pb.h"
 #include "mozilla/Compression.h"
 #include "TreeTraversal.h"              // for ForEachNode
 
+#include <deque>
+#include <set>
+
 uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 namespace layers {
 
 FILE*
 FILEOrDefault(FILE* aFile)
 {
@@ -1336,44 +1341,132 @@ ContainerLayer::Collect3DContextLeaves(n
         else {
           aToSort.AppendElement(layer);
           return TraversalFlag::Skip;
         }
       }
   );
 }
 
-void
-ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
+static nsTArray<LayerPolygon>
+SortLayersWithBSPTree(nsTArray<Layer*>& aArray)
+{
+  std::deque<LayerPolygon> inputLayers;
+  nsTArray<LayerPolygon> orderedLayers;
+
+  // Build a list of polygons to be sorted.
+  for (Layer* layer : aArray) {
+    // Ignore invisible layers.
+    if (!layer->IsVisible()) {
+      continue;
+    }
+
+    const gfx::IntRect& bounds =
+      layer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+    const gfx::Matrix4x4& transform = layer->GetEffectiveTransform();
+
+    gfx::Polygon polygon = gfx::Polygon::FromRect(gfx::Rect(bounds));
+
+    // Transform the polygon to screen space.
+    polygon.TransformToScreenSpace(transform);
+
+    if (polygon.GetPoints().Length() >= 3) {
+      inputLayers.push_back(LayerPolygon(layer, Move(polygon)));
+    }
+  }
+
+  if (inputLayers.empty()) {
+    return orderedLayers;
+  }
+
+  // Build a BSP tree from the list of polygons.
+  BSPTree tree(inputLayers);
+  orderedLayers = Move(tree.GetDrawOrder());
+
+  // Transform the polygons back to layer space.
+  for (LayerPolygon& layerPolygon : orderedLayers) {
+    gfx::Matrix4x4 inverse =
+      layerPolygon.layer->GetEffectiveTransform().Inverse();
+
+    MOZ_ASSERT(layerPolygon.geometry);
+    layerPolygon.geometry->TransformToLayerSpace(inverse);
+  }
+
+  return orderedLayers;
+}
+
+static nsTArray<LayerPolygon>
+StripLayerGeometry(const nsTArray<LayerPolygon>& aLayers)
+{
+  nsTArray<LayerPolygon> layers;
+  std::set<Layer*> uniqueLayers;
+
+  for (const LayerPolygon& layerPolygon : aLayers) {
+    auto result = uniqueLayers.insert(layerPolygon.layer);
+
+    if (result.second) {
+      // Layer was added to the set.
+      layers.AppendElement(LayerPolygon(layerPolygon.layer));
+    }
+  }
+
+  return layers;
+}
+
+nsTArray<LayerPolygon>
+ContainerLayer::SortChildrenBy3DZOrder(SortMode aSortMode)
 {
   AutoTArray<Layer*, 10> toSort;
+  nsTArray<LayerPolygon> drawOrder;
 
-  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
-    ContainerLayer* container = l->AsContainerLayer();
+  for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
+    ContainerLayer* container = layer->AsContainerLayer();
+
     if (container && container->Extend3DContext() &&
         !container->UseIntermediateSurface()) {
+
+      // Collect 3D layers in toSort array.
       container->Collect3DContextLeaves(toSort);
-    } else {
+
+      // Sort the 3D layers.
       if (toSort.Length() > 0) {
-        SortLayersBy3DZOrder(toSort);
-        aArray.AppendElements(Move(toSort));
-        // XXX The move analysis gets confused here, because toSort gets moved
-        // here, and then gets used again outside of the loop. To clarify that
-        // we realize that the array is going to be empty to the move checker,
-        // we clear it again here. (This method renews toSort for the move
-        // analysis)
+        nsTArray<LayerPolygon> sorted = SortLayersWithBSPTree(toSort);
+        drawOrder.AppendElements(Move(sorted));
+
         toSort.ClearAndRetainStorage();
       }
-      aArray.AppendElement(l);
+
+      continue;
     }
+
+    drawOrder.AppendElement(LayerPolygon(layer));
+  }
+
+  if (aSortMode == SortMode::WITHOUT_GEOMETRY) {
+    // Compositor does not support arbitrary layers, strip the layer geometry
+    // and duplicate layers.
+    return StripLayerGeometry(drawOrder);
   }
-  if (toSort.Length() > 0) {
-    SortLayersBy3DZOrder(toSort);
-    aArray.AppendElements(Move(toSort));
+
+  return drawOrder;
+}
+
+bool
+ContainerLayer::AnyAncestorOrThisIs3DContextLeaf()
+{
+  Layer* parent = this;
+  while (parent != nullptr) {
+    if (parent->Is3DContextLeaf()) {
+      return true;
+    }
+
+    parent = parent->GetParent();
   }
+
+  return false;
 }
 
 void
 ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
 {
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
 
@@ -1396,17 +1489,18 @@ ContainerLayer::DefaultComputeEffectiveT
      * WebKit/blink behaviour, but is changing in the latest spec.
      */
     float opacity = GetEffectiveOpacity();
     CompositionOp blendMode = GetEffectiveMixBlendMode();
     if ((HasMultipleChildren() || Creates3DContextWithExtendingChildren()) &&
         ((opacity != 1.0f && !Extend3DContext()) ||
          (blendMode != CompositionOp::OP_OVER))) {
       useIntermediateSurface = true;
-    } else if (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren()) {
+    } else if ((!idealTransform.Is2D() || AnyAncestorOrThisIs3DContextLeaf()) &&
+               Creates3DContextWithExtendingChildren()) {
       useIntermediateSurface = true;
     } else {
       useIntermediateSurface = false;
       gfx::Matrix contTransform;
       bool checkClipRect = false;
       bool checkMaskLayers = false;
 
       if (!idealTransform.Is2D(&contTransform)) {
@@ -1708,17 +1802,17 @@ void WriteSnapshotToDumpFile(Compositor*
   RefPtr<SourceSurface> surf = aTarget->Snapshot();
   RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface();
   WriteSnapshotToDumpFile_internal(aCompositor, dSurf);
 }
 #endif
 
 void
 Layer::Dump(std::stringstream& aStream, const char* aPrefix,
-            bool aDumpHtml, bool aSorted)
+            bool aDumpHtml, bool aSorted, const Maybe<gfx::Polygon>& aGeometry)
 {
 #ifdef MOZ_DUMP_PAINTING
   bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsHostLayer() &&
                                AsHostLayer()->GetCompositableHost();
   bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
                            AsShadowableLayer()->GetCompositableClient();
   nsCString layerId(Name());
   layerId.Append('-');
@@ -1728,17 +1822,17 @@ Layer::Dump(std::stringstream& aStream, 
     aStream << nsPrintfCString(R"(<li><a id="%p" )", this).get();
 #ifdef MOZ_DUMP_PAINTING
     if (dumpCompositorTexture || dumpClientTexture) {
       aStream << nsPrintfCString(R"lit(href="javascript:ViewImage('%s')")lit", layerId.BeginReading()).get();
     }
 #endif
     aStream << ">";
   }
-  DumpSelf(aStream, aPrefix);
+  DumpSelf(aStream, aPrefix, aGeometry);
 
 #ifdef MOZ_DUMP_PAINTING
   if (dumpCompositorTexture) {
     AsHostLayer()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
   } else if (dumpClientTexture) {
     if (aDumpHtml) {
       aStream << nsPrintfCString(R"(<script>array["%s"]=")", layerId.BeginReading()).get();
     }
@@ -1776,48 +1870,70 @@ Layer::Dump(std::stringstream& aStream, 
 #ifdef MOZ_DUMP_PAINTING
   for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) {
     const nsCString& str = mExtraDumpInfo[i];
     aStream << aPrefix << "  Info:\n" << str.get();
   }
 #endif
 
   if (ContainerLayer* container = AsContainerLayer()) {
-    AutoTArray<Layer*, 12> children;
+    nsTArray<LayerPolygon> children;
     if (aSorted) {
-      container->SortChildrenBy3DZOrder(children);
+      children =
+        container->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITH_GEOMETRY);
     } else {
       for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
-        children.AppendElement(l);
+        children.AppendElement(LayerPolygon(l));
       }
     }
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
     if (aDumpHtml) {
       aStream << "<ul>";
     }
 
-    for (Layer* child : children) {
-      child->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
+    for (LayerPolygon& child : children) {
+      child.layer->Dump(aStream, pfx.get(), aDumpHtml, aSorted, child.geometry);
     }
 
     if (aDumpHtml) {
       aStream << "</ul>";
     }
   }
 
   if (aDumpHtml) {
     aStream << "</li>";
   }
 }
 
+static void
+DumpGeometry(std::stringstream& aStream, const Maybe<gfx::Polygon>& aGeometry)
+{
+  aStream << " [geometry=[";
+
+  const nsTArray<gfx::Point4D>& points = aGeometry->GetPoints();
+  for (size_t i = 0; i < points.Length(); ++i) {
+    const gfx::IntPoint point = TruncatedToInt(points[i].As2DPoint());
+    const char* sfx = (i != points.Length() - 1) ? "," : "";
+    AppendToString(aStream, point, "", sfx);
+  }
+
+  aStream << "]]";
+}
+
 void
-Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix)
+Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix,
+                const Maybe<gfx::Polygon>& aGeometry)
 {
   PrintInfo(aStream, aPrefix);
+
+  if (aGeometry) {
+    DumpGeometry(aStream, aGeometry);
+  }
+
   aStream << "\n";
 }
 
 void
 Layer::Dump(layerscope::LayersPacket* aPacket, const void* aParent)
 {
   DumpPacket(aPacket, aParent);
 
@@ -2388,17 +2504,17 @@ LayerManager::Dump(std::stringstream& aS
       aStream << "</li></ul>";
     }
     return;
   }
 
   if (aDumpHtml) {
     aStream << "<ul>";
   }
-  GetRoot()->Dump(aStream, pfx.get(), aDumpHtml);
+  GetRoot()->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
   if (aDumpHtml) {
     aStream << "</ul></li></ul>";
   }
   aStream << "\n";
 }
 
 void
 LayerManager::DumpSelf(std::stringstream& aStream, const char* aPrefix, bool aSorted)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -28,16 +28,17 @@
 #include "mozilla/TimeStamp.h"          // for TimeStamp, TimeDuration
 #include "mozilla/UniquePtr.h"          // for UniquePtr
 #include "mozilla/gfx/BaseMargin.h"     // for BaseMargin
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/TiledRegion.h"    // for TiledIntRegion
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/gfx/UserData.h"       // for UserData, etc
+#include "mozilla/layers/BSPTree.h"     // for LayerPolygon
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsAutoPtr, nsRefPtr, etc
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsCSSPropertyID.h"              // for nsCSSPropertyID
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
@@ -1686,21 +1687,23 @@ public:
   void SetNextSibling(Layer* aSibling) { mNextSibling = aSibling; }
   void SetPrevSibling(Layer* aSibling) { mPrevSibling = aSibling; }
 
   /**
    * Dump information about this layer manager and its managed tree to
    * aStream.
    */
   void Dump(std::stringstream& aStream, const char* aPrefix="",
-            bool aDumpHtml=false, bool aSorted=false);
+            bool aDumpHtml=false, bool aSorted=false,
+            const Maybe<gfx::Polygon>& aGeometry=Nothing());
   /**
    * Dump information about just this layer manager itself to aStream.
    */
-  void DumpSelf(std::stringstream& aStream, const char* aPrefix="");
+  void DumpSelf(std::stringstream& aStream, const char* aPrefix="",
+                const Maybe<gfx::Polygon>& aGeometry=Nothing());
 
   /**
    * Dump information about this layer and its child & sibling layers to
    * layerscope packet.
    */
   void Dump(layerscope::LayersPacket* aPacket, const void* aParent);
 
   /**
@@ -2148,23 +2151,27 @@ public:
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScaleToResolution", this));
     mScaleToResolution = aScaleToResolution;
     mPresShellResolution = aResolution;
     Mutated();
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
 
-  void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
+  enum class SortMode {
+    WITH_GEOMETRY,
+    WITHOUT_GEOMETRY,
+  };
 
-  // These getters can be used anytime.
+  nsTArray<LayerPolygon> SortChildrenBy3DZOrder(SortMode aSortMode);
 
   virtual ContainerLayer* AsContainerLayer() override { return this; }
   virtual const ContainerLayer* AsContainerLayer() const override { return this; }
 
+  // These getters can be used anytime.
   virtual Layer* GetFirstChild() const override { return mFirstChild; }
   virtual Layer* GetLastChild() const override { return mLastChild; }
   float GetPreXScale() const { return mPreXScale; }
   float GetPreYScale() const { return mPreYScale; }
   float GetInheritedXScale() const { return mInheritedXScale; }
   float GetInheritedYScale() const { return mInheritedYScale; }
   float GetPresShellResolution() const { return mPresShellResolution; }
   bool ScaleToResolution() const { return mScaleToResolution; }
@@ -2236,18 +2243,39 @@ public:
   }
 
 protected:
   friend class ReadbackProcessor;
 
   void DidInsertChild(Layer* aLayer);
   void DidRemoveChild(Layer* aLayer);
 
+  bool AnyAncestorOrThisIs3DContextLeaf();
+
   void Collect3DContextLeaves(nsTArray<Layer*>& aToSort);
 
+  // Collects child layers that do not extend 3D context. For ContainerLayers
+  // that do extend 3D context, the 3D context leaves are collected.
+  nsTArray<Layer*> CollectChildren() {
+    nsTArray<Layer*> children;
+
+    for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
+      ContainerLayer* container = layer->AsContainerLayer();
+
+      if (container && container->Extend3DContext() &&
+          !container->UseIntermediateSurface()) {
+        container->Collect3DContextLeaves(children);
+      } else {
+        children.AppendElement(layer);
+      }
+    }
+
+    return children;
+  }
+
   ContainerLayer(LayerManager* aManager, void* aImplData);
 
   /**
    * A default implementation of ComputeEffectiveTransforms for use by OpenGL
    * and D3D.
    */
   void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface);
 
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -715,20 +715,22 @@ BasicLayerManager::PaintSelfOrChildren(P
     } else {
       data->Paint(aGroupTarget->GetDrawTarget(),
                   aGroupTarget->GetDeviceOffset(),
                   aPaintContext.mLayer->GetMaskLayer());
     }
   } else {
     ContainerLayer* container =
         static_cast<ContainerLayer*>(aPaintContext.mLayer);
-    AutoTArray<Layer*, 12> children;
-    container->SortChildrenBy3DZOrder(children);
+
+    nsTArray<LayerPolygon> children =
+      container->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITHOUT_GEOMETRY);
+
     for (uint32_t i = 0; i < children.Length(); i++) {
-      Layer* layer = children.ElementAt(i);
+      Layer* layer = children.ElementAt(i).layer;
       if (layer->IsBackfaceHidden()) {
         continue;
       }
       if (!layer->AsContainerLayer() && !layer->IsVisible()) {
         continue;
       }
 
       PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -42,25 +42,23 @@ protected:
 
     MOZ_COUNT_DTOR(ClientContainerLayer);
   }
 
 public:
   virtual void RenderLayer() override
   {
     RenderMaskLayers(this);
-    
+
     DefaultComputeSupportsComponentAlphaChildren();
 
-    AutoTArray<Layer*, 12> children;
-    SortChildrenBy3DZOrder(children);
-
     ReadbackProcessor readback;
     readback.BuildUpdates(this);
 
+    nsTArray<Layer*> children = CollectChildren();
     for (uint32_t i = 0; i < children.Length(); i++) {
       Layer* child = children.ElementAt(i);
 
       ToClientLayer(child)->RenderLayerWithReadback(&readback);
 
       if (!ClientManager()->GetRepeatTransaction() &&
           !child->GetInvalidRegion().IsEmpty()) {
         child->Mutated();
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -73,17 +73,18 @@ CanvasLayerComposite::GetRenderState()
 {
   if (mDestroyed || !mCompositableHost || !mCompositableHost->IsAttached()) {
     return LayerRenderState();
   }
   return mCompositableHost->GetRenderState();
 }
 
 void
-CanvasLayerComposite::RenderLayer(const IntRect& aClipRect)
+CanvasLayerComposite::RenderLayer(const IntRect& aClipRect,
+                                  const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!mCompositableHost || !mCompositableHost->IsAttached()) {
     return;
   }
 
   mCompositor->MakeCurrent();
 
 #ifdef MOZ_DUMP_PAINTING
--- a/gfx/layers/composite/CanvasLayerComposite.h
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -46,17 +46,18 @@ public:
   virtual void Disconnect() override
   {
     Destroy();
   }
 
   virtual void SetLayerManager(HostLayerManager* aManager) override;
 
   virtual Layer* GetLayer() override;
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual HostLayer* AsHostLayer() override { return this; }
--- a/gfx/layers/composite/ColorLayerComposite.cpp
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -15,26 +15,29 @@
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
-ColorLayerComposite::RenderLayer(const IntRect& aClipRect)
+ColorLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                                 const Maybe<gfx::Polygon>& aGeometry)
 {
   Rect rect(GetBounds());
+
   const Matrix4x4& transform = GetEffectiveTransform();
 
   RenderWithAllMasks(this, mCompositor, aClipRect,
                      [&](EffectChain& effectChain, const IntRect& clipRect) {
     GenEffectChain(effectChain);
-    mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(),
-                          transform);
+
+    mCompositor->DrawGeometry(rect, clipRect, effectChain,
+                              GetEffectiveOpacity(), transform, aGeometry);
   });
 
   mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR, rect, aClipRect,
                                transform);
 }
 
 void
 ColorLayerComposite::GenEffectChain(EffectChain& aEffect)
--- a/gfx/layers/composite/ColorLayerComposite.h
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -42,17 +42,19 @@ public:
   virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
+
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -9,16 +9,17 @@
 #include <stdint.h>                     // for uint64_t
 #include <stdio.h>                      // for FILE
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, RefCounted, etc
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/Effects.h"     // for Texture Effect
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/LayersMessages.h"
@@ -78,17 +79,18 @@ public:
 
   // composite the contents of this buffer host to the compositor's surface
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) = 0;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) = 0;
 
   /**
    * Update the content host.
    * aUpdated is the region which should be updated
    * aUpdatedRegionBack is the region in aNewBackResult which has been updated
    */
   virtual bool UpdateThebes(const ThebesBufferData& aData,
                             const nsIntRegion& aUpdated,
@@ -286,17 +288,17 @@ private:
  * that is accessed only on the compositor thread. During a layer transaction we
  * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
  * side we lookup the ID in the map and attach the corresponding compositable to
  * the layer.
  *
  * CompositableMap must be global because the image bridge doesn't have any
  * reference to whatever we have created with PLayerTransaction. So, the only way to
  * actually connect these two worlds is to have something global that they can
- * both query (in the same  thread). The map is not allocated the map on the 
+ * both query (in the same  thread). The map is not allocated the map on the
  * stack to avoid the badness of static initialization.
  *
  * Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
  * ImageBridge is used by all the existing compositors that have a video, so
  * there isn't an instance or "something" that lives outside the boudaries of a
  * given layer manager on the compositor thread except the image bridge and the
  * thread itself.
  */
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -47,21 +47,21 @@
 #define XY(k)    (k).x, (k).y
 #define WH(k)    (k).width, (k).height
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 
-static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
-                          LayerManagerComposite* aManager,
-                          Layer* aLayer)
+static void
+DrawLayerInfo(const RenderTargetIntRect& aClipRect,
+              LayerManagerComposite* aManager,
+              Layer* aLayer)
 {
-
   if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
     // XXX - should figure out a way to render this, but for now this
     // is hard to do, since it will often get superimposed over the first
     // child of the layer, which is bad.
     return;
   }
 
   std::stringstream ss;
@@ -72,24 +72,18 @@ static void DrawLayerInfo(const RenderTa
   uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500);
 
   IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft();
   aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft,
                                           aLayer->GetEffectiveTransform(), 16,
                                           maxWidth);
 }
 
-template<class ContainerT>
-static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
-{
-  gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
-  return surfaceRect;
-}
-
-static void PrintUniformityInfo(Layer* aLayer)
+static void
+PrintUniformityInfo(Layer* aLayer)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
   if (!profiler_is_active()) {
     return;
   }
 
   // Don't want to print a log for smaller layers
   if (aLayer->GetLocalVisibleRegion().GetBounds().width < 300 ||
@@ -103,23 +97,79 @@ static void PrintUniformityInfo(Layer* a
   }
 
   Point translation = transform.As2D().GetTranslation();
   LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
   PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
 #endif
 }
 
+static Maybe<gfx::Polygon>
+SelectLayerGeometry(const Maybe<gfx::Polygon>& aParentGeometry,
+                    const Maybe<gfx::Polygon>& aChildGeometry)
+{
+  // Both the parent and the child layer were split.
+  if (aParentGeometry && aChildGeometry) {
+    // As we use intermediate surface in these cases, this branch should never
+    // get executed.
+    MOZ_ASSERT(false,
+               "Both parent and child geometry present in nested 3D context!");
+    return Some(aParentGeometry->ClipPolygon(*aChildGeometry));
+  }
+
+  // The parent layer was split.
+  if (aParentGeometry) {
+    return aParentGeometry;
+  }
+
+  // The child layer was split.
+  if(aChildGeometry) {
+    return aChildGeometry;
+  }
+
+  // No split.
+  return Nothing();
+}
+
+static void
+TransformLayerGeometry(Layer* aLayer, Maybe<gfx::Polygon>& aGeometry)
+{
+  Layer* parent = aLayer->GetParent();
+  gfx::Matrix4x4 transform;
+
+  // Collect all parent transforms.
+  while (parent != nullptr && !parent->Is3DContextLeaf()) {
+    transform = transform * parent->GetLocalTransform();
+    parent = parent->GetParent();
+  }
+
+  // Transform the geometry to the parent 3D context leaf coordinate space.
+  aGeometry->TransformToScreenSpace(transform.ProjectTo2D().Inverse());
+}
+
+
+template<class ContainerT>
+static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
+{
+  gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+  return surfaceRect;
+}
+
+
 /* all of the per-layer prepared data we need to maintain */
 struct PreparedLayer
 {
-  PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
-    mLayer(aLayer), mClipRect(aClipRect) {}
+  PreparedLayer(LayerComposite *aLayer,
+                RenderTargetIntRect aClipRect,
+                Maybe<gfx::Polygon> aGeometry)
+  : mLayer(aLayer), mClipRect(aClipRect), mGeometry(aGeometry) {}
+
   LayerComposite* mLayer;
   RenderTargetIntRect mClipRect;
+  Maybe<Polygon> mGeometry;
 };
 
 /* all of the prepared data that we need in RenderLayer() */
 struct PreparedData
 {
   RefPtr<CompositingRenderTarget> mTmpTarget;
   AutoTArray<PreparedLayer, 12> mLayers;
   bool mNeedsSurfaceCopy;
@@ -129,27 +179,30 @@ struct PreparedData
 template<class ContainerT> void
 ContainerPrepare(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
                  const RenderTargetIntRect& aClipRect)
 {
   aContainer->mPrepared = MakeUnique<PreparedData>();
   aContainer->mPrepared->mNeedsSurfaceCopy = false;
 
-  /**
-   * Determine which layers to draw.
-   */
-  AutoTArray<Layer*, 12> children;
-  aContainer->SortChildrenBy3DZOrder(children);
+  const ContainerLayerComposite::SortMode sortMode =
+    aManager->GetCompositor()->SupportsLayerGeometry()
+    ? ContainerLayerComposite::SortMode::WITH_GEOMETRY
+    : ContainerLayerComposite::SortMode::WITHOUT_GEOMETRY;
 
-  for (uint32_t i = 0; i < children.Length(); i++) {
-    LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->AsHostLayer());
+  const nsTArray<LayerPolygon> polygons =
+    aContainer->SortChildrenBy3DZOrder(sortMode);
 
-    RenderTargetIntRect clipRect = layerToRender->GetLayer()->
-        CalculateScissorRect(aClipRect);
+  for (const LayerPolygon& layer : polygons) {
+    LayerComposite* layerToRender =
+      static_cast<LayerComposite*>(layer.layer->ImplData());
+
+    RenderTargetIntRect clipRect =
+      layerToRender->GetLayer()->CalculateScissorRect(aClipRect);
 
     if (layerToRender->GetLayer()->IsBackfaceHidden()) {
       continue;
     }
 
     // We don't want to skip container layers because otherwise their mPrepared
     // may be null which is not allowed.
     if (!layerToRender->GetLayer()->AsContainerLayer()) {
@@ -163,17 +216,17 @@ ContainerPrepare(ContainerT* aContainer,
         CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
         continue;
       }
     }
 
     CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
 
     layerToRender->Prepare(clipRect);
-    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
+    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, layer.geometry));
   }
 
   CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
 
   /**
    * Setup our temporary surface for rendering the contents of this container.
    */
 
@@ -329,28 +382,30 @@ RenderMinimap(ContainerT* aContainer, La
     compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect, aContainer->GetEffectiveTransform());
   }
 
   // Render the viewport.
   r = transform.TransformBounds(viewRect.ToUnknownRect());
   compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
 }
 
-
 template<class ContainerT> void
-RenderLayers(ContainerT* aContainer,
-	     LayerManagerComposite* aManager,
-	     const RenderTargetIntRect& aClipRect)
+RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager,
+             const RenderTargetIntRect& aClipRect,
+             const Maybe<gfx::Polygon>& aGeometry)
 {
   Compositor* compositor = aManager->GetCompositor();
 
   for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
     PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
+
+    const gfx::IntRect clipRect = preparedData.mClipRect.ToUnknownRect();
     LayerComposite* layerToRender = preparedData.mLayer;
-    const RenderTargetIntRect& clipRect = preparedData.mClipRect;
+    const Maybe<gfx::Polygon>& childGeometry = preparedData.mGeometry;
+
     Layer* layer = layerToRender->GetLayer();
 
     if (layerToRender->HasStaleCompositor()) {
       continue;
     }
 
     if (gfxPrefs::LayersDrawFPS()) {
       for (const auto& metadata : layer->GetAllScrollMetadata()) {
@@ -367,46 +422,53 @@ RenderLayers(ContainerT* aContainer,
         color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
       }
       // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
       // and only fill in that area. However the layer bounds takes into account the base translation
       // for the painted layer whereas the checkerboard region does not. One does not simply
       // intersect areas in different coordinate spaces. So we do this a little more permissively
       // and only fill in the background when we know there is checkerboard, which in theory
       // should only occur transiently.
-      gfx::IntRect layerBounds = layer->GetLayerBounds();
       EffectChain effectChain(layer);
       effectChain.mPrimaryEffect = new EffectSolidColor(color);
-      aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height),
-                                          clipRect.ToUnknownRect(),
+      aManager->GetCompositor()->DrawQuad(gfx::Rect(layer->GetLayerBounds()), clipRect,
                                           effectChain, layer->GetEffectiveOpacity(),
                                           layer->GetEffectiveTransform());
     }
 
     if (layerToRender->HasLayerBeenComposited()) {
       // Composer2D will compose this layer so skip GPU composition
       // this time. The flag will be reset for the next composition phase
       // at the beginning of LayerManagerComposite::Rener().
       gfx::IntRect clearRect = layerToRender->GetClearRect();
       if (!clearRect.IsEmpty()) {
         // Clear layer's visible rect on FrameBuffer with transparent pixels
         gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
         compositor->ClearRect(fbRect);
         layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
       }
     } else {
-      layerToRender->RenderLayer(clipRect.ToUnknownRect());
+      Maybe<gfx::Polygon> geometry =
+        SelectLayerGeometry(aGeometry, childGeometry);
+
+      // If we are dealing with a nested 3D context, we might need to transform
+      // the geometry to the coordinate space of the parent 3D context leaf.
+      if (geometry && !layer->Is3DContextLeaf()) {
+        TransformLayerGeometry(layer, geometry);
+      }
+
+      layerToRender->RenderLayer(clipRect, geometry);
     }
 
     if (gfxPrefs::UniformityInfo()) {
       PrintUniformityInfo(layer);
     }
 
     if (gfxPrefs::DrawLayerInfo()) {
-      DrawLayerInfo(clipRect, aManager, layer);
+      DrawLayerInfo(preparedData.mClipRect, aManager, layer);
     }
 
     // Draw a border around scrollable layers.
     // A layer can be scrolled by multiple scroll frames. Draw a border
     // for each.
     // Within the list of scroll frames for a layer, the layer border for a
     // scroll frame lower down is affected by the async transforms on scroll
     // frames higher up, so loop from the top down, and accumulate an async
@@ -497,25 +559,29 @@ RenderIntermediate(ContainerT* aContaine
   RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
 
   if (!surface) {
     return;
   }
 
   compositor->SetRenderTarget(surface);
   // pre-render all of the layers into our temporary
-  RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+  RenderLayers(aContainer, aManager,
+               RenderTargetIntRect::FromUnknownRect(aClipRect),
+               Nothing());
+
   // Unbind the current surface and rebind the previous one.
   compositor->SetRenderTarget(previousTarget);
 }
 
 template<class ContainerT> void
 ContainerRender(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
-                 const gfx::IntRect& aClipRect)
+                 const gfx::IntRect& aClipRect,
+                 const Maybe<gfx::Polygon>& aGeometry)
 {
   MOZ_ASSERT(aContainer->mPrepared);
 
   if (aContainer->UseIntermediateSurface()) {
     RefPtr<CompositingRenderTarget> surface;
 
     if (aContainer->mPrepared->mNeedsSurfaceCopy) {
       // we needed to copy the background so we waited until now to render the intermediate
@@ -527,38 +593,42 @@ ContainerRender(ContainerT* aContainer,
     }
 
     if (!surface) {
       aContainer->mPrepared = nullptr;
       return;
     }
 
     gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+
     RefPtr<Compositor> compositor = aManager->GetCompositor();
 #ifdef MOZ_DUMP_PAINTING
     if (gfxEnv::DumpCompositorTextures()) {
       RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
       if (surf) {
         WriteSnapshotToDumpFile(aContainer, surf);
       }
     }
 #endif
 
     RefPtr<ContainerT> container = aContainer;
     RenderWithAllMasks(aContainer, compositor, aClipRect,
                        [&, surface, compositor, container](EffectChain& effectChain, const IntRect& clipRect) {
       effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
-      compositor->DrawQuad(visibleRect, clipRect, effectChain,
-                           container->GetEffectiveOpacity(),
-                           container->GetEffectiveTransform());
+
+      compositor->DrawGeometry(visibleRect, clipRect, effectChain,
+                               container->GetEffectiveOpacity(),
+                               container->GetEffectiveTransform(), aGeometry);
     });
+
   } else {
-    RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+    RenderLayers(aContainer, aManager,
+                 RenderTargetIntRect::FromUnknownRect(aClipRect),
+                 aGeometry);
   }
-  aContainer->mPrepared = nullptr;
 
   // If it is a scrollable container layer with no child layers, and one of the APZCs
   // attached to it has a nonempty async transform, then that transform is not applied
   // to any visible content. Display a warning box (conditioned on the FPS display being
   // enabled).
   if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
     // Since aContainer doesn't have any children we can just iterate from the top metrics
     // on it down to the bottom using GetFirstChild and not worry about walking onto another
@@ -618,19 +688,26 @@ ContainerLayerComposite::GetFirstChildCo
 {
   if (!mFirstChild) {
     return nullptr;
    }
   return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
-ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+ContainerLayerComposite::Cleanup()
 {
-  ContainerRender(this, mCompositeManager, aClipRect);
+  mPrepared = nullptr;
+}
+
+void
+ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                                     const Maybe<gfx::Polygon>& aGeometry)
+{
+  ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
 }
 
 void
 ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
 {
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
@@ -669,19 +746,20 @@ RefLayerComposite::GetFirstChildComposit
 {
   if (!mFirstChild) {
     return nullptr;
    }
   return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
-RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                               const Maybe<gfx::Polygon>& aGeometry)
 {
-  ContainerRender(this, mCompositeManager, aClipRect);
+  ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
 }
 
 void
 RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
 {
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
--- a/gfx/layers/composite/ContainerLayerComposite.h
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -24,21 +24,23 @@ class ContainerLayerComposite : public C
 {
   template<class ContainerT>
   friend void ContainerPrepare(ContainerT* aContainer,
                                LayerManagerComposite* aManager,
                                const RenderTargetIntRect& aClipRect);
   template<class ContainerT>
   friend void ContainerRender(ContainerT* aContainer,
                               LayerManagerComposite* aManager,
-                              const RenderTargetIntRect& aClipRect);
+                              const RenderTargetIntRect& aClipRect,
+                              const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderLayers(ContainerT* aContainer,
                            LayerManagerComposite* aManager,
-                           const RenderTargetIntRect& aClipRect);
+                           const RenderTargetIntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderIntermediate(ContainerT* aContainer,
                    LayerManagerComposite* aManager,
                    const gfx::IntRect& aClipRect,
                    RefPtr<CompositingRenderTarget> surface);
   template<class ContainerT>
   friend RefPtr<CompositingRenderTarget>
   CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
@@ -74,17 +76,21 @@ public:
       child->SetLayerManager(aManager);
     }
   }
 
   virtual void Destroy() override;
 
   LayerComposite* GetFirstChildComposite() override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void Cleanup() override;
+
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
+
   virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void CleanupResources() override;
@@ -123,21 +129,23 @@ class RefLayerComposite : public RefLaye
 {
   template<class ContainerT>
   friend void ContainerPrepare(ContainerT* aContainer,
                                LayerManagerComposite* aManager,
                                const RenderTargetIntRect& aClipRect);
   template<class ContainerT>
   friend void ContainerRender(ContainerT* aContainer,
                               LayerManagerComposite* aManager,
-                              const gfx::IntRect& aClipRect);
+                              const gfx::IntRect& aClipRect,
+                              const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderLayers(ContainerT* aContainer,
                            LayerManagerComposite* aManager,
-                           const gfx::IntRect& aClipRect);
+                           const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderIntermediate(ContainerT* aContainer,
                    LayerManagerComposite* aManager,
                    const gfx::IntRect& aClipRect,
                    RefPtr<CompositingRenderTarget> surface);
   template<class ContainerT>
   friend RefPtr<CompositingRenderTarget>
   CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
@@ -158,17 +166,19 @@ protected:
 public:
   /** LayerOGL implementation */
   Layer* GetLayer() override { return this; }
 
   void Destroy() override;
 
   LayerComposite* GetFirstChildComposite() override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
+
   virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void CleanupResources() override;
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -33,17 +33,18 @@ ContentHostBase::~ContentHostBase()
 
 void
 ContentHostTexture::Composite(LayerComposite* aLayer,
                               EffectChain& aEffectChain,
                               float aOpacity,
                               const gfx::Matrix4x4& aTransform,
                               const SamplingFilter aSamplingFilter,
                               const IntRect& aClipRect,
-                              const nsIntRegion* aVisibleRegion)
+                              const nsIntRegion* aVisibleRegion,
+                              const Maybe<gfx::Polygon>& aGeometry)
 {
   NS_ASSERTION(aVisibleRegion, "Requires a visible region");
 
   AutoLockCompositableHost lock(this);
   if (lock.Failed()) {
     return;
   }
 
@@ -175,17 +176,20 @@ ContentHostTexture::Composite(LayerCompo
           }
           gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
                          tileScreenRect.width, tileScreenRect.height);
 
           effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
                                         Float(tileRegionRect.y) / texRect.height,
                                         Float(tileRegionRect.width) / texRect.width,
                                         Float(tileRegionRect.height) / texRect.height);
-          GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+
+          GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
+                                        aOpacity, aTransform, aGeometry);
+
           if (usingTiles) {
             DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
             if (iterOnWhite) {
               diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
             }
             GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
                                              aTransform, mFlashCounter);
           }
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -11,16 +11,17 @@
 #include "mozilla-config.h"             // for MOZ_DUMP_PAINTING
 #include "CompositableHost.h"           // for CompositableHost, etc
 #include "RotatedBuffer.h"              // for RotatedContentBuffer, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for etc
 #include "mozilla/layers/TextureHost.h"  // for TextureHost
 #include "mozilla/mozalloc.h"           // for operator delete
@@ -118,17 +119,18 @@ public:
   { }
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false) override;
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -282,17 +282,18 @@ void ImageHost::Attach(Layer* aLayer,
 
 void
 ImageHost::Composite(LayerComposite* aLayer,
                      EffectChain& aEffectChain,
                      float aOpacity,
                      const gfx::Matrix4x4& aTransform,
                      const gfx::SamplingFilter aSamplingFilter,
                      const gfx::IntRect& aClipRect,
-                     const nsIntRegion* aVisibleRegion)
+                     const nsIntRegion* aVisibleRegion,
+                     const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!GetCompositor()) {
     // should only happen when a tab is dragged to another window and
     // async-video is still sending frames but we haven't attached the
     // set the new compositor yet.
     return;
   }
 
@@ -387,18 +388,18 @@ ImageHost::Composite(LayerComposite* aLa
         effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
                                       Float(rect.y - tileRect.y) / tileRect.height,
                                       Float(rect.width) / tileRect.width,
                                       Float(rect.height) / tileRect.height);
         if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
           effect->mTextureCoords.y = effect->mTextureCoords.YMost();
           effect->mTextureCoords.height = -effect->mTextureCoords.height;
         }
-        GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
-                                  aOpacity, aTransform);
+        GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
+                                      aOpacity, aTransform, aGeometry);
         GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
                                          rect, aClipRect, aTransform, mFlashCounter);
       } while (it->NextTile());
       it->EndBigImageIteration();
       // layer border
       GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
                                        aClipRect, aTransform, mFlashCounter);
     } else {
@@ -408,18 +409,18 @@ ImageHost::Composite(LayerComposite* aLa
                                     Float(img->mPictureRect.width) / textureSize.width,
                                     Float(img->mPictureRect.height) / textureSize.height);
 
       if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
         effect->mTextureCoords.y = effect->mTextureCoords.YMost();
         effect->mTextureCoords.height = -effect->mTextureCoords.height;
       }
 
-      GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
-                                aOpacity, aTransform);
+      GetCompositor()->DrawGeometry(pictureRect, aClipRect, aEffectChain,
+                                    aOpacity, aTransform, aGeometry);
       GetCompositor()->DrawDiagnostics(diagnosticFlags,
                                        pictureRect, aClipRect,
                                        aTransform, mFlashCounter);
     }
   }
 
   // Update mBias last. This can change which frame ChooseImage(Index) would
   // return, and we don't want to do that until we've finished compositing
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -7,16 +7,17 @@
 #define MOZILLA_GFX_IMAGEHOST_H
 
 #include <stdio.h>                      // for FILE
 #include "CompositableHost.h"           // for CompositableHost
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsCOMPtr.h"                   // for already_AddRefed
@@ -43,17 +44,18 @@ public:
   virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
 
   virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
 
   virtual void RemoveTextureHost(TextureHost* aTexture) override;
 
   virtual void UseOverlaySource(OverlaySource aOverlay,
                                 const gfx::IntRect& aPictureRect) override;
 
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -85,17 +85,18 @@ ImageLayerComposite::SetLayerManager(Hos
   LayerComposite::SetLayerManager(aManager);
   mManager = aManager;
   if (mImageHost) {
     mImageHost->SetCompositor(mCompositor);
   }
 }
 
 void
-ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
+ImageLayerComposite::RenderLayer(const IntRect& aClipRect,
+                                 const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!mImageHost || !mImageHost->IsAttached()) {
     return;
   }
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxEnv::DumpCompositorTextures()) {
     RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
--- a/gfx/layers/composite/ImageLayerComposite.h
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -40,17 +40,18 @@ public:
   virtual void Disconnect() override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual Layer* GetLayer() override;
 
   virtual void SetLayerManager(HostLayerManager* aManager) override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
 
   virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
 
   virtual void CleanupResources() override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -568,17 +568,17 @@ LayerManagerComposite::DrawPaintTimes(Co
 
 static uint16_t sFrameCount = 0;
 void
 LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
 {
   bool drawFps = gfxPrefs::LayersDrawFPS();
   bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
   bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
-  
+
   TimeStamp now = TimeStamp::Now();
 
   if (drawFps) {
     if (!mFPS) {
       mFPS = MakeUnique<FPSState>();
     }
 
     float alpha = 1;
@@ -921,17 +921,18 @@ LayerManagerComposite::Render(const nsIn
   if (haveLayerEffects) {
     previousTarget = PushGroupForLayerEffects();
   } else {
     mTwoPassTmpTarget = nullptr;
   }
 
   // Render our layers.
   RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
-  RootLayer()->RenderLayer(clipRect.ToUnknownRect());
+  RootLayer()->RenderLayer(clipRect.ToUnknownRect(), Nothing());
+  RootLayer()->Cleanup();
 
   if (!mRegionToClear.IsEmpty()) {
     for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
       const IntRect& r = iter.Get();
       mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height));
     }
   }
 
@@ -1128,17 +1129,17 @@ LayerManagerComposite::RenderToPresentat
   // is cleared.
   ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
   egl->fClearColor(0.0, 0.0, 0.0, 0.0);
   egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
 
   const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
 
   RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
-  RootLayer()->RenderLayer(clipRect);
+  RootLayer()->RenderLayer(clipRect, Nothing());
 
   mCompositor->EndFrame();
 }
 #endif
 
 class TextLayerComposite : public TextLayer,
                            public LayerComposite
 {
@@ -1165,17 +1166,18 @@ public:
   virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override {}
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override {}
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
@@ -1208,17 +1210,18 @@ public:
   virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override {}
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override {}
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -518,17 +518,17 @@ public:
   // These getters can be used anytime.
   float GetShadowOpacity() { return mShadowOpacity; }
   const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
   const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
   const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
   gfx::Matrix4x4 GetShadowTransform();
   bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
   bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
-  
+
   /**
    * Return true if a checkerboarding background color needs to be drawn
    * for this layer.
    */
   virtual bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr) { return false; }
 
 protected:
   HostLayerManager* mCompositorManager;
@@ -572,27 +572,29 @@ public:
   {
     return nullptr;
   }
 
   /* Do NOT call this from the generic LayerComposite destructor.  Only from the
    * concrete class destructor
    */
   virtual void Destroy();
+  virtual void Cleanup() {}
 
   /**
    * Perform a first pass over the layer tree to render all of the intermediate
    * surfaces that we can. This allows us to avoid framebuffer switches in the
    * middle of our render which is inefficient especially on mobile GPUs. This
    * must be called before RenderLayer.
    */
   virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
 
   // TODO: This should also take RenderTargetIntRect like Prepare.
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) = 0;
 
   virtual bool SetCompositableHost(CompositableHost*)
   {
     // We must handle this gracefully, see bug 967824
     NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
     return false;
   }
 
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -6,16 +6,17 @@
 #include "PaintedLayerComposite.h"
 #include "CompositableHost.h"           // for TiledLayerProperties, etc
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for CSSRect, LayerPixel, etc
 #include "gfxEnv.h"                     // for gfxEnv
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter::LINEAR
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ContentHost.h"  // for ContentHost
 #include "mozilla/layers/Effects.h"     // for EffectChain
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsAString.h"
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
@@ -93,17 +94,18 @@ PaintedLayerComposite::GetRenderState()
 {
   if (!mBuffer || !mBuffer->IsAttached() || mDestroyed) {
     return LayerRenderState();
   }
   return mBuffer->GetRenderState();
 }
 
 void
-PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                                   const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!mBuffer || !mBuffer->IsAttached()) {
     return;
   }
   PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
   Compositor* compositor = mCompositeManager->GetCompositor();
@@ -118,27 +120,24 @@ PaintedLayerComposite::RenderLayer(const
   if (gfxEnv::DumpCompositorTextures()) {
     RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
     if (surf) {
       WriteSnapshotToDumpFile(this, surf);
     }
   }
 #endif
 
-
   RenderWithAllMasks(this, compositor, aClipRect,
-                     [&](EffectChain& effectChain, const gfx::IntRect& clipRect) {
+                     [&](EffectChain& effectChain,
+                     const gfx::IntRect& clipRect) {
     mBuffer->SetPaintWillResample(MayResample());
 
-    mBuffer->Composite(this, effectChain,
-                       GetEffectiveOpacity(),
-                       GetEffectiveTransform(),
-                       GetSamplingFilter(),
-                       clipRect,
-                       &visibleRegion);
+    mBuffer->Composite(this, effectChain, GetEffectiveOpacity(),
+                       GetEffectiveTransform(), GetSamplingFilter(),
+                       clipRect, &visibleRegion, aGeometry);
   });
 
   mBuffer->BumpFlashCounter();
 
   compositor->MakeCurrent();
 }
 
 CompositableHost*
--- a/gfx/layers/composite/PaintedLayerComposite.h
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -46,17 +46,18 @@ public:
   CompositableHost* GetCompositableHost() override;
 
   virtual void Destroy() override;
 
   virtual Layer* GetLayer() override;
 
   virtual void SetLayerManager(HostLayerManager* aManager) override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual HostLayer* AsHostLayer() override { return this; }
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -389,17 +389,18 @@ TiledLayerBufferComposite::Clear()
 
 void
 TiledContentHost::Composite(LayerComposite* aLayer,
                             EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::SamplingFilter aSamplingFilter,
                             const gfx::IntRect& aClipRect,
-                            const nsIntRegion* aVisibleRegion /* = nullptr */)
+                            const nsIntRegion* aVisibleRegion /* = nullptr */,
+                            const Maybe<gfx::Polygon>& aGeometry)
 {
   MOZ_ASSERT(mCompositor);
   // Reduce the opacity of the low-precision buffer to make it a
   // little more subtle and less jarring. In particular, text
   // rendered at low-resolution and scaled tends to look pretty
   // heavy and this helps mitigate that. When we reduce the opacity
   // we also make sure to draw the background color behind the
   // reduced-opacity tile so that content underneath doesn't show
@@ -434,33 +435,35 @@ TiledContentHost::Composite(LayerComposi
     renderRegion = &tmpRegion;
   }
 #endif
 
   // Render the low and high precision buffers.
   RenderLayerBuffer(mLowPrecisionTiledBuffer,
                     lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
                     aEffectChain, lowPrecisionOpacityReduction * aOpacity,
-                    aSamplingFilter, aClipRect, *renderRegion, aTransform);
+                    aSamplingFilter, aClipRect, *renderRegion, aTransform, aGeometry);
+
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aSamplingFilter,
-                    aClipRect, *renderRegion, aTransform);
+                    aClipRect, *renderRegion, aTransform, aGeometry);
 }
 
 
 void
 TiledContentHost::RenderTile(TileHost& aTile,
                              EffectChain& aEffectChain,
                              float aOpacity,
                              const gfx::Matrix4x4& aTransform,
                              const gfx::SamplingFilter aSamplingFilter,
                              const gfx::IntRect& aClipRect,
                              const nsIntRegion& aScreenRegion,
                              const IntPoint& aTextureOffset,
                              const IntSize& aTextureBounds,
-                             const gfx::Rect& aVisibleRect)
+                             const gfx::Rect& aVisibleRect,
+                             const Maybe<gfx::Polygon>& aGeometry)
 {
   MOZ_ASSERT(!aTile.IsPlaceholderTile());
 
   AutoLockTextureHost autoLock(aTile.mTextureHost);
   AutoLockTextureHost autoLockOnWhite(aTile.mTextureHostOnWhite);
   if (autoLock.Failed() ||
       autoLockOnWhite.Failed()) {
     NS_WARNING("Failed to lock tile");
@@ -493,35 +496,39 @@ TiledContentHost::RenderTile(TileHost& a
     Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
     Rect textureRect(rect.x - aTextureOffset.x, rect.y - aTextureOffset.y,
                      rect.width, rect.height);
 
     effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width,
                                   textureRect.y / aTextureBounds.height,
                                   textureRect.width / aTextureBounds.width,
                                   textureRect.height / aTextureBounds.height);
-    mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect);
+
+    mCompositor->DrawGeometry(graphicsRect, aClipRect, aEffectChain, opacity,
+                              aTransform, aVisibleRect, aGeometry);
   }
+
   DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
   if (aTile.mTextureHostOnWhite) {
     flags |= DiagnosticFlags::COMPONENT_ALPHA;
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
 }
 
 void
 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                                     const Color* aBackgroundColor,
                                     EffectChain& aEffectChain,
                                     float aOpacity,
                                     const gfx::SamplingFilter aSamplingFilter,
                                     const gfx::IntRect& aClipRect,
                                     nsIntRegion aVisibleRegion,
-                                    gfx::Matrix4x4 aTransform)
+                                    gfx::Matrix4x4 aTransform,
+                                    const Maybe<Polygon>& aGeometry)
 {
   if (!mCompositor) {
     NS_WARNING("Can't render tiled content host - no compositor");
     return;
   }
   float resolution = aLayerBuffer.GetResolution();
   gfx::Size layerScale(1, 1);
 
@@ -567,17 +574,18 @@ TiledContentHost::RenderLayerBuffer(Tile
   if (aBackgroundColor) {
     nsIntRegion backgroundRegion = compositeRegion;
     backgroundRegion.ScaleRoundOut(resolution, resolution);
     EffectChain effect;
     effect.mPrimaryEffect = new EffectSolidColor(*aBackgroundColor);
     for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) {
       const IntRect& rect = iter.Get();
       Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
-      mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform);
+      mCompositor->DrawGeometry(graphicsRect, aClipRect, effect,
+                                1.0, aTransform, aGeometry);
     }
   }
 
   for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
     TileHost& tile = aLayerBuffer.GetTile(i);
     if (tile.IsPlaceholderTile()) {
       continue;
     }
@@ -594,17 +602,19 @@ TiledContentHost::RenderLayerBuffer(Tile
       continue;
     }
 
     tileDrawRegion.ScaleRoundOut(resolution, resolution);
     RenderTile(tile, aEffectChain, aOpacity,
                aTransform, aSamplingFilter, aClipRect, tileDrawRegion,
                tileOffset * resolution, aLayerBuffer.GetTileSize(),
                gfx::Rect(visibleRect.x, visibleRect.y,
-                         visibleRect.width, visibleRect.height));
+                         visibleRect.width, visibleRect.height),
+               aGeometry);
+
     if (tile.mTextureHostOnWhite) {
       componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
     }
   }
 
   gfx::Rect rect(visibleRect.x, visibleRect.y,
                  visibleRect.width, visibleRect.height);
   GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTENT | componentAlphaDiagnostic,
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -231,17 +231,18 @@ public:
                            const SurfaceDescriptorTiles& aTiledDescriptor);
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
 
   virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
 
   virtual TiledContentHost* AsTiledContentHost() override { return this; }
 
   virtual void Attach(Layer* aLayer,
                       Compositor* aCompositor,
                       AttachFlags aFlags = NO_FLAGS) override;
@@ -261,29 +262,31 @@ private:
 
   void RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                          const gfx::Color* aBackgroundColor,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
                          nsIntRegion aMaskRegion,
-                         gfx::Matrix4x4 aTransform);
+                         gfx::Matrix4x4 aTransform,
+                         const Maybe<gfx::Polygon>& aGeometry);
 
   // Renders a single given tile.
   void RenderTile(TileHost& aTile,
                   EffectChain& aEffectChain,
                   float aOpacity,
                   const gfx::Matrix4x4& aTransform,
                   const gfx::SamplingFilter aSamplingFilter,
                   const gfx::IntRect& aClipRect,
                   const nsIntRegion& aScreenRegion,
                   const gfx::IntPoint& aTextureOffset,
                   const gfx::IntSize& aTextureBounds,
-                  const gfx::Rect& aVisibleRect);
+                  const gfx::Rect& aVisibleRect,
+                  const Maybe<gfx::Polygon>& aGeometry);
 
   void EnsureTileStore() {}
 
   TiledLayerBufferComposite    mTiledBuffer;
   TiledLayerBufferComposite    mLowPrecisionTiledBuffer;
 };
 
 } // namespace layers
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1814,10 +1814,16 @@ PerUnitTexturePoolOGL::DestroyTextures()
   if (mGL && mGL->MakeCurrent()) {
     if (mTextures.Length() > 0) {
       mGL->fDeleteTextures(mTextures.Length(), &mTextures[0]);
     }
   }
   mTextures.SetLength(0);
 }
 
+bool
+CompositorOGL::SupportsLayerGeometry() const
+{
+  return gfxPrefs::OGLLayerGeometry();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -166,16 +166,18 @@ public:
 
   virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
                             const gfx::IntRect& aClipRect,
                             const EffectChain& aEffectChain,
                             gfx::Float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Rect& aVisibleRect) override;
 
+  virtual bool SupportsLayerGeometry() const override;
+
   virtual void EndFrame() override;
 
   virtual bool SupportsPartialTextureUpdate() override;
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
   {
     if (!mGLContext)
       return false;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -522,16 +522,17 @@ private:
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
   DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled",          LayerTileFadeInEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms",      LayerTileFadeInDuration, uint32_t, 250);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, true);
   DECL_GFX_PREF(Live, "layers.draw-mask-debug",                DrawMaskLayer, bool, false);
+  DECL_GFX_PREF(Live, "layers.geometry.opengl.enabled",        OGLLayerGeometry, bool, false);
 
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, true);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-max-velocity", ScrollSnapPredictionMaxVelocity, int32_t, 2000);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-sensitivity", ScrollSnapPredictionSensitivity, float, 0.750f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.proximity-threshold", ScrollSnapProximityThreshold, int32_t, 200);
   DECL_GFX_PREF(Live, "layout.css.touch_action.enabled",       TouchActionEnabled, bool, false);