summaryrefslogtreecommitdiff
path: root/chromium/gpu/command_buffer/client/query_tracker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/gpu/command_buffer/client/query_tracker.cc')
-rw-r--r--chromium/gpu/command_buffer/client/query_tracker.cc265
1 files changed, 265 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/client/query_tracker.cc b/chromium/gpu/command_buffer/client/query_tracker.cc
new file mode 100644
index 00000000000..35732bf4a83
--- /dev/null
+++ b/chromium/gpu/command_buffer/client/query_tracker.cc
@@ -0,0 +1,265 @@
+// 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 "gpu/command_buffer/client/atomicops.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) {
+ GPU_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) {
+ GPU_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),
+ flushed_(false),
+ 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_TRANSFERS_COMPLETED_CHROMIUM:
+ case GL_ASYNC_READ_PIXELS_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 serivce.
+ // 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;
+ }
+ }
+ }
+ }
+ gl->helper()->EndQueryEXT(target(), submit_count());
+ MarkAsPending(gl->helper()->InsertToken());
+}
+
+bool QueryTracker::Query::CheckResultsAvailable(
+ CommandBufferHelper* helper) {
+ if (Pending()) {
+ if (info_.sync->process_count == submit_count_ ||
+ helper->IsContextLost()) {
+ // Need a MemoryBarrier here so that sync->result read after
+ // sync->process_count.
+ gpu::MemoryBarrier();
+ switch (target()) {
+ case GL_COMMANDS_ISSUED_CHROMIUM:
+ result_ = std::min(info_.sync->result,
+ static_cast<uint64>(0xFFFFFFFFL));
+ break;
+ case GL_LATENCY_QUERY_CHROMIUM:
+ GPU_DCHECK(info_.sync->result >= client_begin_time_us_);
+ result_ = std::min(info_.sync->result - client_begin_time_us_,
+ static_cast<uint64>(0xFFFFFFFFL));
+ break;
+ case GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM:
+ case GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM:
+ default:
+ result_ = info_.sync->result;
+ break;
+ }
+ state_ = kComplete;
+ } else {
+ if (!flushed_) {
+ // TODO(gman): We could reduce the number of flushes by having a
+ // flush count, recording that count at the time we insert the
+ // EndQuery command and then only flushing here if we've have not
+ // passed that count yet.
+ flushed_ = true;
+ 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 {
+ GPU_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) {
+ GPU_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));
+ GPU_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() &&
+ 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