Add ICU message format support
[chromium-blink-merge.git] / ui / gl / gpu_timing.cc
blobe78442e44ab2c764e8e522a06b45ec34d501d976
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 class TimeElapsedTimerQuery;
15 class TimerQuery;
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 {
23 public:
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);
66 private:
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> {
91 public:
92 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; }
102 private:
103 friend class base::RefCounted<QueryResult>;
104 ~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> {
114 public:
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.
130 protected:
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 {
144 public:
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;
159 } else {
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);
192 } else {
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.
206 return false;
209 GLuint done = 0;
210 glGetQueryObjectuiv(gl_query_id_, GL_QUERY_RESULT_AVAILABLE, &done);
211 return !!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_;
251 private:
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 {
263 public:
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 {
281 GLuint done = 0;
282 glGetQueryObjectuiv(gl_query_id_, GL_QUERY_RESULT_AVAILABLE, &done);
283 return !!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;
302 private:
303 ~TimeStampTimerQuery() override {}
304 uint32_t gl_query_id_ = 0;
305 scoped_refptr<QueryResult> query_result_;
308 GPUTimingImpl::GPUTimingImpl(GLContextReal* context) {
309 DCHECK(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;
332 disjoint_counter_++;
335 return disjoint_counter_;
338 int64 GPUTimingImpl::CalculateTimerOffset() {
339 if (!offset_valid_) {
340 if (timer_type_ == GPUTiming::kTimerTypeDisjoint ||
341 timer_type_ == GPUTiming::kTimerTypeARB) {
342 GLint64 gl_now = 0;
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);
347 } else {
348 offset_ = 0;
349 offset_valid_ = true;
352 return offset_;
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);
373 } else {
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);
388 return 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))
400 break;
401 available_queries++;
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_);
426 query->Destroy();
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) {
472 if (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_);
492 Reset();
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_);
499 Reset();
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_ :
522 time_stamp_result_;
524 DCHECK(result.get());
525 if (result->IsAvailable()) {
526 timer_state_ = kTimerState_ResultAvailable;
527 } else {
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() :
548 *start = time_stamp;
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();
556 return 0;
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) {
567 if (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);
575 if (gpu_timing_)
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";
593 default:
594 return "Unknown";
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;
606 return false;
609 int64 GPUTimingClient::GetCurrentCPUTime() {
610 DCHECK(gpu_timing_);
611 return gpu_timing_->GetCurrentCPUTime();
614 void GPUTimingClient::SetCpuTimeForTesting(
615 const base::Callback<int64(void)>& cpu_time) {
616 DCHECK(gpu_timing_);
617 gpu_timing_->SetCpuTimeForTesting(cpu_time);
620 bool GPUTimingClient::IsForceTimeElapsedQuery() {
621 DCHECK(gpu_timing_);
622 return gpu_timing_->IsForceTimeElapsedQuery();
625 void GPUTimingClient::ForceTimeElapsedQuery() {
626 DCHECK(gpu_timing_);
627 gpu_timing_->ForceTimeElapsedQuery();
630 GPUTimingClient::~GPUTimingClient() {
633 } // namespace gfx