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 int64_t NanoToMicro(uint64_t nano_seconds
) {
15 const uint64_t up
= nano_seconds
+ base::Time::kNanosecondsPerMicrosecond
/ 2;
16 return static_cast<int64_t>(up
/ base::Time::kNanosecondsPerMicrosecond
);
19 class GPUTimingImpl
: public GPUTiming
{
21 GPUTimingImpl(GLContextReal
* context
);
22 ~GPUTimingImpl() override
;
24 void ForceTimeElapsedQuery() { force_time_elapsed_query_
= true; }
25 bool IsForceTimeElapsedQuery() { return force_time_elapsed_query_
; }
27 GPUTiming::TimerType
GetTimerType() const { return timer_type_
; }
29 uint32_t GetDisjointCount();
30 int64
CalculateTimerOffset();
32 scoped_refptr
<QueryResult
> BeginElapsedTimeQuery();
33 void EndElapsedTimeQuery(scoped_refptr
<QueryResult
> result
);
35 scoped_refptr
<QueryResult
> DoTimeStampQuery();
37 int64
GetCurrentCPUTime() {
38 return cpu_time_for_testing_
.is_null()
39 ? (base::TraceTicks::Now() - base::TraceTicks()).InMicroseconds()
40 : cpu_time_for_testing_
.Run();
42 void SetCpuTimeForTesting(const base::Callback
<int64(void)>& cpu_time
) {
43 cpu_time_for_testing_
= cpu_time
;
46 void UpdateQueryResults();
48 int64_t GetMaxTimeStamp() { return max_time_stamp_
; }
49 void UpdateMaxTimeStamp(int64_t value
) {
50 max_time_stamp_
= std::max(max_time_stamp_
, value
);
53 uint32_t GetElapsedQueryCount() { return elapsed_query_count_
; }
54 void IncElapsedQueryCount() { elapsed_query_count_
++; }
55 void DecElapsedQueryCount() { elapsed_query_count_
--; }
57 void SetLastElapsedQuery(scoped_refptr
<TimeElapsedTimerQuery
> query
);
58 scoped_refptr
<TimeElapsedTimerQuery
> GetLastElapsedQuery();
60 void HandleBadQuery();
61 bool IsGoodQueryID(uint32_t query_id
);
64 scoped_refptr
<GPUTimingClient
> CreateGPUTimingClient() override
;
66 base::Callback
<int64(void)> cpu_time_for_testing_
;
67 GPUTiming::TimerType timer_type_
= GPUTiming::kTimerTypeInvalid
;
68 uint32_t disjoint_counter_
= 0;
69 int64 offset_
= 0; // offset cache when timer_type_ == kTimerTypeARB
70 bool offset_valid_
= false;
71 bool force_time_elapsed_query_
= false;
73 uint32_t next_timer_query_id_
= 0;
74 uint32_t next_good_timer_query_id_
= 0; // identify bad ids for disjoints.
75 uint32_t query_disjoint_count_
= 0;
77 // Extra state tracking data for elapsed timer queries.
78 int64_t max_time_stamp_
= 0;
79 uint32_t elapsed_query_count_
= 0;
80 scoped_refptr
<TimeElapsedTimerQuery
> last_elapsed_query_
;
82 std::deque
<scoped_refptr
<TimerQuery
> > queries_
;
84 DISALLOW_COPY_AND_ASSIGN(GPUTimingImpl
);
87 class QueryResult
: public base::RefCounted
<QueryResult
> {
91 bool IsAvailable() const { return available_
; }
92 int64_t GetDelta() const { return end_value_
- start_value_
; }
93 int64_t GetStartValue() const { return start_value_
; }
94 int64_t GetEndValue() const { return end_value_
; }
96 void SetStartValue(int64_t value
) { start_value_
= value
; }
97 void SetEndValue(int64_t value
) { available_
= true; end_value_
= value
; }
100 friend class base::RefCounted
<QueryResult
>;
103 bool available_
= false;
104 int64_t start_value_
= 0;
105 int64_t end_value_
= 0;
107 DISALLOW_COPY_AND_ASSIGN(QueryResult
);
110 class TimerQuery
: public base::RefCounted
<TimerQuery
> {
112 TimerQuery(uint32_t next_id
);
113 virtual void Destroy() = 0;
115 // Returns true when UpdateQueryResults() is ready to be called.
116 virtual bool IsAvailable(GPUTimingImpl
* gpu_timing
) = 0;
118 // Fills out query result start and end, called after IsAvailable() is true.
119 virtual void UpdateQueryResults(GPUTimingImpl
* gpu_timing
) = 0;
121 // Called when Query is next in line, used to transition states.
122 virtual void PrepareNextUpdate(scoped_refptr
<TimerQuery
> prev
) {}
124 uint32_t timer_query_id_
= 0;
125 int64_t time_stamp_
= 0; // Timestamp of the query, could be estimated.
128 friend class base::RefCounted
<TimerQuery
>;
129 virtual ~TimerQuery();
130 DISALLOW_COPY_AND_ASSIGN(TimerQuery
);
133 TimerQuery::TimerQuery(uint32_t next_id
)
134 : timer_query_id_(next_id
) {
137 TimerQuery::~TimerQuery() {
140 class TimeElapsedTimerQuery
: public TimerQuery
{
142 TimeElapsedTimerQuery(GPUTimingImpl
* gpu_timing
, uint32_t next_id
)
143 : TimerQuery(next_id
) {
144 glGenQueries(1, &gl_query_id_
);
147 void Destroy() override
{
148 glDeleteQueries(1, &gl_query_id_
);
151 scoped_refptr
<QueryResult
> StartQuery(GPUTimingImpl
* gpu_timing
) {
152 DCHECK(query_result_start_
.get() == nullptr);
153 query_begin_cpu_time_
= gpu_timing
->GetCurrentCPUTime();
154 if (gpu_timing
->GetElapsedQueryCount() == 0) {
155 first_top_level_query_
= true;
157 // Stop the current timer query.
158 glEndQuery(GL_TIME_ELAPSED
);
161 // begin a new one time elapsed query.
162 glBeginQuery(GL_TIME_ELAPSED
, gl_query_id_
);
163 query_result_start_
= new QueryResult();
165 // Update GPUTiming state.
166 gpu_timing
->SetLastElapsedQuery(this);
167 gpu_timing
->IncElapsedQueryCount();
169 return query_result_start_
;
172 void EndQuery(GPUTimingImpl
* gpu_timing
,
173 scoped_refptr
<QueryResult
> result
) {
174 DCHECK(gpu_timing
->GetElapsedQueryCount() != 0);
176 scoped_refptr
<TimeElapsedTimerQuery
> last_query
=
177 gpu_timing
->GetLastElapsedQuery();
178 DCHECK(last_query
.get());
179 DCHECK(last_query
->query_result_end_
.get() == nullptr);
181 last_query
->query_result_end_
= result
;
182 gpu_timing
->DecElapsedQueryCount();
184 if (gpu_timing
->GetElapsedQueryCount() != 0) {
185 // Continue timer if there are still ongoing queries.
186 glEndQuery(GL_TIME_ELAPSED
);
187 glBeginQuery(GL_TIME_ELAPSED
, gl_query_id_
);
188 gpu_timing
->SetLastElapsedQuery(this);
190 // Simply end the query and reset the current offset
191 glEndQuery(GL_TIME_ELAPSED
);
192 gpu_timing
->SetLastElapsedQuery(nullptr);
196 // Returns true when UpdateQueryResults() is ready to be called.
197 bool IsAvailable(GPUTimingImpl
* gpu_timing
) override
{
198 if (gpu_timing
->GetElapsedQueryCount() != 0 &&
199 gpu_timing
->GetLastElapsedQuery() == this) {
200 // Cannot query if result is available if EndQuery has not been called.
201 // Since only one query is going on at a time, the end query is only not
202 // called for the very last query when ongoing query counter is not 0.
207 glGetQueryObjectuiv(gl_query_id_
, GL_QUERY_RESULT_AVAILABLE
, &done
);
211 // Fills out query result start and end, called after IsAvailable() is true.
212 void UpdateQueryResults(GPUTimingImpl
* gpu_timing
) override
{
213 GLuint64 result_value
= 0;
214 glGetQueryObjectui64v(gl_query_id_
, GL_QUERY_RESULT
, &result_value
);
215 const int64_t micro_results
= NanoToMicro(result_value
);
217 // Adjust prev query end time if it is before the current max.
218 const int64_t start_time
=
219 std::max(first_top_level_query_
? query_begin_cpu_time_
: 0,
220 std::max(prev_query_end_time_
,
221 gpu_timing
->GetMaxTimeStamp()));
223 // As a sanity check, is result value is greater than the time allotted we
224 // can safely say this is garbage data
225 const int64_t max_possible_time
=
226 gpu_timing
->GetCurrentCPUTime() - query_begin_cpu_time_
;
227 if (micro_results
> max_possible_time
) {
228 gpu_timing
->HandleBadQuery();
231 // Elapsed queries need to be adjusted so they are relative to one another.
232 // Absolute timer queries are already relative to one another absolutely.
233 time_stamp_
= start_time
+ micro_results
;
235 if (query_result_start_
.get()) {
236 query_result_start_
->SetStartValue(start_time
);
238 if (query_result_end_
.get()) {
239 query_result_end_
->SetEndValue(time_stamp_
);
243 // Called when Query is next in line, used to transition states.
244 void PrepareNextUpdate(scoped_refptr
<TimerQuery
> prev
) override
{
245 prev_query_end_time_
= prev
->time_stamp_
;
249 ~TimeElapsedTimerQuery() override
{}
251 bool first_top_level_query_
= false;
252 uint32_t gl_query_id_
= 0;
253 int64_t prev_query_end_time_
= 0;
254 int64_t query_begin_cpu_time_
= 0;
255 scoped_refptr
<QueryResult
> query_result_start_
;
256 scoped_refptr
<QueryResult
> query_result_end_
;
259 class TimeStampTimerQuery
: public TimerQuery
{
261 TimeStampTimerQuery(uint32_t next_id
)
262 : TimerQuery(next_id
) {
263 glGenQueries(1, &gl_query_id_
);
266 void Destroy() override
{
267 glDeleteQueries(1, &gl_query_id_
);
270 scoped_refptr
<QueryResult
> DoQuery() {
271 glQueryCounter(gl_query_id_
, GL_TIMESTAMP
);
272 query_result_
= new QueryResult();
273 return query_result_
;
276 // Returns true when UpdateQueryResults() is ready to be called.
277 bool IsAvailable(GPUTimingImpl
* gpu_timing
) override
{
279 glGetQueryObjectuiv(gl_query_id_
, GL_QUERY_RESULT_AVAILABLE
, &done
);
283 // Fills out query result start and end, called after IsAvailable() is true.
284 void UpdateQueryResults(GPUTimingImpl
* gpu_timing
) override
{
285 DCHECK(IsAvailable(gpu_timing
));
287 GLuint64 result_value
= 0;
288 glGetQueryObjectui64v(gl_query_id_
, GL_QUERY_RESULT
, &result_value
);
289 const int64_t micro_results
= NanoToMicro(result_value
);
291 const int64 offset
= gpu_timing
->CalculateTimerOffset();
292 const int64_t adjusted_result
= micro_results
+ offset
;
293 DCHECK(query_result_
.get());
294 query_result_
->SetStartValue(adjusted_result
);
295 query_result_
->SetEndValue(adjusted_result
);
296 time_stamp_
= adjusted_result
;
300 ~TimeStampTimerQuery() override
{}
301 uint32_t gl_query_id_
= 0;
302 scoped_refptr
<QueryResult
> query_result_
;
305 GPUTimingImpl::GPUTimingImpl(GLContextReal
* context
) {
307 const GLVersionInfo
* version_info
= context
->GetVersionInfo();
308 DCHECK(version_info
);
309 if (version_info
->is_es3
&& // glGetInteger64v is supported under ES3.
310 context
->HasExtension("GL_EXT_disjoint_timer_query")) {
311 timer_type_
= GPUTiming::kTimerTypeDisjoint
;
312 } else if (context
->HasExtension("GL_ARB_timer_query")) {
313 timer_type_
= GPUTiming::kTimerTypeARB
;
314 } else if (context
->HasExtension("GL_EXT_timer_query")) {
315 timer_type_
= GPUTiming::kTimerTypeEXT
;
319 GPUTimingImpl::~GPUTimingImpl() {
322 uint32_t GPUTimingImpl::GetDisjointCount() {
323 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
) {
324 GLint disjoint_value
= 0;
325 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
326 if (disjoint_value
) {
327 offset_valid_
= false;
331 return disjoint_counter_
;
334 int64
GPUTimingImpl::CalculateTimerOffset() {
335 if (!offset_valid_
) {
336 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
||
337 timer_type_
== GPUTiming::kTimerTypeARB
) {
339 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
340 int64_t micro_now
= NanoToMicro(gl_now
);
341 offset_
= GetCurrentCPUTime() - micro_now
;
342 offset_valid_
= (timer_type_
== GPUTiming::kTimerTypeARB
);
345 offset_valid_
= true;
351 scoped_refptr
<QueryResult
> GPUTimingImpl::BeginElapsedTimeQuery() {
352 DCHECK(timer_type_
!= GPUTiming::kTimerTypeInvalid
);
354 queries_
.push_back(new TimeElapsedTimerQuery(this, next_timer_query_id_
++));
355 return static_cast<TimeElapsedTimerQuery
*>(
356 queries_
.back().get())->StartQuery(this);
359 void GPUTimingImpl::EndElapsedTimeQuery(scoped_refptr
<QueryResult
> result
) {
360 DCHECK(timer_type_
!= GPUTiming::kTimerTypeInvalid
);
361 DCHECK(result
.get());
363 if (GetElapsedQueryCount() > 1) {
364 // Create new elapsed timer query if there are still ongoing queries.
365 queries_
.push_back(new TimeElapsedTimerQuery(this,
366 next_timer_query_id_
++));
367 static_cast<TimeElapsedTimerQuery
*>(
368 queries_
.back().get())->EndQuery(this, result
);
370 // Simply end the query and reset the current offset
371 DCHECK(GetLastElapsedQuery().get());
372 GetLastElapsedQuery()->EndQuery(this, result
);
373 DCHECK(GetLastElapsedQuery().get() == nullptr);
377 scoped_refptr
<QueryResult
> GPUTimingImpl::DoTimeStampQuery() {
378 DCHECK(timer_type_
== GPUTiming::kTimerTypeDisjoint
||
379 timer_type_
== GPUTiming::kTimerTypeARB
);
381 if (force_time_elapsed_query_
) {
382 // Replace with elapsed timer queries instead.
383 scoped_refptr
<QueryResult
> result
= BeginElapsedTimeQuery();
384 EndElapsedTimeQuery(result
);
388 queries_
.push_back(new TimeStampTimerQuery(next_timer_query_id_
++));
389 return static_cast<TimeStampTimerQuery
*>(queries_
.back().get())->DoQuery();
392 void GPUTimingImpl::UpdateQueryResults() {
393 // Query availability of and count the queries that are available.
394 int available_queries
= 0;
395 for (const scoped_refptr
<TimerQuery
>& query
: queries_
) {
396 if (!query
->IsAvailable(this))
401 // Check for disjoints, this must be done after we checked for availability.
402 const uint32_t disjoint_counter
= GetDisjointCount();
403 if (disjoint_counter
!= query_disjoint_count_
) {
404 next_good_timer_query_id_
= next_timer_query_id_
;
405 query_disjoint_count_
= disjoint_counter
;
408 // Fill in the query result data once we know the disjoint value is updated.
409 // Note that even if disjoint happened and the values may or may not be
410 // garbage, we still fill it in and let GPUTimingClient's detect and disgard
411 // bad query data. The only thing we need to account for here is to not
412 // use garbade timer data to fill states such as max query times.
413 for (int i
= 0; i
< available_queries
; ++i
) {
414 scoped_refptr
<TimerQuery
> query
= queries_
.front();
416 query
->UpdateQueryResults(this);
417 DCHECK(query
->time_stamp_
) << "Query Timestamp was not updated.";
419 // For good queries, keep track of the max valid time stamps.
420 if (IsGoodQueryID(query
->timer_query_id_
))
421 UpdateMaxTimeStamp(query
->time_stamp_
);
424 queries_
.pop_front();
426 if (!queries_
.empty())
427 queries_
.front()->PrepareNextUpdate(query
);
431 void GPUTimingImpl::SetLastElapsedQuery(
432 scoped_refptr
<TimeElapsedTimerQuery
> query
) {
433 last_elapsed_query_
= query
;
436 scoped_refptr
<TimeElapsedTimerQuery
> GPUTimingImpl::GetLastElapsedQuery() {
437 return last_elapsed_query_
;
440 void GPUTimingImpl::HandleBadQuery() {
441 // Mark all queries as bad and signal an artificial disjoint value.
442 next_good_timer_query_id_
= next_timer_query_id_
;
443 offset_valid_
= false;
444 query_disjoint_count_
= ++disjoint_counter_
;
447 bool GPUTimingImpl::IsGoodQueryID(uint32_t query_id
) {
448 return query_id
>= next_good_timer_query_id_
;
451 scoped_refptr
<GPUTimingClient
> GPUTimingImpl::CreateGPUTimingClient() {
452 return new GPUTimingClient(this);
455 GPUTiming
* GPUTiming::CreateGPUTiming(GLContextReal
* context
) {
456 return new GPUTimingImpl(context
);
459 GPUTiming::GPUTiming() {
462 GPUTiming::~GPUTiming() {
465 GPUTimer::~GPUTimer() {
468 void GPUTimer::Destroy(bool have_context
) {
470 if (!end_requested_
) {
471 DCHECK(gpu_timing_client_
->gpu_timing_
);
472 DCHECK(elapsed_timer_result_
.get());
473 gpu_timing_client_
->gpu_timing_
->EndElapsedTimeQuery(
474 elapsed_timer_result_
);
479 void GPUTimer::Start() {
480 DCHECK(gpu_timing_client_
->gpu_timing_
);
481 if (!use_elapsed_timer_
)
482 time_stamp_result_
= gpu_timing_client_
->gpu_timing_
->DoTimeStampQuery();
484 elapsed_timer_result_
=
485 gpu_timing_client_
->gpu_timing_
->BeginElapsedTimeQuery();
488 void GPUTimer::End() {
489 DCHECK(elapsed_timer_result_
.get());
490 end_requested_
= true;
491 gpu_timing_client_
->gpu_timing_
->EndElapsedTimeQuery(elapsed_timer_result_
);
494 bool GPUTimer::IsAvailable() {
497 if (!end_available_
) {
498 DCHECK(elapsed_timer_result_
.get());
499 if (elapsed_timer_result_
->IsAvailable()) {
500 end_available_
= true;
502 gpu_timing_client_
->gpu_timing_
->UpdateQueryResults();
503 end_available_
= elapsed_timer_result_
->IsAvailable();
506 return end_available_
;
509 void GPUTimer::GetStartEndTimestamps(int64
* start
, int64
* end
) {
510 DCHECK(start
&& end
);
511 DCHECK(elapsed_timer_result_
.get());
512 DCHECK(IsAvailable());
513 if (time_stamp_result_
.get()) {
514 DCHECK(time_stamp_result_
->IsAvailable());
515 const int64_t time_stamp
= time_stamp_result_
->GetStartValue();
517 *end
= time_stamp
+ elapsed_timer_result_
->GetDelta();
519 // Use estimation from elasped timer results.
520 *start
= elapsed_timer_result_
->GetStartValue();
521 *end
= elapsed_timer_result_
->GetEndValue();
525 int64
GPUTimer::GetDeltaElapsed() {
526 DCHECK(elapsed_timer_result_
.get());
527 DCHECK(IsAvailable());
528 return elapsed_timer_result_
->GetDelta();
531 GPUTimer::GPUTimer(scoped_refptr
<GPUTimingClient
> gpu_timing_client
,
532 bool use_elapsed_timer
)
533 : use_elapsed_timer_(use_elapsed_timer
),
534 gpu_timing_client_(gpu_timing_client
) {
537 GPUTimingClient::GPUTimingClient(GPUTimingImpl
* gpu_timing
)
538 : gpu_timing_(gpu_timing
) {
540 timer_type_
= gpu_timing
->GetTimerType();
541 disjoint_counter_
= gpu_timing_
->GetDisjointCount();
545 scoped_ptr
<GPUTimer
> GPUTimingClient::CreateGPUTimer(bool prefer_elapsed_time
) {
546 prefer_elapsed_time
|= (timer_type_
== GPUTiming::kTimerTypeEXT
);
548 prefer_elapsed_time
|= gpu_timing_
->IsForceTimeElapsedQuery();
550 return make_scoped_ptr(new GPUTimer(this, prefer_elapsed_time
));
553 bool GPUTimingClient::IsAvailable() {
554 return timer_type_
!= GPUTiming::kTimerTypeInvalid
;
557 const char* GPUTimingClient::GetTimerTypeName() const {
558 switch (timer_type_
) {
559 case GPUTiming::kTimerTypeDisjoint
:
560 return "GL_EXT_disjoint_timer_query";
561 case GPUTiming::kTimerTypeARB
:
562 return "GL_ARB_timer_query";
563 case GPUTiming::kTimerTypeEXT
:
564 return "GL_EXT_timer_query";
570 bool GPUTimingClient::CheckAndResetTimerErrors() {
571 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
) {
572 DCHECK(gpu_timing_
!= nullptr);
573 const uint32_t total_disjoint_count
= gpu_timing_
->GetDisjointCount();
574 const bool disjoint_triggered
= total_disjoint_count
!= disjoint_counter_
;
575 disjoint_counter_
= total_disjoint_count
;
576 return disjoint_triggered
;
581 int64
GPUTimingClient::GetCurrentCPUTime() {
583 return gpu_timing_
->GetCurrentCPUTime();
586 void GPUTimingClient::SetCpuTimeForTesting(
587 const base::Callback
<int64(void)>& cpu_time
) {
589 gpu_timing_
->SetCpuTimeForTesting(cpu_time
);
592 bool GPUTimingClient::IsForceTimeElapsedQuery() {
594 return gpu_timing_
->IsForceTimeElapsedQuery();
597 void GPUTimingClient::ForceTimeElapsedQuery() {
599 gpu_timing_
->ForceTimeElapsedQuery();
602 GPUTimingClient::~GPUTimingClient() {