Bug 1422169: Have the D3D11TextureData destructor defer releasing the last reference to its DrawTarget to the paint thread. r=dvander
MozReview-Commit-ID: 1IJ5RrMJbFC
--- 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);