Bug 1025138 - Integrate DrawTargetTiled preffed off. r=mattwoodrow
authorNicolas Silva <nsilva@mozilla.com>
Wed, 06 Aug 2014 14:40:03 +0200
changeset 219809 7aee66d772f47664b902e68fe9a531d17b274a75
parent 219808 8deaf054ca332f7bb7b11aad72d556b11f5f4aa7
child 219810 c940f5389094246ff8d23a189581afb9f651bc80
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1025138
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1025138 - Integrate DrawTargetTiled preffed off. r=mattwoodrow
gfx/2d/2D.h
gfx/2d/DrawTargetCG.cpp
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetDual.h
gfx/2d/DrawTargetTiled.cpp
gfx/2d/DrawTargetTiled.h
gfx/2d/FilterNodeD2D1.cpp
gfx/layers/TiledLayerBuffer.h
gfx/layers/client/SimpleTiledContentClient.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/composite/TiledContentHost.h
gfx/tests/gtest/TestTiledLayerBuffer.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/src/init/all.js
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -971,17 +971,18 @@ public:
 
   SurfaceFormat GetFormat() { return mFormat; }
 
   /** Tries to get a native surface for a DrawTarget, this may fail if the
    * draw target cannot convert to this surface type.
    */
   virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
 
-  virtual bool IsDualDrawTarget() { return false; }
+  virtual bool IsDualDrawTarget() const { return false; }
+  virtual bool IsTiledDrawTarget() const { return false; }
 
   void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
     mUserData.Add(key, userData, destroy);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
   }
 
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -1589,18 +1589,19 @@ DrawTargetCG::MarkChanged()
     }
     mSnapshot = nullptr;
   }
 }
 
 CGContextRef
 BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
 {
-  if (aDT->GetBackendType() == BackendType::COREGRAPHICS ||
-      aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) {
+  if ((aDT->GetBackendType() == BackendType::COREGRAPHICS ||
+       aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) &&
+      !aDT->IsTiledDrawTarget() && !aDT->IsDualDrawTarget()) {
     DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
     cgDT->MarkChanged();
 
     // swap out the context
     CGContextRef cg = cgDT->mCg;
     cgDT->mCg = nullptr;
 
     // save the state to make it easier for callers to avoid mucking with things
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1418,17 +1418,18 @@ DrawTargetCairo::GetUserSpaceClip()
   cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2);
   return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats
 }
 
 cairo_t*
 BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT)
 {
   if (aDT->GetBackendType() != BackendType::CAIRO ||
-      aDT->IsDualDrawTarget()) {
+      aDT->IsDualDrawTarget() ||
+      aDT->IsTiledDrawTarget()) {
     return nullptr;
   }
   DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
 
   cairoDT->WillChange();
 
   // save the state to make it easier for callers to avoid mucking with things
   cairo_save(cairoDT->mContext);
@@ -1440,17 +1441,18 @@ BorrowedCairoContext::BorrowCairoContext
   return cairo;
 }
 
 void
 BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT,
                                                      cairo_t* aCairo)
 {
   if (aDT->GetBackendType() != BackendType::CAIRO ||
-      aDT->IsDualDrawTarget()) {
+      aDT->IsDualDrawTarget() ||
+      aDT->IsTiledDrawTarget()) {
     return;
   }
   DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
 
   cairo_restore(aCairo);
   cairoDT->mContext = aCairo;
 }
 
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -144,17 +144,17 @@ public:
     return mA->CreateFilter(aType);
   }
 
   virtual void *GetNativeSurface(NativeSurfaceType aType)
   {
     return nullptr;
   }
 
-  virtual bool IsDualDrawTarget()
+  virtual bool IsDualDrawTarget() const
   {
     return true;
   }
      
 private:
   RefPtr<DrawTarget> mA;
   RefPtr<DrawTarget> mB;
 };
--- a/gfx/2d/DrawTargetTiled.cpp
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -16,22 +16,24 @@ DrawTargetTiled::DrawTargetTiled()
 
 bool
 DrawTargetTiled::Init(const TileSet& aTiles)
 {
   if (!aTiles.mTileCount) {
     return false;
   }
 
-  mTiles.resize(aTiles.mTileCount);
-  memcpy(&mTiles.front(), aTiles.mTiles, aTiles.mTileCount * sizeof(Tile));
-
-  for (size_t i = 0; i < mTiles.size(); i++) {
-    if (mTiles[0].mDrawTarget->GetFormat() != mTiles[i].mDrawTarget->GetFormat() ||
-        mTiles[0].mDrawTarget->GetBackendType() != mTiles[i].mDrawTarget->GetBackendType()) {
+  mTiles.reserve(aTiles.mTileCount);
+  for (size_t i = 0; i < aTiles.mTileCount; ++i) {
+    mTiles.push_back(aTiles.mTiles[i]);
+    if (!aTiles.mTiles[i].mDrawTarget) {
+      return false;
+    }
+    if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() ||
+        mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) {
       return false;
     }
     uint32_t newXMost = max(mRect.XMost(),
                             mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
     uint32_t newYMost = max(mRect.YMost(),
                             mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
     mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
     mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
--- a/gfx/2d/DrawTargetTiled.h
+++ b/gfx/2d/DrawTargetTiled.h
@@ -16,16 +16,18 @@ namespace gfx {
 
 class DrawTargetTiled : public DrawTarget
 {
 public:
   DrawTargetTiled();
 
   bool Init(const TileSet& mTiles);
 
+  virtual bool IsTiledDrawTarget() const { return true; }
+
   virtual DrawTargetType GetType() const MOZ_OVERRIDE { return mTiles[0].mDrawTarget->GetType(); }
   virtual BackendType GetBackendType() const { return mTiles[0].mDrawTarget->GetBackendType(); }
   virtual TemporaryRef<SourceSurface> Snapshot();
   virtual IntSize GetSize() { return IntSize(mRect.XMost(), mRect.YMost()); }
 
   virtual void Flush();
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
--- a/gfx/2d/FilterNodeD2D1.cpp
+++ b/gfx/2d/FilterNodeD2D1.cpp
@@ -123,16 +123,20 @@ D2D1_CHANNEL_SELECTOR D2DChannelSelector
   }
 
   MOZ_CRASH("Unknown enum value!");
   return D2D1_CHANNEL_SELECTOR_R;
 }
 
 TemporaryRef<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
 {
+  if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
+      MOZ_CRASH("Incompatible draw target type!");
+      return nullptr;
+  }
   switch (aDT->GetBackendType()) {
     case BackendType::DIRECT2D1_1:
       return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
     case BackendType::DIRECT2D:
       return static_cast<DrawTargetD2D*>(aDT)->GetImageForSurface(aSurface);
     default:
       MOZ_CRASH("Unknown draw target type!");
       return nullptr;
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -501,16 +501,21 @@ TiledLayerBuffer<Derived, Tile>::Update(
       newRetainedTiles[index] = newTile;
 
       y += height;
     }
 
     x += width;
   }
 
+  AsDerived().PostValidate(aPaintRegion);
+  for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
+    AsDerived().UnlockTile(newRetainedTiles[i]);
+  }
+
   // At this point, oldTileCount should be zero
   NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");
 
   mRetainedTiles = newRetainedTiles;
   mValidRegion = aNewValidRegion;
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
--- a/gfx/layers/client/SimpleTiledContentClient.h
+++ b/gfx/layers/client/SimpleTiledContentClient.h
@@ -125,16 +125,19 @@ private:
                                     const nsIntPoint& aTileOrigin,
                                     const nsIntRegion& aDirtyRect);
 
   SimpleTiledLayerTile GetPlaceholderTile() const { return SimpleTiledLayerTile(); }
 
   void ReleaseTile(SimpleTiledLayerTile aTile) { aTile.Release(); }
 
   void SwapTiles(SimpleTiledLayerTile& aTileA, SimpleTiledLayerTile& aTileB) { std::swap(aTileA, aTileB); }
+
+  void PostValidate(const nsIntRegion& aPaintRegion) {}
+  void UnlockTile(SimpleTiledLayerTile aTile) {}
 };
 
 class SimpleTiledContentClient : public CompositableClient
 {
   friend class SimpleClientTiledThebesLayer;
 
 public:
   SimpleTiledContentClient(SimpleClientTiledThebesLayer* aThebesLayer,
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -542,23 +542,21 @@ TileClient::ValidateBackBufferFromFront(
         // Just redraw it all.
         return;
       }
 
       if (!mFrontBuffer->Lock(OpenMode::OPEN_READ)) {
         NS_WARNING("Failed to lock the tile's front buffer");
         return;
       }
-      TextureClientAutoUnlock autoFront(mFrontBuffer);
 
       if (!mBackBuffer->Lock(OpenMode::OPEN_WRITE)) {
         NS_WARNING("Failed to lock the tile's back buffer");
         return;
       }
-      TextureClientAutoUnlock autoBack(mBackBuffer);
 
       // Copy the bounding rect of regionToCopy. As tiles are quite small, it
       // is unlikely that we'd save much by copying each individual rect of the
       // region, but we can reevaluate this if it becomes an issue.
       const nsIntRect rectToCopy = regionToCopy.GetBounds();
       gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height);
       gfx::IntPoint gfxRectToCopyTopLeft = gfxRectToCopy.TopLeft();
       mFrontBuffer->CopyToTextureClient(mBackBuffer, &gfxRectToCopy, &gfxRectToCopyTopLeft);
@@ -584,16 +582,19 @@ TileClient::DiscardFrontBuffer()
       // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
       mManager->AsShadowForwarder()->RemoveTextureFromCompositableAsync(tracker,
                                                                         mCompositableClient,
                                                                         mFrontBuffer);
     }
 #endif
     mManager->GetTexturePool(mFrontBuffer->GetFormat())->ReturnTextureClientDeferred(mFrontBuffer);
     mFrontLock->ReadUnlock();
+    if (mFrontBuffer->IsLocked()) {
+      mFrontBuffer->Unlock();
+    }
     mFrontBuffer = nullptr;
     mFrontLock = nullptr;
   }
 }
 
 void
 TileClient::DiscardBackBuffer()
 {
@@ -621,16 +622,19 @@ TileClient::DiscardBackBuffer()
       // TextureClient can be reused after transaction complete,
       // when RemoveTextureFromCompositableTracker is used.
       mManager->GetTexturePool(mBackBuffer->GetFormat())->ReturnTextureClientDeferred(mBackBuffer);
 #else
       mManager->GetTexturePool(mBackBuffer->GetFormat())->ReturnTextureClient(mBackBuffer);
 #endif
     }
     mBackLock->ReadUnlock();
+    if (mBackBuffer->IsLocked()) {
+      mBackBuffer->Unlock();
+    }
     mBackBuffer.Set(this, nullptr);
     mBackLock = nullptr;
   }
 }
 
 TextureClient*
 TileClient::GetBackBuffer(const nsIntRegion& aDirtyRegion, TextureClientPool *aPool, bool *aCreatedTextureClient, bool aCanRerasterizeValidRegion)
 {
@@ -779,18 +783,17 @@ ClientTiledLayerBuffer::PaintThebes(cons
     // the case there's no point in using a single paint buffer.
     nsIntRect paintBounds = aPaintRegion.GetBounds();
     useSinglePaintBuffer = GetTileStart(paintBounds.x) !=
                            GetTileStart(paintBounds.XMost() - 1) ||
                            GetTileStart(paintBounds.y) !=
                            GetTileStart(paintBounds.YMost() - 1);
   }
   */
-
-  if (useSinglePaintBuffer) {
+  if (useSinglePaintBuffer && !gfxPrefs::TiledDrawTargetEnabled()) {
     nsRefPtr<gfxContext> ctxt;
 
     const nsIntRect bounds = aPaintRegion.GetBounds();
     {
       PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc",
         js::ProfileEntry::Category::GRAPHICS);
 
       gfxImageFormat format =
@@ -916,16 +919,45 @@ void PadDrawTargetOutFromRegion(RefPtr<D
 
   if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
     // we can only pad software targets so if we can't lock the bits don't pad
     region.VisitEdges(lb.visitor, &lb);
     drawTarget->ReleaseBits(lb.data);
   }
 }
 
+void
+ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion)
+{
+  if (gfxPrefs::TiledDrawTargetEnabled() && mMoz2DTiles.size() > 0) {
+    gfx::TileSet tileset;
+    tileset.mTiles = &mMoz2DTiles[0];
+    tileset.mTileCount = mMoz2DTiles.size();
+    RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+    drawTarget->SetTransform(Matrix());
+
+    RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
+
+    mCallback(mThebesLayer, ctx, aPaintRegion, DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+    mMoz2DTiles.clear();
+  }
+}
+
+void
+ClientTiledLayerBuffer::UnlockTile(TileClient aTile)
+{
+  // We locked the back buffer, and flipped so we now need to unlock the front
+  if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
+    aTile.mFrontBuffer->Unlock();
+  }
+  if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
+    aTile.mBackBuffer->Unlock();
+  }
+}
+
 TileClient
 ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
                                     const nsIntPoint& aTileOrigin,
                                     const nsIntRegion& aDirtyRegion)
 {
   PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile",
     js::ProfileEntry::Category::GRAPHICS);
 
@@ -953,20 +985,74 @@ ClientTiledLayerBuffer::ValidateTile(Til
   offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
 
   bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(offsetScaledDirtyRegion,
                         mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())),
                         &createdTextureClient, !usingSinglePaintBuffer);
 
-  if (!backBuffer || !backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
-    NS_WARNING("Failed to lock tile TextureClient for updating.");
-    aTile.DiscardFrontBuffer();
-    aTile.DiscardBackBuffer();
+  // the back buffer may have been already locked in ValidateBackBufferFromFront
+  if (!backBuffer->IsLocked()) {
+    if (!backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
+      NS_WARNING("Failed to lock tile TextureClient for updating.");
+      aTile.DiscardFrontBuffer();
+      return aTile;
+    }
+  }
+
+  if (gfxPrefs::TiledDrawTargetEnabled()) {
+    nsIntRegionRectIterator it(aDirtyRegion);
+    for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) {
+      gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x,
+                         dirtyRect->y - aTileOrigin.y,
+                         dirtyRect->width,
+                         dirtyRect->height);
+      drawRect.Scale(mResolution);
+
+      gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
+                            NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
+                            drawRect.width,
+                            drawRect.height);
+      gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
+      // Mark the newly updated area as invalid in the front buffer
+      aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
+    }
+
+    // The new buffer is now validated, remove the dirty region from it.
+    aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
+                           offsetScaledDirtyRegion);
+
+    aTile.Flip();
+
+    if (createdTextureClient) {
+      if (!mCompositableClient->AddTextureClient(backBuffer)) {
+        NS_WARNING("Failed to add tile TextureClient.");
+        aTile.DiscardFrontBuffer();
+        aTile.DiscardBackBuffer();
+        return aTile;
+      }
+    }
+
+    if (backBuffer->HasInternalBuffer()) {
+      // If our new buffer has an internal buffer, we don't want to keep another
+      // TextureClient around unnecessarily, so discard the back-buffer.
+      aTile.DiscardBackBuffer();
+    }
+
+    // prepare an array of Moz2D tiles that will be painted into in PostValidate
+    gfx::Tile moz2DTile;
+    moz2DTile.mDrawTarget = backBuffer->BorrowDrawTarget();
+    moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+    if (!moz2DTile.mDrawTarget) {
+      aTile.DiscardFrontBuffer();
+      return aTile;
+    }
+
+    mMoz2DTiles.push_back(moz2DTile);
     return aTile;
   }
 
   // We must not keep a reference to the DrawTarget after it has been unlocked,
   // make sure these are null'd before unlocking as destruction of the context
   // may cause the target to be flushed.
   RefPtr<DrawTarget> drawTarget = backBuffer->BorrowDrawTarget();
   drawTarget->SetTransform(Matrix());
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -419,16 +419,20 @@ public:
 
   SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
 
 protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
                           const nsIntRegion& dirtyRect);
 
+  void PostValidate(const nsIntRegion& aPaintRegion);
+
+  void UnlockTile(TileClient aTile);
+
   // If this returns true, we perform the paint operation into a single large
   // buffer and copy it out to the tiles instead of calling PaintThebes() on
   // each tile individually. Somewhat surprisingly, this turns out to be faster
   // on Android.
   bool UseSinglePaintBuffer() { return !gfxPrefs::PerTileDrawing(); }
 
   void ReleaseTile(TileClient aTile) { aTile.Release(); }
 
@@ -445,17 +449,18 @@ private:
   void* mCallbackData;
   CSSToParentLayerScale mFrameResolution;
   bool mLastPaintOpaque;
 
   // The DrawTarget we use when UseSinglePaintBuffer() above is true.
   RefPtr<gfx::DrawTarget>       mSinglePaintDrawTarget;
   nsIntPoint                    mSinglePaintBufferOffset;
   SharedFrameMetricsHelper*  mSharedFrameMetricsHelper;
-
+  // When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
+  std::vector<gfx::Tile> mMoz2DTiles;
   /**
    * Calculates the region to update in a single progressive update transaction.
    * This employs some heuristics to update the most 'sensible' region to
    * update at this point in time, and how large an update should be performed
    * at once to maintain visual coherency.
    *
    * aInvalidRegion is the current invalid region.
    * aOldValidRegion is the valid region of mTiledBuffer at the beginning of the
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -149,16 +149,18 @@ protected:
                         const nsIntPoint& aTileRect,
                         const nsIntRegion& dirtyRect);
 
   // do nothing, the desctructor in the texture host takes care of releasing resources
   void ReleaseTile(TileHost aTile) {}
 
   void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
 
+  void UnlockTile(TileHost aTile) {}
+  void PostValidate(const nsIntRegion& aPaintRegion) {}
 private:
   CSSToParentLayerScale mFrameResolution;
   bool mHasDoubleBufferedTiles;
   bool mUninitialized;
 };
 
 /**
  * ContentHost for tiled Thebes layers. Since tiled layers are special snow
--- a/gfx/tests/gtest/TestTiledLayerBuffer.cpp
+++ b/gfx/tests/gtest/TestTiledLayerBuffer.cpp
@@ -47,16 +47,19 @@ public:
     aTileA = aTileB;
     aTileB = oldTileA;
   }
 
   void TestUpdate(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion)
   {
     Update(aNewValidRegion, aPaintRegion);
   }
+
+  void UnlockTile(TestTiledLayerTile aTile) {}
+  void PostValidate(const nsIntRegion& aPaintRegion) {}
 };
 
 TEST(TiledLayerBuffer, TileConstructor) {
   TestTiledLayerBuffer buffer;
 }
 
 TEST(TiledLayerBuffer, TileStart) {
   TestTiledLayerBuffer buffer;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -220,16 +220,17 @@ private:
   DECL_GFX_PREF(Live, "layers.draw-borders",                   DrawLayerBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-tile-borders",              DrawTileBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.flash-borders",                  FlashLayerBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-layer-info",                DrawLayerInfo, bool, false);
   DECL_GFX_PREF(Live, "layers.dump",                           LayersDump, bool, false);
   DECL_GFX_PREF(Once, "layers.enable-tiles",                   LayersTilesEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.simple-tiles",                   LayersUseSimpleTiles, bool, false);
   DECL_GFX_PREF(Once, "layers.force-per-tile-drawing",         PerTileDrawing, bool, false);
+  DECL_GFX_PREF(Once, "layers.tiled-drawtarget.enabled",       TiledDrawTargetEnabled, bool, false);
   // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-max-pool-size",             LayersTileMaxPoolSize, uint32_t, (uint32_t)50);
   DECL_GFX_PREF(Once, "layers.tile-shrink-pool-timeout",       LayersTileShrinkPoolTimeout, uint32_t, (uint32_t)1000);
   DECL_GFX_PREF(Once, "layers.overzealous-gralloc-unlocking",  OverzealousGrallocUnlocking, bool, false);
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3727,16 +3727,17 @@ pref("layers.acceleration.force-enabled"
 pref("layers.acceleration.draw-fps", false);
 
 pref("layers.dump", false);
 pref("layers.draw-borders", false);
 pref("layers.draw-tile-borders", false);
 pref("layers.draw-bigimage-borders", false);
 pref("layers.frame-counter", false);
 pref("layers.enable-tiles", false);
+pref("layers.tiled-drawtarget.enabled", false);
 pref("layers.low-precision-buffer", false);
 pref("layers.tile-width", 256);
 pref("layers.tile-height", 256);
 // Max number of layers per container. See Overwrite in mobile prefs.
 pref("layers.max-active", -1);
 // When a layer is moving it will add a scroll graph to measure the smoothness
 // of the movement. NOTE: This pref triggers composites to refresh
 // the graph.