blob: e227717678ffb6f1fa4e9a5209c6450a1be52b3b [file] [log] [blame]
Etienne Membrives386015a2015-02-19 17:27:12 +01001// 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.
4
Benjamin Lerman507bb1a2015-02-26 13:15:17 +01005#include "ui/gl/gpu_timing.h"
Etienne Membrives386015a2015-02-19 17:27:12 +01006
7#include "base/time/time.h"
Benjamin Lermane8ca9b72015-02-24 16:42:13 +01008#include "ui/gl/gl_bindings.h"
Etienne Membrives386015a2015-02-19 17:27:12 +01009#include "ui/gl/gl_context.h"
10#include "ui/gl/gl_version_info.h"
11
Benjamin Lerman507bb1a2015-02-26 13:15:17 +010012namespace gfx {
Etienne Membrives386015a2015-02-19 17:27:12 +010013
Benjamin Lerman507bb1a2015-02-26 13:15:17 +010014GPUTiming::GPUTiming(GLContextReal* context) {
15 DCHECK(context);
16 const GLVersionInfo* version_info = context->GetVersionInfo();
17 DCHECK(version_info);
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;
Nick Bray0bcbd3b2015-03-12 16:29:36 -070023 } else if (context->HasExtension("GL_EXT_timer_query")) {
24 timer_type_ = kTimerTypeEXT;
Benjamin Lerman507bb1a2015-02-26 13:15:17 +010025 }
26}
27
28GPUTiming::~GPUTiming() {
29}
30
31scoped_refptr<GPUTimingClient> GPUTiming::CreateGPUTimingClient() {
32 return new GPUTimingClient(this);
Etienne Membrives386015a2015-02-19 17:27:12 +010033}
34
Dave Moorecc0e4f92015-03-10 15:23:04 -070035uint32_t GPUTiming::GetDisjointCount() {
36 if (timer_type_ == kTimerTypeDisjoint) {
37 GLint disjoint_value = 0;
38 glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value);
39 if (disjoint_value) {
40 disjoint_counter_++;
41 }
42 }
43 return disjoint_counter_;
44}
45
Etienne Membrives386015a2015-02-19 17:27:12 +010046GPUTimer::~GPUTimer() {
James Robinsonaa0657c2015-05-11 15:28:26 -070047 // Destroy() must be called before the destructor.
48 DCHECK(queries_[0] == 0);
49 DCHECK(queries_[1] == 0);
50}
51
52void GPUTimer::Destroy(bool have_context) {
53 if (have_context) {
54 glDeleteQueries(2, queries_);
55 }
56 memset(queries_, 0, sizeof(queries_));
Etienne Membrives386015a2015-02-19 17:27:12 +010057}
58
59void GPUTimer::Start() {
Nick Bray0bcbd3b2015-03-12 16:29:36 -070060 switch (gpu_timing_client_->gpu_timing_->timer_type_) {
61 case GPUTiming::kTimerTypeARB:
62 case GPUTiming::kTimerTypeDisjoint:
63 // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
64 glQueryCounter(queries_[0], GL_TIMESTAMP);
65 break;
66 case GPUTiming::kTimerTypeEXT:
67 glBeginQuery(GL_TIME_ELAPSED_EXT, queries_[0]);
68 break;
69 default:
70 NOTREACHED();
71 }
Etienne Membrives386015a2015-02-19 17:27:12 +010072}
73
74void GPUTimer::End() {
75 end_requested_ = true;
Nick Bray0bcbd3b2015-03-12 16:29:36 -070076 DCHECK(gpu_timing_client_->gpu_timing_);
77 switch (gpu_timing_client_->gpu_timing_->timer_type_) {
78 case GPUTiming::kTimerTypeARB:
79 case GPUTiming::kTimerTypeDisjoint:
80 offset_ = gpu_timing_client_->CalculateTimerOffset();
81 glQueryCounter(queries_[1], GL_TIMESTAMP);
82 break;
83 case GPUTiming::kTimerTypeEXT:
84 glEndQuery(GL_TIME_ELAPSED_EXT);
85 break;
86 default:
87 NOTREACHED();
88 }
Etienne Membrives386015a2015-02-19 17:27:12 +010089}
90
91bool GPUTimer::IsAvailable() {
Benjamin Lerman507bb1a2015-02-26 13:15:17 +010092 if (!gpu_timing_client_->IsAvailable() || !end_requested_) {
Etienne Membrives386015a2015-02-19 17:27:12 +010093 return false;
94 }
95 GLint done = 0;
Dave Moore0ae79f42015-03-17 12:56:46 -070096 glGetQueryObjectiv(queries_[1] ? queries_[1] : queries_[0],
97 GL_QUERY_RESULT_AVAILABLE, &done);
Etienne Membrives386015a2015-02-19 17:27:12 +010098 return done != 0;
99}
100
101void GPUTimer::GetStartEndTimestamps(int64* start, int64* end) {
102 DCHECK(start && end);
103 DCHECK(IsAvailable());
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700104 DCHECK(gpu_timing_client_->gpu_timing_);
105 DCHECK(gpu_timing_client_->gpu_timing_->timer_type_ !=
106 GPUTiming::kTimerTypeEXT);
Etienne Membrives386015a2015-02-19 17:27:12 +0100107 GLuint64 begin_stamp = 0;
108 GLuint64 end_stamp = 0;
109 // TODO(dsinclair): It's possible for the timer to wrap during the start/end.
110 // We need to detect if the end is less then the start and correct for the
111 // wrapping.
112 glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &begin_stamp);
113 glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, &end_stamp);
114
115 *start = (begin_stamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
116 *end = (end_stamp / base::Time::kNanosecondsPerMicrosecond) + offset_;
117}
118
119int64 GPUTimer::GetDeltaElapsed() {
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700120 DCHECK(gpu_timing_client_->gpu_timing_);
121 switch (gpu_timing_client_->gpu_timing_->timer_type_) {
122 case GPUTiming::kTimerTypeARB:
123 case GPUTiming::kTimerTypeDisjoint: {
124 int64 start = 0;
125 int64 end = 0;
126 GetStartEndTimestamps(&start, &end);
127 return end - start;
128 } break;
129 case GPUTiming::kTimerTypeEXT: {
130 GLuint64 delta = 0;
131 glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &delta);
132 return static_cast<int64>(delta / base::Time::kNanosecondsPerMicrosecond);
133 } break;
134 default:
135 NOTREACHED();
136 }
137 return 0;
Etienne Membrives386015a2015-02-19 17:27:12 +0100138}
139
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100140GPUTimer::GPUTimer(scoped_refptr<GPUTimingClient> gpu_timing_client)
141 : gpu_timing_client_(gpu_timing_client) {
142 DCHECK(gpu_timing_client_);
143 memset(queries_, 0, sizeof(queries_));
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700144 int queries = 0;
145 DCHECK(gpu_timing_client_->gpu_timing_);
146 switch (gpu_timing_client_->gpu_timing_->timer_type_) {
147 case GPUTiming::kTimerTypeARB:
148 case GPUTiming::kTimerTypeDisjoint:
149 queries = 2;
150 break;
151 case GPUTiming::kTimerTypeEXT:
152 queries = 1;
153 break;
154 default:
155 NOTREACHED();
156 }
Dave Moore0ae79f42015-03-17 12:56:46 -0700157 glGenQueries(queries, queries_);
Etienne Membrives386015a2015-02-19 17:27:12 +0100158}
159
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100160GPUTimingClient::GPUTimingClient(GPUTiming* gpu_timing)
161 : gpu_timing_(gpu_timing) {
162 if (gpu_timing) {
163 timer_type_ = gpu_timing->GetTimerType();
Dave Moorecc0e4f92015-03-10 15:23:04 -0700164 disjoint_counter_ = gpu_timing_->GetDisjointCount();
Etienne Membrives386015a2015-02-19 17:27:12 +0100165 }
Etienne Membrives386015a2015-02-19 17:27:12 +0100166}
167
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100168scoped_ptr<GPUTimer> GPUTimingClient::CreateGPUTimer() {
169 return make_scoped_ptr(new GPUTimer(this));
Etienne Membrives386015a2015-02-19 17:27:12 +0100170}
171
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100172bool GPUTimingClient::IsAvailable() {
173 return timer_type_ != GPUTiming::kTimerTypeInvalid;
174}
175
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700176bool GPUTimingClient::IsTimerOffsetAvailable() {
177 return timer_type_ == GPUTiming::kTimerTypeARB ||
178 timer_type_ == GPUTiming::kTimerTypeDisjoint;
179}
180
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100181const char* GPUTimingClient::GetTimerTypeName() const {
Etienne Membrives386015a2015-02-19 17:27:12 +0100182 switch (timer_type_) {
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100183 case GPUTiming::kTimerTypeDisjoint:
Etienne Membrives386015a2015-02-19 17:27:12 +0100184 return "GL_EXT_disjoint_timer_query";
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100185 case GPUTiming::kTimerTypeARB:
Etienne Membrives386015a2015-02-19 17:27:12 +0100186 return "GL_ARB_timer_query";
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700187 case GPUTiming::kTimerTypeEXT:
188 return "GL_EXT_timer_query";
Etienne Membrives386015a2015-02-19 17:27:12 +0100189 default:
190 return "Unknown";
191 }
192}
193
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100194bool GPUTimingClient::CheckAndResetTimerErrors() {
195 if (timer_type_ == GPUTiming::kTimerTypeDisjoint) {
Dave Moorecc0e4f92015-03-10 15:23:04 -0700196 DCHECK(gpu_timing_ != nullptr);
197 const uint32_t total_disjoint_count = gpu_timing_->GetDisjointCount();
198 const bool disjoint_triggered = total_disjoint_count != disjoint_counter_;
199 disjoint_counter_ = total_disjoint_count;
200 return disjoint_triggered;
Etienne Membrives386015a2015-02-19 17:27:12 +0100201 }
Dave Moorecc0e4f92015-03-10 15:23:04 -0700202 return false;
Etienne Membrives386015a2015-02-19 17:27:12 +0100203}
204
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100205int64 GPUTimingClient::CalculateTimerOffset() {
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700206 DCHECK(IsTimerOffsetAvailable());
Etienne Membrives386015a2015-02-19 17:27:12 +0100207 if (!offset_valid_) {
208 GLint64 gl_now = 0;
209 glGetInteger64v(GL_TIMESTAMP, &gl_now);
210 int64 now =
211 cpu_time_for_testing_.is_null()
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700212 ? (base::TraceTicks::Now() - base::TraceTicks()).InMicroseconds()
Etienne Membrives386015a2015-02-19 17:27:12 +0100213 : cpu_time_for_testing_.Run();
214 offset_ = now - gl_now / base::Time::kNanosecondsPerMicrosecond;
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100215 offset_valid_ = timer_type_ == GPUTiming::kTimerTypeARB;
Etienne Membrives386015a2015-02-19 17:27:12 +0100216 }
217 return offset_;
218}
219
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100220void GPUTimingClient::InvalidateTimerOffset() {
Etienne Membrives386015a2015-02-19 17:27:12 +0100221 offset_valid_ = false;
222}
223
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100224void GPUTimingClient::SetCpuTimeForTesting(
Etienne Membrives386015a2015-02-19 17:27:12 +0100225 const base::Callback<int64(void)>& cpu_time) {
226 cpu_time_for_testing_ = cpu_time;
227}
228
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100229GPUTimingClient::~GPUTimingClient() {
Etienne Membrives386015a2015-02-19 17:27:12 +0100230}
231
Benjamin Lerman507bb1a2015-02-26 13:15:17 +0100232} // namespace gfx