1// Copyright 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 "cc/resources/resource_update_controller.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/single_thread_task_runner.h"
10#include "cc/resources/prioritized_resource.h"
11#include "cc/resources/resource_provider.h"
12#include "ui/gfx/frame_time.h"
13
14namespace {
15
16// Number of partial updates we allow.
17const size_t kPartialTextureUpdatesMax = 12;
18
19// Measured in seconds.
20const double kUploaderBusyTickRate = 0.001;
21
22// Number of blocking update intervals to allow.
23const size_t kMaxBlockingUpdateIntervals = 4;
24
25}  // namespace
26
27namespace cc {
28
29size_t ResourceUpdateController::MaxPartialTextureUpdates() {
30  return kPartialTextureUpdatesMax;
31}
32
33size_t ResourceUpdateController::MaxFullUpdatesPerTick(
34    ResourceProvider* resource_provider) {
35  return resource_provider->EstimatedUploadsPerTick();
36}
37
38ResourceUpdateController::ResourceUpdateController(
39    ResourceUpdateControllerClient* client,
40    base::SingleThreadTaskRunner* task_runner,
41    scoped_ptr<ResourceUpdateQueue> queue,
42    ResourceProvider* resource_provider)
43    : client_(client),
44      queue_(queue.Pass()),
45      resource_provider_(resource_provider),
46      texture_updates_per_tick_(MaxFullUpdatesPerTick(resource_provider)),
47      first_update_attempt_(true),
48      task_runner_(task_runner),
49      task_posted_(false),
50      weak_factory_(this) {}
51
52ResourceUpdateController::~ResourceUpdateController() {}
53
54void ResourceUpdateController::PerformMoreUpdates(
55    base::TimeTicks time_limit) {
56  time_limit_ = time_limit;
57
58  // Update already in progress.
59  if (task_posted_)
60    return;
61
62  // Call UpdateMoreTexturesNow() directly unless it's the first update
63  // attempt. This ensures that we empty the update queue in a finite
64  // amount of time.
65  if (!first_update_attempt_)
66    UpdateMoreTexturesNow();
67
68  // Post a 0-delay task when no updates were left. When it runs,
69  // ReadyToFinalizeTextureUpdates() will be called.
70  if (!UpdateMoreTexturesIfEnoughTimeRemaining()) {
71    task_posted_ = true;
72    task_runner_->PostTask(
73        FROM_HERE,
74        base::Bind(&ResourceUpdateController::OnTimerFired,
75                   weak_factory_.GetWeakPtr()));
76  }
77
78  first_update_attempt_ = false;
79}
80
81void ResourceUpdateController::DiscardUploadsToEvictedResources() {
82  queue_->ClearUploadsToEvictedResources();
83}
84
85void ResourceUpdateController::UpdateTexture(ResourceUpdate update) {
86  update.bitmap->lockPixels();
87  update.texture->SetPixels(
88      resource_provider_,
89      static_cast<const uint8_t*>(update.bitmap->getPixels()),
90      update.content_rect,
91      update.source_rect,
92      update.dest_offset);
93  update.bitmap->unlockPixels();
94}
95
96void ResourceUpdateController::Finalize() {
97  while (queue_->FullUploadSize())
98    UpdateTexture(queue_->TakeFirstFullUpload());
99
100  while (queue_->PartialUploadSize())
101    UpdateTexture(queue_->TakeFirstPartialUpload());
102
103  resource_provider_->FlushUploads();
104}
105
106void ResourceUpdateController::OnTimerFired() {
107  task_posted_ = false;
108  if (!UpdateMoreTexturesIfEnoughTimeRemaining())
109    client_->ReadyToFinalizeTextureUpdates();
110}
111
112base::TimeTicks ResourceUpdateController::UpdateMoreTexturesCompletionTime() {
113  return resource_provider_->EstimatedUploadCompletionTime(
114      texture_updates_per_tick_);
115}
116
117size_t ResourceUpdateController::UpdateMoreTexturesSize() const {
118  return texture_updates_per_tick_;
119}
120
121size_t ResourceUpdateController::MaxBlockingUpdates() const {
122  return UpdateMoreTexturesSize() * kMaxBlockingUpdateIntervals;
123}
124
125bool ResourceUpdateController::UpdateMoreTexturesIfEnoughTimeRemaining() {
126  while (resource_provider_->NumBlockingUploads() < MaxBlockingUpdates()) {
127    if (!queue_->FullUploadSize())
128      return false;
129
130    if (!time_limit_.is_null()) {
131      base::TimeTicks completion_time = UpdateMoreTexturesCompletionTime();
132      if (completion_time > time_limit_)
133        return true;
134    }
135
136    UpdateMoreTexturesNow();
137  }
138
139  task_posted_ = true;
140  task_runner_->PostDelayedTask(
141      FROM_HERE,
142      base::Bind(&ResourceUpdateController::OnTimerFired,
143                 weak_factory_.GetWeakPtr()),
144      base::TimeDelta::FromMilliseconds(kUploaderBusyTickRate * 1000));
145  return true;
146}
147
148void ResourceUpdateController::UpdateMoreTexturesNow() {
149  size_t uploads = std::min(
150      queue_->FullUploadSize(), UpdateMoreTexturesSize());
151
152  if (!uploads)
153    return;
154
155  while (queue_->FullUploadSize() && uploads--)
156    UpdateTexture(queue_->TakeFirstFullUpload());
157
158  resource_provider_->FlushUploads();
159}
160
161}  // namespace cc
162