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 class TimeElapsedTimerQuery
;
17 int64_t NanoToMicro(uint64_t nano_seconds
) {
18 const uint64_t up
= nano_seconds
+ base::Time::kNanosecondsPerMicrosecond
/ 2;
19 return static_cast<int64_t>(up
/ base::Time::kNanosecondsPerMicrosecond
);
22 class GPUTimingImpl
: public GPUTiming
{
24 GPUTimingImpl(GLContextReal
* context
);
25 ~GPUTimingImpl() override
;
27 void ForceTimeElapsedQuery() { force_time_elapsed_query_
= true; }
28 bool IsForceTimeElapsedQuery() { return force_time_elapsed_query_
; }
30 GPUTiming::TimerType
GetTimerType() const { return timer_type_
; }
32 uint32_t GetDisjointCount();
33 int64
CalculateTimerOffset();
35 scoped_refptr
<QueryResult
> BeginElapsedTimeQuery();
36 void EndElapsedTimeQuery(scoped_refptr
<QueryResult
> result
);
38 scoped_refptr
<QueryResult
> DoTimeStampQuery();
40 int64
GetCurrentCPUTime() {
41 return cpu_time_for_testing_
.is_null()
42 ? (base::TraceTicks::Now() - base::TraceTicks()).InMicroseconds()
43 : cpu_time_for_testing_
.Run();
45 void SetCpuTimeForTesting(const base::Callback
<int64(void)>& cpu_time
) {
46 cpu_time_for_testing_
= cpu_time
;
49 void UpdateQueryResults();
51 int64_t GetMaxTimeStamp() { return max_time_stamp_
; }
52 void UpdateMaxTimeStamp(int64_t value
) {
53 max_time_stamp_
= std::max(max_time_stamp_
, value
);
56 uint32_t GetElapsedQueryCount() { return elapsed_query_count_
; }
57 void IncElapsedQueryCount() { elapsed_query_count_
++; }
58 void DecElapsedQueryCount() { elapsed_query_count_
--; }
60 void SetLastElapsedQuery(scoped_refptr
<TimeElapsedTimerQuery
> query
);
61 scoped_refptr
<TimeElapsedTimerQuery
> GetLastElapsedQuery();
63 void HandleBadQuery();
64 bool IsGoodQueryID(uint32_t query_id
);
67 scoped_refptr
<GPUTimingClient
> CreateGPUTimingClient() override
;
69 base::Callback
<int64(void)> cpu_time_for_testing_
;
70 GPUTiming::TimerType timer_type_
= GPUTiming::kTimerTypeInvalid
;
71 uint32_t disjoint_counter_
= 0;
72 int64 offset_
= 0; // offset cache when timer_type_ == kTimerTypeARB
73 bool offset_valid_
= false;
74 bool force_time_elapsed_query_
= false;
76 uint32_t next_timer_query_id_
= 0;
77 uint32_t next_good_timer_query_id_
= 0; // identify bad ids for disjoints.
78 uint32_t query_disjoint_count_
= 0;
80 // Extra state tracking data for elapsed timer queries.
81 int64_t max_time_stamp_
= 0;
82 uint32_t elapsed_query_count_
= 0;
83 scoped_refptr
<TimeElapsedTimerQuery
> last_elapsed_query_
;
85 std::deque
<scoped_refptr
<TimerQuery
> > queries_
;
87 DISALLOW_COPY_AND_ASSIGN(GPUTimingImpl
);
90 class QueryResult
: public base::RefCounted
<QueryResult
> {
94 bool IsAvailable() const { return available_
; }
95 int64_t GetDelta() const { return end_value_
- start_value_
; }
96 int64_t GetStartValue() const { return start_value_
; }
97 int64_t GetEndValue() const { return end_value_
; }
99 void SetStartValue(int64_t value
) { start_value_
= value
; }
100 void SetEndValue(int64_t value
) { available_
= true; end_value_
= value
; }
103 friend class base::RefCounted
<QueryResult
>;
106 bool available_
= false;
107 int64_t start_value_
= 0;
108 int64_t end_value_
= 0;
110 DISALLOW_COPY_AND_ASSIGN(QueryResult
);
113 class TimerQuery
: public base::RefCounted
<TimerQuery
> {
115 TimerQuery(uint32_t next_id
);
116 virtual void Destroy() = 0;
118 // Returns true when UpdateQueryResults() is ready to be called.
119 virtual bool IsAvailable(GPUTimingImpl
* gpu_timing
) = 0;
121 // Fills out query result start and end, called after IsAvailable() is true.
122 virtual void UpdateQueryResults(GPUTimingImpl
* gpu_timing
) = 0;
124 // Called when Query is next in line, used to transition states.
125 virtual void PrepareNextUpdate(scoped_refptr
<TimerQuery
> prev
) {}
127 uint32_t timer_query_id_
= 0;
128 int64_t time_stamp_
= 0; // Timestamp of the query, could be estimated.
131 friend class base::RefCounted
<TimerQuery
>;
132 virtual ~TimerQuery();
133 DISALLOW_COPY_AND_ASSIGN(TimerQuery
);
136 TimerQuery::TimerQuery(uint32_t next_id
)
137 : timer_query_id_(next_id
) {
140 TimerQuery::~TimerQuery() {
143 class TimeElapsedTimerQuery
: public TimerQuery
{
145 TimeElapsedTimerQuery(GPUTimingImpl
* gpu_timing
, uint32_t next_id
)
146 : TimerQuery(next_id
) {
147 glGenQueries(1, &gl_query_id_
);
150 void Destroy() override
{
151 glDeleteQueries(1, &gl_query_id_
);
154 scoped_refptr
<QueryResult
> StartQuery(GPUTimingImpl
* gpu_timing
) {
155 DCHECK(query_result_start_
.get() == nullptr);
156 query_begin_cpu_time_
= gpu_timing
->GetCurrentCPUTime();
157 if (gpu_timing
->GetElapsedQueryCount() == 0) {
158 first_top_level_query_
= true;
160 // Stop the current timer query.
161 glEndQuery(GL_TIME_ELAPSED
);
164 // begin a new one time elapsed query.
165 glBeginQuery(GL_TIME_ELAPSED
, gl_query_id_
);
166 query_result_start_
= new QueryResult();
168 // Update GPUTiming state.
169 gpu_timing
->SetLastElapsedQuery(this);
170 gpu_timing
->IncElapsedQueryCount();
172 return query_result_start_
;
175 void EndQuery(GPUTimingImpl
* gpu_timing
,
176 scoped_refptr
<QueryResult
> result
) {
177 DCHECK(gpu_timing
->GetElapsedQueryCount() != 0);
179 scoped_refptr
<TimeElapsedTimerQuery
> last_query
=
180 gpu_timing
->GetLastElapsedQuery();
181 DCHECK(last_query
.get());
182 DCHECK(last_query
->query_result_end_
.get() == nullptr);
184 last_query
->query_result_end_
= result
;
185 gpu_timing
->DecElapsedQueryCount();
187 if (gpu_timing
->GetElapsedQueryCount() != 0) {
188 // Continue timer if there are still ongoing queries.
189 glEndQuery(GL_TIME_ELAPSED
);
190 glBeginQuery(GL_TIME_ELAPSED
, gl_query_id_
);
191 gpu_timing
->SetLastElapsedQuery(this);
193 // Simply end the query and reset the current offset
194 glEndQuery(GL_TIME_ELAPSED
);
195 gpu_timing
->SetLastElapsedQuery(nullptr);
199 // Returns true when UpdateQueryResults() is ready to be called.
200 bool IsAvailable(GPUTimingImpl
* gpu_timing
) override
{
201 if (gpu_timing
->GetElapsedQueryCount() != 0 &&
202 gpu_timing
->GetLastElapsedQuery() == this) {
203 // Cannot query if result is available if EndQuery has not been called.
204 // Since only one query is going on at a time, the end query is only not
205 // called for the very last query when ongoing query counter is not 0.
210 glGetQueryObjectuiv(gl_query_id_
, GL_QUERY_RESULT_AVAILABLE
, &done
);
214 // Fills out query result start and end, called after IsAvailable() is true.
215 void UpdateQueryResults(GPUTimingImpl
* gpu_timing
) override
{
216 GLuint64 result_value
= 0;
217 glGetQueryObjectui64v(gl_query_id_
, GL_QUERY_RESULT
, &result_value
);
218 const int64_t micro_results
= NanoToMicro(result_value
);
220 // Adjust prev query end time if it is before the current max.
221 const int64_t start_time
=
222 std::max(first_top_level_query_
? query_begin_cpu_time_
: 0,
223 std::max(prev_query_end_time_
,
224 gpu_timing
->GetMaxTimeStamp()));
226 // As a sanity check, is result value is greater than the time allotted we
227 // can safely say this is garbage data
228 const int64_t max_possible_time
=
229 gpu_timing
->GetCurrentCPUTime() - query_begin_cpu_time_
;
230 if (micro_results
> max_possible_time
) {
231 gpu_timing
->HandleBadQuery();
234 // Elapsed queries need to be adjusted so they are relative to one another.
235 // Absolute timer queries are already relative to one another absolutely.
236 time_stamp_
= start_time
+ micro_results
;
238 if (query_result_start_
.get()) {
239 query_result_start_
->SetStartValue(start_time
);
241 if (query_result_end_
.get()) {
242 query_result_end_
->SetEndValue(time_stamp_
);
246 // Called when Query is next in line, used to transition states.
247 void PrepareNextUpdate(scoped_refptr
<TimerQuery
> prev
) override
{
248 prev_query_end_time_
= prev
->time_stamp_
;
252 ~TimeElapsedTimerQuery() override
{}
254 bool first_top_level_query_
= false;
255 uint32_t gl_query_id_
= 0;
256 int64_t prev_query_end_time_
= 0;
257 int64_t query_begin_cpu_time_
= 0;
258 scoped_refptr
<QueryResult
> query_result_start_
;
259 scoped_refptr
<QueryResult
> query_result_end_
;
262 class TimeStampTimerQuery
: public TimerQuery
{
264 TimeStampTimerQuery(uint32_t next_id
)
265 : TimerQuery(next_id
) {
266 glGenQueries(1, &gl_query_id_
);
269 void Destroy() override
{
270 glDeleteQueries(1, &gl_query_id_
);
273 scoped_refptr
<QueryResult
> DoQuery() {
274 glQueryCounter(gl_query_id_
, GL_TIMESTAMP
);
275 query_result_
= new QueryResult();
276 return query_result_
;
279 // Returns true when UpdateQueryResults() is ready to be called.
280 bool IsAvailable(GPUTimingImpl
* gpu_timing
) override
{
282 glGetQueryObjectuiv(gl_query_id_
, GL_QUERY_RESULT_AVAILABLE
, &done
);
286 // Fills out query result start and end, called after IsAvailable() is true.
287 void UpdateQueryResults(GPUTimingImpl
* gpu_timing
) override
{
288 DCHECK(IsAvailable(gpu_timing
));
290 GLuint64 result_value
= 0;
291 glGetQueryObjectui64v(gl_query_id_
, GL_QUERY_RESULT
, &result_value
);
292 const int64_t micro_results
= NanoToMicro(result_value
);
294 const int64 offset
= gpu_timing
->CalculateTimerOffset();
295 const int64_t adjusted_result
= micro_results
+ offset
;
296 DCHECK(query_result_
.get());
297 query_result_
->SetStartValue(adjusted_result
);
298 query_result_
->SetEndValue(adjusted_result
);
299 time_stamp_
= adjusted_result
;
303 ~TimeStampTimerQuery() override
{}
304 uint32_t gl_query_id_
= 0;
305 scoped_refptr
<QueryResult
> query_result_
;
308 GPUTimingImpl::GPUTimingImpl(GLContextReal
* context
) {
310 const GLVersionInfo
* version_info
= context
->GetVersionInfo();
311 DCHECK(version_info
);
312 if (version_info
->is_es3
&& // glGetInteger64v is supported under ES3.
313 context
->HasExtension("GL_EXT_disjoint_timer_query")) {
314 timer_type_
= GPUTiming::kTimerTypeDisjoint
;
315 } else if (context
->HasExtension("GL_ARB_timer_query")) {
316 timer_type_
= GPUTiming::kTimerTypeARB
;
317 } else if (context
->HasExtension("GL_EXT_timer_query")) {
318 timer_type_
= GPUTiming::kTimerTypeEXT
;
319 force_time_elapsed_query_
= true;
323 GPUTimingImpl::~GPUTimingImpl() {
326 uint32_t GPUTimingImpl::GetDisjointCount() {
327 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
) {
328 GLint disjoint_value
= 0;
329 glGetIntegerv(GL_GPU_DISJOINT_EXT
, &disjoint_value
);
330 if (disjoint_value
) {
331 offset_valid_
= false;
335 return disjoint_counter_
;
338 int64
GPUTimingImpl::CalculateTimerOffset() {
339 if (!offset_valid_
) {
340 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
||
341 timer_type_
== GPUTiming::kTimerTypeARB
) {
343 glGetInteger64v(GL_TIMESTAMP
, &gl_now
);
344 int64_t micro_now
= NanoToMicro(gl_now
);
345 offset_
= GetCurrentCPUTime() - micro_now
;
346 offset_valid_
= (timer_type_
== GPUTiming::kTimerTypeARB
);
349 offset_valid_
= true;
355 scoped_refptr
<QueryResult
> GPUTimingImpl::BeginElapsedTimeQuery() {
356 DCHECK(timer_type_
!= GPUTiming::kTimerTypeInvalid
);
358 queries_
.push_back(new TimeElapsedTimerQuery(this, next_timer_query_id_
++));
359 return static_cast<TimeElapsedTimerQuery
*>(
360 queries_
.back().get())->StartQuery(this);
363 void GPUTimingImpl::EndElapsedTimeQuery(scoped_refptr
<QueryResult
> result
) {
364 DCHECK(timer_type_
!= GPUTiming::kTimerTypeInvalid
);
365 DCHECK(result
.get());
367 if (GetElapsedQueryCount() > 1) {
368 // Create new elapsed timer query if there are still ongoing queries.
369 queries_
.push_back(new TimeElapsedTimerQuery(this,
370 next_timer_query_id_
++));
371 static_cast<TimeElapsedTimerQuery
*>(
372 queries_
.back().get())->EndQuery(this, result
);
374 // Simply end the query and reset the current offset
375 DCHECK(GetLastElapsedQuery().get());
376 GetLastElapsedQuery()->EndQuery(this, result
);
377 DCHECK(GetLastElapsedQuery().get() == nullptr);
381 scoped_refptr
<QueryResult
> GPUTimingImpl::DoTimeStampQuery() {
382 DCHECK(timer_type_
!= GPUTiming::kTimerTypeInvalid
);
384 if (force_time_elapsed_query_
) {
385 // Replace with elapsed timer queries instead.
386 scoped_refptr
<QueryResult
> result
= BeginElapsedTimeQuery();
387 EndElapsedTimeQuery(result
);
391 queries_
.push_back(new TimeStampTimerQuery(next_timer_query_id_
++));
392 return static_cast<TimeStampTimerQuery
*>(queries_
.back().get())->DoQuery();
395 void GPUTimingImpl::UpdateQueryResults() {
396 // Query availability of and count the queries that are available.
397 int available_queries
= 0;
398 for (const scoped_refptr
<TimerQuery
>& query
: queries_
) {
399 if (!query
->IsAvailable(this))
404 // Check for disjoints, this must be done after we checked for availability.
405 const uint32_t disjoint_counter
= GetDisjointCount();
406 if (disjoint_counter
!= query_disjoint_count_
) {
407 next_good_timer_query_id_
= next_timer_query_id_
;
408 query_disjoint_count_
= disjoint_counter
;
411 // Fill in the query result data once we know the disjoint value is updated.
412 // Note that even if disjoint happened and the values may or may not be
413 // garbage, we still fill it in and let GPUTimingClient's detect and disgard
414 // bad query data. The only thing we need to account for here is to not
415 // use garbade timer data to fill states such as max query times.
416 for (int i
= 0; i
< available_queries
; ++i
) {
417 scoped_refptr
<TimerQuery
> query
= queries_
.front();
419 query
->UpdateQueryResults(this);
420 DCHECK(query
->time_stamp_
) << "Query Timestamp was not updated.";
422 // For good queries, keep track of the max valid time stamps.
423 if (IsGoodQueryID(query
->timer_query_id_
))
424 UpdateMaxTimeStamp(query
->time_stamp_
);
427 queries_
.pop_front();
429 if (!queries_
.empty())
430 queries_
.front()->PrepareNextUpdate(query
);
434 void GPUTimingImpl::SetLastElapsedQuery(
435 scoped_refptr
<TimeElapsedTimerQuery
> query
) {
436 last_elapsed_query_
= query
;
439 scoped_refptr
<TimeElapsedTimerQuery
> GPUTimingImpl::GetLastElapsedQuery() {
440 return last_elapsed_query_
;
443 void GPUTimingImpl::HandleBadQuery() {
444 // Mark all queries as bad and signal an artificial disjoint value.
445 next_good_timer_query_id_
= next_timer_query_id_
;
446 offset_valid_
= false;
447 query_disjoint_count_
= ++disjoint_counter_
;
450 bool GPUTimingImpl::IsGoodQueryID(uint32_t query_id
) {
451 return query_id
>= next_good_timer_query_id_
;
454 scoped_refptr
<GPUTimingClient
> GPUTimingImpl::CreateGPUTimingClient() {
455 return new GPUTimingClient(this);
458 GPUTiming
* GPUTiming::CreateGPUTiming(GLContextReal
* context
) {
459 return new GPUTimingImpl(context
);
462 GPUTiming::GPUTiming() {
465 GPUTiming::~GPUTiming() {
468 GPUTimer::~GPUTimer() {
471 void GPUTimer::Destroy(bool have_context
) {
473 if (timer_state_
== kTimerState_WaitingForEnd
) {
474 DCHECK(gpu_timing_client_
->gpu_timing_
);
475 DCHECK(elapsed_timer_result_
.get());
476 gpu_timing_client_
->gpu_timing_
->EndElapsedTimeQuery(
477 elapsed_timer_result_
);
482 void GPUTimer::Reset() {
483 // We can reset from any state other than when a Start() is waiting for End().
484 DCHECK(timer_state_
!= kTimerState_WaitingForEnd
);
485 time_stamp_result_
= nullptr;
486 elapsed_timer_result_
= nullptr;
487 timer_state_
= kTimerState_Ready
;
490 void GPUTimer::QueryTimeStamp() {
491 DCHECK(gpu_timing_client_
->gpu_timing_
);
493 time_stamp_result_
= gpu_timing_client_
->gpu_timing_
->DoTimeStampQuery();
494 timer_state_
= kTimerState_WaitingForResult
;
497 void GPUTimer::Start() {
498 DCHECK(gpu_timing_client_
->gpu_timing_
);
500 if (!use_elapsed_timer_
)
501 time_stamp_result_
= gpu_timing_client_
->gpu_timing_
->DoTimeStampQuery();
503 elapsed_timer_result_
=
504 gpu_timing_client_
->gpu_timing_
->BeginElapsedTimeQuery();
505 timer_state_
= kTimerState_WaitingForEnd
;
508 void GPUTimer::End() {
509 DCHECK(timer_state_
== kTimerState_WaitingForEnd
);
510 DCHECK(elapsed_timer_result_
.get());
511 gpu_timing_client_
->gpu_timing_
->EndElapsedTimeQuery(elapsed_timer_result_
);
512 timer_state_
= kTimerState_WaitingForResult
;
515 bool GPUTimer::IsAvailable() {
516 if (timer_state_
== kTimerState_WaitingForResult
) {
517 // Elapsed timer are only used during start/end queries and always after
518 // the timestamp query. Otherwise only the timestamp is used.
519 scoped_refptr
<QueryResult
> result
=
520 elapsed_timer_result_
.get() ?
521 elapsed_timer_result_
:
524 DCHECK(result
.get());
525 if (result
->IsAvailable()) {
526 timer_state_
= kTimerState_ResultAvailable
;
528 gpu_timing_client_
->gpu_timing_
->UpdateQueryResults();
529 if (result
->IsAvailable())
530 timer_state_
= kTimerState_ResultAvailable
;
534 return (timer_state_
== kTimerState_ResultAvailable
);
537 void GPUTimer::GetStartEndTimestamps(int64
* start
, int64
* end
) {
538 DCHECK(start
&& end
);
539 DCHECK(elapsed_timer_result_
.get() || time_stamp_result_
.get());
540 DCHECK(IsAvailable());
541 const int64_t time_stamp
= time_stamp_result_
.get() ?
542 time_stamp_result_
->GetStartValue() :
543 elapsed_timer_result_
->GetStartValue();
544 const int64_t elapsed_time
= elapsed_timer_result_
.get() ?
545 elapsed_timer_result_
->GetDelta() :
549 *end
= time_stamp
+ elapsed_time
;
552 int64
GPUTimer::GetDeltaElapsed() {
553 DCHECK(IsAvailable());
554 if (elapsed_timer_result_
.get())
555 return elapsed_timer_result_
->GetDelta();
559 GPUTimer::GPUTimer(scoped_refptr
<GPUTimingClient
> gpu_timing_client
,
560 bool use_elapsed_timer
)
561 : use_elapsed_timer_(use_elapsed_timer
),
562 gpu_timing_client_(gpu_timing_client
) {
565 GPUTimingClient::GPUTimingClient(GPUTimingImpl
* gpu_timing
)
566 : gpu_timing_(gpu_timing
) {
568 timer_type_
= gpu_timing
->GetTimerType();
569 disjoint_counter_
= gpu_timing_
->GetDisjointCount();
573 scoped_ptr
<GPUTimer
> GPUTimingClient::CreateGPUTimer(bool prefer_elapsed_time
) {
574 prefer_elapsed_time
|= (timer_type_
== GPUTiming::kTimerTypeEXT
);
576 prefer_elapsed_time
|= gpu_timing_
->IsForceTimeElapsedQuery();
578 return make_scoped_ptr(new GPUTimer(this, prefer_elapsed_time
));
581 bool GPUTimingClient::IsAvailable() {
582 return timer_type_
!= GPUTiming::kTimerTypeInvalid
;
585 const char* GPUTimingClient::GetTimerTypeName() const {
586 switch (timer_type_
) {
587 case GPUTiming::kTimerTypeDisjoint
:
588 return "GL_EXT_disjoint_timer_query";
589 case GPUTiming::kTimerTypeARB
:
590 return "GL_ARB_timer_query";
591 case GPUTiming::kTimerTypeEXT
:
592 return "GL_EXT_timer_query";
598 bool GPUTimingClient::CheckAndResetTimerErrors() {
599 if (timer_type_
== GPUTiming::kTimerTypeDisjoint
) {
600 DCHECK(gpu_timing_
!= nullptr);
601 const uint32_t total_disjoint_count
= gpu_timing_
->GetDisjointCount();
602 const bool disjoint_triggered
= total_disjoint_count
!= disjoint_counter_
;
603 disjoint_counter_
= total_disjoint_count
;
604 return disjoint_triggered
;
609 int64
GPUTimingClient::GetCurrentCPUTime() {
611 return gpu_timing_
->GetCurrentCPUTime();
614 void GPUTimingClient::SetCpuTimeForTesting(
615 const base::Callback
<int64(void)>& cpu_time
) {
617 gpu_timing_
->SetCpuTimeForTesting(cpu_time
);
620 bool GPUTimingClient::IsForceTimeElapsedQuery() {
622 return gpu_timing_
->IsForceTimeElapsedQuery();
625 void GPUTimingClient::ForceTimeElapsedQuery() {
627 gpu_timing_
->ForceTimeElapsedQuery();
630 GPUTimingClient::~GPUTimingClient() {