diff options
Diffstat (limited to 'chromium/gpu/command_buffer/service/gpu_tracer.cc')
-rw-r--r-- | chromium/gpu/command_buffer/service/gpu_tracer.cc | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/service/gpu_tracer.cc b/chromium/gpu/command_buffer/service/gpu_tracer.cc new file mode 100644 index 00000000000..6bb2dfb672f --- /dev/null +++ b/chromium/gpu/command_buffer/service/gpu_tracer.cc @@ -0,0 +1,347 @@ +// 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 "base/bind.h" +#include "base/debug/trace_event.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string_util.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "ui/gl/gl_bindings.h" + +namespace gpu { +namespace gles2 { +namespace { + +class Outputter; + +static const unsigned int kProcessInterval = 16; +static Outputter* g_outputter_thread = NULL; + +class Outputter + : private 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(); + g_outputter_thread->Stop(); + } + return g_outputter_thread; + } + + uint64 Id() { return thread_id(); } + + private: + friend class base::RefCounted<Outputter>; + + explicit Outputter(const std::string& name) : base::Thread(name.c_str()) {} + + virtual ~Outputter() { + g_outputter_thread = NULL; + } + + DISALLOW_COPY_AND_ASSIGN(Outputter); +}; + +class Trace : public base::RefCounted<Trace> { + public: + explicit Trace(const std::string& name) : name_(name) {} + + virtual void Start() = 0; + virtual void End() = 0; + + // True if the the results of this query are available. + virtual bool IsAvailable() = 0; + + virtual bool IsProcessable() { return true; } + virtual void Process() = 0; + + virtual const std::string& name() { + return name_; + } + + protected: + virtual ~Trace() {} + + private: + friend class base::RefCounted<Trace>; + + std::string name_; + + DISALLOW_COPY_AND_ASSIGN(Trace); +}; + +class GLARBTimerTrace : public Trace { + public: + GLARBTimerTrace(scoped_refptr<Outputter> outputter, 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: + virtual ~GLARBTimerTrace(); + + void Output(); + + scoped_refptr<Outputter> outputter_; + + int64 offset_; + int64 start_time_; + int64 end_time_; + bool end_requested_; + + GLuint queries_[2]; + + 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 bool IsProcessable() OVERRIDE { return false; } + virtual void Process() OVERRIDE {} + + private: + virtual ~NoopTrace() {} + + DISALLOW_COPY_AND_ASSIGN(NoopTrace); +}; + +class GPUTracerImpl + : public GPUTracer, + public base::SupportsWeakPtr<GPUTracerImpl> { + public: + GPUTracerImpl() + : gpu_category_enabled_( + TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("gpu")), + process_posted_(false) { + } + virtual ~GPUTracerImpl() {} + + // Implementation of gpu::gles2::GPUTracer + virtual bool Begin(const std::string& name) OVERRIDE; + virtual bool End() OVERRIDE; + virtual const std::string& CurrentName() const OVERRIDE; + + // Process any completed traces. + virtual void Process(); + + protected: + // Create a new trace. + virtual scoped_refptr<Trace> CreateTrace(const std::string& name); + + const unsigned char* gpu_category_enabled_; + + private: + void IssueProcessTask(); + + scoped_refptr<Trace> current_trace_; + std::deque<scoped_refptr<Trace> > traces_; + + bool process_posted_; + + DISALLOW_COPY_AND_ASSIGN(GPUTracerImpl); +}; + +class GPUTracerARBTimerQuery : public GPUTracerImpl { + public: + GPUTracerARBTimerQuery(); + virtual ~GPUTracerARBTimerQuery(); + + // Implementation of GPUTracerImpl + virtual void Process() OVERRIDE; + + private: + // Implementation of GPUTracerImpl. + virtual scoped_refptr<Trace> CreateTrace(const std::string& name) OVERRIDE; + + void CalculateTimerOffset(); + + scoped_refptr<Outputter> outputter_; + + int64 timer_offset_; + int64 last_offset_check_; + + DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery); +}; + +GLARBTimerTrace::GLARBTimerTrace(scoped_refptr<Outputter> outputter, + const std::string& name, int64 offset) + : Trace(name), + outputter_(outputter), + offset_(offset), + start_time_(0), + end_time_(0), + end_requested_(false) { + glGenQueries(2, queries_); +} + +GLARBTimerTrace::~GLARBTimerTrace() { +} + +void GLARBTimerTrace::Start() { + glQueryCounter(queries_[0], GL_TIMESTAMP); +} + +void GLARBTimerTrace::End() { + glQueryCounter(queries_[1], GL_TIMESTAMP); + end_requested_ = true; +} + +bool GLARBTimerTrace::IsAvailable() { + if (!end_requested_) + return false; + + GLint done = 0; + glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done); + return !!done; +} + +void GLARBTimerTrace::Process() { + DCHECK(IsAvailable()); + + GLint64 timestamp; + + // 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. + glGetQueryObjecti64v(queries_[0], GL_QUERY_RESULT, ×tamp); + start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; + + glGetQueryObjecti64v(queries_[1], GL_QUERY_RESULT, ×tamp); + end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; + + glDeleteQueries(2, queries_); + + TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("gpu", name().c_str(), + this, outputter_->Id(), start_time_); + TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0("gpu", name().c_str(), + this, outputter_->Id(), end_time_); +} + +bool GPUTracerImpl::Begin(const std::string& name) { + // Make sure we are not nesting trace commands. + if (current_trace_.get()) + return false; + + current_trace_ = CreateTrace(name); + current_trace_->Start(); + return true; +} + +bool GPUTracerImpl::End() { + if (!current_trace_.get()) + return false; + + current_trace_->End(); + if (current_trace_->IsProcessable()) + traces_.push_back(current_trace_); + current_trace_ = NULL; + + IssueProcessTask(); + return true; +} + +void GPUTracerImpl::Process() { + process_posted_ = false; + + while (!traces_.empty() && traces_.front()->IsAvailable()) { + traces_.front()->Process(); + traces_.pop_front(); + } + + IssueProcessTask(); +} + +const std::string& GPUTracerImpl::CurrentName() const { + if (!current_trace_.get()) + return EmptyString(); + return current_trace_->name(); +} + +scoped_refptr<Trace> GPUTracerImpl::CreateTrace(const std::string& name) { + return new NoopTrace(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)); +} + +GPUTracerARBTimerQuery::GPUTracerARBTimerQuery() + : GPUTracerImpl(), + timer_offset_(0), + last_offset_check_(0) { + CalculateTimerOffset(); + outputter_ = Outputter::Create("GL_ARB_timer_query"); +} + +GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() { +} + +scoped_refptr<Trace> GPUTracerARBTimerQuery::CreateTrace( + const std::string& name) { + if (*gpu_category_enabled_) + return new GLARBTimerTrace(outputter_, name, timer_offset_); + return GPUTracerImpl::CreateTrace(name); +} + +void GPUTracerARBTimerQuery::Process() { + GPUTracerImpl::Process(); + + if (*gpu_category_enabled_ && + (last_offset_check_ + base::Time::kMicrosecondsPerSecond) < + base::TimeTicks::NowFromSystemTraceTime().ToInternalValue()) + CalculateTimerOffset(); +} + +void GPUTracerARBTimerQuery::CalculateTimerOffset() { + TRACE_EVENT0("gpu", "CalculateTimerOffset"); + // 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); + base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime(); + + gl_now /= base::Time::kNanosecondsPerMicrosecond; + timer_offset_ = system_now.ToInternalValue() - gl_now; + glDeleteQueries(1, &query); + + last_offset_check_ = system_now.ToInternalValue(); +} + +} // 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 |