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:
Create(const std::string & name)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
Id()39 uint64 Id() { return thread_id(); }
40
41 private:
42 friend class base::RefCounted<Outputter>;
43
Outputter(const std::string & name)44 explicit Outputter(const std::string& name) : base::Thread(name.c_str()) {}
45
~Outputter()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:
Trace(const std::string & name)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
IsProcessable()63 virtual bool IsProcessable() { return true; }
64 virtual void Process() = 0;
65
name()66 virtual const std::string& name() {
67 return name_;
68 }
69
70 protected:
~Trace()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:
NoopTrace(const std::string & name)111 explicit NoopTrace(const std::string& name) : Trace(name) {}
112
113 // Implementation of Tracer
Start()114 virtual void Start() OVERRIDE {}
End()115 virtual void End() OVERRIDE {}
IsAvailable()116 virtual bool IsAvailable() OVERRIDE { return true; }
IsProcessable()117 virtual bool IsProcessable() OVERRIDE { return false; }
Process()118 virtual void Process() OVERRIDE {}
119
120 private:
~NoopTrace()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:
GPUTracerImpl()130 GPUTracerImpl()
131 : gpu_category_enabled_(
132 TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("gpu")),
133 process_posted_(false) {
134 }
~GPUTracerImpl()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
GLARBTimerTrace(scoped_refptr<Outputter> outputter,const std::string & name,int64 offset)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
~GLARBTimerTrace()195 GLARBTimerTrace::~GLARBTimerTrace() {
196 }
197
Start()198 void GLARBTimerTrace::Start() {
199 glQueryCounter(queries_[0], GL_TIMESTAMP);
200 }
201
End()202 void GLARBTimerTrace::End() {
203 glQueryCounter(queries_[1], GL_TIMESTAMP);
204 end_requested_ = true;
205 }
206
IsAvailable()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
Process()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
Begin(const std::string & name)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
End()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
Process()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
CurrentName() const272 const std::string& GPUTracerImpl::CurrentName() const {
273 if (!current_trace_.get())
274 return base::EmptyString();
275 return current_trace_->name();
276 }
277
CreateTrace(const std::string & name)278 scoped_refptr<Trace> GPUTracerImpl::CreateTrace(const std::string& name) {
279 return new NoopTrace(name);
280 }
281
IssueProcessTask()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
GPUTracerARBTimerQuery()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
~GPUTracerARBTimerQuery()301 GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() {
302 }
303
CreateTrace(const std::string & name)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
Process()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
CalculateTimerOffset()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
Create()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