| // Copyright 2018 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 "gpu/command_buffer/service/gles2_query_manager.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/atomicops.h" |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/shared_memory.h" |
| #include "base/time/time.h" |
| #include "gpu/command_buffer/service/error_state.h" |
| #include "gpu/command_buffer/service/feature_info.h" |
| #include "gpu/command_buffer/service/gles2_cmd_decoder.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_fence.h" |
| #include "ui/gl/gpu_timing.h" |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| namespace { |
| |
| class AbstractIntegerQuery : public QueryManager::Query { |
| public: |
| AbstractIntegerQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| void Begin() override; |
| void End(base::subtle::Atomic32 submit_count) override; |
| void QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~AbstractIntegerQuery() override; |
| bool AreAllResultsAvailable(); |
| |
| // Service side query ids. |
| std::vector<GLuint> service_ids_; |
| }; |
| |
| AbstractIntegerQuery::AbstractIntegerQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : Query(manager, target, std::move(buffer), sync) { |
| GLuint service_id = 0; |
| glGenQueries(1, &service_id); |
| DCHECK_NE(0u, service_id); |
| service_ids_.push_back(service_id); |
| } |
| |
| void AbstractIntegerQuery::Begin() { |
| MarkAsActive(); |
| // Delete all but the first one when beginning a new query. |
| if (service_ids_.size() > 1) { |
| glDeleteQueries(service_ids_.size() - 1, &service_ids_[1]); |
| service_ids_.resize(1); |
| } |
| BeginQueryHelper(target(), service_ids_.back()); |
| } |
| |
| void AbstractIntegerQuery::End(base::subtle::Atomic32 submit_count) { |
| EndQueryHelper(target()); |
| AddToPendingQueue(submit_count); |
| } |
| |
| void AbstractIntegerQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| } |
| |
| void AbstractIntegerQuery::Pause() { |
| MarkAsPaused(); |
| EndQueryHelper(target()); |
| } |
| |
| void AbstractIntegerQuery::Resume() { |
| MarkAsActive(); |
| |
| GLuint service_id = 0; |
| glGenQueries(1, &service_id); |
| DCHECK_NE(0u, service_id); |
| service_ids_.push_back(service_id); |
| BeginQueryHelper(target(), service_ids_.back()); |
| } |
| |
| void AbstractIntegerQuery::Destroy(bool have_context) { |
| if (have_context && !IsDeleted()) { |
| glDeleteQueries(service_ids_.size(), &service_ids_[0]); |
| service_ids_.clear(); |
| MarkAsDeleted(); |
| } |
| } |
| |
| AbstractIntegerQuery::~AbstractIntegerQuery() = default; |
| |
| bool AbstractIntegerQuery::AreAllResultsAvailable() { |
| GLuint available = 0; |
| glGetQueryObjectuiv(service_ids_.back(), GL_QUERY_RESULT_AVAILABLE_EXT, |
| &available); |
| return !!available; |
| } |
| |
| class BooleanQuery : public AbstractIntegerQuery { |
| public: |
| BooleanQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| void Process(bool did_finish) override; |
| |
| protected: |
| ~BooleanQuery() override; |
| }; |
| |
| BooleanQuery::BooleanQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : AbstractIntegerQuery(manager, target, std::move(buffer), sync) {} |
| |
| BooleanQuery::~BooleanQuery() = default; |
| |
| void BooleanQuery::Process(bool did_finish) { |
| if (!AreAllResultsAvailable()) |
| return; |
| for (const GLuint& service_id : service_ids_) { |
| GLuint result = 0; |
| glGetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, &result); |
| if (result != 0) { |
| MarkAsCompleted(1); |
| return; |
| } |
| } |
| MarkAsCompleted(0); |
| } |
| |
| class SummedIntegerQuery : public AbstractIntegerQuery { |
| public: |
| SummedIntegerQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| void Process(bool did_finish) override; |
| |
| protected: |
| ~SummedIntegerQuery() override; |
| }; |
| |
| SummedIntegerQuery::SummedIntegerQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : AbstractIntegerQuery(manager, target, std::move(buffer), sync) {} |
| |
| SummedIntegerQuery::~SummedIntegerQuery() = default; |
| |
| void SummedIntegerQuery::Process(bool did_finish) { |
| if (!AreAllResultsAvailable()) |
| return; |
| GLuint summed_result = 0; |
| for (const GLuint& service_id : service_ids_) { |
| GLuint result = 0; |
| glGetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, &result); |
| summed_result += result; |
| } |
| MarkAsCompleted(summed_result); |
| } |
| |
| class CommandLatencyQuery : public QueryManager::Query { |
| public: |
| CommandLatencyQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| |
| void Begin() override; |
| void End(base::subtle::Atomic32 submit_count) override; |
| void QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| void Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~CommandLatencyQuery() override; |
| }; |
| |
| CommandLatencyQuery::CommandLatencyQuery(QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : Query(manager, target, std::move(buffer), sync) {} |
| |
| void CommandLatencyQuery::Begin() { |
| MarkAsActive(); |
| } |
| |
| void CommandLatencyQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void CommandLatencyQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| void CommandLatencyQuery::End(base::subtle::Atomic32 submit_count) { |
| base::TimeDelta now = base::TimeTicks::Now() - base::TimeTicks(); |
| MarkAsPending(submit_count); |
| MarkAsCompleted(now.InMicroseconds()); |
| } |
| |
| void CommandLatencyQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| } |
| |
| void CommandLatencyQuery::Process(bool did_finish) { |
| NOTREACHED(); |
| } |
| |
| void CommandLatencyQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| CommandLatencyQuery::~CommandLatencyQuery() = default; |
| |
| class AsyncReadPixelsCompletedQuery |
| : public GLES2QueryManager::GLES2Query, |
| public base::SupportsWeakPtr<AsyncReadPixelsCompletedQuery> { |
| public: |
| AsyncReadPixelsCompletedQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| |
| void Begin() override; |
| void End(base::subtle::Atomic32 submit_count) override; |
| void QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| void Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| void Complete(); |
| ~AsyncReadPixelsCompletedQuery() override; |
| }; |
| |
| AsyncReadPixelsCompletedQuery::AsyncReadPixelsCompletedQuery( |
| GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : GLES2Query(manager, target, std::move(buffer), sync) {} |
| |
| void AsyncReadPixelsCompletedQuery::Begin() { |
| MarkAsActive(); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::End(base::subtle::Atomic32 submit_count) { |
| MarkAsPending(submit_count); |
| gles2_query_manager()->decoder()->WaitForReadPixels( |
| base::BindOnce(&AsyncReadPixelsCompletedQuery::Complete, AsWeakPtr())); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::QueryCounter( |
| base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Complete() { |
| MarkAsCompleted(1); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Process(bool did_finish) { |
| NOTREACHED(); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| AsyncReadPixelsCompletedQuery::~AsyncReadPixelsCompletedQuery() = default; |
| |
| class GetErrorQuery : public GLES2QueryManager::GLES2Query { |
| public: |
| GetErrorQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| |
| void Begin() override; |
| void End(base::subtle::Atomic32 submit_count) override; |
| void QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| void Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~GetErrorQuery() override; |
| }; |
| |
| GetErrorQuery::GetErrorQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : GLES2Query(manager, target, std::move(buffer), sync) {} |
| |
| void GetErrorQuery::Begin() { |
| MarkAsActive(); |
| } |
| |
| void GetErrorQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void GetErrorQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| void GetErrorQuery::End(base::subtle::Atomic32 submit_count) { |
| MarkAsPending(submit_count); |
| MarkAsCompleted( |
| gles2_query_manager()->decoder()->GetErrorState()->GetGLError()); |
| } |
| |
| void GetErrorQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| } |
| |
| void GetErrorQuery::Process(bool did_finish) { |
| NOTREACHED(); |
| } |
| |
| void GetErrorQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| GetErrorQuery::~GetErrorQuery() = default; |
| |
| class TimeElapsedQuery : public GLES2QueryManager::GLES2Query { |
| public: |
| TimeElapsedQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| |
| // Overridden from QueryManager::Query: |
| void Begin() override; |
| void End(base::subtle::Atomic32 submit_count) override; |
| void QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| void Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~TimeElapsedQuery() override; |
| |
| private: |
| std::unique_ptr<gl::GPUTimer> gpu_timer_; |
| }; |
| |
| TimeElapsedQuery::TimeElapsedQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : GLES2Query(manager, target, std::move(buffer), sync), |
| gpu_timer_(manager->CreateGPUTimer(true)) {} |
| |
| void TimeElapsedQuery::Begin() { |
| // Reset the disjoint value before the query begins if it is safe. |
| SafelyResetDisjointValue(); |
| MarkAsActive(); |
| gpu_timer_->Start(); |
| } |
| |
| void TimeElapsedQuery::End(base::subtle::Atomic32 submit_count) { |
| gpu_timer_->End(); |
| AddToPendingQueue(submit_count); |
| } |
| |
| void TimeElapsedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| } |
| |
| void TimeElapsedQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void TimeElapsedQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| void TimeElapsedQuery::Process(bool did_finish) { |
| if (!gpu_timer_->IsAvailable()) |
| return; |
| |
| // Make sure disjoint value is up to date. This disjoint check is the only one |
| // that needs to be done to validate that this query is valid. If a disjoint |
| // occurs before the client checks the query value we will just hide the |
| // disjoint state since it did not affect this query. |
| UpdateDisjointValue(); |
| |
| const uint64_t nano_seconds = |
| gpu_timer_->GetDeltaElapsed() * base::Time::kNanosecondsPerMicrosecond; |
| MarkAsCompleted(nano_seconds); |
| } |
| |
| void TimeElapsedQuery::Destroy(bool have_context) { |
| gpu_timer_->Destroy(have_context); |
| } |
| |
| TimeElapsedQuery::~TimeElapsedQuery() = default; |
| |
| class TimeStampQuery : public GLES2QueryManager::GLES2Query { |
| public: |
| TimeStampQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync); |
| |
| // Overridden from QueryManager::Query: |
| void Begin() override; |
| void End(base::subtle::Atomic32 submit_count) override; |
| void QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| void Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~TimeStampQuery() override; |
| |
| private: |
| std::unique_ptr<gl::GPUTimer> gpu_timer_; |
| }; |
| |
| TimeStampQuery::TimeStampQuery(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : GLES2Query(manager, target, std::move(buffer), sync), |
| gpu_timer_(manager->CreateGPUTimer(false)) {} |
| |
| void TimeStampQuery::Begin() { |
| NOTREACHED(); |
| } |
| |
| void TimeStampQuery::End(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| } |
| |
| void TimeStampQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void TimeStampQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| void TimeStampQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| // Reset the disjoint value before the query begins if it is safe. |
| SafelyResetDisjointValue(); |
| MarkAsActive(); |
| // After a timestamp has begun, we will want to continually detect |
| // the disjoint value every frame until the context is destroyed. |
| BeginContinualDisjointUpdate(); |
| |
| gpu_timer_->QueryTimeStamp(); |
| AddToPendingQueue(submit_count); |
| } |
| |
| void TimeStampQuery::Process(bool did_finish) { |
| if (!gpu_timer_->IsAvailable()) |
| return; |
| |
| // Make sure disjoint value is up to date. This disjoint check is the only one |
| // that needs to be done to validate that this query is valid. If a disjoint |
| // occurs before the client checks the query value we will just hide the |
| // disjoint state since it did not affect this query. |
| UpdateDisjointValue(); |
| |
| int64_t start = 0; |
| int64_t end = 0; |
| gpu_timer_->GetStartEndTimestamps(&start, &end); |
| DCHECK(start == end); |
| |
| const uint64_t nano_seconds = start * base::Time::kNanosecondsPerMicrosecond; |
| MarkAsCompleted(nano_seconds); |
| } |
| |
| void TimeStampQuery::Destroy(bool have_context) { |
| if (gpu_timer_.get()) { |
| gpu_timer_->Destroy(have_context); |
| gpu_timer_.reset(); |
| } |
| } |
| |
| TimeStampQuery::~TimeStampQuery() = default; |
| |
| } // namespace |
| |
| GLES2QueryManager::GLES2Query::GLES2Query(GLES2QueryManager* manager, |
| GLenum target, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) |
| : QueryManager::Query(manager, target, buffer, sync), |
| gles2_query_manager_(manager) {} |
| |
| GLES2QueryManager::GLES2Query::~GLES2Query() = default; |
| |
| GLES2QueryManager::GLES2QueryManager(GLES2Decoder* decoder, |
| FeatureInfo* feature_info) |
| : decoder_(decoder), |
| use_arb_occlusion_query2_for_occlusion_query_boolean_( |
| feature_info->feature_flags() |
| .use_arb_occlusion_query2_for_occlusion_query_boolean), |
| use_arb_occlusion_query_for_occlusion_query_boolean_( |
| feature_info->feature_flags() |
| .use_arb_occlusion_query_for_occlusion_query_boolean), |
| update_disjoints_continually_(false), |
| disjoint_notify_shm_id_(-1), |
| disjoint_notify_shm_offset_(0), |
| disjoints_notified_(0) { |
| DCHECK(!(use_arb_occlusion_query_for_occlusion_query_boolean_ && |
| use_arb_occlusion_query2_for_occlusion_query_boolean_)); |
| DCHECK(decoder); |
| gl::GLContext* context = decoder_->GetGLContext(); |
| if (context) { |
| gpu_timing_client_ = context->CreateGPUTimingClient(); |
| } else { |
| gpu_timing_client_ = new gl::GPUTimingClient(); |
| } |
| } |
| |
| GLES2QueryManager::~GLES2QueryManager() = default; |
| |
| QueryManager::Query* GLES2QueryManager::CreateQuery( |
| GLenum target, |
| GLuint client_id, |
| scoped_refptr<gpu::Buffer> buffer, |
| QuerySync* sync) { |
| scoped_refptr<Query> query; |
| switch (target) { |
| case GL_LATENCY_QUERY_CHROMIUM: |
| query = new CommandLatencyQuery(this, target, std::move(buffer), sync); |
| break; |
| case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: |
| query = new AsyncReadPixelsCompletedQuery(this, target, std::move(buffer), |
| sync); |
| break; |
| case GL_GET_ERROR_QUERY_CHROMIUM: |
| query = new GetErrorQuery(this, target, std::move(buffer), sync); |
| break; |
| case GL_TIME_ELAPSED: |
| query = new TimeElapsedQuery(this, target, std::move(buffer), sync); |
| break; |
| case GL_TIMESTAMP: |
| query = new TimeStampQuery(this, target, std::move(buffer), sync); |
| break; |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| query = new BooleanQuery(this, target, std::move(buffer), sync); |
| break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| query = new SummedIntegerQuery(this, target, std::move(buffer), sync); |
| break; |
| case GL_SAMPLES_PASSED: |
| query = new SummedIntegerQuery(this, target, std::move(buffer), sync); |
| break; |
| default: |
| return QueryManager::CreateQuery(target, client_id, buffer, sync); |
| } |
| std::pair<QueryMap::iterator, bool> result = |
| queries_.insert(std::make_pair(client_id, query)); |
| DCHECK(result.second); |
| return query.get(); |
| } |
| |
| void GLES2QueryManager::ProcessFrameBeginUpdates() { |
| if (update_disjoints_continually_) |
| UpdateDisjointValue(); |
| } |
| |
| error::Error GLES2QueryManager::SetDisjointSync(int32_t shm_id, |
| uint32_t shm_offset) { |
| if (disjoint_notify_shm_id_ != -1 || shm_id == -1) |
| return error::kInvalidArguments; |
| |
| DisjointValueSync* sync = decoder_->GetSharedMemoryAs<DisjointValueSync*>( |
| shm_id, shm_offset, sizeof(*sync)); |
| if (!sync) |
| return error::kOutOfBounds; |
| |
| sync->Reset(); |
| disjoints_notified_ = 0; |
| |
| disjoint_notify_shm_id_ = shm_id; |
| disjoint_notify_shm_offset_ = shm_offset; |
| return error::kNoError; |
| } |
| |
| std::unique_ptr<gl::GPUTimer> GLES2QueryManager::CreateGPUTimer( |
| bool elapsed_time) { |
| return gpu_timing_client_->CreateGPUTimer(elapsed_time); |
| } |
| |
| bool GLES2QueryManager::GPUTimingAvailable() { |
| return gpu_timing_client_->IsAvailable(); |
| } |
| |
| GLenum GLES2QueryManager::AdjustTargetForEmulation(GLenum target) { |
| switch (target) { |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: |
| case GL_ANY_SAMPLES_PASSED_EXT: |
| if (use_arb_occlusion_query2_for_occlusion_query_boolean_) { |
| // ARB_occlusion_query2 does not have a |
| // GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT |
| // target. |
| target = GL_ANY_SAMPLES_PASSED_EXT; |
| } else if (use_arb_occlusion_query_for_occlusion_query_boolean_) { |
| // ARB_occlusion_query does not have a |
| // GL_ANY_SAMPLES_PASSED_EXT |
| // target. |
| target = GL_SAMPLES_PASSED_ARB; |
| } |
| break; |
| default: |
| break; |
| } |
| return target; |
| } |
| |
| void GLES2QueryManager::UpdateDisjointValue() { |
| if (disjoint_notify_shm_id_ != -1) { |
| if (gpu_timing_client_->CheckAndResetTimerErrors()) { |
| disjoints_notified_++; |
| |
| DisjointValueSync* sync = decoder_->GetSharedMemoryAs<DisjointValueSync*>( |
| disjoint_notify_shm_id_, disjoint_notify_shm_offset_, sizeof(*sync)); |
| if (!sync) { |
| // Shared memory does not seem to be valid, ignore the shm id/offset. |
| disjoint_notify_shm_id_ = -1; |
| disjoint_notify_shm_offset_ = 0; |
| } else { |
| sync->SetDisjointCount(disjoints_notified_); |
| } |
| } |
| } |
| } |
| |
| void GLES2QueryManager::SafelyResetDisjointValue() { |
| // It is only safe to reset the disjoint value is there is no active |
| // elapsed timer and we are not continually updating the disjoint value. |
| if (!update_disjoints_continually_ && !GetActiveQuery(GL_TIME_ELAPSED)) { |
| // Reset the error state without storing the result. |
| gpu_timing_client_->CheckAndResetTimerErrors(); |
| } |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |