1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "gpu/command_buffer/client/query_tracker.h" 6 7 #include <GLES2/gl2.h> 8 #include <GLES2/gl2ext.h> 9 #include <GLES2/gl2extchromium.h> 10 11 #include "base/atomicops.h" 12 #include "gpu/command_buffer/client/gles2_cmd_helper.h" 13 #include "gpu/command_buffer/client/gles2_implementation.h" 14 #include "gpu/command_buffer/client/mapped_memory.h" 15 #include "gpu/command_buffer/common/time.h" 16 17 namespace gpu { 18 namespace gles2 { 19 20 QuerySyncManager::QuerySyncManager(MappedMemoryManager* manager) 21 : mapped_memory_(manager) { 22 DCHECK(manager); 23 } 24 25 QuerySyncManager::~QuerySyncManager() { 26 while (!buckets_.empty()) { 27 mapped_memory_->Free(buckets_.front()->syncs); 28 delete buckets_.front(); 29 buckets_.pop_front(); 30 } 31 } 32 33 bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) { 34 DCHECK(info); 35 if (free_queries_.empty()) { 36 int32 shm_id; 37 unsigned int shm_offset; 38 void* mem = mapped_memory_->Alloc( 39 kSyncsPerBucket * sizeof(QuerySync), &shm_id, &shm_offset); 40 if (!mem) { 41 return false; 42 } 43 QuerySync* syncs = static_cast<QuerySync*>(mem); 44 Bucket* bucket = new Bucket(syncs); 45 buckets_.push_back(bucket); 46 for (size_t ii = 0; ii < kSyncsPerBucket; ++ii) { 47 free_queries_.push_back(QueryInfo(bucket, shm_id, shm_offset, syncs)); 48 ++syncs; 49 shm_offset += sizeof(*syncs); 50 } 51 } 52 *info = free_queries_.front(); 53 ++(info->bucket->used_query_count); 54 info->sync->Reset(); 55 free_queries_.pop_front(); 56 return true; 57 } 58 59 void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) { 60 DCHECK_GT(info.bucket->used_query_count, 0u); 61 --(info.bucket->used_query_count); 62 free_queries_.push_back(info); 63 } 64 65 void QuerySyncManager::Shrink() { 66 std::deque<QueryInfo> new_queue; 67 while (!free_queries_.empty()) { 68 if (free_queries_.front().bucket->used_query_count) 69 new_queue.push_back(free_queries_.front()); 70 free_queries_.pop_front(); 71 } 72 free_queries_.swap(new_queue); 73 74 std::deque<Bucket*> new_buckets; 75 while (!buckets_.empty()) { 76 Bucket* bucket = buckets_.front(); 77 if (bucket->used_query_count) { 78 new_buckets.push_back(bucket); 79 } else { 80 mapped_memory_->Free(bucket->syncs); 81 delete bucket; 82 } 83 buckets_.pop_front(); 84 } 85 buckets_.swap(new_buckets); 86 } 87 88 QueryTracker::Query::Query(GLuint id, GLenum target, 89 const QuerySyncManager::QueryInfo& info) 90 : id_(id), 91 target_(target), 92 info_(info), 93 state_(kUninitialized), 94 submit_count_(0), 95 token_(0), 96 flush_count_(0), 97 client_begin_time_us_(0), 98 result_(0) { 99 } 100 101 102 void QueryTracker::Query::Begin(GLES2Implementation* gl) { 103 // init memory, inc count 104 MarkAsActive(); 105 106 switch (target()) { 107 case GL_GET_ERROR_QUERY_CHROMIUM: 108 // To nothing on begin for error queries. 109 break; 110 case GL_LATENCY_QUERY_CHROMIUM: 111 client_begin_time_us_ = MicrosecondsSinceOriginOfTime(); 112 // tell service about id, shared memory and count 113 gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset()); 114 break; 115 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM: 116 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: 117 default: 118 // tell service about id, shared memory and count 119 gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset()); 120 break; 121 } 122 } 123 124 void QueryTracker::Query::End(GLES2Implementation* gl) { 125 switch (target()) { 126 case GL_GET_ERROR_QUERY_CHROMIUM: { 127 GLenum error = gl->GetClientSideGLError(); 128 if (error == GL_NO_ERROR) { 129 // There was no error so start the query on the serivce. 130 // it will end immediately. 131 gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset()); 132 } else { 133 // There's an error on the client, no need to bother the service. just 134 // set the query as completed and return the error. 135 if (error != GL_NO_ERROR) { 136 state_ = kComplete; 137 result_ = error; 138 return; 139 } 140 } 141 } 142 } 143 flush_count_ = gl->helper()->flush_generation(); 144 gl->helper()->EndQueryEXT(target(), submit_count()); 145 MarkAsPending(gl->helper()->InsertToken()); 146 } 147 148 bool QueryTracker::Query::CheckResultsAvailable( 149 CommandBufferHelper* helper) { 150 if (Pending()) { 151 if (base::subtle::Acquire_Load(&info_.sync->process_count) == 152 submit_count_ || 153 helper->IsContextLost()) { 154 switch (target()) { 155 case GL_COMMANDS_ISSUED_CHROMIUM: 156 result_ = std::min(info_.sync->result, 157 static_cast<uint64>(0xFFFFFFFFL)); 158 break; 159 case GL_LATENCY_QUERY_CHROMIUM: 160 DCHECK(info_.sync->result >= client_begin_time_us_); 161 result_ = std::min(info_.sync->result - client_begin_time_us_, 162 static_cast<uint64>(0xFFFFFFFFL)); 163 break; 164 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM: 165 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: 166 default: 167 result_ = info_.sync->result; 168 break; 169 } 170 state_ = kComplete; 171 } else { 172 if ((helper->flush_generation() - flush_count_ - 1) >= 0x80000000) { 173 helper->Flush(); 174 } else { 175 // Insert no-ops so that eventually the GPU process will see more work. 176 helper->Noop(1); 177 } 178 } 179 } 180 return state_ == kComplete; 181 } 182 183 uint32 QueryTracker::Query::GetResult() const { 184 DCHECK(state_ == kComplete || state_ == kUninitialized); 185 return result_; 186 } 187 188 QueryTracker::QueryTracker(MappedMemoryManager* manager) 189 : query_sync_manager_(manager) { 190 } 191 192 QueryTracker::~QueryTracker() { 193 while (!queries_.empty()) { 194 delete queries_.begin()->second; 195 queries_.erase(queries_.begin()); 196 } 197 while (!removed_queries_.empty()) { 198 delete removed_queries_.front(); 199 removed_queries_.pop_front(); 200 } 201 } 202 203 QueryTracker::Query* QueryTracker::CreateQuery(GLuint id, GLenum target) { 204 DCHECK_NE(0u, id); 205 FreeCompletedQueries(); 206 QuerySyncManager::QueryInfo info; 207 if (!query_sync_manager_.Alloc(&info)) { 208 return NULL; 209 } 210 Query* query = new Query(id, target, info); 211 std::pair<QueryMap::iterator, bool> result = 212 queries_.insert(std::make_pair(id, query)); 213 DCHECK(result.second); 214 return query; 215 } 216 217 QueryTracker::Query* QueryTracker::GetQuery( 218 GLuint client_id) { 219 QueryMap::iterator it = queries_.find(client_id); 220 return it != queries_.end() ? it->second : NULL; 221 } 222 223 void QueryTracker::RemoveQuery(GLuint client_id) { 224 QueryMap::iterator it = queries_.find(client_id); 225 if (it != queries_.end()) { 226 Query* query = it->second; 227 // When you delete a query you can't mark its memory as unused until it's 228 // completed. 229 // Note: If you don't do this you won't mess up the service but you will 230 // mess up yourself. 231 removed_queries_.push_back(query); 232 queries_.erase(it); 233 FreeCompletedQueries(); 234 } 235 } 236 237 void QueryTracker::Shrink() { 238 FreeCompletedQueries(); 239 query_sync_manager_.Shrink(); 240 } 241 242 void QueryTracker::FreeCompletedQueries() { 243 QueryList::iterator it = removed_queries_.begin(); 244 while (it != removed_queries_.end()) { 245 Query* query = *it; 246 if (query->Pending() && 247 base::subtle::Acquire_Load(&query->info_.sync->process_count) != 248 query->submit_count()) { 249 ++it; 250 continue; 251 } 252 253 query_sync_manager_.Free(query->info_); 254 it = removed_queries_.erase(it); 255 delete query; 256 } 257 } 258 259 } // namespace gles2 260 } // namespace gpu 261