| // Copyright (c) 2012 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/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/numerics/safe_math.h" |
| #include "base/synchronization/lock.h" |
| #include "base/time/time.h" |
| #include "gpu/command_buffer/common/gles2_cmd_format.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 { |
| |
| class AbstractIntegerQuery : public QueryManager::Query { |
| public: |
| AbstractIntegerQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool 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, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset) { |
| GLuint service_id = 0; |
| glGenQueries(1, &service_id); |
| DCHECK_NE(0u, service_id); |
| service_ids_.push_back(service_id); |
| } |
| |
| bool 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()); |
| return true; |
| } |
| |
| bool AbstractIntegerQuery::End(base::subtle::Atomic32 submit_count) { |
| EndQueryHelper(target()); |
| return AddToPendingQueue(submit_count); |
| } |
| |
| bool AbstractIntegerQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| 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() { |
| } |
| |
| 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, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| bool Process(bool did_finish) override; |
| |
| protected: |
| ~BooleanQuery() override; |
| }; |
| |
| BooleanQuery::BooleanQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : AbstractIntegerQuery(manager, target, shm_id, shm_offset) {} |
| |
| BooleanQuery::~BooleanQuery() { |
| } |
| |
| bool BooleanQuery::Process(bool did_finish) { |
| if (!AreAllResultsAvailable()) { |
| // Must return true to avoid generating an error at the command |
| // buffer level. |
| return true; |
| } |
| for (const GLuint& service_id : service_ids_) { |
| GLuint result = 0; |
| glGetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, &result); |
| if (result != 0) |
| return MarkAsCompleted(1); |
| } |
| return MarkAsCompleted(0); |
| } |
| |
| class SummedIntegerQuery : public AbstractIntegerQuery { |
| public: |
| SummedIntegerQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| bool Process(bool did_finish) override; |
| |
| protected: |
| ~SummedIntegerQuery() override; |
| }; |
| |
| SummedIntegerQuery::SummedIntegerQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : AbstractIntegerQuery(manager, target, shm_id, shm_offset) {} |
| |
| SummedIntegerQuery::~SummedIntegerQuery() { |
| } |
| |
| bool SummedIntegerQuery::Process(bool did_finish) { |
| if (!AreAllResultsAvailable()) { |
| // Must return true to avoid generating an error at the command |
| // buffer level. |
| return true; |
| } |
| 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; |
| } |
| return MarkAsCompleted(summed_result); |
| } |
| |
| class CommandsIssuedQuery : public QueryManager::Query { |
| public: |
| CommandsIssuedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~CommandsIssuedQuery() override; |
| |
| private: |
| base::TimeTicks begin_time_; |
| }; |
| |
| CommandsIssuedQuery::CommandsIssuedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset) {} |
| |
| bool CommandsIssuedQuery::Begin() { |
| MarkAsActive(); |
| begin_time_ = base::TimeTicks::Now(); |
| return true; |
| } |
| |
| void CommandsIssuedQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void CommandsIssuedQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool CommandsIssuedQuery::End(base::subtle::Atomic32 submit_count) { |
| const base::TimeDelta elapsed = base::TimeTicks::Now() - begin_time_; |
| MarkAsPending(submit_count); |
| return MarkAsCompleted(elapsed.InMicroseconds()); |
| } |
| |
| bool CommandsIssuedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool CommandsIssuedQuery::Process(bool did_finish) { |
| NOTREACHED(); |
| return true; |
| } |
| |
| void CommandsIssuedQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| CommandsIssuedQuery::~CommandsIssuedQuery() { |
| } |
| |
| class CommandLatencyQuery : public QueryManager::Query { |
| public: |
| CommandLatencyQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~CommandLatencyQuery() override; |
| }; |
| |
| CommandLatencyQuery::CommandLatencyQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset) {} |
| |
| bool CommandLatencyQuery::Begin() { |
| MarkAsActive(); |
| return true; |
| } |
| |
| void CommandLatencyQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void CommandLatencyQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool CommandLatencyQuery::End(base::subtle::Atomic32 submit_count) { |
| base::TimeDelta now = base::TimeTicks::Now() - base::TimeTicks(); |
| MarkAsPending(submit_count); |
| return MarkAsCompleted(now.InMicroseconds()); |
| } |
| |
| bool CommandLatencyQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool CommandLatencyQuery::Process(bool did_finish) { |
| NOTREACHED(); |
| return true; |
| } |
| |
| void CommandLatencyQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| CommandLatencyQuery::~CommandLatencyQuery() { |
| } |
| |
| |
| class AsyncReadPixelsCompletedQuery |
| : public QueryManager::Query, |
| public base::SupportsWeakPtr<AsyncReadPixelsCompletedQuery> { |
| public: |
| AsyncReadPixelsCompletedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| void Complete(); |
| ~AsyncReadPixelsCompletedQuery() override; |
| |
| private: |
| bool complete_result_; |
| }; |
| |
| AsyncReadPixelsCompletedQuery::AsyncReadPixelsCompletedQuery( |
| QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset), complete_result_(false) {} |
| |
| bool AsyncReadPixelsCompletedQuery::Begin() { |
| MarkAsActive(); |
| return true; |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool AsyncReadPixelsCompletedQuery::End(base::subtle::Atomic32 submit_count) { |
| if (!AddToPendingQueue(submit_count)) { |
| return false; |
| } |
| manager()->decoder()->WaitForReadPixels( |
| base::Bind(&AsyncReadPixelsCompletedQuery::Complete, |
| AsWeakPtr())); |
| |
| return Process(false); |
| } |
| |
| bool AsyncReadPixelsCompletedQuery::QueryCounter( |
| base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Complete() { |
| complete_result_ = MarkAsCompleted(1); |
| } |
| |
| bool AsyncReadPixelsCompletedQuery::Process(bool did_finish) { |
| return !IsFinished() || complete_result_; |
| } |
| |
| void AsyncReadPixelsCompletedQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| AsyncReadPixelsCompletedQuery::~AsyncReadPixelsCompletedQuery() { |
| } |
| |
| |
| class GetErrorQuery : public QueryManager::Query { |
| public: |
| GetErrorQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~GetErrorQuery() override; |
| |
| private: |
| }; |
| |
| GetErrorQuery::GetErrorQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset) {} |
| |
| bool GetErrorQuery::Begin() { |
| MarkAsActive(); |
| return true; |
| } |
| |
| void GetErrorQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void GetErrorQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool GetErrorQuery::End(base::subtle::Atomic32 submit_count) { |
| MarkAsPending(submit_count); |
| return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError()); |
| } |
| |
| bool GetErrorQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool GetErrorQuery::Process(bool did_finish) { |
| NOTREACHED(); |
| return true; |
| } |
| |
| void GetErrorQuery::Destroy(bool /* have_context */) { |
| if (!IsDeleted()) { |
| MarkAsDeleted(); |
| } |
| } |
| |
| GetErrorQuery::~GetErrorQuery() { |
| } |
| |
| class CommandsCompletedQuery : public QueryManager::Query { |
| public: |
| CommandsCompletedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| // Overridden from QueryManager::Query: |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~CommandsCompletedQuery() override; |
| |
| private: |
| std::unique_ptr<gl::GLFence> fence_; |
| base::TimeTicks begin_time_; |
| }; |
| |
| CommandsCompletedQuery::CommandsCompletedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset) {} |
| |
| bool CommandsCompletedQuery::Begin() { |
| MarkAsActive(); |
| begin_time_ = base::TimeTicks::Now(); |
| return true; |
| } |
| |
| void CommandsCompletedQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void CommandsCompletedQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool CommandsCompletedQuery::End(base::subtle::Atomic32 submit_count) { |
| if (fence_ && fence_->ResetSupported()) { |
| fence_->ResetState(); |
| } |
| else { |
| fence_.reset(gl::GLFence::Create()); |
| } |
| DCHECK(fence_); |
| return AddToPendingQueue(submit_count); |
| } |
| |
| bool CommandsCompletedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool CommandsCompletedQuery::Process(bool did_finish) { |
| // Note: |did_finish| guarantees that the GPU has passed the fence but |
| // we cannot assume that GLFence::HasCompleted() will return true yet as |
| // that's not guaranteed by all GLFence implementations. |
| if (!did_finish && fence_ && !fence_->HasCompleted()) |
| return true; |
| |
| const base::TimeDelta elapsed = base::TimeTicks::Now() - begin_time_; |
| return MarkAsCompleted(elapsed.InMicroseconds()); |
| } |
| |
| void CommandsCompletedQuery::Destroy(bool have_context) { |
| if (have_context && !IsDeleted()) { |
| fence_.reset(); |
| MarkAsDeleted(); |
| } |
| } |
| |
| CommandsCompletedQuery::~CommandsCompletedQuery() {} |
| |
| class TimeElapsedQuery : public QueryManager::Query { |
| public: |
| TimeElapsedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| // Overridden from QueryManager::Query: |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~TimeElapsedQuery() override; |
| |
| private: |
| std::unique_ptr<gl::GPUTimer> gpu_timer_; |
| }; |
| |
| TimeElapsedQuery::TimeElapsedQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset), |
| gpu_timer_(manager->CreateGPUTimer(true)) {} |
| |
| bool TimeElapsedQuery::Begin() { |
| // Reset the disjoint value before the query begins if it is safe. |
| SafelyResetDisjointValue(); |
| MarkAsActive(); |
| gpu_timer_->Start(); |
| return true; |
| } |
| |
| bool TimeElapsedQuery::End(base::subtle::Atomic32 submit_count) { |
| gpu_timer_->End(); |
| return AddToPendingQueue(submit_count); |
| } |
| |
| bool TimeElapsedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void TimeElapsedQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void TimeElapsedQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool TimeElapsedQuery::Process(bool did_finish) { |
| if (!gpu_timer_->IsAvailable()) |
| return true; |
| |
| // 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; |
| return MarkAsCompleted(nano_seconds); |
| } |
| |
| void TimeElapsedQuery::Destroy(bool have_context) { |
| gpu_timer_->Destroy(have_context); |
| } |
| |
| TimeElapsedQuery::~TimeElapsedQuery() {} |
| |
| class TimeStampQuery : public QueryManager::Query { |
| public: |
| TimeStampQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset); |
| |
| // Overridden from QueryManager::Query: |
| bool Begin() override; |
| bool End(base::subtle::Atomic32 submit_count) override; |
| bool QueryCounter(base::subtle::Atomic32 submit_count) override; |
| void Pause() override; |
| void Resume() override; |
| bool Process(bool did_finish) override; |
| void Destroy(bool have_context) override; |
| |
| protected: |
| ~TimeStampQuery() override; |
| |
| private: |
| std::unique_ptr<gl::GPUTimer> gpu_timer_; |
| }; |
| |
| TimeStampQuery::TimeStampQuery(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : Query(manager, target, shm_id, shm_offset), |
| gpu_timer_(manager->CreateGPUTimer(false)) {} |
| |
| bool TimeStampQuery::Begin() { |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool TimeStampQuery::End(base::subtle::Atomic32 submit_count) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void TimeStampQuery::Pause() { |
| MarkAsPaused(); |
| } |
| |
| void TimeStampQuery::Resume() { |
| MarkAsActive(); |
| } |
| |
| bool 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(); |
| return AddToPendingQueue(submit_count); |
| } |
| |
| bool TimeStampQuery::Process(bool did_finish) { |
| if (!gpu_timer_->IsAvailable()) |
| return true; |
| |
| // 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; |
| return MarkAsCompleted(nano_seconds); |
| } |
| |
| void TimeStampQuery::Destroy(bool have_context) { |
| if (gpu_timer_.get()) { |
| gpu_timer_->Destroy(have_context); |
| gpu_timer_.reset(); |
| } |
| } |
| |
| TimeStampQuery::~TimeStampQuery() {} |
| |
| QueryManager::QueryManager( |
| 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), |
| query_count_(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(); |
| } |
| } |
| |
| QueryManager::~QueryManager() { |
| DCHECK(queries_.empty()); |
| |
| // If this triggers, that means something is keeping a reference to |
| // a Query belonging to this. |
| CHECK_EQ(query_count_, 0u); |
| } |
| |
| void QueryManager::Destroy(bool have_context) { |
| active_queries_.clear(); |
| pending_queries_.clear(); |
| pending_transfer_queries_.clear(); |
| active_queries_.clear(); |
| while (!queries_.empty()) { |
| Query* query = queries_.begin()->second.get(); |
| query->Destroy(have_context); |
| queries_.erase(queries_.begin()); |
| } |
| } |
| |
| error::Error QueryManager::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; |
| } |
| |
| QueryManager::Query* QueryManager::CreateQuery(GLenum target, |
| GLuint client_id, |
| int32_t shm_id, |
| uint32_t shm_offset) { |
| scoped_refptr<Query> query; |
| switch (target) { |
| case GL_COMMANDS_ISSUED_CHROMIUM: |
| query = new CommandsIssuedQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_LATENCY_QUERY_CHROMIUM: |
| query = new CommandLatencyQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: |
| query = new AsyncReadPixelsCompletedQuery( |
| this, target, shm_id, shm_offset); |
| break; |
| case GL_GET_ERROR_QUERY_CHROMIUM: |
| query = new GetErrorQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_COMMANDS_COMPLETED_CHROMIUM: |
| query = new CommandsCompletedQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_TIME_ELAPSED: |
| query = new TimeElapsedQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_TIMESTAMP: |
| query = new TimeStampQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| query = new BooleanQuery(this, target, shm_id, shm_offset); |
| break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| query = new SummedIntegerQuery(this, target, shm_id, shm_offset); |
| break; |
| default: { |
| NOTREACHED(); |
| } |
| } |
| std::pair<QueryMap::iterator, bool> result = |
| queries_.insert(std::make_pair(client_id, query)); |
| DCHECK(result.second); |
| return query.get(); |
| } |
| |
| std::unique_ptr<gl::GPUTimer> QueryManager::CreateGPUTimer(bool elapsed_time) { |
| return gpu_timing_client_->CreateGPUTimer(elapsed_time); |
| } |
| |
| bool QueryManager::GPUTimingAvailable() { |
| return gpu_timing_client_->IsAvailable(); |
| } |
| |
| void QueryManager::GenQueries(GLsizei n, const GLuint* queries) { |
| DCHECK_GE(n, 0); |
| for (GLsizei i = 0; i < n; ++i) { |
| generated_query_ids_.insert(queries[i]); |
| } |
| } |
| |
| bool QueryManager::IsValidQuery(GLuint id) { |
| GeneratedQueryIds::iterator it = generated_query_ids_.find(id); |
| return it != generated_query_ids_.end(); |
| } |
| |
| QueryManager::Query* QueryManager::GetQuery(GLuint client_id) { |
| QueryMap::iterator it = queries_.find(client_id); |
| return it != queries_.end() ? it->second.get() : nullptr; |
| } |
| |
| QueryManager::Query* QueryManager::GetActiveQuery(GLenum target) { |
| ActiveQueryMap::iterator it = active_queries_.find(target); |
| return it != active_queries_.end() ? it->second.get() : nullptr; |
| } |
| |
| void QueryManager::RemoveQuery(GLuint client_id) { |
| QueryMap::iterator it = queries_.find(client_id); |
| if (it != queries_.end()) { |
| Query* query = it->second.get(); |
| |
| // Remove from active query map if it is active. |
| ActiveQueryMap::iterator active_it = active_queries_.find(query->target()); |
| bool is_active = (active_it != active_queries_.end() && |
| query == active_it->second.get()); |
| DCHECK(is_active == query->IsActive()); |
| if (is_active) |
| active_queries_.erase(active_it); |
| |
| query->Destroy(true); |
| RemovePendingQuery(query); |
| query->MarkAsDeleted(); |
| queries_.erase(it); |
| } |
| generated_query_ids_.erase(client_id); |
| } |
| |
| void QueryManager::StartTracking(QueryManager::Query* /* query */) { |
| ++query_count_; |
| } |
| |
| void QueryManager::StopTracking(QueryManager::Query* /* query */) { |
| --query_count_; |
| } |
| |
| GLenum QueryManager::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 QueryManager::BeginQueryHelper(GLenum target, GLuint id) { |
| target = AdjustTargetForEmulation(target); |
| glBeginQuery(target, id); |
| } |
| |
| void QueryManager::EndQueryHelper(GLenum target) { |
| target = AdjustTargetForEmulation(target); |
| glEndQuery(target); |
| } |
| |
| void QueryManager::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 QueryManager::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(); |
| } |
| } |
| |
| QueryManager::Query::Query(QueryManager* manager, |
| GLenum target, |
| int32_t shm_id, |
| uint32_t shm_offset) |
| : manager_(manager), |
| target_(target), |
| shm_id_(shm_id), |
| shm_offset_(shm_offset), |
| submit_count_(0), |
| query_state_(kQueryState_Initialize), |
| deleted_(false) { |
| DCHECK(manager); |
| manager_->StartTracking(this); |
| } |
| |
| void QueryManager::Query::RunCallbacks() { |
| for (size_t i = 0; i < callbacks_.size(); i++) { |
| callbacks_[i].Run(); |
| } |
| callbacks_.clear(); |
| } |
| |
| void QueryManager::Query::AddCallback(base::Closure callback) { |
| if (query_state_ == kQueryState_Pending) { |
| callbacks_.push_back(callback); |
| } else { |
| callback.Run(); |
| } |
| } |
| |
| QueryManager::Query::~Query() { |
| // The query is getting deleted, either by the client or |
| // because the context was lost. Call any outstanding |
| // callbacks to avoid leaks. |
| RunCallbacks(); |
| if (manager_) { |
| manager_->StopTracking(this); |
| manager_ = NULL; |
| } |
| } |
| |
| bool QueryManager::Query::MarkAsCompleted(uint64_t result) { |
| UnmarkAsPending(); |
| QuerySync* sync = manager_->decoder_->GetSharedMemoryAs<QuerySync*>( |
| shm_id_, shm_offset_, sizeof(*sync)); |
| if (!sync) { |
| return false; |
| } |
| |
| sync->result = result; |
| base::subtle::Release_Store(&sync->process_count, submit_count_); |
| |
| return true; |
| } |
| |
| bool QueryManager::ProcessPendingQueries(bool did_finish) { |
| while (!pending_queries_.empty()) { |
| Query* query = pending_queries_.front().get(); |
| if (!query->Process(did_finish)) { |
| return false; |
| } |
| if (query->IsPending()) { |
| break; |
| } |
| query->RunCallbacks(); |
| pending_queries_.pop_front(); |
| } |
| // If glFinish() has been called, all of our queries should be completed. |
| DCHECK(!did_finish || pending_queries_.empty()); |
| |
| return true; |
| } |
| |
| bool QueryManager::HavePendingQueries() { |
| return !pending_queries_.empty(); |
| } |
| |
| bool QueryManager::ProcessPendingTransferQueries() { |
| while (!pending_transfer_queries_.empty()) { |
| Query* query = pending_transfer_queries_.front().get(); |
| if (!query->Process(false)) { |
| return false; |
| } |
| if (query->IsPending()) { |
| break; |
| } |
| query->RunCallbacks(); |
| pending_transfer_queries_.pop_front(); |
| } |
| |
| return true; |
| } |
| |
| bool QueryManager::HavePendingTransferQueries() { |
| return !pending_transfer_queries_.empty(); |
| } |
| |
| void QueryManager::ProcessFrameBeginUpdates() { |
| if (update_disjoints_continually_) |
| UpdateDisjointValue(); |
| } |
| |
| bool QueryManager::AddPendingQuery(Query* query, |
| base::subtle::Atomic32 submit_count) { |
| DCHECK(query); |
| DCHECK(!query->IsDeleted()); |
| if (!RemovePendingQuery(query)) { |
| return false; |
| } |
| query->MarkAsPending(submit_count); |
| pending_queries_.push_back(query); |
| return true; |
| } |
| |
| bool QueryManager::AddPendingTransferQuery( |
| Query* query, |
| base::subtle::Atomic32 submit_count) { |
| DCHECK(query); |
| DCHECK(!query->IsDeleted()); |
| if (!RemovePendingQuery(query)) { |
| return false; |
| } |
| query->MarkAsPending(submit_count); |
| pending_transfer_queries_.push_back(query); |
| return true; |
| } |
| |
| bool QueryManager::RemovePendingQuery(Query* query) { |
| DCHECK(query); |
| if (query->IsPending()) { |
| // TODO(gman): Speed this up if this is a common operation. This would only |
| // happen if you do being/end begin/end on the same query without waiting |
| // for the first one to finish. |
| for (QueryQueue::iterator it = pending_queries_.begin(); |
| it != pending_queries_.end(); ++it) { |
| if (it->get() == query) { |
| pending_queries_.erase(it); |
| break; |
| } |
| } |
| for (QueryQueue::iterator it = pending_transfer_queries_.begin(); |
| it != pending_transfer_queries_.end(); ++it) { |
| if (it->get() == query) { |
| pending_transfer_queries_.erase(it); |
| break; |
| } |
| } |
| if (!query->MarkAsCompleted(0)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool QueryManager::BeginQuery(Query* query) { |
| DCHECK(query); |
| if (!RemovePendingQuery(query)) { |
| return false; |
| } |
| if (query->Begin()) { |
| active_queries_[query->target()] = query; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool QueryManager::EndQuery(Query* query, base::subtle::Atomic32 submit_count) { |
| DCHECK(query); |
| if (!RemovePendingQuery(query)) { |
| return false; |
| } |
| |
| // Remove from active query map if it is active. |
| ActiveQueryMap::iterator active_it = active_queries_.find(query->target()); |
| DCHECK(active_it != active_queries_.end()); |
| DCHECK(query == active_it->second.get()); |
| active_queries_.erase(active_it); |
| |
| return query->End(submit_count); |
| } |
| |
| bool QueryManager::QueryCounter( |
| Query* query, base::subtle::Atomic32 submit_count) { |
| DCHECK(query); |
| return query->QueryCounter(submit_count); |
| } |
| |
| void QueryManager::PauseQueries() { |
| for (std::pair<const GLenum, scoped_refptr<Query> >& it : active_queries_) { |
| if (it.second->IsActive()) { |
| it.second->Pause(); |
| DCHECK(it.second->IsPaused()); |
| } |
| } |
| } |
| |
| void QueryManager::ResumeQueries() { |
| for (std::pair<const GLenum, scoped_refptr<Query> >& it : active_queries_) { |
| if (it.second->IsPaused()) { |
| it.second->Resume(); |
| DCHECK(it.second->IsActive()); |
| } |
| } |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |