blob: a5071e335d7ad1cb9088f785168894d98293e995 [file] [log] [blame]
// 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