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/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"
18 static const unsigned int kProcessInterval
= 16;
19 static TraceOutputter
* g_outputter_thread
= NULL
;
21 TraceMarker::TraceMarker(const std::string
& name
)
26 TraceMarker::~TraceMarker() {
29 scoped_refptr
<TraceOutputter
> TraceOutputter::Create(const std::string
& name
) {
30 if (!g_outputter_thread
) {
31 g_outputter_thread
= new TraceOutputter(name
);
33 return g_outputter_thread
;
36 TraceOutputter::TraceOutputter(const std::string
& name
)
37 : named_thread_(name
.c_str()), local_trace_id_(0) {
38 named_thread_
.Start();
42 TraceOutputter::~TraceOutputter() { g_outputter_thread
= NULL
; }
44 void TraceOutputter::Trace(const std::string
& name
,
47 TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
48 TRACE_DISABLED_BY_DEFAULT("gpu.device"),
51 named_thread_
.thread_id(),
53 TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0(
54 TRACE_DISABLED_BY_DEFAULT("gpu.device"),
57 named_thread_
.thread_id(),
62 GPUTrace::GPUTrace(scoped_refptr
<Outputter
> outputter
,
63 const std::string
& name
,
65 GpuTracerType tracer_type
)
67 outputter_(outputter
),
71 tracer_type_(tracer_type
),
72 end_requested_(false) {
73 memset(queries_
, 0, sizeof(queries_
));
74 switch (tracer_type_
) {
75 case kTracerTypeARBTimer
:
76 case kTracerTypeDisjointTimer
:
77 glGenQueriesARB(2, queries_
);
81 tracer_type_
= kTracerTypeInvalid
;
85 GPUTrace::~GPUTrace() {
86 switch (tracer_type_
) {
87 case kTracerTypeInvalid
:
90 case kTracerTypeARBTimer
:
91 case kTracerTypeDisjointTimer
:
92 glDeleteQueriesARB(2, queries_
);
97 void GPUTrace::Start() {
98 TRACE_EVENT_COPY_ASYNC_BEGIN0(
99 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
101 switch (tracer_type_
) {
102 case kTracerTypeInvalid
:
105 case kTracerTypeDisjointTimer
:
106 // For the disjoint timer, GPU idle time does not seem to increment the
107 // internal counter. We must calculate the offset before any query. The
108 // good news is any device that supports disjoint timer will also support
109 // glGetInteger64v, so we can query it directly unlike the ARBTimer case.
110 // The "offset_" variable will always be 0 during normal use cases, only
111 // under the unit tests will it be set to specific test values.
114 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
115 offset_
= base::TimeTicks::NowFromSystemTraceTime().ToInternalValue() -
116 gl_now
/ base::Time::kNanosecondsPerMicrosecond
;
118 // Intentionally fall through to kTracerTypeARBTimer case.xs
119 case kTracerTypeARBTimer
:
120 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
121 glQueryCounter(queries_
[0], GL_TIMESTAMP
);
126 void GPUTrace::End() {
127 end_requested_
= true;
128 switch (tracer_type_
) {
129 case kTracerTypeInvalid
:
132 case kTracerTypeARBTimer
:
133 case kTracerTypeDisjointTimer
:
134 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
135 glQueryCounter(queries_
[1], GL_TIMESTAMP
);
139 TRACE_EVENT_COPY_ASYNC_END0(
140 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
143 bool GPUTrace::IsAvailable() {
144 if (tracer_type_
!= kTracerTypeInvalid
) {
149 glGetQueryObjectiv(queries_
[1], GL_QUERY_RESULT_AVAILABLE
, &done
);
156 void GPUTrace::Process() {
157 if (tracer_type_
== kTracerTypeInvalid
)
160 DCHECK(IsAvailable());
162 GLuint64 begin_stamp
= 0;
163 GLuint64 end_stamp
= 0;
165 // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
166 // We need to detect if the end is less then the start and correct for the
168 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, &begin_stamp
);
169 glGetQueryObjectui64v(queries_
[1], GL_QUERY_RESULT
, &end_stamp
);
171 start_time_
= (begin_stamp
/ base::Time::kNanosecondsPerMicrosecond
) +
173 end_time_
= (end_stamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
174 outputter_
->Trace(name(), start_time_
, end_time_
);
177 GPUTracer::GPUTracer(gles2::GLES2Decoder
* decoder
)
178 : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
179 TRACE_DISABLED_BY_DEFAULT("gpu.service"))),
180 gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
181 TRACE_DISABLED_BY_DEFAULT("gpu.device"))),
184 last_tracer_source_(kTraceGroupInvalid
),
185 tracer_type_(kTracerTypeInvalid
),
186 gpu_timing_synced_(false),
187 gpu_executing_(false),
188 process_posted_(false) {
189 if (gfx::g_driver_gl
.ext
.b_GL_EXT_disjoint_timer_query
) {
190 tracer_type_
= kTracerTypeDisjointTimer
;
191 outputter_
= TraceOutputter::Create("GL_EXT_disjoint_timer_query");
192 } else if (gfx::g_driver_gl
.ext
.b_GL_ARB_timer_query
) {
193 tracer_type_
= kTracerTypeARBTimer
;
194 outputter_
= TraceOutputter::Create("GL_ARB_timer_query");
198 GPUTracer::~GPUTracer() {
201 bool GPUTracer::BeginDecoding() {
205 CalculateTimerOffset();
206 gpu_executing_
= true;
209 // Reset disjoint bit for the disjoint timer.
210 if (tracer_type_
== kTracerTypeDisjointTimer
) {
211 GLint disjoint_value
= 0;
212 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
215 // Begin a Trace for all active markers
216 for (int n
= 0; n
< NUM_TRACER_SOURCES
; n
++) {
217 for (size_t i
= 0; i
< markers_
[n
].size(); i
++) {
218 markers_
[n
][i
].trace_
= CreateTrace(markers_
[n
][i
].name_
);
219 markers_
[n
][i
].trace_
->Start();
226 bool GPUTracer::EndDecoding() {
230 // End Trace for all active markers
232 for (int n
= 0; n
< NUM_TRACER_SOURCES
; n
++) {
233 for (size_t i
= 0; i
< markers_
[n
].size(); i
++) {
234 if (markers_
[n
][i
].trace_
.get()) {
235 markers_
[n
][i
].trace_
->End();
236 if (markers_
[n
][i
].trace_
->IsEnabled())
237 traces_
.push_back(markers_
[n
][i
].trace_
);
238 markers_
[n
][i
].trace_
= 0;
245 gpu_executing_
= false;
247 // NOTE(vmiura): glFlush() here can help give better trace results,
248 // but it distorts the normal device behavior.
252 bool GPUTracer::Begin(const std::string
& name
, GpuTracerSource source
) {
256 DCHECK(source
>= 0 && source
< NUM_TRACER_SOURCES
);
258 // Push new marker from given 'source'
259 last_tracer_source_
= source
;
260 markers_
[source
].push_back(TraceMarker(name
));
264 scoped_refptr
<GPUTrace
> trace
= CreateTrace(name
);
266 markers_
[source
].back().trace_
= trace
;
272 bool GPUTracer::End(GpuTracerSource source
) {
276 DCHECK(source
>= 0 && source
< NUM_TRACER_SOURCES
);
278 // Pop last marker with matching 'source'
279 if (!markers_
[source
].empty()) {
281 scoped_refptr
<GPUTrace
> trace
= markers_
[source
].back().trace_
;
284 if (trace
->IsEnabled())
285 traces_
.push_back(trace
);
290 markers_
[source
].pop_back();
296 bool GPUTracer::IsTracing() {
297 return (*gpu_trace_srv_category
!= 0) || (*gpu_trace_dev_category
!= 0);
300 const std::string
& GPUTracer::CurrentName() const {
301 if (last_tracer_source_
>= 0 &&
302 last_tracer_source_
< NUM_TRACER_SOURCES
&&
303 !markers_
[last_tracer_source_
].empty()) {
304 return markers_
[last_tracer_source_
].back().name_
;
306 return base::EmptyString();
309 scoped_refptr
<GPUTrace
> GPUTracer::CreateTrace(const std::string
& name
) {
310 GpuTracerType tracer_type
= *gpu_trace_dev_category
? tracer_type_
:
313 return new GPUTrace(outputter_
, name
, timer_offset_
, tracer_type
);
316 void GPUTracer::Process() {
317 process_posted_
= false;
322 void GPUTracer::ProcessTraces() {
323 if (tracer_type_
== kTracerTypeInvalid
) {
328 TRACE_EVENT0("gpu", "GPUTracer::ProcessTraces");
330 // Make owning decoder's GL context current
331 if (!decoder_
->MakeCurrent()) {
332 // Skip subsequent GL calls if MakeCurrent fails
337 // Check if disjoint operation has occurred, discard ongoing traces if so.
338 if (tracer_type_
== kTracerTypeDisjointTimer
) {
339 GLint disjoint_value
= 0;
340 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
345 while (!traces_
.empty() && traces_
.front()->IsAvailable()) {
346 traces_
.front()->Process();
350 // Clear pending traces if there were are any errors
351 GLenum err
= glGetError();
352 if (err
!= GL_NO_ERROR
)
356 void GPUTracer::CalculateTimerOffset() {
357 if (tracer_type_
!= kTracerTypeInvalid
) {
358 if (*gpu_trace_dev_category
== '\0') {
359 // If GPU device category is off, invalidate timing sync.
360 gpu_timing_synced_
= false;
362 } else if (tracer_type_
== kTracerTypeDisjointTimer
) {
363 // Disjoint timers offsets should be calculated before every query.
364 gpu_timing_synced_
= true;
368 if (gpu_timing_synced_
)
371 TRACE_EVENT0("gpu", "GPUTracer::CalculateTimerOffset");
373 // NOTE(vmiura): It would be better to use glGetInteger64v, however
374 // it's not available everywhere.
378 glGenQueriesARB(1, &query
);
381 glQueryCounter(query
, GL_TIMESTAMP
);
384 glGetQueryObjectui64v(query
, GL_QUERY_RESULT
, &gl_now
);
385 glDeleteQueriesARB(1, &query
);
387 base::TimeTicks system_now
= base::TimeTicks::NowFromSystemTraceTime();
389 gl_now
/= base::Time::kNanosecondsPerMicrosecond
;
390 timer_offset_
= system_now
.ToInternalValue() - gl_now
;
391 gpu_timing_synced_
= true;
395 void GPUTracer::IssueProcessTask() {
396 if (traces_
.empty() || process_posted_
)
399 process_posted_
= true;
400 base::MessageLoop::current()->PostDelayedTask(
402 base::Bind(&GPUTracer::Process
, base::AsWeakPtr(this)),
403 base::TimeDelta::FromMilliseconds(kProcessInterval
));