mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
262 lines
7.5 KiB
C++
262 lines
7.5 KiB
C++
// 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/client/query_tracker.h"
|
|
|
|
#include <GLES2/gl2.h>
|
|
#include <GLES2/gl2ext.h>
|
|
#include <GLES2/gl2extchromium.h>
|
|
|
|
#include "base/atomicops.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "gpu/command_buffer/client/gles2_cmd_helper.h"
|
|
#include "gpu/command_buffer/client/gles2_implementation.h"
|
|
#include "gpu/command_buffer/client/mapped_memory.h"
|
|
#include "gpu/command_buffer/common/time.h"
|
|
|
|
namespace gpu {
|
|
namespace gles2 {
|
|
|
|
QuerySyncManager::QuerySyncManager(MappedMemoryManager* manager)
|
|
: mapped_memory_(manager) {
|
|
DCHECK(manager);
|
|
}
|
|
|
|
QuerySyncManager::~QuerySyncManager() {
|
|
while (!buckets_.empty()) {
|
|
mapped_memory_->Free(buckets_.front()->syncs);
|
|
delete buckets_.front();
|
|
buckets_.pop_front();
|
|
}
|
|
}
|
|
|
|
bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
|
|
DCHECK(info);
|
|
if (free_queries_.empty()) {
|
|
int32 shm_id;
|
|
unsigned int shm_offset;
|
|
void* mem = mapped_memory_->Alloc(
|
|
kSyncsPerBucket * sizeof(QuerySync), &shm_id, &shm_offset);
|
|
if (!mem) {
|
|
return false;
|
|
}
|
|
QuerySync* syncs = static_cast<QuerySync*>(mem);
|
|
Bucket* bucket = new Bucket(syncs);
|
|
buckets_.push_back(bucket);
|
|
for (size_t ii = 0; ii < kSyncsPerBucket; ++ii) {
|
|
free_queries_.push_back(QueryInfo(bucket, shm_id, shm_offset, syncs));
|
|
++syncs;
|
|
shm_offset += sizeof(*syncs);
|
|
}
|
|
}
|
|
*info = free_queries_.front();
|
|
++(info->bucket->used_query_count);
|
|
info->sync->Reset();
|
|
free_queries_.pop_front();
|
|
return true;
|
|
}
|
|
|
|
void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) {
|
|
DCHECK_GT(info.bucket->used_query_count, 0u);
|
|
--(info.bucket->used_query_count);
|
|
free_queries_.push_back(info);
|
|
}
|
|
|
|
void QuerySyncManager::Shrink() {
|
|
std::deque<QueryInfo> new_queue;
|
|
while (!free_queries_.empty()) {
|
|
if (free_queries_.front().bucket->used_query_count)
|
|
new_queue.push_back(free_queries_.front());
|
|
free_queries_.pop_front();
|
|
}
|
|
free_queries_.swap(new_queue);
|
|
|
|
std::deque<Bucket*> new_buckets;
|
|
while (!buckets_.empty()) {
|
|
Bucket* bucket = buckets_.front();
|
|
if (bucket->used_query_count) {
|
|
new_buckets.push_back(bucket);
|
|
} else {
|
|
mapped_memory_->Free(bucket->syncs);
|
|
delete bucket;
|
|
}
|
|
buckets_.pop_front();
|
|
}
|
|
buckets_.swap(new_buckets);
|
|
}
|
|
|
|
QueryTracker::Query::Query(GLuint id, GLenum target,
|
|
const QuerySyncManager::QueryInfo& info)
|
|
: id_(id),
|
|
target_(target),
|
|
info_(info),
|
|
state_(kUninitialized),
|
|
submit_count_(0),
|
|
token_(0),
|
|
flush_count_(0),
|
|
client_begin_time_us_(0),
|
|
result_(0) {
|
|
}
|
|
|
|
|
|
void QueryTracker::Query::Begin(GLES2Implementation* gl) {
|
|
// init memory, inc count
|
|
MarkAsActive();
|
|
|
|
switch (target()) {
|
|
case GL_GET_ERROR_QUERY_CHROMIUM:
|
|
// To nothing on begin for error queries.
|
|
break;
|
|
case GL_LATENCY_QUERY_CHROMIUM:
|
|
client_begin_time_us_ = MicrosecondsSinceOriginOfTime();
|
|
// tell service about id, shared memory and count
|
|
gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
|
|
break;
|
|
case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
|
|
case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
|
|
default:
|
|
// tell service about id, shared memory and count
|
|
gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QueryTracker::Query::End(GLES2Implementation* gl) {
|
|
switch (target()) {
|
|
case GL_GET_ERROR_QUERY_CHROMIUM: {
|
|
GLenum error = gl->GetClientSideGLError();
|
|
if (error == GL_NO_ERROR) {
|
|
// There was no error so start the query on the service.
|
|
// it will end immediately.
|
|
gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
|
|
} else {
|
|
// There's an error on the client, no need to bother the service. Just
|
|
// set the query as completed and return the error.
|
|
if (error != GL_NO_ERROR) {
|
|
state_ = kComplete;
|
|
result_ = error;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
flush_count_ = gl->helper()->flush_generation();
|
|
gl->helper()->EndQueryEXT(target(), submit_count());
|
|
MarkAsPending(gl->helper()->InsertToken());
|
|
}
|
|
|
|
bool QueryTracker::Query::CheckResultsAvailable(
|
|
CommandBufferHelper* helper) {
|
|
if (Pending()) {
|
|
if (base::subtle::Acquire_Load(&info_.sync->process_count) ==
|
|
submit_count_ ||
|
|
helper->IsContextLost()) {
|
|
switch (target()) {
|
|
case GL_COMMANDS_ISSUED_CHROMIUM:
|
|
result_ = base::saturated_cast<uint32>(info_.sync->result);
|
|
break;
|
|
case GL_LATENCY_QUERY_CHROMIUM:
|
|
// Disabled DCHECK because of http://crbug.com/419236.
|
|
//DCHECK(info_.sync->result >= client_begin_time_us_);
|
|
result_ = base::saturated_cast<uint32>(
|
|
info_.sync->result - client_begin_time_us_);
|
|
break;
|
|
case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
|
|
case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
|
|
default:
|
|
result_ = static_cast<uint32>(info_.sync->result);
|
|
break;
|
|
}
|
|
state_ = kComplete;
|
|
} else {
|
|
if ((helper->flush_generation() - flush_count_ - 1) >= 0x80000000) {
|
|
helper->Flush();
|
|
} else {
|
|
// Insert no-ops so that eventually the GPU process will see more work.
|
|
helper->Noop(1);
|
|
}
|
|
}
|
|
}
|
|
return state_ == kComplete;
|
|
}
|
|
|
|
uint32 QueryTracker::Query::GetResult() const {
|
|
DCHECK(state_ == kComplete || state_ == kUninitialized);
|
|
return result_;
|
|
}
|
|
|
|
QueryTracker::QueryTracker(MappedMemoryManager* manager)
|
|
: query_sync_manager_(manager) {
|
|
}
|
|
|
|
QueryTracker::~QueryTracker() {
|
|
while (!queries_.empty()) {
|
|
delete queries_.begin()->second;
|
|
queries_.erase(queries_.begin());
|
|
}
|
|
while (!removed_queries_.empty()) {
|
|
delete removed_queries_.front();
|
|
removed_queries_.pop_front();
|
|
}
|
|
}
|
|
|
|
QueryTracker::Query* QueryTracker::CreateQuery(GLuint id, GLenum target) {
|
|
DCHECK_NE(0u, id);
|
|
FreeCompletedQueries();
|
|
QuerySyncManager::QueryInfo info;
|
|
if (!query_sync_manager_.Alloc(&info)) {
|
|
return NULL;
|
|
}
|
|
Query* query = new Query(id, target, info);
|
|
std::pair<QueryMap::iterator, bool> result =
|
|
queries_.insert(std::make_pair(id, query));
|
|
DCHECK(result.second);
|
|
return query;
|
|
}
|
|
|
|
QueryTracker::Query* QueryTracker::GetQuery(
|
|
GLuint client_id) {
|
|
QueryMap::iterator it = queries_.find(client_id);
|
|
return it != queries_.end() ? it->second : NULL;
|
|
}
|
|
|
|
void QueryTracker::RemoveQuery(GLuint client_id) {
|
|
QueryMap::iterator it = queries_.find(client_id);
|
|
if (it != queries_.end()) {
|
|
Query* query = it->second;
|
|
// When you delete a query you can't mark its memory as unused until it's
|
|
// completed.
|
|
// Note: If you don't do this you won't mess up the service but you will
|
|
// mess up yourself.
|
|
removed_queries_.push_back(query);
|
|
queries_.erase(it);
|
|
FreeCompletedQueries();
|
|
}
|
|
}
|
|
|
|
void QueryTracker::Shrink() {
|
|
FreeCompletedQueries();
|
|
query_sync_manager_.Shrink();
|
|
}
|
|
|
|
void QueryTracker::FreeCompletedQueries() {
|
|
QueryList::iterator it = removed_queries_.begin();
|
|
while (it != removed_queries_.end()) {
|
|
Query* query = *it;
|
|
if (query->Pending() &&
|
|
base::subtle::Acquire_Load(&query->info_.sync->process_count) !=
|
|
query->submit_count()) {
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
query_sync_manager_.Free(query->info_);
|
|
it = removed_queries_.erase(it);
|
|
delete query;
|
|
}
|
|
}
|
|
|
|
} // namespace gles2
|
|
} // namespace gpu
|