diff --git a/gfx/layers/apz/public/CompositorController.h b/gfx/layers/apz/public/CompositorController.h index 25ed2d0053dd..d378422ba2fb 100644 --- a/gfx/layers/apz/public/CompositorController.h +++ b/gfx/layers/apz/public/CompositorController.h @@ -19,7 +19,8 @@ class CompositorController { NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING virtual void ScheduleRenderOnCompositorThread( - const Maybe& aRenderRootid = Nothing()) = 0; + const nsTArray& aRenderRoots = + nsTArray()) = 0; virtual void ScheduleHideAllPluginWindows() = 0; virtual void ScheduleShowAllPluginWindows() = 0; diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp index 1fda2225f1b0..3918a93c18f4 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -650,11 +650,11 @@ void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) { } void CompositorBridgeParent::ScheduleRenderOnCompositorThread( - const Maybe& aRenderRoot) { + const nsTArray& aRenderRoots) { MOZ_ASSERT(CompositorLoop()); - CompositorLoop()->PostTask(NewRunnableMethod>( + CompositorLoop()->PostTask(NewRunnableMethod>( "layers::CompositorBridgeParent::ScheduleComposition", this, - &CompositorBridgeParent::ScheduleComposition, aRenderRoot)); + &CompositorBridgeParent::ScheduleComposition, aRenderRoots)); } void CompositorBridgeParent::InvalidateOnCompositorThread() { @@ -864,18 +864,14 @@ void CompositorBridgeParent::NotifyShadowTreeTransaction( } void CompositorBridgeParent::ScheduleComposition( - const Maybe& aRenderRoot) { + const nsTArray& aRenderRoots) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (mPaused) { return; } if (mWrBridge) { - if (aRenderRoot.isSome()) { - mWrBridge->ScheduleGenerateFrame(aRenderRoot); - } else { - mWrBridge->ScheduleGenerateFrameAllRenderRoots(); - } + mWrBridge->ScheduleGenerateFrame(aRenderRoots); } else { mCompositorScheduler->ScheduleComposition(); } @@ -2064,14 +2060,15 @@ void CompositorBridgeParent::DidComposite(const VsyncId& aId, } void CompositorBridgeParent::NotifyDidSceneBuild( - wr::RenderRoot aRenderRoot, RefPtr aInfo) { + const nsTArray& aRenderRoots, + RefPtr aInfo) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (mPaused) { return; } if (mWrBridge) { - mWrBridge->NotifyDidSceneBuild(aRenderRoot, aInfo); + mWrBridge->NotifyDidSceneBuild(aRenderRoots, aInfo); } else { mCompositorScheduler->ScheduleComposition(); } diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h index 944bb55cabdf..ae5da5bb87a9 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.h +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -399,7 +399,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, TimeStamp& aCompositeStart, TimeStamp& aRenderStart, TimeStamp& aCompositeEnd, wr::RendererStats* aStats = nullptr); - void NotifyDidSceneBuild(wr::RenderRoot aRenderRoot, + void NotifyDidSceneBuild(const nsTArray& aRenderRoots, RefPtr aInfo); RefPtr GetAsyncImagePipelineManager() const; @@ -427,7 +427,8 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, // Can be called from any thread void ScheduleRenderOnCompositorThread( - const Maybe& aRenderRoot = Nothing()) override; + const nsTArray& aRenderRoots = + nsTArray()) override; void SchedulePauseOnCompositorThread(); void InvalidateOnCompositorThread(); /** @@ -437,8 +438,9 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, bool ScheduleResumeOnCompositorThread(); bool ScheduleResumeOnCompositorThread(int x, int y, int width, int height); - void ScheduleComposition( - const Maybe& aRenderRoot = Nothing()); + void ScheduleComposition(const nsTArray& aRenderRoots = + nsTArray()); + void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint, const FocusTarget& aFocusTarget, bool aScheduleComposite, diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index c017fe5e0368..ac72d1258878 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -2002,6 +2002,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, #endif MOZ_ASSERT(framesGenerated > 0); + wr::RenderRootArray generateFrameTxns; for (auto& api : mApis) { if (!api) { continue; @@ -2009,9 +2010,11 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, auto renderRoot = api->GetRenderRoot(); if (generateFrame[renderRoot]) { fastTxns[renderRoot]->GenerateFrame(); - api->SendTransaction(*fastTxns[renderRoot]); + generateFrameTxns[renderRoot] = fastTxns[renderRoot].ptr(); } } + wr::WebRenderAPI::SendTransactions(mApis, generateFrameTxns); + mMostRecentComposite = TimeStamp::Now(); } @@ -2059,13 +2062,16 @@ void WebRenderBridgeParent::NotifySceneBuiltForEpoch( } void WebRenderBridgeParent::NotifyDidSceneBuild( - wr::RenderRoot aRenderRoot, RefPtr aInfo) { + const nsTArray& aRenderRoots, + RefPtr aInfo) { MOZ_ASSERT(IsRootWebRenderBridgeParent()); if (!mCompositorScheduler) { return; } - mAsyncImageManager->SetWillGenerateFrame(aRenderRoot); + for (auto renderRoot : aRenderRoots) { + mAsyncImageManager->SetWillGenerateFrame(renderRoot); + } // If the scheduler has a composite more recent than our last composite (which // we missed), and we're within the threshold ms of the last vsync, then @@ -2196,6 +2202,19 @@ void WebRenderBridgeParent::ScheduleGenerateFrame( } } +void WebRenderBridgeParent::ScheduleGenerateFrame( + const nsTArray& aRenderRoots) { + if (mCompositorScheduler) { + if (aRenderRoots.IsEmpty()) { + mAsyncImageManager->SetWillGenerateFrameAllRenderRoots(); + } + for (auto renderRoot : aRenderRoots) { + mAsyncImageManager->SetWillGenerateFrame(renderRoot); + } + mCompositorScheduler->ScheduleComposition(); + } +} + void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) { if (mDestroyed) { return; diff --git a/gfx/layers/wr/WebRenderBridgeParent.h b/gfx/layers/wr/WebRenderBridgeParent.h index 769be9abc989..7e9818b3967f 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.h +++ b/gfx/layers/wr/WebRenderBridgeParent.h @@ -231,6 +231,7 @@ class WebRenderBridgeParent final * want to trigger AsyncImagePipelines update checks. */ void ScheduleGenerateFrame(const Maybe& aRenderRoot); + void ScheduleGenerateFrame(const nsTArray& aRenderRoots); void ScheduleGenerateFrameAllRenderRoots(); /** @@ -241,7 +242,7 @@ class WebRenderBridgeParent final */ void ScheduleForcedGenerateFrame(); - void NotifyDidSceneBuild(wr::RenderRoot aRenderRoot, + void NotifyDidSceneBuild(const nsTArray& aRenderRoots, RefPtr aInfo); wr::Epoch UpdateWebRender( diff --git a/gfx/webrender_bindings/RenderThread.cpp b/gfx/webrender_bindings/RenderThread.cpp index e935d644a429..a70cfe7d411b 100644 --- a/gfx/webrender_bindings/RenderThread.cpp +++ b/gfx/webrender_bindings/RenderThread.cpp @@ -941,30 +941,41 @@ void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId, } void wr_schedule_render(mozilla::wr::WrWindowId aWindowId, - mozilla::wr::WrDocumentId aDocumentId) { + const mozilla::wr::WrDocumentId* aDocumentIds, + size_t aDocumentIdsCount) { RefPtr cbp = mozilla::layers:: CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId); if (cbp) { - cbp->ScheduleRenderOnCompositorThread( - Some(wr::RenderRootFromId(aDocumentId))); + InfallibleTArray renderRoots; + renderRoots.SetLength(aDocumentIdsCount); + for (size_t i = 0; i < aDocumentIdsCount; ++i) { + renderRoots[i] = wr::RenderRootFromId(aDocumentIds[i]); + } + cbp->ScheduleRenderOnCompositorThread(renderRoots); } } static void NotifyDidSceneBuild(RefPtr aBridge, - wr::DocumentId aRenderRootId, + const nsTArray& aRenderRoots, RefPtr aInfo) { - aBridge->NotifyDidSceneBuild(wr::RenderRootFromId(aRenderRootId), aInfo); + aBridge->NotifyDidSceneBuild(aRenderRoots, aInfo); } void wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId, - mozilla::wr::WrDocumentId aDocumentId, + const mozilla::wr::WrDocumentId* aDocumentIds, + size_t aDocumentIdsCount, mozilla::wr::WrPipelineInfo aInfo) { RefPtr cbp = mozilla::layers:: CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId); RefPtr info = new wr::WebRenderPipelineInfo(aInfo); if (cbp) { + InfallibleTArray renderRoots; + renderRoots.SetLength(aDocumentIdsCount); + for (size_t i = 0; i < aDocumentIdsCount; ++i) { + renderRoots[i] = wr::RenderRootFromId(aDocumentIds[i]); + } layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction( - "NotifyDidSceneBuild", &NotifyDidSceneBuild, cbp, aDocumentId, info)); + "NotifyDidSceneBuild", &NotifyDidSceneBuild, cbp, renderRoots, info)); } } diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index c60068a2b7c1..4d7967507558 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -366,6 +366,39 @@ void WebRenderAPI::SendTransaction(TransactionBuilder& aTxn) { wr_api_send_transaction(mDocHandle, aTxn.Raw(), aTxn.UseSceneBuilderThread()); } +/* static */ +void WebRenderAPI::SendTransactions( + const RenderRootArray>& aApis, + RenderRootArray& aTxns) { + if (!aApis[RenderRoot::Default]) { + return; + } + + aApis[RenderRoot::Default]->UpdateDebugFlags(gfx::gfxVars::WebRenderDebugFlags()); + AutoTArray documentHandles; + AutoTArray txns; + Maybe useSceneBuilderThread; + for (auto& api : aApis) { + if (!api) { + continue; + } + auto& txn = aTxns[api->GetRenderRoot()]; + if (txn) { + documentHandles.AppendElement(api->mDocHandle); + txns.AppendElement(txn->Raw()); + if (useSceneBuilderThread.isSome()) { + MOZ_ASSERT(txn->UseSceneBuilderThread() == *useSceneBuilderThread); + } else { + useSceneBuilderThread.emplace(txn->UseSceneBuilderThread()); + } + } + } + if (!txns.IsEmpty()) { + wr_api_send_transactions(documentHandles.Elements(), txns.Elements(), + txns.Length(), *useSceneBuilderThread); + } +} + bool WebRenderAPI::HitTest(const wr::WorldPoint& aPoint, wr::WrPipelineId& aOutPipelineId, layers::ScrollableLayerGuid::ViewID& aOutScrollId, diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index b0a2b73a18a8..9e5621b1543c 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -209,6 +209,9 @@ class WebRenderAPI final { RefPtr&& aWidget, const wr::WrWindowId& aWindowId, LayoutDeviceIntSize aSize); + static void SendTransactions(const RenderRootArray>& aApis, + RenderRootArray& aTxns); + already_AddRefed CreateDocument(LayoutDeviceIntSize aSize, int8_t aLayerIndex, wr::RenderRoot aRenderRoot); diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index ce1b17966011..c8fd007253a9 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -581,8 +581,13 @@ extern "C" { fn wr_notifier_nop_frame_done(window_id: WrWindowId); fn wr_notifier_external_event(window_id: WrWindowId, raw_event: usize); - fn wr_schedule_render(window_id: WrWindowId, document_id: WrDocumentId); - fn wr_finished_scene_build(window_id: WrWindowId, document_id: WrDocumentId, pipeline_info: WrPipelineInfo); + fn wr_schedule_render(window_id: WrWindowId, + document_id_array: *const WrDocumentId, + document_id_count: usize); + fn wr_finished_scene_build(window_id: WrWindowId, + document_id_array: *const WrDocumentId, + document_id_count: usize, + pipeline_info: WrPipelineInfo); fn wr_transaction_notification_notified(handler: usize, when: Checkpoint); } @@ -916,7 +921,7 @@ impl SceneBuilderHooks for APZCallbacks { } } - fn post_scene_swap(&self, document_id: DocumentId, info: PipelineInfo, sceneswap_time: u64) { + fn post_scene_swap(&self, document_ids: &Vec, info: PipelineInfo, sceneswap_time: u64) { unsafe { let info = WrPipelineInfo::new(&info); record_telemetry_time(TelemetryProbe::SceneSwapTime, sceneswap_time); @@ -927,12 +932,12 @@ impl SceneBuilderHooks for APZCallbacks { // After a scene swap we should schedule a render for the next vsync, // otherwise there's no guarantee that the new scene will get rendered // anytime soon - unsafe { wr_finished_scene_build(self.window_id, document_id, info) } + unsafe { wr_finished_scene_build(self.window_id, document_ids.as_ptr(), document_ids.len(), info) } unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); } } - fn post_resource_update(&self, document_id: DocumentId) { - unsafe { wr_schedule_render(self.window_id, document_id) } + fn post_resource_update(&self, document_ids: &Vec) { + unsafe { wr_schedule_render(self.window_id, document_ids.as_ptr(), document_ids.len()) } unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); } } @@ -1720,6 +1725,30 @@ pub extern "C" fn wr_api_send_transaction( dh.api.send_transaction(dh.document_id, txn); } +#[no_mangle] +pub unsafe extern "C" fn wr_api_send_transactions( + document_handles: *const *const DocumentHandle, + transactions: *const *mut Transaction, + transaction_count: usize, + is_async: bool +) { + if transaction_count == 0 { + return; + } + let mut out_transactions = Vec::with_capacity(transaction_count); + let mut out_documents = Vec::with_capacity(transaction_count); + for i in 0..transaction_count { + let txn = &mut **transactions.offset(i as isize); + debug_assert!(!txn.is_empty()); + let new_txn = make_transaction(is_async); + out_transactions.push(mem::replace(txn, new_txn)); + out_documents.push((**document_handles.offset(i as isize)).document_id); + } + (**document_handles).api.send_transactions( + out_documents, + out_transactions); +} + #[no_mangle] pub unsafe extern "C" fn wr_transaction_clear_display_list( txn: &mut Transaction, diff --git a/gfx/wr/webrender/src/record.rs b/gfx/wr/webrender/src/record.rs index b7d46427eac0..c878215681f5 100644 --- a/gfx/wr/webrender/src/record.rs +++ b/gfx/wr/webrender/src/record.rs @@ -2,7 +2,7 @@ * 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/. */ -use api::{ApiMsg, FrameMsg, SceneMsg}; +use api::{ApiMsg, FrameMsg, SceneMsg, TransactionMsg}; use bincode::serialize; use byteorder::{LittleEndian, WriteBytesExt}; use std::any::TypeId; @@ -61,32 +61,41 @@ impl ApiRecordingReceiver for BinaryRecorder { } } +fn should_record_transaction_msg(msgs: &TransactionMsg) -> bool { + if msgs.generate_frame { + return true; + } + + for msg in &msgs.scene_ops { + match *msg { + SceneMsg::SetDisplayList { .. } | + SceneMsg::SetRootPipeline { .. } => return true, + _ => {} + } + } + + for msg in &msgs.frame_ops { + match *msg { + FrameMsg::GetScrollNodeState(..) | + FrameMsg::HitTest(..) => {} + _ => return true, + } + } + + false +} + pub fn should_record_msg(msg: &ApiMsg) -> bool { match *msg { ApiMsg::UpdateResources(..) | ApiMsg::AddDocument { .. } | ApiMsg::DeleteDocument(..) => true, - ApiMsg::UpdateDocument(_, ref msgs) => { - if msgs.generate_frame { - return true; - } - - for msg in &msgs.scene_ops { - match *msg { - SceneMsg::SetDisplayList { .. } | - SceneMsg::SetRootPipeline { .. } => return true, - _ => {} + ApiMsg::UpdateDocuments(_, ref msgs) => { + for msg in msgs { + if should_record_transaction_msg(msg) { + return true; } } - - for msg in &msgs.frame_ops { - match *msg { - FrameMsg::GetScrollNodeState(..) | - FrameMsg::HitTest(..) => {} - _ => return true, - } - } - false } _ => false, diff --git a/gfx/wr/webrender/src/render_backend.rs b/gfx/wr/webrender/src/render_backend.rs index 04a3341d026d..8028082a4237 100644 --- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -862,57 +862,59 @@ impl RenderBackend { while let Ok(msg) = self.scene_rx.try_recv() { match msg { - SceneBuilderResult::Transaction(mut txn, result_tx) => { - let has_built_scene = txn.built_scene.is_some(); - if let Some(doc) = self.documents.get_mut(&txn.document_id) { + SceneBuilderResult::Transactions(mut txns, result_tx) => { + for mut txn in txns.drain(..) { + let has_built_scene = txn.built_scene.is_some(); + if let Some(doc) = self.documents.get_mut(&txn.document_id) { - doc.removed_pipelines.append(&mut txn.removed_pipelines); + doc.removed_pipelines.append(&mut txn.removed_pipelines); - if let Some(mut built_scene) = txn.built_scene.take() { - doc.new_async_scene_ready( - built_scene, - &mut self.recycler, - ); + if let Some(mut built_scene) = txn.built_scene.take() { + doc.new_async_scene_ready( + built_scene, + &mut self.recycler, + ); + } + + if let Some(ref tx) = result_tx { + let (resume_tx, resume_rx) = channel(); + tx.send(SceneSwapResult::Complete(resume_tx)).unwrap(); + // Block until the post-swap hook has completed on + // the scene builder thread. We need to do this before + // we can sample from the sampler hook which might happen + // in the update_document call below. + resume_rx.recv().ok(); + } + } else { + // The document was removed while we were building it, skip it. + // TODO: we might want to just ensure that removed documents are + // always forwarded to the scene builder thread to avoid this case. + if let Some(ref tx) = result_tx { + tx.send(SceneSwapResult::Aborted).unwrap(); + } + continue; } - if let Some(tx) = result_tx { - let (resume_tx, resume_rx) = channel(); - tx.send(SceneSwapResult::Complete(resume_tx)).unwrap(); - // Block until the post-swap hook has completed on - // the scene builder thread. We need to do this before - // we can sample from the sampler hook which might happen - // in the update_document call below. - resume_rx.recv().ok(); + self.resource_cache.add_rasterized_blob_images( + txn.rasterized_blobs.take() + ); + if let Some((rasterizer, info)) = txn.blob_rasterizer.take() { + self.resource_cache.set_blob_rasterizer(rasterizer, info); } - } else { - // The document was removed while we were building it, skip it. - // TODO: we might want to just ensure that removed documents are - // always forwarded to the scene builder thread to avoid this case. - if let Some(tx) = result_tx { - tx.send(SceneSwapResult::Aborted).unwrap(); - } - continue; + + self.update_document( + txn.document_id, + txn.resource_updates.take(), + txn.interner_updates.take(), + txn.frame_ops.take(), + txn.notifications.take(), + txn.render_frame, + txn.invalidate_rendered_frame, + &mut frame_counter, + &mut profile_counters, + has_built_scene, + ); } - - self.resource_cache.add_rasterized_blob_images( - txn.rasterized_blobs.take() - ); - if let Some((rasterizer, info)) = txn.blob_rasterizer.take() { - self.resource_cache.set_blob_rasterizer(rasterizer, info); - } - - self.update_document( - txn.document_id, - txn.resource_updates.take(), - txn.interner_updates.take(), - txn.frame_ops.take(), - txn.notifications.take(), - txn.render_frame, - txn.invalidate_rendered_frame, - &mut frame_counter, - &mut profile_counters, - has_built_scene, - ); }, SceneBuilderResult::FlushComplete(tx) => { tx.send(()).ok(); @@ -1120,7 +1122,7 @@ impl RenderBackend { preserve_frame_state: false, }; let txn = TransactionMsg::scene_message(scene_msg); - r.write_msg(*frame_counter, &ApiMsg::UpdateDocument(*id, txn)); + r.write_msg(*frame_counter, &ApiMsg::UpdateDocuments(vec![*id], vec![txn])); r.write_payload(*frame_counter, &Payload::construct_data( epoch, pipeline_id, @@ -1177,10 +1179,10 @@ impl RenderBackend { info!("Recycling stats: {:?}", self.recycler); return false; } - ApiMsg::UpdateDocument(document_id, transaction_msg) => { - self.prepare_transaction( - document_id, - transaction_msg, + ApiMsg::UpdateDocuments(document_ids, transaction_msgs) => { + self.prepare_transactions( + document_ids, + transaction_msgs, frame_counter, profile_counters, ); @@ -1190,99 +1192,112 @@ impl RenderBackend { true } - fn prepare_transaction( + fn prepare_transactions( &mut self, - document_id: DocumentId, - mut transaction_msg: TransactionMsg, + document_ids: Vec, + mut transaction_msgs: Vec, frame_counter: &mut u32, profile_counters: &mut BackendProfileCounters, ) { - let mut txn = Box::new(Transaction { - document_id, - display_list_updates: Vec::new(), - removed_pipelines: Vec::new(), - epoch_updates: Vec::new(), - request_scene_build: None, - blob_rasterizer: None, - blob_requests: Vec::new(), - resource_updates: transaction_msg.resource_updates, - frame_ops: transaction_msg.frame_ops, - rasterized_blobs: Vec::new(), - notifications: transaction_msg.notifications, - set_root_pipeline: None, - render_frame: transaction_msg.generate_frame, - invalidate_rendered_frame: transaction_msg.invalidate_rendered_frame, + let mut use_scene_builder = transaction_msgs.iter() + .any(|transaction_msg| transaction_msg.use_scene_builder_thread); + let use_high_priority = transaction_msgs.iter() + .any(|transaction_msg| !transaction_msg.low_priority); + + let mut txns : Vec> = document_ids.iter().zip(transaction_msgs.drain(..)) + .map(|(&document_id, mut transaction_msg)| { + let mut txn = Box::new(Transaction { + document_id, + display_list_updates: Vec::new(), + removed_pipelines: Vec::new(), + epoch_updates: Vec::new(), + request_scene_build: None, + blob_rasterizer: None, + blob_requests: Vec::new(), + resource_updates: transaction_msg.resource_updates, + frame_ops: transaction_msg.frame_ops, + rasterized_blobs: Vec::new(), + notifications: transaction_msg.notifications, + set_root_pipeline: None, + render_frame: transaction_msg.generate_frame, + invalidate_rendered_frame: transaction_msg.invalidate_rendered_frame, + }); + + self.resource_cache.pre_scene_building_update( + &mut txn.resource_updates, + &mut profile_counters.resources, + ); + + // If we've been above the threshold for reclaiming GPU cache memory for + // long enough, drop it and rebuild it. This needs to be done before any + // updates for this frame are made. + if self.gpu_cache.should_reclaim_memory() { + self.gpu_cache.clear(); + } + + for scene_msg in transaction_msg.scene_ops.drain(..) { + let _timer = profile_counters.total_time.timer(); + self.process_scene_msg( + document_id, + scene_msg, + *frame_counter, + &mut txn, + &mut profile_counters.ipc, + ) + } + + let blobs_to_rasterize = get_blob_image_updates(&txn.resource_updates); + if !blobs_to_rasterize.is_empty() { + let (blob_rasterizer, blob_requests) = self.resource_cache + .create_blob_scene_builder_requests(&blobs_to_rasterize); + + txn.blob_requests = blob_requests; + txn.blob_rasterizer = blob_rasterizer; + } + txn + }).collect(); + + use_scene_builder = use_scene_builder || txns.iter().any(|txn| { + !txn.can_skip_scene_builder() || txn.blob_rasterizer.is_some() }); - self.resource_cache.pre_scene_building_update( - &mut txn.resource_updates, - &mut profile_counters.resources, - ); - - // If we've been above the threshold for reclaiming GPU cache memory for - // long enough, drop it and rebuild it. This needs to be done before any - // updates for this frame are made. - if self.gpu_cache.should_reclaim_memory() { - self.gpu_cache.clear(); - } - - for scene_msg in transaction_msg.scene_ops.drain(..) { - let _timer = profile_counters.total_time.timer(); - self.process_scene_msg( - document_id, - scene_msg, - *frame_counter, - &mut txn, - &mut profile_counters.ipc, - ) - } - - let blobs_to_rasterize = get_blob_image_updates(&txn.resource_updates); - if !blobs_to_rasterize.is_empty() { - let (blob_rasterizer, blob_requests) = self.resource_cache - .create_blob_scene_builder_requests(&blobs_to_rasterize); - - txn.blob_requests = blob_requests; - txn.blob_rasterizer = blob_rasterizer; - } - - if !transaction_msg.use_scene_builder_thread && - txn.can_skip_scene_builder() && - txn.blob_rasterizer.is_none() { - - self.update_document( - txn.document_id, - txn.resource_updates.take(), - None, - txn.frame_ops.take(), - txn.notifications.take(), - txn.render_frame, - txn.invalidate_rendered_frame, - frame_counter, - profile_counters, - false - ); + if use_scene_builder { + for txn in txns.iter_mut() { + let doc = self.documents.get_mut(&txn.document_id).unwrap(); + if txn.should_build_scene() { + txn.request_scene_build = Some(SceneRequest { + view: doc.view.clone(), + font_instances: self.resource_cache.get_font_instances(), + output_pipelines: doc.output_pipelines.clone(), + }); + } + } + } else { + for mut txn in txns { + self.update_document( + txn.document_id, + txn.resource_updates.take(), + None, + txn.frame_ops.take(), + txn.notifications.take(), + txn.render_frame, + txn.invalidate_rendered_frame, + frame_counter, + profile_counters, + false + ); + } return; } - let doc = self.documents.get_mut(&document_id).unwrap(); - - if txn.should_build_scene() { - txn.request_scene_build = Some(SceneRequest { - view: doc.view.clone(), - font_instances: self.resource_cache.get_font_instances(), - output_pipelines: doc.output_pipelines.clone(), - }); - } - - let tx = if transaction_msg.low_priority { - &self.low_priority_scene_tx - } else { + let tx = if use_high_priority { &self.scene_tx + } else { + &self.low_priority_scene_tx }; - tx.send(SceneBuilderRequest::Transaction(txn)).unwrap(); + tx.send(SceneBuilderRequest::Transactions(txns)).unwrap(); } fn update_document( diff --git a/gfx/wr/webrender/src/renderer.rs b/gfx/wr/webrender/src/renderer.rs index c5b3cad1b1cb..1d80381897b3 100644 --- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -5496,11 +5496,11 @@ pub trait SceneBuilderHooks { /// This is called after each scene swap occurs. The PipelineInfo contains /// the updated epochs and pipelines removed in the new scene compared to /// the old scene. - fn post_scene_swap(&self, document_id: DocumentId, info: PipelineInfo, sceneswap_time: u64); + fn post_scene_swap(&self, document_id: &Vec, info: PipelineInfo, sceneswap_time: u64); /// This is called after a resource update operation on the scene builder /// thread, in the case where resource updates were applied without a scene /// build. - fn post_resource_update(&self, document_id: DocumentId); + fn post_resource_update(&self, document_ids: &Vec); /// This is called after a scene build completes without any changes being /// made. We guarantee that each pre_scene_build call will be matched with /// exactly one of post_scene_swap, post_resource_update or diff --git a/gfx/wr/webrender/src/scene_builder.rs b/gfx/wr/webrender/src/scene_builder.rs index 5624fe40d804..29ae7cc7d45c 100644 --- a/gfx/wr/webrender/src/scene_builder.rs +++ b/gfx/wr/webrender/src/scene_builder.rs @@ -28,6 +28,7 @@ use resource_cache::{AsyncBlobImageInfo, FontInstanceMap}; use render_backend::DocumentView; use renderer::{PipelineInfo, SceneBuilderHooks}; use scene::Scene; +use std::iter; use std::sync::mpsc::{channel, Receiver, Sender}; use std::mem::replace; use time::precise_time_ns; @@ -136,7 +137,7 @@ pub struct BuiltScene { // Message from render backend to scene builder. pub enum SceneBuilderRequest { - Transaction(Box), + Transactions(Vec>), ExternalEvent(ExternalEvent), DeleteDocument(DocumentId), WakeUp, @@ -155,7 +156,7 @@ pub enum SceneBuilderRequest { // Message from scene builder to render backend. pub enum SceneBuilderResult { - Transaction(Box, Option>), + Transactions(Vec>, Option>), ExternalEvent(ExternalEvent), FlushComplete(MsgSender<()>), ClearNamespace(IdNamespace), @@ -321,9 +322,11 @@ impl SceneBuilder { Ok(SceneBuilderRequest::Flush(tx)) => { self.send(SceneBuilderResult::FlushComplete(tx)); } - Ok(SceneBuilderRequest::Transaction(mut txn)) => { - let built_txn = self.process_transaction(&mut txn); - self.forward_built_transaction(built_txn); + Ok(SceneBuilderRequest::Transactions(mut txns)) => { + let built_txns : Vec> = txns.iter_mut() + .map(|txn| self.process_transaction(txn)) + .collect(); + self.forward_built_transactions(built_txns); } Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { self.documents.remove(&document_id); @@ -429,7 +432,7 @@ impl SceneBuilder { }, ); - let txn = Box::new(BuiltTransaction { + let txns = vec![Box::new(BuiltTransaction { document_id: item.document_id, render_frame: item.build_frame, invalidate_rendered_frame: false, @@ -443,9 +446,9 @@ impl SceneBuilder { scene_build_start_time, scene_build_end_time: precise_time_ns(), interner_updates, - }); + })]; - self.forward_built_transaction(txn); + self.forward_built_transactions(txns); } } @@ -550,36 +553,49 @@ impl SceneBuilder { }) } - /// Send the result of process_transaction back to the render backend. - fn forward_built_transaction(&mut self, txn: Box) { - // We only need the pipeline info and the result channel if we - // have a hook callback *and* if this transaction actually built - // a new scene that is going to get swapped in. In other cases - // pipeline_info can be None and we can avoid some overhead from - // invoking the hooks and blocking on the channel. - let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &txn.built_scene) { - (&Some(ref hooks), &Some(ref built)) => { - let info = PipelineInfo { - epochs: built.scene.pipeline_epochs.iter() - .map(|(&pipeline_id, &epoch)| ((pipeline_id, txn.document_id), epoch)) - .collect(), - removed_pipelines: txn.removed_pipelines.clone(), - }; - let (tx, rx) = channel(); + /// Send the results of process_transaction back to the render backend. + fn forward_built_transactions(&mut self, txns: Vec>) { + let (pipeline_info, result_tx, result_rx) = match &self.hooks { + &Some(ref hooks) => { + if txns.iter().any(|txn| txn.built_scene.is_some()) { + let info = PipelineInfo { + epochs: txns.iter() + .filter(|txn| txn.built_scene.is_some()) + .map(|txn| { + txn.built_scene.as_ref().unwrap() + .scene.pipeline_epochs.iter() + .zip(iter::repeat(txn.document_id)) + .map(|((&pipeline_id, &epoch), document_id)| ((pipeline_id, document_id), epoch)) + }).flatten().collect(), + removed_pipelines: txns.iter() + .map(|txn| txn.removed_pipelines.clone()) + .flatten().collect(), + }; - hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time); + let (tx, rx) = channel(); + let txn = txns.iter().find(|txn| txn.built_scene.is_some()).unwrap(); + hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time); - (Some(info), Some(tx), Some(rx)) + (Some(info), Some(tx), Some(rx)) + } else { + (None, None, None) + } } - _ => (None, None, None), + _ => (None, None, None) }; - let document_id = txn.document_id; let scene_swap_start_time = precise_time_ns(); - let has_resources_updates = !txn.resource_updates.is_empty(); - let invalidate_rendered_frame = txn.invalidate_rendered_frame; + let document_ids = txns.iter().map(|txn| txn.document_id).collect(); + let have_resources_updates : Vec = if pipeline_info.is_none() { + txns.iter() + .filter(|txn| !txn.resource_updates.is_empty() || txn.invalidate_rendered_frame) + .map(|txn| txn.document_id.clone()) + .collect() + } else { + Vec::new() + }; - self.tx.send(SceneBuilderResult::Transaction(txn, result_tx)).unwrap(); + self.tx.send(SceneBuilderResult::Transactions(txns, result_tx)).unwrap(); let _ = self.api_tx.send(ApiMsg::WakeUp); @@ -587,7 +603,7 @@ impl SceneBuilder { // Block until the swap is done, then invoke the hook. let swap_result = result_rx.unwrap().recv(); let scene_swap_time = precise_time_ns() - scene_swap_start_time; - self.hooks.as_ref().unwrap().post_scene_swap(document_id, + self.hooks.as_ref().unwrap().post_scene_swap(&document_ids, pipeline_info, scene_swap_time); // Once the hook is done, allow the RB thread to resume match swap_result { @@ -596,9 +612,9 @@ impl SceneBuilder { }, _ => (), }; - } else if has_resources_updates || invalidate_rendered_frame { + } else if !have_resources_updates.is_empty() { if let &Some(ref hooks) = &self.hooks { - hooks.post_resource_update(document_id); + hooks.post_resource_update(&have_resources_updates); } } else { if let &Some(ref hooks) = &self.hooks { @@ -634,9 +650,11 @@ impl LowPrioritySceneBuilder { pub fn run(&mut self) { loop { match self.rx.recv() { - Ok(SceneBuilderRequest::Transaction(txn)) => { - let txn = self.process_transaction(txn); - self.tx.send(SceneBuilderRequest::Transaction(txn)).unwrap(); + Ok(SceneBuilderRequest::Transactions(mut txns)) => { + let txns : Vec> = txns.drain(..) + .map(|txn| self.process_transaction(txn)) + .collect(); + self.tx.send(SceneBuilderRequest::Transactions(txns)).unwrap(); } Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { self.tx.send(SceneBuilderRequest::DeleteDocument(document_id)).unwrap(); diff --git a/gfx/wr/webrender_api/src/api.rs b/gfx/wr/webrender_api/src/api.rs index 2e617030c661..5ccfbbf0b929 100644 --- a/gfx/wr/webrender_api/src/api.rs +++ b/gfx/wr/webrender_api/src/api.rs @@ -439,6 +439,11 @@ impl Transaction { } } +pub struct DocumentTransaction { + pub document_id: DocumentId, + pub transaction: Transaction, +} + /// Represents a transaction in the format sent through the channel. #[derive(Clone, Deserialize, Serialize)] pub struct TransactionMsg { @@ -726,7 +731,7 @@ pub enum ApiMsg { /// Adds a new document with given initial size. AddDocument(DocumentId, FramebufferIntSize, DocumentLayer), /// A message targeted at a particular document. - UpdateDocument(DocumentId, TransactionMsg), + UpdateDocuments(Vec, Vec), /// Deletes an existing document. DeleteDocument(DocumentId), /// An opaque handle that must be passed to the render notifier. It is used by Gecko @@ -758,7 +763,7 @@ impl fmt::Debug for ApiMsg { ApiMsg::CloneApi(..) => "ApiMsg::CloneApi", ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient", ApiMsg::AddDocument(..) => "ApiMsg::AddDocument", - ApiMsg::UpdateDocument(..) => "ApiMsg::UpdateDocument", + ApiMsg::UpdateDocuments(..) => "ApiMsg::UpdateDocuments", ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument", ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent", ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace", @@ -1216,7 +1221,7 @@ impl RenderApi { // `RenderApi` instances for layout and compositor. //assert_eq!(document_id.0, self.namespace_id); self.api_sender - .send(ApiMsg::UpdateDocument(document_id, TransactionMsg::scene_message(msg))) + .send(ApiMsg::UpdateDocuments(vec![document_id], vec![TransactionMsg::scene_message(msg)])) .unwrap() } @@ -1226,7 +1231,7 @@ impl RenderApi { // `RenderApi` instances for layout and compositor. //assert_eq!(document_id.0, self.namespace_id); self.api_sender - .send(ApiMsg::UpdateDocument(document_id, TransactionMsg::frame_message(msg))) + .send(ApiMsg::UpdateDocuments(vec![document_id], vec![TransactionMsg::frame_message(msg)])) .unwrap() } @@ -1235,7 +1240,24 @@ impl RenderApi { for payload in payloads { self.payload_sender.send_payload(payload).unwrap(); } - self.api_sender.send(ApiMsg::UpdateDocument(document_id, msg)).unwrap(); + self.api_sender.send(ApiMsg::UpdateDocuments(vec![document_id], vec![msg])).unwrap(); + } + + pub fn send_transactions(&self, document_ids: Vec, mut transactions: Vec) { + debug_assert!(document_ids.len() == transactions.len()); + let length = document_ids.len(); + let (msgs, mut document_payloads) = transactions.drain(..) + .fold((Vec::with_capacity(length), Vec::with_capacity(length)), + |(mut msgs, mut document_payloads), transaction| { + let (msg, payloads) = transaction.finalize(); + msgs.push(msg); + document_payloads.push(payloads); + (msgs, document_payloads) + }); + for payload in document_payloads.drain(..).flatten() { + self.payload_sender.send_payload(payload).unwrap(); + } + self.api_sender.send(ApiMsg::UpdateDocuments(document_ids.clone(), msgs)).unwrap(); } /// Does a hit test on display items in the specified document, at the given diff --git a/gfx/wr/wrench/src/binary_frame_reader.rs b/gfx/wr/wrench/src/binary_frame_reader.rs index d5007cf91ee8..b8479ded05b8 100644 --- a/gfx/wr/wrench/src/binary_frame_reader.rs +++ b/gfx/wr/wrench/src/binary_frame_reader.rs @@ -140,21 +140,24 @@ impl WrenchThing for BinaryFrameReader { // (b) SetDisplayList // (c) GenerateFrame that occurs *after* (a) and (b) match msg { - ApiMsg::UpdateDocument(_, ref txn) => { - if txn.generate_frame { - found_frame_marker = true; - } - for doc_msg in &txn.scene_ops { - match *doc_msg { - SceneMsg::SetDisplayList { .. } => { - found_frame_marker = false; - found_display_list = true; + ApiMsg::UpdateDocuments(_, ref txns) => { + for txn in txns { + if txn.generate_frame { + // TODO: is this appropriate, or do we need a ternary value / something else? + found_frame_marker = true; + } + for doc_msg in &txn.scene_ops { + match *doc_msg { + SceneMsg::SetDisplayList { .. } => { + found_frame_marker = false; + found_display_list = true; + } + SceneMsg::SetRootPipeline(..) => { + found_frame_marker = false; + found_pipeline = true; + } + _ => {} } - SceneMsg::SetRootPipeline(..) => { - found_frame_marker = false; - found_pipeline = true; - } - _ => {} } } } diff --git a/gfx/wr/wrench/src/json_frame_writer.rs b/gfx/wr/wrench/src/json_frame_writer.rs index 693d0dc93c95..7f4741102a32 100644 --- a/gfx/wr/wrench/src/json_frame_writer.rs +++ b/gfx/wr/wrench/src/json_frame_writer.rs @@ -271,6 +271,31 @@ impl JsonFrameWriter { self.images.insert(key, data); Some(path) } + + fn update_document(&mut self, txn: &TransactionMsg) { + self.update_resources(&txn.resource_updates); + for doc_msg in &txn.scene_ops { + match *doc_msg { + SceneMsg::SetDisplayList { + ref epoch, + ref pipeline_id, + ref background, + ref viewport_size, + ref list_descriptor, + .. + } => { + self.begin_write_display_list( + epoch, + pipeline_id, + background, + viewport_size, + list_descriptor, + ); + } + _ => {} + } + } + } } impl fmt::Debug for JsonFrameWriter { @@ -283,29 +308,9 @@ impl webrender::ApiRecordingReceiver for JsonFrameWriter { fn write_msg(&mut self, _: u32, msg: &ApiMsg) { match *msg { ApiMsg::UpdateResources(ref updates) => self.update_resources(updates), - - ApiMsg::UpdateDocument(_, ref txn) => { - self.update_resources(&txn.resource_updates); - for doc_msg in &txn.scene_ops { - match *doc_msg { - SceneMsg::SetDisplayList { - ref epoch, - ref pipeline_id, - ref background, - ref viewport_size, - ref list_descriptor, - .. - } => { - self.begin_write_display_list( - epoch, - pipeline_id, - background, - viewport_size, - list_descriptor, - ); - } - _ => {} - } + ApiMsg::UpdateDocuments(_, ref txns) => { + for txn in txns { + self.update_document(txn) } } ApiMsg::CloneApi(..) => {} diff --git a/gfx/wr/wrench/src/ron_frame_writer.rs b/gfx/wr/wrench/src/ron_frame_writer.rs index ecab36d8a10b..f8c1b59abb26 100644 --- a/gfx/wr/wrench/src/ron_frame_writer.rs +++ b/gfx/wr/wrench/src/ron_frame_writer.rs @@ -149,6 +149,31 @@ impl RonFrameWriter { } } } + + fn update_document(&mut self, txn: &TransactionMsg) { + self.update_resources(&txn.resource_updates); + for doc_msg in &txn.scene_ops { + match *doc_msg { + SceneMsg::SetDisplayList { + ref epoch, + ref pipeline_id, + ref background, + ref viewport_size, + ref list_descriptor, + .. + } => { + self.begin_write_display_list( + epoch, + pipeline_id, + background, + viewport_size, + list_descriptor, + ); + } + _ => {} + } + } + } } impl fmt::Debug for RonFrameWriter { @@ -161,28 +186,9 @@ impl webrender::ApiRecordingReceiver for RonFrameWriter { fn write_msg(&mut self, _: u32, msg: &ApiMsg) { match *msg { ApiMsg::UpdateResources(ref updates) => self.update_resources(updates), - ApiMsg::UpdateDocument(_, ref txn) => { - self.update_resources(&txn.resource_updates); - for doc_msg in &txn.scene_ops { - match *doc_msg { - SceneMsg::SetDisplayList { - ref epoch, - ref pipeline_id, - ref background, - ref viewport_size, - ref list_descriptor, - .. - } => { - self.begin_write_display_list( - epoch, - pipeline_id, - background, - viewport_size, - list_descriptor, - ); - } - _ => {} - } + ApiMsg::UpdateDocuments(_, ref txns) => { + for txn in txns { + self.update_document(txn) } } ApiMsg::CloneApi(..) => {} diff --git a/gfx/wr/wrench/src/yaml_frame_writer.rs b/gfx/wr/wrench/src/yaml_frame_writer.rs index 7f4f3d3c0435..4ec9ed1379ab 100644 --- a/gfx/wr/wrench/src/yaml_frame_writer.rs +++ b/gfx/wr/wrench/src/yaml_frame_writer.rs @@ -443,6 +443,46 @@ impl YamlFrameWriterReceiver { scene: Scene::new(), } } + + fn update_document(&mut self, txn: &TransactionMsg) { + self.frame_writer.update_resources(&txn.resource_updates); + for doc_msg in &txn.scene_ops { + match *doc_msg { + SceneMsg::SetDisplayList { + ref epoch, + ref pipeline_id, + ref background, + ref viewport_size, + ref list_descriptor, + .. + } => { + self.frame_writer.begin_write_display_list( + &mut self.scene, + epoch, + pipeline_id, + background, + viewport_size, + list_descriptor, + ); + } + SceneMsg::SetRootPipeline(ref pipeline_id) => { + self.scene.set_root_pipeline_id(pipeline_id.clone()); + } + SceneMsg::RemovePipeline(ref pipeline_id) => { + self.scene.remove_pipeline(pipeline_id); + } + _ => {} + } + } + for doc_msg in &txn.frame_ops { + match *doc_msg { + FrameMsg::UpdateDynamicProperties(ref properties) => { + self.scene.properties.set_properties(properties); + } + _ => {} + } + } + } } impl fmt::Debug for YamlFrameWriterReceiver { @@ -1234,43 +1274,9 @@ impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver { ApiMsg::UpdateResources(ref updates) => { self.frame_writer.update_resources(updates); } - ApiMsg::UpdateDocument(_, ref txn) => { - self.frame_writer.update_resources(&txn.resource_updates); - for doc_msg in &txn.scene_ops { - match *doc_msg { - SceneMsg::SetDisplayList { - ref epoch, - ref pipeline_id, - ref background, - ref viewport_size, - ref list_descriptor, - .. - } => { - self.frame_writer.begin_write_display_list( - &mut self.scene, - epoch, - pipeline_id, - background, - viewport_size, - list_descriptor, - ); - } - SceneMsg::SetRootPipeline(ref pipeline_id) => { - self.scene.set_root_pipeline_id(pipeline_id.clone()); - } - SceneMsg::RemovePipeline(ref pipeline_id) => { - self.scene.remove_pipeline(pipeline_id); - } - _ => {} - } - } - for doc_msg in &txn.frame_ops { - match *doc_msg { - FrameMsg::UpdateDynamicProperties(ref properties) => { - self.scene.properties.set_properties(properties); - } - _ => {} - } + ApiMsg::UpdateDocuments(_, ref txns) => { + for txn in txns { + self.update_document(txn); } } _ => {}