static const unsigned int kProcessInterval = 16;
static TraceOutputter* g_outputter_thread = NULL;
+TraceMarker::TraceMarker(const std::string& name)
+ : name_(name),
+ trace_(NULL) {
+}
+
+TraceMarker::~TraceMarker() {
+}
+
scoped_refptr<TraceOutputter> TraceOutputter::Create(const std::string& name) {
if (!g_outputter_thread) {
g_outputter_thread = new TraceOutputter(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 kTracerTypeDisjointTimer:
+ // For the disjoint timer, GPU idle time does not seem to increment the
+ // internal counter. We must calculate the offset before any query. The
+ // good news is any device that supports disjoint timer will also support
+ // glGetInteger64v, so we can query it directly unlike the ARBTimer case.
+ // The "offset_" variable will always be 0 during normal use cases, only
+ // under the unit tests will it be set to specific test values.
+ if (offset_ == 0) {
+ GLint64 gl_now = 0;
+ glGetInteger64v(GL_TIMESTAMP, &gl_now);
+ offset_ = base::TimeTicks::NowFromSystemTraceTime().ToInternalValue() -
+ gl_now / base::Time::kNanosecondsPerMicrosecond;
+ }
+ // Intentionally fall through to kTracerTypeARBTimer case.xs
+ case kTracerTypeARBTimer:
+ // 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(
}
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, ×tamp);
- start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
-
- glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, ×tamp);
- end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
+ glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &begin_stamp);
+ glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, &end_stamp);
- 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_);
}
-struct TraceMarker {
- TraceMarker(const std::string& name, GpuTracerSource source)
- : name_(name), source_(source) {}
-
- std::string name_;
- GpuTracerSource source_;
- scoped_refptr<GPUTrace> trace_;
-};
-
-class GPUTracerImpl
- : public GPUTracer,
- public base::SupportsWeakPtr<GPUTracerImpl> {
- public:
- GPUTracerImpl()
- : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
- TRACE_DISABLED_BY_DEFAULT("gpu.service"))),
- gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
- TRACE_DISABLED_BY_DEFAULT("gpu.device"))),
- gpu_executing_(false),
- process_posted_(false) {}
- virtual ~GPUTracerImpl() {}
-
- // Implementation of gpu::gles2::GPUTracer
- virtual bool BeginDecoding() OVERRIDE;
- virtual bool EndDecoding() OVERRIDE;
- virtual bool Begin(const std::string& name, GpuTracerSource source) OVERRIDE;
- virtual bool End(GpuTracerSource source) OVERRIDE;
- virtual const std::string& CurrentName() const OVERRIDE;
- virtual bool IsTracing() OVERRIDE {
- return (*gpu_trace_srv_category != 0) || (*gpu_trace_dev_category != 0);
+GPUTracer::GPUTracer(gles2::GLES2Decoder* decoder)
+ : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("gpu.service"))),
+ gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("gpu.device"))),
+ decoder_(decoder),
+ timer_offset_(0),
+ last_tracer_source_(kTraceGroupInvalid),
+ tracer_type_(kTracerTypeInvalid),
+ gpu_timing_synced_(false),
+ gpu_executing_(false),
+ process_posted_(false) {
+ 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");
}
- virtual void CalculateTimerOffset() {}
-
- // Process any completed traces.
- virtual void Process();
- virtual void ProcessTraces();
-
- protected:
- // Create a new trace.
- virtual scoped_refptr<GPUTrace> CreateTrace(const std::string& name);
-
- const unsigned char* gpu_trace_srv_category;
- const unsigned char* gpu_trace_dev_category;
-
- protected:
- void IssueProcessTask();
-
- std::vector<TraceMarker> markers_;
- std::deque<scoped_refptr<GPUTrace> > traces_;
-
- bool gpu_executing_;
- bool process_posted_;
-
- DISALLOW_COPY_AND_ASSIGN(GPUTracerImpl);
-};
-
-class GPUTracerARBTimerQuery : public GPUTracerImpl {
- public:
- explicit GPUTracerARBTimerQuery(gles2::GLES2Decoder* decoder);
- virtual ~GPUTracerARBTimerQuery();
-
- // Implementation of GPUTracerImpl
- virtual void ProcessTraces() OVERRIDE;
-
- protected:
- // Implementation of GPUTracerImpl.
- virtual bool BeginDecoding() OVERRIDE;
- virtual bool EndDecoding() OVERRIDE;
- virtual scoped_refptr<GPUTrace> CreateTrace(const std::string& name) OVERRIDE;
- virtual void CalculateTimerOffset() OVERRIDE;
-
- scoped_refptr<Outputter> outputter_;
-
- bool gpu_timing_synced_;
- int64 timer_offset_;
-
- gles2::GLES2Decoder* decoder_;
+}
- DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery);
-};
+GPUTracer::~GPUTracer() {
+}
-bool GPUTracerImpl::BeginDecoding() {
+bool GPUTracer::BeginDecoding() {
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 (size_t i = 0; i < markers_.size(); i++) {
- markers_[i].trace_ = CreateTrace(markers_[i].name_);
- markers_[i].trace_->Start();
+ for (int n = 0; n < NUM_TRACER_SOURCES; n++) {
+ for (size_t i = 0; i < markers_[n].size(); i++) {
+ markers_[n][i].trace_ = CreateTrace(markers_[n][i].name_);
+ markers_[n][i].trace_->Start();
+ }
}
}
return true;
}
-bool GPUTracerImpl::EndDecoding() {
+bool GPUTracer::EndDecoding() {
if (!gpu_executing_)
return false;
// End Trace for all active markers
if (IsTracing()) {
- for (size_t i = 0; i < markers_.size(); i++) {
- if (markers_[i].trace_) {
- markers_[i].trace_->End();
- if (markers_[i].trace_->IsEnabled())
- traces_.push_back(markers_[i].trace_);
- markers_[i].trace_ = 0;
+ for (int n = 0; n < NUM_TRACER_SOURCES; n++) {
+ for (size_t i = 0; i < markers_[n].size(); i++) {
+ if (markers_[n][i].trace_.get()) {
+ markers_[n][i].trace_->End();
+ if (markers_[n][i].trace_->IsEnabled())
+ traces_.push_back(markers_[n][i].trace_);
+ markers_[n][i].trace_ = 0;
+ }
}
}
IssueProcessTask();
}
gpu_executing_ = false;
+
+ // NOTE(vmiura): glFlush() here can help give better trace results,
+ // but it distorts the normal device behavior.
return true;
}
-bool GPUTracerImpl::Begin(const std::string& name, GpuTracerSource source) {
+bool GPUTracer::Begin(const std::string& name, GpuTracerSource source) {
if (!gpu_executing_)
return false;
+ DCHECK(source >= 0 && source < NUM_TRACER_SOURCES);
+
// Push new marker from given 'source'
- markers_.push_back(TraceMarker(name, source));
+ last_tracer_source_ = source;
+ markers_[source].push_back(TraceMarker(name));
// Create trace
if (IsTracing()) {
scoped_refptr<GPUTrace> trace = CreateTrace(name);
trace->Start();
- markers_.back().trace_ = trace;
+ markers_[source].back().trace_ = trace;
}
+
return true;
}
-bool GPUTracerImpl::End(GpuTracerSource source) {
+bool GPUTracer::End(GpuTracerSource source) {
if (!gpu_executing_)
return false;
+ DCHECK(source >= 0 && source < NUM_TRACER_SOURCES);
+
// Pop last marker with matching 'source'
- for (int i = markers_.size() - 1; i >= 0; i--) {
- if (markers_[i].source_ == source) {
- // End trace
- if (IsTracing()) {
- scoped_refptr<GPUTrace> trace = markers_[i].trace_;
- if (trace) {
- trace->End();
- if (trace->IsEnabled())
- traces_.push_back(trace);
- IssueProcessTask();
- }
+ if (!markers_[source].empty()) {
+ if (IsTracing()) {
+ scoped_refptr<GPUTrace> trace = markers_[source].back().trace_;
+ if (trace.get()) {
+ trace->End();
+ if (trace->IsEnabled())
+ traces_.push_back(trace);
+ IssueProcessTask();
}
-
- markers_.erase(markers_.begin() + i);
- return true;
}
+
+ markers_[source].pop_back();
+ return true;
}
return false;
}
-void GPUTracerImpl::Process() {
- process_posted_ = false;
- ProcessTraces();
- IssueProcessTask();
+bool GPUTracer::IsTracing() {
+ return (*gpu_trace_srv_category != 0) || (*gpu_trace_dev_category != 0);
}
-void GPUTracerImpl::ProcessTraces() {
- while (!traces_.empty() && traces_.front()->IsAvailable()) {
- traces_.front()->Process();
- traces_.pop_front();
+const std::string& GPUTracer::CurrentName() const {
+ if (last_tracer_source_ >= 0 &&
+ last_tracer_source_ < NUM_TRACER_SOURCES &&
+ !markers_[last_tracer_source_].empty()) {
+ return markers_[last_tracer_source_].back().name_;
}
+ return base::EmptyString();
}
-const std::string& GPUTracerImpl::CurrentName() const {
- if (markers_.empty())
- return base::EmptyString();
- return markers_.back().name_;
-}
+scoped_refptr<GPUTrace> GPUTracer::CreateTrace(const std::string& name) {
+ GpuTracerType tracer_type = *gpu_trace_dev_category ? tracer_type_ :
+ kTracerTypeInvalid;
-scoped_refptr<GPUTrace> GPUTracerImpl::CreateTrace(
- const std::string& name) {
- return new GPUTrace(name);
-}
-
-void GPUTracerImpl::IssueProcessTask() {
- if (traces_.empty() || process_posted_)
- return;
-
- process_posted_ = true;
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&GPUTracerImpl::Process, base::AsWeakPtr(this)),
- base::TimeDelta::FromMilliseconds(kProcessInterval));
+ return new GPUTrace(outputter_, name, timer_offset_, tracer_type);
}
-GPUTracerARBTimerQuery::GPUTracerARBTimerQuery(gles2::GLES2Decoder* decoder)
- : timer_offset_(0), decoder_(decoder) {
- outputter_ = TraceOutputter::Create("GL_ARB_timer_query");
-}
-
-GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() {
-}
-
-scoped_refptr<GPUTrace> GPUTracerARBTimerQuery::CreateTrace(
- const std::string& name) {
- if (*gpu_trace_dev_category)
- return new GPUTrace(outputter_, name, timer_offset_);
- return GPUTracerImpl::CreateTrace(name);
+void GPUTracer::Process() {
+ process_posted_ = false;
+ ProcessTraces();
+ IssueProcessTask();
}
-bool GPUTracerARBTimerQuery::BeginDecoding() {
- 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;
+void GPUTracer::ProcessTraces() {
+ if (tracer_type_ == kTracerTypeInvalid) {
+ traces_.clear();
+ return;
}
- return GPUTracerImpl::BeginDecoding();
-}
-
-bool GPUTracerARBTimerQuery::EndDecoding() {
- bool ret = GPUTracerImpl::EndDecoding();
-
- // NOTE(vmiura_: glFlush() here can help give better trace results,
- // but it distorts the normal device behavior.
- return ret;
-}
-
-void GPUTracerARBTimerQuery::ProcessTraces() {
- TRACE_EVENT0("gpu", "GPUTracerARBTimerQuery::ProcessTraces");
+ TRACE_EVENT0("gpu", "GPUTracer::ProcessTraces");
// Make owning decoder's GL context current
if (!decoder_->MakeCurrent()) {
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();
traces_.clear();
}
-void GPUTracerARBTimerQuery::CalculateTimerOffset() {
- TRACE_EVENT0("gpu", "GPUTracerARBTimerQuery::CalculateTimerOffset");
-
- // NOTE(vmiura): It would be better to use glGetInteger64v, however
- // it's not available everywhere.
- GLuint64 gl_now = 0;
- GLuint query;
- glFinish();
- glGenQueries(1, &query);
- glQueryCounter(query, GL_TIMESTAMP);
- glFinish();
- glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now);
- base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime();
-
- gl_now /= base::Time::kNanosecondsPerMicrosecond;
- timer_offset_ = system_now.ToInternalValue() - gl_now;
- glDeleteQueries(1, &query);
-}
+void GPUTracer::CalculateTimerOffset() {
+ if (tracer_type_ != kTracerTypeInvalid) {
+ if (*gpu_trace_dev_category == '\0') {
+ // If GPU device category is off, invalidate timing sync.
+ gpu_timing_synced_ = false;
+ return;
+ } else if (tracer_type_ == kTracerTypeDisjointTimer) {
+ // Disjoint timers offsets should be calculated before every query.
+ gpu_timing_synced_ = true;
+ timer_offset_ = 0;
+ }
+
+ if (gpu_timing_synced_)
+ return;
+
+ TRACE_EVENT0("gpu", "GPUTracer::CalculateTimerOffset");
-GPUTracer::GPUTracer() {}
+ // NOTE(vmiura): It would be better to use glGetInteger64v, however
+ // it's not available everywhere.
+ GLuint64 gl_now = 0;
+ GLuint query;
-GPUTracer::~GPUTracer() {}
+ glGenQueriesARB(1, &query);
-scoped_ptr<GPUTracer> GPUTracer::Create(gles2::GLES2Decoder* decoder) {
- if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) {
- return scoped_ptr<GPUTracer>(new GPUTracerARBTimerQuery(decoder));
+ glFinish();
+ glQueryCounter(query, GL_TIMESTAMP);
+ glFinish();
+
+ glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now);
+ glDeleteQueriesARB(1, &query);
+
+ base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime();
+
+ gl_now /= base::Time::kNanosecondsPerMicrosecond;
+ timer_offset_ = system_now.ToInternalValue() - gl_now;
+ gpu_timing_synced_ = true;
}
- return scoped_ptr<GPUTracer>(new GPUTracerImpl());
+}
+
+void GPUTracer::IssueProcessTask() {
+ if (traces_.empty() || process_posted_)
+ return;
+
+ process_posted_ = true;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&GPUTracer::Process, base::AsWeakPtr(this)),
+ base::TimeDelta::FromMilliseconds(kProcessInterval));
}
} // namespace gles2