Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 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. |
| 4 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 5 | #include "ui/gl/gpu_timing.h" |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 6 | |
| 7 | #include "base/time/time.h" |
Benjamin Lerman | e8ca9b7 | 2015-02-24 16:42:13 +0100 | [diff] [blame] | 8 | #include "ui/gl/gl_bindings.h" |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 9 | #include "ui/gl/gl_context.h" |
| 10 | #include "ui/gl/gl_version_info.h" |
| 11 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 12 | namespace gfx { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 13 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 14 | GPUTiming::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 Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 23 | } else if (context->HasExtension("GL_EXT_timer_query")) { |
| 24 | timer_type_ = kTimerTypeEXT; |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 25 | } |
| 26 | } |
| 27 | |
| 28 | GPUTiming::~GPUTiming() { |
| 29 | } |
| 30 | |
| 31 | scoped_refptr<GPUTimingClient> GPUTiming::CreateGPUTimingClient() { |
| 32 | return new GPUTimingClient(this); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 33 | } |
| 34 | |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 35 | uint32_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 Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 46 | GPUTimer::~GPUTimer() { |
James Robinson | aa0657c | 2015-05-11 15:28:26 -0700 | [diff] [blame] | 47 | // Destroy() must be called before the destructor. |
| 48 | DCHECK(queries_[0] == 0); |
| 49 | DCHECK(queries_[1] == 0); |
| 50 | } |
| 51 | |
| 52 | void GPUTimer::Destroy(bool have_context) { |
| 53 | if (have_context) { |
| 54 | glDeleteQueries(2, queries_); |
| 55 | } |
| 56 | memset(queries_, 0, sizeof(queries_)); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | void GPUTimer::Start() { |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 60 | 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 Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | void GPUTimer::End() { |
| 75 | end_requested_ = true; |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 76 | 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 Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | bool GPUTimer::IsAvailable() { |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 92 | if (!gpu_timing_client_->IsAvailable() || !end_requested_) { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 93 | return false; |
| 94 | } |
| 95 | GLint done = 0; |
Dave Moore | 0ae79f4 | 2015-03-17 12:56:46 -0700 | [diff] [blame] | 96 | glGetQueryObjectiv(queries_[1] ? queries_[1] : queries_[0], |
| 97 | GL_QUERY_RESULT_AVAILABLE, &done); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 98 | return done != 0; |
| 99 | } |
| 100 | |
| 101 | void GPUTimer::GetStartEndTimestamps(int64* start, int64* end) { |
| 102 | DCHECK(start && end); |
| 103 | DCHECK(IsAvailable()); |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 104 | DCHECK(gpu_timing_client_->gpu_timing_); |
| 105 | DCHECK(gpu_timing_client_->gpu_timing_->timer_type_ != |
| 106 | GPUTiming::kTimerTypeEXT); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 107 | 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 | |
| 119 | int64 GPUTimer::GetDeltaElapsed() { |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 120 | 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 Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 138 | } |
| 139 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 140 | GPUTimer::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 Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 144 | 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 Moore | 0ae79f4 | 2015-03-17 12:56:46 -0700 | [diff] [blame] | 157 | glGenQueries(queries, queries_); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 158 | } |
| 159 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 160 | GPUTimingClient::GPUTimingClient(GPUTiming* gpu_timing) |
| 161 | : gpu_timing_(gpu_timing) { |
| 162 | if (gpu_timing) { |
| 163 | timer_type_ = gpu_timing->GetTimerType(); |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 164 | disjoint_counter_ = gpu_timing_->GetDisjointCount(); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 165 | } |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 166 | } |
| 167 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 168 | scoped_ptr<GPUTimer> GPUTimingClient::CreateGPUTimer() { |
| 169 | return make_scoped_ptr(new GPUTimer(this)); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 170 | } |
| 171 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 172 | bool GPUTimingClient::IsAvailable() { |
| 173 | return timer_type_ != GPUTiming::kTimerTypeInvalid; |
| 174 | } |
| 175 | |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 176 | bool GPUTimingClient::IsTimerOffsetAvailable() { |
| 177 | return timer_type_ == GPUTiming::kTimerTypeARB || |
| 178 | timer_type_ == GPUTiming::kTimerTypeDisjoint; |
| 179 | } |
| 180 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 181 | const char* GPUTimingClient::GetTimerTypeName() const { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 182 | switch (timer_type_) { |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 183 | case GPUTiming::kTimerTypeDisjoint: |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 184 | return "GL_EXT_disjoint_timer_query"; |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 185 | case GPUTiming::kTimerTypeARB: |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 186 | return "GL_ARB_timer_query"; |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 187 | case GPUTiming::kTimerTypeEXT: |
| 188 | return "GL_EXT_timer_query"; |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 189 | default: |
| 190 | return "Unknown"; |
| 191 | } |
| 192 | } |
| 193 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 194 | bool GPUTimingClient::CheckAndResetTimerErrors() { |
| 195 | if (timer_type_ == GPUTiming::kTimerTypeDisjoint) { |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 196 | 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 Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 201 | } |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 202 | return false; |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 203 | } |
| 204 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 205 | int64 GPUTimingClient::CalculateTimerOffset() { |
Nick Bray | 0bcbd3b | 2015-03-12 16:29:36 -0700 | [diff] [blame] | 206 | DCHECK(IsTimerOffsetAvailable()); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 207 | 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 Luu | 235cf3d | 2015-06-11 10:01:25 -0700 | [diff] [blame] | 212 | ? (base::TraceTicks::Now() - base::TraceTicks()).InMicroseconds() |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 213 | : cpu_time_for_testing_.Run(); |
| 214 | offset_ = now - gl_now / base::Time::kNanosecondsPerMicrosecond; |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 215 | offset_valid_ = timer_type_ == GPUTiming::kTimerTypeARB; |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 216 | } |
| 217 | return offset_; |
| 218 | } |
| 219 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 220 | void GPUTimingClient::InvalidateTimerOffset() { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 221 | offset_valid_ = false; |
| 222 | } |
| 223 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 224 | void GPUTimingClient::SetCpuTimeForTesting( |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 225 | const base::Callback<int64(void)>& cpu_time) { |
| 226 | cpu_time_for_testing_ = cpu_time; |
| 227 | } |
| 228 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 229 | GPUTimingClient::~GPUTimingClient() { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 230 | } |
| 231 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 232 | } // namespace gfx |