Bug 1425453 - Move some WebRender api calls into transactions. r=kats
authorNicolas Silva <nsilva@mozilla.com>
Fri, 12 Jan 2018 12:24:03 +0100
changeset 450822 c86b10bbd7b8c099a9bf5d7e6662c236c6e164c7
parent 450821 438bbffd59b07cf64c7ef8d7526a1a9a3980bd7b
child 450823 61c9a681836a72bedb42ae41b8b0d697e6ae7453
push id8543
push userryanvm@gmail.com
push dateTue, 16 Jan 2018 14:33:22 +0000
treeherdermozilla-beta@a6525ed16a32 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1425453
milestone59.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 1425453 - Move some WebRender api calls into transactions. r=kats
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/webrender_bindings/src/bindings.rs
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -271,40 +271,50 @@ AsyncImagePipelineManager::ApplyAsyncIma
 {
   if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
     return;
   }
 
   ++mAsyncImageEpoch; // Update webrender epoch
   wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
 
+  // TODO: We can improve upon this by using two transactions: one for everything that
+  // doesn't change the display list (in other words does not cause the scene to be
+  // re-built), and one for the rest. This way, if an async pipeline needs to re-build
+  // its display list, other async pipelines can still be rendered while the scene is
+  // building.
+  wr::TransactionBuilder txn;
+
   // We use a pipeline with a very small display list for each video element.
   // Update each of them if needed.
   for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
     wr::ResourceUpdateQueue resourceUpdates;
+
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     AsyncImagePipeline* pipeline = iter.Data();
 
     nsTArray<wr::ImageKey> keys;
     auto op = UpdateImageKeys(resourceUpdates, pipeline, keys);
 
+    txn.UpdateResources(resourceUpdates);
+
     bool updateDisplayList = pipeline->mInitialised &&
                              (pipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
                              !!pipeline->mCurrentTexture;
 
     // Request to generate frame if there is an update.
     if (updateDisplayList || !op.isNothing()) {
       SetWillGenerateFrame();
     }
 
     if (!updateDisplayList) {
       // We don't need to update the display list, either because we can't or because
       // the previous one is still up to date.
       // We may, however, have updated some resources.
-      mApi->UpdatePipelineResources(resourceUpdates, pipelineId, epoch);
+      txn.UpdateEpoch(pipelineId, epoch);
       if (pipeline->mCurrentTexture) {
         HoldExternalImage(pipelineId, epoch, pipeline->mCurrentTexture->AsWebRenderTextureHost());
       }
       continue;
     }
     pipeline->mIsChanged = false;
 
     wr::LayoutSize contentSize { pipeline->mScBounds.Width(), pipeline->mScBounds.Height() };
@@ -346,21 +356,24 @@ AsyncImagePipelineManager::ApplyAsyncIma
                         pipeline->mFilter,
                         keys[0]);
     }
     builder.PopStackingContext();
 
     wr::BuiltDisplayList dl;
     wr::LayoutSize builderContentSize;
     builder.Finalize(builderContentSize, dl);
-    mApi->SetDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.Width(), pipeline->mScBounds.Height()),
-                         pipelineId, builderContentSize,
-                         dl.dl_desc, dl.dl,
-                         resourceUpdates);
+    txn.SetDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f),
+                       epoch,
+                       LayerSize(pipeline->mScBounds.Width(), pipeline->mScBounds.Height()),
+                       pipelineId, builderContentSize,
+                       dl.dl_desc, dl.dl);
   }
+
+  mApi->SendTransaction(txn);
 }
 
 void
 AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
 {
   if (mDestroyed) {
     return;
   }
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -598,39 +598,43 @@ WebRenderBridgeParent::RecvSetDisplayLis
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
   uint32_t wrEpoch = GetNextWrEpoch();
 
   mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
+
   ProcessWebRenderParentCommands(aCommands);
 
   wr::ResourceUpdateQueue resources;
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, resources)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
+  wr::TransactionBuilder txn;
+  txn.UpdateResources(resources);
 
   wr::Vec<uint8_t> dlData(Move(dl));
 
   // If id namespaces do not match, it means the command is obsolete, probably
   // because the tab just moved to a new window.
   // In that case do not send the commands to webrender.
   if (mIdNamespace == aIdNamespace) {
     if (mWidget) {
       LayoutDeviceIntSize size = mWidget->GetClientSize();
-      mApi->SetWindowParameters(size);
+      txn.SetWindowParameters(size);
     }
     gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
-    mApi->SetDisplayList(clearColor, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
-                        mPipelineId, aContentSize,
-                        dlDesc, dlData,
-                        resources);
+    txn.SetDisplayList(clearColor, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
+                       mPipelineId, aContentSize,
+                       dlDesc, dlData);
+
+    mApi->SendTransaction(txn);
 
     ScheduleGenerateFrame();
 
     if (ShouldParentObserveEpoch()) {
       mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
     }
   }
 
@@ -681,36 +685,36 @@ WebRenderBridgeParent::RecvEmptyTransact
     ProcessWebRenderParentCommands(aCommands);
     ScheduleGenerateFrame();
   }
 
   mScrollData.SetFocusTarget(aFocusTarget);
   UpdateAPZ(false);
 
   if (!aCommands.IsEmpty()) {
+    wr::TransactionBuilder txn;
     uint32_t wrEpoch = GetNextWrEpoch();
-    // Send empty UpdatePipelineResources to WebRender just to notify a new epoch.
-    // The epoch is used to know a timing of calling DidComposite().
-    // This is much simpler than tracking an epoch of AsyncImagePipeline.
-    wr::ResourceUpdateQueue resourceUpdates;
-    mApi->UpdatePipelineResources(resourceUpdates, mPipelineId, wr::NewEpoch(wrEpoch));
+    txn.UpdateEpoch(mPipelineId, wr::NewEpoch(wrEpoch));
+    mApi->SendTransaction(txn);
+
     HoldPendingTransactionId(wrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
   } else {
     bool sendDidComposite = false;
     if (mPendingTransactionIds.empty()) {
       sendDidComposite = true;
     }
     HoldPendingTransactionId(mWrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
     // If WebRenderBridgeParent does not have pending DidComposites,
     // send DidComposite now.
     if (sendDidComposite) {
       TimeStamp now = TimeStamp::Now();
       mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
     }
   }
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetFocusTarget(const FocusTarget& aFocusTarget)
 {
   mScrollData.SetFocusTarget(aFocusTarget);
   UpdateAPZ(false);
@@ -1238,21 +1242,25 @@ WebRenderBridgeParent::CompositeToTarget
 
   wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId());
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   auto startTime = TimeStamp::Now();
   mApi->SetFrameStartTime(startTime);
 #endif
 
+  wr::TransactionBuilder txn;
+
   if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
-    mApi->GenerateFrame(opacityArray, transformArray);
-  } else {
-    mApi->GenerateFrame();
+    txn.UpdateDynamicProperties(opacityArray, transformArray);
   }
+
+  txn.GenerateFrame();
+
+  mApi->SendTransaction(txn);
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(uint32_t aWrEpoch,
                                                 uint64_t aTransactionId,
                                                 const TimeStamp& aTxnStartTime,
                                                 const TimeStamp& aFwdTime)
 {
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1067,16 +1067,19 @@ pub extern "C" fn wr_resource_updates_de
     resources.delete_image(key);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_send_transaction(
     dh: &mut DocumentHandle,
     transaction: &mut Transaction
 ) {
+    if transaction.is_empty() {
+        return;
+    }
     let txn = mem::replace(transaction, Transaction::new());
     dh.api.send_transaction(dh.document_id, txn);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_update_resources(
     dh: &mut DocumentHandle,
     resources: &mut ResourceUpdates