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, ×tamp);
207 start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
208
209 glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, ×tamp);
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