#include "components/viz/common/gpu/context_cache_controller.h"
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "gpu/command_buffer/client/context_support.h"
namespace viz {
namespace {
static const int kIdleCleanupDelaySeconds = 1;
}
ContextCacheController::ScopedToken::ScopedToken() = default;
ContextCacheController::ScopedToken::~ScopedToken() {
DCHECK(released_);
}
void ContextCacheController::ScopedToken::Release() {
DCHECK(!released_);
released_ = true;
}
ContextCacheController::ContextCacheController(
gpu::ContextSupport* context_support,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: context_support_(context_support), task_runner_(std::move(task_runner)) {
weak_ptr_ = weak_factory_.GetWeakPtr();
}
ContextCacheController::~ContextCacheController() {
if (held_visibility_)
ClientBecameNotVisible(std::move(held_visibility_));
}
void ContextCacheController::SetLock(base::Lock* lock) {
context_lock_ = lock;
}
std::unique_ptr<ContextCacheController::ScopedVisibility>
ContextCacheController::ClientBecameVisible() {
if (context_lock_)
context_lock_->AssertAcquired();
bool became_visible = num_clients_visible_ == 0;
++num_clients_visible_;
if (became_visible) {
context_support_->SetAggressivelyFreeResources(false);
if (on_clients_visibility_changed_cb_)
on_clients_visibility_changed_cb_.Run(became_visible);
}
return base::WrapUnique(new ScopedVisibility());
}
void ContextCacheController::ClientBecameNotVisible(
std::unique_ptr<ScopedVisibility> scoped_visibility) {
DCHECK(scoped_visibility);
scoped_visibility->Release();
if (context_lock_)
context_lock_->AssertAcquired();
DCHECK_GT(num_clients_visible_, 0u);
--num_clients_visible_;
if (num_clients_visible_ == 0) {
if (on_clients_visibility_changed_cb_)
on_clients_visibility_changed_cb_.Run(false);
InvalidatePendingIdleCallbacks();
context_support_->SetAggressivelyFreeResources(true);
context_support_->FlushPendingWork();
}
}
void ContextCacheController::ClientBecameNotVisibleDuringShutdown(
std::unique_ptr<ScopedVisibility> scoped_visibility) {
if (!held_visibility_)
held_visibility_ = std::move(scoped_visibility);
else
ClientBecameNotVisible(std::move(scoped_visibility));
}
std::unique_ptr<ContextCacheController::ScopedBusy>
ContextCacheController::ClientBecameBusy() {
if (context_lock_)
context_lock_->AssertAcquired();
++num_clients_busy_;
InvalidatePendingIdleCallbacks();
return base::WrapUnique(new ScopedBusy());
}
void ContextCacheController::ClientBecameNotBusy(
std::unique_ptr<ScopedBusy> scoped_busy) {
DCHECK(scoped_busy);
scoped_busy->Release();
if (context_lock_)
context_lock_->AssertAcquired();
DCHECK_GT(num_clients_busy_, 0u);
--num_clients_busy_;
if (num_clients_busy_ == 0 && num_clients_visible_ > 0 && task_runner_) {
if (!callback_pending_) {
{
base::AutoLock hold(current_idle_generation_lock_);
PostIdleCallback(current_idle_generation_);
}
callback_pending_ = true;
}
}
}
void ContextCacheController::SetNotifyAllClientsVisibilityChangedCb(
base::RepeatingCallback<void(bool)> on_clients_visibility_changed_cb) {
DCHECK(!on_clients_visibility_changed_cb_);
on_clients_visibility_changed_cb_ =
std::move(on_clients_visibility_changed_cb);
}
void ContextCacheController::PostIdleCallback(
uint32_t current_idle_generation) const {
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ContextCacheController::OnIdle, weak_ptr_,
current_idle_generation),
base::Seconds(kIdleCleanupDelaySeconds));
}
void ContextCacheController::InvalidatePendingIdleCallbacks() {
base::AutoLock hold(current_idle_generation_lock_);
++current_idle_generation_;
}
void ContextCacheController::OnIdle(uint32_t idle_generation)
NO_THREAD_SAFETY_ANALYSIS {
{
base::AutoLock hold(current_idle_generation_lock_);
if (current_idle_generation_ != idle_generation) {
PostIdleCallback(current_idle_generation_);
return;
}
}
if (context_lock_ && !context_lock_->Try()) {
base::AutoLock hold(current_idle_generation_lock_);
PostIdleCallback(current_idle_generation_);
return;
}
context_support_->SetAggressivelyFreeResources(true);
context_support_->FlushPendingWork();
context_support_->SetAggressivelyFreeResources(false);
callback_pending_ = false;
if (context_lock_)
context_lock_->Release();
}
}