| // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/gl/gpu_timing.h" |
| |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/time/time.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_version_info.h" |
| |
| namespace gl { |
| |
| class TimeElapsedTimerQuery; |
| class TimerQuery; |
| |
| int64_t NanoToMicro(uint64_t nano_seconds) { |
| const uint64_t up = nano_seconds + base::Time::kNanosecondsPerMicrosecond / 2; |
| return static_cast<int64_t>(up / base::Time::kNanosecondsPerMicrosecond); |
| } |
| |
| int32_t QueryTimestampBits() { |
| GLint timestamp_bits = 0; |
| glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, ×tamp_bits); |
| return static_cast<int32_t>(timestamp_bits); |
| } |
| |
| class GPUTimingImpl : public GPUTiming { |
| public: |
| GPUTimingImpl(GLContextReal* context); |
| ~GPUTimingImpl() override; |
| |
| void ForceTimeElapsedQuery() { force_time_elapsed_query_ = true; } |
| bool IsForceTimeElapsedQuery() { return force_time_elapsed_query_; } |
| |
| GPUTiming::TimerType GetTimerType() const { return timer_type_; } |
| |
| uint32_t GetDisjointCount(); |
| int64_t CalculateTimerOffset(); |
| |
| scoped_refptr<QueryResult> BeginElapsedTimeQuery(); |
| void EndElapsedTimeQuery(scoped_refptr<QueryResult> result); |
| |
| scoped_refptr<QueryResult> DoTimeStampQuery(); |
| |
| int64_t GetCurrentCPUTime() { |
| return cpu_time_for_testing_.is_null() |
| ? (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds() |
| : cpu_time_for_testing_.Run(); |
| } |
| void SetCpuTimeForTesting(const base::Callback<int64_t(void)>& cpu_time) { |
| cpu_time_for_testing_ = cpu_time; |
| } |
| |
| void UpdateQueryResults(); |
| |
| int64_t GetMaxTimeStamp() { return max_time_stamp_; } |
| void UpdateMaxTimeStamp(int64_t value) { |
| max_time_stamp_ = std::max(max_time_stamp_, value); |
| } |
| |
| uint32_t GetElapsedQueryCount() { return elapsed_query_count_; } |
| void IncElapsedQueryCount() { elapsed_query_count_++; } |
| void DecElapsedQueryCount() { elapsed_query_count_--; } |
| |
| void SetLastElapsedQuery(scoped_refptr<TimeElapsedTimerQuery> query); |
| scoped_refptr<TimeElapsedTimerQuery> GetLastElapsedQuery(); |
| |
| void HandleBadQuery(); |
| bool IsGoodQueryID(uint32_t query_id); |
| |
| private: |
| scoped_refptr<GPUTimingClient> CreateGPUTimingClient() override; |
| |
| base::Callback<int64_t(void)> cpu_time_for_testing_; |
| GPUTiming::TimerType timer_type_ = GPUTiming::kTimerTypeInvalid; |
| uint32_t disjoint_counter_ = 0; |
| int64_t offset_ = 0; // offset cache when timer_type_ == kTimerTypeARB |
| bool offset_valid_ = false; |
| bool force_time_elapsed_query_ = false; |
| int32_t timestamp_bit_count_gl_ = -1; // gl implementation timestamp bits |
| |
| uint32_t next_timer_query_id_ = 0; |
| uint32_t next_good_timer_query_id_ = 0; // identify bad ids for disjoints. |
| uint32_t query_disjoint_count_ = 0; |
| |
| // Extra state tracking data for elapsed timer queries. |
| int64_t max_time_stamp_ = 0; |
| uint32_t elapsed_query_count_ = 0; |
| scoped_refptr<TimeElapsedTimerQuery> last_elapsed_query_; |
| |
| std::deque<scoped_refptr<TimerQuery> > queries_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GPUTimingImpl); |
| }; |
| |
| class QueryResult : public base::RefCounted<QueryResult> { |
| public: |
| QueryResult() {} |
| |
| bool IsAvailable() const { return available_; } |
| int64_t GetDelta() const { return end_value_ - start_value_; } |
| int64_t GetStartValue() const { return start_value_; } |
| int64_t GetEndValue() const { return end_value_; } |
| |
| void SetStartValue(int64_t value) { start_value_ = value; } |
| void SetEndValue(int64_t value) { available_ = true; end_value_ = value; } |
| |
| private: |
| friend class base::RefCounted<QueryResult>; |
| ~QueryResult() {} |
| |
| bool available_ = false; |
| int64_t start_value_ = 0; |
| int64_t end_value_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(QueryResult); |
| }; |
| |
| class TimerQuery : public base::RefCounted<TimerQuery> { |
| public: |
| TimerQuery(uint32_t next_id); |
| virtual void Destroy() = 0; |
| |
| // Returns true when UpdateQueryResults() is ready to be called. |
| virtual bool IsAvailable(GPUTimingImpl* gpu_timing) = 0; |
| |
| // Fills out query result start and end, called after IsAvailable() is true. |
| virtual void UpdateQueryResults(GPUTimingImpl* gpu_timing) = 0; |
| |
| // Called when Query is next in line, used to transition states. |
| virtual void PrepareNextUpdate(scoped_refptr<TimerQuery> prev) {} |
| |
| uint32_t timer_query_id_ = 0; |
| int64_t time_stamp_ = 0; // Timestamp of the query, could be estimated. |
| |
| protected: |
| friend class base::RefCounted<TimerQuery>; |
| virtual ~TimerQuery(); |
| DISALLOW_COPY_AND_ASSIGN(TimerQuery); |
| }; |
| |
| TimerQuery::TimerQuery(uint32_t next_id) |
| : timer_query_id_(next_id) { |
| } |
| |
| TimerQuery::~TimerQuery() { |
| } |
| |
| class TimeElapsedTimerQuery : public TimerQuery { |
| public: |
| TimeElapsedTimerQuery(GPUTimingImpl* gpu_timing, uint32_t next_id) |
| : TimerQuery(next_id) { |
| glGenQueries(1, &gl_query_id_); |
| } |
| |
| void Destroy() override { |
| glDeleteQueries(1, &gl_query_id_); |
| } |
| |
| scoped_refptr<QueryResult> StartQuery(GPUTimingImpl* gpu_timing) { |
| DCHECK(query_result_start_.get() == nullptr); |
| query_begin_cpu_time_ = gpu_timing->GetCurrentCPUTime(); |
| if (gpu_timing->GetElapsedQueryCount() == 0) { |
| first_top_level_query_ = true; |
| } else { |
| // Stop the current timer query. |
| glEndQuery(GL_TIME_ELAPSED); |
| } |
| |
| // begin a new one time elapsed query. |
| glBeginQuery(GL_TIME_ELAPSED, gl_query_id_); |
| query_result_start_ = new QueryResult(); |
| |
| // Update GPUTiming state. |
| gpu_timing->SetLastElapsedQuery(this); |
| gpu_timing->IncElapsedQueryCount(); |
| |
| return query_result_start_; |
| } |
| |
| void EndQuery(GPUTimingImpl* gpu_timing, |
| scoped_refptr<QueryResult> result) { |
| DCHECK(gpu_timing->GetElapsedQueryCount() != 0); |
| |
| scoped_refptr<TimeElapsedTimerQuery> last_query = |
| gpu_timing->GetLastElapsedQuery(); |
| DCHECK(last_query.get()); |
| DCHECK(last_query->query_result_end_.get() == nullptr); |
| |
| last_query->query_result_end_ = result; |
| gpu_timing->DecElapsedQueryCount(); |
| |
| if (gpu_timing->GetElapsedQueryCount() != 0) { |
| // Continue timer if there are still ongoing queries. |
| glEndQuery(GL_TIME_ELAPSED); |
| glBeginQuery(GL_TIME_ELAPSED, gl_query_id_); |
| gpu_timing->SetLastElapsedQuery(this); |
| } else { |
| // Simply end the query and reset the current offset |
| glEndQuery(GL_TIME_ELAPSED); |
| gpu_timing->SetLastElapsedQuery(nullptr); |
| } |
| } |
| |
| // Returns true when UpdateQueryResults() is ready to be called. |
| bool IsAvailable(GPUTimingImpl* gpu_timing) override { |
| if (gpu_timing->GetElapsedQueryCount() != 0 && |
| gpu_timing->GetLastElapsedQuery() == this) { |
| // Cannot query if result is available if EndQuery has not been called. |
| // Since only one query is going on at a time, the end query is only not |
| // called for the very last query when ongoing query counter is not 0. |
| return false; |
| } |
| |
| GLuint done = 0; |
| glGetQueryObjectuiv(gl_query_id_, GL_QUERY_RESULT_AVAILABLE, &done); |
| return !!done; |
| } |
| |
| // Fills out query result start and end, called after IsAvailable() is true. |
| void UpdateQueryResults(GPUTimingImpl* gpu_timing) override { |
| GLuint64 result_value = 0; |
| glGetQueryObjectui64v(gl_query_id_, GL_QUERY_RESULT, &result_value); |
| const int64_t micro_results = NanoToMicro(result_value); |
| |
| // Adjust prev query end time if it is before the current max. |
| const int64_t start_time = |
| std::max(first_top_level_query_ ? query_begin_cpu_time_ : 0, |
| std::max(prev_query_end_time_, |
| gpu_timing->GetMaxTimeStamp())); |
| |
| // As a sanity check, is result value is greater than the time allotted we |
| // can safely say this is garbage data |
| const int64_t max_possible_time = |
| gpu_timing->GetCurrentCPUTime() - query_begin_cpu_time_; |
| if (micro_results > max_possible_time) { |
| gpu_timing->HandleBadQuery(); |
| } |
| |
| // Elapsed queries need to be adjusted so they are relative to one another. |
| // Absolute timer queries are already relative to one another absolutely. |
| time_stamp_ = start_time + micro_results; |
| |
| if (query_result_start_.get()) { |
| query_result_start_->SetStartValue(start_time); |
| } |
| if (query_result_end_.get()) { |
| query_result_end_->SetEndValue(time_stamp_); |
| } |
| } |
| |
| // Called when Query is next in line, used to transition states. |
| void PrepareNextUpdate(scoped_refptr<TimerQuery> prev) override { |
| prev_query_end_time_ = prev->time_stamp_; |
| } |
| |
| private: |
| ~TimeElapsedTimerQuery() override {} |
| |
| bool first_top_level_query_ = false; |
| uint32_t gl_query_id_ = 0; |
| int64_t prev_query_end_time_ = 0; |
| int64_t query_begin_cpu_time_ = 0; |
| scoped_refptr<QueryResult> query_result_start_; |
| scoped_refptr<QueryResult> query_result_end_; |
| }; |
| |
| class TimeStampTimerQuery : public TimerQuery { |
| public: |
| TimeStampTimerQuery(uint32_t next_id) |
| : TimerQuery(next_id) { |
| glGenQueries(1, &gl_query_id_); |
| } |
| |
| void Destroy() override { |
| glDeleteQueries(1, &gl_query_id_); |
| } |
| |
| scoped_refptr<QueryResult> DoQuery() { |
| glQueryCounter(gl_query_id_, GL_TIMESTAMP); |
| query_result_ = new QueryResult(); |
| return query_result_; |
| } |
| |
| // Returns true when UpdateQueryResults() is ready to be called. |
| bool IsAvailable(GPUTimingImpl* gpu_timing) override { |
| GLuint done = 0; |
| glGetQueryObjectuiv(gl_query_id_, GL_QUERY_RESULT_AVAILABLE, &done); |
| return !!done; |
| } |
| |
| // Fills out query result start and end, called after IsAvailable() is true. |
| void UpdateQueryResults(GPUTimingImpl* gpu_timing) override { |
| DCHECK(IsAvailable(gpu_timing)); |
| |
| GLuint64 result_value = 0; |
| glGetQueryObjectui64v(gl_query_id_, GL_QUERY_RESULT, &result_value); |
| const int64_t micro_results = NanoToMicro(result_value); |
| |
| const int64_t offset = gpu_timing->CalculateTimerOffset(); |
| const int64_t adjusted_result = micro_results + offset; |
| DCHECK(query_result_.get()); |
| query_result_->SetStartValue(adjusted_result); |
| query_result_->SetEndValue(adjusted_result); |
| time_stamp_ = adjusted_result; |
| } |
| |
| private: |
| ~TimeStampTimerQuery() override {} |
| uint32_t gl_query_id_ = 0; |
| scoped_refptr<QueryResult> query_result_; |
| }; |
| |
| GPUTimingImpl::GPUTimingImpl(GLContextReal* context) { |
| DCHECK(context); |
| const GLVersionInfo* version_info = context->GetVersionInfo(); |
| DCHECK(version_info); |
| if (context->HasExtension("GL_EXT_disjoint_timer_query")) { |
| timer_type_ = GPUTiming::kTimerTypeDisjoint; |
| } else if (context->HasExtension("GL_ARB_timer_query")) { |
| timer_type_ = GPUTiming::kTimerTypeARB; |
| } else if (context->HasExtension("GL_EXT_timer_query")) { |
| timer_type_ = GPUTiming::kTimerTypeEXT; |
| force_time_elapsed_query_ = true; |
| timestamp_bit_count_gl_ = 0; |
| } |
| // The command glGetInteger64v is only supported under ES3 and GL3.2. Since it |
| // is only used for timestamps, we workaround this by emulating timestamps |
| // so WebGL 1.0 will still have access to the extension. |
| if (!version_info->IsAtLeastGLES(3, 0) && !version_info->IsAtLeastGL(3, 2)) { |
| force_time_elapsed_query_ = true; |
| timestamp_bit_count_gl_ = 0; |
| } |
| } |
| |
| GPUTimingImpl::~GPUTimingImpl() { |
| } |
| |
| uint32_t GPUTimingImpl::GetDisjointCount() { |
| if (timer_type_ == GPUTiming::kTimerTypeDisjoint) { |
| GLint disjoint_value = 0; |
| glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value); |
| if (disjoint_value) { |
| offset_valid_ = false; |
| disjoint_counter_++; |
| } |
| } |
| return disjoint_counter_; |
| } |
| |
| int64_t GPUTimingImpl::CalculateTimerOffset() { |
| if (!offset_valid_) { |
| if (timer_type_ == GPUTiming::kTimerTypeDisjoint || |
| timer_type_ == GPUTiming::kTimerTypeARB) { |
| GLint64 gl_now = 0; |
| glGetInteger64v(GL_TIMESTAMP, &gl_now); |
| const int64_t cpu_time = GetCurrentCPUTime(); |
| const int64_t micro_offset = cpu_time - NanoToMicro(gl_now); |
| |
| // We cannot expect these instructions to run with the accuracy |
| // within 1 microsecond, instead discard differences which are less |
| // than a single millisecond. |
| base::TimeDelta delta = |
| base::TimeDelta::FromMicroseconds(micro_offset - offset_); |
| |
| if (delta.magnitude().InMilliseconds() >= 1) { |
| offset_ = micro_offset; |
| offset_valid_ = (timer_type_ == GPUTiming::kTimerTypeARB); |
| } |
| } else { |
| offset_ = 0; |
| offset_valid_ = true; |
| } |
| } |
| return offset_; |
| } |
| |
| scoped_refptr<QueryResult> GPUTimingImpl::BeginElapsedTimeQuery() { |
| DCHECK(timer_type_ != GPUTiming::kTimerTypeInvalid); |
| |
| queries_.push_back(new TimeElapsedTimerQuery(this, next_timer_query_id_++)); |
| return static_cast<TimeElapsedTimerQuery*>( |
| queries_.back().get())->StartQuery(this); |
| } |
| |
| void GPUTimingImpl::EndElapsedTimeQuery(scoped_refptr<QueryResult> result) { |
| DCHECK(timer_type_ != GPUTiming::kTimerTypeInvalid); |
| DCHECK(result.get()); |
| |
| if (GetElapsedQueryCount() > 1) { |
| // Create new elapsed timer query if there are still ongoing queries. |
| queries_.push_back(new TimeElapsedTimerQuery(this, |
| next_timer_query_id_++)); |
| static_cast<TimeElapsedTimerQuery*>( |
| queries_.back().get())->EndQuery(this, result); |
| } else { |
| // Simply end the query and reset the current offset |
| DCHECK(GetLastElapsedQuery().get()); |
| GetLastElapsedQuery()->EndQuery(this, result); |
| DCHECK(GetLastElapsedQuery().get() == nullptr); |
| } |
| } |
| |
| scoped_refptr<QueryResult> GPUTimingImpl::DoTimeStampQuery() { |
| DCHECK(timer_type_ != GPUTiming::kTimerTypeInvalid); |
| |
| // Certain GL drivers have timestamp bit count set to 0 which means timestamps |
| // aren't supported. Emulate them with time elapsed queries if that is the |
| // case. |
| if (timestamp_bit_count_gl_ == -1) { |
| DCHECK(timer_type_ != GPUTiming::kTimerTypeEXT); |
| timestamp_bit_count_gl_ = QueryTimestampBits(); |
| force_time_elapsed_query_ = (timestamp_bit_count_gl_ == 0); |
| } |
| |
| if (force_time_elapsed_query_) { |
| // Replace with elapsed timer queries instead. |
| scoped_refptr<QueryResult> result = BeginElapsedTimeQuery(); |
| EndElapsedTimeQuery(result); |
| return result; |
| } |
| |
| queries_.push_back(new TimeStampTimerQuery(next_timer_query_id_++)); |
| return static_cast<TimeStampTimerQuery*>(queries_.back().get())->DoQuery(); |
| } |
| |
| void GPUTimingImpl::UpdateQueryResults() { |
| // Query availability of and count the queries that are available. |
| int available_queries = 0; |
| for (const scoped_refptr<TimerQuery>& query : queries_) { |
| if (!query->IsAvailable(this)) |
| break; |
| available_queries++; |
| } |
| |
| // Check for disjoints, this must be done after we checked for availability. |
| const uint32_t disjoint_counter = GetDisjointCount(); |
| if (disjoint_counter != query_disjoint_count_) { |
| next_good_timer_query_id_ = next_timer_query_id_; |
| query_disjoint_count_ = disjoint_counter; |
| } |
| |
| // Fill in the query result data once we know the disjoint value is updated. |
| // Note that even if disjoint happened and the values may or may not be |
| // garbage, we still fill it in and let GPUTimingClient's detect and disgard |
| // bad query data. The only thing we need to account for here is to not |
| // use garbade timer data to fill states such as max query times. |
| for (int i = 0; i < available_queries; ++i) { |
| scoped_refptr<TimerQuery> query = queries_.front(); |
| |
| query->UpdateQueryResults(this); |
| DCHECK(query->time_stamp_) << "Query Timestamp was not updated."; |
| |
| // For good queries, keep track of the max valid time stamps. |
| if (IsGoodQueryID(query->timer_query_id_)) |
| UpdateMaxTimeStamp(query->time_stamp_); |
| |
| query->Destroy(); |
| queries_.pop_front(); |
| |
| if (!queries_.empty()) |
| queries_.front()->PrepareNextUpdate(query); |
| } |
| } |
| |
| void GPUTimingImpl::SetLastElapsedQuery( |
| scoped_refptr<TimeElapsedTimerQuery> query) { |
| last_elapsed_query_ = query; |
| } |
| |
| scoped_refptr<TimeElapsedTimerQuery> GPUTimingImpl::GetLastElapsedQuery() { |
| return last_elapsed_query_; |
| } |
| |
| void GPUTimingImpl::HandleBadQuery() { |
| // Mark all queries as bad and signal an artificial disjoint value. |
| next_good_timer_query_id_ = next_timer_query_id_; |
| offset_valid_ = false; |
| query_disjoint_count_ = ++disjoint_counter_; |
| } |
| |
| bool GPUTimingImpl::IsGoodQueryID(uint32_t query_id) { |
| return query_id >= next_good_timer_query_id_; |
| } |
| |
| scoped_refptr<GPUTimingClient> GPUTimingImpl::CreateGPUTimingClient() { |
| return new GPUTimingClient(this); |
| } |
| |
| GPUTiming* GPUTiming::CreateGPUTiming(GLContextReal* context) { |
| return new GPUTimingImpl(context); |
| } |
| |
| GPUTiming::GPUTiming() { |
| } |
| |
| GPUTiming::~GPUTiming() { |
| } |
| |
| GPUTimer::~GPUTimer() { |
| } |
| |
| void GPUTimer::Destroy(bool have_context) { |
| if (have_context) { |
| if (timer_state_ == kTimerState_WaitingForEnd) { |
| DCHECK(gpu_timing_client_->gpu_timing_); |
| DCHECK(elapsed_timer_result_.get()); |
| gpu_timing_client_->gpu_timing_->EndElapsedTimeQuery( |
| elapsed_timer_result_); |
| } |
| } |
| } |
| |
| void GPUTimer::Reset() { |
| // We can reset from any state other than when a Start() is waiting for End(). |
| DCHECK(timer_state_ != kTimerState_WaitingForEnd); |
| time_stamp_result_ = nullptr; |
| elapsed_timer_result_ = nullptr; |
| timer_state_ = kTimerState_Ready; |
| } |
| |
| void GPUTimer::QueryTimeStamp() { |
| DCHECK(gpu_timing_client_->gpu_timing_); |
| Reset(); |
| time_stamp_result_ = gpu_timing_client_->gpu_timing_->DoTimeStampQuery(); |
| timer_state_ = kTimerState_WaitingForResult; |
| } |
| |
| void GPUTimer::Start() { |
| DCHECK(gpu_timing_client_->gpu_timing_); |
| Reset(); |
| if (!use_elapsed_timer_) |
| time_stamp_result_ = gpu_timing_client_->gpu_timing_->DoTimeStampQuery(); |
| |
| elapsed_timer_result_ = |
| gpu_timing_client_->gpu_timing_->BeginElapsedTimeQuery(); |
| timer_state_ = kTimerState_WaitingForEnd; |
| } |
| |
| void GPUTimer::End() { |
| DCHECK(timer_state_ == kTimerState_WaitingForEnd); |
| DCHECK(elapsed_timer_result_.get()); |
| gpu_timing_client_->gpu_timing_->EndElapsedTimeQuery(elapsed_timer_result_); |
| timer_state_ = kTimerState_WaitingForResult; |
| } |
| |
| bool GPUTimer::IsAvailable() { |
| if (timer_state_ == kTimerState_WaitingForResult) { |
| // Elapsed timer are only used during start/end queries and always after |
| // the timestamp query. Otherwise only the timestamp is used. |
| scoped_refptr<QueryResult> result = |
| elapsed_timer_result_.get() ? |
| elapsed_timer_result_ : |
| time_stamp_result_; |
| |
| DCHECK(result.get()); |
| if (result->IsAvailable()) { |
| timer_state_ = kTimerState_ResultAvailable; |
| } else { |
| gpu_timing_client_->gpu_timing_->UpdateQueryResults(); |
| if (result->IsAvailable()) |
| timer_state_ = kTimerState_ResultAvailable; |
| } |
| } |
| |
| return (timer_state_ == kTimerState_ResultAvailable); |
| } |
| |
| void GPUTimer::GetStartEndTimestamps(int64_t* start, int64_t* end) { |
| DCHECK(start && end); |
| DCHECK(elapsed_timer_result_.get() || time_stamp_result_.get()); |
| DCHECK(IsAvailable()); |
| const int64_t time_stamp = time_stamp_result_.get() ? |
| time_stamp_result_->GetStartValue() : |
| elapsed_timer_result_->GetStartValue(); |
| const int64_t elapsed_time = elapsed_timer_result_.get() ? |
| elapsed_timer_result_->GetDelta() : |
| 0; |
| |
| *start = time_stamp; |
| *end = time_stamp + elapsed_time; |
| } |
| |
| int64_t GPUTimer::GetDeltaElapsed() { |
| DCHECK(IsAvailable()); |
| if (elapsed_timer_result_.get()) |
| return elapsed_timer_result_->GetDelta(); |
| return 0; |
| } |
| |
| GPUTimer::GPUTimer(scoped_refptr<GPUTimingClient> gpu_timing_client, |
| bool use_elapsed_timer) |
| : use_elapsed_timer_(use_elapsed_timer), |
| gpu_timing_client_(gpu_timing_client) { |
| } |
| |
| GPUTimingClient::GPUTimingClient(GPUTimingImpl* gpu_timing) |
| : gpu_timing_(gpu_timing) { |
| if (gpu_timing) { |
| timer_type_ = gpu_timing->GetTimerType(); |
| disjoint_counter_ = gpu_timing_->GetDisjointCount(); |
| } |
| } |
| |
| std::unique_ptr<GPUTimer> GPUTimingClient::CreateGPUTimer( |
| bool prefer_elapsed_time) { |
| prefer_elapsed_time |= (timer_type_ == GPUTiming::kTimerTypeEXT); |
| if (gpu_timing_) |
| prefer_elapsed_time |= gpu_timing_->IsForceTimeElapsedQuery(); |
| |
| return base::WrapUnique(new GPUTimer(this, prefer_elapsed_time)); |
| } |
| |
| bool GPUTimingClient::IsAvailable() { |
| return timer_type_ != GPUTiming::kTimerTypeInvalid; |
| } |
| |
| const char* GPUTimingClient::GetTimerTypeName() const { |
| switch (timer_type_) { |
| case GPUTiming::kTimerTypeDisjoint: |
| return "GL_EXT_disjoint_timer_query"; |
| case GPUTiming::kTimerTypeARB: |
| return "GL_ARB_timer_query"; |
| case GPUTiming::kTimerTypeEXT: |
| return "GL_EXT_timer_query"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| bool GPUTimingClient::CheckAndResetTimerErrors() { |
| if (timer_type_ == GPUTiming::kTimerTypeDisjoint) { |
| DCHECK(gpu_timing_ != nullptr); |
| const uint32_t total_disjoint_count = gpu_timing_->GetDisjointCount(); |
| const bool disjoint_triggered = total_disjoint_count != disjoint_counter_; |
| disjoint_counter_ = total_disjoint_count; |
| return disjoint_triggered; |
| } |
| return false; |
| } |
| |
| int64_t GPUTimingClient::GetCurrentCPUTime() { |
| DCHECK(gpu_timing_); |
| return gpu_timing_->GetCurrentCPUTime(); |
| } |
| |
| void GPUTimingClient::SetCpuTimeForTesting( |
| const base::Callback<int64_t(void)>& cpu_time) { |
| DCHECK(gpu_timing_); |
| gpu_timing_->SetCpuTimeForTesting(cpu_time); |
| } |
| |
| bool GPUTimingClient::IsForceTimeElapsedQuery() { |
| DCHECK(gpu_timing_); |
| return gpu_timing_->IsForceTimeElapsedQuery(); |
| } |
| |
| void GPUTimingClient::ForceTimeElapsedQuery() { |
| DCHECK(gpu_timing_); |
| gpu_timing_->ForceTimeElapsedQuery(); |
| } |
| |
| GPUTimingClient::~GPUTimingClient() { |
| } |
| |
| } // namespace gl |