Add basic DrawTargetCapture logging ability (bug 1435938, r=bas) draft
authorRyan Hunt <rhunt@eqrion.net>
Mon, 05 Feb 2018 22:00:45 -0600
changeset 751380 0aaddea1d6675ce027a4068b3f8d4d4db0a04a97
parent 751379 f72d9fdf6e97fd7bde0098f9e4ca6c5ca952ef79
child 751381 f827c36f3bf5a55b20a42f21dcab8d1935e956e5
push id97954
push userbmo:rhunt@eqrion.net
push dateTue, 06 Feb 2018 04:37:30 +0000
reviewersbas
bugs1435938
milestone60.0a1
Add basic DrawTargetCapture logging ability (bug 1435938, r=bas) MozReview-Commit-ID: LhbC4mWub6k
gfx/2d/2D.h
gfx/2d/CaptureCommandList.h
gfx/2d/DrawCommand.h
gfx/2d/DrawCommands.h
gfx/2d/DrawTargetCapture.cpp
gfx/2d/DrawTargetCapture.h
gfx/2d/Logging.h
gfx/2d/moz.build
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1442,16 +1442,18 @@ protected:
   SurfaceFormat mFormat;
 };
 
 class DrawTargetCapture : public DrawTarget
 {
 public:
   virtual bool IsCaptureDT() const override { return true; }
 
+  virtual void Dump() = 0;
+
   /**
    * Returns true if the recording only contains FillGlyph calls with
    * a single font and color. Returns the list of Glyphs along with
    * the font and color as outparams if so.
    */
   virtual bool ContainsOnlyColoredGlyphs(RefPtr<ScaledFont>& aScaledFont,
                                          Color& aColor,
                                          std::vector<Glyph>& aGlyphs) = 0;
--- a/gfx/2d/CaptureCommandList.h
+++ b/gfx/2d/CaptureCommandList.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_gfx_2d_CaptureCommandList_h
 #define mozilla_gfx_2d_CaptureCommandList_h
 
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include <vector>
 
 #include "DrawCommand.h"
+#include "Logging.h"
 
 namespace mozilla {
 namespace gfx {
 
 class CaptureCommandList
 {
 public:
   CaptureCommandList()
@@ -81,16 +82,25 @@ public:
     }
 
   private:
     CaptureCommandList& mParent;
     uint8_t* mCurrent;
     uint8_t* mEnd;
   };
 
+  void Log(TreeLog& aStream)
+  {
+    for (iterator iter(*this); !iter.Done(); iter.Next()) {
+      DrawingCommand* cmd = iter.Get();
+      cmd->Log(aStream);
+      aStream << "\n";
+    }
+  }
+
 private:
   CaptureCommandList(const CaptureCommandList& aOther) = delete;
   void operator =(const CaptureCommandList& aOther) = delete;
 
 private:
   std::vector<uint8_t> mStorage;
   DrawingCommand* mLastCommand;
 };
--- a/gfx/2d/DrawCommand.h
+++ b/gfx/2d/DrawCommand.h
@@ -9,16 +9,17 @@
 
 #include <math.h>
 
 #include "2D.h"
 #include "Blur.h"
 #include "Filters.h"
 #include <vector>
 #include "FilterNodeCapture.h"
+#include "Logging.h"
 
 namespace mozilla {
 namespace gfx {
 
 class CaptureCommandList;
 
 enum class CommandType : int8_t {
   DRAWSURFACE = 0,
@@ -50,16 +51,17 @@ enum class CommandType : int8_t {
 class DrawingCommand
 {
 public:
   virtual ~DrawingCommand() {}
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform = nullptr) const = 0;
   virtual bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const { return false; }
   virtual void CloneInto(CaptureCommandList* aList) = 0;
+  virtual void Log(TreeLog& aLog) const = 0;
 
   CommandType GetType() { return mType; }
 
 protected:
   explicit DrawingCommand(CommandType aType)
     : mType(aType)
   {
   }
--- a/gfx/2d/DrawCommands.h
+++ b/gfx/2d/DrawCommands.h
@@ -11,16 +11,17 @@
 
 #include "2D.h"
 #include "Blur.h"
 #include "Filters.h"
 #include <vector>
 #include "CaptureCommandList.h"
 #include "DrawCommand.h"
 #include "FilterNodeCapture.h"
+#include "Logging.h"
 
 namespace mozilla {
 namespace gfx {
 
 #define CLONE_INTO(Type) new (aList->Append<Type>()) Type
 
 class StrokeOptionsCommand : public DrawingCommand
 {
@@ -76,16 +77,26 @@ public:
     }
   }
 
   ~StoredPattern()
   {
     reinterpret_cast<Pattern*>(mPattern)->~Pattern();
   }
 
+  Pattern* Get()
+  {
+    return reinterpret_cast<Pattern*>(mPattern);
+  }
+
+  const Pattern* Get() const
+  {
+    return reinterpret_cast<const Pattern*>(mPattern);
+  }
+
   operator Pattern&()
   {
     return *reinterpret_cast<Pattern*>(mPattern);
   }
 
   operator const Pattern&() const
   {
     return *reinterpret_cast<const Pattern*>(mPattern);
@@ -130,16 +141,26 @@ public:
     CLONE_INTO(DrawSurfaceCommand)(mSurface, mDest, mSource, mSurfOptions, mOptions);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->DrawSurface(mSurface, mDest, mSource, mSurfOptions, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[DrawSurface surf=" << mSurface;
+    aStream << " dest=" << mDest;
+    aStream << " src=" << mSource;
+    aStream << " surfOpt=" << mSurfOptions;
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::DRAWSURFACE;
 
 private:
   RefPtr<SourceSurface> mSurface;
   Rect mDest;
   Rect mSource;
   DrawSurfaceOptions mSurfOptions;
@@ -170,16 +191,27 @@ public:
     CLONE_INTO(DrawSurfaceWithShadowCommand)(mSurface, mDest, mColor, mOffset, mSigma, mOperator);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->DrawSurfaceWithShadow(mSurface, mDest, mColor, mOffset, mSigma, mOperator);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[DrawSurfaceWithShadow surf=" << mSurface;
+    aStream << " dest=" << mDest;
+    aStream << " color=" << mColor;
+    aStream << " offset=" << mOffset;
+    aStream << " sigma=" << mSigma;
+    aStream << " op=" << mOperator;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::DRAWSURFACEWITHSHADOW;
 
 private:
   RefPtr<SourceSurface> mSurface;
   Point mDest;
   Color mColor;
   Point mOffset;
@@ -207,16 +239,25 @@ public:
   {
     RefPtr<FilterNode> filter = mFilter;
     if (mFilter->GetBackendType() == FilterBackend::FILTER_BACKEND_CAPTURE) {
       filter = static_cast<FilterNodeCapture*>(filter.get())->Validate(aDT);
     }
     aDT->DrawFilter(filter, mSourceRect, mDestPoint, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[DrawFilter surf=" << mFilter;
+    aStream << " src=" << mSourceRect;
+    aStream << " dest=" << mDestPoint;
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::DRAWFILTER;
 
 private:
   RefPtr<FilterNode> mFilter;
   Rect mSourceRect;
   Point mDestPoint;
   DrawOptions mOptions;
@@ -236,16 +277,21 @@ public:
     CLONE_INTO(ClearRectCommand)(mRect);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->ClearRect(mRect);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[ClearRect rect=" << mRect << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::CLEARRECT;
 
 private:
   Rect mRect;
 };
 
 class CopySurfaceCommand : public DrawingCommand
@@ -271,16 +317,24 @@ public:
     MOZ_ASSERT(!aTransform || !aTransform->HasNonIntegerTranslation());
     Point dest(Float(mDestination.x), Float(mDestination.y));
     if (aTransform) {
       dest = aTransform->TransformPoint(dest);
     }
     aDT->CopySurface(mSurface, mSourceRect, IntPoint(uint32_t(dest.x), uint32_t(dest.y)));
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[CopySurface surf=" << mSurface;
+    aStream << " src=" << mSourceRect;
+    aStream << " dest=" << mDestination;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::COPYSURFACE;
 
 private:
   RefPtr<SourceSurface> mSurface;
   IntRect mSourceRect;
   IntPoint mDestination;
 };
@@ -309,16 +363,24 @@ public:
   }
 
   bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const override
   {
     aDeviceRect = aTransform.TransformBounds(mRect);
     return true;
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[FillRect rect=" << mRect;
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::FILLRECT;
 
 private:
   Rect mRect;
   StoredPattern mPattern;
   DrawOptions mOptions;
 };
@@ -342,16 +404,24 @@ public:
     CLONE_INTO(StrokeRectCommand)(mRect, mPattern, mStrokeOptions, mOptions);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[StrokeRect rect=" << mRect;
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::STROKERECT;
 
 private:
   Rect mRect;
   StoredPattern mPattern;
   DrawOptions mOptions;
 };
@@ -377,16 +447,25 @@ public:
     CLONE_INTO(StrokeLineCommand)(mStart, mEnd, mPattern, mStrokeOptions, mOptions);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->StrokeLine(mStart, mEnd, mPattern, mStrokeOptions, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[StrokeLine start=" << mStart;
+    aStream << " end=" << mEnd;
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::STROKELINE;
 
 private:
   Point mStart;
   Point mEnd;
   StoredPattern mPattern;
   DrawOptions mOptions;
@@ -416,16 +495,24 @@ public:
   }
 
   bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const override
   {
     aDeviceRect = mPath->GetBounds(aTransform);
     return true;
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[FillCommand path=" << mPath;
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::FILL;
 
 private:
   RefPtr<Path> mPath;
   StoredPattern mPattern;
   DrawOptions mOptions;
 };
@@ -496,16 +583,24 @@ public:
   }
 
   bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const override
   {
     aDeviceRect = PathExtentsToMaxStrokeExtents(mStrokeOptions, mPath->GetBounds(aTransform), aTransform);
     return true;
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[Stroke path=" << mPath;
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::STROKE;
 
 private:
   RefPtr<Path> mPath;
   StoredPattern mPattern;
   DrawOptions mOptions;
 };
@@ -539,16 +634,25 @@ public:
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     GlyphBuffer buf;
     buf.mNumGlyphs = mGlyphs.size();
     buf.mGlyphs = &mGlyphs.front();
     aDT->FillGlyphs(mFont, buf, mPattern, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[FillGlyphs font=" << mFont;
+    aStream << " glyphCount=" << mGlyphs.size();
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::FILLGLYPHS;
 
 private:
   RefPtr<ScaledFont> mFont;
   std::vector<Glyph> mGlyphs;
   StoredPattern mPattern;
   DrawOptions mOptions;
@@ -584,16 +688,25 @@ public:
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     GlyphBuffer buf;
     buf.mNumGlyphs = mGlyphs.size();
     buf.mGlyphs = &mGlyphs.front();
     aDT->StrokeGlyphs(mFont, buf, mPattern, mStrokeOptions, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[StrokeGlyphs font=" << mFont;
+    aStream << " glyphCount=" << mGlyphs.size();
+    aStream << " pattern=" << mPattern.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::STROKEGLYPHS;
 
 private:
   RefPtr<ScaledFont> mFont;
   std::vector<Glyph> mGlyphs;
   StoredPattern mPattern;
   DrawOptions mOptions;
@@ -617,16 +730,24 @@ public:
     CLONE_INTO(MaskCommand)(mSource, mMask, mOptions);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->Mask(mSource, mMask, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[Mask source=" << mSource.Get();
+    aStream << " mask=" << mMask.Get();
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::MASK;
 
 private:
   StoredPattern mSource;
   StoredPattern mMask;
   DrawOptions mOptions;
 };
@@ -651,16 +772,25 @@ public:
     CLONE_INTO(MaskSurfaceCommand)(mSource, mMask, mOffset, mOptions);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->MaskSurface(mSource, mMask, mOffset, mOptions);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[Mask source=" << mSource.Get();
+    aStream << " mask=" << mMask;
+    aStream << " offset=" << &mOffset;
+    aStream << " opt=" << mOptions;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::MASKSURFACE;
 
 private:
   StoredPattern mSource;
   RefPtr<SourceSurface> mMask;
   Point mOffset;
   DrawOptions mOptions;
@@ -680,16 +810,21 @@ public:
     CLONE_INTO(PushClipCommand)(mPath);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->PushClip(mPath);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[PushClip path=" << mPath << "]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::PUSHCLIP;
 
 private:
   RefPtr<Path> mPath;
 };
 
 class PushClipRectCommand : public DrawingCommand
@@ -706,16 +841,21 @@ public:
     CLONE_INTO(PushClipRectCommand)(mRect);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->PushClipRect(mRect);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[PushClipRect rect=" << mRect << "]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::PUSHCLIPRECT;
 
 private:
   Rect mRect;
 };
 
 class PushLayerCommand : public DrawingCommand
@@ -743,16 +883,27 @@ public:
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->PushLayer(mOpaque, mOpacity, mMask,
                    mMaskTransform, mBounds, mCopyBackground);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[PushLayer opaque=" << mOpaque;
+    aStream << " opacity=" << mOpacity;
+    aStream << " mask=" << mMask;
+    aStream << " maskTransform=" << mMaskTransform;
+    aStream << " bounds=" << mBounds;
+    aStream << " copyBackground=" << mCopyBackground;
+    aStream << "]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::PUSHLAYER;
 
 private:
   bool mOpaque;
   float mOpacity;
   RefPtr<SourceSurface> mMask;
   Matrix mMaskTransform;
@@ -773,16 +924,21 @@ public:
     CLONE_INTO(PopClipCommand)();
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->PopClip();
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[PopClip]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::POPCLIP;
 };
 
 class PopLayerCommand : public DrawingCommand
 {
 public:
   PopLayerCommand()
@@ -795,16 +951,21 @@ public:
     CLONE_INTO(PopLayerCommand)();
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->PopLayer();
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[PopLayer]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::POPLAYER;
 };
 
 class SetTransformCommand : public DrawingCommand
 {
   friend class DrawTargetCaptureImpl;
 public:
@@ -823,16 +984,21 @@ public:
   {
     if (aMatrix) {
       aDT->SetTransform(mTransform * (*aMatrix));
     } else {
       aDT->SetTransform(mTransform);
     }
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[SetTransform transform=" << mTransform << "]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::SETTRANSFORM;
 
 private:
   Matrix mTransform;
 };
 
 class SetPermitSubpixelAACommand : public DrawingCommand
@@ -850,16 +1016,21 @@ public:
     CLONE_INTO(SetPermitSubpixelAACommand)(mPermitSubpixelAA);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aMatrix) const override
   {
     aDT->SetPermitSubpixelAA(mPermitSubpixelAA);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[SetPermitSubpixelAA permitSubpixelAA=" << mPermitSubpixelAA << "]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::SETPERMITSUBPIXELAA;
 
 private:
   bool mPermitSubpixelAA;
 };
 
 class FlushCommand : public DrawingCommand
@@ -875,16 +1046,21 @@ public:
     CLONE_INTO(FlushCommand)();
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->Flush();
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[Flush]";
+  }
+
   static const bool AffectsSnapshot = false;
   static const CommandType Type = CommandType::FLUSH;
 };
 
 class BlurCommand : public DrawingCommand
 {
 public:
   explicit BlurCommand(const AlphaBoxBlur& aBlur)
@@ -897,16 +1073,21 @@ public:
     CLONE_INTO(BlurCommand)(mBlur);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override
   {
     aDT->Blur(mBlur);
   }
 
+  void Log(TreeLog& aStream) const override
+  {
+    aStream << "[Blur]";
+  }
+
   static const bool AffectsSnapshot = true;
   static const CommandType Type = CommandType::BLUR;
 
 private:
   AlphaBoxBlur mBlur;
 };
 
 #undef CLONE_INTO
--- a/gfx/2d/DrawTargetCapture.cpp
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -438,10 +438,20 @@ DrawTargetCaptureImpl::CreateFilter(Filt
 {
   if (mRefDT->GetBackendType() == BackendType::DIRECT2D1_1) {
     return MakeRefPtr<FilterNodeCapture>(aType).forget();
   } else {
     return mRefDT->CreateFilter(aType);
   }
 }
 
+void
+DrawTargetCaptureImpl::Dump()
+{
+  TreeLog output;
+  output << "DrawTargetCapture(" << (void*)(this) << ")\n";
+  TreeAutoIndent indent(output);
+  mCommands.Log(output);
+  output << "\n";
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetCapture.h
+++ b/gfx/2d/DrawTargetCapture.h
@@ -144,16 +144,18 @@ public:
     return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
   }
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
 
   void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform);
 
   bool ContainsOnlyColoredGlyphs(RefPtr<ScaledFont>& aScaledFont, Color& aColor, std::vector<Glyph>& aGlyphs) override;
 
+  void Dump() override;
+
 protected:
   virtual ~DrawTargetCaptureImpl();
 
   void MarkChanged();
 
 private:
   // This storage system was used to minimize the amount of heap allocations
   // that are required while recording. It should be noted there's no
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -15,16 +15,17 @@
 #ifdef MOZ_LOGGING
 #include "mozilla/Logging.h"
 #endif
 #include "mozilla/Tuple.h"
 
 #if defined(MOZ_WIDGET_ANDROID)
 #include "nsDebug.h"
 #endif
+#include "2D.h"
 #include "Point.h"
 #include "BaseRect.h"
 #include "Matrix.h"
 #include "LoggingConstants.h"
 
 #if defined(MOZ_LOGGING)
 extern GFX2D_API mozilla::LogModule* GetGFX2DLog();
 #endif
@@ -352,16 +353,22 @@ public:
     return *this;
   }
   Log &operator <<(double aDouble) {
     if (MOZ_UNLIKELY(LogIt())) {
       mMessage << aDouble;
     }
     return *this;
   }
+  Log &operator <<(const Color& aColor) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Color(" << aColor.r << ", " << aColor.g << ", " << aColor.b << ", " << aColor.a << ")";
+    }
+    return *this;
+  }
   template <typename T, typename Sub, typename Coord>
   Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint) {
     if (MOZ_UNLIKELY(LogIt())) {
       mMessage << "Point" << aPoint;
     }
     return *this;
   }
   template <typename T, typename Sub>
@@ -389,16 +396,215 @@ public:
     if (MOZ_UNLIKELY(LogIt())) {
       mMessage << std::showbase << std::hex
                << aHex.mVal
                << std::noshowbase << std::dec;
     }
     return *this;
   }
 
+  Log &operator<<(const SourceSurface* aSurface) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "SourceSurface(" << (void*)(aSurface) << ")";
+    }
+    return *this;
+  }
+  Log &operator<<(const Path* aPath) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Path(" << (void*)(aPath) << ")";
+    }
+    return *this;
+  }
+  Log &operator<<(const Pattern* aPattern) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Pattern(" << (void*)(aPattern) << ")";
+    }
+    return *this;
+  }
+  Log &operator<<(const ScaledFont* aFont) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "ScaledFont(" << (void*)(aFont) << ")";
+    }
+    return *this;
+  }
+  Log &operator<<(const FilterNode* aFilter) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "FilterNode(" << (void*)(aFilter) << ")";
+    }
+    return *this;
+  }
+  Log &operator<<(const DrawOptions& aOptions) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "DrawOptions(" << aOptions.mAlpha << ", ";
+      (*this) << aOptions.mCompositionOp;
+      mMessage << ", ";
+      (*this) << aOptions.mAntialiasMode;
+      mMessage << ")";
+    }
+    return *this;
+  }
+  Log &operator<<(const DrawSurfaceOptions& aOptions) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "DrawSurfaceOptions(";
+      (*this) << aOptions.mSamplingFilter;
+      mMessage << ", ";
+      (*this) << aOptions.mSamplingBounds;
+      mMessage << ")";
+    }
+    return *this;
+  }
+
+  Log& operator<<(SamplingBounds aBounds) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      switch(aBounds) {
+        case SamplingBounds::UNBOUNDED:
+          mMessage << "SamplingBounds::UNBOUNDED";
+          break;
+        case SamplingBounds::BOUNDED:
+          mMessage << "SamplingBounds::BOUNDED";
+          break;
+        default:
+          mMessage << "Invalid SamplingBounds (" << (int)aBounds << ")";
+          break;
+      }
+    }
+    return *this;
+  }
+  Log& operator<<(SamplingFilter aFilter) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      switch(aFilter) {
+        case SamplingFilter::GOOD:
+          mMessage << "SamplingFilter::GOOD";
+          break;
+        case SamplingFilter::LINEAR:
+          mMessage << "SamplingFilter::LINEAR";
+          break;
+        case SamplingFilter::POINT:
+          mMessage << "SamplingFilter::POINT";
+          break;
+        default:
+          mMessage << "Invalid SamplingFilter (" << (int)aFilter << ")";
+          break;
+      }
+    }
+    return *this;
+  }
+  Log& operator<<(AntialiasMode aMode) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      switch(aMode) {
+        case AntialiasMode::NONE:
+          mMessage << "AntialiasMode::NONE";
+          break;
+        case AntialiasMode::GRAY:
+          mMessage << "AntialiasMode::GRAY";
+          break;
+        case AntialiasMode::SUBPIXEL:
+          mMessage << "AntialiasMode::SUBPIXEL";
+          break;
+        case AntialiasMode::DEFAULT:
+          mMessage << "AntialiasMode::DEFAULT";
+          break;
+        default:
+          mMessage << "Invalid AntialiasMode (" << (int)aMode << ")";
+          break;
+      }
+    }
+    return *this;
+  }
+  Log& operator<<(CompositionOp aOp) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      switch(aOp) {
+        case CompositionOp::OP_OVER:
+          mMessage << "CompositionOp::OP_OVER";
+          break;
+        case CompositionOp::OP_ADD:
+          mMessage << "CompositionOp::OP_ADD";
+          break;
+        case CompositionOp::OP_ATOP:
+          mMessage << "CompositionOp::OP_ATOP";
+          break;
+        case CompositionOp::OP_OUT:
+          mMessage << "CompositionOp::OP_OUT";
+          break;
+        case CompositionOp::OP_IN:
+          mMessage << "CompositionOp::OP_IN";
+          break;
+        case CompositionOp::OP_SOURCE:
+          mMessage << "CompositionOp::OP_SOURCE";
+          break;
+        case CompositionOp::OP_DEST_IN:
+          mMessage << "CompositionOp::OP_DEST_IN";
+          break;
+        case CompositionOp::OP_DEST_OUT:
+          mMessage << "CompositionOp::OP_DEST_OUT";
+          break;
+        case CompositionOp::OP_DEST_OVER:
+          mMessage << "CompositionOp::OP_DEST_OVER";
+          break;
+        case CompositionOp::OP_DEST_ATOP:
+          mMessage << "CompositionOp::OP_DEST_ATOP";
+          break;
+        case CompositionOp::OP_XOR:
+          mMessage << "CompositionOp::OP_XOR";
+          break;
+        case CompositionOp::OP_MULTIPLY:
+          mMessage << "CompositionOp::OP_MULTIPLY";
+          break;
+        case CompositionOp::OP_SCREEN:
+          mMessage << "CompositionOp::OP_SCREEN";
+          break;
+        case CompositionOp::OP_OVERLAY:
+          mMessage << "CompositionOp::OP_OVERLAY";
+          break;
+        case CompositionOp::OP_DARKEN:
+          mMessage << "CompositionOp::OP_DARKEN";
+          break;
+        case CompositionOp::OP_LIGHTEN:
+          mMessage << "CompositionOp::OP_LIGHTEN";
+          break;
+        case CompositionOp::OP_COLOR_DODGE:
+          mMessage << "CompositionOp::OP_COLOR_DODGE";
+          break;
+        case CompositionOp::OP_COLOR_BURN:
+          mMessage << "CompositionOp::OP_COLOR_BURN";
+          break;
+        case CompositionOp::OP_HARD_LIGHT:
+          mMessage << "CompositionOp::OP_HARD_LIGHT";
+          break;
+        case CompositionOp::OP_SOFT_LIGHT:
+          mMessage << "CompositionOp::OP_SOFT_LIGHT";
+          break;
+        case CompositionOp::OP_DIFFERENCE:
+          mMessage << "CompositionOp::OP_DIFFERENCE";
+          break;
+        case CompositionOp::OP_EXCLUSION:
+          mMessage << "CompositionOp::OP_EXCLUSION";
+          break;
+        case CompositionOp::OP_HUE:
+          mMessage << "CompositionOp::OP_HUE";
+          break;
+        case CompositionOp::OP_SATURATION:
+          mMessage << "CompositionOp::OP_SATURATION";
+          break;
+        case CompositionOp::OP_COLOR:
+          mMessage << "CompositionOp::OP_COLOR";
+          break;
+        case CompositionOp::OP_LUMINOSITY:
+          mMessage << "CompositionOp::OP_LUMINOSITY";
+          break;
+        case CompositionOp::OP_COUNT:
+          mMessage << "CompositionOp::OP_COUNT";
+          break;
+        default:
+          mMessage << "Invalid CompositionOp (" << (int)aOp << ")";
+          break;
+      }
+    }
+    return *this;
+  }
   Log& operator<<(SurfaceFormat aFormat) {
     if (MOZ_UNLIKELY(LogIt())) {
       switch(aFormat) {
         case SurfaceFormat::B8G8R8A8:
           mMessage << "SurfaceFormat::B8G8R8A8";
           break;
         case SurfaceFormat::B8G8R8X8:
           mMessage << "SurfaceFormat::B8G8R8X8";
@@ -632,17 +838,20 @@ public:
           mPrefFunction(nullptr) {}
 
   template <typename T>
   TreeLog& operator<<(const T& aObject) {
     if (mConditionedOnPref && !mPrefFunction()) {
       return *this;
     }
     if (mStartOfLine) {
-      mLog << '[' << mPrefix << "] " << std::string(mDepth * INDENT_PER_LEVEL, ' ');
+      if (!mPrefix.empty()) {
+        mLog << '[' << mPrefix << "] ";
+      }
+      mLog << std::string(mDepth * INDENT_PER_LEVEL, ' ');
       mStartOfLine = false;
     }
     mLog << aObject;
     if (EndsInNewline(aObject)) {
       // Don't indent right here as the user may change the indent
       // between now and the first output to the next line.
       mLog.Flush();
       mStartOfLine = true;
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -153,16 +153,17 @@ elif CONFIG['CPU_ARCH'].startswith('mips
     ]
 
 UNIFIED_SOURCES += [
     'BezierUtils.cpp',
     'Blur.cpp',
     'CaptureCommandList.cpp',
     'DataSourceSurface.cpp',
     'DataSurfaceHelpers.cpp',
+    'DrawCommands.cpp',
     'DrawEventRecorder.cpp',
     'DrawingJob.cpp',
     'DrawTarget.cpp',
     'DrawTargetCairo.cpp',
     'DrawTargetCapture.cpp',
     'DrawTargetDual.cpp',
     'DrawTargetRecording.cpp',
     'DrawTargetTiled.cpp',