1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "gpu/command_buffer/service/gpu_tracer.h" 6 7 #include <deque> 8 9 #include "base/bind.h" 10 #include "base/debug/trace_event.h" 11 #include "base/memory/weak_ptr.h" 12 #include "base/strings/string_util.h" 13 #include "base/threading/thread.h" 14 #include "base/time/time.h" 15 #include "ui/gl/gl_bindings.h" 16 17 namespace gpu { 18 namespace gles2 { 19 namespace { 20 21 class Outputter; 22 23 static const unsigned int kProcessInterval = 16; 24 static Outputter* g_outputter_thread = NULL; 25 26 class Outputter 27 : private base::Thread, 28 public base::RefCounted<Outputter> { 29 public: 30 static scoped_refptr<Outputter> Create(const std::string& name) { 31 if (!g_outputter_thread) { 32 g_outputter_thread = new Outputter(name); 33 g_outputter_thread->Start(); 34 g_outputter_thread->Stop(); 35 } 36 return g_outputter_thread; 37 } 38 39 uint64 Id() { return thread_id(); } 40 41 private: 42 friend class base::RefCounted<Outputter>; 43 44 explicit Outputter(const std::string& name) : base::Thread(name.c_str()) {} 45 46 virtual ~Outputter() { 47 g_outputter_thread = NULL; 48 } 49 50 DISALLOW_COPY_AND_ASSIGN(Outputter); 51 }; 52 53 class Trace : public base::RefCounted<Trace> { 54 public: 55 explicit Trace(const std::string& name) : name_(name) {} 56 57 virtual void Start() = 0; 58 virtual void End() = 0; 59 60 // True if the the results of this query are available. 61 virtual bool IsAvailable() = 0; 62 63 virtual bool IsProcessable() { return true; } 64 virtual void Process() = 0; 65 66 virtual const std::string& name() { 67 return name_; 68 } 69 70 protected: 71 virtual ~Trace() {} 72 73 private: 74 friend class base::RefCounted<Trace>; 75 76 std::string name_; 77 78 DISALLOW_COPY_AND_ASSIGN(Trace); 79 }; 80 81 class GLARBTimerTrace : public Trace { 82 public: 83 GLARBTimerTrace(scoped_refptr<Outputter> outputter, const std::string& name, 84 int64 offset); 85 86 // Implementation of Tracer 87 virtual void Start() OVERRIDE; 88 virtual void End() OVERRIDE; 89 virtual bool IsAvailable() OVERRIDE; 90 virtual void Process() OVERRIDE; 91 92 private: 93 virtual ~GLARBTimerTrace(); 94 95 void Output(); 96 97 scoped_refptr<Outputter> outputter_; 98 99 int64 offset_; 100 int64 start_time_; 101 int64 end_time_; 102 bool end_requested_; 103 104 GLuint queries_[2]; 105 106 DISALLOW_COPY_AND_ASSIGN(GLARBTimerTrace); 107 }; 108 109 class NoopTrace : public Trace { 110 public: 111 explicit NoopTrace(const std::string& name) : Trace(name) {} 112 113 // Implementation of Tracer 114 virtual void Start() OVERRIDE {} 115 virtual void End() OVERRIDE {} 116 virtual bool IsAvailable() OVERRIDE { return true; } 117 virtual bool IsProcessable() OVERRIDE { return false; } 118 virtual void Process() OVERRIDE {} 119 120 private: 121 virtual ~NoopTrace() {} 122 123 DISALLOW_COPY_AND_ASSIGN(NoopTrace); 124 }; 125 126 class GPUTracerImpl 127 : public GPUTracer, 128 public base::SupportsWeakPtr<GPUTracerImpl> { 129 public: 130 GPUTracerImpl() 131 : gpu_category_enabled_( 132 TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("gpu")), 133 process_posted_(false) { 134 } 135 virtual ~GPUTracerImpl() {} 136 137 // Implementation of gpu::gles2::GPUTracer 138 virtual bool Begin(const std::string& name) OVERRIDE; 139 virtual bool End() OVERRIDE; 140 virtual const std::string& CurrentName() const OVERRIDE; 141 142 // Process any completed traces. 143 virtual void Process(); 144 145 protected: 146 // Create a new trace. 147 virtual scoped_refptr<Trace> CreateTrace(const std::string& name); 148 149 const unsigned char* gpu_category_enabled_; 150 151 private: 152 void IssueProcessTask(); 153 154 scoped_refptr<Trace> current_trace_; 155 std::deque<scoped_refptr<Trace> > traces_; 156 157 bool process_posted_; 158 159 DISALLOW_COPY_AND_ASSIGN(GPUTracerImpl); 160 }; 161 162 class GPUTracerARBTimerQuery : public GPUTracerImpl { 163 public: 164 GPUTracerARBTimerQuery(); 165 virtual ~GPUTracerARBTimerQuery(); 166 167 // Implementation of GPUTracerImpl 168 virtual void Process() OVERRIDE; 169 170 private: 171 // Implementation of GPUTracerImpl. 172 virtual scoped_refptr<Trace> CreateTrace(const std::string& name) OVERRIDE; 173 174 void CalculateTimerOffset(); 175 176 scoped_refptr<Outputter> outputter_; 177 178 int64 timer_offset_; 179 int64 last_offset_check_; 180 181 DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery); 182 }; 183 184 GLARBTimerTrace::GLARBTimerTrace(scoped_refptr<Outputter> outputter, 185 const std::string& name, int64 offset) 186 : Trace(name), 187 outputter_(outputter), 188 offset_(offset), 189 start_time_(0), 190 end_time_(0), 191 end_requested_(false) { 192 glGenQueries(2, queries_); 193 } 194 195 GLARBTimerTrace::~GLARBTimerTrace() { 196 } 197 198 void GLARBTimerTrace::Start() { 199 glQueryCounter(queries_[0], GL_TIMESTAMP); 200 } 201 202 void GLARBTimerTrace::End() { 203 glQueryCounter(queries_[1], GL_TIMESTAMP); 204 end_requested_ = true; 205 } 206 207 bool GLARBTimerTrace::IsAvailable() { 208 if (!end_requested_) 209 return false; 210 211 GLint done = 0; 212 glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done); 213 return !!done; 214 } 215 216 void GLARBTimerTrace::Process() { 217 DCHECK(IsAvailable()); 218 219 GLint64 timestamp; 220 221 // TODO(dsinclair): It's possible for the timer to wrap during the start/end. 222 // We need to detect if the end is less then the start and correct for the 223 // wrapping. 224 glGetQueryObjecti64v(queries_[0], GL_QUERY_RESULT, ×tamp); 225 start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; 226 227 glGetQueryObjecti64v(queries_[1], GL_QUERY_RESULT, ×tamp); 228 end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; 229 230 glDeleteQueries(2, queries_); 231 232 TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("gpu", name().c_str(), 233 this, outputter_->Id(), start_time_); 234 TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0("gpu", name().c_str(), 235 this, outputter_->Id(), end_time_); 236 } 237 238 bool GPUTracerImpl::Begin(const std::string& name) { 239 // Make sure we are not nesting trace commands. 240 if (current_trace_.get()) 241 return false; 242 243 current_trace_ = CreateTrace(name); 244 current_trace_->Start(); 245 return true; 246 } 247 248 bool GPUTracerImpl::End() { 249 if (!current_trace_.get()) 250 return false; 251 252 current_trace_->End(); 253 if (current_trace_->IsProcessable()) 254 traces_.push_back(current_trace_); 255 current_trace_ = NULL; 256 257 IssueProcessTask(); 258 return true; 259 } 260 261 void GPUTracerImpl::Process() { 262 process_posted_ = false; 263 264 while (!traces_.empty() && traces_.front()->IsAvailable()) { 265 traces_.front()->Process(); 266 traces_.pop_front(); 267 } 268 269 IssueProcessTask(); 270 } 271 272 const std::string& GPUTracerImpl::CurrentName() const { 273 if (!current_trace_.get()) 274 return EmptyString(); 275 return current_trace_->name(); 276 } 277 278 scoped_refptr<Trace> GPUTracerImpl::CreateTrace(const std::string& name) { 279 return new NoopTrace(name); 280 } 281 282 void GPUTracerImpl::IssueProcessTask() { 283 if (traces_.empty() || process_posted_) 284 return; 285 286 process_posted_ = true; 287 base::MessageLoop::current()->PostDelayedTask( 288 FROM_HERE, 289 base::Bind(&GPUTracerImpl::Process, base::AsWeakPtr(this)), 290 base::TimeDelta::FromMilliseconds(kProcessInterval)); 291 } 292 293 GPUTracerARBTimerQuery::GPUTracerARBTimerQuery() 294 : GPUTracerImpl(), 295 timer_offset_(0), 296 last_offset_check_(0) { 297 CalculateTimerOffset(); 298 outputter_ = Outputter::Create("GL_ARB_timer_query"); 299 } 300 301 GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() { 302 } 303 304 scoped_refptr<Trace> GPUTracerARBTimerQuery::CreateTrace( 305 const std::string& name) { 306 if (*gpu_category_enabled_) 307 return new GLARBTimerTrace(outputter_, name, timer_offset_); 308 return GPUTracerImpl::CreateTrace(name); 309 } 310 311 void GPUTracerARBTimerQuery::Process() { 312 GPUTracerImpl::Process(); 313 314 if (*gpu_category_enabled_ && 315 (last_offset_check_ + base::Time::kMicrosecondsPerSecond) < 316 base::TimeTicks::NowFromSystemTraceTime().ToInternalValue()) 317 CalculateTimerOffset(); 318 } 319 320 void GPUTracerARBTimerQuery::CalculateTimerOffset() { 321 TRACE_EVENT0("gpu", "CalculateTimerOffset"); 322 // TODO(dsinclair): Change to glGetInteger64v. 323 GLuint64 gl_now = 0; 324 GLuint query; 325 glGenQueries(1, &query); 326 327 glQueryCounter(query, GL_TIMESTAMP); 328 glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now); 329 base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime(); 330 331 gl_now /= base::Time::kNanosecondsPerMicrosecond; 332 timer_offset_ = system_now.ToInternalValue() - gl_now; 333 glDeleteQueries(1, &query); 334 335 last_offset_check_ = system_now.ToInternalValue(); 336 } 337 338 } // namespace 339 340 scoped_ptr<GPUTracer> GPUTracer::Create() { 341 if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) 342 return scoped_ptr<GPUTracer>(new GPUTracerARBTimerQuery()); 343 return scoped_ptr<GPUTracer>(new GPUTracerImpl()); 344 } 345 346 } // namespace gles2 347 } // namespace gpu 348