summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/CheckerboardEvent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/apz/src/CheckerboardEvent.cpp')
-rw-r--r--gfx/layers/apz/src/CheckerboardEvent.cpp194
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