blob: 42b7ea331dd724eccc5b267888f4c93df9a66bcd [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 <set>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "base/threading/thread.h"
#include "cc/platform_color.h"
#include "cc/rendering_stats.h"
#include "cc/resource_pool.h"
#include "cc/switches.h"
#include "cc/tile.h"
#include "third_party/skia/include/core/SkDevice.h"
namespace {
const char* kRasterThreadNamePrefix = "CompositorRaster";
const int kMaxRasterThreads = 64;
const int kDefaultNumberOfRasterThreads = 1;
// Allow two pending raster tasks per thread. This keeps resource usage
// low while making sure raster threads aren't unnecessarily idle.
const int kNumPendingRasterTasksPerThread = 2;
// Determine bin based on three categories of tiles: things we need now,
// things we need soon, and eventually.
cc::TileManagerBin BinFromTilePriority(const cc::TilePriority& prio) {
// 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;
if (prio.time_to_needed_in_seconds() == std::numeric_limits<float>::max())
return cc::NEVER_BIN;
if (prio.resolution == cc::NON_IDEAL_RESOLUTION)
return cc::EVENTUALLY_BIN;
if (prio.time_to_needed_in_seconds() == 0 ||
prio.distance_to_visible_in_pixels < backfling_guard_distance_pixels)
return cc::NOW_BIN;
if (prio.time_to_needed_in_seconds() < prepainting_window_time_seconds)
return cc::SOON_BIN;
return cc::EVENTUALLY_BIN;
}
} // namespace
namespace cc {
class RasterThread : public base::Thread {
public:
RasterThread(const std::string name)
: base::Thread(name.c_str()),
num_pending_tasks_(0) {
Start();
}
virtual ~RasterThread() {
Stop();
}
int num_pending_tasks() { return num_pending_tasks_; }
void PostRasterTaskAndReply(const tracked_objects::Location& from_here,
PicturePileImpl* picture_pile,
uint8_t* mapped_buffer,
const gfx::Rect& rect,
float contents_scale,
RenderingStats* stats,
const base::Closure& reply) {
++num_pending_tasks_;
message_loop_proxy()->PostTaskAndReply(
from_here,
base::Bind(&RunRasterTask,
base::Unretained(picture_pile),
mapped_buffer,
rect,
contents_scale,
stats),
base::Bind(&RasterThread::RunReply, base::Unretained(this), reply));
}
void PostImageDecodingTaskAndReply(const tracked_objects::Location& from_here,
skia::LazyPixelRef* pixel_ref,
RenderingStats* stats,
const base::Closure& reply) {
++num_pending_tasks_;
message_loop_proxy()->PostTaskAndReply(
from_here,
base::Bind(&RunImageDecodeTask, pixel_ref, stats),
base::Bind(&RasterThread::RunReply, base::Unretained(this), reply));
}
private:
static void RunRasterTask(PicturePileImpl* picture_pile,
uint8_t* mapped_buffer,
const gfx::Rect& rect,
float contents_scale,
RenderingStats* stats) {
TRACE_EVENT0("cc", "RasterThread::RunRasterTask");
DCHECK(picture_pile);
DCHECK(mapped_buffer);
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height());
bitmap.setPixels(mapped_buffer);
SkDevice device(bitmap);
SkCanvas canvas(&device);
picture_pile->Raster(
&canvas,
rect,
contents_scale,
stats);
}
static void RunImageDecodeTask(skia::LazyPixelRef* pixel_ref,
RenderingStats* stats) {
TRACE_EVENT0("cc", "RasterThread::RunImageDecodeTask");
base::TimeTicks decodeBeginTime = base::TimeTicks::Now();
pixel_ref->Decode();
stats->totalDeferredImageDecodeTimeInSeconds +=
(base::TimeTicks::Now() - decodeBeginTime).InSecondsF();
}
void RunReply(const base::Closure& reply) {
--num_pending_tasks_;
reply.Run();
}
int num_pending_tasks_;
DISALLOW_COPY_AND_ASSIGN(RasterThread);
};
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) {
}
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)),
manage_tiles_pending_(false),
manage_tiles_call_count_(0),
check_for_completed_set_pixels_pending_(false) {
// Initialize all threads.
const std::string thread_name_prefix = kRasterThreadNamePrefix;
while (raster_threads_.size() < num_raster_threads) {
int thread_number = raster_threads_.size() + 1;
scoped_ptr<RasterThread> thread = make_scoped_ptr(
new RasterThread(thread_name_prefix +
StringPrintf("Worker%d", thread_number).c_str()));
raster_threads_.append(thread.Pass());
}
ResetBinCounts();
}
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 raster tasks and release any
// uninitialized resources.
raster_threads_.clear();
ManageTiles();
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);
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) {
FreeResourcesForTile(tile);
tiles_.erase(it);
return;
}
}
DCHECK(false) << "Could not find tile version.";
}
void TileManager::WillModifyTilePriority(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;
}
void TileManager::ScheduleCheckForCompletedSetPixels() {
if (check_for_completed_set_pixels_pending_)
return;
client_->ScheduleCheckForCompletedSetPixels();
check_for_completed_set_pixels_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.raster_bin != bms.raster_bin)
return ams.raster_bin < bms.raster_bin;
if (ams.resolution != bms.resolution)
return ams.resolution < ams.resolution;
return
ams.time_to_needed_in_seconds <
bms.time_to_needed_in_seconds;
}
};
void TileManager::ManageTiles() {
TRACE_EVENT0("cc", "TileManager::ManageTiles");
manage_tiles_pending_ = false;
++manage_tiles_call_count_;
const bool smoothness_takes_priority =
global_state_.smoothness_takes_priority;
// 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();
mts.bin[ACTIVE_TREE] = BinFromTilePriority(tile->priority(ACTIVE_TREE));
mts.bin[PENDING_TREE] = BinFromTilePriority(tile->priority(PENDING_TREE));
TilePriority prio;
if (smoothness_takes_priority)
prio = tile->priority(ACTIVE_TREE);
else
prio = tile->combined_priority();
mts.resolution = prio.resolution;
mts.time_to_needed_in_seconds = prio.time_to_needed_in_seconds();
mts.raster_bin = BinFromTilePriority(prio);
}
// 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();
mts.bin[ACTIVE_TREE] = bin_map[mts.bin[ACTIVE_TREE]];
mts.bin[PENDING_TREE] = bin_map[mts.bin[PENDING_TREE]];
mts.raster_bin = bin_map[mts.raster_bin];
}
// Update bin counts.
ResetBinCounts();
for (TileVector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = *it;
ManagedTileState& mts = tile->managed_state();
for (int i = 0; i < NUM_TREES; ++i)
tiles_in_bin_count_[mts.bin[i]][i]++;
// Increment drawable count if GetResourceId() doesn't return 0.
if (tile->GetResourceId()) {
for (int i = 0; i < NUM_TREES; ++i)
drawable_tiles_in_bin_count_[mts.bin[i]][i]++;
}
}
// Sort by bin.
std::sort(tiles_.begin(), tiles_.end(), BinComparator());
// Assign gpu memory and determine what tiles need to be rasterized.
AssignGpuMemoryToTiles();
// Finally, kick the rasterizer.
DispatchMoreTasks();
}
void TileManager::CheckForCompletedSetPixels() {
check_for_completed_set_pixels_pending_ = false;
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())) {
ScheduleCheckForCompletedSetPixels();
break;
}
// It's now safe to release the pixel buffer.
resource_pool_->resource_provider()->releasePixelBuffer(
tile->managed_state().resource->id());
DidFinishTileInitialization(tile);
tiles_with_pending_set_pixels_.pop();
}
}
void TileManager::GetRenderingStats(RenderingStats* stats) {
stats->totalRasterizeTimeInSeconds =
rendering_stats_.totalRasterizeTimeInSeconds;
stats->totalPixelsRasterized = rendering_stats_.totalPixelsRasterized;
stats->totalDeferredImageDecodeCount =
rendering_stats_.totalDeferredImageDecodeCount;
stats->totalDeferredImageCacheHitCount =
rendering_stats_.totalDeferredImageCacheHitCount;
stats->totalImageGatheringCount = rendering_stats_.totalImageGatheringCount;
stats->totalDeferredImageDecodeTimeInSeconds =
rendering_stats_.totalDeferredImageDecodeTimeInSeconds;
stats->totalImageGatheringTimeInSeconds =
rendering_stats_.totalImageGatheringTimeInSeconds;
}
int TileManager::GetTilesInBinCount(TileManagerBin bin, WhichTree tree) {
DCHECK(bin >= 0);
DCHECK(bin < NUM_BINS);
DCHECK(tree >= 0);
DCHECK(tree < NUM_TREES);
return tiles_in_bin_count_[bin][tree];
}
int TileManager::GetDrawableTilesInBinCount(
TileManagerBin bin, WhichTree tree) {
DCHECK(bin >= 0);
DCHECK(bin < NUM_BINS);
DCHECK(tree >= 0);
DCHECK(tree < NUM_TREES);
return drawable_tiles_in_bin_count_[bin][tree];
}
void TileManager::ResetBinCounts() {
for (int i = 0; i < NUM_BINS; ++i)
for (int j = 0; j < NUM_TREES; ++j)
tiles_in_bin_count_[i][j] = drawable_tiles_in_bin_count_[i][j] = 0;
}
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();
size_t bytes_left = global_state_.memory_limit_in_bytes - unreleasable_bytes;
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.raster_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;
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);
}
// 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());
}
RasterThread* TileManager::GetFreeRasterThread() {
RasterThread* thread = 0;
for (RasterThreadVector::iterator it = raster_threads_.begin();
it != raster_threads_.end(); ++it) {
if ((*it)->num_pending_tasks() == kNumPendingRasterTasksPerThread)
continue;
// Check if this is the best thread we've found so far.
if (!thread || (*it)->num_pending_tasks() < thread->num_pending_tasks())
thread = *it;
}
return thread;
}
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.
std::list<Tile*>::iterator it = tiles_with_image_decoding_tasks_.begin();
while (it != tiles_with_image_decoding_tasks_.end()) {
DispatchImageDecodingTasksForTile(*it);
ManagedTileState& managed_state = (*it)->managed_state();
if (managed_state.pending_pixel_refs.empty()) {
RasterThread* thread = GetFreeRasterThread();
if (!thread)
return;
DispatchOneRasterTask(thread, *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();
DispatchImageDecodingTasksForTile(tile);
ManagedTileState& managed_state = tile->managed_state();
if (!managed_state.pending_pixel_refs.empty()) {
tiles_with_image_decoding_tasks_.push_back(tile);
} else {
RasterThread* thread = GetFreeRasterThread();
if (!thread)
return;
DispatchOneRasterTask(thread, 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();
const_cast<PicturePileImpl *>(tile->picture_pile())->GatherPixelRefs(
tile->content_rect_, managed_state.pending_pixel_refs);
rendering_stats_.totalImageGatheringCount++;
rendering_stats_.totalImageGatheringTimeInSeconds +=
(base::TimeTicks::Now() - gather_begin_time).InSecondsF();
managed_state.need_to_gather_pixel_refs = false;
}
}
void TileManager::DispatchImageDecodingTasksForTile(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 {
RasterThread* thread = GetFreeRasterThread();
if (!thread)
return;
DispatchOneImageDecodingTask(thread, tile, *it);
++it;
}
}
}
void TileManager::DispatchOneImageDecodingTask(RasterThread* thread,
scoped_refptr<Tile> tile,
skia::LazyPixelRef* pixel_ref) {
TRACE_EVENT0("cc", "TileManager::DispatchOneImageDecodingTask");
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;
RenderingStats* stats = new RenderingStats();
thread->PostImageDecodingTaskAndReply(
FROM_HERE,
pixel_ref,
stats,
base::Bind(&TileManager::OnImageDecodingTaskCompleted,
base::Unretained(this),
tile,
pixel_ref_id,
stats));
}
void TileManager::OnImageDecodingTaskCompleted(scoped_refptr<Tile> tile,
uint32_t pixel_ref_id,
RenderingStats* stats) {
TRACE_EVENT0("cc", "TileManager::OnImageDecoded");
pending_decode_tasks_.erase(pixel_ref_id);
rendering_stats_.totalDeferredImageDecodeTimeInSeconds +=
stats->totalDeferredImageDecodeTimeInSeconds;
rendering_stats_.totalDeferredImageDecodeCount++;
delete stats;
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(
RasterThread* thread, 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;
ResourceProvider::ResourceId resource_id = resource->id();
scoped_refptr<PicturePileImpl> picture_pile_clone =
tile->picture_pile()->GetCloneForDrawingOnThread(thread);
RenderingStats* stats = new RenderingStats();
thread->PostRasterTaskAndReply(
FROM_HERE,
picture_pile_clone.get(),
resource_pool_->resource_provider()->mapPixelBuffer(resource_id),
tile->content_rect_,
tile->contents_scale(),
stats,
base::Bind(&TileManager::OnRasterTaskCompleted,
base::Unretained(this),
tile,
base::Passed(&resource),
picture_pile_clone,
manage_tiles_call_count_,
stats));
}
void TileManager::OnRasterTaskCompleted(
scoped_refptr<Tile> tile,
scoped_ptr<ResourcePool::Resource> resource,
scoped_refptr<PicturePileImpl> picture_pile_clone,
int manage_tiles_call_count_when_dispatched,
RenderingStats* stats) {
TRACE_EVENT0("cc", "TileManager::OnRasterTaskCompleted");
rendering_stats_.totalRasterizeTimeInSeconds +=
stats->totalRasterizeTimeInSeconds;
rendering_stats_.totalPixelsRasterized += stats->totalPixelsRasterized;
delete stats;
// 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());
managed_tile_state.resource = resource.Pass();
tiles_with_pending_set_pixels_.push(tile);
ScheduleCheckForCompletedSetPixels();
} else {
resource_pool_->resource_provider()->releasePixelBuffer(resource->id());
resource_pool_->ReleaseResource(resource.Pass());
managed_tile_state.resource_is_being_initialized = false;
}
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;
for (int i = 0; i < NUM_TREES; ++i)
drawable_tiles_in_bin_count_[managed_tile_state.bin[i]][i]++;
}
}