Chromium Code Reviews| Index: gpu/command_buffer/service/gpu_tracer.cc |
| diff --git a/gpu/command_buffer/service/gpu_tracer.cc b/gpu/command_buffer/service/gpu_tracer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..3316c82fffc6ec4a853fbdd835607298a212296b |
| --- /dev/null |
| +++ b/gpu/command_buffer/service/gpu_tracer.cc |
| @@ -0,0 +1,361 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "gpu/command_buffer/service/gpu_tracer.h" |
| + |
| +#include "base/threading/non_thread_safe.h" |
| +#include "base/threading/thread.h" |
| +#include "base/time.h" |
| +#include "ui/gl/gl_bindings.h" |
| + |
| +namespace gpu { |
| +namespace gles2 { |
| + |
| +class GPUTracer::Trace { |
| + public: |
| + virtual ~Trace() {}; |
| + |
| + virtual void Start() = 0; |
| + virtual void End() = 0; |
| + |
| + // True if the the results of this query are available. |
| + virtual bool IsAvailable() = 0; |
| + |
| + const char* name() { |
| + return name_.c_str(); |
| + } |
| + |
| + protected: |
| + explicit Trace(const std::string& name) : name_(name) {} |
| + |
| + virtual int64 start_time() = 0; |
| + virtual int64 end_time() = 0; |
| + |
| + private: |
| + friend GPUTracer::Outputter; |
|
jonathan.backer
2012/11/23 18:55:41
Why friend? To read the name? You could hide the T
dsinclair
2012/11/23 21:13:56
Friend'd to give access to the start_time and end_
|
| + |
| + std::string name_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Trace); |
| +}; |
| + |
| +class GPUTracer::Outputter |
| + : public base::Thread, |
| + public base::NonThreadSafe { |
|
jonathan.backer
2012/11/23 18:55:41
base::NonThreadSafe is for sanity checking that yo
dsinclair
2012/11/23 21:13:56
copy-paste. Removed.
|
| + public: |
| + static linked_ptr<GPUTracer::Outputter> Create(const std::string& name) { |
|
jonathan.backer
2012/11/23 18:55:41
Why a linked_ptr? And why a factory?
dsinclair
2012/11/23 21:13:56
Was half an implementation of something else. Chan
|
| + linked_ptr<GPUTracer::Outputter> out = linked_ptr<GPUTracer::Outputter>( |
| + new GPUTracer::Outputter(name)); |
| + |
| + // We need to start/stop the thread in order to get a thread_id. |
| + out->Start(); |
| + out->Stop(); |
| + return out; |
| + } |
| + |
| + virtual ~Outputter() {} |
| + |
| + virtual void Output(GPUTracer::Trace* trace) { |
| + // TODO(dsinclair): Change to TRACE_EVENT |
| + LOG(INFO) << "Thread " << thread_id() << ": " |
| + << thread_name() << ": " |
| + << trace->name() << ": " |
| + << trace_start_time(trace) << " " |
| + << trace_end_time(trace); |
| + } |
| + |
| + protected: |
| + explicit Outputter(const std::string& name) : base::Thread(name.c_str()) { |
| + } |
| + |
| + |
| + int64 trace_start_time(GPUTracer::Trace* trace) { |
| + return trace->start_time(); |
| + } |
| + |
| + int64 trace_end_time(GPUTracer::Trace* trace) { |
| + return trace->end_time(); |
| + } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(Outputter); |
| +}; |
| + |
| +namespace { |
| + |
| +static const unsigned int kGPUTraceMaxQueueSize = 1024; |
| + |
| +class GPUTracerSystemTime : public GPUTracer { |
| + public: |
| + GPUTracerSystemTime(); |
| + ~GPUTracerSystemTime(); |
| + |
| + linked_ptr<GPUTracer::Trace> CreateTrace(const std::string& name) OVERRIDE; |
| + |
| + private: |
| + class Trace : public GPUTracer::Trace { |
| + public: |
| + explicit Trace(const std::string& name); |
| + ~Trace() OVERRIDE; |
| + |
| + void Start() OVERRIDE; |
| + void End() OVERRIDE; |
| + bool IsAvailable() OVERRIDE; |
| + |
| + protected: |
| + int64 start_time() OVERRIDE; |
| + int64 end_time() OVERRIDE; |
| + |
| + private: |
| + bool has_ended_; |
| + base::TimeTicks start_time_; |
| + base::TimeTicks end_time_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Trace); |
| + }; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(GPUTracerSystemTime); |
| +}; |
| + |
| +class GPUTracerARBTimerQuery : public GPUTracer { |
| + public: |
| + GPUTracerARBTimerQuery(); |
| + ~GPUTracerARBTimerQuery(); |
| + |
| + linked_ptr<GPUTracer::Trace> CreateTrace(const std::string& name) OVERRIDE; |
| + |
| + private: |
| + class Trace : public GPUTracer::Trace { |
| + public: |
| + explicit Trace(const std::string& name); |
| + ~Trace() OVERRIDE; |
| + |
| + void Start() OVERRIDE; |
| + void End() OVERRIDE; |
| + bool IsAvailable() OVERRIDE; |
| + |
| + protected: |
| + int64 start_time() OVERRIDE; |
| + int64 end_time() OVERRIDE; |
| + |
| + private: |
| + unsigned int start_timer_; |
| + unsigned int end_timer_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Trace); |
| + }; |
| + |
| + class Outputter : public GPUTracer::Outputter { |
| + public: |
| + static linked_ptr<GPUTracer::Outputter> Create(const std::string& name, |
| + int64 timer_offset) { |
| + linked_ptr<GPUTracer::Outputter> out = linked_ptr<GPUTracer::Outputter>( |
| + new GPUTracerARBTimerQuery::Outputter(name, timer_offset)); |
| + |
| + // We need to start/stop the thread in order to get a thread_id. |
| + out->Start(); |
| + out->Stop(); |
| + return out; |
| + } |
| + |
| + ~Outputter() {} |
| + |
| + void Output(GPUTracer::Trace* trace) OVERRIDE { |
| + // TODO(dsinclair): Change to TRACE_EVENT |
| + LOG(INFO) << "Thread " << thread_id() << ": " |
| + << thread_name() << ": " |
| + << trace->name() << ": " |
| + << trace_start_time(trace) << " " |
| + << trace_end_time(trace); |
| + } |
| + |
| + protected: |
| + explicit Outputter(const std::string& name, int64 timer_offset) |
| + : GPUTracer::Outputter(name.c_str()), |
| + timer_offset_(timer_offset) { |
| + } |
| + |
| + int64 trace_start_time(GPUTracer::Trace* trace) { |
| + return GPUTracer::Outputter::trace_start_time(trace) + timer_offset_; |
| + } |
| + |
| + int64 trace_end_time(GPUTracer::Trace* trace) { |
| + return GPUTracer::Outputter::trace_end_time(trace) + timer_offset_; |
| + } |
| + |
| + private: |
| + int64 timer_offset_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Outputter); |
| + }; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery); |
| +}; |
| + |
| +GPUTracerSystemTime::GPUTracerSystemTime() : GPUTracer() { |
| + output_ = linked_ptr<GPUTracer::Outputter>( |
| + GPUTracer::Outputter::Create("SystemTime")); |
| +} |
| + |
| +GPUTracerSystemTime::~GPUTracerSystemTime() { |
| +} |
| + |
| +linked_ptr<GPUTracer::Trace> GPUTracerSystemTime::CreateTrace( |
| + const std::string& name) { |
| + return linked_ptr<GPUTracer::Trace>(new GPUTracerSystemTime::Trace(name)); |
| +} |
| + |
| +GPUTracerSystemTime::Trace::Trace(const std::string& name) : |
| + GPUTracer::Trace(name), |
| + has_ended_(false), |
| + start_time_(), |
| + end_time_() { |
| +} |
| + |
| +GPUTracerSystemTime::Trace::~Trace() { |
| +} |
| + |
| +void GPUTracerSystemTime::Trace::Start() { |
| + start_time_ = base::TimeTicks::NowFromSystemTraceTime(); |
| +} |
| + |
| +int64 GPUTracerSystemTime::Trace::start_time() { |
| + return start_time_.ToInternalValue(); |
| +} |
| + |
| +void GPUTracerSystemTime::Trace::End() { |
| + end_time_ = base::TimeTicks::NowFromSystemTraceTime(); |
| + has_ended_ = true; |
| +} |
| + |
| +int64 GPUTracerSystemTime::Trace::end_time() { |
| + return end_time_.ToInternalValue(); |
| +} |
| + |
| +bool GPUTracerSystemTime::Trace::IsAvailable() { |
| + return has_ended_; |
| +} |
| + |
| +GPUTracerARBTimerQuery::GPUTracerARBTimerQuery() : GPUTracer() { |
| + // TODO(dsinclair): Change to glGetInteger64v. |
| + GLuint64 gl_now = 0; |
| + GLuint query; |
| + glGenQueries(1, &query); |
| + |
| + // Flush the pipeline so our query is more accurate. |
| + glFinish(); |
| + |
| + glQueryCounter(query, GL_TIMESTAMP); |
| + glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now); |
| + glDeleteQueries(1, &query); |
| + |
| + gl_now /= base::Time::kNanosecondsPerMicrosecond; |
| + |
| + base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime(); |
| + |
| + int64 time_offset = system_now.ToInternalValue() - gl_now; |
| + output_ = linked_ptr<GPUTracer::Outputter>( |
| + GPUTracerARBTimerQuery::Outputter::Create("GL_ARB_timer_query", |
| + time_offset)); |
| +} |
| + |
| +GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() { |
| +} |
| + |
| +linked_ptr<GPUTracer::Trace> GPUTracerARBTimerQuery::CreateTrace( |
| + const std::string& name) { |
| + return linked_ptr<GPUTracer::Trace>(new GPUTracerARBTimerQuery::Trace(name)); |
| +} |
| + |
| +GPUTracerARBTimerQuery::Trace::Trace(const std::string& name) : |
| + GPUTracer::Trace(name), |
| + start_timer_(0), |
| + end_timer_(0) { |
| + GLuint queries[2]; |
| + glGenQueries(2, queries); |
| + |
| + start_timer_ = queries[0]; |
| + end_timer_ = queries[1]; |
| +} |
| + |
| +GPUTracerARBTimerQuery::Trace::~Trace() { |
| + GLuint queries[] = {start_timer_, end_timer_}; |
| + glDeleteQueries(2, queries); |
| +} |
| + |
| +void GPUTracerARBTimerQuery::Trace::Start() { |
| + glQueryCounter(start_timer_, GL_TIMESTAMP); |
| +} |
| + |
| +int64 GPUTracerARBTimerQuery::Trace::start_time() { |
| + GLint64 timestamp; |
| + glGetQueryObjecti64v(start_timer_, GL_QUERY_RESULT, ×tamp); |
| + return timestamp / base::Time::kNanosecondsPerMicrosecond; |
| +} |
| + |
| +void GPUTracerARBTimerQuery::Trace::End() { |
| + glQueryCounter(end_timer_, GL_TIMESTAMP); |
| +} |
| + |
| +int64 GPUTracerARBTimerQuery::Trace::end_time() { |
| + GLint64 timestamp; |
| + glGetQueryObjecti64v(end_timer_, GL_QUERY_RESULT, ×tamp); |
| + return timestamp / base::Time::kNanosecondsPerMicrosecond; |
| +} |
| + |
| +bool GPUTracerARBTimerQuery::Trace::IsAvailable() { |
| + GLint done = 0; |
| + glGetQueryObjectiv(end_timer_, GL_QUERY_RESULT_AVAILABLE, &done); |
| + return !!done; |
| +} |
| + |
| +} // namespace |
| + |
| +scoped_ptr<GPUTracer> GPUTracer::create() { |
| + if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) |
| + return scoped_ptr<GPUTracer>(new GPUTracerARBTimerQuery()); |
| + |
| + return scoped_ptr<GPUTracer>(new GPUTracerSystemTime()); |
| +} |
| + |
| +GPUTracer::GPUTracer() { |
| +} |
| + |
| +GPUTracer::~GPUTracer() { |
| +} |
| + |
| +bool GPUTracer::Begin(const std::string& name) { |
| + // Make sure we are not nesting trace commands. |
| + if (current_trace_.get() != NULL) |
| + return false; |
| + |
| + current_trace_ = CreateTrace(name); |
| + current_trace_->Start(); |
| + return true; |
| +} |
| + |
| +bool GPUTracer::End() { |
| + if (current_trace_.get() == NULL) { |
| + return false; |
| + } |
| + |
| + current_trace_->End(); |
| + traces_.push_back(current_trace_); |
| + current_trace_ = linked_ptr<GPUTracer::Trace>(NULL); |
|
jonathan.backer
2012/11/23 18:55:41
I think that you can just call .reset() here.
dsinclair
2012/11/23 21:13:56
Done.
|
| + |
| + if (traces_.size() > kGPUTraceMaxQueueSize) |
| + Process(); |
| + |
| + return true; |
| +} |
| + |
| +void GPUTracer::Process() { |
| + while (!traces_.empty() && traces_.front()->IsAvailable()) { |
| + output_->Output(traces_.front().get()); |
| + traces_.pop_front(); |
| + } |
| +} |
| + |
| +} // namespace gles2 |
| +} // namespace gpu |