// Copyright 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 "cc/resources/resource_update_controller.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/resource_provider.h"
#include "ui/gfx/frame_time.h"
namespace {
// Number of partial updates we allow.
const size_t kPartialTextureUpdatesMax = 12;
// Measured in seconds.
const double kUploaderBusyTickRate = 0.001;
// Number of blocking update intervals to allow.
const size_t kMaxBlockingUpdateIntervals = 4;
} // namespace
namespace cc {
size_t ResourceUpdateController::MaxPartialTextureUpdates() {
return kPartialTextureUpdatesMax;
}
size_t ResourceUpdateController::MaxFullUpdatesPerTick(
ResourceProvider* resource_provider) {
return resource_provider->EstimatedUploadsPerTick();
}
ResourceUpdateController::ResourceUpdateController(
ResourceUpdateControllerClient* client,
base::SingleThreadTaskRunner* task_runner,
scoped_ptr<ResourceUpdateQueue> queue,
ResourceProvider* resource_provider)
: client_(client),
queue_(queue.Pass()),
resource_provider_(resource_provider),
texture_updates_per_tick_(MaxFullUpdatesPerTick(resource_provider)),
first_update_attempt_(true),
task_runner_(task_runner),
task_posted_(false),
weak_factory_(this) {}
ResourceUpdateController::~ResourceUpdateController() {}
void ResourceUpdateController::PerformMoreUpdates(
base::TimeTicks time_limit) {
time_limit_ = time_limit;
// Update already in progress.
if (task_posted_)
return;
// Call UpdateMoreTexturesNow() directly unless it's the first update
// attempt. This ensures that we empty the update queue in a finite
// amount of time.
if (!first_update_attempt_)
UpdateMoreTexturesNow();
// Post a 0-delay task when no updates were left. When it runs,
// ReadyToFinalizeTextureUpdates() will be called.
if (!UpdateMoreTexturesIfEnoughTimeRemaining()) {
task_posted_ = true;
task_runner_->PostTask(
FROM_HERE,
base::Bind(&ResourceUpdateController::OnTimerFired,
weak_factory_.GetWeakPtr()));
}
first_update_attempt_ = false;
}
void ResourceUpdateController::DiscardUploadsToEvictedResources() {
queue_->ClearUploadsToEvictedResources();
}
void ResourceUpdateController::UpdateTexture(ResourceUpdate update) {
update.bitmap->lockPixels();
update.texture->SetPixels(
resource_provider_,
static_cast<const uint8_t*>(update.bitmap->getPixels()),
update.content_rect,
update.source_rect,
update.dest_offset);
update.bitmap->unlockPixels();
}
void ResourceUpdateController::Finalize() {
while (queue_->FullUploadSize())
UpdateTexture(queue_->TakeFirstFullUpload());
while (queue_->PartialUploadSize())
UpdateTexture(queue_->TakeFirstPartialUpload());
resource_provider_->FlushUploads();
}
void ResourceUpdateController::OnTimerFired() {
task_posted_ = false;
if (!UpdateMoreTexturesIfEnoughTimeRemaining())
client_->ReadyToFinalizeTextureUpdates();
}
base::TimeTicks ResourceUpdateController::UpdateMoreTexturesCompletionTime() {
return resource_provider_->EstimatedUploadCompletionTime(
texture_updates_per_tick_);
}
size_t ResourceUpdateController::UpdateMoreTexturesSize() const {
return texture_updates_per_tick_;
}
size_t ResourceUpdateController::MaxBlockingUpdates() const {
return UpdateMoreTexturesSize() * kMaxBlockingUpdateIntervals;
}
bool ResourceUpdateController::UpdateMoreTexturesIfEnoughTimeRemaining() {
while (resource_provider_->NumBlockingUploads() < MaxBlockingUpdates()) {
if (!queue_->FullUploadSize())
return false;
if (!time_limit_.is_null()) {
base::TimeTicks completion_time = UpdateMoreTexturesCompletionTime();
if (completion_time > time_limit_)
return true;
}
UpdateMoreTexturesNow();
}
task_posted_ = true;
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&ResourceUpdateController::OnTimerFired,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kUploaderBusyTickRate * 1000));
return true;
}
void ResourceUpdateController::UpdateMoreTexturesNow() {
size_t uploads = std::min(
queue_->FullUploadSize(), UpdateMoreTexturesSize());
if (!uploads)
return;
while (queue_->FullUploadSize() && uploads--)
UpdateTexture(queue_->TakeFirstFullUpload());
resource_provider_->FlushUploads();
}
} // namespace cc