diff options
Diffstat (limited to 'gfx/layers/apz/src/CheckerboardEvent.cpp')
-rw-r--r-- | gfx/layers/apz/src/CheckerboardEvent.cpp | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/CheckerboardEvent.cpp b/gfx/layers/apz/src/CheckerboardEvent.cpp new file mode 100644 index 0000000000..5716f2d11a --- /dev/null +++ b/gfx/layers/apz/src/CheckerboardEvent.cpp @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +#include "CheckerboardEvent.h" +#include "mozilla/Logging.h" + +#include <algorithm> // for std::sort + +static mozilla::LazyLogModule sApzCheckLog("apz.checkerboard"); + +namespace mozilla { +namespace layers { + +// Relatively arbitrary limit to prevent a perma-checkerboard event from +// eating up gobs of memory. Ideally we shouldn't have perma-checkerboarding +// but better to guard against it. +#define LOG_LENGTH_LIMIT (50 * 1024) + +const char* CheckerboardEvent::sDescriptions[] = { + "page", + "painted displayport", + "requested displayport", + "viewport", +}; + +const char* CheckerboardEvent::sColors[] = { + "brown", + "lightgreen", + "yellow", + "red", +}; + +CheckerboardEvent::CheckerboardEvent(bool aRecordTrace) + : mRecordTrace(aRecordTrace), + mOriginTime(TimeStamp::Now()), + mCheckerboardingActive(false), + mLastSampleTime(mOriginTime), + mFrameCount(0), + mTotalPixelMs(0), + mPeakPixels(0), + mRendertraceLock("Rendertrace") {} + +uint32_t CheckerboardEvent::GetSeverity() { + // Scale the total into a 32-bit value + return (uint32_t)sqrt((double)mTotalPixelMs); +} + +uint32_t CheckerboardEvent::GetPeak() { return mPeakPixels; } + +TimeDuration CheckerboardEvent::GetDuration() { return mEndTime - mStartTime; } + +std::string CheckerboardEvent::GetLog() { + MonitorAutoLock lock(mRendertraceLock); + return mRendertraceInfo.str(); +} + +bool CheckerboardEvent::IsRecordingTrace() { return mRecordTrace; } + +void CheckerboardEvent::UpdateRendertraceProperty( + RendertraceProperty aProperty, const CSSRect& aRect, + const std::string& aExtraInfo) { + if (!mRecordTrace) { + return; + } + MonitorAutoLock lock(mRendertraceLock); + if (!mCheckerboardingActive) { + mBufferedProperties[aProperty].Update(aProperty, aRect, aExtraInfo, lock); + } else { + LogInfo(aProperty, TimeStamp::Now(), aRect, aExtraInfo, lock); + } +} + +void CheckerboardEvent::LogInfo(RendertraceProperty aProperty, + const TimeStamp& aTimestamp, + const CSSRect& aRect, + const std::string& aExtraInfo, + const MonitorAutoLock& aProofOfLock) { + MOZ_ASSERT(mRecordTrace); + if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) { + // The log is already long enough, don't put more things into it. We'll + // append a truncation message when this event ends. + return; + } + // The log is consumed by the page at about:checkerboard. The format is not + // formally specced, but an informal description can be found at + // https://searchfox.org/mozilla-central/rev/d866b96d74ec2a63f09ee418f048d23f4fd379a2/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js#86 + mRendertraceInfo << "RENDERTRACE " + << (aTimestamp - mOriginTime).ToMilliseconds() << " rect " + << sColors[aProperty] << " " << aRect.X() << " " << aRect.Y() + << " " << aRect.Width() << " " << aRect.Height() << " " + << "// " << sDescriptions[aProperty] << aExtraInfo << '\n'; +} + +bool CheckerboardEvent::RecordFrameInfo(uint32_t aCssPixelsCheckerboarded) { + TimeStamp sampleTime = TimeStamp::Now(); + bool eventEnding = false; + if (aCssPixelsCheckerboarded > 0) { + if (!mCheckerboardingActive) { + StartEvent(); + } + MOZ_ASSERT(mCheckerboardingActive); + MOZ_ASSERT(sampleTime >= mLastSampleTime); + mTotalPixelMs += + (uint64_t)((sampleTime - mLastSampleTime).ToMilliseconds() * + aCssPixelsCheckerboarded); + if (aCssPixelsCheckerboarded > mPeakPixels) { + mPeakPixels = aCssPixelsCheckerboarded; + } + mFrameCount++; + } else { + if (mCheckerboardingActive) { + StopEvent(); + eventEnding = true; + } + MOZ_ASSERT(!mCheckerboardingActive); + } + mLastSampleTime = sampleTime; + return eventEnding; +} + +void CheckerboardEvent::StartEvent() { + MOZ_LOG(sApzCheckLog, LogLevel::Debug, ("Starting checkerboard event")); + MOZ_ASSERT(!mCheckerboardingActive); + mCheckerboardingActive = true; + mStartTime = TimeStamp::Now(); + + if (!mRecordTrace) { + return; + } + MonitorAutoLock lock(mRendertraceLock); + std::vector<PropertyValue> history; + for (PropertyBuffer& bufferedProperty : mBufferedProperties) { + bufferedProperty.Flush(history, lock); + } + std::sort(history.begin(), history.end()); + for (const PropertyValue& p : history) { + LogInfo(p.mProperty, p.mTimeStamp, p.mRect, p.mExtraInfo, lock); + } + mRendertraceInfo << " -- checkerboarding starts below --\n"; +} + +void CheckerboardEvent::StopEvent() { + MOZ_LOG(sApzCheckLog, LogLevel::Debug, ("Stopping checkerboard event")); + mCheckerboardingActive = false; + mEndTime = TimeStamp::Now(); + + if (!mRecordTrace) { + return; + } + MonitorAutoLock lock(mRendertraceLock); + if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) { + mRendertraceInfo << "[logging aborted due to length limitations]\n"; + } + mRendertraceInfo << "Checkerboarded for " << mFrameCount << " frames (" + << (mEndTime - mStartTime).ToMilliseconds() << " ms), " + << mPeakPixels << " peak, " << GetSeverity() << " severity." + << '\n'; +} + +bool CheckerboardEvent::PropertyValue::operator<( + const PropertyValue& aOther) const { + if (mTimeStamp < aOther.mTimeStamp) { + return true; + } else if (mTimeStamp > aOther.mTimeStamp) { + return false; + } + return mProperty < aOther.mProperty; +} + +CheckerboardEvent::PropertyBuffer::PropertyBuffer() : mIndex(0) {} + +void CheckerboardEvent::PropertyBuffer::Update( + RendertraceProperty aProperty, const CSSRect& aRect, + const std::string& aExtraInfo, const MonitorAutoLock& aProofOfLock) { + mValues[mIndex] = {aProperty, TimeStamp::Now(), aRect, aExtraInfo}; + mIndex = (mIndex + 1) % BUFFER_SIZE; +} + +void CheckerboardEvent::PropertyBuffer::Flush( + std::vector<PropertyValue>& aOut, const MonitorAutoLock& aProofOfLock) { + for (uint32_t i = 0; i < BUFFER_SIZE; i++) { + uint32_t ix = (mIndex + i) % BUFFER_SIZE; + if (!mValues[ix].mTimeStamp.IsNull()) { + aOut.push_back(mValues[ix]); + mValues[ix].mTimeStamp = TimeStamp(); + } + } +} + +} // namespace layers +} // namespace mozilla |