Bug 1387639 Sync Textures once all async OMTP paints are done. r=mattwoodrow
authorMason Chang <mchang@mozilla.com>
Thu, 10 Aug 2017 21:41:31 -0700
changeset 424176 e7f2be7b6b95e9c128978a55c6b7c81c9f6ab6af
parent 424175 3f44f13e8021c0a4f23270db804322d42fa40828
child 424177 2dd2794946eb3998f131b6a96fec709c213ede27
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1387639
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1387639 Sync Textures once all async OMTP paints are done. r=mattwoodrow
gfx/layers/PaintThread.cpp
gfx/layers/PaintThread.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/client/ClientPaintedLayer.cpp
gfx/layers/d3d11/TextureD3D11.cpp
gfx/layers/d3d11/TextureD3D11.h
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -4,16 +4,18 @@
  * 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 "PaintThread.h"
 
 #include "base/task.h"
 #include "gfxPrefs.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/SyncObject.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SyncRunnable.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
@@ -202,16 +204,45 @@ PaintThread::EndAsyncPainting(Compositor
 
   mDrawTargetsToFlush.Clear();
 
   if (aBridge) {
     aBridge->NotifyFinishedAsyncPaintLayer();
   }
 }
 
+void
+PaintThread::SynchronizePaintTextures(SyncObjectClient* aSyncObject)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aSyncObject);
+
+  RefPtr<SyncObjectClient> syncObject(aSyncObject);
+  RefPtr<PaintThread> self = this;
+  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::SyncTextureData",
+    [self, syncObject]() -> void
+  {
+    self->SyncTextureData(syncObject);
+  });
+
+  if (!gfxPrefs::LayersOMTPForceSync()) {
+    sThread->Dispatch(task.forget());
+  } else {
+    SyncRunnable::DispatchToThread(sThread, task);
+  }
+}
+
+void
+PaintThread::SyncTextureData(SyncObjectClient* aSyncObject)
+{
+  MOZ_ASSERT(IsOnPaintThread());
+  MOZ_ASSERT(aSyncObject);
+
+  aSyncObject->Synchronize();
+}
 
 void
 PaintThread::PaintContents(CapturedPaintState* aState,
                            PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState);
 
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -70,16 +70,24 @@ public:
   void PaintContents(CapturedPaintState* aState,
                      PrepDrawTargetForPaintingCallback aCallback);
 
   // To 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 FinishedLayerBatch();
 
+  // Must be called on the main thread. Tells the paint thread
+  // to schedule a sync textures after all async paints are done.
+  // NOTE: The other paint thread functions are on a per PAINT
+  // or per paint layer basis. This MUST be called at the end
+  // of a layer transaction as multiple paints can occur
+  // with multiple layers. We only have to do this once per transaction.
+  void SynchronizePaintTextures(SyncObjectClient* aSyncObject);
+
   // Sync Runnables need threads to be ref counted,
   // But this thread lives through the whole process.
   // We're only temporarily using sync runnables so
   // Override release/addref but don't do anything.
   void Release();
   void AddRef();
 
   // Helper for asserts.
@@ -88,16 +96,17 @@ public:
 private:
   bool Init();
   void ShutdownOnPaintThread();
   void InitOnPaintThread();
   void PaintContentsAsync(CompositorBridgeChild* aBridge,
                           CapturedPaintState* aState,
                           PrepDrawTargetForPaintingCallback aCallback);
   void EndAsyncPainting(CompositorBridgeChild* aBridge);
+  void SyncTextureData(SyncObjectClient* aSyncObject);
 
   static StaticAutoPtr<PaintThread> sSingleton;
   static StaticRefPtr<nsIThread> sThread;
   static PlatformThreadId sThreadId;
 
   // This shouldn't be very many elements, so a list should be fine.
   // Should only be accessed on the paint thread.
   nsTArray<RefPtr<gfx::DrawTarget>> mDrawTargetsToFlush;
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -95,16 +95,17 @@ ClientLayerManager::ClientLayerManager(n
   , mLatestTransactionId(0)
   , mLastPaintTime(TimeDuration::Forever())
   , mTargetRotation(ROTATION_0)
   , mRepeatTransaction(false)
   , mIsRepeatTransaction(false)
   , mTransactionIncomplete(false)
   , mCompositorMightResample(false)
   , mNeedsComposite(false)
+  , mTextureSyncOnPaintThread(false)
   , mPaintSequenceNumber(0)
   , mDeviceResetSequenceNumber(0)
   , mForwarder(new ShadowLayerForwarder(this))
 {
   MOZ_COUNT_CTOR(ClientLayerManager);
   mMemoryPressureObserver = new MemoryPressureObserver(this);
 
   if (XRE_IsContentProcess()) {
@@ -353,16 +354,17 @@ ClientLayerManager::EndTransactionIntern
 #endif
 
   NS_ASSERTION(InConstruction(), "Should be in construction phase");
   mPhase = PHASE_DRAWING;
 
   ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
 
   mTransactionIncomplete = false;
+  mTextureSyncOnPaintThread = false;
 
   // Apply pending tree updates before recomputing effective
   // properties.
   GetRoot()->ApplyPendingUpdatesToSubtree();
 
   mPaintedLayerCallback = aCallback;
   mPaintedLayerCallbackData = aCallbackData;
 
@@ -720,21 +722,27 @@ ClientLayerManager::StopFrameTimeRecordi
 
 void
 ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
 {
   AutoProfilerTracing tracing("Paint", "ForwardTransaction");
   TimeStamp start = TimeStamp::Now();
 
   // Skip the synchronization for buffer since we also skip the painting during
-  // device-reset status.
+  // device-reset status. With OMTP, we have to wait for async paints
+  // before we synchronize and it's done on the paint thread.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (mForwarder->GetSyncObject() &&
         mForwarder->GetSyncObject()->IsSyncObjectValid()) {
-      mForwarder->GetSyncObject()->Synchronize();
+      if (mTextureSyncOnPaintThread) {
+        // We have to wait for all async paints to finish to do this
+        PaintThread::Get()->SynchronizePaintTextures(mForwarder->GetSyncObject());
+      } else {
+        mForwarder->GetSyncObject()->Synchronize();
+      }
     }
   }
 
   mPhase = PHASE_FORWARD;
 
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(!mIsRepeatTransaction);
   TimeStamp transactionStart;
   if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -12,16 +12,17 @@
 #include "gfxPrefs.h"
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/LinkedList.h"         // For LinkedList
 #include "mozilla/WidgetUtils.h"        // for ScreenRotation
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FocusTarget.h"  // for FocusTarget
 #include "mozilla/layers/LayersTypes.h"  // for BufferMode, LayersBackend, etc
+#include "mozilla/layers/PaintThread.h" // For PaintThread
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder, etc
 #include "mozilla/layers/APZTestData.h" // for APZTestData
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsIObserver.h"                // for nsIObserver
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsTArray.h"                   // for nsTArray
 #include "nscore.h"                     // for nsAString
@@ -153,16 +154,17 @@ public:
   void HandleMemoryPressure();
 
   void SetRepeatTransaction() { mRepeatTransaction = true; }
   bool GetRepeatTransaction() { return mRepeatTransaction; }
 
   bool IsRepeatTransaction() { return mIsRepeatTransaction; }
 
   void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+  void SetNeedTextureSyncOnPaintThread() { mTextureSyncOnPaintThread = true; }
 
   bool HasShadowTarget() { return !!mShadowTarget; }
 
   void SetShadowTarget(gfxContext* aTarget) { mShadowTarget = aTarget; }
 
   bool CompositorMightResample() { return mCompositorMightResample; } 
   
   DrawPaintedLayerCallback GetPaintedLayerCallback() const
@@ -344,16 +346,17 @@ private:
 
   // Used to repeat the transaction right away (to avoid rebuilding
   // a display list) to support progressive drawing.
   bool mRepeatTransaction;
   bool mIsRepeatTransaction;
   bool mTransactionIncomplete;
   bool mCompositorMightResample;
   bool mNeedsComposite;
+  bool mTextureSyncOnPaintThread;
 
   // An incrementing sequence number for paints.
   // Incremented in BeginTransaction(), but not for repeat transactions.
   uint32_t mPaintSequenceNumber;
 
   // A sequence number for checking whether we have not yet acknowledged
   // a device reset.
   uint64_t mDeviceResetSequenceNumber;
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -263,16 +263,17 @@ ClientPaintedLayer::PaintOffMainThread()
     didUpdate = true;
   }
 
   PaintThread::Get()->FinishedLayerBatch();
   mContentClient->EndPaint(nullptr);
 
   if (didUpdate) {
     UpdateContentClient(state);
+    ClientManager()->SetNeedTextureSyncOnPaintThread();
   }
   return true;
 }
 
 void
 ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
 {
   RenderMaskLayers(this);
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -1618,16 +1618,17 @@ SyncObjectD3D11Host::Synchronize()
     gfxCriticalNote << "GFX: AL_D3D11 abandoned sync";
   }
 
   return true;
 }
 
 SyncObjectD3D11Client::SyncObjectD3D11Client(SyncHandle aSyncHandle, ID3D11Device* aDevice)
  : mSyncHandle(aSyncHandle)
+ , mSyncLock("SyncObjectD3D11")
 {
   if (!aDevice) {
     mDevice = DeviceManagerDx::Get()->GetContentDevice();
     return;
   }
 
   mDevice = aDevice;
 }
@@ -1673,19 +1674,29 @@ SyncObjectD3D11Client::IsSyncObjectValid
 {
   RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
   if (!dev || (NS_IsMainThread() && dev != mDevice)) {
     return false;
   }
   return true;
 }
 
+// We have only 1 sync object. As a thing that somehow works,
+// we copy each of the textures that need to be synced with the compositor
+// into our sync object and only use a lock for this sync object.
+// This way, we don't have to sync every texture we send to the compositor.
+// We only have to do this once per transaction.
 void
 SyncObjectD3D11Client::Synchronize()
 {
+  // This can be called from the paint or main thread depending on OMTP.
+  // We need this lock in addition to the AutoTextureLock in case we have to
+  // init here.
+  MutexAutoLock syncLock(mSyncLock);
+
   if (!mSyncedTextures.size()) {
     return;
   }
   if (!Init()) {
     return;
   }
 
   HRESULT hr;
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -479,16 +479,17 @@ public:
 private:
   bool Init();
 
   SyncHandle mSyncHandle;
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11Texture2D> mSyncTexture;
   RefPtr<IDXGIKeyedMutex> mKeyedMutex;
   std::vector<ID3D11Texture2D*> mSyncedTextures;
+  Mutex mSyncLock;
 };
 
 inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel)
 {
   int32_t maxTextureSize;
   switch (aFeatureLevel) {
   case D3D_FEATURE_LEVEL_11_1:
   case D3D_FEATURE_LEVEL_11_0: