1 // Copyright (c) 2015 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 "ui/gl/gpu_timing.h"
7 #include "base/time/time.h"
8 #include "ui/gl/gl_bindings.h"
9 #include "ui/gl/gl_context.h"
10 #include "ui/gl/gl_version_info.h"
14 GPUTiming::GPUTiming(GLContextReal
* context
) {
16 const GLVersionInfo
* version_info
= context
->GetVersionInfo();
18 if (version_info
->is_es3
&& // glGetInteger64v is supported under ES3.
19 context
->HasExtension("GL_EXT_disjoint_timer_query")) {
20 timer_type_
= kTimerTypeDisjoint
;
21 } else if (context
->HasExtension("GL_ARB_timer_query")) {
22 timer_type_
= kTimerTypeARB
;
23 } else if (context
->HasExtension("GL_EXT_timer_query")) {
24 timer_type_
= kTimerTypeEXT
;
28 GPUTiming::~GPUTiming() {
31 scoped_refptr
<GPUTimingClient
> GPUTiming::CreateGPUTimingClient() {
32 return new GPUTimingClient(this);
35 uint32_t GPUTiming::GetDisjointCount() {
36 if (timer_type_
== kTimerTypeDisjoint
) {
37 GLint disjoint_value
= 0;
38 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
43 return disjoint_counter_
;
46 GPUTimer::~GPUTimer() {
47 // Destroy() must be called before the destructor.
48 DCHECK(queries_
[0] == 0);
49 DCHECK(queries_
[1] == 0);
52 void GPUTimer::Destroy(bool have_context
) {
54 glDeleteQueries(2, queries_
);
56 memset(queries_
, 0, sizeof(queries_
));
59 void GPUTimer::Start() {
60 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
61 case GPUTiming::kTimerTypeARB
:
62 case GPUTiming::kTimerTypeDisjoint
:
63 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
64 glQueryCounter(queries_
[0], GL_TIMESTAMP
);
66 case GPUTiming::kTimerTypeEXT
:
67 glBeginQuery(GL_TIME_ELAPSED_EXT
, queries_
[0]);
74 void GPUTimer::End() {
75 end_requested_
= true;
76 DCHECK(gpu_timing_client_
->gpu_timing_
);
77 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
78 case GPUTiming::kTimerTypeARB
:
79 case GPUTiming::kTimerTypeDisjoint
:
80 offset_
= gpu_timing_client_
->CalculateTimerOffset();
81 glQueryCounter(queries_
[1], GL_TIMESTAMP
);
83 case GPUTiming::kTimerTypeEXT
:
84 glEndQuery(GL_TIME_ELAPSED_EXT
);
91 bool GPUTimer::IsAvailable() {
92 if (!gpu_timing_client_
->IsAvailable() || !end_requested_
) {
96 glGetQueryObjectiv(queries_
[1] ? queries_
[1] : queries_
[0],
97 GL_QUERY_RESULT_AVAILABLE
, &done
);
101 void GPUTimer::GetStartEndTimestamps(int64
* start
, int64
* end
) {
102 DCHECK(start
&& end
);
103 DCHECK(IsAvailable());
104 DCHECK(gpu_timing_client_
->gpu_timing_
);
105 DCHECK(gpu_timing_client_
->gpu_timing_
->timer_type_
!=
106 GPUTiming::kTimerTypeEXT
);
107 GLuint64 begin_stamp
= 0;
108 GLuint64 end_stamp
= 0;
109 // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
110 // We need to detect if the end is less then the start and correct for the
112 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, &begin_stamp
);
113 glGetQueryObjectui64v(queries_
[1], GL_QUERY_RESULT
, &end_stamp
);
115 *start
= (begin_stamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
116 *end
= (end_stamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
119 int64
GPUTimer::GetDeltaElapsed() {
120 DCHECK(gpu_timing_client_
->gpu_timing_
);
121 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
122 case GPUTiming::kTimerTypeARB
:
123 case GPUTiming::kTimerTypeDisjoint
: {
126 GetStartEndTimestamps(&start
, &end
);
129 case GPUTiming::kTimerTypeEXT
: {
131 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, &delta
);
132 return static_cast<int64
>(delta
/ base::Time::kNanosecondsPerMicrosecond
);
140 GPUTimer::GPUTimer(scoped_refptr
<GPUTimingClient
> gpu_timing_client
)
141 : gpu_timing_client_(gpu_timing_client
) {
142 DCHECK(gpu_timing_client_
);
143 memset(queries_
, 0, sizeof(queries_
));
145 DCHECK(gpu_timing_client_
->gpu_timing_
);
146 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
147 case GPUTiming::kTimerTypeARB
:
148 case GPUTiming::kTimerTypeDisjoint
:
151 case GPUTiming::kTimerTypeEXT
:
157 glGenQueries(queries
, queries_
);
160 GPUTimingClient::GPUTimingClient(GPUTiming
* gpu_timing
)
161 : gpu_timing_(gpu_timing
) {
163 timer_type_
= gpu_timing
->GetTimerType();
164 disjoint_counter_
= gpu_timing_
->GetDisjointCount();
168 scoped_ptr
<GPUTimer
> GPUTimingClient::CreateGPUTimer() {
169 return make_scoped_ptr(new GPUTimer(this));
172 bool GPUTimingClient::IsAvailable() {
173 return timer_type_
!= GPUTiming::kTimerTypeInvalid
;
176 bool GPUTimingClient::IsTimerOffsetAvailable() {
177 return timer_type_
== GPUTiming::kTimerTypeARB
||
178 timer_type_
== GPUTiming::kTimerTypeDisjoint
;
181 const char* GPUTimingClient::GetTimerTypeName() const {
182 switch (timer_type_
) {
183 case GPUTiming::kTimerTypeDisjoint
:
184 return "GL_EXT_disjoint_timer_query";
185 case GPUTiming::kTimerTypeARB
:
186 return "GL_ARB_timer_query";
187 case GPUTiming::kTimerTypeEXT
:
188 return "GL_EXT_timer_query";
194 bool GPUTimingClient::CheckAndResetTimerErrors() {
195 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
) {
196 DCHECK(gpu_timing_
!= nullptr);
197 const uint32_t total_disjoint_count
= gpu_timing_
->GetDisjointCount();
198 const bool disjoint_triggered
= total_disjoint_count
!= disjoint_counter_
;
199 disjoint_counter_
= total_disjoint_count
;
200 return disjoint_triggered
;
205 int64
GPUTimingClient::CalculateTimerOffset() {
206 DCHECK(IsTimerOffsetAvailable());
207 if (!offset_valid_
) {
209 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
211 cpu_time_for_testing_
.is_null()
212 ? base::TimeTicks::NowFromSystemTraceTime().ToInternalValue()
213 : cpu_time_for_testing_
.Run();
214 offset_
= now
- gl_now
/ base::Time::kNanosecondsPerMicrosecond
;
215 offset_valid_
= timer_type_
== GPUTiming::kTimerTypeARB
;
220 void GPUTimingClient::InvalidateTimerOffset() {
221 offset_valid_
= false;
224 void GPUTimingClient::SetCpuTimeForTesting(
225 const base::Callback
<int64(void)>& cpu_time
) {
226 cpu_time_for_testing_
= cpu_time
;
229 GPUTimingClient::~GPUTimingClient() {