Bug 780074: Change the fps counter to provide better estimates of instantaneous fps. r=jrmuizel

This commit is contained in:
Chris Jones 2012-09-04 22:17:13 -07:00
parent a4736dd52a
commit d0c5ba562a
2 changed files with 247 additions and 225 deletions

View File

@ -48,6 +48,240 @@ using namespace mozilla::gl;
int ShaderProgramOGL::sCurrentProgramKey = 0;
#endif
static const double kFpsWindowMs = 250.0;
static const size_t kNumFrameTimeStamps = 16;
struct FPSCounter {
FPSCounter() : mCurrentFrameIndex(0) {}
// We keep a circular buffer of the time points at which the last K
// frames were drawn. To estimate FPS, we count the number of
// frames we've drawn within the last kFPSWindowMs milliseconds and
// divide by the amount time since the first of those frames.
TimeStamp mFrames[kNumFrameTimeStamps];
size_t mCurrentFrameIndex;
void AddFrame(TimeStamp aNewFrame) {
mFrames[mCurrentFrameIndex] = aNewFrame;
mCurrentFrameIndex = (mCurrentFrameIndex + 1) % kNumFrameTimeStamps;
}
double AddFrameAndGetFps(TimeStamp aCurrentFrame) {
AddFrame(aCurrentFrame);
return EstimateFps(aCurrentFrame);
}
double GetFpsAt(TimeStamp aNow) {
return EstimateFps(aNow);
}
private:
double EstimateFps(TimeStamp aNow) {
TimeStamp beginningOfWindow =
(aNow - TimeDuration::FromMilliseconds(kFpsWindowMs));
TimeStamp earliestFrameInWindow = aNow;
size_t numFramesDrawnInWindow = 0;
for (size_t i = 0; i < kNumFrameTimeStamps; ++i) {
const TimeStamp& frame = mFrames[i];
if (!frame.IsNull() && frame > beginningOfWindow) {
++numFramesDrawnInWindow;
earliestFrameInWindow = PR_MIN(earliestFrameInWindow, frame);
}
}
double realWindowSecs = (aNow - earliestFrameInWindow).ToSeconds();
if (realWindowSecs == 0.0) {
return 0.0;
}
return double(numFramesDrawnInWindow) / realWindowSecs;
}
};
struct FPSState {
GLuint mTexture;
FPSCounter mCompositionFps;
FPSCounter mTransactionFps;
FPSState() : mTexture(0) { }
void DrawFPS(TimeStamp, GLContext*, ShaderProgramOGL*);
void NotifyShadowTreeTransaction() {
mTransactionFps.AddFrame(TimeStamp::Now());
}
};
void
FPSState::DrawFPS(TimeStamp aNow,
GLContext* context, ShaderProgramOGL* copyprog)
{
int fps = int(mCompositionFps.AddFrameAndGetFps(aNow));
int txnFps = int(mTransactionFps.GetFpsAt(aNow));
GLint viewport[4];
context->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
if (!mTexture) {
// Bind the number of textures we need, in this case one.
context->fGenTextures(1, &mTexture);
context->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MIN_FILTER,LOCAL_GL_NEAREST);
context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MAG_FILTER,LOCAL_GL_NEAREST);
unsigned char text[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 255, 255, 0, 255, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0,
0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
// convert from 8 bit to 32 bit so that don't have to write the text above out in 32 bit format
// we rely on int being 32 bits
unsigned int* buf = (unsigned int*)malloc(64 * 8 * 4);
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 41; j++) {
unsigned int purple = 0xfff000ff;
unsigned int white = 0xffffffff;
buf[i * 64 + j] = (text[i * 41 + j] == 0) ? purple : white;
}
}
context->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 64, 8, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf);
free(buf);
}
struct Vertex2D {
float x,y;
};
const Vertex2D vertices[] = {
{ -1.0f, 1.0f - 42.f / viewport[3] },
{ -1.0f, 1.0f},
{ -1.0f + 22.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 22.f / viewport[2], 1.0f },
{ -1.0f + 22.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 22.f / viewport[2], 1.0f },
{ -1.0f + 44.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 44.f / viewport[2], 1.0f },
{ -1.0f + 44.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 44.f / viewport[2], 1.0f },
{ -1.0f + 66.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 66.f / viewport[2], 1.0f }
};
const Vertex2D vertices2[] = {
{ -1.0f + 80.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 80.f / viewport[2], 1.0f },
{ -1.0f + 102.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 102.f / viewport[2], 1.0f },
{ -1.0f + 102.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 102.f / viewport[2], 1.0f },
{ -1.0f + 124.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 124.f / viewport[2], 1.0f },
{ -1.0f + 124.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 124.f / viewport[2], 1.0f },
{ -1.0f + 146.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 146.f / viewport[2], 1.0f },
};
int v1 = fps % 10;
int v10 = (fps % 100) / 10;
int v100 = (fps % 1000) / 100;
int txn1 = txnFps % 10;
int txn10 = (txnFps % 100) / 10;
int txn100 = (txnFps % 1000) / 100;
// Feel free to comment these texture coordinates out and use one
// of the ones below instead, or play around with your own values.
const GLfloat texCoords[] = {
(v100 * 4.f) / 64, 7.f / 8,
(v100 * 4.f) / 64, 0.0f,
(v100 * 4.f + 4) / 64, 7.f / 8,
(v100 * 4.f + 4) / 64, 0.0f,
(v10 * 4.f) / 64, 7.f / 8,
(v10 * 4.f) / 64, 0.0f,
(v10 * 4.f + 4) / 64, 7.f / 8,
(v10 * 4.f + 4) / 64, 0.0f,
(v1 * 4.f) / 64, 7.f / 8,
(v1 * 4.f) / 64, 0.0f,
(v1 * 4.f + 4) / 64, 7.f / 8,
(v1 * 4.f + 4) / 64, 0.0f,
};
const GLfloat texCoords2[] = {
(txn100 * 4.f) / 64, 7.f / 8,
(txn100 * 4.f) / 64, 0.0f,
(txn100 * 4.f + 4) / 64, 7.f / 8,
(txn100 * 4.f + 4) / 64, 0.0f,
(txn10 * 4.f) / 64, 7.f / 8,
(txn10 * 4.f) / 64, 0.0f,
(txn10 * 4.f + 4) / 64, 7.f / 8,
(txn10 * 4.f + 4) / 64, 0.0f,
(txn1 * 4.f) / 64, 7.f / 8,
(txn1 * 4.f) / 64, 0.0f,
(txn1 * 4.f + 4) / 64, 7.f / 8,
(txn1 * 4.f + 4) / 64, 0.0f,
};
// Turn necessary features on
context->fEnable(LOCAL_GL_BLEND);
context->fBlendFunc(LOCAL_GL_ONE, LOCAL_GL_SRC_COLOR);
context->fActiveTexture(LOCAL_GL_TEXTURE0);
context->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
copyprog->Activate();
copyprog->SetTextureUnit(0);
// we're going to use client-side vertex arrays for this.
context->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
// "COPY"
context->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ZERO,
LOCAL_GL_ONE, LOCAL_GL_ZERO);
// enable our vertex attribs; we'll call glVertexPointer below
// to fill with the correct data.
GLint vcattr = copyprog->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
GLint tcattr = copyprog->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
context->fEnableVertexAttribArray(vcattr);
context->fEnableVertexAttribArray(tcattr);
context->fVertexAttribPointer(vcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, vertices);
context->fVertexAttribPointer(tcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, texCoords);
context->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 12);
context->fVertexAttribPointer(vcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, vertices2);
context->fVertexAttribPointer(tcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, texCoords2);
context->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 12);
}
/**
* LayerManagerOGL
*/
@ -514,202 +748,6 @@ LayerManagerOGL::RootLayer() const
bool LayerManagerOGL::sDrawFPS = false;
void
LayerManagerOGL::FPSState::NotifyShadowTreeTransaction()
{
contentFCount++;
}
/* This function tries to stick to portable C89 as much as possible
* so that it can be easily copied into other applications */
void
LayerManagerOGL::FPSState::DrawFPS(GLContext* context, ShaderProgramOGL* copyprog)
{
fcount++;
int rate = 30;
if (fcount >= rate) {
TimeStamp now = TimeStamp::Now();
TimeDuration duration = now - last;
last = now;
fps = rate / duration.ToSeconds() + .5;
fcount = 0;
}
if (contentFCount >= rate) {
TimeStamp now = TimeStamp::Now();
TimeDuration duration = now - contentLast;
contentLast = now;
contentFps = contentFCount / duration.ToSeconds() + .5;
contentFCount = 0;
}
GLint viewport[4];
context->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
static GLuint texture;
if (!initialized) {
// Bind the number of textures we need, in this case one.
context->fGenTextures(1, &texture);
context->fBindTexture(LOCAL_GL_TEXTURE_2D, texture);
context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MIN_FILTER,LOCAL_GL_NEAREST);
context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MAG_FILTER,LOCAL_GL_NEAREST);
unsigned char text[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 255, 255, 0, 255, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0,
0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
// convert from 8 bit to 32 bit so that don't have to write the text above out in 32 bit format
// we rely on int being 32 bits
unsigned int* buf = (unsigned int*)malloc(64 * 8 * 4);
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 41; j++) {
unsigned int purple = 0xfff000ff;
unsigned int white = 0xffffffff;
buf[i * 64 + j] = (text[i * 41 + j] == 0) ? purple : white;
}
}
context->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 64, 8, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf);
free(buf);
initialized = true;
}
struct Vertex2D {
float x,y;
};
const Vertex2D vertices[] = {
{ -1.0f, 1.0f - 42.f / viewport[3] },
{ -1.0f, 1.0f},
{ -1.0f + 22.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 22.f / viewport[2], 1.0f },
{ -1.0f + 22.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 22.f / viewport[2], 1.0f },
{ -1.0f + 44.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 44.f / viewport[2], 1.0f },
{ -1.0f + 44.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 44.f / viewport[2], 1.0f },
{ -1.0f + 66.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 66.f / viewport[2], 1.0f },
};
const Vertex2D vertices2[] = {
{ -1.0f + 80.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 80.f / viewport[2], 1.0f },
{ -1.0f + 102.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 102.f / viewport[2], 1.0f },
{ -1.0f + 102.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 102.f / viewport[2], 1.0f },
{ -1.0f + 124.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 124.f / viewport[2], 1.0f },
{ -1.0f + 124.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 124.f / viewport[2], 1.0f },
{ -1.0f + 146.f / viewport[2], 1.0f - 42.f / viewport[3] },
{ -1.0f + 146.f / viewport[2], 1.0f },
};
int v1 = fps % 10;
int v10 = (fps % 100) / 10;
int v100 = (fps % 1000) / 100;
int content1 = contentFps % 10;
int content10 = (contentFps % 100) / 10;
int content100 = (contentFps % 1000) / 100;
// Feel free to comment these texture coordinates out and use one
// of the ones below instead, or play around with your own values.
const GLfloat texCoords[] = {
(v100 * 4.f) / 64, 7.f / 8,
(v100 * 4.f) / 64, 0.0f,
(v100 * 4.f + 4) / 64, 7.f / 8,
(v100 * 4.f + 4) / 64, 0.0f,
(v10 * 4.f) / 64, 7.f / 8,
(v10 * 4.f) / 64, 0.0f,
(v10 * 4.f + 4) / 64, 7.f / 8,
(v10 * 4.f + 4) / 64, 0.0f,
(v1 * 4.f) / 64, 7.f / 8,
(v1 * 4.f) / 64, 0.0f,
(v1 * 4.f + 4) / 64, 7.f / 8,
(v1 * 4.f + 4) / 64, 0.0f,
};
const GLfloat texCoords2[] = {
(content100 * 4.f) / 64, 7.f / 8,
(content100 * 4.f) / 64, 0.0f,
(content100 * 4.f + 4) / 64, 7.f / 8,
(content100 * 4.f + 4) / 64, 0.0f,
(content10 * 4.f) / 64, 7.f / 8,
(content10 * 4.f) / 64, 0.0f,
(content10 * 4.f + 4) / 64, 7.f / 8,
(content10 * 4.f + 4) / 64, 0.0f,
(content1 * 4.f) / 64, 7.f / 8,
(content1 * 4.f) / 64, 0.0f,
(content1 * 4.f + 4) / 64, 7.f / 8,
(content1 * 4.f + 4) / 64, 0.0f,
};
// Turn necessary features on
context->fEnable(LOCAL_GL_BLEND);
context->fBlendFunc(LOCAL_GL_ONE, LOCAL_GL_SRC_COLOR);
context->fActiveTexture(LOCAL_GL_TEXTURE0);
context->fBindTexture(LOCAL_GL_TEXTURE_2D, texture);
copyprog->Activate();
copyprog->SetTextureUnit(0);
// we're going to use client-side vertex arrays for this.
context->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
// "COPY"
context->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ZERO,
LOCAL_GL_ONE, LOCAL_GL_ZERO);
// enable our vertex attribs; we'll call glVertexPointer below
// to fill with the correct data.
GLint vcattr = copyprog->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
GLint tcattr = copyprog->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
context->fEnableVertexAttribArray(vcattr);
context->fEnableVertexAttribArray(tcattr);
context->fVertexAttribPointer(vcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, vertices);
context->fVertexAttribPointer(tcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, texCoords);
context->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 12);
context->fVertexAttribPointer(vcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, vertices2);
context->fVertexAttribPointer(tcattr,
2, LOCAL_GL_FLOAT,
LOCAL_GL_FALSE,
0, texCoords2);
context->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 12);
}
// |aTexCoordRect| is the rectangle from the texture that we want to
// draw using the given program. The program already has a necessary
// offset and scale, so the geometry that needs to be drawn is a unit
@ -788,7 +826,9 @@ LayerManagerOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
void
LayerManagerOGL::NotifyShadowTreeTransaction()
{
mFPS.NotifyShadowTreeTransaction();
if (mFPS) {
mFPS->NotifyShadowTreeTransaction();
}
}
void
@ -900,8 +940,14 @@ LayerManagerOGL::Render()
return;
}
if (sDrawFPS) {
mFPS.DrawFPS(mGLContext, GetProgram(Copy2DProgramType));
if (sDrawFPS && !mFPS) {
mFPS = new FPSState();
} else if (!sDrawFPS && mFPS) {
mFPS = nullptr;
}
if (mFPS) {
mFPS->DrawFPS(TimeStamp::Now(), mGLContext, GetProgram(Copy2DProgramType));
}
if (mGLContext->IsDoubleBuffered()) {

View File

@ -42,6 +42,7 @@ class ShadowContainerLayer;
class ShadowImageLayer;
class ShadowCanvasLayer;
class ShadowColorLayer;
struct FPSState;
/**
* This is the LayerManager used for OpenGL 2.1 and OpenGL ES 2.0.
@ -438,32 +439,7 @@ private:
DrawThebesLayerCallback mThebesLayerCallback;
void *mThebesLayerCallbackData;
gfxMatrix mWorldMatrix;
struct FPSState
{
GLuint texture;
int fps;
bool initialized;
int fcount;
TimeStamp last;
int contentFps;
int contentFCount;
TimeStamp contentLast;
FPSState()
: texture(0)
, fps(0)
, initialized(false)
, fcount(0)
, contentFps(0)
, contentFCount(0)
{
contentLast = last = TimeStamp::Now();
}
void DrawFPS(GLContext*, ShaderProgramOGL*);
void NotifyShadowTreeTransaction();
} mFPS;
nsAutoPtr<FPSState> mFPS;
static bool sDrawFPS;
};