Bug 737967 - Add simple platform GPU Profiling based on GL_TIME_ELAPSED_EXT. r=ehsan,jgilbert

This commit is contained in:
Benoit Girard 2014-05-24 16:14:14 +00:00
parent b0a9ca79de
commit 005eb0f56e
12 changed files with 318 additions and 1 deletions

View File

@ -173,6 +173,9 @@ CompositorOGL::CleanupResources()
mQuadVBO = 0;
}
mGLContext->MakeCurrent();
mContextStateTracker.DestroyOGL(mGLContext);
// On the main thread the Widget will be destroyed soon and calling MakeCurrent
// after that could cause a crash (at least with GLX, see bug 1059793), unless
// context is marked as destroyed.
@ -664,6 +667,8 @@ CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface)
= static_cast<CompositingRenderTargetOGL*>(aSurface);
if (mCurrentRenderTarget != surface) {
mCurrentRenderTarget = surface;
mContextStateTracker.PopOGLSection(gl(), "Frame");
mContextStateTracker.PushOGLSection(gl(), "Frame");
surface->BindRenderTarget();
}
}
@ -768,6 +773,8 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
CompositingRenderTargetOGL::RenderTargetForWindow(this,
IntSize(width, height));
mCurrentRenderTarget->BindRenderTarget();
mContextStateTracker.PushOGLSection(gl(), "Frame");
#ifdef DEBUG
mWindowRenderTarget = mCurrentRenderTarget;
#endif
@ -1345,6 +1352,8 @@ CompositorOGL::EndFrame()
}
#endif
mContextStateTracker.PopOGLSection(gl(), "Frame");
mFrameInProgress = false;
if (mTarget) {

View File

@ -6,6 +6,7 @@
#ifndef MOZILLA_GFX_COMPOSITOROGL_H
#define MOZILLA_GFX_COMPOSITOROGL_H
#include "ContextStateTracker.h"
#include "gfx2DGlue.h"
#include "GLContextTypes.h" // for GLContext, etc
#include "GLDefs.h" // for GLuint, LOCAL_GL_TEXTURE_2D, etc
@ -387,6 +388,8 @@ private:
RefPtr<CompositorTexturePoolOGL> mTexturePool;
ContextStateTrackerOGL mContextStateTracker;
bool mDestroyed;
/**

View File

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "ContextStateTracker.h"
#include "GLContext.h"
#include "ProfilerMarkers.h"
namespace mozilla {
void
ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
{
if (!profiler_feature_active("gpu")) {
return;
}
if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
return;
}
if (mSectionStack.Length() > 0) {
// We need to end the query since we're starting a new section and restore it
// when this section is finished.
aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
Top().mCpuTimeEnd = TimeStamp::Now();
}
ContextState newSection(aSectionName);
GLuint queryObject;
aGL->fGenQueries(1, &queryObject);
newSection.mStartQueryHandle = queryObject;
newSection.mCpuTimeStart = TimeStamp::Now();
aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
mSectionStack.AppendElement(newSection);
}
void
ContextStateTrackerOGL::PopOGLSection(GLContext* aGL, const char* aSectionName)
{
// We might have ignored a section start if we started profiling
// in the middle section. If so we will ignore this unmatched end.
if (mSectionStack.Length() == 0) {
return;
}
int i = mSectionStack.Length() - 1;
MOZ_ASSERT(strcmp(mSectionStack[i].mSectionName, aSectionName) == 0);
aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
mSectionStack[i].mCpuTimeEnd = TimeStamp::Now();
mCompletedSections.AppendElement(mSectionStack[i]);
mSectionStack.RemoveElementAt(i);
if (i - 1 >= 0) {
const char* sectionToRestore = Top().mSectionName;
// We need to restore the outer section
// Well do this by completing this section and adding a new
// one with the same name
mCompletedSections.AppendElement(Top());
mSectionStack.RemoveElementAt(i - 1);
ContextState newSection(sectionToRestore);
GLuint queryObject;
aGL->fGenQueries(1, &queryObject);
newSection.mStartQueryHandle = queryObject;
newSection.mCpuTimeStart = TimeStamp::Now();
aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
mSectionStack.AppendElement(newSection);
}
Flush(aGL);
}
void
ContextStateTrackerOGL::Flush(GLContext* aGL)
{
TimeStamp now = TimeStamp::Now();
while (mCompletedSections.Length() != 0) {
// On mac we see QUERY_RESULT_AVAILABLE cause a GL flush if we query it
// too early. For profiling we rather have the last 200ms of data missing
// while causing let's measurement distortions.
if (mCompletedSections[0].mCpuTimeEnd + TimeDuration::FromMilliseconds(200) > now) {
break;
}
GLuint handle = mCompletedSections[0].mStartQueryHandle;
// We've waiting 200ms, content rendering at > 20 FPS will be ready. We
// shouldn't see any flushes now.
GLuint returned = 0;
aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
if (!returned) {
break;
}
GLuint gpuTime = 0;
aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT, &gpuTime);
aGL->fDeleteQueries(1, &handle);
PROFILER_MARKER_PAYLOAD("gpu_timer_query", new GPUMarkerPayload(
mCompletedSections[0].mCpuTimeStart,
mCompletedSections[0].mCpuTimeEnd,
0,
gpuTime
));
mCompletedSections.RemoveElementAt(0);
}
}
void
ContextStateTrackerOGL::DestroyOGL(GLContext* aGL)
{
while (mCompletedSections.Length() != 0) {
GLuint handle = (GLuint)mCompletedSections[0].mStartQueryHandle;
aGL->fDeleteQueries(1, &handle);
mCompletedSections.RemoveElementAt(0);
}
}
}

View File

@ -0,0 +1,81 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
#ifndef GFX_CONTEXTSTATETRACKER_H
#define GFX_CONTEXTSTATETRACKER_H
#include "GLTypes.h"
#include "mozilla/TimeStamp.h"
#include "nsTArray.h"
#include <string.h>
namespace mozilla {
namespace gl {
class GLContext;
}
/**
* This class tracks the state of the context for debugging and profiling.
* Each section pushes a new stack entry and must be matched by an end section.
* All nested section must be ended before ending a parent section.
*/
class ContextStateTracker {
public:
ContextStateTracker() {}
private:
bool IsProfiling() { return true; }
protected:
typedef GLuint TimerQueryHandle;
class ContextState {
public:
ContextState(const char* aSectionName)
: mSectionName(aSectionName)
{}
const char* mSectionName;
mozilla::TimeStamp mCpuTimeStart;
mozilla::TimeStamp mCpuTimeEnd;
TimerQueryHandle mStartQueryHandle;
};
ContextState& Top() {
MOZ_ASSERT(mSectionStack.Length());
return mSectionStack[mSectionStack.Length() - 1];
}
nsTArray<ContextState> mCompletedSections;
nsTArray<ContextState> mSectionStack;
};
class ID3D11DeviceContext;
class ContextStateTrackerD3D11 MOZ_FINAL : public ContextStateTracker {
public:
// TODO Implement me
void PushD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
void PopD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
void DestroyD3D11(ID3D11DeviceContext* aCtxt) {}
private:
void Flush();
};
class ContextStateTrackerOGL MOZ_FINAL : public ContextStateTracker {
typedef mozilla::gl::GLContext GLContext;
public:
void PushOGLSection(GLContext* aGL, const char* aSectionName);
void PopOGLSection(GLContext* aGL, const char* aSectionName);
void DestroyOGL(GLContext* aGL);
private:
void Flush(GLContext* aGL);
};
}
#endif

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'ContextStateTracker.h',
'DrawMode.h',
'gfx2DGlue.h',
'gfx3DMatrix.h',
@ -196,6 +197,7 @@ if CONFIG['INTEL_ARCHITECTURE']:
SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
SOURCES += [
'ContextStateTracker.cpp',
# Includes mac system header conflicting with point/size,
# and includes glxXlibSurface.h which drags in Xrender.h
'gfxASurface.cpp',

View File

@ -146,6 +146,11 @@ static inline void profiler_free_backtrace(ProfilerBacktrace* aBacktrace) {}
static inline bool profiler_is_active() { return false; }
// Check if an external profiler feature is active.
// Supported:
// * gpu
static inline bool profiler_feature_active(const char*) { return false; }
// Internal-only. Used by the event tracer.
static inline void profiler_responsiveness(const mozilla::TimeStamp& aTime) {}

View File

@ -44,6 +44,8 @@ void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace);
bool mozilla_sampler_is_active();
bool mozilla_sampler_feature_active(const char* aName);
void mozilla_sampler_responsiveness(const mozilla::TimeStamp& time);
void mozilla_sampler_frame_number(int frameNumber);

View File

@ -123,6 +123,12 @@ bool profiler_is_active()
return mozilla_sampler_is_active();
}
static inline
bool profiler_feature_active(const char* aName)
{
return mozilla_sampler_feature_active(aName);
}
static inline
void profiler_responsiveness(const mozilla::TimeStamp& aTime)
{

View File

@ -84,6 +84,34 @@ ProfilerMarkerTracing::streamPayloadImp(JSStreamWriter& b)
b.EndObject();
}
GPUMarkerPayload::GPUMarkerPayload(
const mozilla::TimeStamp& aCpuTimeStart,
const mozilla::TimeStamp& aCpuTimeEnd,
uint64_t aGpuTimeStart,
uint64_t aGpuTimeEnd)
: ProfilerMarkerPayload(aCpuTimeStart, aCpuTimeEnd)
, mCpuTimeStart(aCpuTimeStart)
, mCpuTimeEnd(aCpuTimeEnd)
, mGpuTimeStart(aGpuTimeStart)
, mGpuTimeEnd(aGpuTimeEnd)
{
}
void
GPUMarkerPayload::streamPayloadImp(JSStreamWriter& b)
{
b.BeginObject();
streamCommonProps("gpu_timer_query", b);
b.NameValue("cpustart", profiler_time(mCpuTimeStart));
b.NameValue("cpuend", profiler_time(mCpuTimeEnd));
b.NameValue("gpustart", (int)mGpuTimeStart);
b.NameValue("gpuend", (int)mGpuTimeEnd);
b.EndObject();
}
ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
: mImg(aImg)
{}

View File

@ -8,6 +8,7 @@
#include "JSStreamWriter.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Attributes.h"
#include "nsAutoPtr.h"
#include "Units.h" // For ScreenIntPoint
@ -52,6 +53,8 @@ public:
return streamPayload(b);
}
mozilla::TimeStamp GetStartTime() const { return mStartTime; }
protected:
/**
* Called from the main thread
@ -186,4 +189,26 @@ private:
mozilla::TimeStamp mVsyncTimestamp;
};
class GPUMarkerPayload : public ProfilerMarkerPayload
{
public:
GPUMarkerPayload(const mozilla::TimeStamp& aCpuTimeStart,
const mozilla::TimeStamp& aCpuTimeEnd,
uint64_t aGpuTimeStart,
uint64_t aGpuTimeEnd);
~GPUMarkerPayload() {}
protected:
virtual void
streamPayload(JSStreamWriter& b) MOZ_OVERRIDE { return streamPayloadImp(b); }
private:
void streamPayloadImp(JSStreamWriter& b);
mozilla::TimeStamp mCpuTimeStart;
mozilla::TimeStamp mCpuTimeEnd;
uint64_t mGpuTimeStart;
uint64_t mGpuTimeEnd;
};
#endif // PROFILER_MARKERS_H

View File

@ -67,6 +67,7 @@ class TableTicker: public Sampler {
mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
mProfileJava = hasFeature(aFeatures, aFeatureCount, "java");
mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu");
mProfilePower = hasFeature(aFeatures, aFeatureCount, "power");
// Users sometimes ask to filter by a list of threads but forget to request
// profiling non main threads. Let's make it implificit if we have a filter
@ -200,6 +201,7 @@ class TableTicker: public Sampler {
bool HasUnwinderThread() const { return mUnwinderThread; }
bool ProfileJS() const { return mProfileJS; }
bool ProfileJava() const { return mProfileJava; }
bool ProfileGPU() const { return mProfileGPU; }
bool ProfilePower() const { return mProfilePower; }
bool ProfileThreads() const { return mProfileThreads; }
bool InPrivacyMode() const { return mPrivacyMode; }
@ -226,6 +228,7 @@ protected:
bool mUseStackWalk;
bool mJankOnly;
bool mProfileJS;
bool mProfileGPU;
bool mProfileThreads;
bool mUnwinderThread;
bool mProfileJava;

View File

@ -44,6 +44,7 @@ int sFrameNumber = 0;
int sLastFrameNumber = 0;
int sInitCount = 0; // Each init must have a matched shutdown.
static bool sIsProfiling = false; // is raced on
static bool sIsGPUProfiling = false; // is raced on
// env variables to control the profiler
const char* PROFILER_MODE = "MOZ_PROFILER_MODE";
@ -681,6 +682,8 @@ const char** mozilla_sampler_get_features()
// Tell the JS engine to emmit pseudostack entries in the
// pro/epilogue.
"js",
// GPU Profiling (may not be supported by the GL)
"gpu",
// Profile the registered secondary threads.
"threads",
// Do not include user-identifiable information
@ -782,6 +785,7 @@ void mozilla_sampler_start(int aProfileEntries, double aInterval,
}
sIsProfiling = true;
sIsGPUProfiling = t->ProfileGPU();
if (Sampler::CanNotifyObservers()) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
@ -865,6 +869,19 @@ void mozilla_sampler_resume() {
}
}
bool mozilla_sampler_feature_active(const char* aName)
{
if (!profiler_is_active()) {
return false;
}
if (strcmp(aName, "gpu") == 0) {
return sIsGPUProfiling;
}
return false;
}
bool mozilla_sampler_is_active()
{
return sIsProfiling;
@ -1041,7 +1058,10 @@ void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPay
if (!stack) {
return;
}
mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ?
aPayload->GetStartTime() : mozilla::TimeStamp::Now();
mozilla::TimeDuration delta = origin - sStartTime;
stack->addMarker(aMarker, payload.forget(), static_cast<float>(delta.ToMilliseconds()));
}