Bug 1422475: Create FilterNodes on the paint thread when using Direct2D. r=dvander draft
authorBas Schouten <bschouten@mozilla.com>
Wed, 06 Dec 2017 21:23:05 +0100
changeset 708372 80be94d61abdcbf207cfc0b086aa8e3100274b8b
parent 708017 286203c73311ec77e93adfaddc6e7e0cc770fbd4
child 743187 13a1880d3ad0529efcda9ef8081a2ca301cf2381
push id92379
push userbschouten@mozilla.com
push dateWed, 06 Dec 2017 20:23:42 +0000
reviewersdvander
bugs1422475
milestone59.0a1
Bug 1422475: Create FilterNodes on the paint thread when using Direct2D. r=dvander MozReview-Commit-ID: 9hlcf8SZNWZ
gfx/2d/DrawCommand.h
gfx/2d/DrawTargetCapture.cpp
gfx/2d/DrawTargetCapture.h
gfx/2d/FilterNodeCapture.cpp
gfx/2d/FilterNodeCapture.h
gfx/2d/Filters.h
gfx/2d/moz.build
layout/reftests/bugs/961887-2.html
--- 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 "CaptureCommandList.h"
+#include "FilterNodeCapture.h"
 
 namespace mozilla {
 namespace gfx {
 
 enum class CommandType : int8_t {
   DRAWSURFACE = 0,
   DRAWFILTER,
   DRAWSURFACEWITHSHADOW,
@@ -241,17 +242,21 @@ public:
   }
 
   void CloneInto(CaptureCommandList* aList) {
     CLONE_INTO(DrawFilterCommand)(mFilter, mSourceRect, mDestPoint, mOptions);
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
   {
-    aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions);
+    FilterNode* filter = mFilter.get();
+    if (mFilter->GetBackendType() == FilterBackend::FILTER_BACKEND_CAPTURE) {
+      filter = static_cast<FilterNodeCapture*>(filter)->Validate(aDT);
+    }
+    aDT->DrawFilter(filter, mSourceRect, mDestPoint, mOptions);
   }
 
   static const bool AffectsSnapshot = true;
 
 private:
   RefPtr<FilterNode> mFilter;
   Rect mSourceRect;
   Point mDestPoint;
--- a/gfx/2d/DrawTargetCapture.cpp
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -3,16 +3,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/. */
 
 #include "DrawTargetCapture.h"
 #include "DrawCommand.h"
 #include "gfxPlatform.h"
 #include "SourceSurfaceCapture.h"
+#include "FilterNodeCapture.h"
 
 namespace mozilla {
 namespace gfx {
 
 
 DrawTargetCaptureImpl::~DrawTargetCaptureImpl()
 {
   if (mSnapshot && !mSnapshot->hasOneRef()) {
@@ -413,10 +414,20 @@ DrawTargetCaptureImpl::CreateSimilarDraw
 
 RefPtr<DrawTarget>
 DrawTargetCaptureImpl::CreateSimilarRasterTarget(const IntSize& aSize, SurfaceFormat aFormat) const
 {
   MOZ_ASSERT(!mRefDT->IsCaptureDT());
   return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
 }
 
+already_AddRefed<FilterNode>
+DrawTargetCaptureImpl::CreateFilter(FilterType aType)
+{
+  if (mRefDT->GetBackendType() == BackendType::DIRECT2D1_1) {
+    return MakeRefPtr<FilterNodeCapture>(aType).forget();
+  } else {
+    return mRefDT->CreateFilter(aType);
+  }
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetCapture.h
+++ b/gfx/2d/DrawTargetCapture.h
@@ -138,20 +138,17 @@ public:
 
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = ExtendMode::CLAMP) const override
   {
     return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
   }
-  virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
-  {
-    return mRefDT->CreateFilter(aType);
-  }
+  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;
 
 protected:
   virtual ~DrawTargetCaptureImpl();
 
new file mode 100644
--- /dev/null
+++ b/gfx/2d/FilterNodeCapture.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FilterNodeCapture.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct Setter
+{
+  Setter(FilterNode* aNode, DrawTarget* aDT, bool aInputsChanged)
+    : mNode{aNode}, mIndex{0}, mDT{aDT}, mInputsChanged{aInputsChanged} {}
+  template<typename T>
+  void match(T& aValue) { mNode->SetAttribute(mIndex, aValue); }
+
+  FilterNode* mNode;
+  uint32_t mIndex;
+  DrawTarget* mDT;
+  bool mInputsChanged;
+};
+
+template<>
+void
+Setter::match<std::vector<Float>>(std::vector<Float>& aValue)
+{
+  mNode->SetAttribute(mIndex, aValue.data(), aValue.size());
+}
+
+template<>
+void
+Setter::match<RefPtr<SourceSurface>>(RefPtr<SourceSurface>& aSurface)
+{
+  if (!mInputsChanged) {
+    return;
+  }
+  mNode->SetInput(mIndex, aSurface);
+}
+
+template<>
+void
+Setter::match<RefPtr<FilterNode>>(RefPtr<FilterNode>& aNode)
+{
+  FilterNode* node = aNode;
+  if (node->GetBackendType() == FilterBackend::FILTER_BACKEND_CAPTURE) {
+    FilterNodeCapture* captureNode = static_cast<FilterNodeCapture*>(node);
+    node = captureNode->Validate(mDT);
+  }
+  if (!mInputsChanged) {
+    return;
+  }
+
+  mNode->SetInput(mIndex, node);
+}
+
+FilterNode*
+FilterNodeCapture::Validate(DrawTarget* aDT)
+{
+  if (!mFilterNodeInternal) {
+    mFilterNodeInternal = aDT->CreateFilter(mType);
+  }
+
+  if (!mFilterNodeInternal) {
+    return nullptr;
+  }
+
+  Setter setter(mFilterNodeInternal, aDT, mInputsChanged);
+
+  for (auto attribute : mAttributes)
+  {
+    setter.mIndex = attribute.first;
+    // Variant's matching doesn't seem to compile to terribly efficient code,
+    // this is probably fine since this happens on the paint thread, if ever
+    // needed it would be fairly simple to write something more optimized.
+    attribute.second.match(setter);
+  }
+  mAttributes.clear();
+
+  for (auto input : mInputs) {
+    setter.mIndex = input.first;
+    input.second.match(setter);
+  }
+  mInputsChanged = false;
+
+  return mFilterNodeInternal.get();
+}
+
+void
+FilterNodeCapture::SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize)
+{
+  std::vector<Float> vec(aSize);
+  memcpy(vec.data(), aValues, sizeof(Float) * aSize);
+  AttributeValue att(std::move(vec));
+  auto result = mAttributes.insert({ aIndex, att });
+  if (!result.second) {
+    result.first->second = att;
+  }
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/2d/FilterNodeCapture.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_FILTERNODECAPTURE_H_
+#define MOZILLA_GFX_FILTERNODECAPTURE_H_
+
+#include "2D.h"
+#include "Filters.h"
+#include <unordered_map>
+#include <vector>
+#include "mozilla/Variant.h"
+
+namespace mozilla {
+namespace gfx {
+
+class FilterNodeCapture final : public FilterNode
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCapture)
+
+  explicit FilterNodeCapture(FilterType aType)
+    : mType{aType}
+    , mInputsChanged{true}
+  {
+  }
+
+  virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_CAPTURE; }
+
+  FilterNode* Validate(DrawTarget* aDT);
+
+  template<typename T, typename C>
+  void Replace(uint32_t aIndex, T& aValue, C& aContainer)
+  {
+    // This replace function avoids generating the hash twice.
+    auto result = aContainer.insert({ aIndex, C::value_type::second_type{aValue} });
+    if (!result.second) {
+      result.first->second = C::value_type::second_type{aValue};
+    }
+  }
+
+  virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override
+  {
+    mInputsChanged = true;
+    Replace(aIndex, RefPtr<SourceSurface>{aSurface}, mInputs);
+  }
+  virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override
+  {
+    mInputsChanged = true;
+    Replace(aIndex, RefPtr<FilterNode>{aFilter}, mInputs);
+  }
+
+  using AttributeValue = Variant<
+    uint32_t,
+    Float,
+    Point,
+    Matrix5x4,
+    Point3D,
+    Size,
+    IntSize,
+    Color,
+    Rect,
+    IntRect,
+    bool,
+    std::vector<Float>,
+    IntPoint,
+    Matrix
+  >;
+
+  virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, Float aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Point &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Point3D &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Size &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Color &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Rect &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, bool aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize) override;
+  virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue) override { Replace(aIndex, aValue, mAttributes); }
+  virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue) override { Replace(aIndex, aValue, mAttributes); }
+
+private:
+  FilterType mType;
+  RefPtr<FilterNode> mFilterNodeInternal;
+
+  // This only contains attributes that were set since the last validation.
+  std::unordered_map<uint32_t, AttributeValue> mAttributes;
+
+  // This always contains all inputs, so that Validate may be called on input
+  // filter nodes.
+  std::unordered_map<uint32_t, Variant<RefPtr<SourceSurface>, RefPtr<FilterNode>>> mInputs;
+  
+  // This is true if SetInput was called since the last validation.
+  bool mInputsChanged;
+};
+
+}
+}
+
+#endif
--- a/gfx/2d/Filters.h
+++ b/gfx/2d/Filters.h
@@ -17,17 +17,18 @@
 namespace mozilla {
 namespace gfx {
 
 class SourceSurface;
 
 enum FilterBackend {
   FILTER_BACKEND_SOFTWARE = 0,
   FILTER_BACKEND_DIRECT2D1_1,
-  FILTER_BACKEND_RECORDING
+  FILTER_BACKEND_RECORDING,
+  FILTER_BACKEND_CAPTURE
 };
 
 enum TransformFilterAtts
 {
   ATT_TRANSFORM_MATRIX = 0,                 // Matrix
   ATT_TRANSFORM_FILTER                      // Filter
 };
 
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -162,16 +162,17 @@ UNIFIED_SOURCES += [
     'DrawingJob.cpp',
     'DrawTarget.cpp',
     'DrawTargetCairo.cpp',
     'DrawTargetCapture.cpp',
     'DrawTargetDual.cpp',
     'DrawTargetRecording.cpp',
     'DrawTargetTiled.cpp',
     'DrawTargetWrapAndRecord.cpp',
+    'FilterNodeCapture.cpp',
     'FilterNodeSoftware.cpp',
     'FilterProcessing.cpp',
     'FilterProcessingScalar.cpp',
     'ImageScaling.cpp',
     'JobScheduler.cpp',
     'Matrix.cpp',
     'Path.cpp',
     'PathCairo.cpp',
--- a/layout/reftests/bugs/961887-2.html
+++ b/layout/reftests/bugs/961887-2.html
@@ -1,9 +1,9 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <html lang="en">
 <meta charset="utf-8">
 <title>Make sure that we don't pull background colors into container layers that have effects applied on them</title>
 
 <style>
 
 #outer {
   background-color: white;