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 glDeleteQueries(2, queries_
);
50 void GPUTimer::Start() {
51 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
52 case GPUTiming::kTimerTypeARB
:
53 case GPUTiming::kTimerTypeDisjoint
:
54 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
55 glQueryCounter(queries_
[0], GL_TIMESTAMP
);
57 case GPUTiming::kTimerTypeEXT
:
58 glBeginQuery(GL_TIME_ELAPSED_EXT
, queries_
[0]);
65 void GPUTimer::End() {
66 end_requested_
= true;
67 DCHECK(gpu_timing_client_
->gpu_timing_
);
68 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
69 case GPUTiming::kTimerTypeARB
:
70 case GPUTiming::kTimerTypeDisjoint
:
71 offset_
= gpu_timing_client_
->CalculateTimerOffset();
72 glQueryCounter(queries_
[1], GL_TIMESTAMP
);
74 case GPUTiming::kTimerTypeEXT
:
75 glEndQuery(GL_TIME_ELAPSED_EXT
);
82 bool GPUTimer::IsAvailable() {
83 if (!gpu_timing_client_
->IsAvailable() || !end_requested_
) {
87 glGetQueryObjectiv(queries_
[1] ? queries_
[1] : queries_
[0],
88 GL_QUERY_RESULT_AVAILABLE
, &done
);
92 void GPUTimer::GetStartEndTimestamps(int64
* start
, int64
* end
) {
94 DCHECK(IsAvailable());
95 DCHECK(gpu_timing_client_
->gpu_timing_
);
96 DCHECK(gpu_timing_client_
->gpu_timing_
->timer_type_
!=
97 GPUTiming::kTimerTypeEXT
);
98 GLuint64 begin_stamp
= 0;
99 GLuint64 end_stamp
= 0;
100 // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
101 // We need to detect if the end is less then the start and correct for the
103 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, &begin_stamp
);
104 glGetQueryObjectui64v(queries_
[1], GL_QUERY_RESULT
, &end_stamp
);
106 *start
= (begin_stamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
107 *end
= (end_stamp
/ base::Time::kNanosecondsPerMicrosecond
) + offset_
;
110 int64
GPUTimer::GetDeltaElapsed() {
111 DCHECK(gpu_timing_client_
->gpu_timing_
);
112 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
113 case GPUTiming::kTimerTypeARB
:
114 case GPUTiming::kTimerTypeDisjoint
: {
117 GetStartEndTimestamps(&start
, &end
);
120 case GPUTiming::kTimerTypeEXT
: {
122 glGetQueryObjectui64v(queries_
[0], GL_QUERY_RESULT
, &delta
);
123 return static_cast<int64
>(delta
/ base::Time::kNanosecondsPerMicrosecond
);
131 GPUTimer::GPUTimer(scoped_refptr
<GPUTimingClient
> gpu_timing_client
)
132 : gpu_timing_client_(gpu_timing_client
) {
133 DCHECK(gpu_timing_client_
);
134 memset(queries_
, 0, sizeof(queries_
));
136 DCHECK(gpu_timing_client_
->gpu_timing_
);
137 switch (gpu_timing_client_
->gpu_timing_
->timer_type_
) {
138 case GPUTiming::kTimerTypeARB
:
139 case GPUTiming::kTimerTypeDisjoint
:
142 case GPUTiming::kTimerTypeEXT
:
148 glGenQueries(queries
, queries_
);
151 GPUTimingClient::GPUTimingClient(GPUTiming
* gpu_timing
)
152 : gpu_timing_(gpu_timing
) {
154 timer_type_
= gpu_timing
->GetTimerType();
155 disjoint_counter_
= gpu_timing_
->GetDisjointCount();
159 scoped_ptr
<GPUTimer
> GPUTimingClient::CreateGPUTimer() {
160 return make_scoped_ptr(new GPUTimer(this));
163 bool GPUTimingClient::IsAvailable() {
164 return timer_type_
!= GPUTiming::kTimerTypeInvalid
;
167 bool GPUTimingClient::IsTimerOffsetAvailable() {
168 return timer_type_
== GPUTiming::kTimerTypeARB
||
169 timer_type_
== GPUTiming::kTimerTypeDisjoint
;
172 const char* GPUTimingClient::GetTimerTypeName() const {
173 switch (timer_type_
) {
174 case GPUTiming::kTimerTypeDisjoint
:
175 return "GL_EXT_disjoint_timer_query";
176 case GPUTiming::kTimerTypeARB
:
177 return "GL_ARB_timer_query";
178 case GPUTiming::kTimerTypeEXT
:
179 return "GL_EXT_timer_query";
185 bool GPUTimingClient::CheckAndResetTimerErrors() {
186 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
) {
187 DCHECK(gpu_timing_
!= nullptr);
188 const uint32_t total_disjoint_count
= gpu_timing_
->GetDisjointCount();
189 const bool disjoint_triggered
= total_disjoint_count
!= disjoint_counter_
;
190 disjoint_counter_
= total_disjoint_count
;
191 return disjoint_triggered
;
196 int64
GPUTimingClient::CalculateTimerOffset() {
197 DCHECK(IsTimerOffsetAvailable());
198 if (!offset_valid_
) {
200 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
202 cpu_time_for_testing_
.is_null()
203 ? base::TimeTicks::NowFromSystemTraceTime().ToInternalValue()
204 : cpu_time_for_testing_
.Run();
205 offset_
= now
- gl_now
/ base::Time::kNanosecondsPerMicrosecond
;
206 offset_valid_
= timer_type_
== GPUTiming::kTimerTypeARB
;
211 void GPUTimingClient::InvalidateTimerOffset() {
212 offset_valid_
= false;
215 void GPUTimingClient::SetCpuTimeForTesting(
216 const base::Callback
<int64(void)>& cpu_time
) {
217 cpu_time_for_testing_
= cpu_time
;
220 GPUTimingClient::~GPUTimingClient() {