Bug 1422169: Have the D3D11TextureData destructor defer releasing the last reference to its DrawTarget to the paint thread. r=dvander draft
authorBas Schouten <bschouten@mozilla.com>
Fri, 01 Dec 2017 00:15:55 +0100
changeset 705986 13160f3d8d630ce98d5cb3eef034852c7ec9e9f8
parent 705339 14aa1b15bb8ff0bd1489c246faea224a0e7a8c56
child 742531 592be726ddb8a6ba39a838861328fa6c7e5ab43c
push id91658
push userbschouten@mozilla.com
push dateThu, 30 Nov 2017 23:16:16 +0000
reviewersdvander
bugs1422169
milestone59.0a1
Bug 1422169: Have the D3D11TextureData destructor defer releasing the last reference to its DrawTarget to the paint thread. r=dvander MozReview-Commit-ID: 1IJ5RrMJbFC
gfx/layers/PaintThread.cpp
gfx/layers/PaintThread.h
gfx/layers/d3d11/TextureD3D11.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -355,16 +355,26 @@ PaintThread::EndLayer()
 #ifndef OMTP_FORCE_SYNC
   sThread->Dispatch(task.forget());
 #else
   SyncRunnable::DispatchToThread(sThread, task);
 #endif
 }
 
 void
+PaintThread::Dispatch(RefPtr<Runnable>& aRunnable)
+{
+#ifndef OMTP_FORCE_SYNC
+  sThread->Dispatch(aRunnable.forget());
+#else
+  SyncRunnable::DispatchToThread(sThread, aRunnable);
+#endif
+}
+
+void
 PaintThread::AsyncEndLayer()
 {
   MOZ_ASSERT(IsOnPaintThread());
   // Textureclient forces a flush once we "end paint", so
   // users of this texture expect all the drawing to be complete.
   // Force a flush now.
   for (size_t i = 0; i < mDrawTargetsToFlush.Length(); i++) {
     mDrawTargetsToFlush[i]->Flush();
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -137,16 +137,19 @@ public:
   void PaintContents(CapturedPaintState* aState,
                      PrepDrawTargetForPaintingCallback aCallback);
 
   // Must be called on the main thread. Signifies that the current
   // batch of CapturedPaintStates* for PaintContents have been recorded
   // and the main thread is finished recording this layer.
   void EndLayer();
 
+  // This allows external users to run code on the paint thread.
+  void Dispatch(RefPtr<Runnable>& aRunnable);
+
   // Must be called on the main thread. Signifies that the current
   // layer tree transaction has been finished and any async paints
   // for it have been queued on the paint thread. This MUST be called
   // at the end of a layer transaction as it will be used to do an optional
   // texture sync and then unblock the main thread if it is waiting to paint
   // a new frame.
   void EndLayerTransaction(SyncObjectClient* aSyncObject);
 
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/webrender/RenderD3D11TextureHostOGL.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/webrender/WebRenderAPI.h"
+#include "PaintThread.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 static const GUID sD3D11TextureUsage =
@@ -282,28 +283,47 @@ D3D11TextureData::D3D11TextureData(ID3D1
                                    bool aIsForOutOfBandContent)
 : DXGITextureData(aSize, aFormat, aNeedsClear, aNeedsClearWhite, aIsForOutOfBandContent)
 , mTexture(aTexture)
 {
   MOZ_ASSERT(aTexture);
   mHasSynchronization = HasKeyedMutex(aTexture);
 }
 
-D3D11TextureData::~D3D11TextureData()
+static void DestroyDrawTarget(RefPtr<DrawTarget>& aDT, RefPtr<ID3D11Texture2D>& aTexture)
 {
-#ifdef DEBUG
   // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is
   // when it calls EndDraw. This EndDraw should not execute anything so it
   // shouldn't -really- need the lock but the debug layer chokes on this.
+#ifdef DEBUG
+  LockD3DTexture(aTexture.get());
+#endif
+  aDT = nullptr;
+#ifdef DEBUG
+  UnlockD3DTexture(aTexture.get());
+#endif
+  aTexture = nullptr;
+}
+
+D3D11TextureData::~D3D11TextureData()
+{
   if (mDrawTarget) {
-    Lock(OpenMode::OPEN_NONE);
-    mDrawTarget = nullptr;
-    Unlock();
+    if (PaintThread::Get() && gfxPrefs::Direct2DDestroyDTOnPaintThread()) {
+      RefPtr<DrawTarget> dt = mDrawTarget;
+      RefPtr<ID3D11Texture2D> tex = mTexture;
+      RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::RunFunction",
+        [dt, tex]() mutable { DestroyDrawTarget(dt, tex); });
+      PaintThread::Get()->Dispatch(task);
+    }
+#ifdef DEBUG
+    else {
+      DestroyDrawTarget(mDrawTarget, mTexture);
+    }
+#endif
   }
-#endif
 }
 
 bool
 D3D11TextureData::Lock(OpenMode aMode)
 {
   if (!LockD3DTexture(mTexture.get())) {
     return false;
   }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -449,16 +449,17 @@ private:
   // Size in megabytes
   DECL_GFX_PREF(Once, "gfx.content.skia-font-cache-size",      SkiaContentFontCacheSize, int32_t, 10);
 
   DECL_GFX_PREF(Once, "gfx.device-reset.limit",                DeviceResetLimitCount, int32_t, 10);
   DECL_GFX_PREF(Once, "gfx.device-reset.threshold-ms",         DeviceResetThresholdMilliseconds, int32_t, -1);
 
   DECL_GFX_PREF(Once, "gfx.direct2d.disabled",                 Direct2DDisabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled",            Direct2DForceEnabled, bool, false);
+  DECL_GFX_PREF(Live, "gfx.direct2d.destroy-dt-on-paintthread",Direct2DDestroyDTOnPaintThread, bool, true);
   DECL_GFX_PREF(Live, "gfx.direct3d11.reuse-decoder-device",   Direct3D11ReuseDecoderDevice, int32_t, -1);
   DECL_GFX_PREF(Live, "gfx.direct3d11.allow-keyed-mutex",      Direct3D11AllowKeyedMutex, bool, true);
   DECL_GFX_PREF(Live, "gfx.direct3d11.use-double-buffering",   Direct3D11UseDoubleBuffering, bool, false);
   DECL_GFX_PREF(Once, "gfx.direct3d11.enable-debug-layer",     Direct3D11EnableDebugLayer, bool, false);
   DECL_GFX_PREF(Once, "gfx.direct3d11.break-on-error",         Direct3D11BreakOnError, bool, false);
   DECL_GFX_PREF(Once, "gfx.direct3d11.sleep-on-create-device", Direct3D11SleepOnCreateDevice, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.downloadable_fonts.keep_variation_tables", KeepVariationTables, bool, false);
   DECL_GFX_PREF(Live, "gfx.downloadable_fonts.otl_validation", ValidateOTLTables, bool, true);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4825,16 +4825,20 @@ pref("widget.window-transforms.disabled"
 #ifdef XP_WIN
 // Whether to disable the automatic detection and use of direct2d.
 pref("gfx.direct2d.disabled", false);
 
 // Whether to attempt to enable Direct2D regardless of automatic detection or
 // blacklisting
 pref("gfx.direct2d.force-enabled", false);
 
+// Whether to defer destruction of Direct2D DrawTargets to the paint thread
+// when using OMTP.
+pref("gfx.direct2d.destroy-dt-on-paintthread", true);
+
 pref("gfx.direct3d11.enable-debug-layer", false);
 pref("gfx.direct3d11.break-on-error", false);
 
 pref("layers.prefer-opengl", false);
 #endif
 
 // Copy-on-write canvas
 pref("layers.shared-buffer-provider.enabled", true);