Files
palemoon27/gfx/layers/composite/FPSCounter.cpp
T
roytam1 fa9ed1e11b import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1166942 - Add logging of color layer bounds. r=nical (442de4b6c)
- Bug 1152109 - Don't clear plugin data cache in ShadowLayers code, we need to resend this during repaints that do not involve reflow. r=roc (8670e73f9)
- Bug 1158122 - Remove all occurences of nsIntRect in gfx/layers/composite. r=nical (dcfa2050a)
- Bug 1158122 - Remove all occurences of nsIntRect in gfx/ipc/. r=nical (1ca3800b5)
- Bug 1158122 - Remove all occurences of nsIntRect in gfx/gl/. r=nical (187d70360)
- Bug 1158122 - Remove all occurences of nsIntRect in gfx/thebes/. r=nical (487ab747c)
- Bug 1155621 - Remove no-op gfx2DGlue conversion helpers. r=Bas (f2ea364d4)
- Bug 1158122 - Remove some occurences of nsIntRect in gfx/layers/. r=nical (b1bb2e736)
- Bug 1158122 - Remove all occurences of nsIntRect in gfx/layers/client. r=nical (f0e71c984)
- Bug 1158122 - Remove the remaining occurences of nsIntRect in gfx/layers. r=nical (571ff6de0)
- Bug 1148350 - Add a test. r=botond (226b59773)
- Bug 1158122 - Remove most occurences of nsIntRect in gfx/tests. r=nical (cb3b2541c)
- Bug 1032848 - Part 1: Implement WebIDL for HTMLCanvasElement::CaptureStream. r=smaug, r=mt (210e34454)
- Bug 1032848 - Part 2: Implement HTMLCanvasElement::CaptureStream. r=mt, r=jesup, r=jgilbert, r=gwright (bec93b4d3)
- Bug 1032848 followup: Add 'override' annotation to Notify() method in CanvasCaptureMediaStream.cpp. rs=ehsan (9866fd266)
- Bug 1032848 - Part 3: Add tests for HTMLCanvasElement::CaptureStream. r=mt, r=jgilbert, r=jesup (bde68ba7c)
- Bug 987498 - Part 1 - Layers support OverlayImage. r=roc (8b22b789f)
- Bug 987498 - Part 2 - HTMLMediaElement supports playback OverlayImage. r=roc (247dc15a2)
- Bug 987498 - Part 3 - Implement DOMHwMediaStream. r=roc (f0d9de519)
- Bug 987498 - Part 4 -Implement SetImageSize on DOMHwMediaStream. r=roc (850cf6ad0)
- Bug 1147194 - Do scroll info layer hoisting when building the display list. r=tn (64ae65e97)
- Bug 1161389 - Skip AccessibleCaret frame if nsDisplayListBuilder doesn't build caret. r=roc (0e8328488)
- Bug 1068881 (Part 2) - Add reftests for rounding image dest rects to zero size. r=roc (00202564b)
- Bug 1164227 - Don't allow invalid region simplification to invalidate unchanged scrolled contents. r=roc (3cde9cd56)
- Bug 1148022 - When frame metrics are attached to the container layer of the root scroll frame, make sure that its scroll frame is active. r=tn (5369f9175)
- Bug 1154478 - Really force-enable the event-regions code when APZ is enabled. r=tn (e7ba54c3d)
- Move AsyncPanZoomAnimation into its own header. (bug 1139220 part 1, r=kats) (e06fd8854)
- Bug 1139180 - Add BUG_COMPONENT metadata to moz.build for files in layout/. r=roc (468dedf02)
- Factor the guts of AsyncScroll into a base helper class. (bug 1139220 part 2, r=kgilbert) (1719bcba6)
- Use the main-thread key spline animation logic for Desktop APZ. (bug 1139220 part 3, r=kats,kgilbert) (84a1d8948)
- Bug 1147038. Use the correct clip for root scroll frames in root content documents. r=mstange (cae882bdf)
- Use Maybe to communicate the APZ scrollframe clip. (bug 1148582 part 1, r=mstange) (d22c5cfeb)
2020-12-03 09:53:51 +08:00

452 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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 <stddef.h> // for size_t
#include "Units.h" // for ScreenIntRect
#include "gfxRect.h" // for gfxRect
#include "gfxPrefs.h" // for gfxPrefs
#include "mozilla/gfx/Point.h" // for IntSize, Point
#include "mozilla/gfx/Rect.h" // for Rect
#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
#include "mozilla/layers/Compositor.h" // for Compositor
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
#include "nsPoint.h" // for nsIntPoint
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsIFile.h" // for nsIFile
#include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
#include "prprf.h" // for PR_snprintf
#include "FPSCounter.h"
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
using namespace mozilla::gl;
FPSCounter::FPSCounter(const char* aName)
: mWriteIndex(0)
, mFPSName(aName)
{
Init();
}
FPSCounter::~FPSCounter() { }
void
FPSCounter::Init()
{
for (int i = 0; i < kMaxFrames; i++) {
mFrameTimestamps.AppendElement(TimeStamp());
}
mLastInterval = TimeStamp::Now();
}
// Returns true if we captured a full interval of data
bool
FPSCounter::CapturedFullInterval(TimeStamp aTimestamp) {
TimeDuration duration = aTimestamp - mLastInterval;
return duration.ToSeconds() >= kFpsDumpInterval;
}
void
FPSCounter::AddFrame(TimeStamp aTimestamp) {
NS_ASSERTION(mWriteIndex < kMaxFrames, "We probably have a bug with the circular buffer");
NS_ASSERTION(mWriteIndex >= 0, "Circular Buffer index should never be negative");
int index = mWriteIndex++;
if (mWriteIndex == kMaxFrames) {
mWriteIndex = 0;
}
mFrameTimestamps[index] = aTimestamp;
if (CapturedFullInterval(aTimestamp)) {
PrintFPS();
WriteFrameTimeStamps();
mLastInterval = aTimestamp;
}
}
double
FPSCounter::AddFrameAndGetFps(TimeStamp aTimestamp) {
AddFrame(aTimestamp);
return GetFPS(aTimestamp);
}
int
FPSCounter::GetLatestReadIndex()
{
if (mWriteIndex == 0) {
return kMaxFrames - 1;
}
return mWriteIndex - 1;
}
TimeStamp
FPSCounter::GetLatestTimeStamp()
{
TimeStamp timestamp = mFrameTimestamps[GetLatestReadIndex()];
MOZ_ASSERT(!timestamp.IsNull(), "Cannot use null timestamps");
return timestamp;
}
// Returns true if we iterated over a full interval of data
bool
FPSCounter::IteratedFullInterval(TimeStamp aTimestamp, double aDuration) {
MOZ_ASSERT(mIteratorIndex >= 0, "Cannot be negative");
MOZ_ASSERT(mIteratorIndex < kMaxFrames, "Iterator index cannot be greater than kMaxFrames");
TimeStamp currentStamp = mFrameTimestamps[mIteratorIndex];
TimeDuration duration = aTimestamp - currentStamp;
return duration.ToSeconds() >= aDuration;
}
void
FPSCounter::ResetReverseIterator()
{
mIteratorIndex = GetLatestReadIndex();
}
/***
* Returns true if we have another timestamp that is valid and
* is within the given duration that we're interested in.
* Duration is in seconds
*/
bool FPSCounter::HasNext(TimeStamp aTimestamp, double aDuration)
{
// Order of evaluation here has to stay the same
// otherwise IteratedFullInterval reads from mFrameTimestamps which cannot
// be null
return (mIteratorIndex != mWriteIndex) // Didn't loop around the buffer
&& !mFrameTimestamps[mIteratorIndex].IsNull() // valid data
&& !IteratedFullInterval(aTimestamp, aDuration);
}
TimeStamp
FPSCounter::GetNextTimeStamp()
{
TimeStamp timestamp = mFrameTimestamps[mIteratorIndex--];
MOZ_ASSERT(!timestamp.IsNull(), "Reading Invalid Timestamp Data");
if (mIteratorIndex == -1) {
mIteratorIndex = kMaxFrames - 1;
}
return timestamp;
}
/**
* GetFPS calculates how many frames we've already composited from the current
* frame timestamp and we iterate from the latest timestamp we recorded,
* going back in time. When we hit a frame that is longer than the 1 second
* from the current composited frame, we return how many frames we've counted.
* Just a visualization:
*
* aTimestamp
* Frames: 1 2 3 4 5 6 7 8 9 10 11 12
* Time -------------------------->
*
* GetFPS iterates from aTimestamp, which is the current frame.
* Then starting at frame 12, going back to frame 11, 10, etc, we calculate
* the duration of the recorded frame timestamp from aTimestamp.
* Once duration is greater than 1 second, we return how many frames
* we composited.
*/
double
FPSCounter::GetFPS(TimeStamp aTimestamp)
{
int frameCount = 0;
int duration = 1.0; // Only care about the last 1s of data
ResetReverseIterator();
while (HasNext(aTimestamp, duration)) {
GetNextTimeStamp();
frameCount++;
}
return frameCount;
}
// Iterate the same way we do in GetFPS()
int
FPSCounter::BuildHistogram(std::map<int, int>& aFpsData)
{
TimeStamp currentIntervalStart = GetLatestTimeStamp();
TimeStamp currentTimeStamp = GetLatestTimeStamp();
TimeStamp startTimeStamp = GetLatestTimeStamp();
int frameCount = 0;
int totalFrameCount = 0;
ResetReverseIterator();
while (HasNext(startTimeStamp)) {
currentTimeStamp = GetNextTimeStamp();
TimeDuration interval = currentIntervalStart - currentTimeStamp;
if (interval.ToSeconds() >= 1.0 ) {
currentIntervalStart = currentTimeStamp;
aFpsData[frameCount]++;
frameCount = 0;
}
frameCount++;
totalFrameCount++;
}
TimeDuration totalTime = currentIntervalStart - currentTimeStamp;
printf_stderr("Discarded %d frames over %f ms in histogram for %s\n",
frameCount, totalTime.ToMilliseconds(), mFPSName);
return totalFrameCount;
}
// Iterate the same way we do in GetFPS()
void
FPSCounter::WriteFrameTimeStamps(PRFileDesc* fd)
{
const int bufferSize = 256;
char buffer[bufferSize];
int writtenCount = PR_snprintf(buffer, bufferSize, "FPS Data for: %s\n", mFPSName);
MOZ_ASSERT(writtenCount >= 0);
PR_Write(fd, buffer, writtenCount);
ResetReverseIterator();
TimeStamp startTimeStamp = GetLatestTimeStamp();
MOZ_ASSERT(HasNext(startTimeStamp));
TimeStamp previousSample = GetNextTimeStamp();
MOZ_ASSERT(HasNext(startTimeStamp));
TimeStamp nextTimeStamp = GetNextTimeStamp();
while (HasNext(startTimeStamp)) {
TimeDuration duration = previousSample - nextTimeStamp;
writtenCount = PR_snprintf(buffer, bufferSize, "%f,\n", duration.ToMilliseconds());
MOZ_ASSERT(writtenCount >= 0);
PR_Write(fd, buffer, writtenCount);
previousSample = nextTimeStamp;
nextTimeStamp = GetNextTimeStamp();
}
}
double
FPSCounter::GetMean(std::map<int, int> aHistogram)
{
double average = 0.0;
double samples = 0.0;
for (std::map<int, int>::iterator iter = aHistogram.begin();
iter != aHistogram.end(); ++iter)
{
int fps = iter->first;
int count = iter->second;
average += fps * count;
samples += count;
}
return average / samples;
}
double
FPSCounter::GetStdDev(std::map<int, int> aHistogram)
{
double sumOfDifferences = 0;
double average = GetMean(aHistogram);
double samples = 0.0;
for (std::map<int, int>::iterator iter = aHistogram.begin();
iter != aHistogram.end(); ++iter)
{
int fps = iter->first;
int count = iter->second;
double diff = ((double) fps) - average;
diff *= diff;
for (int i = 0; i < count; i++) {
sumOfDifferences += diff;
}
samples += count;
}
double stdDev = sumOfDifferences / samples;
return sqrt(stdDev);
}
void
FPSCounter::PrintFPS()
{
if (!gfxPrefs::FPSPrintHistogram()) {
return;
}
std::map<int, int> histogram;
int totalFrames = BuildHistogram(histogram);
TimeDuration measurementInterval = mFrameTimestamps[GetLatestReadIndex()] - mLastInterval;
printf_stderr("FPS for %s. Total Frames: %d Time Interval: %f seconds\n",
mFPSName, totalFrames, measurementInterval.ToSecondsSigDigits());
PrintHistogram(histogram);
}
void
FPSCounter::PrintHistogram(std::map<int, int>& aHistogram)
{
int length = 0;
const int kBufferLength = 512;
char buffer[kBufferLength];
for (std::map<int, int>::iterator iter = aHistogram.begin();
iter != aHistogram.end(); iter++)
{
int fps = iter->first;
int count = iter->second;
length += PR_snprintf(buffer + length, kBufferLength - length,
"FPS: %d = %d. ", fps, count);
NS_ASSERTION(length >= kBufferLength, "Buffer overrun while printing FPS histogram.");
}
printf_stderr("%s\n", buffer);
printf_stderr("Mean: %f , std dev %f\n", GetMean(aHistogram), GetStdDev(aHistogram));
}
// Write FPS timestamp data to a file only if
// draw-fps.write-to-file is true
nsresult
FPSCounter::WriteFrameTimeStamps()
{
if (!gfxPrefs::WriteFPSToFile()) {
return NS_OK;
}
MOZ_ASSERT(mWriteIndex == 0);
nsCOMPtr<nsIFile> resultFile;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(resultFile));
NS_ENSURE_SUCCESS(rv, rv);
if (!strncmp(mFPSName, "Compositor", strlen(mFPSName))) {
resultFile->Append(NS_LITERAL_STRING("fps.txt"));
} else {
resultFile->Append(NS_LITERAL_STRING("txn.txt"));
}
PRFileDesc* fd = nullptr;
int mode = 644;
int openFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
rv = resultFile->OpenNSPRFileDesc(openFlags, mode, &fd);
NS_ENSURE_SUCCESS(rv, rv);
WriteFrameTimeStamps(fd);
PR_Close(fd);
nsAutoCString path;
rv = resultFile->GetNativePath(path);
NS_ENSURE_SUCCESS(rv, rv);
printf_stderr("Wrote FPS data to file: %s\n", path.get());
return NS_OK;
}
FPSState::FPSState()
: mCompositionFps("Compositor")
, mTransactionFps("LayerTransactions")
{
}
// Size of the builtin font.
static const float FontHeight = 7.f;
static const float FontWidth = 4.f;
// Scale the font when drawing it to the viewport for better readability.
static const float FontScaleX = 2.f;
static const float FontScaleY = 3.f;
static void DrawDigits(unsigned int aValue,
int aOffsetX, int aOffsetY,
Compositor* aCompositor,
EffectChain& aEffectChain)
{
if (aValue > 999) {
aValue = 999;
}
unsigned int divisor = 100;
float textureWidth = FontWidth * 10;
gfx::Float opacity = 1;
gfx::Matrix4x4 transform;
transform.PreScale(FontScaleX, FontScaleY, 1);
for (size_t n = 0; n < 3; ++n) {
unsigned int digit = aValue % (divisor * 10) / divisor;
divisor /= 10;
RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
Rect clipRect = Rect(0, 0, 300, 100);
aCompositor->DrawQuad(drawRect, clipRect,
aEffectChain, opacity, transform);
}
}
void FPSState::DrawFPS(TimeStamp aNow,
int aOffsetX, int aOffsetY,
unsigned int aFillRatio,
Compositor* aCompositor)
{
if (!mFPSTextureSource) {
const char *text =
" "
" XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
" X X X X X X X X X X X X X X"
" X X X XXX XXX XXX XXX XXX X XXX XXX"
" X X X X X X X X X X X X X"
" XXX XXX XXX XXX X XXX XXX X XXX X"
" ";
// Convert the text encoding above to RGBA.
int w = FontWidth * 10;
int h = FontHeight;
uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
uint32_t purple = 0xfff000ff;
uint32_t white = 0xffffffff;
buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
}
}
int bytesPerPixel = 4;
RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
mFPSTextureSource = aCompositor->CreateDataTextureSource();
mFPSTextureSource->Update(fpsSurface);
}
EffectChain effectChain;
effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8,
mFPSTextureSource,
Filter::POINT,
true);
unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
unsigned int txnFps = unsigned(mTransactionFps.GetFPS(aNow));
DrawDigits(fps, aOffsetX + 0, aOffsetY, aCompositor, effectChain);
DrawDigits(txnFps, aOffsetX + FontWidth * 4, aOffsetY, aCompositor, effectChain);
DrawDigits(aFillRatio, aOffsetX + FontWidth * 8, aOffsetY, aCompositor, effectChain);
}
} // end namespace layers
} // end namespace mozilla