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.
5 #include "gpu/command_buffer/service/gpu_tracer.h"
10 #include "base/strings/string_util.h"
11 #include "base/time/time.h"
12 #include "base/trace_event/trace_event.h"
13 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
14 #include "gpu/command_buffer/service/context_group.h"
15 #include "ui/gl/gl_version_info.h"
20 static const unsigned int kProcessInterval
= 16;
21 static TraceOutputter
* g_outputter_thread
= NULL
;
26 int64
CPUTime::GetCurrentTime() {
27 return base::TimeTicks::NowFromSystemTraceTime().ToInternalValue();
33 TraceMarker::TraceMarker(const std::string
& category
, const std::string
& name
)
34 : category_(category
),
39 TraceMarker::~TraceMarker() {
42 scoped_refptr
<TraceOutputter
> TraceOutputter::Create(const std::string
& name
) {
43 if (!g_outputter_thread
) {
44 g_outputter_thread
= new TraceOutputter(name
);
46 return g_outputter_thread
;
49 TraceOutputter::TraceOutputter(const std::string
& name
)
50 : named_thread_(name
.c_str()), local_trace_id_(0) {
51 named_thread_
.Start();
55 TraceOutputter::~TraceOutputter() { g_outputter_thread
= NULL
; }
57 void TraceOutputter::TraceDevice(const std::string
& category
,
58 const std::string
& name
,
61 TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP1(
62 TRACE_DISABLED_BY_DEFAULT("gpu.device"),
65 named_thread_
.thread_id(),
69 TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP1(
70 TRACE_DISABLED_BY_DEFAULT("gpu.device"),
73 named_thread_
.thread_id(),
80 void TraceOutputter::TraceServiceBegin(const std::string
& category
,
81 const std::string
& name
) {
82 TRACE_EVENT_COPY_BEGIN1(TRACE_DISABLED_BY_DEFAULT("gpu.service"),
83 name
.c_str(), "gl_category", category
.c_str());
86 void TraceOutputter::TraceServiceEnd(const std::string
& category
,
87 const std::string
& name
) {
88 TRACE_EVENT_COPY_END1(TRACE_DISABLED_BY_DEFAULT("gpu.service"),
89 name
.c_str(), "gl_category", category
.c_str());
92 GPUTrace::GPUTrace(scoped_refptr
<Outputter
> outputter
,
93 scoped_refptr
<CPUTime
> cpu_time
,
94 const std::string
& category
,
95 const std::string
& name
,
97 GpuTracerType tracer_type
)
98 : category_(category
),
100 outputter_(outputter
),
105 tracer_type_(tracer_type
),
106 end_requested_(false) {
107 memset(queries_
, 0, sizeof(queries_
));
108 switch (tracer_type_
) {
109 case kTracerTypeARBTimer
:
110 case kTracerTypeDisjointTimer
:
111 glGenQueriesARB(2, queries_
);
115 tracer_type_
= kTracerTypeInvalid
;
119 GPUTrace::~GPUTrace() {
120 switch (tracer_type_
) {
121 case kTracerTypeInvalid
:
124 case kTracerTypeARBTimer
:
125 case kTracerTypeDisjointTimer
:
126 glDeleteQueriesARB(2, queries_
);
131 void GPUTrace::Start(bool trace_service
) {
133 outputter_
->TraceServiceBegin(category_
, name_
);
136 switch (tracer_type_
) {
137 case kTracerTypeInvalid
:
140 case kTracerTypeDisjointTimer
:
141 // For the disjoint timer, GPU idle time does not seem to increment the
142 // internal counter. We must calculate the offset before any query.
143 // glGetInteger64v is supported under ES3 which we check for before using
144 // the kTracerTypeDisjointTimer.
147 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
148 offset_
= cpu_time_
->GetCurrentTime() -
149 gl_now
/ base::Time::kNanosecondsPerMicrosecond
;
151 // Intentionally fall through to kTracerTypeARBTimer case.xs
152 case kTracerTypeARBTimer
:
153 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
154 glQueryCounter(queries_
[0], GL_TIMESTAMP
);
159 void GPUTrace::End(bool tracing_service
) {
160 end_requested_
= true;
161 switch (tracer_type_
) {
162 case kTracerTypeInvalid
:
165 case kTracerTypeARBTimer
:
166 case kTracerTypeDisjointTimer
:
167 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
168 glQueryCounter(queries_
[1], GL_TIMESTAMP
);
172 if (tracing_service
) {
173 outputter_
->TraceServiceEnd(category_
, name_
);
177 bool GPUTrace::IsAvailable() {
178 if (tracer_type_
!= kTracerTypeInvalid
) {
183 glGetQueryObjectivARB(queries_
[1], GL_QUERY_RESULT_AVAILABLE
, &done
);
190 void GPUTrace::Process() {
191 if (tracer_type_
== kTracerTypeInvalid
)
194 DCHECK(IsAvailable());
196 GLuint64 begin_stamp
= 0;
197 GLuint64 end_stamp
= 0;
199 // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
200 // We need to detect if the end is less then the start and correct for the
202 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, &begin_stamp
);
203 glGetQueryObjectui64v(queries_
[1], GL_QUERY_RESULT
, &end_stamp
);
205 start_time_
= (begin_stamp
/ base::Time::kNanosecondsPerMicrosecond
) +
207 end_time_
= (end_stamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
208 outputter_
->TraceDevice(category_
, name_
, start_time_
, end_time_
);
211 GPUTracer::GPUTracer(gles2::GLES2Decoder
* decoder
)
212 : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
213 TRACE_DISABLED_BY_DEFAULT("gpu.service"))),
214 gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
215 TRACE_DISABLED_BY_DEFAULT("gpu.device"))),
218 tracer_type_(kTracerTypeInvalid
),
219 gpu_timing_synced_(false),
220 gpu_executing_(false),
221 process_posted_(false) {
224 GPUTracer::~GPUTracer() {
227 bool GPUTracer::BeginDecoding() {
231 if (outputter_
== NULL
) {
232 tracer_type_
= DetermineTracerType();
233 const char* tracer_type_name
= "Unknown";
234 switch (tracer_type_
) {
235 case kTracerTypeDisjointTimer
:
236 tracer_type_name
= "GL_EXT_disjoint_timer_query";
238 case kTracerTypeARBTimer
:
239 tracer_type_name
= "GL_ARB_timer_query";
246 outputter_
= CreateOutputter(tracer_type_name
);
249 if (cpu_time_
== NULL
) {
250 cpu_time_
= CreateCPUTime();
253 CalculateTimerOffset();
254 gpu_executing_
= true;
257 // Reset disjoint bit for the disjoint timer.
258 if (tracer_type_
== kTracerTypeDisjointTimer
) {
259 GLint disjoint_value
= 0;
260 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
263 // Begin a Trace for all active markers
264 for (int n
= 0; n
< NUM_TRACER_SOURCES
; n
++) {
265 for (size_t i
= 0; i
< markers_
[n
].size(); i
++) {
266 TraceMarker
& trace_marker
= markers_
[n
][i
];
267 trace_marker
.trace_
= CreateTrace(trace_marker
.category_
,
269 trace_marker
.trace_
->Start(*gpu_trace_srv_category
!= 0);
276 bool GPUTracer::EndDecoding() {
280 // End Trace for all active markers
282 for (int n
= 0; n
< NUM_TRACER_SOURCES
; n
++) {
283 for (size_t i
= 0; i
< markers_
[n
].size(); i
++) {
284 TraceMarker
& marker
= markers_
[n
][i
];
285 if (marker
.trace_
.get()) {
286 marker
.trace_
->End(*gpu_trace_srv_category
!= 0);
287 if (marker
.trace_
->IsEnabled())
288 traces_
.push_back(marker
.trace_
);
290 markers_
[n
][i
].trace_
= 0;
297 gpu_executing_
= false;
299 // NOTE(vmiura): glFlush() here can help give better trace results,
300 // but it distorts the normal device behavior.
304 bool GPUTracer::Begin(const std::string
& category
, const std::string
& name
,
305 GpuTracerSource source
) {
309 DCHECK(source
>= 0 && source
< NUM_TRACER_SOURCES
);
311 // Push new marker from given 'source'
312 markers_
[source
].push_back(TraceMarker(category
, name
));
316 scoped_refptr
<GPUTrace
> trace
= CreateTrace(category
, name
);
317 trace
->Start(*gpu_trace_srv_category
!= 0);
318 markers_
[source
].back().trace_
= trace
;
324 bool GPUTracer::End(GpuTracerSource source
) {
328 DCHECK(source
>= 0 && source
< NUM_TRACER_SOURCES
);
330 // Pop last marker with matching 'source'
331 if (!markers_
[source
].empty()) {
333 scoped_refptr
<GPUTrace
> trace
= markers_
[source
].back().trace_
;
335 trace
->End(*gpu_trace_srv_category
!= 0);
336 if (trace
->IsEnabled())
337 traces_
.push_back(trace
);
342 markers_
[source
].pop_back();
348 bool GPUTracer::IsTracing() {
349 return (*gpu_trace_srv_category
!= 0) || (*gpu_trace_dev_category
!= 0);
352 const std::string
& GPUTracer::CurrentCategory(GpuTracerSource source
) const {
354 source
< NUM_TRACER_SOURCES
&&
355 !markers_
[source
].empty()) {
356 return markers_
[source
].back().category_
;
358 return base::EmptyString();
361 const std::string
& GPUTracer::CurrentName(GpuTracerSource source
) const {
363 source
< NUM_TRACER_SOURCES
&&
364 !markers_
[source
].empty()) {
365 return markers_
[source
].back().name_
;
367 return base::EmptyString();
370 scoped_refptr
<GPUTrace
> GPUTracer::CreateTrace(const std::string
& category
,
371 const std::string
& name
) {
372 GpuTracerType tracer_type
= *gpu_trace_dev_category
? tracer_type_
:
375 return new GPUTrace(outputter_
, cpu_time_
, category
, name
,
376 timer_offset_
, tracer_type
);
379 scoped_refptr
<Outputter
> GPUTracer::CreateOutputter(const std::string
& name
) {
380 return TraceOutputter::Create(name
);
383 scoped_refptr
<CPUTime
> GPUTracer::CreateCPUTime() {
384 return new CPUTime();
387 GpuTracerType
GPUTracer::DetermineTracerType() {
388 ContextGroup
* context_group
= decoder_
->GetContextGroup();
389 const gpu::gles2::FeatureInfo
* feature_info
= context_group
->feature_info();
390 const gfx::GLVersionInfo
& version_info
= feature_info
->gl_version_info();
392 if (version_info
.is_es3
&&
393 gfx::g_driver_gl
.ext
.b_GL_EXT_disjoint_timer_query
) {
394 return kTracerTypeDisjointTimer
;
395 } else if (gfx::g_driver_gl
.ext
.b_GL_ARB_timer_query
) {
396 return kTracerTypeARBTimer
;
399 return kTracerTypeInvalid
;
402 void GPUTracer::PostTask() {
403 base::MessageLoop::current()->PostDelayedTask(
405 base::Bind(&GPUTracer::Process
, base::AsWeakPtr(this)),
406 base::TimeDelta::FromMilliseconds(kProcessInterval
));
409 void GPUTracer::Process() {
410 process_posted_
= false;
415 void GPUTracer::ProcessTraces() {
416 if (tracer_type_
== kTracerTypeInvalid
) {
421 TRACE_EVENT0("gpu", "GPUTracer::ProcessTraces");
423 // Make owning decoder's GL context current
424 if (!decoder_
->MakeCurrent()) {
425 // Skip subsequent GL calls if MakeCurrent fails
430 // Check if disjoint operation has occurred, discard ongoing traces if so.
431 if (tracer_type_
== kTracerTypeDisjointTimer
) {
432 GLint disjoint_value
= 0;
433 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
438 while (!traces_
.empty() && traces_
.front()->IsAvailable()) {
439 traces_
.front()->Process();
443 // Clear pending traces if there were are any errors
444 GLenum err
= glGetError();
445 if (err
!= GL_NO_ERROR
)
449 void GPUTracer::CalculateTimerOffset() {
450 if (tracer_type_
!= kTracerTypeInvalid
) {
451 if (*gpu_trace_dev_category
== '\0') {
452 // If GPU device category is off, invalidate timing sync.
453 gpu_timing_synced_
= false;
454 } else if (!gpu_timing_synced_
&& tracer_type_
== kTracerTypeARBTimer
) {
455 TRACE_EVENT0("gpu", "GPUTracer::CalculateTimerOffset");
457 // ARB Timer is written for OpenGL 3.2 which contains glGetInteger64v().
459 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
460 timer_offset_
= cpu_time_
->GetCurrentTime() -
461 gl_now
/ base::Time::kNanosecondsPerMicrosecond
;
462 gpu_timing_synced_
= true;
467 void GPUTracer::IssueProcessTask() {
468 if (traces_
.empty() || process_posted_
)
471 process_posted_
= true;