diff --git a/gfx/layers/apz/public/CompositorController.h b/gfx/layers/apz/public/CompositorController.h index e374e5e7ca95..ad71a8faf56a 100644 --- a/gfx/layers/apz/public/CompositorController.h +++ b/gfx/layers/apz/public/CompositorController.h @@ -21,7 +21,7 @@ class CompositorController { /** * Ask the compositor to schedule a new composite. */ - virtual void ScheduleRenderOnCompositorThread() = 0; + virtual void ScheduleRenderOnCompositorThread(wr::RenderReasons aReasons) = 0; protected: virtual ~CompositorController() = default; diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index fffb7a997fd9..d4568b022012 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -723,7 +723,8 @@ void APZCTreeManager::SampleForWebRender(const Maybe& aVsyncId, bool activeAnimations = AdvanceAnimationsInternal(lock, aSampleTime); if (activeAnimations && controller) { - controller->ScheduleRenderOnCompositorThread(); + controller->ScheduleRenderOnCompositorThread( + wr::RenderReasons::ANIMATED_PROPERTY); } nsTArray transforms; diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 2e45ce3330c6..64865168c6a7 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -4168,7 +4168,8 @@ const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort( void AsyncPanZoomController::ScheduleComposite() { if (mCompositorController) { - mCompositorController->ScheduleRenderOnCompositorThread(); + mCompositorController->ScheduleRenderOnCompositorThread( + wr::RenderReasons::APZ); } } diff --git a/gfx/layers/ipc/CompositableTransactionParent.cpp b/gfx/layers/ipc/CompositableTransactionParent.cpp index 294ddd6ca321..c16e11281171 100644 --- a/gfx/layers/ipc/CompositableTransactionParent.cpp +++ b/gfx/layers/ipc/CompositableTransactionParent.cpp @@ -52,7 +52,7 @@ static bool ScheduleComposition(CompositableHost* aCompositable) { if (!cp) { return false; } - cp->ScheduleComposition(); + cp->ScheduleComposition(wr::RenderReasons::ASYNC_IMAGE); return true; } diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index 3aba8c962814..19b6477473d0 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -395,11 +395,12 @@ bool CompositorBridgeChild::SendAdoptChild(const LayersId& id) { return PCompositorBridgeChild::SendAdoptChild(id); } -bool CompositorBridgeChild::SendFlushRendering() { +bool CompositorBridgeChild::SendFlushRendering( + const wr::RenderReasons& aReasons) { if (!mCanSend) { return false; } - return PCompositorBridgeChild::SendFlushRendering(); + return PCompositorBridgeChild::SendFlushRendering(aReasons); } bool CompositorBridgeChild::SendStartFrameTimeRecording( diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h index 3034136ad120..4f52559b9c70 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.h +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -126,7 +126,7 @@ class CompositorBridgeChild final : public PCompositorBridgeChild, bool SendResume(); bool SendResumeAsync(); bool SendAdoptChild(const LayersId& id); - bool SendFlushRendering(); + bool SendFlushRendering(const wr::RenderReasons& aReasons); bool SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex); bool SendStopFrameTimeRecording(const uint32_t& startIndex, diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp index 1cd174527018..0aaf73805489 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -505,31 +505,34 @@ CompositorBridgeParent::RecvWaitOnTransactionProcessed() { return IPC_OK(); } -mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering() { +mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering( + const wr::RenderReasons& aReasons) { if (mWrBridge) { - mWrBridge->FlushRendering(); + mWrBridge->FlushRendering(aReasons); return IPC_OK(); } if (mCompositorScheduler->NeedsComposite()) { CancelCurrentCompositeTask(); - ForceComposeToTarget(nullptr); + ForceComposeToTarget(aReasons, nullptr, nullptr); } return IPC_OK(); } -mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync() { +mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync( + const wr::RenderReasons& aReasons) { if (mWrBridge) { - mWrBridge->FlushRendering(false); + mWrBridge->FlushRendering(aReasons, false); return IPC_OK(); } - return RecvFlushRendering(); + return RecvFlushRendering(aReasons); } -mozilla::ipc::IPCResult CompositorBridgeParent::RecvForcePresent() { +mozilla::ipc::IPCResult CompositorBridgeParent::RecvForcePresent( + const wr::RenderReasons& aReasons) { if (mWrBridge) { - mWrBridge->ScheduleForcedGenerateFrame(); + mWrBridge->ScheduleForcedGenerateFrame(aReasons); } return IPC_OK(); } @@ -576,11 +579,12 @@ void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) { &CompositorBridgeParent::DeferredDestroy)); } -void CompositorBridgeParent::ScheduleRenderOnCompositorThread() { +void CompositorBridgeParent::ScheduleRenderOnCompositorThread( + wr::RenderReasons aReasons) { MOZ_ASSERT(CompositorThread()); - CompositorThread()->Dispatch( - NewRunnableMethod("layers::CompositorBridgeParent::ScheduleComposition", - this, &CompositorBridgeParent::ScheduleComposition)); + CompositorThread()->Dispatch(NewRunnableMethod( + "layers::CompositorBridgeParent::ScheduleComposition", this, + &CompositorBridgeParent::ScheduleComposition, aReasons)); } void CompositorBridgeParent::PauseComposition() { @@ -631,17 +635,18 @@ void CompositorBridgeParent::ResumeComposition() { mPaused = false; Invalidate(); - mCompositorScheduler->ForceComposeToTarget(nullptr, nullptr); + mCompositorScheduler->ForceComposeToTarget(wr::RenderReasons::WIDGET, nullptr, + nullptr); // if anyone's waiting to make sure that composition really got resumed, tell // them lock.NotifyAll(); } -void CompositorBridgeParent::ForceComposition() { +void CompositorBridgeParent::ForceComposition(wr::RenderReasons aReasons) { // Cancel the orientation changed state to force composition mForceCompositionTask = nullptr; - ScheduleRenderOnCompositorThread(); + ScheduleRenderOnCompositorThread(aReasons); } void CompositorBridgeParent::CancelCurrentCompositeTask() { @@ -673,30 +678,31 @@ void CompositorBridgeParent::NotifyShadowTreeTransaction( bool aScheduleComposite, uint32_t aPaintSequenceNumber, bool aIsRepeatTransaction, bool aHitTestUpdate) { if (aScheduleComposite) { - ScheduleComposition(); + ScheduleComposition(wr::RenderReasons::OTHER); } } -void CompositorBridgeParent::ScheduleComposition() { +void CompositorBridgeParent::ScheduleComposition(wr::RenderReasons aReasons) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (mPaused) { return; } if (mWrBridge) { - mWrBridge->ScheduleGenerateFrame(); + mWrBridge->ScheduleGenerateFrame(aReasons); } else { - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(aReasons); } } -void CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, +void CompositorBridgeParent::ForceComposeToTarget(wr::RenderReasons aReasons, + DrawTarget* aTarget, const gfx::IntRect* aRect) { AUTO_PROFILER_LABEL("CompositorBridgeParent::ForceComposeToTarget", GRAPHICS); AutoRestore override(mOverrideComposeReadiness); mOverrideComposeReadiness = true; - mCompositorScheduler->ForceComposeToTarget(aTarget, aRect); + mCompositorScheduler->ForceComposeToTarget(aReasons, aTarget, aRect); } PAPZCTreeManagerParent* CompositorBridgeParent::AllocPAPZCTreeManagerParent( @@ -837,7 +843,7 @@ bool CompositorBridgeParent::SetTestSampleTime(const LayersId& aId, } if (mWrBridge) { - mWrBridge->FlushRendering(); + mWrBridge->FlushRendering(wr::RenderReasons::TESTING); return true; } @@ -950,7 +956,7 @@ void CompositorBridgeParent::SetFixedLayerMargins(ScreenIntCoord aTop, } Invalidate(); - ScheduleComposition(); + ScheduleComposition(wr::RenderReasons::RESIZE); } CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent( @@ -1003,7 +1009,7 @@ void CompositorBridgeParent::NotifyVsync(const VsyncEvent& aVsync, /* static */ void CompositorBridgeParent::ScheduleForcedComposition( - const LayersId& aLayersId) { + const LayersId& aLayersId, wr::RenderReasons aReasons) { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); @@ -1019,9 +1025,9 @@ void CompositorBridgeParent::ScheduleForcedComposition( } if (cbp->mWrBridge) { - cbp->mWrBridge->ScheduleForcedGenerateFrame(); + cbp->mWrBridge->ScheduleForcedGenerateFrame(aReasons); } else if (cbp->CanComposite()) { - cbp->mCompositorScheduler->ScheduleComposition(); + cbp->mCompositorScheduler->ScheduleComposition(aReasons); } } @@ -1090,7 +1096,6 @@ mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild( const LayersId& child) { RefPtr oldApzUpdater; APZCTreeManagerParent* parent; - bool scheduleComposition = false; bool apzEnablementChanged = false; RefPtr childWrBridge; @@ -1142,10 +1147,6 @@ mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild( parent = sIndirectLayerTrees[child].mApzcTreeManagerParent; } - if (scheduleComposition) { - ScheduleComposition(); - } - if (childWrBridge) { MOZ_ASSERT(mWrBridge); RefPtr api = mWrBridge->GetWebRenderAPI(); @@ -1643,7 +1644,7 @@ void CompositorBridgeParent::NotifyDidSceneBuild( if (mWrBridge) { mWrBridge->NotifyDidSceneBuild(aInfo); } else { - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(wr::RenderReasons::SCENE); } } diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h index 076c270dd14f..f1d38f8c68e0 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.h +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -178,7 +178,8 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent, MOZ_CRASH("Should only be called on ContentCompositorBridgeParent."); } - virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget, + virtual void ForceComposeToTarget(wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) { MOZ_CRASH(); } @@ -219,8 +220,10 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent, PCompositorWidgetParent* aActor) = 0; virtual mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& id) = 0; - virtual mozilla::ipc::IPCResult RecvFlushRenderingAsync() = 0; - virtual mozilla::ipc::IPCResult RecvForcePresent() = 0; + virtual mozilla::ipc::IPCResult RecvFlushRenderingAsync( + const wr::RenderReasons& aReasons) = 0; + virtual mozilla::ipc::IPCResult RecvForcePresent( + const wr::RenderReasons& aReasons) = 0; virtual mozilla::ipc::IPCResult RecvBeginRecording( const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) = 0; virtual mozilla::ipc::IPCResult RecvEndRecordingToDisk( @@ -241,7 +244,8 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent, CompositorOptions* compositorOptions) = 0; virtual mozilla::ipc::IPCResult RecvNotifyChildRecreated( const LayersId& id, CompositorOptions* compositorOptions) = 0; - virtual mozilla::ipc::IPCResult RecvFlushRendering() = 0; + virtual mozilla::ipc::IPCResult RecvFlushRendering( + const wr::RenderReasons& aReasons) = 0; virtual mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() = 0; virtual mozilla::ipc::IPCResult RecvStartFrameTimeRecording( const int32_t& bufferSize, uint32_t* startIndex) = 0; @@ -308,10 +312,13 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, mozilla::ipc::IPCResult RecvNotifyChildRecreated( const LayersId& child, CompositorOptions* aOptions) override; mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override; - mozilla::ipc::IPCResult RecvFlushRendering() override; - mozilla::ipc::IPCResult RecvFlushRenderingAsync() override; + mozilla::ipc::IPCResult RecvFlushRendering( + const wr::RenderReasons& aReasons) override; + mozilla::ipc::IPCResult RecvFlushRenderingAsync( + const wr::RenderReasons& aReasons) override; mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override; - mozilla::ipc::IPCResult RecvForcePresent() override; + mozilla::ipc::IPCResult RecvForcePresent( + const wr::RenderReasons& aReasons) override; mozilla::ipc::IPCResult RecvStartFrameTimeRecording( const int32_t& aBufferSize, uint32_t* aOutStartIndex) override; @@ -408,9 +415,9 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, void AsyncRender(); // Can be called from any thread - void ScheduleRenderOnCompositorThread() override; + void ScheduleRenderOnCompositorThread(wr::RenderReasons aReasons) override; - void ScheduleComposition(); + void ScheduleComposition(wr::RenderReasons aReasons); void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint, const FocusTarget& aFocusTarget, @@ -426,7 +433,8 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, void ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, bool aIsFirstPaint); - static void ScheduleForcedComposition(const LayersId& aLayersId); + static void ScheduleForcedComposition(const LayersId& aLayersId, + wr::RenderReasons aReasons); /** * Returns the unique layer tree identifier that corresponds to the root @@ -526,7 +534,8 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, widget::CompositorWidget* GetWidget() { return mWidget; } virtual void ForceComposeToTarget( - gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override; + wr::RenderReasons aReasons, gfx::DrawTarget* aTarget, + const gfx::IntRect* aRect = nullptr) override; PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent( const LayersId& aLayersId) override; @@ -648,7 +657,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, bool IsPaused() { return mPaused; } protected: - void ForceComposition(); + void ForceComposition(wr::RenderReasons aReasons); void CancelCurrentCompositeTask(); /** diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp index 4662a1e3ae7e..bbccaf11b114 100644 --- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp +++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp @@ -69,10 +69,12 @@ CompositorVsyncScheduler::CompositorVsyncScheduler( mLastVsyncTime(TimeStamp::Now()), mLastVsyncOutputTime(TimeStamp::Now()), mIsObservingVsync(false), + mRendersDelayedByVsyncReasons(wr::RenderReasons::NONE), mVsyncNotificationsSkipped(0), mWidget(aWidget), mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"), mCurrentCompositeTask(nullptr), + mCurrentCompositeTaskReasons(wr::RenderReasons::NONE), mCurrentVRTaskMonitor("CurrentVRTaskMonitor"), mCurrentVRTask(nullptr) { mVsyncObserver = new Observer(this); @@ -108,13 +110,15 @@ void CompositorVsyncScheduler::Destroy() { CancelCurrentVRTask(); } -void CompositorVsyncScheduler::PostCompositeTask( - const VsyncEvent& aVsyncEvent) { +void CompositorVsyncScheduler::PostCompositeTask(const VsyncEvent& aVsyncEvent, + wr::RenderReasons aReasons) { MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + mCurrentCompositeTaskReasons = mCurrentCompositeTaskReasons | aReasons; if (mCurrentCompositeTask == nullptr && CompositorThread()) { - RefPtr task = NewCancelableRunnableMethod( - "layers::CompositorVsyncScheduler::Composite", this, - &CompositorVsyncScheduler::Composite, aVsyncEvent); + RefPtr task = + NewCancelableRunnableMethod( + "layers::CompositorVsyncScheduler::Composite", this, + &CompositorVsyncScheduler::Composite, aVsyncEvent, aReasons); mCurrentCompositeTask = task; CompositorThread()->Dispatch(task.forget()); } @@ -131,7 +135,7 @@ void CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) { } } -void CompositorVsyncScheduler::ScheduleComposition() { +void CompositorVsyncScheduler::ScheduleComposition(wr::RenderReasons aReasons) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (!mVsyncObserver) { // Destroy was already called on this object. @@ -146,7 +150,7 @@ void CompositorVsyncScheduler::ScheduleComposition() { if (mAsapScheduling) { // Used only for performance testing purposes, and when recording/replaying // to ensure that graphics are up to date. - PostCompositeTask(vsyncEvent); + PostCompositeTask(vsyncEvent, aReasons); } else { if (!mCompositeRequestedAt) { mCompositeRequestedAt = TimeStamp::Now(); @@ -157,7 +161,9 @@ void CompositorVsyncScheduler::ScheduleComposition() { // through the main thread of the UI process. It's possible that // we're blocking there waiting on a composite, so schedule an initial // one now to get things started. - PostCompositeTask(vsyncEvent); + PostCompositeTask(vsyncEvent, aReasons); + } else { + mRendersDelayedByVsyncReasons = aReasons; } } } @@ -188,10 +194,10 @@ bool CompositorVsyncScheduler::NotifyVsync(const VsyncEvent& aVsync) { #if defined(MOZ_WIDGET_ANDROID) gfx::VRManager* vm = gfx::VRManager::Get(); if (!vm->IsPresenting()) { - PostCompositeTask(aVsync); + PostCompositeTask(aVsync, wr::RenderReasons::VSYNC); } #else - PostCompositeTask(aVsync); + PostCompositeTask(aVsync, wr::RenderReasons::VSYNC); #endif // defined(MOZ_WIDGET_ANDROID) PostVRTask(aVsync.mTime); @@ -208,22 +214,31 @@ void CompositorVsyncScheduler::CancelCurrentVRTask() { } } -void CompositorVsyncScheduler::CancelCurrentCompositeTask() { +wr::RenderReasons CompositorVsyncScheduler::CancelCurrentCompositeTask() { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread()); MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + wr::RenderReasons canceledTaskRenderReasons = mCurrentCompositeTaskReasons; + mCurrentCompositeTaskReasons = wr::RenderReasons::NONE; if (mCurrentCompositeTask) { mCurrentCompositeTask->Cancel(); mCurrentCompositeTask = nullptr; } + + return canceledTaskRenderReasons; } -void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent) { +void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent, + wr::RenderReasons aReasons) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(mVsyncSchedulerOwner); { // scope lock MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + aReasons = + aReasons | mCurrentCompositeTaskReasons | mRendersDelayedByVsyncReasons; + mCurrentCompositeTaskReasons = wr::RenderReasons::NONE; + mRendersDelayedByVsyncReasons = wr::RenderReasons::NONE; mCurrentCompositeTask = nullptr; } @@ -253,7 +268,8 @@ void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent) { mLastComposeTime = SampleTime::FromVsync(aVsyncEvent.mTime); // Tell the owner to do a composite - mVsyncSchedulerOwner->CompositeToTarget(aVsyncEvent.mId, nullptr, nullptr); + mVsyncSchedulerOwner->CompositeToTarget(aVsyncEvent.mId, aReasons, nullptr, + nullptr); mVsyncNotificationsSkipped = 0; @@ -267,7 +283,8 @@ void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent) { } } -void CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, +void CompositorVsyncScheduler::ForceComposeToTarget(wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, const IntRect* aRect) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); @@ -289,7 +306,7 @@ void CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, mLastComposeTime = SampleTime::FromNow(); MOZ_ASSERT(mVsyncSchedulerOwner); - mVsyncSchedulerOwner->CompositeToTarget(VsyncId(), aTarget, aRect); + mVsyncSchedulerOwner->CompositeToTarget(VsyncId(), aReasons, aTarget, aRect); } bool CompositorVsyncScheduler::NeedsComposite() { @@ -300,8 +317,8 @@ bool CompositorVsyncScheduler::NeedsComposite() { bool CompositorVsyncScheduler::FlushPendingComposite() { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (mCompositeRequestedAt) { - CancelCurrentCompositeTask(); - ForceComposeToTarget(nullptr, nullptr); + wr::RenderReasons reasons = CancelCurrentCompositeTask(); + ForceComposeToTarget(reasons, nullptr, nullptr); return true; } return false; diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.h b/gfx/layers/ipc/CompositorVsyncScheduler.h index 67e3d73b0323..220f8d5338d4 100644 --- a/gfx/layers/ipc/CompositorVsyncScheduler.h +++ b/gfx/layers/ipc/CompositorVsyncScheduler.h @@ -15,6 +15,7 @@ #include "mozilla/TimeStamp.h" // for TimeStamp #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/SampleTime.h" +#include "mozilla/webrender/webrender_ffi.h" #include "mozilla/VsyncDispatcher.h" #include "mozilla/widget/CompositorWidget.h" #include "nsISupportsImpl.h" @@ -60,12 +61,14 @@ class CompositorVsyncScheduler { * composition soon (likely at the next vsync). This must be called on the * compositor thread. */ - void ScheduleComposition(); + void ScheduleComposition(wr::RenderReasons aReasons); /** * Cancel any composite task that has been scheduled but hasn't run yet. + * + * Returns the render reasons of the canceled task if any. */ - void CancelCurrentCompositeTask(); + wr::RenderReasons CancelCurrentCompositeTask(); /** * Check if a composite is pending. This is generally true between a call @@ -77,7 +80,8 @@ class CompositorVsyncScheduler { * Force a composite to happen right away, without waiting for the next vsync. * This must be called on the compositor thread. */ - void ForceComposeToTarget(gfx::DrawTarget* aTarget, + void ForceComposeToTarget(wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, const gfx::IntRect* aRect); /** @@ -113,7 +117,8 @@ class CompositorVsyncScheduler { // Post a task to run Composite() on the compositor thread, if there isn't // such a task already queued. Can be called from any thread. - void PostCompositeTask(const VsyncEvent& aVsyncEvent); + void PostCompositeTask(const VsyncEvent& aVsyncEvent, + wr::RenderReasons aReasons); // Post a task to run DispatchVREvents() on the VR thread, if there isn't // such a task already queued. Can be called from any thread. @@ -126,7 +131,7 @@ class CompositorVsyncScheduler { // This gets run at vsync time and "does" a composite (which really means // update internal state and call the owner to do the composite). - void Composite(const VsyncEvent& aVsyncEvent); + void Composite(const VsyncEvent& aVsyncEvent, wr::RenderReasons aReasons); void ObserveVsync(); void UnobserveVsync(); @@ -155,6 +160,8 @@ class CompositorVsyncScheduler { bool mAsapScheduling; bool mIsObservingVsync; + // Accessed on the compositor thread. + wr::RenderReasons mRendersDelayedByVsyncReasons; TimeStamp mCompositeRequestedAt; int32_t mVsyncNotificationsSkipped; widget::CompositorWidget* mWidget; @@ -162,6 +169,8 @@ class CompositorVsyncScheduler { mozilla::Monitor mCurrentCompositeTaskMonitor; RefPtr mCurrentCompositeTask; + // Accessed on multiple threads, guarded by mCurrentCompositeTaskMonitor. + wr::RenderReasons mCurrentCompositeTaskReasons; mozilla::Monitor mCurrentVRTaskMonitor; RefPtr mCurrentVRTask; diff --git a/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h index 1691f6535ae3..ac147fd24159 100644 --- a/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h +++ b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h @@ -8,6 +8,7 @@ #define mozilla_layers_CompositorVsyncSchedulerOwner_h #include "mozilla/VsyncDispatcher.h" +#include "mozilla/webrender/webrender_ffi.h" namespace mozilla { @@ -21,7 +22,8 @@ class CompositorVsyncSchedulerOwner { public: virtual bool IsPendingComposite() = 0; virtual void FinishPendingComposite() = 0; - virtual void CompositeToTarget(VsyncId aId, gfx::DrawTarget* aTarget, + virtual void CompositeToTarget(VsyncId aId, wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) = 0; virtual TimeDuration GetVsyncInterval() const = 0; }; diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.h b/gfx/layers/ipc/ContentCompositorBridgeParent.h index 8e00629e5ab3..e0ab1ddb7e2f 100644 --- a/gfx/layers/ipc/ContentCompositorBridgeParent.h +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h @@ -61,11 +61,17 @@ class ContentCompositorBridgeParent final : public CompositorBridgeParentBase { mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override { return IPC_FAIL_NO_REASON(this); } - mozilla::ipc::IPCResult RecvFlushRendering() override { return IPC_OK(); } - mozilla::ipc::IPCResult RecvFlushRenderingAsync() override { + mozilla::ipc::IPCResult RecvFlushRendering( + const wr::RenderReasons&) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvFlushRenderingAsync( + const wr::RenderReasons&) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvForcePresent(const wr::RenderReasons&) override { return IPC_OK(); } - mozilla::ipc::IPCResult RecvForcePresent() override { return IPC_OK(); } mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override { return IPC_OK(); } diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl index a2cedeef81eb..04389c95856a 100644 --- a/gfx/layers/ipc/PCompositorBridge.ipdl +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -39,6 +39,7 @@ using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h"; using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::RenderReasons from "mozilla/webrender/webrender_ffi.h"; using base::ProcessId from "base/process.h"; using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h"; using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h"; @@ -176,18 +177,18 @@ parent: // Make sure any pending composites are started immediately and // block until they are completed. - sync FlushRendering(); + sync FlushRendering(RenderReasons aReasons); // Same as FlushRendering, but asynchronous, since not all platforms require // synchronous repaints on resize. - async FlushRenderingAsync(); + async FlushRenderingAsync(RenderReasons aReasons); // Make sure any pending composites have been received. sync WaitOnTransactionProcessed(); // Force an additional frame presentation to be executed. This is used to // work around a windows presentation bug (See Bug 1232042) - async ForcePresent(); + async ForcePresent(RenderReasons aReasons); sync StartFrameTimeRecording(int32_t bufferSize) returns (uint32_t startIndex); diff --git a/gfx/layers/ipc/PWebRenderBridge.ipdl b/gfx/layers/ipc/PWebRenderBridge.ipdl index 6ce05d9ab541..85d540fa756d 100644 --- a/gfx/layers/ipc/PWebRenderBridge.ipdl +++ b/gfx/layers/ipc/PWebRenderBridge.ipdl @@ -22,6 +22,7 @@ using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h"; using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::RenderReasons from "mozilla/webrender/webrender_ffi.h"; using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::ExternalImageKeyPair from "mozilla/webrender/WebRenderTypes.h"; @@ -70,7 +71,7 @@ parent: // Invalidate rendered frame async InvalidateRenderedFrame(); // Schedule a composite if one isn't already scheduled. - async ScheduleComposite(); + async ScheduleComposite(RenderReasons aReasons); // Save the frame capture to disk async Capture(); diff --git a/gfx/layers/ipc/UiCompositorControllerParent.cpp b/gfx/layers/ipc/UiCompositorControllerParent.cpp index df4e1f554fec..2cb079c3ea09 100644 --- a/gfx/layers/ipc/UiCompositorControllerParent.cpp +++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp @@ -95,7 +95,7 @@ UiCompositorControllerParent::RecvInvalidateAndRender() { mRootLayerTreeId); if (parent) { parent->Invalidate(); - parent->ScheduleComposition(); + parent->ScheduleComposition(wr::RenderReasons::OTHER); } return IPC_OK(); } @@ -141,7 +141,7 @@ UiCompositorControllerParent::RecvRequestScreenPixels() { if (state && state->mWrBridge) { state->mWrBridge->RequestScreenPixels(this); - state->mWrBridge->ScheduleForcedGenerateFrame(); + state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER); } #endif // defined(MOZ_WIDGET_ANDROID) diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 6590fb1056e8..2cfad38859f0 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -338,6 +338,7 @@ WebRenderBridgeParent::WebRenderBridgeParent( mScreenPixelsTarget(nullptr), #endif mBlobTileSize(256), + mSkippedCompositeReasons(wr::RenderReasons::NONE), mDestroyed(false), mReceivedDisplayList(false), mIsFirstPaint(true), @@ -963,7 +964,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvUpdateResources( // There are resource updates, then we update Epoch of transaction. txn.UpdateEpoch(mPipelineId, mWrEpoch); mAsyncImageManager->SetWillGenerateFrame(); - ScheduleGenerateFrame(); + ScheduleGenerateFrame(wr::RenderReasons::RESOURCE_UPDATE); } else { // If TransactionBuilder does not have resource updates nor display list, // ScheduleGenerateFrame is not triggered via SceneBuilder and there is no @@ -1413,6 +1414,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEmptyTransaction( UpdateAPZFocusState(aFocusTarget); bool scheduleAnyComposite = false; + wr::RenderReasons renderReasons = wr::RenderReasons::NONE; if (aTransactionData) { bool scheduleComposite = false; @@ -1425,6 +1427,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEmptyTransaction( return IPC_FAIL(this, "Failed to process empty transaction update."); } scheduleAnyComposite = scheduleAnyComposite || scheduleComposite; + renderReasons |= wr::RenderReasons::RESOURCE_UPDATE; } // If we are going to kick off a new composite as a result of this @@ -1446,7 +1449,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEmptyTransaction( /* aUseForTelemetry */ scheduleAnyComposite); if (scheduleAnyComposite) { - ScheduleGenerateFrame(); + ScheduleGenerateFrame(renderReasons); } else if (sendDidComposite) { // The only thing in the pending transaction id queue should be the entry // we just added, and now we're going to pretend we rendered it @@ -1537,7 +1540,7 @@ bool WebRenderBridgeParent::ProcessWebRenderParentCommands( case WebRenderParentCommand::TOpUpdatedAsyncImagePipeline: { const OpUpdatedAsyncImagePipeline& op = cmd.get_OpUpdatedAsyncImagePipeline(); - aTxn.InvalidateRenderedFrame(); + aTxn.InvalidateRenderedFrame(wr::RenderReasons::ASYNC_IMAGE); mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(), aTxn, txnForImageBridge); break; @@ -1602,10 +1605,10 @@ void WebRenderBridgeParent::FlushSceneBuilds() { // shouldn't be calling this function all that much in production so this // is probably fine. If it becomes an issue we can add more state tracking // machinery to optimize it away. - ScheduleGenerateFrame(); + ScheduleGenerateFrame(wr::RenderReasons::FLUSH); } -void WebRenderBridgeParent::FlushFrameGeneration() { +void WebRenderBridgeParent::FlushFrameGeneration(wr::RenderReasons aReasons) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(IsRootWebRenderBridgeParent()); // This function is only useful on // the root WRBP @@ -1616,7 +1619,7 @@ void WebRenderBridgeParent::FlushFrameGeneration() { mCompositorScheduler->CancelCurrentCompositeTask(); // Update timestamp of scheduler for APZ and animation. mCompositorScheduler->UpdateLastComposeTime(); - MaybeGenerateFrame(VsyncId(), /* aForceGenerateFrame */ true); + MaybeGenerateFrame(VsyncId(), /* aForceGenerateFrame */ true, aReasons); } } @@ -1637,7 +1640,7 @@ void WebRenderBridgeParent::DisableNativeCompositor() { // Disable WebRender's native compositor usage mApi->EnableNativeCompositor(false); // Ensure we generate and render a frame immediately. - ScheduleForcedGenerateFrame(); + ScheduleForcedGenerateFrame(wr::RenderReasons::CONFIG_CHANGE); mDisablingNativeCompositor = true; } @@ -1782,7 +1785,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvGetSnapshot( MOZ_ASSERT((uint32_t)(size.width * 4) == stride); FlushSceneBuilds(); - FlushFrameGeneration(); + FlushFrameGeneration(wr::RenderReasons::SNAPSHOT); mApi->Readback(start, size, bufferTexture->GetFormat(), Range(buffer, buffer_size), aNeedsYFlip); @@ -1923,7 +1926,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvClearCachedResources() { mApi->SendTransaction(txn); // Schedule generate frame to clean up Pipeline - ScheduleGenerateFrame(); + ScheduleGenerateFrame(wr::RenderReasons::CLEAR_RESOURCES); ClearAnimationResources(); @@ -1985,11 +1988,12 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvInvalidateRenderedFrame() { wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()), IsRootWebRenderBridgeParent()); - InvalidateRenderedFrame(); + InvalidateRenderedFrame(wr::RenderReasons::WIDGET); return IPC_OK(); } -mozilla::ipc::IPCResult WebRenderBridgeParent::RecvScheduleComposite() { +mozilla::ipc::IPCResult WebRenderBridgeParent::RecvScheduleComposite( + const wr::RenderReasons& aReasons) { LOG("WebRenderBridgeParent::RecvScheduleComposite() PipelineId %" PRIx64 " Id %" PRIx64 " root %d", wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()), @@ -1997,27 +2001,29 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvScheduleComposite() { // Caller of LayerManager::ScheduleComposite() expects that it trigger // composite. Then we do not want to skip generate frame. - ScheduleForcedGenerateFrame(); + ScheduleForcedGenerateFrame(aReasons); return IPC_OK(); } -void WebRenderBridgeParent::InvalidateRenderedFrame() { +void WebRenderBridgeParent::InvalidateRenderedFrame( + wr::RenderReasons aReasons) { if (mDestroyed) { return; } wr::TransactionBuilder fastTxn(mApi, /* aUseSceneBuilderThread */ false); - fastTxn.InvalidateRenderedFrame(); + fastTxn.InvalidateRenderedFrame(aReasons); mApi->SendTransaction(fastTxn); } -void WebRenderBridgeParent::ScheduleForcedGenerateFrame() { +void WebRenderBridgeParent::ScheduleForcedGenerateFrame( + wr::RenderReasons aReasons) { if (mDestroyed) { return; } - InvalidateRenderedFrame(); - ScheduleGenerateFrame(); + InvalidateRenderedFrame(aReasons); + ScheduleGenerateFrame(aReasons); } mozilla::ipc::IPCResult WebRenderBridgeParent::RecvCapture() { @@ -2050,7 +2056,7 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSyncWithCompositor() { FlushSceneBuilds(); if (RefPtr root = GetRootWebRenderBridgeParent()) { - root->FlushFrameGeneration(); + root->FlushFrameGeneration(wr::RenderReasons::CONTENT_SYNC); } FlushFramePresentation(); // Finally, we force the AsyncImagePipelineManager to handle all the @@ -2194,12 +2200,14 @@ void WebRenderBridgeParent::CompositeIfNeeded() { if (mSkippedComposite) { mSkippedComposite = false; if (mCompositorScheduler) { - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(mSkippedCompositeReasons); } + mSkippedCompositeReasons = wr::RenderReasons::NONE; } } void WebRenderBridgeParent::CompositeToTarget(VsyncId aId, + wr::RenderReasons aReasons, gfx::DrawTarget* aTarget, const gfx::IntRect* aRect) { // This function should only get called in the root WRBP @@ -2235,6 +2243,7 @@ void WebRenderBridgeParent::CompositeToTarget(VsyncId aId, wr::RenderThread::Get()->TooManyPendingFrames(mApi->GetId())) { // Render thread is busy, try next time. mSkippedComposite = true; + mSkippedCompositeReasons = mSkippedCompositeReasons | aReasons; ResetPreviousSampleTime(); // Record that we skipped presenting a frame for @@ -2251,7 +2260,7 @@ void WebRenderBridgeParent::CompositeToTarget(VsyncId aId, } mCompositionOpportunityId = mCompositionOpportunityId.Next(); - MaybeGenerateFrame(aId, /* aForceGenerateFrame */ false); + MaybeGenerateFrame(aId, /* aForceGenerateFrame */ false, aReasons); } TimeDuration WebRenderBridgeParent::GetVsyncInterval() const { @@ -2264,7 +2273,8 @@ TimeDuration WebRenderBridgeParent::GetVsyncInterval() const { } void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, - bool aForceGenerateFrame) { + bool aForceGenerateFrame, + wr::RenderReasons aReasons) { // This function should only get called in the root WRBP MOZ_ASSERT(IsRootWebRenderBridgeParent()); LOG("WebRenderBridgeParent::MaybeGenerateFrame() PipelineId %" PRIx64 @@ -2304,7 +2314,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, // Trigger another CompositeToTarget() call because there might be another // frame that we want to generate after this one. // It will check if we actually want to generate the frame or not. - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(aReasons); } bool generateFrame = mAsyncImageManager->GetAndResetWillGenerateFrame() || @@ -2321,7 +2331,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, if (RefPtr sampler = GetOMTASampler()) { if (sampler->HasAnimations()) { - ScheduleGenerateFrame(); + ScheduleGenerateFrame(wr::RenderReasons::ANIMATED_PROPERTY); } } @@ -2336,7 +2346,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, #endif MOZ_ASSERT(generateFrame); - fastTxn.GenerateFrame(aId); + fastTxn.GenerateFrame(aId, aReasons); mApi->SendTransaction(fastTxn); #if defined(MOZ_WIDGET_ANDROID) @@ -2352,7 +2362,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, mDisablingNativeCompositor = false; // Ensure we generate and render a frame immediately. - ScheduleForcedGenerateFrame(); + ScheduleForcedGenerateFrame(aReasons); } } @@ -2406,7 +2416,7 @@ void WebRenderBridgeParent::NotifyDidSceneBuild( mMostRecentComposite >= lastVsync || ((TimeStamp::Now() - lastVsync).ToMilliseconds() > StaticPrefs::gfx_webrender_late_scenebuild_threshold())) { - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(wr::RenderReasons::SCENE); return; } @@ -2425,13 +2435,14 @@ void WebRenderBridgeParent::NotifyDidSceneBuild( // we did all of display list building and scene building within the // threshold), then don't do an early composite. if (startId == lastVsyncId) { - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(wr::RenderReasons::SCENE); return; } } } - CompositeToTarget(mCompositorScheduler->GetLastVsyncId(), nullptr, nullptr); + CompositeToTarget(mCompositorScheduler->GetLastVsyncId(), + wr::RenderReasons::SCENE, nullptr, nullptr); } static Telemetry::HistogramID GetHistogramId(const bool aIsLargePaint, @@ -2555,14 +2566,15 @@ LayersId WebRenderBridgeParent::GetLayersId() const { return wr::AsLayersId(mPipelineId); } -void WebRenderBridgeParent::ScheduleGenerateFrame() { +void WebRenderBridgeParent::ScheduleGenerateFrame(wr::RenderReasons aReasons) { if (mCompositorScheduler) { mAsyncImageManager->SetWillGenerateFrame(); - mCompositorScheduler->ScheduleComposition(); + mCompositorScheduler->ScheduleComposition(aReasons); } } -void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) { +void WebRenderBridgeParent::FlushRendering(wr::RenderReasons aReasons, + bool aWaitForPresent) { if (mDestroyed) { return; } @@ -2570,7 +2582,7 @@ void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) { // This gets called during e.g. window resizes, so we need to flush the // scene (which has the display list at the new window size). FlushSceneBuilds(); - FlushFrameGeneration(); + FlushFrameGeneration(aReasons); if (aWaitForPresent) { FlushFramePresentation(); } @@ -2616,7 +2628,7 @@ bool WebRenderBridgeParent::Resume() { } // Ensure we generate and render a frame immediately. - ScheduleForcedGenerateFrame(); + ScheduleForcedGenerateFrame(wr::RenderReasons::WIDGET); return true; } @@ -2633,7 +2645,7 @@ void WebRenderBridgeParent::ClearResources() { wr::Epoch wrEpoch = GetNextWrEpoch(); mReceivedDisplayList = false; // Schedule generate frame to clean up Pipeline - ScheduleGenerateFrame(); + ScheduleGenerateFrame(wr::RenderReasons::CLEAR_RESOURCES); // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction. for (const auto& entry : mTextureHosts) { diff --git a/gfx/layers/wr/WebRenderBridgeParent.h b/gfx/layers/wr/WebRenderBridgeParent.h index 25cbb7a600a0..02c32afd4cde 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.h +++ b/gfx/layers/wr/WebRenderBridgeParent.h @@ -143,7 +143,8 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, mozilla::ipc::IPCResult RecvClearCachedResources() override; mozilla::ipc::IPCResult RecvInvalidateRenderedFrame() override; - mozilla::ipc::IPCResult RecvScheduleComposite() override; + mozilla::ipc::IPCResult RecvScheduleComposite( + const wr::RenderReasons& aReasons) override; mozilla::ipc::IPCResult RecvCapture() override; mozilla::ipc::IPCResult RecvStartCaptureSequence( const nsCString& path, const uint32_t& aFlags) override; @@ -182,7 +183,8 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, // CompositorVsyncSchedulerOwner bool IsPendingComposite() override { return false; } void FinishPendingComposite() override {} - void CompositeToTarget(VsyncId aId, gfx::DrawTarget* aTarget, + void CompositeToTarget(VsyncId aId, wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override; TimeDuration GetVsyncInterval() const override; @@ -240,7 +242,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, return aFontKey.mNamespace == mIdNamespace; } - void FlushRendering(bool aWaitForPresent = true); + void FlushRendering(wr::RenderReasons aReasons, bool aWaitForPresent = true); /** * Schedule generating WebRender frame definitely at next composite timing. @@ -253,7 +255,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, * Call CompositorVsyncScheduler::ScheduleComposition() directly, if we just * want to trigger AsyncImagePipelines update checks. */ - void ScheduleGenerateFrame(); + void ScheduleGenerateFrame(wr::RenderReasons aReason); /** * Invalidate rendered frame. @@ -261,7 +263,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, * WebRender could skip frame rendering if there is no update. * This function is used to force invalidating even when there is no update. */ - void InvalidateRenderedFrame(); + void InvalidateRenderedFrame(wr::RenderReasons aReasons); /** * Schedule forced frame rendering at next composite timing. @@ -269,7 +271,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, * WebRender could skip frame rendering if there is no update. * This function is used to force rendering even when there is no update. */ - void ScheduleForcedGenerateFrame(); + void ScheduleForcedGenerateFrame(wr::RenderReasons aReasons); void NotifyDidSceneBuild(RefPtr aInfo); @@ -412,10 +414,11 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, void RollbackWrEpoch(); void FlushSceneBuilds(); - void FlushFrameGeneration(); + void FlushFrameGeneration(wr::RenderReasons aReasons); void FlushFramePresentation(); - void MaybeGenerateFrame(VsyncId aId, bool aForceGenerateFrame); + void MaybeGenerateFrame(VsyncId aId, bool aForceGenerateFrame, + wr::RenderReasons aReasons); VsyncId GetVsyncIdForEpoch(const wr::Epoch& aEpoch) { for (auto& id : mPendingTransactionIds) { @@ -504,6 +507,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, #endif uint32_t mBoolParameterBits; uint16_t mBlobTileSize; + wr::RenderReasons mSkippedCompositeReasons; bool mDestroyed; bool mReceivedDisplayList; bool mIsFirstPaint; diff --git a/gfx/layers/wr/WebRenderImageHost.cpp b/gfx/layers/wr/WebRenderImageHost.cpp index 94d0955a31c8..e531a9a8c29f 100644 --- a/gfx/layers/wr/WebRenderImageHost.cpp +++ b/gfx/layers/wr/WebRenderImageHost.cpp @@ -69,7 +69,8 @@ void WebRenderImageHost::UseTextureHost( for (const auto& it : mWrBridges) { RefPtr wrBridge = it.second->WrBridge(); if (wrBridge && wrBridge->CompositorScheduler()) { - wrBridge->CompositorScheduler()->ScheduleComposition(); + wrBridge->CompositorScheduler()->ScheduleComposition( + wr::RenderReasons::ASYNC_IMAGE); } } } diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp index 5872210f6976..029b4e424491 100644 --- a/gfx/layers/wr/WebRenderLayerManager.cpp +++ b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -685,7 +685,7 @@ void WebRenderLayerManager::RemoveDidCompositeObserver( mDidCompositeObservers.RemoveElement(aObserver); } -void WebRenderLayerManager::FlushRendering() { +void WebRenderLayerManager::FlushRendering(wr::RenderReasons aReasons) { CompositorBridgeChild* cBridge = GetCompositorBridgeChild(); if (!cBridge) { return; @@ -696,15 +696,19 @@ void WebRenderLayerManager::FlushRendering() { // might happen. bool resizing = mWidget && mWidget->IsResizingNativeWidget().valueOr(true); + if (resizing) { + aReasons = aReasons | wr::RenderReasons::RESIZE; + } + // Limit async FlushRendering to !resizing and Win DComp. // XXX relax the limitation if (WrBridge()->GetCompositorUseDComp() && !resizing) { - cBridge->SendFlushRenderingAsync(); + cBridge->SendFlushRenderingAsync(aReasons); } else if (mWidget->SynchronouslyRepaintOnResize() || StaticPrefs::layers_force_synchronous_resize()) { - cBridge->SendFlushRendering(); + cBridge->SendFlushRendering(aReasons); } else { - cBridge->SendFlushRenderingAsync(); + cBridge->SendFlushRenderingAsync(aReasons); } } @@ -731,8 +735,8 @@ void WebRenderLayerManager::SendInvalidRegion(const nsIntRegion& aRegion) { } } -void WebRenderLayerManager::ScheduleComposite() { - WrBridge()->SendScheduleComposite(); +void WebRenderLayerManager::ScheduleComposite(wr::RenderReasons aReasons) { + WrBridge()->SendScheduleComposite(aReasons); } already_AddRefed diff --git a/gfx/layers/wr/WebRenderLayerManager.h b/gfx/layers/wr/WebRenderLayerManager.h index 851066f8fe79..2558d4be5f57 100644 --- a/gfx/layers/wr/WebRenderLayerManager.h +++ b/gfx/layers/wr/WebRenderLayerManager.h @@ -110,12 +110,12 @@ class WebRenderLayerManager final : public WindowRenderer { void AddDidCompositeObserver(DidCompositeObserver* aObserver); void RemoveDidCompositeObserver(DidCompositeObserver* aObserver); - void FlushRendering() override; + void FlushRendering(wr::RenderReasons aReasons) override; void WaitOnTransactionProcessed() override; void SendInvalidRegion(const nsIntRegion& aRegion); - void ScheduleComposite(); + void ScheduleComposite(wr::RenderReasons aReasons); void SetNeedsComposite(bool aNeedsComposite) { mNeedsComposite = aNeedsComposite; diff --git a/gfx/layers/wr/WebRenderMessageUtils.h b/gfx/layers/wr/WebRenderMessageUtils.h index ac30e5e4d3c8..3e7f31f46092 100644 --- a/gfx/layers/wr/WebRenderMessageUtils.h +++ b/gfx/layers/wr/WebRenderMessageUtils.h @@ -145,6 +145,10 @@ template <> struct ParamTraits : public PlainOldDataSerializer {}; +template <> +struct ParamTraits + : public PlainOldDataSerializer {}; + } // namespace IPC #endif // GFX_WEBRENDERMESSAGEUTILS_H diff --git a/gfx/webrender_bindings/RenderThread.cpp b/gfx/webrender_bindings/RenderThread.cpp index f30d4db6c9a1..2289e8e7222d 100644 --- a/gfx/webrender_bindings/RenderThread.cpp +++ b/gfx/webrender_bindings/RenderThread.cpp @@ -1234,17 +1234,19 @@ void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId, std::move(evt)); } -static void NotifyScheduleRender(mozilla::wr::WrWindowId aWindowId) { +static void NotifyScheduleRender(mozilla::wr::WrWindowId aWindowId, + wr::RenderReasons aReasons) { RefPtr cbp = mozilla::layers:: CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId); if (cbp) { - cbp->ScheduleComposition(); + cbp->ScheduleComposition(aReasons); } } -void wr_schedule_render(mozilla::wr::WrWindowId aWindowId) { +void wr_schedule_render(mozilla::wr::WrWindowId aWindowId, + wr::RenderReasons aReasons) { layers::CompositorThread()->Dispatch(NewRunnableFunction( - "NotifyScheduleRender", &NotifyScheduleRender, aWindowId)); + "NotifyScheduleRender", &NotifyScheduleRender, aWindowId, aReasons)); } static void NotifyDidSceneBuild( diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index b9a2a7a93d68..adabcc1639dc 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -276,12 +276,13 @@ void TransactionBuilder::ClearDisplayList(Epoch aEpoch, wr_transaction_clear_display_list(mTxn, aEpoch, aPipelineId); } -void TransactionBuilder::GenerateFrame(const VsyncId& aVsyncId) { - wr_transaction_generate_frame(mTxn, aVsyncId.mId); +void TransactionBuilder::GenerateFrame(const VsyncId& aVsyncId, + wr::RenderReasons aReasons) { + wr_transaction_generate_frame(mTxn, aVsyncId.mId, aReasons); } -void TransactionBuilder::InvalidateRenderedFrame() { - wr_transaction_invalidate_rendered_frame(mTxn); +void TransactionBuilder::InvalidateRenderedFrame(wr::RenderReasons aReasons) { + wr_transaction_invalidate_rendered_frame(mTxn, aReasons); } bool TransactionBuilder::IsEmpty() const { diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index 6c41cd54837a..a6f213124557 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -119,9 +119,9 @@ class TransactionBuilder final { void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline); - void GenerateFrame(const VsyncId& aVsyncId); + void GenerateFrame(const VsyncId& aVsyncId, wr::RenderReasons aReasons); - void InvalidateRenderedFrame(); + void InvalidateRenderedFrame(wr::RenderReasons aReasons); void SetDocumentView(const LayoutDeviceIntRect& aDocRect); diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index 6147f1808276..829fbc153273 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -530,7 +530,7 @@ extern "C" { fn wr_notifier_new_frame_ready(window_id: WrWindowId); 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); + fn wr_schedule_render(window_id: WrWindowId, reasons: RenderReasons); // NOTE: This moves away from pipeline_info. fn wr_finished_scene_build(window_id: WrWindowId, pipeline_info: &mut WrPipelineInfo); @@ -986,7 +986,7 @@ impl SceneBuilderHooks for APZCallbacks { } fn post_resource_update(&self, _document_ids: &Vec) { - unsafe { wr_schedule_render(self.window_id) } + unsafe { wr_schedule_render(self.window_id, RenderReasons::POST_RESOURCE_UPDATES_HOOK) } unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); } @@ -1872,13 +1872,13 @@ pub extern "C" fn wr_transaction_set_document_view(txn: &mut Transaction, doc_re } #[no_mangle] -pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction, id: u64) { - txn.generate_frame(id); +pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction, id: u64, reasons: RenderReasons) { + txn.generate_frame(id, reasons); } #[no_mangle] -pub extern "C" fn wr_transaction_invalidate_rendered_frame(txn: &mut Transaction) { - txn.invalidate_rendered_frame(); +pub extern "C" fn wr_transaction_invalidate_rendered_frame(txn: &mut Transaction, reasons: RenderReasons) { + txn.invalidate_rendered_frame(reasons); } fn wr_animation_properties_into_vec( diff --git a/gfx/wr/example-compositor/compositor/src/main.rs b/gfx/wr/example-compositor/compositor/src/main.rs index 138ea8c11809..bad6d246a64f 100644 --- a/gfx/wr/example-compositor/compositor/src/main.rs +++ b/gfx/wr/example-compositor/compositor/src/main.rs @@ -487,7 +487,7 @@ fn main() { ); } - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(document_id, txn); // Tick the compositor (in this sample, we don't block on UI events) @@ -534,7 +534,7 @@ fn main() { } } - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(document_id, txn); current_epoch.0 += 1; time += 0.001; diff --git a/gfx/wr/examples/animation.rs b/gfx/wr/examples/animation.rs index d96c7316f6b6..53e486f8ab9e 100644 --- a/gfx/wr/examples/animation.rs +++ b/gfx/wr/examples/animation.rs @@ -200,7 +200,7 @@ impl Example for App { colors: vec![], }, ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(document_id, txn); } _ => (), diff --git a/gfx/wr/examples/common/boilerplate.rs b/gfx/wr/examples/common/boilerplate.rs index 7af57675e94c..38f5f78db061 100644 --- a/gfx/wr/examples/common/boilerplate.rs +++ b/gfx/wr/examples/common/boilerplate.rs @@ -214,7 +214,7 @@ pub fn main_wrapper( builder.end(), ); txn.set_root_pipeline(pipeline_id); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(document_id, txn); println!("Entering event loop"); @@ -303,7 +303,7 @@ pub fn main_wrapper( layout_size, builder.end(), ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); } api.send_transaction(document_id, txn); diff --git a/gfx/wr/examples/document.rs b/gfx/wr/examples/document.rs index db4c76c1a668..6579189c81a6 100644 --- a/gfx/wr/examples/document.rs +++ b/gfx/wr/examples/document.rs @@ -124,7 +124,7 @@ impl Example for App { doc.content_rect.size(), builder.end(), ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(doc.id, txn); } } diff --git a/gfx/wr/examples/image_resize.rs b/gfx/wr/examples/image_resize.rs index 50d821a7b898..1e84a7c4a50a 100644 --- a/gfx/wr/examples/image_resize.rs +++ b/gfx/wr/examples/image_resize.rs @@ -104,7 +104,7 @@ impl Example for App { &DirtyRect::All, ); let mut txn = Transaction::new(); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(document_id, txn); } _ => {} diff --git a/gfx/wr/examples/multiwindow.rs b/gfx/wr/examples/multiwindow.rs index 1ebc53099f0f..a86af760ea18 100644 --- a/gfx/wr/examples/multiwindow.rs +++ b/gfx/wr/examples/multiwindow.rs @@ -284,7 +284,7 @@ impl Window { builder.end(), ); txn.set_root_pipeline(self.pipeline_id); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); api.send_transaction(self.document_id, txn); renderer.update(); diff --git a/gfx/wr/examples/scrolling.rs b/gfx/wr/examples/scrolling.rs index b840f204c765..d4d54e085bea 100644 --- a/gfx/wr/examples/scrolling.rs +++ b/gfx/wr/examples/scrolling.rs @@ -190,7 +190,7 @@ impl Example for App { ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), ScrollClamping::ToContentBounds, ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); } } winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => { @@ -211,7 +211,7 @@ impl Example for App { ScrollClamping::ToContentBounds, ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::empty()); } winit::WindowEvent::MouseInput { .. } => { let results = api.hit_test( diff --git a/gfx/wr/webrender/src/internal_types.rs b/gfx/wr/webrender/src/internal_types.rs index 9e4b91657569..c9db561609e5 100644 --- a/gfx/wr/webrender/src/internal_types.rs +++ b/gfx/wr/webrender/src/internal_types.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::{ColorF, DocumentId, ExternalImageId, PrimitiveFlags, Parameter}; +use api::{ColorF, DocumentId, ExternalImageId, PrimitiveFlags, Parameter, RenderReasons}; use api::{ImageFormat, NotificationRequest, Shadow, FilterOp, ImageBufferKind}; use api::units::*; use api; @@ -653,6 +653,7 @@ pub struct RenderedDocument { pub frame: Frame, pub is_new_scene: bool, pub profile: TransactionProfile, + pub render_reasons: RenderReasons, pub frame_stats: Option } diff --git a/gfx/wr/webrender/src/profiler.rs b/gfx/wr/webrender/src/profiler.rs index ac011c89f4d1..c55c823e19dc 100644 --- a/gfx/wr/webrender/src/profiler.rs +++ b/gfx/wr/webrender/src/profiler.rs @@ -93,6 +93,8 @@ static PROFILER_PRESETS: &'static[(&'static str, &'static str)] = &[ (&"Interners", "Interned primitives,Interned clips,Interned pictures,Interned text runs,Interned normal borders,Interned image borders,Interned images,Interned YUV images,Interned line decorations,Interned linear gradients,Interned radial gradients,Interned conic gradients,Interned filter data,Interned backdrops"), // Gpu sampler queries (need the pref gfx.webrender.debug.gpu-sampler-queries). (&"GPU samplers", &"Alpha targets samplers,Transparent pass samplers,Opaque pass samplers,Total samplers"), + + (&"Render reasons", &"Reason scene, Reason animated propert, Reason resource update, Reason async image, Reason clear resources, Reason APZ, Reason resize, Reason widget, Reason cache flush, Reason snapshot, Reason resource hook, Reason config change, Reason content sync, Reason flush, Reason vsync, Reason testing, Reason other"), ]; fn find_preset(name: &str) -> Option<&'static str> { @@ -231,7 +233,26 @@ pub const DEPTH_TARGETS_MEM: usize = 100; pub const SHADER_BUILD_TIME: usize = 101; -pub const NUM_PROFILER_EVENTS: usize = 102; +pub const RENDER_REASON_FIRST: usize = 102; +pub const RENDER_REASON_SCENE: usize = 102; +pub const RENDER_REASON_ANIMATED_PROPERTY: usize = 103; +pub const RENDER_REASON_RESOURCE_UPDATE: usize = 104; +pub const RENDER_REASON_ASYNC_IMAGE: usize = 105; +pub const RENDER_REASON_CLEAR_RESOURCES: usize = 106; +pub const RENDER_REASON_APZ: usize = 107; +pub const RENDER_REASON_RESIZE: usize = 108; +pub const RENDER_REASON_WIDGET: usize = 109; +pub const RENDER_REASON_TEXTURE_CACHE_FLUSH: usize = 110; +pub const RENDER_REASON_SNAPSHOT: usize = 111; +pub const RENDER_REASON_POST_RESOURCE_UPDATE_HOOKS: usize = 112; +pub const RENDER_REASON_CONFIG_CHANGE: usize = 113; +pub const RENDER_REASON_CONTENT_SYNC: usize = 114; +pub const RENDER_REASON_FLUSH: usize = 115; +pub const RENDER_REASON_TESTING: usize = 116; +pub const RENDER_REASON_OTHER: usize = 117; +pub const RENDER_REASON_VSYNC: usize = 118; + +pub const NUM_PROFILER_EVENTS: usize = 119; pub struct Profiler { counters: Vec, @@ -386,6 +407,25 @@ impl Profiler { float("Depth targets mem", "MB", DEPTH_TARGETS_MEM, Expected::none()), float("Shader build time", "ms", SHADER_BUILD_TIME, Expected::none()), + // We use the expected range to highlight render reasons that are happening. + float("Reason scene", "", RENDER_REASON_SCENE, expected(0.0..0.01)), + float("Reason animated propert", "", RENDER_REASON_ANIMATED_PROPERTY, expected(0.0..0.01)), + float("Reason resource update", "", RENDER_REASON_RESOURCE_UPDATE, expected(0.0..0.01)), + float("Reason async image", "", RENDER_REASON_ASYNC_IMAGE, expected(0.0..0.01)), + float("Reason clear resources", "", RENDER_REASON_CLEAR_RESOURCES, expected(0.0..0.01)), + float("Reason APZ", "", RENDER_REASON_APZ, expected(0.0..0.01)), + float("Reason resize", "", RENDER_REASON_RESIZE, expected(0.0..0.01)), + float("Reason widget", "", RENDER_REASON_WIDGET, expected(0.0..0.01)), + float("Reason texture cache flush", "", RENDER_REASON_TEXTURE_CACHE_FLUSH, expected(0.0..0.01)), + float("Reason snapshot", "", RENDER_REASON_SNAPSHOT, expected(0.0..0.01)), + float("Reason resource hook", "", RENDER_REASON_POST_RESOURCE_UPDATE_HOOKS, expected(0.0..0.01)), + float("Reason config change", "", RENDER_REASON_CONFIG_CHANGE, expected(0.0..0.01)), + float("Reason content sync", "", RENDER_REASON_CONTENT_SYNC, expected(0.0..0.01)), + float("Reason flush", "", RENDER_REASON_FLUSH, expected(0.0..0.01)), + float("Reason testing", "", RENDER_REASON_TESTING, expected(0.0..0.01)), + float("Reason other", "", RENDER_REASON_OTHER, expected(0.0..0.01)), + float("On vsync", "", RENDER_REASON_VSYNC, expected(0.0..0.01)), + ]; let mut counters = Vec::with_capacity(profile_counters.len()); diff --git a/gfx/wr/webrender/src/render_api.rs b/gfx/wr/webrender/src/render_api.rs index 03a8904be80b..5057bd364d2b 100644 --- a/gfx/wr/webrender/src/render_api.rs +++ b/gfx/wr/webrender/src/render_api.rs @@ -21,7 +21,7 @@ use crate::api::{DocumentId, PipelineId, PropertyBindingId, PropertyBindingKey, use crate::api::{HitTestResult, HitTesterRequest, ApiHitTester, PropertyValue, DynamicProperties}; use crate::api::{ScrollClamping, TileSize, NotificationRequest, DebugFlags}; use crate::api::{GlyphDimensionRequest, GlyphIndexRequest, GlyphIndex, GlyphDimensions}; -use crate::api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation}; +use crate::api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation, RenderReasons}; use crate::api::DEFAULT_TILE_SIZE; use crate::api::units::*; use crate::api_resources::ApiResources; @@ -174,6 +174,9 @@ pub struct Transaction { pub invalidate_rendered_frame: bool, low_priority: bool, + + /// + pub render_reasons: RenderReasons, } impl Transaction { @@ -188,6 +191,7 @@ impl Transaction { generate_frame: GenerateFrame::No, invalidate_rendered_frame: false, low_priority: false, + render_reasons: RenderReasons::empty(), } } @@ -358,8 +362,9 @@ impl Transaction { /// as to when happened. /// /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready - pub fn generate_frame(&mut self, id: u64) { + pub fn generate_frame(&mut self, id: u64, reasons: RenderReasons) { self.generate_frame = GenerateFrame::Yes{ id }; + self.render_reasons |= reasons; } /// Invalidate rendered frame. It ensure that frame will be rendered during @@ -368,8 +373,9 @@ impl Transaction { /// But there are cases that needs to force rendering. /// - Content of image is updated by reusing same ExternalImageId. /// - Platform requests it if pixels become stale (like wakeup from standby). - pub fn invalidate_rendered_frame(&mut self) { + pub fn invalidate_rendered_frame(&mut self, reasons: RenderReasons) { self.invalidate_rendered_frame = true; + self.render_reasons |= reasons } /// Reset the list of animated property bindings that should be used to resolve @@ -412,6 +418,7 @@ impl Transaction { blob_requests: Vec::new(), rasterized_blobs: Vec::new(), profile: TransactionProfile::new(), + render_reasons: self.render_reasons, }) } @@ -597,6 +604,8 @@ pub struct TransactionMsg { pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>, /// Collect various data along the rendering pipeline to display it in the embedded profiler. pub profile: TransactionProfile, + /// Keep track of who asks rendering to happen. + pub render_reasons: RenderReasons, } impl fmt::Debug for TransactionMsg { @@ -1219,6 +1228,7 @@ impl RenderApi { blob_requests: Vec::new(), rasterized_blobs: Vec::new(), profile: TransactionProfile::new(), + render_reasons: RenderReasons::empty(), }) } diff --git a/gfx/wr/webrender/src/render_backend.rs b/gfx/wr/webrender/src/render_backend.rs index bfddd2637dd4..fd1a6ae694d1 100644 --- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -12,7 +12,7 @@ use api::{DebugFlags, BlobImageHandler, Parameter, BoolParameter}; use api::{DocumentId, ExternalScrollId, HitTestResult}; use api::{IdNamespace, PipelineId, RenderNotifier, ScrollClamping}; use api::{NotificationRequest, Checkpoint, QualitySettings}; -use api::{PrimitiveKeyKind}; +use api::{PrimitiveKeyKind, RenderReasons}; use api::units::*; use api::channel::{single_msg_channel, Sender, Receiver}; #[cfg(any(feature = "capture", feature = "replay"))] @@ -436,7 +436,8 @@ impl Document { debug_flags: DebugFlags, tile_cache_logger: &mut TileCacheLogger, tile_caches: &mut FastHashMap>, - frame_stats: Option + frame_stats: Option, + render_reasons: RenderReasons, ) -> RenderedDocument { let frame_build_start_time = precise_time_ns(); @@ -487,7 +488,8 @@ impl Document { frame, is_new_scene, profile: self.profile.take_and_reset(), - frame_stats: frame_stats + frame_stats: frame_stats, + render_reasons, } } @@ -862,6 +864,7 @@ impl RenderBackend { txn.frame_ops.take(), txn.notifications.take(), txn.render_frame, + RenderReasons::SCENE, None, txn.invalidate_rendered_frame, frame_counter, @@ -1204,6 +1207,7 @@ impl RenderBackend { txn.frame_ops.take(), txn.notifications.take(), txn.generate_frame.as_bool(), + txn.render_reasons, txn.generate_frame.id(), txn.invalidate_rendered_frame, frame_counter, @@ -1241,6 +1245,7 @@ impl RenderBackend { Vec::default(), Vec::default(), false, + RenderReasons::empty(), None, false, frame_counter, @@ -1261,6 +1266,7 @@ impl RenderBackend { mut frame_ops: Vec, mut notifications: Vec, mut render_frame: bool, + render_reasons: RenderReasons, generated_frame_id: Option, invalidate_rendered_frame: bool, frame_counter: &mut u32, @@ -1352,7 +1358,8 @@ impl RenderBackend { self.debug_flags, &mut self.tile_cache_logger, &mut self.tile_caches, - frame_stats + frame_stats, + render_reasons, ); debug!("generated frame for document {:?} with {} passes", @@ -1544,6 +1551,7 @@ impl RenderBackend { &mut self.tile_cache_logger, &mut self.tile_caches, None, + RenderReasons::empty(), ); // After we rendered the frames, there are pending updates to both // GPU cache and resources. Instead of serializing them, we are going to make sure @@ -1814,7 +1822,13 @@ impl RenderBackend { let msg_publish = ResultMsg::PublishDocument( id, - RenderedDocument { frame, is_new_scene: true, profile: TransactionProfile::new(), frame_stats: None }, + RenderedDocument { + frame, + is_new_scene: true, + profile: TransactionProfile::new(), + render_reasons: RenderReasons::empty(), + frame_stats: None, + }, self.resource_cache.pending_updates(), ); self.result_tx.send(msg_publish).unwrap(); diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs index 1256847a094f..e5dda1d1f0d8 100644 --- a/gfx/wr/webrender/src/renderer/mod.rs +++ b/gfx/wr/webrender/src/renderer/mod.rs @@ -35,7 +35,7 @@ //! calling `DrawTarget::to_framebuffer_rect` use api::{BlobImageHandler, ColorF, ColorU, MixBlendMode}; -use api::{DocumentId, Epoch, ExternalImageHandler}; +use api::{DocumentId, Epoch, ExternalImageHandler, RenderReasons}; use api::CrashAnnotator; #[cfg(feature = "replay")] use api::ExternalImageId; @@ -1502,6 +1502,7 @@ impl Renderer { doc.profile.merge(&mut prev_doc.profile); if prev_doc.frame.must_be_drawn() { + prev_doc.render_reasons |= RenderReasons::TEXTURE_CACHE_FLUSH; self.render_impl( document_id, &mut prev_doc, @@ -2092,6 +2093,31 @@ impl Renderer { self.profiler.update_frame_stats(stats); } + // Turn the render reasons bitflags into something we can see in the profiler. + // For now this is just a binary yes/no for each bit, which means that when looking + // at "Render reasons" in the profiler HUD the average view indicates the proportion + // of frames that had the bit set over a half second window whereas max shows whether + // the bit as been set at least once during that time window. + // We could implement better ways to visualize this information. + let add_markers = thread_is_being_profiled(); + for i in 0..RenderReasons::NUM_BITS { + let counter = profiler::RENDER_REASON_FIRST + i as usize; + let mut val = 0.0; + let reason_bit = RenderReasons::from_bits_truncate(1 << i); + if active_doc.render_reasons.contains(reason_bit) { + val = 1.0; + if add_markers { + let event_str = format!("Render reason {:?}", reason_bit); + if let Ok(event_str) = std::ffi::CString::new(&event_str[..]) { + add_event_marker(event_str.as_c_str()); + } + } + } + self.profile.set(counter, val); + } + active_doc.render_reasons = RenderReasons::empty(); + + self.texture_resolver.update_profile(&mut self.profile); // Note: this clears the values in self.profile. diff --git a/gfx/wr/webrender_api/src/lib.rs b/gfx/wr/webrender_api/src/lib.rs index bedfca87ef01..7e3659c3ca97 100644 --- a/gfx/wr/webrender_api/src/lib.rs +++ b/gfx/wr/webrender_api/src/lib.rs @@ -524,6 +524,42 @@ pub enum BoolParameter { DrawCallsForTextureCopy = 3, } +bitflags! { + /// Flags to track why we are rendering. + #[repr(C)] + #[derive(Default, Deserialize, MallocSizeOf, Serialize)] + pub struct RenderReasons: u32 { + /// Equivalent of empty() for the C++ side. + const NONE = 0; + const SCENE = 1 << 0; + const ANIMATED_PROPERTY = 1 << 1; + const RESOURCE_UPDATE = 1 << 2; + const ASYNC_IMAGE = 1 << 3; + const CLEAR_RESOURCES = 1 << 4; + const APZ = 1 << 5; + /// Window resize + const RESIZE = 1 << 6; + /// Various widget-related reasons + const WIDGET = 1 << 7; + /// See Frame::must_be_drawn + const TEXTURE_CACHE_FLUSH = 1 << 8; + const SNAPSHOT = 1 << 9; + const POST_RESOURCE_UPDATES_HOOK = 1 << 10; + const CONFIG_CHANGE = 1 << 11; + const CONTENT_SYNC = 1 << 12; + const FLUSH = 1 << 13; + const TESTING = 1 << 14; + const OTHER = 1 << 15; + /// Vsync isn't actually "why" we render but it can be useful + /// to see which frames were driven by the vsync scheduler so + /// we store a bit for it. + const VSYNC = 1 << 16; + } +} + +impl RenderReasons { + pub const NUM_BITS: u32 = 17; +} bitflags! { /// Flags to enable/disable various builtin debugging tools. diff --git a/gfx/wr/wrench/src/rawtest.rs b/gfx/wr/wrench/src/rawtest.rs index 03b1b00ca606..7be5e4afbef5 100644 --- a/gfx/wr/wrench/src/rawtest.rs +++ b/gfx/wr/wrench/src/rawtest.rs @@ -106,7 +106,7 @@ impl<'a> RawtestHarness<'a> { ); epoch.0 += 1; - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::TESTING); self.wrench.api.send_transaction(self.wrench.document_id, txn); } @@ -1252,7 +1252,7 @@ impl<'a> RawtestHarness<'a> { layout_size, builder.end(), ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::TESTING); self.wrench.api.send_transaction(self.wrench.document_id, txn); @@ -1287,7 +1287,7 @@ impl<'a> RawtestHarness<'a> { // 6. rebuild the scene and compare again let mut txn = Transaction::new(); txn.set_root_pipeline(captured.root_pipeline_id.unwrap()); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::TESTING); self.wrench.api.send_transaction(captured.document_id, txn); let pixels2 = self.render_and_get_pixels(window_rect); self.compare_pixels(pixels0, pixels2, window_rect.size()); @@ -1319,7 +1319,7 @@ impl<'a> RawtestHarness<'a> { layout_size, builder.end(), ); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::TESTING); self.wrench.api.send_transaction(doc_id, txn); // Ensure we get a notification from rendering the above, even though diff --git a/gfx/wr/wrench/src/wrench.rs b/gfx/wr/wrench/src/wrench.rs index b0b294818dcc..be5934b00f2f 100644 --- a/gfx/wr/wrench/src/wrench.rs +++ b/gfx/wr/wrench/src/wrench.rs @@ -580,7 +580,7 @@ impl Wrench { txn.scroll_node_with_id(*offset, *id, ScrollClamping::NoClamping); } - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::TESTING); self.api.send_transaction(self.document_id, txn); } @@ -601,7 +601,7 @@ impl Wrench { pub fn refresh(&mut self) { self.begin_frame(); let mut txn = Transaction::new(); - txn.generate_frame(0); + txn.generate_frame(0, RenderReasons::TESTING); self.api.send_transaction(self.document_id, txn); } diff --git a/layout/painting/WindowRenderer.h b/layout/painting/WindowRenderer.h index 3da8be1656e1..b58ffd307a2a 100644 --- a/layout/painting/WindowRenderer.h +++ b/layout/painting/WindowRenderer.h @@ -7,6 +7,7 @@ #ifndef MOZILLA_PAINTING_WINDOWRENDERER_H #define MOZILLA_PAINTING_WINDOWRENDERER_H +#include "mozilla/webrender/webrender_ffi.h" #include "mozilla/layers/LayersTypes.h" #include "mozilla/dom/Animation.h" // for Animation #include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, ScrollableLayerGuid::ViewID @@ -164,7 +165,7 @@ class WindowRenderer : public FrameRecorder { * Note: This may sychronously wait on a remote compositor * to complete rendering. */ - virtual void FlushRendering() {} + virtual void FlushRendering(wr::RenderReasons aReasons) {} /** * Make sure that the previous transaction has been diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp index e22821fa292a..1fcd55aa4afb 100644 --- a/view/nsViewManager.cpp +++ b/view/nsViewManager.cpp @@ -311,7 +311,7 @@ void nsViewManager::Refresh(nsView* aView, #endif WindowRenderer* renderer = widget->GetWindowRenderer(); if (!renderer->NeedsWidgetInvalidation()) { - renderer->FlushRendering(); + renderer->FlushRendering(wr::RenderReasons::WIDGET); } else { presShell->SyncPaintFallback(aView); } diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index cfbd461f32cc..6d35fc47e210 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -3591,7 +3591,7 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { } if (knowsCompositor && layerManager && layerManager->NeedsComposite()) { - layerManager->ScheduleComposite(); + layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); layerManager->SetNeedsComposite(false); } @@ -3863,7 +3863,7 @@ gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget, // frame, and its contents might be incorrect. See bug 1280653 comment 7 // and comment 10. Specifically we must ensure we recomposite the frame // as soon as possible to avoid the corrupted frame being displayed. - GetWindowRenderer()->FlushRendering(); + GetWindowRenderer()->FlushRendering(wr::RenderReasons::WIDGET); return FALSE; } @@ -6042,7 +6042,7 @@ void nsWindow::ResumeCompositorHiddenWindow() { mCompositorState = COMPOSITOR_ENABLED; remoteRenderer->SendResumeAsync(); } - remoteRenderer->SendForcePresent(); + remoteRenderer->SendForcePresent(wr::RenderReasons::WIDGET); } } @@ -6158,7 +6158,7 @@ void nsWindow::ResumeCompositor() { if (remoteRenderer) { mCompositorState = COMPOSITOR_ENABLED; remoteRenderer->SendResumeAsync(); - remoteRenderer->SendForcePresent(); + remoteRenderer->SendForcePresent(wr::RenderReasons::WIDGET); } } diff --git a/widget/windows/CompositorWidgetParent.cpp b/widget/windows/CompositorWidgetParent.cpp index 30b4bf67d076..c114ab7e4600 100644 --- a/widget/windows/CompositorWidgetParent.cpp +++ b/widget/windows/CompositorWidgetParent.cpp @@ -206,7 +206,7 @@ void CompositorWidgetParent::UpdateCompositorWnd(const HWND aCompositorWnd, // Schedule composition after ::SetParent() call in parent // process. layers::CompositorBridgeParent::ScheduleForcedComposition( - self->mRootLayerTreeID.ref()); + self->mRootLayerTreeID.ref(), wr::RenderReasons::WIDGET); } }, [self](const mozilla::ipc::ResponseRejectReason&) {}); diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp index e5f507dc8279..6c8bbe408f88 100644 --- a/widget/windows/nsWindowGfx.cpp +++ b/widget/windows/nsWindowGfx.cpp @@ -127,7 +127,7 @@ nsIWidgetListener* nsWindow::GetPaintListener() { void nsWindow::ForcePresent() { if (mResizeState != RESIZING) { if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { - remoteRenderer->SendForcePresent(); + remoteRenderer->SendForcePresent(wr::RenderReasons::WIDGET); } } } @@ -190,7 +190,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) { !mBounds.IsEqualEdges(mLastPaintBounds)) { // Do an early async composite so that we at least have something on the // screen in the right place, even if the content is out of date. - layerManager->ScheduleComposite(); + layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); } mLastPaintBounds = mBounds; @@ -256,7 +256,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) { } if (knowsCompositor && layerManager && layerManager->NeedsComposite()) { - layerManager->ScheduleComposite(); + layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); layerManager->SetNeedsComposite(false); }