Bug 1538710 - Add WR API endpoint for sending multiple transactions r=nical

We discussed this a bit in Orlando. Essentially, we want to run cleanup
operations in texture_cache before all documents' frames, and then be
able to ensure that every document generates a frame, because otherwise
we will run into problems with evicted cache items used by non-updating-
but-still-rendering documents. Accordingly, we need an endpoint to
lump all of the transactions that generate frames together. This adds
that and builds out all of the plumbing necessary.

Differential Revision: https://phabricator.services.mozilla.com/D25132

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Doug Thayer 2019-04-15 18:56:04 +00:00
Родитель e97f2269a5
Коммит 3efeb79ae5
18 изменённых файлов: 503 добавлений и 323 удалений

Просмотреть файл

@ -19,7 +19,8 @@ class CompositorController {
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
virtual void ScheduleRenderOnCompositorThread( virtual void ScheduleRenderOnCompositorThread(
const Maybe<wr::RenderRoot>& aRenderRootid = Nothing()) = 0; const nsTArray<wr::RenderRoot>& aRenderRoots =
nsTArray<wr::RenderRoot>()) = 0;
virtual void ScheduleHideAllPluginWindows() = 0; virtual void ScheduleHideAllPluginWindows() = 0;
virtual void ScheduleShowAllPluginWindows() = 0; virtual void ScheduleShowAllPluginWindows() = 0;

Просмотреть файл

@ -650,11 +650,11 @@ void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) {
} }
void CompositorBridgeParent::ScheduleRenderOnCompositorThread( void CompositorBridgeParent::ScheduleRenderOnCompositorThread(
const Maybe<wr::RenderRoot>& aRenderRoot) { const nsTArray<wr::RenderRoot>& aRenderRoots) {
MOZ_ASSERT(CompositorLoop()); MOZ_ASSERT(CompositorLoop());
CompositorLoop()->PostTask(NewRunnableMethod<Maybe<wr::RenderRoot>>( CompositorLoop()->PostTask(NewRunnableMethod<nsTArray<wr::RenderRoot>>(
"layers::CompositorBridgeParent::ScheduleComposition", this, "layers::CompositorBridgeParent::ScheduleComposition", this,
&CompositorBridgeParent::ScheduleComposition, aRenderRoot)); &CompositorBridgeParent::ScheduleComposition, aRenderRoots));
} }
void CompositorBridgeParent::InvalidateOnCompositorThread() { void CompositorBridgeParent::InvalidateOnCompositorThread() {
@ -864,18 +864,14 @@ void CompositorBridgeParent::NotifyShadowTreeTransaction(
} }
void CompositorBridgeParent::ScheduleComposition( void CompositorBridgeParent::ScheduleComposition(
const Maybe<wr::RenderRoot>& aRenderRoot) { const nsTArray<wr::RenderRoot>& aRenderRoots) {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
if (mPaused) { if (mPaused) {
return; return;
} }
if (mWrBridge) { if (mWrBridge) {
if (aRenderRoot.isSome()) { mWrBridge->ScheduleGenerateFrame(aRenderRoots);
mWrBridge->ScheduleGenerateFrame(aRenderRoot);
} else {
mWrBridge->ScheduleGenerateFrameAllRenderRoots();
}
} else { } else {
mCompositorScheduler->ScheduleComposition(); mCompositorScheduler->ScheduleComposition();
} }
@ -2064,14 +2060,15 @@ void CompositorBridgeParent::DidComposite(const VsyncId& aId,
} }
void CompositorBridgeParent::NotifyDidSceneBuild( void CompositorBridgeParent::NotifyDidSceneBuild(
wr::RenderRoot aRenderRoot, RefPtr<wr::WebRenderPipelineInfo> aInfo) { const nsTArray<wr::RenderRoot>& aRenderRoots,
RefPtr<wr::WebRenderPipelineInfo> aInfo) {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
if (mPaused) { if (mPaused) {
return; return;
} }
if (mWrBridge) { if (mWrBridge) {
mWrBridge->NotifyDidSceneBuild(aRenderRoot, aInfo); mWrBridge->NotifyDidSceneBuild(aRenderRoots, aInfo);
} else { } else {
mCompositorScheduler->ScheduleComposition(); mCompositorScheduler->ScheduleComposition();
} }

Просмотреть файл

@ -399,7 +399,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
TimeStamp& aCompositeStart, TimeStamp& aCompositeStart,
TimeStamp& aRenderStart, TimeStamp& aCompositeEnd, TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
wr::RendererStats* aStats = nullptr); wr::RendererStats* aStats = nullptr);
void NotifyDidSceneBuild(wr::RenderRoot aRenderRoot, void NotifyDidSceneBuild(const nsTArray<wr::RenderRoot>& aRenderRoots,
RefPtr<wr::WebRenderPipelineInfo> aInfo); RefPtr<wr::WebRenderPipelineInfo> aInfo);
RefPtr<AsyncImagePipelineManager> GetAsyncImagePipelineManager() const; RefPtr<AsyncImagePipelineManager> GetAsyncImagePipelineManager() const;
@ -427,7 +427,8 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
// Can be called from any thread // Can be called from any thread
void ScheduleRenderOnCompositorThread( void ScheduleRenderOnCompositorThread(
const Maybe<wr::RenderRoot>& aRenderRoot = Nothing()) override; const nsTArray<wr::RenderRoot>& aRenderRoots =
nsTArray<wr::RenderRoot>()) override;
void SchedulePauseOnCompositorThread(); void SchedulePauseOnCompositorThread();
void InvalidateOnCompositorThread(); void InvalidateOnCompositorThread();
/** /**
@ -437,8 +438,9 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
bool ScheduleResumeOnCompositorThread(); bool ScheduleResumeOnCompositorThread();
bool ScheduleResumeOnCompositorThread(int x, int y, int width, int height); bool ScheduleResumeOnCompositorThread(int x, int y, int width, int height);
void ScheduleComposition( void ScheduleComposition(const nsTArray<wr::RenderRoot>& aRenderRoots =
const Maybe<wr::RenderRoot>& aRenderRoot = Nothing()); nsTArray<wr::RenderRoot>());
void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint, void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint,
const FocusTarget& aFocusTarget, const FocusTarget& aFocusTarget,
bool aScheduleComposite, bool aScheduleComposite,

Просмотреть файл

@ -2002,6 +2002,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId,
#endif #endif
MOZ_ASSERT(framesGenerated > 0); MOZ_ASSERT(framesGenerated > 0);
wr::RenderRootArray<wr::TransactionBuilder*> generateFrameTxns;
for (auto& api : mApis) { for (auto& api : mApis) {
if (!api) { if (!api) {
continue; continue;
@ -2009,9 +2010,11 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId,
auto renderRoot = api->GetRenderRoot(); auto renderRoot = api->GetRenderRoot();
if (generateFrame[renderRoot]) { if (generateFrame[renderRoot]) {
fastTxns[renderRoot]->GenerateFrame(); fastTxns[renderRoot]->GenerateFrame();
api->SendTransaction(*fastTxns[renderRoot]); generateFrameTxns[renderRoot] = fastTxns[renderRoot].ptr();
} }
} }
wr::WebRenderAPI::SendTransactions(mApis, generateFrameTxns);
mMostRecentComposite = TimeStamp::Now(); mMostRecentComposite = TimeStamp::Now();
} }
@ -2059,13 +2062,16 @@ void WebRenderBridgeParent::NotifySceneBuiltForEpoch(
} }
void WebRenderBridgeParent::NotifyDidSceneBuild( void WebRenderBridgeParent::NotifyDidSceneBuild(
wr::RenderRoot aRenderRoot, RefPtr<wr::WebRenderPipelineInfo> aInfo) { const nsTArray<wr::RenderRoot>& aRenderRoots,
RefPtr<wr::WebRenderPipelineInfo> aInfo) {
MOZ_ASSERT(IsRootWebRenderBridgeParent()); MOZ_ASSERT(IsRootWebRenderBridgeParent());
if (!mCompositorScheduler) { if (!mCompositorScheduler) {
return; return;
} }
mAsyncImageManager->SetWillGenerateFrame(aRenderRoot); for (auto renderRoot : aRenderRoots) {
mAsyncImageManager->SetWillGenerateFrame(renderRoot);
}
// If the scheduler has a composite more recent than our last composite (which // 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 // 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<wr::RenderRoot>& aRenderRoots) {
if (mCompositorScheduler) {
if (aRenderRoots.IsEmpty()) {
mAsyncImageManager->SetWillGenerateFrameAllRenderRoots();
}
for (auto renderRoot : aRenderRoots) {
mAsyncImageManager->SetWillGenerateFrame(renderRoot);
}
mCompositorScheduler->ScheduleComposition();
}
}
void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) { void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) {
if (mDestroyed) { if (mDestroyed) {
return; return;

Просмотреть файл

@ -231,6 +231,7 @@ class WebRenderBridgeParent final
* want to trigger AsyncImagePipelines update checks. * want to trigger AsyncImagePipelines update checks.
*/ */
void ScheduleGenerateFrame(const Maybe<wr::RenderRoot>& aRenderRoot); void ScheduleGenerateFrame(const Maybe<wr::RenderRoot>& aRenderRoot);
void ScheduleGenerateFrame(const nsTArray<wr::RenderRoot>& aRenderRoots);
void ScheduleGenerateFrameAllRenderRoots(); void ScheduleGenerateFrameAllRenderRoots();
/** /**
@ -241,7 +242,7 @@ class WebRenderBridgeParent final
*/ */
void ScheduleForcedGenerateFrame(); void ScheduleForcedGenerateFrame();
void NotifyDidSceneBuild(wr::RenderRoot aRenderRoot, void NotifyDidSceneBuild(const nsTArray<wr::RenderRoot>& aRenderRoots,
RefPtr<wr::WebRenderPipelineInfo> aInfo); RefPtr<wr::WebRenderPipelineInfo> aInfo);
wr::Epoch UpdateWebRender( wr::Epoch UpdateWebRender(

Просмотреть файл

@ -941,30 +941,41 @@ void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId,
} }
void wr_schedule_render(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<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers:: RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId); CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
if (cbp) { if (cbp) {
cbp->ScheduleRenderOnCompositorThread( InfallibleTArray<wr::RenderRoot> renderRoots;
Some(wr::RenderRootFromId(aDocumentId))); renderRoots.SetLength(aDocumentIdsCount);
for (size_t i = 0; i < aDocumentIdsCount; ++i) {
renderRoots[i] = wr::RenderRootFromId(aDocumentIds[i]);
}
cbp->ScheduleRenderOnCompositorThread(renderRoots);
} }
} }
static void NotifyDidSceneBuild(RefPtr<layers::CompositorBridgeParent> aBridge, static void NotifyDidSceneBuild(RefPtr<layers::CompositorBridgeParent> aBridge,
wr::DocumentId aRenderRootId, const nsTArray<wr::RenderRoot>& aRenderRoots,
RefPtr<wr::WebRenderPipelineInfo> aInfo) { RefPtr<wr::WebRenderPipelineInfo> aInfo) {
aBridge->NotifyDidSceneBuild(wr::RenderRootFromId(aRenderRootId), aInfo); aBridge->NotifyDidSceneBuild(aRenderRoots, aInfo);
} }
void wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId, 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) { mozilla::wr::WrPipelineInfo aInfo) {
RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers:: RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId); CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
RefPtr<wr::WebRenderPipelineInfo> info = new wr::WebRenderPipelineInfo(aInfo); RefPtr<wr::WebRenderPipelineInfo> info = new wr::WebRenderPipelineInfo(aInfo);
if (cbp) { if (cbp) {
InfallibleTArray<wr::RenderRoot> renderRoots;
renderRoots.SetLength(aDocumentIdsCount);
for (size_t i = 0; i < aDocumentIdsCount; ++i) {
renderRoots[i] = wr::RenderRootFromId(aDocumentIds[i]);
}
layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction( layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction(
"NotifyDidSceneBuild", &NotifyDidSceneBuild, cbp, aDocumentId, info)); "NotifyDidSceneBuild", &NotifyDidSceneBuild, cbp, renderRoots, info));
} }
} }

Просмотреть файл

@ -366,6 +366,39 @@ void WebRenderAPI::SendTransaction(TransactionBuilder& aTxn) {
wr_api_send_transaction(mDocHandle, aTxn.Raw(), aTxn.UseSceneBuilderThread()); wr_api_send_transaction(mDocHandle, aTxn.Raw(), aTxn.UseSceneBuilderThread());
} }
/* static */
void WebRenderAPI::SendTransactions(
const RenderRootArray<RefPtr<WebRenderAPI>>& aApis,
RenderRootArray<TransactionBuilder*>& aTxns) {
if (!aApis[RenderRoot::Default]) {
return;
}
aApis[RenderRoot::Default]->UpdateDebugFlags(gfx::gfxVars::WebRenderDebugFlags());
AutoTArray<DocumentHandle*, kRenderRootCount> documentHandles;
AutoTArray<Transaction*, kRenderRootCount> txns;
Maybe<bool> 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, bool WebRenderAPI::HitTest(const wr::WorldPoint& aPoint,
wr::WrPipelineId& aOutPipelineId, wr::WrPipelineId& aOutPipelineId,
layers::ScrollableLayerGuid::ViewID& aOutScrollId, layers::ScrollableLayerGuid::ViewID& aOutScrollId,

Просмотреть файл

@ -209,6 +209,9 @@ class WebRenderAPI final {
RefPtr<widget::CompositorWidget>&& aWidget, RefPtr<widget::CompositorWidget>&& aWidget,
const wr::WrWindowId& aWindowId, LayoutDeviceIntSize aSize); const wr::WrWindowId& aWindowId, LayoutDeviceIntSize aSize);
static void SendTransactions(const RenderRootArray<RefPtr<WebRenderAPI>>& aApis,
RenderRootArray<TransactionBuilder*>& aTxns);
already_AddRefed<WebRenderAPI> CreateDocument(LayoutDeviceIntSize aSize, already_AddRefed<WebRenderAPI> CreateDocument(LayoutDeviceIntSize aSize,
int8_t aLayerIndex, int8_t aLayerIndex,
wr::RenderRoot aRenderRoot); wr::RenderRoot aRenderRoot);

Просмотреть файл

@ -581,8 +581,13 @@ extern "C" {
fn wr_notifier_nop_frame_done(window_id: WrWindowId); fn wr_notifier_nop_frame_done(window_id: WrWindowId);
fn wr_notifier_external_event(window_id: WrWindowId, fn wr_notifier_external_event(window_id: WrWindowId,
raw_event: usize); raw_event: usize);
fn wr_schedule_render(window_id: WrWindowId, document_id: WrDocumentId); fn wr_schedule_render(window_id: WrWindowId,
fn wr_finished_scene_build(window_id: WrWindowId, document_id: WrDocumentId, pipeline_info: WrPipelineInfo); 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); 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<DocumentId>, info: PipelineInfo, sceneswap_time: u64) {
unsafe { unsafe {
let info = WrPipelineInfo::new(&info); let info = WrPipelineInfo::new(&info);
record_telemetry_time(TelemetryProbe::SceneSwapTime, sceneswap_time); 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, // 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 // otherwise there's no guarantee that the new scene will get rendered
// anytime soon // 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); } unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
} }
fn post_resource_update(&self, document_id: DocumentId) { fn post_resource_update(&self, document_ids: &Vec<DocumentId>) {
unsafe { wr_schedule_render(self.window_id, document_id) } 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); } 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); 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] #[no_mangle]
pub unsafe extern "C" fn wr_transaction_clear_display_list( pub unsafe extern "C" fn wr_transaction_clear_display_list(
txn: &mut Transaction, txn: &mut Transaction,

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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 bincode::serialize;
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{LittleEndian, WriteBytesExt};
use std::any::TypeId; 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 { pub fn should_record_msg(msg: &ApiMsg) -> bool {
match *msg { match *msg {
ApiMsg::UpdateResources(..) | ApiMsg::UpdateResources(..) |
ApiMsg::AddDocument { .. } | ApiMsg::AddDocument { .. } |
ApiMsg::DeleteDocument(..) => true, ApiMsg::DeleteDocument(..) => true,
ApiMsg::UpdateDocument(_, ref msgs) => { ApiMsg::UpdateDocuments(_, ref msgs) => {
if msgs.generate_frame { for msg in msgs {
return true; if should_record_transaction_msg(msg) {
} 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 false
} }
_ => false, _ => false,

Просмотреть файл

@ -862,57 +862,59 @@ impl RenderBackend {
while let Ok(msg) = self.scene_rx.try_recv() { while let Ok(msg) = self.scene_rx.try_recv() {
match msg { match msg {
SceneBuilderResult::Transaction(mut txn, result_tx) => { SceneBuilderResult::Transactions(mut txns, result_tx) => {
let has_built_scene = txn.built_scene.is_some(); for mut txn in txns.drain(..) {
if let Some(doc) = self.documents.get_mut(&txn.document_id) { 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() { if let Some(mut built_scene) = txn.built_scene.take() {
doc.new_async_scene_ready( doc.new_async_scene_ready(
built_scene, built_scene,
&mut self.recycler, &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 { self.resource_cache.add_rasterized_blob_images(
let (resume_tx, resume_rx) = channel(); txn.rasterized_blobs.take()
tx.send(SceneSwapResult::Complete(resume_tx)).unwrap(); );
// Block until the post-swap hook has completed on if let Some((rasterizer, info)) = txn.blob_rasterizer.take() {
// the scene builder thread. We need to do this before self.resource_cache.set_blob_rasterizer(rasterizer, info);
// 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. self.update_document(
// TODO: we might want to just ensure that removed documents are txn.document_id,
// always forwarded to the scene builder thread to avoid this case. txn.resource_updates.take(),
if let Some(tx) = result_tx { txn.interner_updates.take(),
tx.send(SceneSwapResult::Aborted).unwrap(); txn.frame_ops.take(),
} txn.notifications.take(),
continue; 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) => { SceneBuilderResult::FlushComplete(tx) => {
tx.send(()).ok(); tx.send(()).ok();
@ -1120,7 +1122,7 @@ impl RenderBackend {
preserve_frame_state: false, preserve_frame_state: false,
}; };
let txn = TransactionMsg::scene_message(scene_msg); 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( r.write_payload(*frame_counter, &Payload::construct_data(
epoch, epoch,
pipeline_id, pipeline_id,
@ -1177,10 +1179,10 @@ impl RenderBackend {
info!("Recycling stats: {:?}", self.recycler); info!("Recycling stats: {:?}", self.recycler);
return false; return false;
} }
ApiMsg::UpdateDocument(document_id, transaction_msg) => { ApiMsg::UpdateDocuments(document_ids, transaction_msgs) => {
self.prepare_transaction( self.prepare_transactions(
document_id, document_ids,
transaction_msg, transaction_msgs,
frame_counter, frame_counter,
profile_counters, profile_counters,
); );
@ -1190,99 +1192,112 @@ impl RenderBackend {
true true
} }
fn prepare_transaction( fn prepare_transactions(
&mut self, &mut self,
document_id: DocumentId, document_ids: Vec<DocumentId>,
mut transaction_msg: TransactionMsg, mut transaction_msgs: Vec<TransactionMsg>,
frame_counter: &mut u32, frame_counter: &mut u32,
profile_counters: &mut BackendProfileCounters, profile_counters: &mut BackendProfileCounters,
) { ) {
let mut txn = Box::new(Transaction { let mut use_scene_builder = transaction_msgs.iter()
document_id, .any(|transaction_msg| transaction_msg.use_scene_builder_thread);
display_list_updates: Vec::new(), let use_high_priority = transaction_msgs.iter()
removed_pipelines: Vec::new(), .any(|transaction_msg| !transaction_msg.low_priority);
epoch_updates: Vec::new(),
request_scene_build: None, let mut txns : Vec<Box<Transaction>> = document_ids.iter().zip(transaction_msgs.drain(..))
blob_rasterizer: None, .map(|(&document_id, mut transaction_msg)| {
blob_requests: Vec::new(), let mut txn = Box::new(Transaction {
resource_updates: transaction_msg.resource_updates, document_id,
frame_ops: transaction_msg.frame_ops, display_list_updates: Vec::new(),
rasterized_blobs: Vec::new(), removed_pipelines: Vec::new(),
notifications: transaction_msg.notifications, epoch_updates: Vec::new(),
set_root_pipeline: None, request_scene_build: None,
render_frame: transaction_msg.generate_frame, blob_rasterizer: None,
invalidate_rendered_frame: transaction_msg.invalidate_rendered_frame, 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( if use_scene_builder {
&mut txn.resource_updates, for txn in txns.iter_mut() {
&mut profile_counters.resources, let doc = self.documents.get_mut(&txn.document_id).unwrap();
);
// 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 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; return;
} }
let doc = self.documents.get_mut(&document_id).unwrap(); let tx = if use_high_priority {
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 {
&self.scene_tx &self.scene_tx
} else {
&self.low_priority_scene_tx
}; };
tx.send(SceneBuilderRequest::Transaction(txn)).unwrap(); tx.send(SceneBuilderRequest::Transactions(txns)).unwrap();
} }
fn update_document( fn update_document(

Просмотреть файл

@ -5496,11 +5496,11 @@ pub trait SceneBuilderHooks {
/// This is called after each scene swap occurs. The PipelineInfo contains /// This is called after each scene swap occurs. The PipelineInfo contains
/// the updated epochs and pipelines removed in the new scene compared to /// the updated epochs and pipelines removed in the new scene compared to
/// the old scene. /// the old scene.
fn post_scene_swap(&self, document_id: DocumentId, info: PipelineInfo, sceneswap_time: u64); fn post_scene_swap(&self, document_id: &Vec<DocumentId>, info: PipelineInfo, sceneswap_time: u64);
/// This is called after a resource update operation on the scene builder /// This is called after a resource update operation on the scene builder
/// thread, in the case where resource updates were applied without a scene /// thread, in the case where resource updates were applied without a scene
/// build. /// build.
fn post_resource_update(&self, document_id: DocumentId); fn post_resource_update(&self, document_ids: &Vec<DocumentId>);
/// This is called after a scene build completes without any changes being /// 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 /// made. We guarantee that each pre_scene_build call will be matched with
/// exactly one of post_scene_swap, post_resource_update or /// exactly one of post_scene_swap, post_resource_update or

Просмотреть файл

@ -28,6 +28,7 @@ use resource_cache::{AsyncBlobImageInfo, FontInstanceMap};
use render_backend::DocumentView; use render_backend::DocumentView;
use renderer::{PipelineInfo, SceneBuilderHooks}; use renderer::{PipelineInfo, SceneBuilderHooks};
use scene::Scene; use scene::Scene;
use std::iter;
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
use std::mem::replace; use std::mem::replace;
use time::precise_time_ns; use time::precise_time_ns;
@ -136,7 +137,7 @@ pub struct BuiltScene {
// Message from render backend to scene builder. // Message from render backend to scene builder.
pub enum SceneBuilderRequest { pub enum SceneBuilderRequest {
Transaction(Box<Transaction>), Transactions(Vec<Box<Transaction>>),
ExternalEvent(ExternalEvent), ExternalEvent(ExternalEvent),
DeleteDocument(DocumentId), DeleteDocument(DocumentId),
WakeUp, WakeUp,
@ -155,7 +156,7 @@ pub enum SceneBuilderRequest {
// Message from scene builder to render backend. // Message from scene builder to render backend.
pub enum SceneBuilderResult { pub enum SceneBuilderResult {
Transaction(Box<BuiltTransaction>, Option<Sender<SceneSwapResult>>), Transactions(Vec<Box<BuiltTransaction>>, Option<Sender<SceneSwapResult>>),
ExternalEvent(ExternalEvent), ExternalEvent(ExternalEvent),
FlushComplete(MsgSender<()>), FlushComplete(MsgSender<()>),
ClearNamespace(IdNamespace), ClearNamespace(IdNamespace),
@ -321,9 +322,11 @@ impl SceneBuilder {
Ok(SceneBuilderRequest::Flush(tx)) => { Ok(SceneBuilderRequest::Flush(tx)) => {
self.send(SceneBuilderResult::FlushComplete(tx)); self.send(SceneBuilderResult::FlushComplete(tx));
} }
Ok(SceneBuilderRequest::Transaction(mut txn)) => { Ok(SceneBuilderRequest::Transactions(mut txns)) => {
let built_txn = self.process_transaction(&mut txn); let built_txns : Vec<Box<BuiltTransaction>> = txns.iter_mut()
self.forward_built_transaction(built_txn); .map(|txn| self.process_transaction(txn))
.collect();
self.forward_built_transactions(built_txns);
} }
Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { Ok(SceneBuilderRequest::DeleteDocument(document_id)) => {
self.documents.remove(&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, document_id: item.document_id,
render_frame: item.build_frame, render_frame: item.build_frame,
invalidate_rendered_frame: false, invalidate_rendered_frame: false,
@ -443,9 +446,9 @@ impl SceneBuilder {
scene_build_start_time, scene_build_start_time,
scene_build_end_time: precise_time_ns(), scene_build_end_time: precise_time_ns(),
interner_updates, 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. /// Send the results of process_transaction back to the render backend.
fn forward_built_transaction(&mut self, txn: Box<BuiltTransaction>) { fn forward_built_transactions(&mut self, txns: Vec<Box<BuiltTransaction>>) {
// We only need the pipeline info and the result channel if we let (pipeline_info, result_tx, result_rx) = match &self.hooks {
// have a hook callback *and* if this transaction actually built &Some(ref hooks) => {
// a new scene that is going to get swapped in. In other cases if txns.iter().any(|txn| txn.built_scene.is_some()) {
// pipeline_info can be None and we can avoid some overhead from let info = PipelineInfo {
// invoking the hooks and blocking on the channel. epochs: txns.iter()
let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &txn.built_scene) { .filter(|txn| txn.built_scene.is_some())
(&Some(ref hooks), &Some(ref built)) => { .map(|txn| {
let info = PipelineInfo { txn.built_scene.as_ref().unwrap()
epochs: built.scene.pipeline_epochs.iter() .scene.pipeline_epochs.iter()
.map(|(&pipeline_id, &epoch)| ((pipeline_id, txn.document_id), epoch)) .zip(iter::repeat(txn.document_id))
.collect(), .map(|((&pipeline_id, &epoch), document_id)| ((pipeline_id, document_id), epoch))
removed_pipelines: txn.removed_pipelines.clone(), }).flatten().collect(),
}; removed_pipelines: txns.iter()
let (tx, rx) = channel(); .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 scene_swap_start_time = precise_time_ns();
let has_resources_updates = !txn.resource_updates.is_empty(); let document_ids = txns.iter().map(|txn| txn.document_id).collect();
let invalidate_rendered_frame = txn.invalidate_rendered_frame; let have_resources_updates : Vec<DocumentId> = 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); let _ = self.api_tx.send(ApiMsg::WakeUp);
@ -587,7 +603,7 @@ impl SceneBuilder {
// Block until the swap is done, then invoke the hook. // Block until the swap is done, then invoke the hook.
let swap_result = result_rx.unwrap().recv(); let swap_result = result_rx.unwrap().recv();
let scene_swap_time = precise_time_ns() - scene_swap_start_time; 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); pipeline_info, scene_swap_time);
// Once the hook is done, allow the RB thread to resume // Once the hook is done, allow the RB thread to resume
match swap_result { 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 { if let &Some(ref hooks) = &self.hooks {
hooks.post_resource_update(document_id); hooks.post_resource_update(&have_resources_updates);
} }
} else { } else {
if let &Some(ref hooks) = &self.hooks { if let &Some(ref hooks) = &self.hooks {
@ -634,9 +650,11 @@ impl LowPrioritySceneBuilder {
pub fn run(&mut self) { pub fn run(&mut self) {
loop { loop {
match self.rx.recv() { match self.rx.recv() {
Ok(SceneBuilderRequest::Transaction(txn)) => { Ok(SceneBuilderRequest::Transactions(mut txns)) => {
let txn = self.process_transaction(txn); let txns : Vec<Box<Transaction>> = txns.drain(..)
self.tx.send(SceneBuilderRequest::Transaction(txn)).unwrap(); .map(|txn| self.process_transaction(txn))
.collect();
self.tx.send(SceneBuilderRequest::Transactions(txns)).unwrap();
} }
Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { Ok(SceneBuilderRequest::DeleteDocument(document_id)) => {
self.tx.send(SceneBuilderRequest::DeleteDocument(document_id)).unwrap(); self.tx.send(SceneBuilderRequest::DeleteDocument(document_id)).unwrap();

Просмотреть файл

@ -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. /// Represents a transaction in the format sent through the channel.
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
pub struct TransactionMsg { pub struct TransactionMsg {
@ -726,7 +731,7 @@ pub enum ApiMsg {
/// Adds a new document with given initial size. /// Adds a new document with given initial size.
AddDocument(DocumentId, FramebufferIntSize, DocumentLayer), AddDocument(DocumentId, FramebufferIntSize, DocumentLayer),
/// A message targeted at a particular document. /// A message targeted at a particular document.
UpdateDocument(DocumentId, TransactionMsg), UpdateDocuments(Vec<DocumentId>, Vec<TransactionMsg>),
/// Deletes an existing document. /// Deletes an existing document.
DeleteDocument(DocumentId), DeleteDocument(DocumentId),
/// An opaque handle that must be passed to the render notifier. It is used by Gecko /// 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::CloneApi(..) => "ApiMsg::CloneApi",
ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient", ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient",
ApiMsg::AddDocument(..) => "ApiMsg::AddDocument", ApiMsg::AddDocument(..) => "ApiMsg::AddDocument",
ApiMsg::UpdateDocument(..) => "ApiMsg::UpdateDocument", ApiMsg::UpdateDocuments(..) => "ApiMsg::UpdateDocuments",
ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument", ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument",
ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent", ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace", ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace",
@ -1216,7 +1221,7 @@ impl RenderApi {
// `RenderApi` instances for layout and compositor. // `RenderApi` instances for layout and compositor.
//assert_eq!(document_id.0, self.namespace_id); //assert_eq!(document_id.0, self.namespace_id);
self.api_sender self.api_sender
.send(ApiMsg::UpdateDocument(document_id, TransactionMsg::scene_message(msg))) .send(ApiMsg::UpdateDocuments(vec![document_id], vec![TransactionMsg::scene_message(msg)]))
.unwrap() .unwrap()
} }
@ -1226,7 +1231,7 @@ impl RenderApi {
// `RenderApi` instances for layout and compositor. // `RenderApi` instances for layout and compositor.
//assert_eq!(document_id.0, self.namespace_id); //assert_eq!(document_id.0, self.namespace_id);
self.api_sender self.api_sender
.send(ApiMsg::UpdateDocument(document_id, TransactionMsg::frame_message(msg))) .send(ApiMsg::UpdateDocuments(vec![document_id], vec![TransactionMsg::frame_message(msg)]))
.unwrap() .unwrap()
} }
@ -1235,7 +1240,24 @@ impl RenderApi {
for payload in payloads { for payload in payloads {
self.payload_sender.send_payload(payload).unwrap(); 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<DocumentId>, mut transactions: Vec<Transaction>) {
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 /// Does a hit test on display items in the specified document, at the given

Просмотреть файл

@ -140,21 +140,24 @@ impl WrenchThing for BinaryFrameReader {
// (b) SetDisplayList // (b) SetDisplayList
// (c) GenerateFrame that occurs *after* (a) and (b) // (c) GenerateFrame that occurs *after* (a) and (b)
match msg { match msg {
ApiMsg::UpdateDocument(_, ref txn) => { ApiMsg::UpdateDocuments(_, ref txns) => {
if txn.generate_frame { for txn in txns {
found_frame_marker = true; if txn.generate_frame {
} // TODO: is this appropriate, or do we need a ternary value / something else?
for doc_msg in &txn.scene_ops { found_frame_marker = true;
match *doc_msg { }
SceneMsg::SetDisplayList { .. } => { for doc_msg in &txn.scene_ops {
found_frame_marker = false; match *doc_msg {
found_display_list = true; 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;
}
_ => {}
} }
} }
} }

Просмотреть файл

@ -271,6 +271,31 @@ impl JsonFrameWriter {
self.images.insert(key, data); self.images.insert(key, data);
Some(path) 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 { impl fmt::Debug for JsonFrameWriter {
@ -283,29 +308,9 @@ impl webrender::ApiRecordingReceiver for JsonFrameWriter {
fn write_msg(&mut self, _: u32, msg: &ApiMsg) { fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
match *msg { match *msg {
ApiMsg::UpdateResources(ref updates) => self.update_resources(updates), ApiMsg::UpdateResources(ref updates) => self.update_resources(updates),
ApiMsg::UpdateDocuments(_, ref txns) => {
ApiMsg::UpdateDocument(_, ref txn) => { for txn in txns {
self.update_resources(&txn.resource_updates); self.update_document(txn)
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::CloneApi(..) => {} ApiMsg::CloneApi(..) => {}

Просмотреть файл

@ -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 { impl fmt::Debug for RonFrameWriter {
@ -161,28 +186,9 @@ impl webrender::ApiRecordingReceiver for RonFrameWriter {
fn write_msg(&mut self, _: u32, msg: &ApiMsg) { fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
match *msg { match *msg {
ApiMsg::UpdateResources(ref updates) => self.update_resources(updates), ApiMsg::UpdateResources(ref updates) => self.update_resources(updates),
ApiMsg::UpdateDocument(_, ref txn) => { ApiMsg::UpdateDocuments(_, ref txns) => {
self.update_resources(&txn.resource_updates); for txn in txns {
for doc_msg in &txn.scene_ops { self.update_document(txn)
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::CloneApi(..) => {} ApiMsg::CloneApi(..) => {}

Просмотреть файл

@ -443,6 +443,46 @@ impl YamlFrameWriterReceiver {
scene: Scene::new(), 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 { impl fmt::Debug for YamlFrameWriterReceiver {
@ -1234,43 +1274,9 @@ impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver {
ApiMsg::UpdateResources(ref updates) => { ApiMsg::UpdateResources(ref updates) => {
self.frame_writer.update_resources(updates); self.frame_writer.update_resources(updates);
} }
ApiMsg::UpdateDocument(_, ref txn) => { ApiMsg::UpdateDocuments(_, ref txns) => {
self.frame_writer.update_resources(&txn.resource_updates); for txn in txns {
for doc_msg in &txn.scene_ops { self.update_document(txn);
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);
}
_ => {}
}
} }
} }
_ => {} _ => {}