Skip to content

Commit

Permalink
Added support for GPU Tracing on mobile devices which support it.
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
dyen authored and Commit bot committed Sep 3, 2014
1 parent bbde62b commit 882e1b7
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 122 deletions.
178 changes: 115 additions & 63 deletions gpu/command_buffer/service/gpu_tracer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,80 +59,105 @@ 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(
TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
}

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_);
}

Expand All @@ -144,39 +169,36 @@ 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;
}
}

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++) {
Expand Down Expand Up @@ -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() {
Expand All @@ -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;
}

Expand All @@ -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();
Expand All @@ -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;
}
}

Expand Down
17 changes: 12 additions & 5 deletions gpu/command_buffer/service/gpu_tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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_;
Expand Down Expand Up @@ -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();
Expand All @@ -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];

Expand Down
Loading

0 comments on commit 882e1b7

Please sign in to comment.