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(const std::string
& name
)
67 end_requested_(false),
71 GPUTrace::GPUTrace(scoped_refptr
<Outputter
> outputter
,
72 const std::string
& name
,
75 outputter_(outputter
),
79 end_requested_(false),
81 glGenQueries(2, queries_
);
84 GPUTrace::~GPUTrace() {
86 glDeleteQueries(2, queries_
);
89 void GPUTrace::Start() {
90 TRACE_EVENT_COPY_ASYNC_BEGIN0(
91 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
93 glQueryCounter(queries_
[0], GL_TIMESTAMP
);
97 void GPUTrace::End() {
99 glQueryCounter(queries_
[1], GL_TIMESTAMP
);
100 end_requested_
= true;
103 TRACE_EVENT_COPY_ASYNC_END0(
104 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this);
107 bool GPUTrace::IsAvailable() {
110 else if (!end_requested_
)
114 glGetQueryObjectiv(queries_
[1], GL_QUERY_RESULT_AVAILABLE
, &done
);
118 void GPUTrace::Process() {
122 DCHECK(IsAvailable());
126 // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
127 // We need to detect if the end is less then the start and correct for the
129 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, ×tamp
);
130 start_time_
= (timestamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
132 glGetQueryObjectui64v(queries_
[1], GL_QUERY_RESULT
, ×tamp
);
133 end_time_
= (timestamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
135 glDeleteQueries(2, queries_
);
136 outputter_
->Trace(name(), start_time_
, end_time_
);
139 GPUTracer::GPUTracer(gles2::GLES2Decoder
* decoder
)
140 : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
141 TRACE_DISABLED_BY_DEFAULT("gpu.service"))),
142 gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
143 TRACE_DISABLED_BY_DEFAULT("gpu.device"))),
146 last_tracer_source_(kTraceGroupInvalid
),
148 gpu_timing_synced_(false),
149 gpu_executing_(false),
150 process_posted_(false) {
151 if (gfx::g_driver_gl
.ext
.b_GL_ARB_timer_query
) {
152 outputter_
= TraceOutputter::Create("GL_ARB_timer_query");
157 GPUTracer::~GPUTracer() {
160 bool GPUTracer::BeginDecoding() {
162 if (*gpu_trace_dev_category
) {
163 // Make sure timing is synced before tracing
164 if (!gpu_timing_synced_
) {
165 CalculateTimerOffset();
166 gpu_timing_synced_
= true;
169 // If GPU device category is off, invalidate timing sync
170 gpu_timing_synced_
= false;
177 gpu_executing_
= true;
180 // Begin a Trace for all active markers
181 for (int n
= 0; n
< NUM_TRACER_SOURCES
; n
++) {
182 for (size_t i
= 0; i
< markers_
[n
].size(); i
++) {
183 markers_
[n
][i
].trace_
= CreateTrace(markers_
[n
][i
].name_
);
184 markers_
[n
][i
].trace_
->Start();
191 bool GPUTracer::EndDecoding() {
195 // End Trace for all active markers
197 for (int n
= 0; n
< NUM_TRACER_SOURCES
; n
++) {
198 for (size_t i
= 0; i
< markers_
[n
].size(); i
++) {
199 if (markers_
[n
][i
].trace_
.get()) {
200 markers_
[n
][i
].trace_
->End();
201 if (markers_
[n
][i
].trace_
->IsEnabled())
202 traces_
.push_back(markers_
[n
][i
].trace_
);
203 markers_
[n
][i
].trace_
= 0;
210 gpu_executing_
= false;
212 // NOTE(vmiura): glFlush() here can help give better trace results,
213 // but it distorts the normal device behavior.
217 bool GPUTracer::Begin(const std::string
& name
, GpuTracerSource source
) {
221 DCHECK(source
>= 0 && source
< NUM_TRACER_SOURCES
);
223 // Push new marker from given 'source'
224 last_tracer_source_
= source
;
225 markers_
[source
].push_back(TraceMarker(name
));
229 scoped_refptr
<GPUTrace
> trace
= CreateTrace(name
);
231 markers_
[source
].back().trace_
= trace
;
237 bool GPUTracer::End(GpuTracerSource source
) {
241 DCHECK(source
>= 0 && source
< NUM_TRACER_SOURCES
);
243 // Pop last marker with matching 'source'
244 if (!markers_
[source
].empty()) {
246 scoped_refptr
<GPUTrace
> trace
= markers_
[source
].back().trace_
;
249 if (trace
->IsEnabled())
250 traces_
.push_back(trace
);
255 markers_
[source
].pop_back();
261 bool GPUTracer::IsTracing() {
262 return (*gpu_trace_srv_category
!= 0) || (*gpu_trace_dev_category
!= 0);
265 const std::string
& GPUTracer::CurrentName() const {
266 if (last_tracer_source_
>= 0 &&
267 last_tracer_source_
< NUM_TRACER_SOURCES
&&
268 !markers_
[last_tracer_source_
].empty()) {
269 return markers_
[last_tracer_source_
].back().name_
;
271 return base::EmptyString();
274 scoped_refptr
<GPUTrace
> GPUTracer::CreateTrace(const std::string
& name
) {
275 if (enabled_
&& *gpu_trace_dev_category
)
276 return new GPUTrace(outputter_
, name
, timer_offset_
);
278 return new GPUTrace(name
);
281 void GPUTracer::Process() {
282 process_posted_
= false;
287 void GPUTracer::ProcessTraces() {
289 while (!traces_
.empty() && traces_
.front()->IsAvailable()) {
290 traces_
.front()->Process();
296 TRACE_EVENT0("gpu", "GPUTracer::ProcessTraces");
298 // Make owning decoder's GL context current
299 if (!decoder_
->MakeCurrent()) {
300 // Skip subsequent GL calls if MakeCurrent fails
305 while (!traces_
.empty() && traces_
.front()->IsAvailable()) {
306 traces_
.front()->Process();
310 // Clear pending traces if there were are any errors
311 GLenum err
= glGetError();
312 if (err
!= GL_NO_ERROR
)
316 void GPUTracer::CalculateTimerOffset() {
318 TRACE_EVENT0("gpu", "GPUTracer::CalculateTimerOffset");
320 // NOTE(vmiura): It would be better to use glGetInteger64v, however
321 // it's not available everywhere.
325 glGenQueries(1, &query
);
326 glQueryCounter(query
, GL_TIMESTAMP
);
328 glGetQueryObjectui64v(query
, GL_QUERY_RESULT
, &gl_now
);
329 base::TimeTicks system_now
= base::TimeTicks::NowFromSystemTraceTime();
331 gl_now
/= base::Time::kNanosecondsPerMicrosecond
;
332 timer_offset_
= system_now
.ToInternalValue() - gl_now
;
333 glDeleteQueries(1, &query
);
337 void GPUTracer::IssueProcessTask() {
338 if (traces_
.empty() || process_posted_
)
341 process_posted_
= true;
342 base::MessageLoop::current()->PostDelayedTask(
344 base::Bind(&GPUTracer::Process
, base::AsWeakPtr(this)),
345 base::TimeDelta::FromMilliseconds(kProcessInterval
));