blob: 6535125e072379010aec8b6f000cf19831ae1b44 [file] [log] [blame]
// 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/tile_manager.h"
#include <algorithm>
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "cc/platform_color.h"
#include "cc/raster_worker_pool.h"
#include "cc/resource_pool.h"
#include "cc/tile.h"
namespace cc {
namespace {
// If we raster too fast we become upload bound, and pending
// uploads consume memory. For maximum upload throughput, we would
// want to allow for upload_throughput * pipeline_time of pending
// uploads, after which we are just wasting memory. Since we don't
// know our upload throughput yet, this just caps our memory usage.
#if defined(OS_ANDROID)
// For reference, the Nexus10 can upload 1MB in about 2.5ms.
// Assuming a three frame deep pipeline this implies ~20MB.
const int kMaxPendingUploadBytes = 20 * 1024 * 1024;
#else
const int kMaxPendingUploadBytes = 100 * 1024 * 1024;
#endif
// Determine bin based on three categories of tiles: things we need now,
// things we need soon, and eventually.
TileManagerBin BinFromTilePriority(const TilePriority& prio) {
if (!prio.is_live)
return NEVER_BIN;
// The amount of time for which we want to have prepainting coverage.
const double prepainting_window_time_seconds = 1.0;
const double backfling_guard_distance_pixels = 314.0;
// Explicitly limit how far ahead we will prepaint to limit memory usage.
if (prio.distance_to_visible_in_pixels >
TilePriority::kMaxDistanceInContentSpace)
return NEVER_BIN;
if (prio.time_to_visible_in_seconds == std::numeric_limits<float>::max())
return NEVER_BIN;
if (prio.time_to_visible_in_seconds == 0 ||
prio.distance_to_visible_in_pixels < backfling_guard_distance_pixels)
return NOW_BIN;
if (prio.resolution == NON_IDEAL_RESOLUTION)
return EVENTUALLY_BIN;
if (prio.time_to_visible_in_seconds < prepainting_window_time_seconds)
return SOON_BIN;
return EVENTUALLY_BIN;
}
std::string ValueToString(scoped_ptr<base::Value> value)
{
std::string str;
base::JSONWriter::Write(value.get(), &str);
return str;
}
} // namespace
scoped_ptr<base::Value> TileManagerBinAsValue(TileManagerBin bin) {
switch (bin) {
case NOW_BIN:
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"NOW_BIN"));
case SOON_BIN:
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"SOON_BIN"));
case EVENTUALLY_BIN:
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"EVENTUALLY_BIN"));
case NEVER_BIN:
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"NEVER_BIN"));
default:
DCHECK(false) << "Unrecognized TileManagerBin value";
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"<unknown TileManagerBin value>"));
}
}
ManagedTileState::ManagedTileState()
: can_use_gpu_memory(false),
can_be_freed(true),
resource_is_being_initialized(false),
contents_swizzled(false),
need_to_gather_pixel_refs(true),
gpu_memmgr_stats_bin(NEVER_BIN),
raster_state(IDLE_STATE) {
for (int i = 0; i < NUM_TREES; ++i)
tree_bin[i] = NEVER_BIN;
}
ManagedTileState::~ManagedTileState() {
DCHECK(!resource);
DCHECK(!resource_is_being_initialized);
}
TileManager::TileManager(
TileManagerClient* client,
ResourceProvider* resource_provider,
size_t num_raster_threads)
: client_(client),
resource_pool_(ResourcePool::Create(resource_provider)),
raster_worker_pool_(RasterWorkerPool::Create(num_raster_threads)),
manage_tiles_pending_(false),
manage_tiles_call_count_(0),
bytes_pending_set_pixels_(0),
ever_exceeded_memory_budget_(false) {
bool worker_pool_is_running = raster_worker_pool_->Start();
CHECK(worker_pool_is_running);
for (int i = 0; i < NUM_STATES; ++i) {
for (int j = 0; j < NUM_TREES; ++j) {
for (int k = 0; k < NUM_BINS; ++k)
raster_state_count_[i][j][k] = 0;
}
}
}
TileManager::~TileManager() {
// Reset global state and manage. This should cause
// our memory usage to drop to zero.
global_state_ = GlobalStateThatImpactsTilePriority();
AssignGpuMemoryToTiles();
// This should finish all pending tasks and release any uninitialized
// resources.
raster_worker_pool_.reset();
CheckForCompletedTileUploads();
DCHECK(tiles_with_pending_set_pixels_.size() == 0);
DCHECK(tiles_.size() == 0);
}
void TileManager::SetGlobalState(
const GlobalStateThatImpactsTilePriority& global_state) {
global_state_ = global_state;
resource_pool_->SetMaxMemoryUsageBytes(global_state_.memory_limit_in_bytes);
ScheduleManageTiles();
}
void TileManager::RegisterTile(Tile* tile) {
tiles_.push_back(tile);
const ManagedTileState& mts = tile->managed_state();
for (int i = 0; i < NUM_TREES; ++i)
++raster_state_count_[mts.raster_state][i][mts.tree_bin[i]];
ScheduleManageTiles();
}
void TileManager::UnregisterTile(Tile* tile) {
for (TileList::iterator it = tiles_with_image_decoding_tasks_.begin();
it != tiles_with_image_decoding_tasks_.end(); it++) {
if (*it == tile) {
tiles_with_image_decoding_tasks_.erase(it);
break;
}
}
for (TileVector::iterator it = tiles_that_need_to_be_rasterized_.begin();
it != tiles_that_need_to_be_rasterized_.end(); it++) {
if (*it == tile) {
tiles_that_need_to_be_rasterized_.erase(it);
break;
}
}
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); it++) {
if (*it == tile) {
const ManagedTileState& mts = tile->managed_state();
for (int i = 0; i < NUM_TREES; ++i)
--raster_state_count_[mts.raster_state][i][mts.tree_bin[i]];
FreeResourcesForTile(tile);
tiles_.erase(it);
return;
}
}
DCHECK(false) << "Could not find tile version.";
}
void TileManager::WillModifyTilePriority(
Tile* tile, WhichTree tree, const TilePriority& new_priority) {
// TODO(nduca): Do something smarter if reprioritization turns out to be
// costly.
ScheduleManageTiles();
}
void TileManager::ScheduleManageTiles() {
if (manage_tiles_pending_)
return;
client_->ScheduleManageTiles();
manage_tiles_pending_ = true;
}
class BinComparator {
public:
bool operator() (const Tile* a, const Tile* b) const {
const ManagedTileState& ams = a->managed_state();
const ManagedTileState& bms = b->managed_state();
if (ams.bin[HIGH_PRIORITY_BIN] != bms.bin[HIGH_PRIORITY_BIN])
return ams.bin[HIGH_PRIORITY_BIN] < bms.bin[HIGH_PRIORITY_BIN];
if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN])
return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN];
if (ams.resolution != bms.resolution)
return ams.resolution < bms.resolution;
if (ams.time_to_needed_in_seconds != bms.time_to_needed_in_seconds)
return ams.time_to_needed_in_seconds < bms.time_to_needed_in_seconds;
gfx::Rect a_rect = a->content_rect();
gfx::Rect b_rect = b->content_rect();
if (a_rect.y() != b_rect.y())
return a_rect.y() < b_rect.y();
return a_rect.x() < b_rect.x();
}
};
void TileManager::SortTiles() {
TRACE_EVENT0("cc", "TileManager::SortTiles");
// Sort by bin, resolution and time until needed.
std::sort(tiles_.begin(), tiles_.end(), BinComparator());
}
void TileManager::ManageTiles() {
TRACE_EVENT0("cc", "TileManager::ManageTiles");
manage_tiles_pending_ = false;
++manage_tiles_call_count_;
const TreePriority tree_priority = global_state_.tree_priority;
TRACE_COUNTER_ID1("cc", "TileCount", this, tiles_.size());
// For each tree, bin into different categories of tiles.
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = *it;
ManagedTileState& mts = tile->managed_state();
TilePriority prio[NUM_BIN_PRIORITIES];
switch (tree_priority) {
case SAME_PRIORITY_FOR_BOTH_TREES:
prio[HIGH_PRIORITY_BIN] = prio[LOW_PRIORITY_BIN] =
tile->combined_priority();
break;
case SMOOTHNESS_TAKES_PRIORITY:
prio[HIGH_PRIORITY_BIN] = tile->priority(ACTIVE_TREE);
prio[LOW_PRIORITY_BIN] = tile->priority(PENDING_TREE);
break;
case NEW_CONTENT_TAKES_PRIORITY:
prio[HIGH_PRIORITY_BIN] = tile->priority(PENDING_TREE);
prio[LOW_PRIORITY_BIN] = tile->priority(ACTIVE_TREE);
break;
}
mts.resolution = prio[HIGH_PRIORITY_BIN].resolution;
mts.time_to_needed_in_seconds =
prio[HIGH_PRIORITY_BIN].time_to_visible_in_seconds;
mts.bin[HIGH_PRIORITY_BIN] = BinFromTilePriority(prio[HIGH_PRIORITY_BIN]);
mts.bin[LOW_PRIORITY_BIN] = BinFromTilePriority(prio[LOW_PRIORITY_BIN]);
mts.gpu_memmgr_stats_bin = BinFromTilePriority(tile->combined_priority());
DidTileBinChange(tile,
BinFromTilePriority(tile->priority(ACTIVE_TREE)),
ACTIVE_TREE);
DidTileBinChange(tile,
BinFromTilePriority(tile->priority(PENDING_TREE)),
PENDING_TREE);
}
// Memory limit policy works by mapping some bin states to the NEVER bin.
TileManagerBin bin_map[NUM_BINS];
if (global_state_.memory_limit_policy == ALLOW_NOTHING) {
bin_map[NOW_BIN] = NEVER_BIN;
bin_map[SOON_BIN] = NEVER_BIN;
bin_map[EVENTUALLY_BIN] = NEVER_BIN;
bin_map[NEVER_BIN] = NEVER_BIN;
} else if (global_state_.memory_limit_policy == ALLOW_ABSOLUTE_MINIMUM) {
bin_map[NOW_BIN] = NOW_BIN;
bin_map[SOON_BIN] = NEVER_BIN;
bin_map[EVENTUALLY_BIN] = NEVER_BIN;
bin_map[NEVER_BIN] = NEVER_BIN;
} else if (global_state_.memory_limit_policy == ALLOW_PREPAINT_ONLY) {
bin_map[NOW_BIN] = NOW_BIN;
bin_map[SOON_BIN] = SOON_BIN;
bin_map[EVENTUALLY_BIN] = NEVER_BIN;
bin_map[NEVER_BIN] = NEVER_BIN;
} else {
bin_map[NOW_BIN] = NOW_BIN;
bin_map[SOON_BIN] = SOON_BIN;
bin_map[EVENTUALLY_BIN] = EVENTUALLY_BIN;
bin_map[NEVER_BIN] = NEVER_BIN;
}
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = *it;
ManagedTileState& mts = tile->managed_state();
for (int i = 0; i < NUM_BIN_PRIORITIES; ++i)
mts.bin[i] = bin_map[mts.bin[i]];
DidTileBinChange(tile, bin_map[mts.tree_bin[ACTIVE_TREE]], ACTIVE_TREE);
DidTileBinChange(tile, bin_map[mts.tree_bin[PENDING_TREE]], PENDING_TREE);
}
SortTiles();
// Assign gpu memory and determine what tiles need to be rasterized.
AssignGpuMemoryToTiles();
TRACE_EVENT_INSTANT1("cc", "DidManage", "state", ValueToString(AsValue()));
// Finally, kick the rasterizer.
DispatchMoreTasks();
}
void TileManager::CheckForCompletedTileUploads() {
while (!tiles_with_pending_set_pixels_.empty()) {
Tile* tile = tiles_with_pending_set_pixels_.front();
DCHECK(tile->managed_state().resource);
// Set pixel tasks complete in the order they are posted.
if (!resource_pool_->resource_provider()->didSetPixelsComplete(
tile->managed_state().resource->id())) {
break;
}
if (tile->priority(ACTIVE_TREE).distance_to_visible_in_pixels == 0 &&
tile->priority(ACTIVE_TREE).resolution == HIGH_RESOLUTION)
client_->DidUploadVisibleHighResolutionTile();
// It's now safe to release the pixel buffer.
resource_pool_->resource_provider()->releasePixelBuffer(
tile->managed_state().resource->id());
DidFinishTileInitialization(tile);
bytes_pending_set_pixels_ -= tile->bytes_consumed_if_allocated();
DidTileRasterStateChange(tile, IDLE_STATE);
tiles_with_pending_set_pixels_.pop();
}
DispatchMoreTasks();
}
void TileManager::GetMemoryStats(
size_t* memoryRequiredBytes,
size_t* memoryNiceToHaveBytes,
size_t* memoryUsedBytes) const {
*memoryRequiredBytes = 0;
*memoryNiceToHaveBytes = 0;
*memoryUsedBytes = 0;
for(size_t i = 0; i < tiles_.size(); i++) {
const Tile* tile = tiles_[i];
const ManagedTileState& mts = tile->managed_state();
size_t tile_bytes = tile->bytes_consumed_if_allocated();
if (mts.gpu_memmgr_stats_bin == NOW_BIN)
*memoryRequiredBytes += tile_bytes;
if (mts.gpu_memmgr_stats_bin != NEVER_BIN)
*memoryNiceToHaveBytes += tile_bytes;
if (mts.can_use_gpu_memory)
*memoryUsedBytes += tile_bytes;
}
}
scoped_ptr<base::Value> TileManager::AsValue() const {
scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
state->SetInteger("tile_count", tiles_.size());
state->Set("global_state", global_state_.AsValue().release());
state->Set("memory_requirements", GetMemoryRequirementsAsValue().release());
return state.PassAs<base::Value>();
}
scoped_ptr<base::Value> TileManager::GetMemoryRequirementsAsValue() const {
scoped_ptr<base::DictionaryValue> requirements(
new base::DictionaryValue());
size_t memoryRequiredBytes;
size_t memoryNiceToHaveBytes;
size_t memoryUsedBytes;
GetMemoryStats(&memoryRequiredBytes,
&memoryNiceToHaveBytes,
&memoryUsedBytes);
requirements->SetInteger("memory_required_bytes", memoryRequiredBytes);
requirements->SetInteger("memory_nice_to_have_bytes", memoryNiceToHaveBytes);
requirements->SetInteger("memory_used_bytes", memoryUsedBytes);
return requirements.PassAs<base::Value>();
}
void TileManager::GetRenderingStats(RenderingStats* stats) {
raster_worker_pool_->GetRenderingStats(stats);
stats->totalDeferredImageCacheHitCount =
rendering_stats_.totalDeferredImageCacheHitCount;
stats->totalImageGatheringCount = rendering_stats_.totalImageGatheringCount;
stats->totalImageGatheringTime =
rendering_stats_.totalImageGatheringTime;
}
bool TileManager::HasPendingWorkScheduled(WhichTree tree) const {
// Always true when ManageTiles() call is pending.
if (manage_tiles_pending_)
return true;
for (int i = 0; i < NUM_STATES; ++i) {
switch (i) {
case WAITING_FOR_RASTER_STATE:
case RASTER_STATE:
case SET_PIXELS_STATE:
for (int j = 0; j < NEVER_BIN; ++j) {
if (raster_state_count_[i][tree][j])
return true;
}
break;
case IDLE_STATE:
break;
default:
NOTREACHED();
}
}
return false;
}
void TileManager::AssignGpuMemoryToTiles() {
TRACE_EVENT0("cc", "TileManager::AssignGpuMemoryToTiles");
// Some memory cannot be released. Figure out which.
size_t unreleasable_bytes = 0;
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = *it;
if (!tile->managed_state().can_be_freed)
unreleasable_bytes += tile->bytes_consumed_if_allocated();
}
// Now give memory out to the tiles until we're out, and build
// the needs-to-be-rasterized queue.
tiles_that_need_to_be_rasterized_.erase(
tiles_that_need_to_be_rasterized_.begin(),
tiles_that_need_to_be_rasterized_.end());
// Reset the image decoding list so that we don't mess up with tile
// priorities. Tiles will be added to the image decoding list again
// when DispatchMoreTasks() is called.
tiles_with_image_decoding_tasks_.clear();
// By clearing the tiles_that_need_to_be_rasterized_ vector and
// tiles_with_image_decoding_tasks_ list above we move all tiles
// currently waiting for raster to idle state.
// Call DidTileRasterStateChange() for each of these tiles to
// have this state change take effect.
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = *it;
if (tile->managed_state().raster_state == WAITING_FOR_RASTER_STATE)
DidTileRasterStateChange(tile, IDLE_STATE);
}
size_t bytes_left = global_state_.memory_limit_in_bytes - unreleasable_bytes;
size_t bytes_that_exceeded_memory_budget = 0;
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = *it;
size_t tile_bytes = tile->bytes_consumed_if_allocated();
ManagedTileState& managed_tile_state = tile->managed_state();
if (!managed_tile_state.can_be_freed)
continue;
if (managed_tile_state.bin[HIGH_PRIORITY_BIN] == NEVER_BIN &&
managed_tile_state.bin[LOW_PRIORITY_BIN] == NEVER_BIN) {
managed_tile_state.can_use_gpu_memory = false;
FreeResourcesForTile(tile);
continue;
}
if (tile_bytes > bytes_left) {
managed_tile_state.can_use_gpu_memory = false;
bytes_that_exceeded_memory_budget += tile_bytes;
FreeResourcesForTile(tile);
continue;
}
bytes_left -= tile_bytes;
managed_tile_state.can_use_gpu_memory = true;
if (!managed_tile_state.resource &&
!managed_tile_state.resource_is_being_initialized) {
tiles_that_need_to_be_rasterized_.push_back(tile);
DidTileRasterStateChange(tile, WAITING_FOR_RASTER_STATE);
}
}
if (bytes_that_exceeded_memory_budget)
ever_exceeded_memory_budget_ = true;
if (ever_exceeded_memory_budget_) {
TRACE_COUNTER_ID2("cc", "over_memory_budget", this,
"budget", global_state_.memory_limit_in_bytes,
"over", bytes_that_exceeded_memory_budget);
}
// Reverse two tiles_that_need_* vectors such that pop_back gets
// the highest priority tile.
std::reverse(
tiles_that_need_to_be_rasterized_.begin(),
tiles_that_need_to_be_rasterized_.end());
}
void TileManager::FreeResourcesForTile(Tile* tile) {
ManagedTileState& managed_tile_state = tile->managed_state();
DCHECK(managed_tile_state.can_be_freed);
if (managed_tile_state.resource)
resource_pool_->ReleaseResource(managed_tile_state.resource.Pass());
}
bool TileManager::CanDispatchRasterTask(Tile* tile) {
if (raster_worker_pool_->IsBusy())
return false;
size_t new_bytes_pending = bytes_pending_set_pixels_;
new_bytes_pending += tile->bytes_consumed_if_allocated();
return new_bytes_pending <= kMaxPendingUploadBytes;
}
void TileManager::DispatchMoreTasks() {
// Because tiles in the image decoding list have higher priorities, we
// need to process those tiles first before we start to handle the tiles
// in the need_to_be_rasterized queue.
for(TileList::iterator it = tiles_with_image_decoding_tasks_.begin();
it != tiles_with_image_decoding_tasks_.end(); ) {
DispatchImageDecodeTasksForTile(*it);
ManagedTileState& managed_state = (*it)->managed_state();
if (managed_state.pending_pixel_refs.empty()) {
if (!CanDispatchRasterTask(*it))
return;
DispatchOneRasterTask(*it);
tiles_with_image_decoding_tasks_.erase(it++);
} else {
++it;
}
}
// Process all tiles in the need_to_be_rasterized queue. If a tile has
// image decoding tasks, put it to the back of the image decoding list.
while (!tiles_that_need_to_be_rasterized_.empty()) {
Tile* tile = tiles_that_need_to_be_rasterized_.back();
DispatchImageDecodeTasksForTile(tile);
ManagedTileState& managed_state = tile->managed_state();
if (!managed_state.pending_pixel_refs.empty()) {
tiles_with_image_decoding_tasks_.push_back(tile);
} else {
if (!CanDispatchRasterTask(tile))
return;
DispatchOneRasterTask(tile);
}
tiles_that_need_to_be_rasterized_.pop_back();
}
}
void TileManager::GatherPixelRefsForTile(Tile* tile) {
TRACE_EVENT0("cc", "TileManager::GatherPixelRefsForTile");
ManagedTileState& managed_state = tile->managed_state();
if (managed_state.need_to_gather_pixel_refs) {
base::TimeTicks gather_begin_time = base::TimeTicks::Now();
tile->picture_pile()->GatherPixelRefs(
tile->content_rect_,
tile->contents_scale_,
managed_state.pending_pixel_refs);
rendering_stats_.totalImageGatheringCount++;
rendering_stats_.totalImageGatheringTime +=
base::TimeTicks::Now() - gather_begin_time;
managed_state.need_to_gather_pixel_refs = false;
}
}
void TileManager::DispatchImageDecodeTasksForTile(Tile* tile) {
GatherPixelRefsForTile(tile);
std::list<skia::LazyPixelRef*>& pending_pixel_refs =
tile->managed_state().pending_pixel_refs;
std::list<skia::LazyPixelRef*>::iterator it = pending_pixel_refs.begin();
while (it != pending_pixel_refs.end()) {
if (pending_decode_tasks_.end() != pending_decode_tasks_.find(
(*it)->getGenerationID())) {
++it;
continue;
}
// TODO(qinmin): passing correct image size to PrepareToDecode().
if ((*it)->PrepareToDecode(skia::LazyPixelRef::PrepareParams())) {
rendering_stats_.totalDeferredImageCacheHitCount++;
pending_pixel_refs.erase(it++);
} else {
if (raster_worker_pool_->IsBusy())
return;
DispatchOneImageDecodeTask(tile, *it);
++it;
}
}
}
void TileManager::DispatchOneImageDecodeTask(
scoped_refptr<Tile> tile, skia::LazyPixelRef* pixel_ref) {
TRACE_EVENT0("cc", "TileManager::DispatchOneImageDecodeTask");
uint32_t pixel_ref_id = pixel_ref->getGenerationID();
DCHECK(pending_decode_tasks_.end() ==
pending_decode_tasks_.find(pixel_ref_id));
pending_decode_tasks_[pixel_ref_id] = pixel_ref;
raster_worker_pool_->PostImageDecodeTaskAndReply(
pixel_ref,
base::Bind(&TileManager::OnImageDecodeTaskCompleted,
base::Unretained(this),
tile,
pixel_ref_id));
}
void TileManager::OnImageDecodeTaskCompleted(
scoped_refptr<Tile> tile, uint32_t pixel_ref_id) {
TRACE_EVENT0("cc", "TileManager::OnImageDecodeTaskCompleted");
pending_decode_tasks_.erase(pixel_ref_id);
for (TileList::iterator it = tiles_with_image_decoding_tasks_.begin();
it != tiles_with_image_decoding_tasks_.end(); ++it) {
std::list<skia::LazyPixelRef*>& pixel_refs =
(*it)->managed_state().pending_pixel_refs;
for (std::list<skia::LazyPixelRef*>::iterator pixel_it =
pixel_refs.begin(); pixel_it != pixel_refs.end(); ++pixel_it) {
if (pixel_ref_id == (*pixel_it)->getGenerationID()) {
pixel_refs.erase(pixel_it);
break;
}
}
}
DispatchMoreTasks();
}
void TileManager::DispatchOneRasterTask(scoped_refptr<Tile> tile) {
TRACE_EVENT0("cc", "TileManager::DispatchOneRasterTask");
ManagedTileState& managed_tile_state = tile->managed_state();
DCHECK(managed_tile_state.can_use_gpu_memory);
scoped_ptr<ResourcePool::Resource> resource =
resource_pool_->AcquireResource(tile->tile_size_.size(), tile->format_);
resource_pool_->resource_provider()->acquirePixelBuffer(resource->id());
managed_tile_state.resource_is_being_initialized = true;
managed_tile_state.can_be_freed = false;
DidTileRasterStateChange(tile, RASTER_STATE);
ResourceProvider::ResourceId resource_id = resource->id();
raster_worker_pool_->PostRasterTaskAndReply(
tile->picture_pile(),
resource_pool_->resource_provider()->mapPixelBuffer(resource_id),
tile->content_rect_,
tile->contents_scale(),
base::Bind(&TileManager::OnRasterTaskCompleted,
base::Unretained(this),
tile,
base::Passed(&resource),
manage_tiles_call_count_));
}
void TileManager::OnRasterTaskCompleted(
scoped_refptr<Tile> tile,
scoped_ptr<ResourcePool::Resource> resource,
int manage_tiles_call_count_when_dispatched) {
TRACE_EVENT0("cc", "TileManager::OnRasterTaskCompleted");
// Release raster resources.
resource_pool_->resource_provider()->unmapPixelBuffer(resource->id());
ManagedTileState& managed_tile_state = tile->managed_state();
managed_tile_state.can_be_freed = true;
// Tile can be freed after the completion of the raster task. Call
// AssignGpuMemoryToTiles() to re-assign gpu memory to highest priority
// tiles if ManageTiles() was called since task was dispatched. The result
// of this could be that this tile is no longer allowed to use gpu
// memory and in that case we need to abort initialization and free all
// associated resources before calling DispatchMoreTasks().
if (manage_tiles_call_count_when_dispatched != manage_tiles_call_count_)
AssignGpuMemoryToTiles();
// Finish resource initialization if |can_use_gpu_memory| is true.
if (managed_tile_state.can_use_gpu_memory) {
// The component order may be bgra if we're uploading bgra pixels to rgba
// texture. Mark contents as swizzled if image component order is
// different than texture format.
managed_tile_state.contents_swizzled =
!PlatformColor::sameComponentOrder(tile->format_);
// Tile resources can't be freed until upload has completed.
managed_tile_state.can_be_freed = false;
resource_pool_->resource_provider()->beginSetPixels(resource->id());
resource_pool_->resource_provider()->shallowFlushIfSupported();
managed_tile_state.resource = resource.Pass();
bytes_pending_set_pixels_ += tile->bytes_consumed_if_allocated();
DidTileRasterStateChange(tile, SET_PIXELS_STATE);
tiles_with_pending_set_pixels_.push(tile);
} else {
resource_pool_->resource_provider()->releasePixelBuffer(resource->id());
resource_pool_->ReleaseResource(resource.Pass());
managed_tile_state.resource_is_being_initialized = false;
DidTileRasterStateChange(tile, IDLE_STATE);
}
DispatchMoreTasks();
}
void TileManager::DidFinishTileInitialization(Tile* tile) {
ManagedTileState& managed_tile_state = tile->managed_state();
DCHECK(managed_tile_state.resource);
managed_tile_state.resource_is_being_initialized = false;
managed_tile_state.can_be_freed = true;
}
void TileManager::DidTileRasterStateChange(Tile* tile, TileRasterState state) {
ManagedTileState& mts = tile->managed_state();
DCHECK_LT(state, NUM_STATES);
for (int i = 0; i < NUM_TREES; ++i) {
// Decrement count for current state.
--raster_state_count_[mts.raster_state][i][mts.tree_bin[i]];
DCHECK_GE(raster_state_count_[mts.raster_state][i][mts.tree_bin[i]], 0);
// Increment count for new state.
++raster_state_count_[state][i][mts.tree_bin[i]];
}
mts.raster_state = state;
}
void TileManager::DidTileBinChange(Tile* tile,
TileManagerBin bin,
WhichTree tree) {
ManagedTileState& mts = tile->managed_state();
// Decrement count for current bin.
--raster_state_count_[mts.raster_state][tree][mts.tree_bin[tree]];
DCHECK_GE(raster_state_count_[mts.raster_state][tree][mts.tree_bin[tree]], 0);
// Increment count for new bin.
++raster_state_count_[mts.raster_state][tree][bin];
mts.tree_bin[tree] = bin;
}
} // namespace cc