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..907ffc4d21daa2499fbcce19c412633431ec24da |
--- /dev/null |
+++ b/gpu/command_buffer/service/gpu_tracer.cc |
@@ -0,0 +1,299 @@ |
+// 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 <deque> |
+#include <stack> |
jonathan.backer
2012/12/03 14:23:14
necessary?
dsinclair
2012/12/03 16:47:42
Done.
|
+ |
+#include "base/bind.h" |
+#include "base/debug/trace_event.h" |
+#include "base/threading/non_thread_safe.h" |
jonathan.backer
2012/12/03 14:23:14
necessary?
dsinclair
2012/12/03 16:47:42
Done.
|
+#include "base/threading/thread.h" |
+#include "base/time.h" |
+#include "ui/gl/gl_bindings.h" |
+ |
+namespace gpu { |
+namespace gles2 { |
+namespace { |
+ |
+static const unsigned int kGPUTraceMaxQueueSize = 1024; |
+ |
+class Outputter |
+ : public base::Thread, |
+ public base::RefCounted<Outputter> { |
+ public: |
+ static scoped_refptr<Outputter> Create(const std::string& name) { |
+ if (!g_outputter_thread) { |
+ g_outputter_thread = new Outputter(name); |
+ g_outputter_thread->Start(); |
+ } |
+ return g_outputter_thread; |
+ } |
+ |
+ private: |
+ explicit Outputter(const std::string& name) : base::Thread(name.c_str()) {} |
+ |
+ virtual ~Outputter() { |
+ g_outputter_thread->Stop(); |
+ g_outputter_thread = NULL; |
+ } |
+ |
+ static Outputter* g_outputter_thread; |
+ |
+ friend class base::RefCounted<Outputter>; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Outputter); |
+}; |
+ |
+Outputter* Outputter::g_outputter_thread = NULL; |
+ |
+class Trace : public base::RefCountedThreadSafe<Trace> { |
+ public: |
+ virtual void Start() = 0; |
+ virtual void End() = 0; |
+ |
+ // True if the the results of this query are available. |
+ virtual bool IsAvailable() = 0; |
+ |
+ virtual void Process() = 0; |
+ |
+ virtual const char* name() { |
+ return name_.c_str(); |
+ } |
+ |
jonathan.backer
2012/12/03 14:23:14
nit: extra new-line
dsinclair
2012/12/03 16:47:42
Done.
|
+ |
+ protected: |
jonathan.backer
2012/12/03 14:23:14
private? I don't see why you'd want to subclass th
dsinclair
2012/12/03 16:47:42
Done.
|
+ explicit Trace(const std::string& name) : name_(name) {} |
+ virtual ~Trace() {}; |
jonathan.backer
2012/12/03 14:23:14
semi-colon?
dsinclair
2012/12/03 16:47:42
Done.
|
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<Trace>; |
+ |
+ std::string name_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Trace); |
+}; |
+ |
+class GLARBTimerTrace : public Trace { |
+ public: |
+ GLARBTimerTrace(scoped_refptr<Outputter> output, const std::string& name, |
+ int64 offset); |
+ |
+ // Implementation of Tracer |
+ virtual void Start() OVERRIDE; |
+ virtual void End() OVERRIDE; |
+ virtual bool IsAvailable() OVERRIDE; |
+ virtual void Process() OVERRIDE; |
+ |
+ private: |
+ ~GLARBTimerTrace() OVERRIDE; |
+ |
+ void Output(); |
+ |
+ scoped_refptr<Outputter> output_; |
jonathan.backer
2012/12/03 14:23:14
nit: s/output_/outputter_?
I think that instances
dsinclair
2012/12/03 16:47:42
Done.
|
+ |
+ int64 offset_; |
+ int64 start_time_; |
+ int64 end_time_; |
+ |
+ unsigned int start_timer_; |
+ unsigned int end_timer_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GLARBTimerTrace); |
+}; |
+ |
+class NoopTrace : public Trace { |
+ public: |
+ explicit NoopTrace(const std::string& name) : Trace(name) {} |
+ |
+ // Implementation of Tracer |
+ virtual void Start() OVERRIDE {} |
+ virtual void End() OVERRIDE {} |
+ virtual bool IsAvailable() OVERRIDE { return true; } |
+ virtual void Process() OVERRIDE {} |
+ |
+ private: |
+ ~NoopTrace() {} |
+ |
+ DISALLOW_COPY_AND_ASSIGN(NoopTrace); |
+}; |
+ |
+class GPUTracerImpl : public GPUTracer { |
+ public: |
+ GPUTracerImpl() |
+ : gpu_category_enabled_(TRACE_EVENT_API_GET_CATEGORY_ENABLED("gpu")) { |
+ } |
+ virtual ~GPUTracerImpl() {} |
+ |
+ // Implementation of gpu::gles2::GPUTracer |
+ virtual bool Begin(const std::string& name) OVERRIDE; |
+ virtual bool End() OVERRIDE; |
+ virtual void Process() OVERRIDE; |
+ virtual const char* CurrentName() OVERRIDE; |
+ |
+ protected: |
+ // Create a new trace. |
+ virtual scoped_refptr<Trace> CreateTrace(const std::string& name); |
+ |
+ scoped_refptr<Outputter> output_; |
+ |
+ const unsigned char* gpu_category_enabled_; |
+ |
+ private: |
+ scoped_refptr<Trace> current_trace_; |
+ std::deque<scoped_refptr<Trace> > traces_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GPUTracerImpl); |
+}; |
+ |
+class GPUTracerARBTimerQuery : public GPUTracerImpl { |
+ public: |
+ GPUTracerARBTimerQuery(); |
+ virtual ~GPUTracerARBTimerQuery(); |
+ |
+ // Implementation of GPUTracerImpl. |
+ virtual scoped_refptr<Trace> CreateTrace(const std::string& name) OVERRIDE; |
jonathan.backer
2012/12/03 14:23:14
why did this become public? seems like it should b
dsinclair
2012/12/03 16:47:42
Done.
|
+ |
+ private: |
+ int64 timer_offset_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery); |
+}; |
+ |
+GLARBTimerTrace::GLARBTimerTrace(scoped_refptr<Outputter> output, |
+ const std::string& name, int64 offset) |
+ : Trace(name), |
+ output_(output), |
+ offset_(offset), |
+ start_time_(0), |
+ end_time_(0), |
+ start_timer_(0), |
+ end_timer_(0) { |
+ GLuint queries[2]; |
+ glGenQueries(2, queries); |
+ |
+ start_timer_ = queries[0]; |
+ end_timer_ = queries[1]; |
+} |
+ |
+GLARBTimerTrace::~GLARBTimerTrace() { |
+} |
+ |
+void GLARBTimerTrace::Start() { |
+ glQueryCounter(start_timer_, GL_TIMESTAMP); |
+} |
+ |
+void GLARBTimerTrace::End() { |
+ glQueryCounter(end_timer_, GL_TIMESTAMP); |
+} |
+ |
+bool GLARBTimerTrace::IsAvailable() { |
+ GLint done = 0; |
+ glGetQueryObjectiv(end_timer_, GL_QUERY_RESULT_AVAILABLE, &done); |
+ return !!done; |
+} |
+ |
+void GLARBTimerTrace::Process() { |
+ DCHECK(IsAvailable()); |
+ |
+ GLint64 timestamp; |
+ |
+ glGetQueryObjecti64v(start_timer_, GL_QUERY_RESULT, ×tamp); |
+ start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; |
+ |
+ glGetQueryObjecti64v(end_timer_, GL_QUERY_RESULT, ×tamp); |
+ end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; |
+ |
+ GLuint queries[] = {start_timer_, end_timer_}; |
jonathan.backer
2012/12/03 14:23:14
nit: maybe just store the timer ids in an array to
dsinclair
2012/12/03 16:47:42
Done.
|
+ glDeleteQueries(2, queries); |
+ |
+ output_->message_loop()->PostTask(FROM_HERE, |
+ base::Bind(&GLARBTimerTrace::Output, this)); |
+} |
+ |
+void GLARBTimerTrace::Output() { |
+ // TODO(dsinclair): Output TRACE_EVENT |
+} |
+ |
+bool GPUTracerImpl::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 GPUTracerImpl::End() { |
+ if (current_trace_.get() == NULL) |
+ return false; |
+ |
+ current_trace_->End(); |
+ traces_.push_back(current_trace_); |
+ current_trace_ = NULL; |
+ |
+ if (traces_.size() > kGPUTraceMaxQueueSize) |
+ Process(); |
+ |
+ return true; |
+} |
+ |
+void GPUTracerImpl::Process() { |
+ while (!traces_.empty() && traces_.front()->IsAvailable()) { |
+ traces_.front()->Process(); |
+ traces_.pop_front(); |
+ } |
+} |
+ |
+const char* GPUTracerImpl::CurrentName() { |
+ if (current_trace_ == NULL) |
+ return NULL; |
+ return current_trace_->name(); |
+} |
+ |
+scoped_refptr<Trace> GPUTracerImpl::CreateTrace(const std::string& name) { |
+ return new NoopTrace(name); |
+} |
+ |
+GPUTracerARBTimerQuery::GPUTracerARBTimerQuery() : GPUTracerImpl() { |
+ // TODO(dsinclair): This offset can drift, we should move to another thread |
+ // in order to re-calculate the drift every second-ish. |
+ // TODO(dsinclair): Change to glGetInteger64v. |
+ GLuint64 gl_now = 0; |
+ GLuint query; |
+ glGenQueries(1, &query); |
+ |
+ glQueryCounter(query, GL_TIMESTAMP); |
+ glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now); |
+ glDeleteQueries(1, &query); |
jonathan.backer
2012/12/03 14:23:14
seems like we should get |system_now| as close as
dsinclair
2012/12/03 16:47:42
Done.
|
+ |
+ gl_now /= base::Time::kNanosecondsPerMicrosecond; |
+ base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime(); |
+ timer_offset_ = system_now.ToInternalValue() - gl_now; |
+ |
+ output_ = Outputter::Create("GL_ARB_timer_query"); |
+} |
+ |
+GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() { |
+} |
+ |
+scoped_refptr<Trace> GPUTracerARBTimerQuery::CreateTrace( |
+ const std::string& name) { |
+ if (*gpu_category_enabled_) |
+ return new GLARBTimerTrace(output_, name, timer_offset_); |
+ return GPUTracerImpl::CreateTrace(name); |
+} |
+ |
+} // 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 GPUTracerImpl()); |
+} |
+ |
+} // namespace gles2 |
+} // namespace gpu |