Update UnusedResources lint suppressions.
[chromium-blink-merge.git] / ui / gl / gpu_timing.cc
blobc90225434d6fddbcdc42cbe8306ab2ef98186ca2
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"
12 namespace gfx {
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 {
20 public:
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);
63 private:
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> {
88 public:
89 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; }
99 private:
100 friend class base::RefCounted<QueryResult>;
101 ~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> {
111 public:
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.
127 protected:
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 {
141 public:
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;
156 } else {
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);
189 } else {
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.
203 return false;
206 GLuint done = 0;
207 glGetQueryObjectuiv(gl_query_id_, GL_QUERY_RESULT_AVAILABLE, &done);
208 return !!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_;
248 private:
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 {
260 public:
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 {
278 GLuint done = 0;
279 glGetQueryObjectuiv(gl_query_id_, GL_QUERY_RESULT_AVAILABLE, &done);
280 return !!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;
299 private:
300 ~TimeStampTimerQuery() override {}
301 uint32_t gl_query_id_ = 0;
302 scoped_refptr<QueryResult> query_result_;
305 GPUTimingImpl::GPUTimingImpl(GLContextReal* context) {
306 DCHECK(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;
328 disjoint_counter_++;
331 return disjoint_counter_;
334 int64 GPUTimingImpl::CalculateTimerOffset() {
335 if (!offset_valid_) {
336 if (timer_type_ == GPUTiming::kTimerTypeDisjoint ||
337 timer_type_ == GPUTiming::kTimerTypeARB) {
338 GLint64 gl_now = 0;
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);
343 } else {
344 offset_ = 0;
345 offset_valid_ = true;
348 return offset_;
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);
369 } else {
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);
385 return 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))
397 break;
398 available_queries++;
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_);
423 query->Destroy();
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) {
469 if (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() {
495 if (!end_requested_)
496 return false;
497 if (!end_available_) {
498 DCHECK(elapsed_timer_result_.get());
499 if (elapsed_timer_result_->IsAvailable()) {
500 end_available_ = true;
501 } else {
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();
516 *start = time_stamp;
517 *end = time_stamp + elapsed_timer_result_->GetDelta();
518 } else {
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) {
539 if (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);
547 if (gpu_timing_)
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";
565 default:
566 return "Unknown";
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;
578 return false;
581 int64 GPUTimingClient::GetCurrentCPUTime() {
582 DCHECK(gpu_timing_);
583 return gpu_timing_->GetCurrentCPUTime();
586 void GPUTimingClient::SetCpuTimeForTesting(
587 const base::Callback<int64(void)>& cpu_time) {
588 DCHECK(gpu_timing_);
589 gpu_timing_->SetCpuTimeForTesting(cpu_time);
592 bool GPUTimingClient::IsForceTimeElapsedQuery() {
593 DCHECK(gpu_timing_);
594 return gpu_timing_->IsForceTimeElapsedQuery();
597 void GPUTimingClient::ForceTimeElapsedQuery() {
598 DCHECK(gpu_timing_);
599 gpu_timing_->ForceTimeElapsedQuery();
602 GPUTimingClient::~GPUTimingClient() {
605 } // namespace gfx