blob: 039e494de8b1d4bd5ea3df100c89ec10cb508690 [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/sync_point_manager.h"
#include <climits>
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/sequence_checker.h"
namespace gpu {
static const int kMaxSyncBase = INT_MAX;
scoped_refptr<SyncPointClientState> SyncPointClientState::Create() {
return new SyncPointClientState;
}
uint32_t SyncPointClientState::GenerateUnprocessedOrderNumber(
SyncPointManager* sync_point_manager) {
const uint32_t order_num = sync_point_manager->GenerateOrderNumber();
base::subtle::Release_Store(&unprocessed_order_num_, order_num);
return order_num;
}
SyncPointClientState::SyncPointClientState()
: processed_order_num_(0),
unprocessed_order_num_(0),
current_order_num_(0) {
}
SyncPointClientState::~SyncPointClientState() {
}
SyncPointClient::~SyncPointClient() {
sync_point_manager_->DestroySyncPointClient(namespace_id_, client_id_);
}
SyncPointClient::SyncPointClient(SyncPointManager* sync_point_manager,
scoped_refptr<SyncPointClientState> state,
CommandBufferNamespace namespace_id,
uint64_t client_id)
: sync_point_manager_(sync_point_manager),
client_state_(state),
namespace_id_(namespace_id),
client_id_(client_id) {
}
SyncPointManager::SyncPointManager(bool allow_threaded_wait)
: allow_threaded_wait_(allow_threaded_wait),
// To reduce the risk that a sync point created in a previous GPU process
// will be in flight in the next GPU process, randomize the starting sync
// point number. http://crbug.com/373452
next_sync_point_(base::RandInt(1, kMaxSyncBase)),
retire_cond_var_(&lock_) {
global_order_num_.GetNext();
}
SyncPointManager::~SyncPointManager() {
for (const ClientMap& client_map : client_maps_) {
DCHECK(client_map.empty());
}
}
scoped_ptr<SyncPointClient> SyncPointManager::CreateSyncPointClient(
scoped_refptr<SyncPointClientState> client_state,
CommandBufferNamespace namespace_id, uint64_t client_id) {
DCHECK_GE(namespace_id, 0);
DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_));
base::AutoLock auto_lock(client_maps_lock_);
ClientMap& client_map = client_maps_[namespace_id];
std::pair<ClientMap::iterator, bool> result = client_map.insert(
std::make_pair(client_id, new SyncPointClient(this,
client_state,
namespace_id,
client_id)));
DCHECK(result.second);
return make_scoped_ptr(result.first->second);
}
scoped_refptr<SyncPointClientState> SyncPointManager::GetSyncPointClientState(
CommandBufferNamespace namespace_id, uint64_t client_id) {
DCHECK_GE(namespace_id, 0);
DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_));
base::AutoLock auto_lock(client_maps_lock_);
ClientMap& client_map = client_maps_[namespace_id];
ClientMap::iterator it = client_map.find(client_id);
if (it != client_map.end()) {
return it->second->client_state();
}
return nullptr;
}
uint32 SyncPointManager::GenerateSyncPoint() {
base::AutoLock lock(lock_);
uint32 sync_point = next_sync_point_++;
// When an integer overflow occurs, don't return 0.
if (!sync_point)
sync_point = next_sync_point_++;
// Note: wrapping would take days for a buggy/compromized renderer that would
// insert sync points in a loop, but if that were to happen, better explicitly
// crash the GPU process than risk worse.
// For normal operation (at most a few per frame), it would take ~a year to
// wrap.
CHECK(sync_point_map_.find(sync_point) == sync_point_map_.end());
sync_point_map_.insert(std::make_pair(sync_point, ClosureList()));
return sync_point;
}
void SyncPointManager::RetireSyncPoint(uint32 sync_point) {
ClosureList list;
{
base::AutoLock lock(lock_);
SyncPointMap::iterator it = sync_point_map_.find(sync_point);
if (it == sync_point_map_.end()) {
LOG(ERROR) << "Attempted to retire sync point that"
" didn't exist or was already retired.";
return;
}
list.swap(it->second);
sync_point_map_.erase(it);
if (allow_threaded_wait_)
retire_cond_var_.Broadcast();
}
for (ClosureList::iterator i = list.begin(); i != list.end(); ++i)
i->Run();
}
void SyncPointManager::AddSyncPointCallback(uint32 sync_point,
const base::Closure& callback) {
{
base::AutoLock lock(lock_);
SyncPointMap::iterator it = sync_point_map_.find(sync_point);
if (it != sync_point_map_.end()) {
it->second.push_back(callback);
return;
}
}
callback.Run();
}
bool SyncPointManager::IsSyncPointRetired(uint32 sync_point) {
base::AutoLock lock(lock_);
return IsSyncPointRetiredLocked(sync_point);
}
void SyncPointManager::WaitSyncPoint(uint32 sync_point) {
if (!allow_threaded_wait_) {
DCHECK(IsSyncPointRetired(sync_point));
return;
}
base::AutoLock lock(lock_);
while (!IsSyncPointRetiredLocked(sync_point)) {
retire_cond_var_.Wait();
}
}
bool SyncPointManager::IsSyncPointRetiredLocked(uint32 sync_point) {
lock_.AssertAcquired();
return sync_point_map_.find(sync_point) == sync_point_map_.end();
}
uint32_t SyncPointManager::GenerateOrderNumber() {
return global_order_num_.GetNext();
}
void SyncPointManager::DestroySyncPointClient(
CommandBufferNamespace namespace_id, uint64_t client_id) {
DCHECK_GE(namespace_id, 0);
DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_));
base::AutoLock auto_lock(client_maps_lock_);
ClientMap& client_map = client_maps_[namespace_id];
ClientMap::iterator it = client_map.find(client_id);
DCHECK(it != client_map.end());
client_map.erase(it);
}
} // namespace gpu