Bug 1510853 - Add CONTENT_FRAME_TIME_REASON. r=jrmuizel, data-review=chutten

MozReview-Commit-ID: 9RV9ZkHXZTR

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Matt Woodrow 2018-12-07 23:28:41 +00:00
Родитель 011bd0697f
Коммит 9abd7f512c
13 изменённых файлов: 179 добавлений и 79 удалений

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

@ -681,6 +681,9 @@ void ClientLayerManager::ForwardTransaction(bool aScheduleComposite) {
mLatestTransactionId = mLatestTransactionId =
mTransactionIdAllocator->GetTransactionId(!mIsRepeatTransaction); mTransactionIdAllocator->GetTransactionId(!mIsRepeatTransaction);
TimeStamp refreshStart = mTransactionIdAllocator->GetTransactionStart(); TimeStamp refreshStart = mTransactionIdAllocator->GetTransactionStart();
if (!refreshStart) {
refreshStart = mTransactionStart;
}
if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) { if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime); mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);

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

@ -671,11 +671,12 @@ void CompositorBridgeParent::PauseComposition() {
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
if (mCompositor) { if (mCompositor) {
mCompositor->Pause(); mCompositor->Pause();
DidComposite(now, now); DidComposite(VsyncId(), now, now);
} else if (mWrBridge) { } else if (mWrBridge) {
mWrBridge->Pause(); mWrBridge->Pause();
NotifyPipelineRendered(mWrBridge->PipelineId(), NotifyPipelineRendered(mWrBridge->PipelineId(),
mWrBridge->GetCurrentEpoch(), now, now, now); mWrBridge->GetCurrentEpoch(), VsyncId(), now, now,
now);
} }
} }
@ -899,7 +900,7 @@ void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
if (!CanComposite()) { if (!CanComposite()) {
TimeStamp end = TimeStamp::Now(); TimeStamp end = TimeStamp::Now();
DidComposite(start, end); DidComposite(aId, start, end);
return; return;
} }
@ -993,7 +994,7 @@ void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
if (!aTarget) { if (!aTarget) {
TimeStamp end = TimeStamp::Now(); TimeStamp end = TimeStamp::Now();
DidComposite(start, end); DidComposite(aId, start, end);
} }
// We're not really taking advantage of the stored composite-again-time here. // We're not really taking advantage of the stored composite-again-time here.
@ -1246,7 +1247,7 @@ void CompositorBridgeParent::ShadowLayersUpdated(
ScheduleComposition(); ScheduleComposition();
if (mPaused) { if (mPaused) {
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
DidComposite(now, now); DidComposite(VsyncId(), now, now);
} }
} }
mLayerManager->NotifyShadowTreeTransaction(); mLayerManager->NotifyShadowTreeTransaction();
@ -1284,7 +1285,7 @@ bool CompositorBridgeParent::SetTestSampleTime(const LayersId& aId,
CancelCurrentCompositeTask(); CancelCurrentCompositeTask();
// Pretend we composited in case someone is wating for this event. // Pretend we composited in case someone is wating for this event.
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
DidComposite(now, now); DidComposite(VsyncId(), now, now);
} }
} }
@ -1314,7 +1315,7 @@ void CompositorBridgeParent::ApplyAsyncProperties(
CancelCurrentCompositeTask(); CancelCurrentCompositeTask();
// Pretend we composited in case someone is waiting for this event. // Pretend we composited in case someone is waiting for this event.
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
DidComposite(now, now); DidComposite(VsyncId(), now, now);
} }
} }
} }
@ -1684,8 +1685,8 @@ mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild(
GetAnimationStorage(), mWrBridge->GetTextureFactoryIdentifier()); GetAnimationStorage(), mWrBridge->GetTextureFactoryIdentifier());
// Pretend we composited, since parent CompositorBridgeParent was replaced. // Pretend we composited, since parent CompositorBridgeParent was replaced.
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, now, now, NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, VsyncId(),
now); now, now, now);
} }
if (oldApzUpdater) { if (oldApzUpdater) {
@ -1976,19 +1977,14 @@ CompositorBridgeParent::LayerTreeState::InProcessSharingController() const {
return mParent; return mParent;
} }
void CompositorBridgeParent::DidComposite(LayersId aId, void CompositorBridgeParent::DidComposite(const VsyncId& aId,
TimeStamp& aCompositeStart, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) { TimeStamp& aCompositeEnd) {
MOZ_ASSERT(aId == mRootLayerTreeID);
DidComposite(aCompositeStart, aCompositeEnd);
}
void CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) {
if (mWrBridge) { if (mWrBridge) {
MOZ_ASSERT(false); // This should never get called for a WR compositor MOZ_ASSERT(false); // This should never get called for a WR compositor
} else { } else {
NotifyDidComposite(mPendingTransaction, aCompositeStart, aCompositeEnd); NotifyDidComposite(mPendingTransaction, aId, aCompositeStart,
aCompositeEnd);
#if defined(ENABLE_FRAME_LATENCY_LOG) #if defined(ENABLE_FRAME_LATENCY_LOG)
if (mPendingTransaction.IsValid()) { if (mPendingTransaction.IsValid()) {
if (mRefreshStartTime) { if (mRefreshStartTime) {
@ -2017,8 +2013,9 @@ void CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart,
void CompositorBridgeParent::NotifyPipelineRendered( void CompositorBridgeParent::NotifyPipelineRendered(
const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
TimeStamp& aCompositeStart, TimeStamp& aRenderStart, const VsyncId& aCompositeStartId, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd, wr::RendererStats* aStats) { TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
wr::RendererStats* aStats) {
if (!mWrBridge || !mAsyncImageManager) { if (!mWrBridge || !mAsyncImageManager) {
return; return;
} }
@ -2033,7 +2030,8 @@ void CompositorBridgeParent::NotifyPipelineRendered(
if (!mPaused) { if (!mPaused) {
TransactionId transactionId = mWrBridge->FlushTransactionIdsForEpoch( TransactionId transactionId = mWrBridge->FlushTransactionIdsForEpoch(
aEpoch, aCompositeStart, aRenderStart, aCompositeEnd, uiController); aEpoch, aCompositeStartId, aCompositeStart, aRenderStart,
aCompositeEnd, uiController);
Unused << SendDidComposite(LayersId{0}, transactionId, aCompositeStart, Unused << SendDidComposite(LayersId{0}, transactionId, aCompositeStart,
aCompositeEnd); aCompositeEnd);
@ -2052,8 +2050,8 @@ void CompositorBridgeParent::NotifyPipelineRendered(
wrBridge->RemoveEpochDataPriorTo(aEpoch); wrBridge->RemoveEpochDataPriorTo(aEpoch);
if (!mPaused) { if (!mPaused) {
TransactionId transactionId = wrBridge->FlushTransactionIdsForEpoch( TransactionId transactionId = wrBridge->FlushTransactionIdsForEpoch(
aEpoch, aCompositeStart, aRenderStart, aCompositeEnd, uiController, aEpoch, aCompositeStartId, aCompositeStart, aRenderStart,
aStats, &stats); aCompositeEnd, uiController, aStats, &stats);
Unused << wrBridge->GetCompositorBridge()->SendDidComposite( Unused << wrBridge->GetCompositorBridge()->SendDidComposite(
wrBridge->GetLayersId(), transactionId, aCompositeStart, wrBridge->GetLayersId(), transactionId, aCompositeStart,
aCompositeEnd); aCompositeEnd);
@ -2071,6 +2069,7 @@ CompositorBridgeParent::GetAsyncImagePipelineManager() const {
} }
void CompositorBridgeParent::NotifyDidComposite(TransactionId aTransactionId, void CompositorBridgeParent::NotifyDidComposite(TransactionId aTransactionId,
VsyncId aId,
TimeStamp& aCompositeStart, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) { TimeStamp& aCompositeEnd) {
MOZ_ASSERT( MOZ_ASSERT(
@ -2088,13 +2087,13 @@ void CompositorBridgeParent::NotifyDidComposite(TransactionId aTransactionId,
} }
MonitorAutoLock lock(*sIndirectLayerTreesLock); MonitorAutoLock lock(*sIndirectLayerTreesLock);
ForEachIndirectLayerTree( ForEachIndirectLayerTree([&](LayerTreeState* lts,
[&](LayerTreeState* lts, const LayersId& aLayersId) -> void { const LayersId& aLayersId) -> void {
if (lts->mCrossProcessParent && lts->mParent == this) { if (lts->mCrossProcessParent && lts->mParent == this) {
CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent; CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
cpcp->DidCompositeLocked(aLayersId, aCompositeStart, aCompositeEnd); cpcp->DidCompositeLocked(aLayersId, aId, aCompositeStart, aCompositeEnd);
} }
}); });
} }
void CompositorBridgeParent::InvalidateRemoteLayers() { void CompositorBridgeParent::InvalidateRemoteLayers() {

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

@ -147,9 +147,6 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent,
LayersObserverEpoch aEpoch, LayersObserverEpoch aEpoch,
bool aActive) = 0; bool aActive) = 0;
virtual void DidComposite(LayersId aId, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) = 0;
// HostIPCAllocator // HostIPCAllocator
base::ProcessId GetChildProcessId() override; base::ProcessId GetChildProcessId() override;
void NotifyNotUsed(PTextureParent* aTexture, void NotifyNotUsed(PTextureParent* aTexture,
@ -314,6 +311,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
void NotifyWebRenderContextPurge(); void NotifyWebRenderContextPurge();
void NotifyPipelineRendered(const wr::PipelineId& aPipelineId, void NotifyPipelineRendered(const wr::PipelineId& aPipelineId,
const wr::Epoch& aEpoch, const wr::Epoch& aEpoch,
const VsyncId& aCompositeStartId,
TimeStamp& aCompositeStart, TimeStamp& aCompositeStart,
TimeStamp& aRenderStart, TimeStamp& aCompositeEnd, TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
wr::RendererStats* aStats = nullptr); wr::RendererStats* aStats = nullptr);
@ -628,11 +626,10 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
*/ */
bool CanComposite(); bool CanComposite();
void DidComposite(LayersId aId, TimeStamp& aCompositeStart, void DidComposite(const VsyncId& aId, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) override; TimeStamp& aCompositeEnd);
void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
void NotifyDidComposite(TransactionId aTransactionId, void NotifyDidComposite(TransactionId aTransactionId, VsyncId aId,
TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd); TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
// The indirect layer tree lock must be held before calling this function. // The indirect layer tree lock must be held before calling this function.

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

@ -394,17 +394,13 @@ void CrossProcessCompositorBridgeParent::ShadowLayersUpdated(
aInfo.transactionStart(), aInfo.url(), aInfo.fwdTime()); aInfo.transactionStart(), aInfo.url(), aInfo.fwdTime());
} }
void CrossProcessCompositorBridgeParent::DidComposite(
LayersId aId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) {
MonitorAutoLock lock(*sIndirectLayerTreesLock);
DidCompositeLocked(aId, aCompositeStart, aCompositeEnd);
}
void CrossProcessCompositorBridgeParent::DidCompositeLocked( void CrossProcessCompositorBridgeParent::DidCompositeLocked(
LayersId aId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) { LayersId aId, const VsyncId& aVsyncId, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) {
sIndirectLayerTreesLock->AssertCurrentThreadOwns(); sIndirectLayerTreesLock->AssertCurrentThreadOwns();
if (LayerTransactionParent* layerTree = sIndirectLayerTrees[aId].mLayerTree) { if (LayerTransactionParent* layerTree = sIndirectLayerTrees[aId].mLayerTree) {
TransactionId transactionId = layerTree->FlushTransactionId(aCompositeEnd); TransactionId transactionId =
layerTree->FlushTransactionId(aVsyncId, aCompositeEnd);
if (transactionId.IsValid()) { if (transactionId.IsValid()) {
Unused << SendDidComposite(aId, transactionId, aCompositeStart, Unused << SendDidComposite(aId, transactionId, aCompositeStart,
aCompositeEnd); aCompositeEnd);

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

@ -137,10 +137,8 @@ class CrossProcessCompositorBridgeParent final
// Use DidCompositeLocked if you already hold a lock on // Use DidCompositeLocked if you already hold a lock on
// sIndirectLayerTreesLock; Otherwise use DidComposite, which would request // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request
// the lock automatically. // the lock automatically.
void DidCompositeLocked(LayersId aId, TimeStamp& aCompositeStart, void DidCompositeLocked(LayersId aId, const VsyncId& aVsyncId,
TimeStamp& aCompositeEnd); TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
void DidComposite(LayersId aId, TimeStamp& aCompositeStart,
TimeStamp& aCompositeEnd) override;
PTextureParent* AllocPTextureParent( PTextureParent* AllocPTextureParent(
const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock, const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,

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

@ -39,6 +39,8 @@
#include "mozilla/layers/TextureHost.h" #include "mozilla/layers/TextureHost.h"
#include "mozilla/layers/AsyncCompositionManager.h" #include "mozilla/layers/AsyncCompositionManager.h"
using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
namespace mozilla { namespace mozilla {
namespace layers { namespace layers {
@ -884,12 +886,49 @@ bool LayerTransactionParent::IsSameProcess() const {
} }
TransactionId LayerTransactionParent::FlushTransactionId( TransactionId LayerTransactionParent::FlushTransactionId(
TimeStamp& aCompositeEnd) { const VsyncId& aId, TimeStamp& aCompositeEnd) {
if (mId.IsValid() && mPendingTransaction.IsValid() && !mVsyncRate.IsZero()) { if (mId.IsValid() && mPendingTransaction.IsValid() && !mVsyncRate.IsZero()) {
double latencyMs = (aCompositeEnd - mTxnStartTime).ToMilliseconds(); double latencyMs = (aCompositeEnd - mTxnStartTime).ToMilliseconds();
double latencyNorm = latencyMs / mVsyncRate.ToMilliseconds(); double latencyNorm = latencyMs / mVsyncRate.ToMilliseconds();
int32_t fracLatencyNorm = lround(latencyNorm * 100.0); int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm); Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
// Record CONTENT_FRAME_TIME_REASON. See
// WebRenderBridgeParent::FlushTransactionIdsForEpoch for more details.
//
// Note that deseralizing a layers update (RecvUpdate) can delay the receipt
// of the composite vsync message
// (CompositorBridgeParent::CompositeToTarget), since they're using the same
// thread. This can mean that compositing might start significantly late,
// but this code will still detect it as having successfully started on the
// right vsync (which is somewhat correct). We'd now have reduced time left
// in the vsync interval to finish compositing, so the chances of a missed
// frame increases. This is effectively including the RecvUpdate work as
// part of the 'compositing' phase for this metric, but it isn't included in
// COMPOSITE_TIME, and *is* included in CONTENT_FULL_PAINT_TIME.
latencyMs = (aCompositeEnd - mRefreshStartTime).ToMilliseconds();
latencyNorm = latencyMs / mVsyncRate.ToMilliseconds();
fracLatencyNorm = lround(latencyNorm * 100.0);
if (fracLatencyNorm < 200) {
// Success
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::OnTime);
} else {
if (mTxnVsyncId == VsyncId() || aId == VsyncId() || mTxnVsyncId >= aId) {
// Vsync ids are nonsensical, possibly something got trigged from
// outside vsync?
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::NoVsync);
} else if (aId - mTxnVsyncId > 1) {
// Composite started late (and maybe took too long as well)
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::MissedComposite);
} else {
// Composite start on time, but must have taken too long.
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::SlowComposite);
}
}
} }
#if defined(ENABLE_FRAME_LATENCY_LOG) #if defined(ENABLE_FRAME_LATENCY_LOG)

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

@ -84,7 +84,8 @@ class LayerTransactionParent final : public PLayerTransactionParent,
mTxnURL = aURL; mTxnURL = aURL;
mFwdTime = aFwdTime; mFwdTime = aFwdTime;
} }
TransactionId FlushTransactionId(TimeStamp& aCompositeEnd); TransactionId FlushTransactionId(const VsyncId& aId,
TimeStamp& aCompositeEnd);
// CompositableParentManager // CompositableParentManager
void SendAsyncMessage( void SendAsyncMessage(

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

@ -35,6 +35,8 @@
#include "mozilla/webrender/RenderThread.h" #include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/CompositorWidget.h" #include "mozilla/widget/CompositorWidget.h"
using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
#ifdef MOZ_GECKO_PROFILER #ifdef MOZ_GECKO_PROFILER
#include "ProfilerMarkerPayload.h" #include "ProfilerMarkerPayload.h"
#endif #endif
@ -955,7 +957,8 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetDisplayList(
// though DisplayList was not pushed to webrender. // though DisplayList was not pushed to webrender.
if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) { if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, now, now, now); cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, VsyncId(), now, now,
now);
} }
} }
@ -1076,7 +1079,8 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEmptyTransaction(
MOZ_ASSERT(mPendingTransactionIds.size() == 1); MOZ_ASSERT(mPendingTransactionIds.size() == 1);
if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) { if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
TimeStamp now = TimeStamp::Now(); TimeStamp now = TimeStamp::Now();
cbp->NotifyPipelineRendered(mPipelineId, mWrEpoch, now, now, now); cbp->NotifyPipelineRendered(mPipelineId, mWrEpoch, VsyncId(), now, now,
now);
} }
} }
@ -1218,7 +1222,7 @@ void WebRenderBridgeParent::FlushFrameGeneration() {
mCompositorScheduler->CancelCurrentCompositeTask(); mCompositorScheduler->CancelCurrentCompositeTask();
// Update timestamp of scheduler for APZ and animation. // Update timestamp of scheduler for APZ and animation.
mCompositorScheduler->UpdateLastComposeTime(); mCompositorScheduler->UpdateLastComposeTime();
MaybeGenerateFrame(/* aForceGenerateFrame */ true); MaybeGenerateFrame(VsyncId(), /* aForceGenerateFrame */ true);
} }
} }
@ -1679,7 +1683,7 @@ void WebRenderBridgeParent::CompositeToTarget(VsyncId aId,
} }
return; return;
} }
MaybeGenerateFrame(/* aForceGenerateFrame */ false); MaybeGenerateFrame(aId, /* aForceGenerateFrame */ false);
} }
TimeDuration WebRenderBridgeParent::GetVsyncInterval() const { TimeDuration WebRenderBridgeParent::GetVsyncInterval() const {
@ -1691,7 +1695,8 @@ TimeDuration WebRenderBridgeParent::GetVsyncInterval() const {
return TimeDuration(); return TimeDuration();
} }
void WebRenderBridgeParent::MaybeGenerateFrame(bool aForceGenerateFrame) { void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId,
bool aForceGenerateFrame) {
// This function should only get called in the root WRBP // This function should only get called in the root WRBP
MOZ_ASSERT(IsRootWebRenderBridgeParent()); MOZ_ASSERT(IsRootWebRenderBridgeParent());
@ -1739,7 +1744,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(bool aForceGenerateFrame) {
SetAPZSampleTime(); SetAPZSampleTime();
wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId(), start); wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId(), aId, start);
#if defined(ENABLE_FRAME_LATENCY_LOG) #if defined(ENABLE_FRAME_LATENCY_LOG)
auto startTime = TimeStamp::Now(); auto startTime = TimeStamp::Now();
@ -1782,10 +1787,10 @@ void WebRenderBridgeParent::NotifySceneBuiltForEpoch(
} }
TransactionId WebRenderBridgeParent::FlushTransactionIdsForEpoch( TransactionId WebRenderBridgeParent::FlushTransactionIdsForEpoch(
const wr::Epoch& aEpoch, const TimeStamp& aCompositeStartTime, const wr::Epoch& aEpoch, const VsyncId& aCompositeStartId,
const TimeStamp& aRenderStartTime, const TimeStamp& aEndTime, const TimeStamp& aCompositeStartTime, const TimeStamp& aRenderStartTime,
UiCompositorControllerParent* aUiController, wr::RendererStats* aStats, const TimeStamp& aEndTime, UiCompositorControllerParent* aUiController,
nsTArray<FrameStats>* aOutputStats) { wr::RendererStats* aStats, nsTArray<FrameStats>* aOutputStats) {
TransactionId id{0}; TransactionId id{0};
while (!mPendingTransactionIds.empty()) { while (!mPendingTransactionIds.empty()) {
const auto& transactionId = mPendingTransactionIds.front(); const auto& transactionId = mPendingTransactionIds.front();
@ -1822,6 +1827,50 @@ TransactionId WebRenderBridgeParent::FlushTransactionIdsForEpoch(
} }
#endif #endif
// Record CONTENT_FRAME_TIME_REASON.
//
// This uses the refresh start time (CONTENT_FRAME_TIME uses the start of
// display list building), since that includes layout/style time, and 200
// should correlate more closely with missing a vsync.
//
// Also of note is that when the root WebRenderBridgeParent decides to
// skip a composite (due to the Renderer being busy), that won't notify
// child WebRenderBridgeParents. That failure will show up as the
// composite starting late (since it did), but it's really a fault of a
// slow composite on the previous frame, not a slow
// CONTENT_FULL_PAINT_TIME. It would be nice to have a separate bucket for
// this category (scene was ready on the next vsync, but we chose not to
// composite), but I can't find a way to locate the right child
// WebRenderBridgeParents from the root. WebRender notifies us of the
// child pipelines contained within a render, after it finishes, but I
// can't see how to query what child pipeline would have been rendered,
// when we choose to not do it.
latencyMs = (aEndTime - transactionId.mRefreshStartTime).ToMilliseconds();
latencyNorm = latencyMs / mVsyncRate.ToMilliseconds();
fracLatencyNorm = lround(latencyNorm * 100.0);
if (fracLatencyNorm < 200) {
// Success
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::OnTime);
} else {
if (transactionId.mVsyncId == VsyncId() ||
aCompositeStartId == VsyncId() ||
transactionId.mVsyncId >= aCompositeStartId) {
// Vsync ids are nonsensical, possibly something got trigged from
// outside vsync?
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::NoVsync);
} else if (aCompositeStartId - transactionId.mVsyncId > 1) {
// Composite started late (and maybe took too long as well)
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::MissedComposite);
} else {
// Composite start on time, but must have taken too long.
Telemetry::AccumulateCategorical(
LABELS_CONTENT_FRAME_TIME_REASON::SlowComposite);
}
}
if (fracLatencyNorm > 200) { if (fracLatencyNorm > 200) {
aOutputStats->AppendElement(FrameStats( aOutputStats->AppendElement(FrameStats(
transactionId.mId, aCompositeStartTime, aRenderStartTime, aEndTime, transactionId.mId, aCompositeStartTime, aRenderStartTime, aEndTime,

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

@ -181,9 +181,9 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
const bool aIsFirstPaint, const bool aUseForTelemetry = true); const bool aIsFirstPaint, const bool aUseForTelemetry = true);
TransactionId LastPendingTransactionId(); TransactionId LastPendingTransactionId();
TransactionId FlushTransactionIdsForEpoch( TransactionId FlushTransactionIdsForEpoch(
const wr::Epoch& aEpoch, const TimeStamp& aCompositeStartTime, const wr::Epoch& aEpoch, const VsyncId& aCompositeStartId,
const TimeStamp& aRenderStartTime, const TimeStamp& aEndTime, const TimeStamp& aCompositeStartTime, const TimeStamp& aRenderStartTime,
UiCompositorControllerParent* aUiController, const TimeStamp& aEndTime, UiCompositorControllerParent* aUiController,
wr::RendererStats* aStats = nullptr, wr::RendererStats* aStats = nullptr,
nsTArray<FrameStats>* aOutputStats = nullptr); nsTArray<FrameStats>* aOutputStats = nullptr);
void NotifySceneBuiltForEpoch(const wr::Epoch& aEpoch, void NotifySceneBuiltForEpoch(const wr::Epoch& aEpoch,
@ -316,7 +316,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
void FlushFrameGeneration(); void FlushFrameGeneration();
void FlushFramePresentation(); void FlushFramePresentation();
void MaybeGenerateFrame(bool aForceGenerateFrame); void MaybeGenerateFrame(VsyncId aId, bool aForceGenerateFrame);
private: private:
struct PendingTransactionId { struct PendingTransactionId {

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

@ -244,6 +244,7 @@ void RenderThread::HandleFrame(wr::WindowId aWindowId, bool aRender) {
} }
TimeStamp startTime; TimeStamp startTime;
VsyncId startId;
bool hadSlowFrame; bool hadSlowFrame;
{ // scope lock { // scope lock
@ -253,11 +254,13 @@ void RenderThread::HandleFrame(wr::WindowId aWindowId, bool aRender) {
WindowInfo* info = it->second; WindowInfo* info = it->second;
MOZ_ASSERT(info->mPendingCount > 0); MOZ_ASSERT(info->mPendingCount > 0);
startTime = info->mStartTimes.front(); startTime = info->mStartTimes.front();
startId = info->mStartIds.front();
hadSlowFrame = info->mHadSlowFrame; hadSlowFrame = info->mHadSlowFrame;
info->mHadSlowFrame = false; info->mHadSlowFrame = false;
} }
UpdateAndRender(aWindowId, startTime, aRender, /* aReadbackSize */ Nothing(), UpdateAndRender(aWindowId, startId, startTime, aRender,
/* aReadbackSize */ Nothing(),
/* aReadbackBuffer */ Nothing(), hadSlowFrame); /* aReadbackBuffer */ Nothing(), hadSlowFrame);
FrameRenderingComplete(aWindowId); FrameRenderingComplete(aWindowId);
} }
@ -304,6 +307,7 @@ void RenderThread::RunEvent(wr::WindowId aWindowId,
static void NotifyDidRender(layers::CompositorBridgeParent* aBridge, static void NotifyDidRender(layers::CompositorBridgeParent* aBridge,
RefPtr<WebRenderPipelineInfo> aInfo, RefPtr<WebRenderPipelineInfo> aInfo,
VsyncId aCompositeStartId,
TimeStamp aCompositeStart, TimeStamp aRenderStart, TimeStamp aCompositeStart, TimeStamp aRenderStart,
TimeStamp aEnd, bool aRender, TimeStamp aEnd, bool aRender,
RendererStats aStats) { RendererStats aStats) {
@ -317,13 +321,9 @@ static void NotifyDidRender(layers::CompositorBridgeParent* aBridge,
auto info = aInfo->Raw(); auto info = aInfo->Raw();
for (uintptr_t i = 0; i < info.epochs.length; i++) { for (uintptr_t i = 0; i < info.epochs.length; i++) {
aBridge->NotifyPipelineRendered(info.epochs.data[i].pipeline_id, aBridge->NotifyPipelineRendered(
info.epochs.data[i].epoch, aCompositeStart, info.epochs.data[i].pipeline_id, info.epochs.data[i].epoch,
aRenderStart, aEnd, &aStats); aCompositeStartId, aCompositeStart, aRenderStart, aEnd, &aStats);
}
if (aBridge->GetWrBridge()) {
aBridge->GetWrBridge()->CompositeIfNeeded();
} }
} }
@ -336,6 +336,7 @@ static void NotifyDidStartRender(layers::CompositorBridgeParent* aBridge) {
} }
void RenderThread::UpdateAndRender(wr::WindowId aWindowId, void RenderThread::UpdateAndRender(wr::WindowId aWindowId,
const VsyncId& aStartId,
const TimeStamp& aStartTime, bool aRender, const TimeStamp& aStartTime, bool aRender,
const Maybe<gfx::IntSize>& aReadbackSize, const Maybe<gfx::IntSize>& aReadbackSize,
const Maybe<Range<uint8_t>>& aReadbackBuffer, const Maybe<Range<uint8_t>>& aReadbackBuffer,
@ -374,8 +375,8 @@ void RenderThread::UpdateAndRender(wr::WindowId aWindowId,
layers::CompositorThreadHolder::Loop()->PostTask( layers::CompositorThreadHolder::Loop()->PostTask(
NewRunnableFunction("NotifyDidRenderRunnable", &NotifyDidRender, NewRunnableFunction("NotifyDidRenderRunnable", &NotifyDidRender,
renderer->GetCompositorBridge(), info, aStartTime, renderer->GetCompositorBridge(), info, aStartId,
start, end, aRender, stats)); aStartTime, start, end, aRender, stats));
if (rendered) { if (rendered) {
// Wait for GPU after posting NotifyDidRender, since the wait is not // Wait for GPU after posting NotifyDidRender, since the wait is not
@ -463,6 +464,7 @@ void RenderThread::SetDestroyed(wr::WindowId aWindowId) {
} }
void RenderThread::IncPendingFrameCount(wr::WindowId aWindowId, void RenderThread::IncPendingFrameCount(wr::WindowId aWindowId,
const VsyncId& aStartId,
const TimeStamp& aStartTime) { const TimeStamp& aStartTime) {
MutexAutoLock lock(mFrameCountMapLock); MutexAutoLock lock(mFrameCountMapLock);
auto it = mWindowInfos.find(AsUint64(aWindowId)); auto it = mWindowInfos.find(AsUint64(aWindowId));
@ -472,6 +474,7 @@ void RenderThread::IncPendingFrameCount(wr::WindowId aWindowId,
} }
it->second->mPendingCount++; it->second->mPendingCount++;
it->second->mStartTimes.push(aStartTime); it->second->mStartTimes.push(aStartTime);
it->second->mStartIds.push(aStartId);
} }
void RenderThread::DecPendingFrameCount(wr::WindowId aWindowId) { void RenderThread::DecPendingFrameCount(wr::WindowId aWindowId) {
@ -495,6 +498,7 @@ void RenderThread::DecPendingFrameCount(wr::WindowId aWindowId) {
mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME,
info->mStartTimes.front()); info->mStartTimes.front());
info->mStartTimes.pop(); info->mStartTimes.pop();
info->mStartIds.pop();
} }
void RenderThread::IncRenderingFrameCount(wr::WindowId aWindowId) { void RenderThread::IncRenderingFrameCount(wr::WindowId aWindowId) {
@ -529,6 +533,7 @@ void RenderThread::FrameRenderingComplete(wr::WindowId aWindowId) {
mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME,
info->mStartTimes.front()); info->mStartTimes.front());
info->mStartTimes.pop(); info->mStartTimes.pop();
info->mStartIds.pop();
} }
void RenderThread::NotifySlowFrame(wr::WindowId aWindowId) { void RenderThread::NotifySlowFrame(wr::WindowId aWindowId) {

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

@ -21,6 +21,7 @@
#include "mozilla/webrender/WebRenderTypes.h" #include "mozilla/webrender/WebRenderTypes.h"
#include "mozilla/layers/SynchronousTask.h" #include "mozilla/layers/SynchronousTask.h"
#include "GLContext.h" #include "GLContext.h"
#include "mozilla/VsyncDispatcher.h"
#include <list> #include <list>
#include <queue> #include <queue>
@ -168,8 +169,9 @@ class RenderThread final {
void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aCallBack); void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aCallBack);
/// Can only be called from the render thread. /// Can only be called from the render thread.
void UpdateAndRender(wr::WindowId aWindowId, const TimeStamp& aStartTime, void UpdateAndRender(wr::WindowId aWindowId, const VsyncId& aStartId,
bool aRender, const Maybe<gfx::IntSize>& aReadbackSize, const TimeStamp& aStartTime, bool aRender,
const Maybe<gfx::IntSize>& aReadbackSize,
const Maybe<Range<uint8_t>>& aReadbackBuffer, const Maybe<Range<uint8_t>>& aReadbackBuffer,
bool aHadSlowFrame); bool aHadSlowFrame);
@ -200,7 +202,7 @@ class RenderThread final {
/// Can be called from any thread. /// Can be called from any thread.
bool TooManyPendingFrames(wr::WindowId aWindowId); bool TooManyPendingFrames(wr::WindowId aWindowId);
/// Can be called from any thread. /// Can be called from any thread.
void IncPendingFrameCount(wr::WindowId aWindowId, void IncPendingFrameCount(wr::WindowId aWindowId, const VsyncId& aStartId,
const TimeStamp& aStartTime); const TimeStamp& aStartTime);
/// Can be called from any thread. /// Can be called from any thread.
void DecPendingFrameCount(wr::WindowId aWindowId); void DecPendingFrameCount(wr::WindowId aWindowId);
@ -266,6 +268,7 @@ class RenderThread final {
// One entry in this queue for each pending frame, so the length // One entry in this queue for each pending frame, so the length
// should always equal mPendingCount // should always equal mPendingCount
std::queue<TimeStamp> mStartTimes; std::queue<TimeStamp> mStartTimes;
std::queue<VsyncId> mStartIds;
bool mHadSlowFrame = false; bool mHadSlowFrame = false;
}; };

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

@ -372,8 +372,9 @@ void WebRenderAPI::Readback(const TimeStamp& aStartTime, gfx::IntSize size,
~Readback() { MOZ_COUNT_DTOR(Readback); } ~Readback() { MOZ_COUNT_DTOR(Readback); }
virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override { virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
aRenderThread.UpdateAndRender(aWindowId, mStartTime, /* aRender */ true, aRenderThread.UpdateAndRender(aWindowId, VsyncId(), mStartTime,
Some(mSize), Some(mBuffer), false); /* aRender */ true, Some(mSize),
Some(mBuffer), false);
layers::AutoCompleteTask complete(mTask); layers::AutoCompleteTask complete(mTask);
} }

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

@ -13211,6 +13211,15 @@
"n_buckets": 50, "n_buckets": 50,
"description": "The time, in percentage of a vsync interval, spent from beginning a paint in the content process until that frame is presented in the compositor by WebRender, excluding time spent uploading any content" "description": "The time, in percentage of a vsync interval, spent from beginning a paint in the content process until that frame is presented in the compositor by WebRender, excluding time spent uploading any content"
}, },
"CONTENT_FRAME_TIME_REASON": {
"record_in_processes": ["main", "gpu"],
"alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
"bug_numbers": [1510853],
"expires_in_version": "73",
"kind": "categorical",
"description": "The reason that CONTENT_FRAME_TIME recorded a slow (>200) result, if any.",
"labels": ["OnTime", "NoVsync", "MissedComposite", "SlowComposite"]
},
"CONTENT_LARGE_PAINT_PHASE_WEIGHT": { "CONTENT_LARGE_PAINT_PHASE_WEIGHT": {
"record_in_processes": ["main", "content"], "record_in_processes": ["main", "content"],
"alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"], "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],