Commit 882e1b72 authored by dyen's avatar dyen Committed by Commit bot

Added support for GPU Tracing on mobile devices which support it.

Newer mobile devices may support the Open GL extension
EXT_disjoint_timer_query, these devices can now be traced in Chrome.

R=vmiura@chromium.org
BUG= https://code.google.com/p/chromium/issues/detail?id=397294
TEST= trybots

Review URL: https://codereview.chromium.org/509723002

Cr-Commit-Position: refs/heads/master@{#293150}
parent bbde62b5
......@@ -59,45 +59,68 @@ void TraceOutputter::Trace(const std::string& name,
++local_trace_id_;
}
GPUTrace::GPUTrace(const std::string& name)
: name_(name),
outputter_(NULL),
offset_(0),
end_time_(0),
end_requested_(false),
enabled_(false) {
}
GPUTrace::GPUTrace(scoped_refptr<Outputter> outputter,
const std::string& name,
int64 offset)
int64 offset,
GpuTracerType tracer_type)
: name_(name),
outputter_(outputter),
offset_(offset),
start_time_(0),
end_time_(0),
end_requested_(false),
enabled_(true) {
glGenQueries(2, queries_);
tracer_type_(tracer_type),
end_requested_(false) {
memset(queries_, 0, sizeof(queries_));
switch (tracer_type_) {
case kTracerTypeARBTimer:
case kTracerTypeDisjointTimer:
glGenQueriesARB(2, queries_);
break;
default:
tracer_type_ = kTracerTypeInvalid;
}
}
GPUTrace::~GPUTrace() {
if (enabled_)
glDeleteQueries(2, queries_);
switch (tracer_type_) {
case kTracerTypeInvalid:
break;
case kTracerTypeARBTimer:
case kTracerTypeDisjointTimer:
glDeleteQueriesARB(2, queries_);
break;
}
}
void GPUTrace::Start() {
TRACE_EVENT_COPY_ASYNC_BEGIN0(
TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
if (enabled_) {
glQueryCounter(queries_[0], GL_TIMESTAMP);
switch (tracer_type_) {
case kTracerTypeInvalid:
break;
case kTracerTypeARBTimer:
case kTracerTypeDisjointTimer:
// GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
glQueryCounter(queries_[0], GL_TIMESTAMP);
break;
}
}
void GPUTrace::End() {
if (enabled_) {
glQueryCounter(queries_[1], GL_TIMESTAMP);
end_requested_ = true;
end_requested_ = true;
switch (tracer_type_) {
case kTracerTypeInvalid:
break;
case kTracerTypeARBTimer:
case kTracerTypeDisjointTimer:
// GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
glQueryCounter(queries_[1], GL_TIMESTAMP);
break;
}
TRACE_EVENT_COPY_ASYNC_END0(
......@@ -105,34 +128,36 @@ void GPUTrace::End() {
}
bool GPUTrace::IsAvailable() {
if (!enabled_)
return true;
else if (!end_requested_)
return false;
if (tracer_type_ != kTracerTypeInvalid) {
if (!end_requested_)
return false;
GLint done = 0;
glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done);
return !!done;
GLint done = 0;
glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done);
return !!done;
}
return true;
}
void GPUTrace::Process() {
if (!enabled_)
if (tracer_type_ == kTracerTypeInvalid)
return;
DCHECK(IsAvailable());
GLuint64 timestamp;
GLuint64 begin_stamp = 0;
GLuint64 end_stamp = 0;
// TODO(dsinclair): It's possible for the timer to wrap during the start/end.
// We need to detect if the end is less then the start and correct for the
// wrapping.
glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &timestamp);
start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &begin_stamp);
glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, &end_stamp);
glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, &timestamp);
end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
glDeleteQueries(2, queries_);
start_time_ = (begin_stamp / base::Time::kNanosecondsPerMicrosecond) +
offset_;
end_time_ = (end_stamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
outputter_->Trace(name(), start_time_, end_time_);
}
......@@ -144,13 +169,16 @@ GPUTracer::GPUTracer(gles2::GLES2Decoder* decoder)
decoder_(decoder),
timer_offset_(0),
last_tracer_source_(kTraceGroupInvalid),
enabled_(false),
tracer_type_(kTracerTypeInvalid),
gpu_timing_synced_(false),
gpu_executing_(false),
process_posted_(false) {
if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) {
if (gfx::g_driver_gl.ext.b_GL_EXT_disjoint_timer_query) {
tracer_type_ = kTracerTypeDisjointTimer;
outputter_ = TraceOutputter::Create("GL_EXT_disjoint_timer_query");
} else if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) {
tracer_type_ = kTracerTypeARBTimer;
outputter_ = TraceOutputter::Create("GL_ARB_timer_query");
enabled_ = true;
}
}
......@@ -158,25 +186,19 @@ GPUTracer::~GPUTracer() {
}
bool GPUTracer::BeginDecoding() {
if (enabled_) {
if (*gpu_trace_dev_category) {
// Make sure timing is synced before tracing
if (!gpu_timing_synced_) {
CalculateTimerOffset();
gpu_timing_synced_ = true;
}
} else {
// If GPU device category is off, invalidate timing sync
gpu_timing_synced_ = false;
}
}
if (gpu_executing_)
return false;
CalculateTimerOffset();
gpu_executing_ = true;
if (IsTracing()) {
// Reset disjoint bit for the disjoint timer.
if (tracer_type_ == kTracerTypeDisjointTimer) {
GLint disjoint_value = 0;
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value);
}
// Begin a Trace for all active markers
for (int n = 0; n < NUM_TRACER_SOURCES; n++) {
for (size_t i = 0; i < markers_[n].size(); i++) {
......@@ -272,10 +294,10 @@ const std::string& GPUTracer::CurrentName() const {
}
scoped_refptr<GPUTrace> GPUTracer::CreateTrace(const std::string& name) {
if (enabled_ && *gpu_trace_dev_category)
return new GPUTrace(outputter_, name, timer_offset_);
else
return new GPUTrace(name);
GpuTracerType tracer_type = *gpu_trace_dev_category ? tracer_type_ :
kTracerTypeInvalid;
return new GPUTrace(outputter_, name, timer_offset_, tracer_type);
}
void GPUTracer::Process() {
......@@ -285,11 +307,8 @@ void GPUTracer::Process() {
}
void GPUTracer::ProcessTraces() {
if (!enabled_) {
while (!traces_.empty() && traces_.front()->IsAvailable()) {
traces_.front()->Process();
traces_.pop_front();
}
if (tracer_type_ == kTracerTypeInvalid) {
traces_.clear();
return;
}
......@@ -302,6 +321,14 @@ void GPUTracer::ProcessTraces() {
return;
}
// Check if disjoint operation has occurred, discard ongoing traces if so.
if (tracer_type_ == kTracerTypeDisjointTimer) {
GLint disjoint_value = 0;
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value);
if (disjoint_value)
traces_.clear();
}
while (!traces_.empty() && traces_.front()->IsAvailable()) {
traces_.front()->Process();
traces_.pop_front();
......@@ -314,23 +341,48 @@ void GPUTracer::ProcessTraces() {
}
void GPUTracer::CalculateTimerOffset() {
if (enabled_) {
if (tracer_type_ != kTracerTypeInvalid) {
// If GPU device category is off, invalidate timing sync.
if (*gpu_trace_dev_category == '\0') {
gpu_timing_synced_ = false;
return;
}
if (gpu_timing_synced_)
return;
TRACE_EVENT0("gpu", "GPUTracer::CalculateTimerOffset");
// NOTE(vmiura): It would be better to use glGetInteger64v, however
// it's not available everywhere.
GLuint64 gl_now = 0;
GLuint query;
GLint disjoint_value = 0;
if (tracer_type_ == kTracerTypeDisjointTimer) {
// Clear the disjoint bit before we do any queries.
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value);
}
glFinish();
glGenQueries(1, &query);
glGenQueriesARB(1, &query);
glQueryCounter(query, GL_TIMESTAMP);
glFinish();
glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now);
glDeleteQueriesARB(1, &query);
if (tracer_type_ == kTracerTypeDisjointTimer) {
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value);
if (disjoint_value)
return;
}
base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime();
gl_now /= base::Time::kNanosecondsPerMicrosecond;
timer_offset_ = system_now.ToInternalValue() - gl_now;
glDeleteQueries(1, &query);
gpu_timing_synced_ = true;
}
}
......
......@@ -33,6 +33,13 @@ enum GpuTracerSource {
NUM_TRACER_SOURCES
};
enum GpuTracerType {
kTracerTypeInvalid = -1,
kTracerTypeARBTimer,
kTracerTypeDisjointTimer
};
// Marker structure for a Trace.
struct TraceMarker {
TraceMarker(const std::string& name);
......@@ -86,7 +93,7 @@ class GPUTracer : public base::SupportsWeakPtr<GPUTracer> {
int64 timer_offset_;
GpuTracerSource last_tracer_source_;
bool enabled_;
GpuTracerType tracer_type_;
bool gpu_timing_synced_;
bool gpu_executing_;
bool process_posted_;
......@@ -126,12 +133,12 @@ class TraceOutputter : public Outputter {
class GPU_EXPORT GPUTrace
: public base::RefCounted<GPUTrace> {
public:
explicit GPUTrace(const std::string& name);
GPUTrace(scoped_refptr<Outputter> outputter,
const std::string& name,
int64 offset);
int64 offset,
GpuTracerType tracer_type);
bool IsEnabled() { return enabled_; }
bool IsEnabled() { return tracer_type_ != kTracerTypeInvalid; }
const std::string& name() { return name_; }
void Start();
......@@ -152,8 +159,8 @@ class GPU_EXPORT GPUTrace
int64 offset_;
int64 start_time_;
int64 end_time_;
GpuTracerType tracer_type_;
bool end_requested_;
bool enabled_;
GLuint queries_[2];
......
......@@ -51,14 +51,14 @@ class GlFakeQueries {
void SetCurrentGLTime(GLint64 current_time) { current_time_ = current_time; }
void GenQueries(GLsizei n, GLuint* ids) {
void GenQueriesARB(GLsizei n, GLuint* ids) {
for (GLsizei i = 0; i < n; i++) {
ids[i] = next_query_id_++;
alloced_queries_.insert(ids[i]);
}
}
void DeleteQueries(GLsizei n, const GLuint* ids) {
void DeleteQueriesARB(GLsizei n, const GLuint* ids) {
for (GLsizei i = 0; i < n; i++) {
alloced_queries_.erase(ids[i]);
query_timestamp_.erase(ids[i]);
......@@ -109,56 +109,13 @@ class GlFakeQueries {
std::map<GLuint, GLint64> query_timestamp_;
};
class GpuTracerTest : public GpuServiceTest {
class BaseGpuTracerTest : public GpuServiceTest {
public:
GpuTracerTest() {}
BaseGpuTracerTest() {}
///////////////////////////////////////////////////////////////////////////
protected:
virtual void SetUp() {
GpuServiceTest::SetUp();
gl_fake_queries_.Reset();
}
virtual void TearDown() {
gl_.reset();
gl_fake_queries_.Reset();
GpuServiceTest::TearDown();
}
void SetupTimerQueryMocks() {
// Delegate query APIs used by GPUTrace to a GlFakeQueries
EXPECT_CALL(*gl_, GenQueries(_, NotNull())).Times(AtLeast(1)).WillOnce(
Invoke(&gl_fake_queries_, &GlFakeQueries::GenQueries));
EXPECT_CALL(*gl_, GetQueryObjectiv(_, GL_QUERY_RESULT_AVAILABLE, NotNull()))
.Times(AtLeast(2))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::GetQueryObjectiv));
EXPECT_CALL(*gl_, QueryCounter(_, GL_TIMESTAMP))
.Times(AtLeast(2))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::QueryCounter));
EXPECT_CALL(*gl_, GetQueryObjectui64v(_, GL_QUERY_RESULT, NotNull()))
.Times(AtLeast(2))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::GetQueryObjectui64v));
EXPECT_CALL(*gl_, DeleteQueries(2, NotNull()))
.Times(AtLeast(1))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::DeleteQueries));
}
GlFakeQueries gl_fake_queries_;
};
TEST_F(GpuTracerTest, GPUTrace) {
// Test basic timer query functionality
{
void DoTraceTest() {
MockOutputter* outputter = new MockOutputter();
scoped_refptr<Outputter> outputter_ref = outputter;
......@@ -180,7 +137,8 @@ TEST_F(GpuTracerTest, GPUTrace) {
Trace(trace_name, expect_start_time, expect_end_time));
scoped_refptr<GPUTrace> trace =
new GPUTrace(outputter_ref, trace_name, offset_time);
new GPUTrace(outputter_ref, trace_name, offset_time,
GetTracerType());
gl_fake_queries_.SetCurrentGLTime(start_timestamp);
trace->Start();
......@@ -203,6 +161,76 @@ TEST_F(GpuTracerTest, GPUTrace) {
// Proces should output expected Trace results to MockOutputter
trace->Process();
}
protected:
virtual void SetUp() {
GpuServiceTest::SetUp();
gl_fake_queries_.Reset();
}
virtual void TearDown() {
gl_.reset();
gl_fake_queries_.Reset();
GpuServiceTest::TearDown();
}
virtual void SetupTimerQueryMocks() {
// Delegate query APIs used by GPUTrace to a GlFakeQueries
EXPECT_CALL(*gl_, GenQueriesARB(_, NotNull())).Times(AtLeast(1)).WillOnce(
Invoke(&gl_fake_queries_, &GlFakeQueries::GenQueriesARB));
EXPECT_CALL(*gl_, GetQueryObjectiv(_, GL_QUERY_RESULT_AVAILABLE, NotNull()))
.Times(AtLeast(2))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::GetQueryObjectiv));
EXPECT_CALL(*gl_, QueryCounter(_, GL_TIMESTAMP))
.Times(AtLeast(2))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::QueryCounter));
EXPECT_CALL(*gl_, GetQueryObjectui64v(_, GL_QUERY_RESULT, NotNull()))
.Times(AtLeast(2))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::GetQueryObjectui64v));
EXPECT_CALL(*gl_, DeleteQueriesARB(2, NotNull()))
.Times(AtLeast(1))
.WillRepeatedly(
Invoke(&gl_fake_queries_, &GlFakeQueries::DeleteQueriesARB));
}
virtual GpuTracerType GetTracerType() = 0;
GlFakeQueries gl_fake_queries_;
};
class GpuARBTimerTracerTest : public BaseGpuTracerTest {
protected:
virtual GpuTracerType GetTracerType() OVERRIDE {
return kTracerTypeARBTimer;
}
};
class GpuDisjointTimerTracerTest : public BaseGpuTracerTest {
protected:
virtual GpuTracerType GetTracerType() OVERRIDE {
return kTracerTypeDisjointTimer;
}
};
TEST_F(GpuARBTimerTracerTest, GPUTrace) {
// Test basic timer query functionality
{
DoTraceTest();
}
}
TEST_F(GpuDisjointTimerTracerTest, GPUTrace) {
// Test basic timer query functionality
{
DoTraceTest();
}
}
} // namespace gles2
......
......@@ -359,10 +359,7 @@ GL_FUNCTIONS = [
'names': ['glGetQueryObjecti64v'],
'arguments': 'GLuint id, GLenum pname, GLint64* params', },
{ 'return_type': 'void',
'names': ['glGetQueryObjectiv'],
'arguments': 'GLuint id, GLenum pname, GLint* params', },
{ 'return_type': 'void',
'names': ['glGetQueryObjectui64v'],
'names': ['glGetQueryObjectui64v', 'glGetQueryObjectui64vEXT'],
'arguments': 'GLuint id, GLenum pname, GLuint64* params', },
{ 'return_type': 'void',
'names': ['glGetQueryObjectuiv'],
......@@ -370,6 +367,10 @@ GL_FUNCTIONS = [
{ 'return_type': 'void',
'names': ['glGetQueryObjectuivARB', 'glGetQueryObjectuivEXT'],
'arguments': 'GLuint id, GLenum pname, GLuint* params', },
{ 'return_type': 'void',
'names': ['glGetQueryObjectiv', 'glGetQueryObjectivARB',
'glGetQueryObjectivEXT'],
'arguments': 'GLuint id, GLenum pname, GLint* params', },
{ 'return_type': 'void',
'names': ['glGetRenderbufferParameterivEXT', 'glGetRenderbufferParameteriv'],
'arguments': 'GLenum target, GLenum pname, GLint* params', },
......@@ -501,7 +502,7 @@ GL_FUNCTIONS = [
'names': ['glPushGroupMarkerEXT'],
'arguments': 'GLsizei length, const char* marker', },
{ 'return_type': 'void',
'names': ['glQueryCounter'],
'names': ['glQueryCounter', 'glQueryCounterEXT'],
'arguments': 'GLuint id, GLenum target', },
{ 'return_type': 'void',
'names': ['glReadBuffer'],
......
......@@ -222,6 +222,14 @@
#define GL_PATH_PROJECTION_CHROMIUM 0x1701
#endif
#ifndef GL_EXT_disjoint_timer_query
#define GL_EXT_disjoint_timer_query 1
#define GL_QUERY_COUNTER_BITS_EXT 0x8864
#define GL_TIME_ELAPSED_EXT 0x88BF
#define GL_TIMESTAMP_EXT 0x8E28
#define GL_GPU_DISJOINT_EXT 0x8FBB
#endif
#define GL_GLEXT_PROTOTYPES 1
#if defined(OS_WIN)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment