• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/strings/string_util.h"
12 #include "base/time/time.h"
13 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
14 
15 namespace gpu {
16 namespace gles2 {
17 
18 static const unsigned int kProcessInterval = 16;
19 static TraceOutputter* g_outputter_thread = NULL;
20 
Create(const std::string & name)21 scoped_refptr<TraceOutputter> TraceOutputter::Create(const std::string& name) {
22   if (!g_outputter_thread) {
23     g_outputter_thread = new TraceOutputter(name);
24   }
25   return g_outputter_thread;
26 }
27 
TraceOutputter(const std::string & name)28 TraceOutputter::TraceOutputter(const std::string& name)
29     : named_thread_(name.c_str()), local_trace_id_(0) {
30   named_thread_.Start();
31   named_thread_.Stop();
32 }
33 
~TraceOutputter()34 TraceOutputter::~TraceOutputter() { g_outputter_thread = NULL; }
35 
Trace(const std::string & name,int64 start_time,int64 end_time)36 void TraceOutputter::Trace(const std::string& name,
37                            int64 start_time,
38                            int64 end_time) {
39   TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
40       TRACE_DISABLED_BY_DEFAULT("gpu.device"),
41       name.c_str(),
42       local_trace_id_,
43       named_thread_.thread_id(),
44       start_time);
45   TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0(
46       TRACE_DISABLED_BY_DEFAULT("gpu.device"),
47       name.c_str(),
48       local_trace_id_,
49       named_thread_.thread_id(),
50       end_time);
51   ++local_trace_id_;
52 }
53 
54 class NoopTrace : public Trace {
55  public:
NoopTrace(const std::string & name)56   explicit NoopTrace(const std::string& name) : Trace(name) {}
57 
58   // Implementation of Tracer
Start()59   virtual void Start() OVERRIDE {
60     TRACE_EVENT_COPY_ASYNC_BEGIN0(
61         TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
62   }
End()63   virtual void End() OVERRIDE {
64     TRACE_EVENT_COPY_ASYNC_END0(
65         TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
66   }
IsAvailable()67   virtual bool IsAvailable() OVERRIDE { return true; }
IsProcessable()68   virtual bool IsProcessable() OVERRIDE { return false; }
Process()69   virtual void Process() OVERRIDE {}
70 
71  private:
~NoopTrace()72   virtual ~NoopTrace() {}
73 
74   DISALLOW_COPY_AND_ASSIGN(NoopTrace);
75 };
76 
77 struct TraceMarker {
TraceMarkergpu::gles2::TraceMarker78   TraceMarker(const std::string& name, GpuTracerSource source)
79       : name_(name), source_(source) {}
80 
81   std::string name_;
82   GpuTracerSource source_;
83   scoped_refptr<Trace> trace_;
84 };
85 
86 class GPUTracerImpl
87     : public GPUTracer,
88       public base::SupportsWeakPtr<GPUTracerImpl> {
89  public:
GPUTracerImpl()90   GPUTracerImpl()
91       : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
92             TRACE_DISABLED_BY_DEFAULT("gpu.service"))),
93         gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
94             TRACE_DISABLED_BY_DEFAULT("gpu.device"))),
95         gpu_executing_(false),
96         process_posted_(false) {}
~GPUTracerImpl()97   virtual ~GPUTracerImpl() {}
98 
99   // Implementation of gpu::gles2::GPUTracer
100   virtual bool BeginDecoding() OVERRIDE;
101   virtual bool EndDecoding() OVERRIDE;
102   virtual bool Begin(const std::string& name, GpuTracerSource source) OVERRIDE;
103   virtual bool End(GpuTracerSource source) OVERRIDE;
104   virtual const std::string& CurrentName() const OVERRIDE;
IsTracing()105   virtual bool IsTracing() OVERRIDE {
106     return (*gpu_trace_srv_category != 0) || (*gpu_trace_dev_category != 0);
107   }
CalculateTimerOffset()108   virtual void CalculateTimerOffset() {}
109 
110   // Process any completed traces.
111   virtual void Process();
112   virtual void ProcessTraces();
113 
114  protected:
115   // Create a new trace.
116   virtual scoped_refptr<Trace> CreateTrace(const std::string& name);
117 
118   const unsigned char* gpu_trace_srv_category;
119   const unsigned char* gpu_trace_dev_category;
120 
121  protected:
122   void IssueProcessTask();
123 
124   std::vector<TraceMarker> markers_;
125   std::deque<scoped_refptr<Trace> > traces_;
126 
127   bool gpu_executing_;
128   bool process_posted_;
129 
130   DISALLOW_COPY_AND_ASSIGN(GPUTracerImpl);
131 };
132 
133 class GPUTracerARBTimerQuery : public GPUTracerImpl {
134  public:
135   explicit GPUTracerARBTimerQuery(gles2::GLES2Decoder* decoder);
136   virtual ~GPUTracerARBTimerQuery();
137 
138   // Implementation of GPUTracerImpl
139   virtual void ProcessTraces() OVERRIDE;
140 
141  protected:
142   // Implementation of GPUTracerImpl.
143   virtual bool BeginDecoding() OVERRIDE;
144   virtual bool EndDecoding() OVERRIDE;
145   virtual scoped_refptr<Trace> CreateTrace(const std::string& name) OVERRIDE;
146   virtual void CalculateTimerOffset() OVERRIDE;
147 
148   scoped_refptr<Outputter> outputter_;
149 
150   bool gpu_timing_synced_;
151   int64 timer_offset_;
152 
153   gles2::GLES2Decoder* decoder_;
154 
155   DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery);
156 };
157 
IsProcessable()158 bool Trace::IsProcessable() { return true; }
159 
name()160 const std::string& Trace::name() { return name_; }
161 
GLARBTimerTrace(scoped_refptr<Outputter> outputter,const std::string & name,int64 offset)162 GLARBTimerTrace::GLARBTimerTrace(scoped_refptr<Outputter> outputter,
163                                  const std::string& name,
164                                  int64 offset)
165     : Trace(name),
166       outputter_(outputter),
167       offset_(offset),
168       start_time_(0),
169       end_time_(0),
170       end_requested_(false) {
171   glGenQueries(2, queries_);
172 }
173 
~GLARBTimerTrace()174 GLARBTimerTrace::~GLARBTimerTrace() { glDeleteQueries(2, queries_); }
175 
Start()176 void GLARBTimerTrace::Start() {
177   TRACE_EVENT_COPY_ASYNC_BEGIN0(
178       TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
179   glQueryCounter(queries_[0], GL_TIMESTAMP);
180 }
181 
End()182 void GLARBTimerTrace::End() {
183   glQueryCounter(queries_[1], GL_TIMESTAMP);
184   end_requested_ = true;
185   TRACE_EVENT_COPY_ASYNC_END0(
186       TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
187 }
188 
IsAvailable()189 bool GLARBTimerTrace::IsAvailable() {
190   if (!end_requested_)
191     return false;
192 
193   GLint done = 0;
194   glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done);
195   return !!done;
196 }
197 
Process()198 void GLARBTimerTrace::Process() {
199   DCHECK(IsAvailable());
200 
201   GLuint64 timestamp;
202 
203   // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
204   // We need to detect if the end is less then the start and correct for the
205   // wrapping.
206   glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &timestamp);
207   start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
208 
209   glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, &timestamp);
210   end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
211 
212   glDeleteQueries(2, queries_);
213   outputter_->Trace(name(), start_time_, end_time_);
214 }
215 
BeginDecoding()216 bool GPUTracerImpl::BeginDecoding() {
217   if (gpu_executing_)
218     return false;
219 
220   gpu_executing_ = true;
221 
222   if (IsTracing()) {
223     // Begin a Trace for all active markers
224     for (size_t i = 0; i < markers_.size(); i++) {
225       markers_[i].trace_ = CreateTrace(markers_[i].name_);
226       markers_[i].trace_->Start();
227     }
228   }
229   return true;
230 }
231 
EndDecoding()232 bool GPUTracerImpl::EndDecoding() {
233   if (!gpu_executing_)
234     return false;
235 
236   // End Trace for all active markers
237   if (IsTracing()) {
238     for (size_t i = 0; i < markers_.size(); i++) {
239       if (markers_[i].trace_) {
240         markers_[i].trace_->End();
241         if (markers_[i].trace_->IsProcessable())
242           traces_.push_back(markers_[i].trace_);
243         markers_[i].trace_ = 0;
244       }
245     }
246     IssueProcessTask();
247   }
248 
249   gpu_executing_ = false;
250   return true;
251 }
252 
Begin(const std::string & name,GpuTracerSource source)253 bool GPUTracerImpl::Begin(const std::string& name, GpuTracerSource source) {
254   if (!gpu_executing_)
255     return false;
256 
257   // Push new marker from given 'source'
258   markers_.push_back(TraceMarker(name, source));
259 
260   // Create trace
261   if (IsTracing()) {
262     scoped_refptr<Trace> trace = CreateTrace(name);
263     trace->Start();
264     markers_.back().trace_ = trace;
265   }
266   return true;
267 }
268 
End(GpuTracerSource source)269 bool GPUTracerImpl::End(GpuTracerSource source) {
270   if (!gpu_executing_)
271     return false;
272 
273   // Pop last marker with matching 'source'
274   for (int i = markers_.size() - 1; i >= 0; i--) {
275     if (markers_[i].source_ == source) {
276       // End trace
277       if (IsTracing()) {
278         scoped_refptr<Trace> trace = markers_[i].trace_;
279         if (trace) {
280           trace->End();
281           if (trace->IsProcessable())
282             traces_.push_back(trace);
283           IssueProcessTask();
284         }
285       }
286 
287       markers_.erase(markers_.begin() + i);
288       return true;
289     }
290   }
291   return false;
292 }
293 
Process()294 void GPUTracerImpl::Process() {
295   process_posted_ = false;
296   ProcessTraces();
297   IssueProcessTask();
298 }
299 
ProcessTraces()300 void GPUTracerImpl::ProcessTraces() {
301   while (!traces_.empty() && traces_.front()->IsAvailable()) {
302     traces_.front()->Process();
303     traces_.pop_front();
304   }
305 }
306 
CurrentName() const307 const std::string& GPUTracerImpl::CurrentName() const {
308   if (markers_.empty())
309     return base::EmptyString();
310   return markers_.back().name_;
311 }
312 
CreateTrace(const std::string & name)313 scoped_refptr<Trace> GPUTracerImpl::CreateTrace(const std::string& name) {
314   return new NoopTrace(name);
315 }
316 
IssueProcessTask()317 void GPUTracerImpl::IssueProcessTask() {
318   if (traces_.empty() || process_posted_)
319     return;
320 
321   process_posted_ = true;
322   base::MessageLoop::current()->PostDelayedTask(
323       FROM_HERE,
324       base::Bind(&GPUTracerImpl::Process, base::AsWeakPtr(this)),
325       base::TimeDelta::FromMilliseconds(kProcessInterval));
326 }
327 
GPUTracerARBTimerQuery(gles2::GLES2Decoder * decoder)328 GPUTracerARBTimerQuery::GPUTracerARBTimerQuery(gles2::GLES2Decoder* decoder)
329     : timer_offset_(0), decoder_(decoder) {
330   outputter_ = TraceOutputter::Create("GL_ARB_timer_query");
331 }
332 
~GPUTracerARBTimerQuery()333 GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() {
334 }
335 
CreateTrace(const std::string & name)336 scoped_refptr<Trace> GPUTracerARBTimerQuery::CreateTrace(
337     const std::string& name) {
338   if (*gpu_trace_dev_category)
339     return new GLARBTimerTrace(outputter_, name, timer_offset_);
340   return GPUTracerImpl::CreateTrace(name);
341 }
342 
BeginDecoding()343 bool GPUTracerARBTimerQuery::BeginDecoding() {
344   if (*gpu_trace_dev_category) {
345     // Make sure timing is synced before tracing
346     if (!gpu_timing_synced_) {
347       CalculateTimerOffset();
348       gpu_timing_synced_ = true;
349     }
350   } else {
351     // If GPU device category is off, invalidate timing sync
352     gpu_timing_synced_ = false;
353   }
354 
355   return GPUTracerImpl::BeginDecoding();
356 }
357 
EndDecoding()358 bool GPUTracerARBTimerQuery::EndDecoding() {
359   bool ret = GPUTracerImpl::EndDecoding();
360 
361   // NOTE(vmiura_: glFlush() here can help give better trace results,
362   // but it distorts the normal device behavior.
363   return ret;
364 }
365 
ProcessTraces()366 void GPUTracerARBTimerQuery::ProcessTraces() {
367   TRACE_EVENT0("gpu", "GPUTracerARBTimerQuery::ProcessTraces");
368 
369   // Make owning decoder's GL context current
370   if (!decoder_->MakeCurrent()) {
371     // Skip subsequent GL calls if MakeCurrent fails
372     traces_.clear();
373     return;
374   }
375 
376   while (!traces_.empty() && traces_.front()->IsAvailable()) {
377     traces_.front()->Process();
378     traces_.pop_front();
379   }
380 
381   // Clear pending traces if there were are any errors
382   GLenum err = glGetError();
383   if (err != GL_NO_ERROR)
384     traces_.clear();
385 }
386 
CalculateTimerOffset()387 void GPUTracerARBTimerQuery::CalculateTimerOffset() {
388   TRACE_EVENT0("gpu", "GPUTracerARBTimerQuery::CalculateTimerOffset");
389 
390   // NOTE(vmiura): It would be better to use glGetInteger64v, however
391   // it's not available everywhere.
392   GLuint64 gl_now = 0;
393   GLuint query;
394   glFinish();
395   glGenQueries(1, &query);
396   glQueryCounter(query, GL_TIMESTAMP);
397   glFinish();
398   glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now);
399   base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime();
400 
401   gl_now /= base::Time::kNanosecondsPerMicrosecond;
402   timer_offset_ = system_now.ToInternalValue() - gl_now;
403   glDeleteQueries(1, &query);
404 }
405 
GPUTracer()406 GPUTracer::GPUTracer() {}
407 
~GPUTracer()408 GPUTracer::~GPUTracer() {}
409 
Create(gles2::GLES2Decoder * decoder)410 scoped_ptr<GPUTracer> GPUTracer::Create(gles2::GLES2Decoder* decoder) {
411   if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) {
412     return scoped_ptr<GPUTracer>(new GPUTracerARBTimerQuery(decoder));
413   }
414   return scoped_ptr<GPUTracer>(new GPUTracerImpl());
415 }
416 
417 }  // namespace gles2
418 }  // namespace gpu
419