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; |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | GPUTiming::~GPUTiming() { |
| 27 | } |
| 28 | |
| 29 | scoped_refptr<GPUTimingClient> GPUTiming::CreateGPUTimingClient() { |
| 30 | return new GPUTimingClient(this); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 31 | } |
| 32 | |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 33 | uint32_t GPUTiming::GetDisjointCount() { |
| 34 | if (timer_type_ == kTimerTypeDisjoint) { |
| 35 | GLint disjoint_value = 0; |
| 36 | glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value); |
| 37 | if (disjoint_value) { |
| 38 | disjoint_counter_++; |
| 39 | } |
| 40 | } |
| 41 | return disjoint_counter_; |
| 42 | } |
| 43 | |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 44 | GPUTimer::~GPUTimer() { |
| 45 | glDeleteQueriesARB(2, queries_); |
| 46 | } |
| 47 | |
| 48 | void GPUTimer::Start() { |
| 49 | // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value. |
| 50 | glQueryCounter(queries_[0], GL_TIMESTAMP); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 51 | } |
| 52 | |
| 53 | void GPUTimer::End() { |
| 54 | end_requested_ = true; |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 55 | offset_ = gpu_timing_client_->CalculateTimerOffset(); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 56 | glQueryCounter(queries_[1], GL_TIMESTAMP); |
| 57 | } |
| 58 | |
| 59 | bool GPUTimer::IsAvailable() { |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 60 | if (!gpu_timing_client_->IsAvailable() || !end_requested_) { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 61 | return false; |
| 62 | } |
| 63 | GLint done = 0; |
| 64 | glGetQueryObjectivARB(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done); |
| 65 | return done != 0; |
| 66 | } |
| 67 | |
| 68 | void GPUTimer::GetStartEndTimestamps(int64* start, int64* end) { |
| 69 | DCHECK(start && end); |
| 70 | DCHECK(IsAvailable()); |
| 71 | GLuint64 begin_stamp = 0; |
| 72 | GLuint64 end_stamp = 0; |
| 73 | // TODO(dsinclair): It's possible for the timer to wrap during the start/end. |
| 74 | // We need to detect if the end is less then the start and correct for the |
| 75 | // wrapping. |
| 76 | glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, &begin_stamp); |
| 77 | glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, &end_stamp); |
| 78 | |
| 79 | *start = (begin_stamp / base::Time::kNanosecondsPerMicrosecond) + offset_; |
| 80 | *end = (end_stamp / base::Time::kNanosecondsPerMicrosecond) + offset_; |
| 81 | } |
| 82 | |
| 83 | int64 GPUTimer::GetDeltaElapsed() { |
| 84 | int64 start = 0; |
| 85 | int64 end = 0; |
| 86 | GetStartEndTimestamps(&start, &end); |
| 87 | return end - start; |
| 88 | } |
| 89 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 90 | GPUTimer::GPUTimer(scoped_refptr<GPUTimingClient> gpu_timing_client) |
| 91 | : gpu_timing_client_(gpu_timing_client) { |
| 92 | DCHECK(gpu_timing_client_); |
| 93 | memset(queries_, 0, sizeof(queries_)); |
| 94 | glGenQueriesARB(2, queries_); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 95 | } |
| 96 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 97 | GPUTimingClient::GPUTimingClient(GPUTiming* gpu_timing) |
| 98 | : gpu_timing_(gpu_timing) { |
| 99 | if (gpu_timing) { |
| 100 | timer_type_ = gpu_timing->GetTimerType(); |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 101 | disjoint_counter_ = gpu_timing_->GetDisjointCount(); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 102 | } |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 103 | } |
| 104 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 105 | scoped_ptr<GPUTimer> GPUTimingClient::CreateGPUTimer() { |
| 106 | return make_scoped_ptr(new GPUTimer(this)); |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 107 | } |
| 108 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 109 | bool GPUTimingClient::IsAvailable() { |
| 110 | return timer_type_ != GPUTiming::kTimerTypeInvalid; |
| 111 | } |
| 112 | |
| 113 | const char* GPUTimingClient::GetTimerTypeName() const { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 114 | switch (timer_type_) { |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 115 | case GPUTiming::kTimerTypeDisjoint: |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 116 | return "GL_EXT_disjoint_timer_query"; |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 117 | case GPUTiming::kTimerTypeARB: |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 118 | return "GL_ARB_timer_query"; |
| 119 | default: |
| 120 | return "Unknown"; |
| 121 | } |
| 122 | } |
| 123 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 124 | bool GPUTimingClient::CheckAndResetTimerErrors() { |
| 125 | if (timer_type_ == GPUTiming::kTimerTypeDisjoint) { |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 126 | DCHECK(gpu_timing_ != nullptr); |
| 127 | const uint32_t total_disjoint_count = gpu_timing_->GetDisjointCount(); |
| 128 | const bool disjoint_triggered = total_disjoint_count != disjoint_counter_; |
| 129 | disjoint_counter_ = total_disjoint_count; |
| 130 | return disjoint_triggered; |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 131 | } |
Dave Moore | cc0e4f9 | 2015-03-10 15:23:04 -0700 | [diff] [blame] | 132 | return false; |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 133 | } |
| 134 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 135 | int64 GPUTimingClient::CalculateTimerOffset() { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 136 | if (!offset_valid_) { |
| 137 | GLint64 gl_now = 0; |
| 138 | glGetInteger64v(GL_TIMESTAMP, &gl_now); |
| 139 | int64 now = |
| 140 | cpu_time_for_testing_.is_null() |
| 141 | ? base::TimeTicks::NowFromSystemTraceTime().ToInternalValue() |
| 142 | : cpu_time_for_testing_.Run(); |
| 143 | offset_ = now - gl_now / base::Time::kNanosecondsPerMicrosecond; |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 144 | offset_valid_ = timer_type_ == GPUTiming::kTimerTypeARB; |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 145 | } |
| 146 | return offset_; |
| 147 | } |
| 148 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 149 | void GPUTimingClient::InvalidateTimerOffset() { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 150 | offset_valid_ = false; |
| 151 | } |
| 152 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 153 | void GPUTimingClient::SetCpuTimeForTesting( |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 154 | const base::Callback<int64(void)>& cpu_time) { |
| 155 | cpu_time_for_testing_ = cpu_time; |
| 156 | } |
| 157 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 158 | GPUTimingClient::~GPUTimingClient() { |
Etienne Membrives | 386015a | 2015-02-19 17:27:12 +0100 | [diff] [blame] | 159 | } |
| 160 | |
Benjamin Lerman | 507bb1a | 2015-02-26 13:15:17 +0100 | [diff] [blame] | 161 | } // namespace gfx |