Bug 1363677 - Skip Flush() and EndDraw() if the ID2D1DeviceContext is stale. r=dvander, a=gchang
authorKevin Chen <kechen@mozilla.com>
Wed, 10 May 2017 16:22:08 +0800
changeset 396320 252e9fecd13598633ee11e26408534f378c05d14
parent 396319 518c6f7cde3efeb4a1ebb12d1f17b04402f7cace
child 396321 66efefd445ba5a4d9bb084ac6dd97375ec459043
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander, gchang
bugs1363677
milestone54.0
Bug 1363677 - Skip Flush() and EndDraw() if the ID2D1DeviceContext is stale. r=dvander, a=gchang MozReview-Commit-ID: 6KnryMZE8Qj
gfx/2d/2D.h
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
gfx/2d/Factory.cpp
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1573,16 +1573,17 @@ public:
   /*
    * Attempts to create and install a D2D1 device from the supplied Direct3D11 device.
    * Returns true on success, or false on failure and leaves the D2D1/Direct3D11 devices unset.
    */
   static bool SetDirect3D11Device(ID3D11Device *aDevice);
   static bool SetDWriteFactory(IDWriteFactory *aFactory);
   static ID3D11Device *GetDirect3D11Device();
   static ID2D1Device *GetD2D1Device();
+  static uint32_t GetD2D1DeviceSeq();
   static IDWriteFactory *GetDWriteFactory();
   static bool SupportsD2D1();
 
   static already_AddRefed<GlyphRenderingOptions>
     CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
 
   static uint64_t GetD2DVRAMUsageDrawTarget();
   static uint64_t GetD2DVRAMUsageSourceSurface();
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -37,16 +37,17 @@ ID2D1Factory1 *D2DFactory1()
 {
   return DrawTargetD2D1::factory();
 }
 
 DrawTargetD2D1::DrawTargetD2D1()
   : mPushedLayers(1)
   , mUsedCommandListsSincePurge(0)
   , mDidComplexBlendWithListInList(false)
+  , mDeviceSeq(0)
 {
 }
 
 DrawTargetD2D1::~DrawTargetD2D1()
 {
   PopAllClips();
 
   if (mSnapshot) {
@@ -55,17 +56,17 @@ DrawTargetD2D1::~DrawTargetD2D1()
     // MarkIndependent is running.
     RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
     // mSnapshot can be treated as independent of this DrawTarget since we know
     // this DrawTarget won't change again.
     deathGrip->MarkIndependent();
     // mSnapshot will be cleared now.
   }
 
-  if (mDC) {
+  if (mDC && IsDeviceContextValid()) {
     // The only way mDC can be null is if Init failed, but it can happen and the
     // destructor is the only place where we need to check for it since the
     // DrawTarget will destroyed right after Init fails.
     mDC->EndDraw();
   }
 
   // Targets depending on us can break that dependency, since we're obviously not going to
   // be modified in the future.
@@ -101,28 +102,30 @@ DrawTargetD2D1::Snapshot()
 // this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
 // are expensive though, especially relatively when little work is done, so
 // we try to reduce the amount of times we execute these purges.
 static const uint32_t kPushedLayersBeforePurge = 25;
 
 void
 DrawTargetD2D1::Flush()
 {
-  if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
-      mPushedLayers.size() == 1) {
-    // It's important to pop all clips as otherwise layers can forget about
-    // their clip when doing an EndDraw. When we have layers pushed we cannot
-    // easily pop all underlying clips to delay the purge until we have no
-    // layers pushed.
-    PopAllClips();
-    mUsedCommandListsSincePurge = 0;
-    mDC->EndDraw();
-    mDC->BeginDraw();
-  } else {
-    mDC->Flush();
+  if (IsDeviceContextValid()) {
+    if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
+        mPushedLayers.size() == 1) {
+      // It's important to pop all clips as otherwise layers can forget about
+      // their clip when doing an EndDraw. When we have layers pushed we cannot
+      // easily pop all underlying clips to delay the purge until we have no
+      // layers pushed.
+      PopAllClips();
+      mUsedCommandListsSincePurge = 0;
+      mDC->EndDraw();
+      mDC->BeginDraw();
+    } else {
+      mDC->Flush();
+    }
   }
 
   // We no longer depend on any target.
   for (TargetSet::iterator iter = mDependingOnTargets.begin();
        iter != mDependingOnTargets.end(); iter++) {
     (*iter)->mDependentTargets.erase(this);
   }
   mDependingOnTargets.clear();
@@ -1001,16 +1004,18 @@ bool
 DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
   ID2D1Device* device = Factory::GetD2D1Device();
   if (!device) {
     gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
     return false;
+  } else {
+    mDeviceSeq = Factory::GetD2D1DeviceSeq();
   }
 
   hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
 
   if (FAILED(hr)) {
     gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
     return false;
   }
@@ -1066,16 +1071,18 @@ bool
 DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
   ID2D1Device* device = Factory::GetD2D1Device();
   if (!device) {
     gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
     return false;
+  } else {
+    mDeviceSeq = Factory::GetD2D1DeviceSeq();
   }
 
   hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
 
   if (FAILED(hr)) {
     gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
     return false;
   }
@@ -1932,10 +1939,15 @@ DrawTargetD2D1::PushD2DLayer(ID2D1Device
 
   D2D1_ANTIALIAS_MODE antialias =
     aPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
 
   mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias, aTransform,
                                         1.0, nullptr, options), nullptr);
 }
 
+bool
+DrawTargetD2D1::IsDeviceContextValid() {
+  return (mDeviceSeq == Factory::GetD2D1DeviceSeq()) && Factory::GetD2D1Device();
+}
+
 }
 }
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -220,20 +220,22 @@ private:
   already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
   void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
 
   void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
                     bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
                     const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
 
+  // This function is used to determine if the mDC is still valid; if it is
+  // stale, we should avoid using it to execute any draw commands.
+  bool IsDeviceContextValid();
+
   IntSize mSize;
 
-  RefPtr<ID3D11Device> mDevice;
-  RefPtr<ID3D11Texture2D> mTexture;
   RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
   // This is only valid if mCurrentClippedGeometry is non-null. And will
   // only be the intersection of all pixel-aligned retangular clips. This is in
   // device space.
   IntRect mCurrentClipBounds;
   mutable RefPtr<ID2D1DeviceContext> mDC;
   RefPtr<ID2D1Bitmap1> mBitmap;
   RefPtr<ID2D1CommandList> mCommandList;
@@ -286,14 +288,16 @@ private:
   // subsequently used -again- as an input to a blend effect for a command list,
   // this causes an infinite recursion inside D2D as it tries to resolve the bounds.
   // If we resolve the current command list before this happens
   // we can avoid the subsequent hang. (See bug 1293586)
   bool mDidComplexBlendWithListInList;
 
   static ID2D1Factory1 *mFactory;
   static IDWriteFactory *mDWriteFactory;
+  // This value is uesed to verify if the DrawTarget is created by a stale device.
+  uint32_t mDeviceSeq;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -159,16 +159,17 @@ namespace gfx {
 // In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
 int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
 
 #ifdef MOZ_ENABLE_FREETYPE
 FT_Library Factory::mFTLibrary = nullptr;
 #endif
 
 #ifdef WIN32
+static uint32_t mDeviceSeq = 0;
 ID3D11Device *Factory::mD3D11Device = nullptr;
 ID2D1Device *Factory::mD2D1Device = nullptr;
 IDWriteFactory *Factory::mDWriteFactory = nullptr;
 #endif
 
 DrawEventRecorder *Factory::mRecorder;
 
 mozilla::gfx::Config* Factory::sConfig = nullptr;
@@ -671,16 +672,18 @@ Factory::SetDirect3D11Device(ID3D11Devic
   RefPtr<IDXGIDevice> device;
   aDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(device));
   HRESULT hr = factory->CreateDevice(device, &mD2D1Device);
   if (FAILED(hr)) {
     gfxCriticalError() << "[D2D1] Failed to create gfx factory's D2D1 device, code: " << hexa(hr);
 
     mD3D11Device = nullptr;
     return false;
+  } else {
+    mDeviceSeq++;
   }
 
   return true;
 }
 
 ID3D11Device*
 Factory::GetDirect3D11Device()
 {
@@ -688,16 +691,22 @@ Factory::GetDirect3D11Device()
 }
 
 ID2D1Device*
 Factory::GetD2D1Device()
 {
   return mD2D1Device;
 }
 
+uint32_t
+Factory::GetD2D1DeviceSeq()
+{
+  return mDeviceSeq;
+}
+
 IDWriteFactory*
 Factory::GetDWriteFactory()
 {
   return mDWriteFactory;
 }
 
 bool
 Factory::SupportsD2D1()