Bug 1286412 - Add compositor support for triangle layers (for OpenGL backend) draft
authorMiko Mynttinen <mikokm@gmail.com>
Fri, 07 Oct 2016 10:58:13 -0700
changeset 422290 f9b862472cdb49baa7afdb68deab5cb59bbfe45a
parent 422289 d7f547b67718c4da7021af2519e3f41a925a2042
child 533296 a9ab237dfa3c655779d759eed57407a387ebcaeb
push id31731
push userbmo:mikokm@gmail.com
push dateFri, 07 Oct 2016 20:00:40 +0000
bugs1286412
milestone52.0a1
Bug 1286412 - Add compositor support for triangle layers (for OpenGL backend) MozReview-Commit-ID: 75q8ja7G1ko
gfx/2d/Matrix.h
gfx/2d/Polygon.h
gfx/layers/Compositor.cpp
gfx/layers/Compositor.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
gfx/layers/opengl/OGLShaderProgram.cpp
gfx/layers/opengl/OGLShaderProgram.h
gfx/tests/gtest/TestPolygon.cpp
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_MATRIX_H_
 #define MOZILLA_GFX_MATRIX_H_
 
 #include "Types.h"
+#include "Triangle.h"
 #include "Rect.h"
 #include "Point.h"
 #include "Quaternion.h"
 #include <iosfwd>
 #include <math.h>
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
@@ -698,16 +699,23 @@ public:
 
     if (max_x < min_x || max_y < min_y) {
       return RectTyped<TargetUnits, F>(0, 0, 0, 0);
     }
 
     return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
   }
 
+  template<class F>
+  RectTyped<TargetUnits, F> TransformAndClipBounds(const TriangleTyped<SourceUnits, F>& aTriangle,
+                                                   const RectTyped<TargetUnits, F>& aClip) const
+  {
+    return TransformAndClipBounds(aTriangle.BoundingBox(), aClip);
+  }
+
   /**
    * TransformAndClipRect projects a rectangle and clips against view frustum
    * clipping planes in homogenous space so that its projected vertices are
    * constrained within the 2d rectangle passed in aClip.
    * The resulting vertices are populated in aVerts.  aVerts must be
    * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points.
    * The vertex count is returned by TransformAndClipRect.  It is possible to
    * emit fewer that 3 vertices, indicating that aRect will not be visible
--- a/gfx/2d/Polygon.h
+++ b/gfx/2d/Polygon.h
@@ -181,31 +181,17 @@ public:
 
         // Add the intersection point to both polygons.
         aBackPoints.AppendElement(p);
         aFrontPoints.AppendElement(p);
       }
     }
   }
 
-  void TransformToLayerSpace(const Matrix4x4Typed<Units, Units>& aTransform)
-  {
-    TransformPoints(aTransform);
-    mNormal = Point3DTyped<Units>(0.0f, 0.0f, 1.0f);
-  }
-
-  void TransformToScreenSpace(const Matrix4x4Typed<Units, Units>& aTransform)
-  {
-    TransformPoints(aTransform);
-
-    // Normal vectors should be transformed using inverse transpose.
-    mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
-  }
-
-  nsTArray<TriangleTyped<Units>> Triangulate() const
+  nsTArray<TriangleTyped<Units>> ToTriangles() const
   {
     nsTArray<TriangleTyped<Units>> triangles;
 
     if (mPoints.Length() < 3) {
       return triangles;
     }
 
     for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
@@ -213,16 +199,30 @@ public:
                                     Point(mPoints[i].x, mPoints[i].y),
                                     Point(mPoints[i+1].x, mPoints[i+1].y));
       triangles.AppendElement(Move(triangle));
     }
 
     return triangles;
   }
 
+  void TransformToLayerSpace(const Matrix4x4Typed<Units, Units>& aTransform)
+  {
+    TransformPoints(aTransform);
+    mNormal = Point3DTyped<Units>(0.0f, 0.0f, 1.0f);
+  }
+
+  void TransformToScreenSpace(const Matrix4x4Typed<Units, Units>& aTransform)
+  {
+    TransformPoints(aTransform);
+
+    // Normal vectors should be transformed using inverse transpose.
+    mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
+  }
+
 private:
   void ClipPolygonWithEdge(Polygon3DTyped<Units>& aPolygon,
                            const PointTyped<Units>& aFirst,
                            const PointTyped<Units>& aSecond) const
   {
     const Point3DTyped<Units> a(aFirst.x, aFirst.y, 0.0f);
     const Point3DTyped<Units> b(aSecond.x, aSecond.y, 0.0f);
     const Point3DTyped<Units> normal(b.y - a.y, a.x - b.x, 0.0f);
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -227,16 +227,109 @@ Compositor::DrawDiagnosticsInternal(Diag
     color.r *= flash;
     color.g *= flash;
     color.b *= flash;
   }
 
   SlowDrawRect(aVisibleRect, color, aClipRect, aTransform, lWidth);
 }
 
+static void
+UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
+                         const gfx::Rect& aRect,
+                         const gfx::Rect& aIntersection,
+                         gfx::Rect aTextureCoords)
+{
+  // Calculate the relative offset of the intersection within the layer.
+  float dx = (aIntersection.x - aRect.x) / aRect.width;
+  float dy = (aIntersection.y - aRect.y) / aRect.height;
+
+  // Update the texture offset.
+  float x = aTextureCoords.x + dx * aTextureCoords.width;
+  float y = aTextureCoords.y + dy * aTextureCoords.height;
+
+  // Scale the texture width and height.
+  float w = aTextureCoords.width * aIntersection.width / aRect.width;
+  float h = aTextureCoords.height * aIntersection.height / aRect.height;
+
+  static const auto ValidateAndClamp = [](float& f) {
+    // Allow some numerical inaccuracy.
+    MOZ_ASSERT(f >= -0.0001f && f <= 1.0001f);
+
+    if (f >= 1.0f) f = 1.0f;
+    if (f <= 0.0f) f = 0.0f;
+  };
+
+  auto UpdatePoint = [&](const gfx::Point& p, gfx::Point& t)
+  {
+    t.x = x + (p.x - aIntersection.x) / aIntersection.width * w;
+    t.y = y + (p.y - aIntersection.y) / aIntersection.height * h;
+
+    ValidateAndClamp(t.x);
+    ValidateAndClamp(t.y);
+  };
+
+  UpdatePoint(aTriangle.p1, aTriangle.textureCoords.p1);
+  UpdatePoint(aTriangle.p2, aTriangle.textureCoords.p2);
+  UpdatePoint(aTriangle.p3, aTriangle.textureCoords.p3);
+}
+
+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::Polygon3D>& aGeometry)
+{
+  if (!aGeometry) {
+    DrawQuad(aRect, aClipRect, aEffectChain,
+             aOpacity, aTransform, aVisibleRect);
+    return;
+  }
+
+  // Cull invisible polygons.
+  if (aRect.Intersect(aGeometry->BoundingBox()).IsEmpty()) {
+    return;
+  }
+
+  gfx::Polygon3D clipped = aGeometry->ClipPolygon(aRect);
+  nsTArray<gfx::Triangle> triangles = clipped.ToTriangles();
+
+  for (gfx::Triangle& geometry : triangles) {
+    const gfx::Rect intersection = aRect.Intersect(geometry.BoundingBox());
+
+    // Cull invisible triangles.
+    if (intersection.IsEmpty()) {
+      continue;
+    }
+
+    MOZ_ASSERT(aRect.width > 0.0f && aRect.height > 0.0f);
+    MOZ_ASSERT(intersection.width > 0.0f && intersection.height > 0.0f);
+
+    gfx::TexturedTriangle triangle(Move(geometry));
+    triangle.width = aRect.width;
+    triangle.height = aRect.height;
+
+    // Since the texture was created for non-split geometry, we need to
+    // update the texture coordinates to account for the split.
+    if (aEffectChain.mPrimaryEffect->mType == EffectTypes::RGB) {
+      TexturedEffect* texturedEffect =
+        static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+      UpdateTextureCoordinates(triangle, aRect, intersection,
+                               texturedEffect->mTextureCoords);
+    }
+
+    DrawTriangle(triangle, aClipRect, aEffectChain,
+                 aOpacity, aTransform, aVisibleRect);
+  }
+}
+
 void
 Compositor::SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& aColor,
                      const gfx::IntRect& aClipRect,
                      const gfx::Matrix4x4& aTransform, int aStrokeWidth)
 {
   // TODO This should draw a rect using a single draw call but since
   // this is only used for debugging overlays it's not worth optimizing ATM.
   float opacity = 1.0f;
@@ -485,16 +578,28 @@ Compositor::ComputeBackdropCopyRect(cons
   gfx::Matrix4x4 transform;
   transform.PostScale(rtSize.width, rtSize.height, 1.0);
   transform.PostTranslate(-result.x, -result.y, 0.0);
   transform.PostScale(1 / float(result.width), 1 / float(result.height), 1.0);
   *aOutTransform = transform;
   return result;
 }
 
+gfx::IntRect
+Compositor::ComputeBackdropCopyRect(const gfx::Triangle& aTriangle,
+                                    const gfx::IntRect& aClipRect,
+                                    const gfx::Matrix4x4& aTransform,
+                                    gfx::Matrix4x4* aOutTransform,
+                                    gfx::Rect* aOutLayerQuad)
+{
+  gfx::Rect boundingBox = aTriangle.BoundingBox();
+  return ComputeBackdropCopyRect(boundingBox, aClipRect, aTransform,
+                                 aOutTransform, aOutLayerQuad);
+}
+
 void
 Compositor::SetInvalid()
 {
   mParent = nullptr;
 }
 
 bool
 Compositor::IsValid() const
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -7,18 +7,20 @@
 #define MOZILLA_GFX_COMPOSITOR_H
 
 #include "Units.h"                      // for ScreenPoint
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for already_AddRefed, RefCounted
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon3D
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for Float
+#include "mozilla/gfx/Triangle.h"       // for Triangle, TexturedTriangle
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticTypes, etc
 #include "mozilla/layers/FenceUtils.h"  // for FenceHandle
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"
 #include <vector>
 #include "mozilla/WidgetUtils.h"
@@ -305,16 +307,35 @@ public:
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) = 0;
 
   /**
    * Declare an offset to use when rendering layers. This will be ignored when
    * rendering to a target instead of the screen.
    */
   virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) = 0;
 
+  void 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::Polygon3D>& aGeometry);
+
+  void DrawGeometry(const gfx::Rect& aRect,
+                    const gfx::IntRect& aClipRect,
+                    const EffectChain &aEffectChain,
+                    gfx::Float aOpacity,
+                    const gfx::Matrix4x4& aTransform,
+                    const Maybe<gfx::Polygon3D>& aGeometry)
+  {
+    DrawGeometry(aRect, aClipRect, aEffectChain, aOpacity,
+                 aTransform, aRect, aGeometry);
+  }
+
   /**
    * Tell the compositor to draw a quad. What to do draw and how it is
    * drawn is specified by aEffectChain. aRect is the quad to draw, in user space.
    * aTransform transforms from user space to screen space. If texture coords are
    * required, these will be in the primary effect in the effect chain.
    * aVisibleRect is used to determine which edges should be antialiased,
    * without applying the effect to the inner edges of a tiled layer.
    */
@@ -329,16 +350,26 @@ public:
    * layer.
    */
   void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
                         const EffectChain& aEffectChain,
                         gfx::Float aOpacity, const gfx::Matrix4x4& aTransform) {
       DrawQuad(aRect, aClipRect, aEffectChain, aOpacity, aTransform, aRect);
   }
 
+  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)
+  {
+    MOZ_CRASH("Compositor::DrawTriangle is not implemented for the current platform!");
+  }
+
   /**
    * 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);
 
@@ -587,22 +618,28 @@ protected:
   /**
    * Given a layer rect, clip, and transform, compute the area of the backdrop that
    * needs to be copied for mix-blending. The output transform translates from 0..1
    * space into the backdrop rect space.
    *
    * The transformed layer quad is also optionally returned - this is the same as
    * the result rect, before rounding.
    */
-  gfx::IntRect ComputeBackdropCopyRect(
-    const gfx::Rect& aRect,
-    const gfx::IntRect& aClipRect,
-    const gfx::Matrix4x4& aTransform,
-    gfx::Matrix4x4* aOutTransform,
-    gfx::Rect* aOutLayerQuad = nullptr);
+  gfx::IntRect ComputeBackdropCopyRect(const gfx::Rect& aRect,
+                                       const gfx::IntRect& aClipRect,
+                                       const gfx::Matrix4x4& aTransform,
+                                       gfx::Matrix4x4* aOutTransform,
+                                       gfx::Rect* aOutLayerQuad = nullptr);
+
+  gfx::IntRect ComputeBackdropCopyRect(const gfx::Triangle& aTriangle,
+                                       const gfx::IntRect& aClipRect,
+                                       const gfx::Matrix4x4& aTransform,
+                                       gfx::Matrix4x4* aOutTransform,
+                                       gfx::Rect* aOutLayerQuad = nullptr);
+
 
   /**
    * An array of locks that will need to be unlocked after the next composition.
    */
   nsTArray<RefPtr<TextureHost>> mUnlockAfterComposition;
 
   /**
    * An array of TextureHosts that will need to call NotifyNotUsed() after the next composition.
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -17,16 +17,17 @@
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxRect.h"                    // for gfxRect
 #include "gfxUtils.h"                   // for gfxUtils, etc
 #include "mozilla/ArrayUtils.h"         // for ArrayLength
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4, Matrix
+#include "mozilla/gfx/Triangle.h"       // for Triangle
 #include "mozilla/gfx/gfxVars.h"        // for gfxVars
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite, etc
 #include "mozilla/layers/CompositingRenderTargetOGL.h"
 #include "mozilla/layers/Effects.h"     // for EffectChain, TexturedEffect, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureSource, etc
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureSourceOGL, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAppRunner.h"
@@ -53,16 +54,19 @@ namespace mozilla {
 
 using namespace std;
 using namespace gfx;
 
 namespace layers {
 
 using namespace mozilla::gl;
 
+static const GLuint kCoordinateAttributeIndex = 0;
+static const GLuint kTexCoordinateAttributeIndex = 1;
+
 static void
 BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
                    GLenum aTexUnit, const gfx::Matrix4x4& aTransform)
 {
   MOZ_ASSERT(LOCAL_GL_TEXTURE0 <= aTexUnit && aTexUnit <= LOCAL_GL_TEXTURE31);
   aSourceMask->BindTexture(aTexUnit, gfx::SamplingFilter::LINEAR);
   aProgram->SetMaskTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
   aProgram->SetMaskLayerTransform(aTransform);
@@ -183,16 +187,17 @@ CompositorOGL::CleanupResources()
   RefPtr<GLContext> ctx = mGLContext->GetSharedContext();
   if (!ctx) {
     ctx = mGLContext;
   }
 
   if (!ctx->MakeCurrent()) {
     // Leak resources!
     mQuadVBO = 0;
+    mTriangleVBO = 0;
     mGLContext = nullptr;
     mPrograms.clear();
     return;
   }
 
   for (std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.begin();
        iter != mPrograms.end();
        iter++) {
@@ -202,16 +207,21 @@ CompositorOGL::CleanupResources()
 
   ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
   if (mQuadVBO) {
     ctx->fDeleteBuffers(1, &mQuadVBO);
     mQuadVBO = 0;
   }
 
+  if (mTriangleVBO) {
+    ctx->fDeleteBuffers(1, &mTriangleVBO);
+    mTriangleVBO = 0;
+  }
+
   mGLContext->MakeCurrent();
 
   mBlitTextureImageHelper = nullptr;
 
   mContextStateTracker.DestroyOGL(mGLContext);
 
   // On the main thread the Widget will be destroyed soon and calling MakeCurrent
   // after that could cause a crash (at least with GLX, see bug 1059793), unless
@@ -352,20 +362,21 @@ CompositorOGL::Initialize(nsCString* con
      * texture2DRect).
      */
     if (!mGLContext->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle)){
       *out_failureReason = "FEATURE_FAILURE_OPENGL_ARB_EXT";
       return false;
     }
   }
 
-  /* Create a simple quad VBO */
+  // Create a VBO for triangle vertices.
+  mGLContext->fGenBuffers(1, &mTriangleVBO);
 
+  /* Create a simple quad VBO */
   mGLContext->fGenBuffers(1, &mQuadVBO);
-  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
 
   // 4 quads, with the number of the quad (vertexID) encoded in w.
   GLfloat vertices[] = {
     0.0f, 0.0f, 0.0f, 0.0f,
     1.0f, 0.0f, 0.0f, 0.0f,
     0.0f, 1.0f, 0.0f, 0.0f,
     1.0f, 0.0f, 0.0f, 0.0f,
     0.0f, 1.0f, 0.0f, 0.0f,
@@ -388,16 +399,18 @@ CompositorOGL::Initialize(nsCString* con
     0.0f, 0.0f, 0.0f, 3.0f,
     1.0f, 0.0f, 0.0f, 3.0f,
     0.0f, 1.0f, 0.0f, 3.0f,
     1.0f, 0.0f, 0.0f, 3.0f,
     0.0f, 1.0f, 0.0f, 3.0f,
     1.0f, 1.0f, 0.0f, 3.0f,
   };
   HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+
+  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
   mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
                           verticesOnHeap.ByteLength(),
                           verticesOnHeap.Data(),
                           LOCAL_GL_STATIC_DRAW);
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 
   nsCOMPtr<nsIConsoleService>
     console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
@@ -437,52 +450,34 @@ static IntSize
 CalculatePOTSize(const IntSize& aSize, GLContext* gl)
 {
   if (CanUploadNonPowerOfTwo(gl))
     return aSize;
 
   return IntSize(RoundUpPow2(aSize.width), RoundUpPow2(aSize.height));
 }
 
-// |aRect| is the rectangle we want to draw to. We will draw it with
-// up to 4 draw commands if necessary to avoid wrapping.
-// |aTexCoordRect| is the rectangle from the texture that we want to
-// draw using the given program.
-// |aTexture| is the texture we are drawing. Its actual size can be
-// larger than the rectangle given by |texCoordRect|.
-void
-CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
-                                              const Rect& aRect,
-                                              const Rect& aTexCoordRect,
-                                              TextureSource *aTexture)
+gfx::Rect
+CompositorOGL::GetTextureCoordinates(gfx::Rect textureRect, TextureSource* aTexture)
 {
-  Rect scaledTexCoordRect = aTexCoordRect;
-
   // If the OpenGL setup does not support non-power-of-two textures then the
   // texture's width and height will have been increased to the next
   // power-of-two (unless already a power of two). In that case we must scale
   // the texture coordinates to account for that.
   if (!CanUploadNonPowerOfTwo(mGLContext)) {
     const IntSize& textureSize = aTexture->GetSize();
     const IntSize potSize = CalculatePOTSize(textureSize, mGLContext);
     if (potSize != textureSize) {
       const float xScale = (float)textureSize.width / (float)potSize.width;
       const float yScale = (float)textureSize.height / (float)potSize.height;
-      scaledTexCoordRect.Scale(xScale, yScale);
+      textureRect.Scale(xScale, yScale);
     }
   }
 
-  Rect layerRects[4];
-  Rect textureRects[4];
-  size_t rects = DecomposeIntoNoRepeatRects(aRect,
-                                            scaledTexCoordRect,
-                                            &layerRects,
-                                            &textureRects);
-
-  BindAndDrawQuads(aProg, rects, layerRects, textureRects);
+  return textureRect;
 }
 
 void
 CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget)
 {
   MOZ_ASSERT(aRenderTarget);
   const gfx::IntSize& size = aRenderTarget->mInitParams.mSize;
 
@@ -991,29 +986,57 @@ CompositorOGL::DrawQuad(const Rect& aRec
                         const EffectChain &aEffectChain,
                         Float aOpacity,
                         const gfx::Matrix4x4& aTransform,
                         const gfx::Rect& aVisibleRect)
 {
   PROFILER_LABEL("CompositorOGL", "DrawQuad",
     js::ProfileEntry::Category::GRAPHICS);
 
+  DrawGeometry(aRect, aClipRect, aEffectChain,
+               aOpacity, aTransform, aVisibleRect);
+}
+
+void
+CompositorOGL::DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+                            const gfx::IntRect& aClipRect,
+                            const EffectChain& aEffectChain,
+                            gfx::Float aOpacity,
+                            const gfx::Matrix4x4& aTransform,
+                            const gfx::Rect& aVisibleRect)
+{
+  PROFILER_LABEL("CompositorOGL", "DrawTriangle",
+    js::ProfileEntry::Category::GRAPHICS);
+
+  DrawGeometry(aTriangle, aClipRect, aEffectChain,
+               aOpacity, aTransform, aVisibleRect);
+}
+
+template<typename Geometry>
+void
+CompositorOGL::DrawGeometry(const Geometry& aGeometry,
+                            const IntRect& aClipRect,
+                            const EffectChain &aEffectChain,
+                            Float aOpacity,
+                            const gfx::Matrix4x4& aTransform,
+                            const gfx::Rect& aVisibleRect)
+{
   MOZ_ASSERT(mFrameInProgress, "frame not started");
   MOZ_ASSERT(mCurrentRenderTarget, "No destination");
 
   MakeCurrent();
 
   IntPoint offset = mCurrentRenderTarget->GetOrigin();
   IntSize size = mCurrentRenderTarget->GetSize();
 
   Rect renderBound(0, 0, size.width, size.height);
   renderBound.IntersectRect(renderBound, Rect(aClipRect));
   renderBound.MoveBy(offset);
 
-  Rect destRect = aTransform.TransformAndClipBounds(aRect, renderBound);
+  Rect destRect = aTransform.TransformAndClipBounds(aGeometry, renderBound);
 
   // XXX: This doesn't handle 3D transforms. It also doesn't handled rotated
   //      quads. Fix me.
   mPixelsFilled += destRect.width * destRect.height;
 
   // Do a simple culling if this rect is out of target buffer.
   // Inflate a small size to avoid some numerical imprecision issue.
   destRect.Inflate(1, 1);
@@ -1101,22 +1124,26 @@ CompositorOGL::DrawQuad(const Rect& aRec
   // could be visible
   bool bEnableAA = gfxPrefs::LayersDEAAEnabled() &&
                    !aTransform.Is2DIntegerTranslation();
 
   bool colorMatrix = aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX];
   ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect,
                                               maskType, blendMode, colorMatrix,
                                               bEnableAA);
+
   config.SetOpacity(aOpacity != 1.f);
+  ApplyPrimitiveConfig(config, aGeometry);
+
   ShaderProgramOGL *program = GetShaderProgramFor(config);
   ActivateProgram(program);
   program->SetProjectionMatrix(mProjMatrix);
   program->SetLayerTransform(aTransform);
   LayerScope::SetLayerTransform(aTransform);
+
   if (colorMatrix) {
       EffectColorMatrix* effectColorMatrix =
         static_cast<EffectColorMatrix*>(aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX].get());
       program->SetColorMatrix(effectColorMatrix->mColorMatrix);
   }
 
   if (BlendOpIsMixBlendMode(blendMode)) {
     gfx::Matrix4x4 backdropTransform;
@@ -1124,28 +1151,30 @@ CompositorOGL::DrawQuad(const Rect& aRec
     if (gl()->IsExtensionSupported(GLContext::NV_texture_barrier)) {
       // The NV_texture_barrier extension lets us read directly from the
       // backbuffer. Let's do that.
       // We need to tell OpenGL about this, so that it can make sure everything
       // on the GPU is happening in the right order.
       gl()->fTextureBarrier();
       mixBlendBackdrop = mCurrentRenderTarget->GetTextureHandle();
     } else {
-      gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, &backdropTransform);
+      gfx::IntRect rect = ComputeBackdropCopyRect(aGeometry, aClipRect,
+                                                  aTransform, &backdropTransform);
       mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
       createdMixBlendBackdropTexture = true;
     }
     program->SetBackdropTransform(backdropTransform);
   }
 
   program->SetRenderOffset(offset.x, offset.y);
   LayerScope::SetRenderOffset(offset.x, offset.y);
 
   if (aOpacity != 1.f)
     program->SetLayerOpacity(aOpacity);
+
   if (config.mFeatures & ENABLE_TEXTURE_RECT) {
     TextureSourceOGL* source = nullptr;
     if (aEffectChain.mPrimaryEffect->mType == EffectTypes::COMPONENT_ALPHA) {
       EffectComponentAlpha* effectComponentAlpha =
         static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
       source = effectComponentAlpha->mOnWhite->AsSourceOGL();
     } else {
       TexturedEffect* texturedEffect =
@@ -1234,17 +1263,17 @@ CompositorOGL::DrawQuad(const Rect& aRec
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
       }
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1);
       }
 
       didSetBlendMode = SetBlendMode(gl(), blendMode);
 
-      BindAndDrawQuad(program, aRect);
+      BindAndDrawGeometry(program, aGeometry);
     }
     break;
 
   case EffectTypes::RGB: {
       TexturedEffect* texturedEffect =
           static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
       TextureSource *source = texturedEffect->mTexture;
 
@@ -1261,17 +1290,18 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
       if (maskType != MaskType::MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
       }
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
       }
 
-      BindAndDrawQuadWithTextureRect(program, aRect, texturedEffect->mTextureCoords, source);
+      BindAndDrawGeometryWithTextureRect(program, aGeometry,
+                                         texturedEffect->mTextureCoords, source);
     }
     break;
   case EffectTypes::YCBCR: {
       EffectYCbCr* effectYCbCr =
         static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
       TextureSource* sourceYCbCr = effectYCbCr->mTexture;
       const int Y = 0, Cb = 1, Cr = 2;
       TextureSourceOGL* sourceY =  sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
@@ -1292,20 +1322,20 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
       if (maskType != MaskType::MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
       }
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
       }
       didSetBlendMode = SetBlendMode(gl(), blendMode);
-      BindAndDrawQuadWithTextureRect(program,
-                                     aRect,
-                                     effectYCbCr->mTextureCoords,
-                                     sourceYCbCr->GetSubSource(Y));
+      BindAndDrawGeometryWithTextureRect(program,
+                                         aGeometry,
+                                         effectYCbCr->mTextureCoords,
+                                         sourceYCbCr->GetSubSource(Y));
     }
     break;
   case EffectTypes::NV12: {
       EffectNV12* effectNV12 =
         static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
       TextureSource* sourceNV12 = effectNV12->mTexture;
       const int Y = 0, CbCr = 1;
       TextureSourceOGL* sourceY =  sourceNV12->GetSubSource(Y)->AsSourceOGL();
@@ -1329,20 +1359,20 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
       if (maskType != MaskType::MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
       }
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
       }
       didSetBlendMode = SetBlendMode(gl(), blendMode);
-      BindAndDrawQuadWithTextureRect(program,
-                                     aRect,
-                                     effectNV12->mTextureCoords,
-                                     sourceNV12->GetSubSource(Y));
+      BindAndDrawGeometryWithTextureRect(program,
+                                         aGeometry,
+                                         effectNV12->mTextureCoords,
+                                         sourceNV12->GetSubSource(Y));
     }
     break;
   case EffectTypes::RENDER_TARGET: {
       EffectRenderTarget* effectRenderTarget =
         static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
       RefPtr<CompositingRenderTargetOGL> surface
         = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get());
 
@@ -1368,17 +1398,17 @@ CompositorOGL::DrawQuad(const Rect& aRec
         program->SetTexCoordMultiplier(surface->GetSize().width,
                                        surface->GetSize().height);
       }
 
       // Drawing is always flipped, but when copying between surfaces we want to avoid
       // this. Pass true for the flip parameter to introduce a second flip
       // that cancels the other one out.
       didSetBlendMode = SetBlendMode(gl(), blendMode);
-      BindAndDrawQuad(program, aRect);
+      BindAndDrawGeometry(program, aGeometry);
     }
     break;
   case EffectTypes::COMPONENT_ALPHA: {
       MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
       MOZ_ASSERT(blendMode == gfx::CompositionOp::OP_OVER, "Can't support blend modes with component alpha!");
       EffectComponentAlpha* effectComponentAlpha =
         static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
       TextureSourceOGL* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceOGL();
@@ -1399,29 +1429,29 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
       if (maskType != MaskType::MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
       }
       // Pass 1.
       gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_COLOR,
                                LOCAL_GL_ONE, LOCAL_GL_ONE);
       program->SetTexturePass2(false);
-      BindAndDrawQuadWithTextureRect(program,
-                                     aRect,
-                                     effectComponentAlpha->mTextureCoords,
-                                     effectComponentAlpha->mOnBlack);
+      BindAndDrawGeometryWithTextureRect(program,
+                                         aGeometry,
+                                         effectComponentAlpha->mTextureCoords,
+                                         effectComponentAlpha->mOnBlack);
 
       // Pass 2.
       gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE,
                                LOCAL_GL_ONE, LOCAL_GL_ONE);
       program->SetTexturePass2(true);
-      BindAndDrawQuadWithTextureRect(program,
-                                     aRect,
-                                     effectComponentAlpha->mTextureCoords,
-                                     effectComponentAlpha->mOnBlack);
+      BindAndDrawGeometryWithTextureRect(program,
+                                         aGeometry,
+                                         effectComponentAlpha->mTextureCoords,
+                                         effectComponentAlpha->mOnBlack);
 
       mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                      LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
     }
     break;
   default:
     MOZ_ASSERT(false, "Unhandled effect type");
     break;
@@ -1432,17 +1462,128 @@ CompositorOGL::DrawQuad(const Rect& aRec
                              LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
   }
   if (createdMixBlendBackdropTexture) {
     gl()->fDeleteTextures(1, &mixBlendBackdrop);
   }
 
   // in case rendering has used some other GL context
   MakeCurrent();
-  LayerScope::DrawEnd(mGLContext, aEffectChain, aRect.width, aRect.height);
+
+  LayerScope::DrawEnd(mGLContext, aEffectChain,
+                      aGeometry.width, aGeometry.height);
+}
+
+void
+CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+                                   const gfx::Rect& aRect,
+                                   const gfx::Rect& aTextureRect)
+{
+  BindAndDrawQuad(aProgram, aRect, aTextureRect);
+}
+
+void
+CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+                                   const gfx::TexturedTriangle& aTriangle,
+                                   const gfx::Rect& aTextureRect)
+{
+  NS_ASSERTION(aProgram->HasInitialized(), "Shader program not correctly initialized");
+
+  const gfx::TexturedTriangle& t = aTriangle;
+  const gfx::Triangle& tex = t.textureCoords;
+
+  GLfloat vertices[] = {
+    t.p1.x, t.p1.y, 0.0f, 1.0f, tex.p1.x, tex.p1.y,
+    t.p2.x, t.p2.y, 0.0f, 1.0f, tex.p2.x, tex.p2.y,
+    t.p3.x, t.p3.y, 0.0f, 1.0f, tex.p3.x, tex.p3.y
+  };
+
+  HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTriangleVBO);
+  mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
+                          verticesOnHeap.ByteLength(),
+                          verticesOnHeap.Data(),
+                          LOCAL_GL_STREAM_DRAW);
+
+  const GLsizei stride = 6 * sizeof(GLfloat);
+  InitializeVAO(kCoordinateAttributeIndex, 4, stride, 0);
+  InitializeVAO(kTexCoordinateAttributeIndex, 2, stride, 4 * sizeof(GLfloat));
+
+  mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 3);
+
+  mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+  mGLContext->fDisableVertexAttribArray(kTexCoordinateAttributeIndex);
+  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+}
+
+// |aRect| is the rectangle we want to draw to. We will draw it with
+// up to 4 draw commands if necessary to avoid wrapping.
+// |aTexCoordRect| is the rectangle from the texture that we want to
+// draw using the given program.
+// |aTexture| is the texture we are drawing. Its actual size can be
+// larger than the rectangle given by |texCoordRect|.
+void
+CompositorOGL::BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+                                                  const Rect& aRect,
+                                                  const Rect& aTexCoordRect,
+                                                  TextureSource *aTexture)
+{
+  Rect scaledTexCoordRect = GetTextureCoordinates(aTexCoordRect, aTexture);
+  Rect layerRects[4];
+  Rect textureRects[4];
+  size_t rects = DecomposeIntoNoRepeatRects(aRect,
+                                            scaledTexCoordRect,
+                                            &layerRects,
+                                            &textureRects);
+
+  BindAndDrawQuads(aProg, rects, layerRects, textureRects);
+}
+
+void
+CompositorOGL::BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+                                                  const gfx::TexturedTriangle& aTriangle,
+                                                  const gfx::Rect& aTexCoordRect,
+                                                  TextureSource *aTexture)
+{
+  BindAndDrawGeometry(aProg, aTriangle,
+                      GetTextureCoordinates(aTexCoordRect, aTexture));
+}
+
+void
+CompositorOGL::BindAndDrawQuads(ShaderProgramOGL *aProg,
+                                int aQuads,
+                                const Rect* aLayerRects,
+                                const Rect* aTextureRects)
+{
+  NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
+
+  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+  InitializeVAO(kCoordinateAttributeIndex, 4, 0, 0);
+
+  aProg->SetLayerRects(aLayerRects);
+  if (aProg->GetTextureCount() > 0) {
+    aProg->SetTextureRects(aTextureRects);
+  }
+
+  // We are using GL_TRIANGLES here because the Mac Intel drivers fail to properly
+  // process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
+  mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
+  mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+  LayerScope::SetDrawRects(aQuads, aLayerRects, aTextureRects);
+}
+
+void
+CompositorOGL::InitializeVAO(const GLuint aAttrib, const GLint aComponents,
+                             const GLsizei aStride, const size_t aOffset)
+{
+  mGLContext->fVertexAttribPointer(aAttrib, aComponents, LOCAL_GL_FLOAT,
+                                   LOCAL_GL_FALSE, aStride,
+                                   reinterpret_cast<GLvoid*>(aOffset));
+  mGLContext->fEnableVertexAttribArray(aAttrib);
 }
 
 void
 CompositorOGL::EndFrame()
 {
   PROFILER_LABEL("CompositorOGL", "EndFrame",
     js::ProfileEntry::Category::GRAPHICS);
 
@@ -1615,43 +1756,16 @@ void
 CompositorOGL::MakeCurrent(MakeCurrentFlags aFlags) {
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return;
   }
   mGLContext->MakeCurrent(aFlags & ForceMakeCurrent);
 }
 
-void
-CompositorOGL::BindAndDrawQuads(ShaderProgramOGL *aProg,
-                                int aQuads,
-                                const Rect* aLayerRects,
-                                const Rect* aTextureRects)
-{
-  NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
-
-  const GLuint coordAttribIndex = 0;
-
-  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
-  mGLContext->fVertexAttribPointer(coordAttribIndex, 4,
-                                   LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
-                                   (GLvoid*) 0);
-  mGLContext->fEnableVertexAttribArray(coordAttribIndex);
-
-  aProg->SetLayerRects(aLayerRects);
-  if (aProg->GetTextureCount() > 0) {
-    aProg->SetTextureRects(aTextureRects);
-  }
-
-  // We are using GL_TRIANGLES here because the Mac Intel drivers fail to properly
-  // process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
-  mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
-  LayerScope::SetDrawRects(aQuads, aLayerRects, aTextureRects);
-}
-
 GLBlitTextureImageHelper*
 CompositorOGL::BlitTextureImageHelper()
 {
     if (!mBlitTextureImageHelper) {
         mBlitTextureImageHelper = MakeUnique<GLBlitTextureImageHelper>(this);
     }
 
     return mBlitTextureImageHelper.get();
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -15,16 +15,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override, final
 #include "mozilla/RefPtr.h"             // for already_AddRefed, RefPtr
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
+#include "mozilla/gfx/Triangle.h"       // for Triangle
 #include "mozilla/gfx/Types.h"          // for Float, SurfaceFormat, etc
 #include "mozilla/layers/Compositor.h"  // for SurfaceInitMode, Compositor, etc
 #include "mozilla/layers/CompositorTypes.h"  // for MaskType::MaskType::NumMaskTypes, etc
 #include "mozilla/layers/LayersTypes.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION, NS_WARNING
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsTArray.h"                   // for AutoTArray, nsTArray, etc
@@ -206,16 +207,23 @@ public:
 
   virtual void DrawQuad(const gfx::Rect& aRect,
                         const gfx::IntRect& aClipRect,
                         const EffectChain &aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4& aTransform,
                         const gfx::Rect& aVisibleRect) override;
 
+  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 void EndFrame() override;
   virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
 
   virtual bool SupportsPartialTextureUpdate() override;
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
   {
     if (!mGLContext)
@@ -304,16 +312,24 @@ public:
     return gfx::IntSize (mSurfaceSize.width, mSurfaceSize.height);
   }
 
   const ScreenPoint& GetScreenRenderOffset() const {
     return mRenderOffset;
   }
 
 private:
+  template<typename Geometry>
+  void DrawGeometry(const Geometry& aGeometry,
+                    const gfx::IntRect& aClipRect,
+                    const EffectChain &aEffectChain,
+                    gfx::Float aOpacity,
+                    const gfx::Matrix4x4& aTransform,
+                    const gfx::Rect& aVisibleRect);
+
   void PrepareViewport(CompositingRenderTargetOGL *aRenderTarget);
 
   /** Widget associated with this compositor */
   LayoutDeviceIntSize mWidgetSize;
   RefPtr<GLContext> mGLContext;
   UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
   gfx::Matrix4x4 mProjMatrix;
 
@@ -334,16 +350,22 @@ private:
 #endif
 
   /**
    * VBO that has some basics in it for a textured quad, including vertex
    * coords and texcoords.
    */
   GLuint mQuadVBO;
 
+
+  /**
+  * VBO that stores dynamic triangle geometry.
+  */
+  GLuint mTriangleVBO;
+
   bool mHasBGRA;
 
   /**
    * When rendering to some EGL surfaces (e.g. on Android), we rely on being told
    * about size changes (via SetSurfaceSize) rather than pulling this information
    * from the widget.
    */
   bool mUseExternalSurfaceSize;
@@ -368,51 +390,93 @@ private:
                           gfx::IntRect *aClipRectOut = nullptr,
                           gfx::IntRect *aRenderBoundsOut = nullptr) override;
 
   ShaderConfigOGL GetShaderConfigFor(Effect *aEffect,
                                      MaskType aMask = MaskType::MaskNone,
                                      gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER,
                                      bool aColorMatrix = false,
                                      bool aDEAAEnabled = false) const;
+
   ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL &aConfig);
 
+  void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+                            const gfx::Rect&)
+  {
+    aConfig.SetDynamicGeometry(false);
+  }
+
+  void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+                            const gfx::TexturedTriangle&)
+  {
+    aConfig.SetDynamicGeometry(true);
+  }
+
   /**
    * Create a FBO backed by a texture.
    * Note that the texture target type will be
    * of the type returned by FBOTextureTarget; different
    * shaders are required to sample from the different
    * texture types.
    */
   void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
                             GLuint aSourceFrameBuffer,
                             GLuint *aFBO, GLuint *aTexture);
+
   GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer);
 
+  gfx::Point3D GetLineCoefficients(const gfx::Point& aPoint1,
+                                   const gfx::Point& aPoint2);
+
+  void ActivateProgram(ShaderProgramOGL *aProg);
+
+  void CleanupResources();
+
   void BindAndDrawQuads(ShaderProgramOGL *aProg,
                         int aQuads,
                         const gfx::Rect* aLayerRect,
                         const gfx::Rect* aTextureRect);
+
   void BindAndDrawQuad(ShaderProgramOGL *aProg,
                        const gfx::Rect& aLayerRect,
-                       const gfx::Rect& aTextureRect = gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f)) {
+                       const gfx::Rect& aTextureRect =
+                         gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f))
+  {
     gfx::Rect layerRects[4];
     gfx::Rect textureRects[4];
     layerRects[0] = aLayerRect;
     textureRects[0] = aTextureRect;
     BindAndDrawQuads(aProg, 1, layerRects, textureRects);
   }
-  void BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
-                                      const gfx::Rect& aRect,
-                                      const gfx::Rect& aTexCoordRect,
-                                      TextureSource *aTexture);
-  gfx::Point3D GetLineCoefficients(const gfx::Point& aPoint1,
-                                   const gfx::Point& aPoint2);
-  void ActivateProgram(ShaderProgramOGL *aProg);
-  void CleanupResources();
+
+  void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+                           const gfx::Rect& aRect,
+                           const gfx::Rect& aTextureRect =
+                             gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+  void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+                           const gfx::TexturedTriangle& aTriangle,
+                           const gfx::Rect& aTextureRect =
+                             gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+  void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+                                          const gfx::Rect& aRect,
+                                          const gfx::Rect& aTexCoordRect,
+                                          TextureSource *aTexture);
+
+  void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+                                         const gfx::TexturedTriangle& aTriangle,
+                                         const gfx::Rect& aTexCoordRect,
+                                         TextureSource *aTexture);
+
+  void InitializeVAO(const GLuint aAttribIndex, const GLint aComponents,
+                     const GLsizei aStride, const size_t aOffset);
+
+  gfx::Rect GetTextureCoordinates(gfx::Rect textureRect,
+                                  TextureSource* aTexture);
 
   /**
    * Bind the texture behind the current render target as the backdrop for a
    * mix-blend shader.
    */
   void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit);
 
   /**
--- a/gfx/layers/opengl/OGLShaderProgram.cpp
+++ b/gfx/layers/opengl/OGLShaderProgram.cpp
@@ -156,16 +156,22 @@ ShaderConfigOGL::SetDEAA(bool aEnabled)
 }
 
 void
 ShaderConfigOGL::SetCompositionOp(gfx::CompositionOp aOp)
 {
   mCompositionOp = aOp;
 }
 
+void
+ShaderConfigOGL::SetDynamicGeometry(bool aEnabled)
+{
+  SetFeature(ENABLE_DYNAMIC_GEOMETRY, aEnabled);
+}
+
 /* static */ ProgramProfileOGL
 ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
 {
   ProgramProfileOGL result;
   ostringstream fs, vs;
 
   AddUniforms(result);
 
@@ -182,37 +188,49 @@ ProgramProfileOGL::GetProfileFor(ShaderC
   if (aConfig.mFeatures & ENABLE_DEAA) {
     vs << "uniform mat4 uLayerTransformInverse;" << endl;
     vs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
     vs << "uniform vec2 uVisibleCenter;" << endl;
     vs << "uniform vec2 uViewportSize;" << endl;
   }
   vs << "uniform vec2 uRenderTargetOffset;" << endl;
   vs << "attribute vec4 aCoord;" << endl;
+  result.mAttributes.AppendElement(Pair<nsCString, GLuint> {"aCoord", 0});
 
   if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
     vs << "uniform mat4 uTextureTransform;" << endl;
     vs << "uniform vec4 uTextureRects[4];" << endl;
     vs << "varying vec2 vTexCoord;" << endl;
+
+    if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+      vs << "attribute vec2 aTexCoord;" << endl;
+      result.mAttributes.AppendElement(Pair<nsCString, GLuint> {"aTexCoord", 1});
+    }
   }
 
   if (BlendOpIsMixBlendMode(blendOp)) {
     vs << "uniform mat4 uBackdropTransform;" << endl;
     vs << "varying vec2 vBackdropCoord;" << endl;
   }
 
   if (aConfig.mFeatures & ENABLE_MASK) {
     vs << "uniform mat4 uMaskTransform;" << endl;
     vs << "varying vec3 vMaskCoord;" << endl;
   }
 
   vs << "void main() {" << endl;
-  vs << "  int vertexID = int(aCoord.w);" << endl;
-  vs << "  vec4 layerRect = uLayerRects[vertexID];" << endl;
-  vs << "  vec4 finalPosition = vec4(aCoord.xy * layerRect.zw + layerRect.xy, 0.0, 1.0);" << endl;
+
+  if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+    vs << "  vec4 finalPosition = vec4(aCoord.xy, 0.0, 1.0);" << endl;
+  } else {
+    vs << "  int vertexID = int(aCoord.w);" << endl;
+    vs << "  vec4 layerRect = uLayerRects[vertexID];" << endl;
+    vs << "  vec4 finalPosition = vec4(aCoord.xy * layerRect.zw + layerRect.xy, 0.0, 1.0);" << endl;
+  }
+
   vs << "  finalPosition = uLayerTransform * finalPosition;" << endl;
 
   if (aConfig.mFeatures & ENABLE_DEAA) {
     // XXX kip - The DEAA shader could be made simpler if we switch to
     //           using dynamic vertex buffers instead of sending everything
     //           in through uniforms.  This would enable passing information
     //           about how to dilate each vertex explicitly and eliminate the
     //           need to extrapolate this with the sub-pixel coverage
@@ -248,32 +266,43 @@ ProgramProfileOGL::GetProfileFor(ShaderC
     vs << "    vec4 visibleCenter = uLayerTransform * vec4(uVisibleCenter, 0.0, 1.0);" << endl;
     vs << "    vec2 dilateDir = finalPosition.xy / finalPosition.w - visibleCenter.xy / visibleCenter.w;" << endl;
     vs << "    vec2 offset = sign(dilateDir) * 0.5;" << endl;
     vs << "    finalPosition.xy += offset * finalPosition.w;" << endl;
     if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
       // We must adjust the texture coordinates to compensate for the dilation
       vs << "    coordAdjusted = uLayerTransformInverse * finalPosition;" << endl;
       vs << "    coordAdjusted /= coordAdjusted.w;" << endl;
-      vs << "    coordAdjusted.xy -= layerRect.xy;" << endl;
-      vs << "    coordAdjusted.xy /= layerRect.zw;" << endl;
+
+      if (!(aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY)) {
+        vs << "    coordAdjusted.xy -= layerRect.xy;" << endl;
+        vs << "    coordAdjusted.xy /= layerRect.zw;" << endl;
+      }
     }
     vs << "  }" << endl;
 
     if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+      if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+        vs << "  vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;" << endl;
+      } else {
+        vs << "  vec4 textureRect = uTextureRects[vertexID];" << endl;
+        vs << "  vec2 texCoord = coordAdjusted.xy * textureRect.zw + textureRect.xy;" << endl;
+        vs << "  vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
+      }
+    }
+  } else if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+    if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+      vs << "  vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;" << endl;
+    } else {
       vs << "  vec4 textureRect = uTextureRects[vertexID];" << endl;
-      vs << "  vec2 texCoord = coordAdjusted.xy * textureRect.zw + textureRect.xy;" << endl;
+      vs << "  vec2 texCoord = aCoord.xy * textureRect.zw + textureRect.xy;" << endl;
       vs << "  vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
     }
+  }
 
-  } else if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
-    vs << "  vec4 textureRect = uTextureRects[vertexID];" << endl;
-    vs << "  vec2 texCoord = aCoord.xy * textureRect.zw + textureRect.xy;" << endl;
-    vs << "  vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
-  }
   if (aConfig.mFeatures & ENABLE_MASK) {
     vs << "  vMaskCoord.xy = (uMaskTransform * (finalPosition / finalPosition.w)).xy;" << endl;
     // correct for perspective correct interpolation, see comment in D3D11 shader
     vs << "  vMaskCoord.z = 1.0;" << endl;
     vs << "  vMaskCoord *= finalPosition.w;" << endl;
   }
   vs << "  finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
   vs << "  finalPosition = uMatrixProj * finalPosition;" << endl;
@@ -857,16 +886,21 @@ ShaderProgramOGL::CreateProgram(const ch
 
   if (!vertexShader || !fragmentShader)
     return false;
 
   GLint result = mGL->fCreateProgram();
   mGL->fAttachShader(result, vertexShader);
   mGL->fAttachShader(result, fragmentShader);
 
+  for (Pair<nsCString, GLuint>& attribute : mProfile.mAttributes) {
+    mGL->fBindAttribLocation(result, attribute.second(),
+                             attribute.first().get());
+  }
+
   mGL->fLinkProgram(result);
 
   GLint success, len;
   mGL->fGetProgramiv(result, LOCAL_GL_LINK_STATUS, &success);
   mGL->fGetProgramiv(result, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
   /* Even if linking is successful, there may still be warnings.  Print them
    * in a debug build.  The > 10 is to catch silly compilers that might put
    * some whitespace in the log but otherwise leave it empty.
--- a/gfx/layers/opengl/OGLShaderProgram.h
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_OGLSHADERPROGRAM_H
 #define GFX_OGLSHADERPROGRAM_H
 
 #include "GLContext.h"                  // for fast inlines of glUniform*
 #include "gfxTypes.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
+#include "mozilla/Pair.h"               // for Pair
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsTArray.h"                   // for nsTArray
 #include "mozilla/layers/CompositorTypes.h"
@@ -34,17 +35,18 @@ enum ShaderFeatures {
   ENABLE_TEXTURE_COMPONENT_ALPHA=0x20,
   ENABLE_TEXTURE_NO_ALPHA=0x40,
   ENABLE_TEXTURE_RB_SWAP=0x80,
   ENABLE_OPACITY=0x100,
   ENABLE_BLUR=0x200,
   ENABLE_COLOR_MATRIX=0x400,
   ENABLE_MASK=0x800,
   ENABLE_NO_PREMUL_ALPHA=0x1000,
-  ENABLE_DEAA=0x2000
+  ENABLE_DEAA=0x2000,
+  ENABLE_DYNAMIC_GEOMETRY=0x4000
 };
 
 class KnownUniform {
 public:
   // this needs to be kept in sync with strings in 'AddUniforms'
   enum KnownUniformName {
     NotAKnownUniform = -1,
 
@@ -221,16 +223,17 @@ public:
   void SetNV12(bool aEnabled);
   void SetComponentAlpha(bool aEnabled);
   void SetColorMatrix(bool aEnabled);
   void SetBlur(bool aEnabled);
   void SetMask(bool aEnabled);
   void SetDEAA(bool aEnabled);
   void SetCompositionOp(gfx::CompositionOp aOp);
   void SetNoPremultipliedAlpha();
+  void SetDynamicGeometry(bool aEnabled);
 
   bool operator< (const ShaderConfigOGL& other) const {
     return mFeatures < other.mFeatures ||
            (mFeatures == other.mFeatures &&
             (int)mCompositionOp < (int)other.mCompositionOp);
   }
 
 public:
@@ -272,16 +275,19 @@ struct ProgramProfileOGL
    * ShaderConfigOGL
    */
   static ProgramProfileOGL GetProfileFor(ShaderConfigOGL aConfig);
 
   // the source code for the program's shaders
   std::string mVertexShaderString;
   std::string mFragmentShaderString;
 
+  // the vertex attributes
+  nsTArray<Pair<nsCString, GLuint>> mAttributes;
+
   KnownUniform mUniforms[KnownUniform::KnownUniformCount];
   nsTArray<const char *> mDefines;
   size_t mTextureCount;
 
   ProgramProfileOGL() :
     mTextureCount(0)
   {}
 
--- a/gfx/tests/gtest/TestPolygon.cpp
+++ b/gfx/tests/gtest/TestPolygon.cpp
@@ -18,17 +18,17 @@ TEST(Polygon3D, TriangulateRectangle)
 {
   const Polygon3D p {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f)
   };
 
-  const nsTArray<Triangle> triangles = p.Triangulate();
+  const nsTArray<Triangle> triangles = p.ToTriangles();
   const nsTArray<Triangle> expected = {
     Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f)),
     Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
   };
 
   AssertArrayEQ(triangles, expected);
 }
 
@@ -37,17 +37,17 @@ TEST(Polygon3D, TriangulatePentagon)
   const Polygon3D p {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(0.5f, 1.5f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f)
   };
 
-  const nsTArray<Triangle> triangles = p.Triangulate();
+  const nsTArray<Triangle> triangles = p.ToTriangles();
   const nsTArray<Triangle> expected = {
     Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(0.5f, 1.5f)),
     Triangle(Point(0.0f, 0.0f), Point(0.5f, 1.5f), Point(1.0f, 1.0f)),
     Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
   };
 
   AssertArrayEQ(triangles, expected);
 }