站在老罗的肩膀上:https://blog.youkuaiyun.com/luoshengyang/article/details/51348829
CC Layer Tree 的绘制指令存储在displayitem的paint_op_buffer_中,要将绘制指令打包送入GPU,如下:
开始准备光栅化的条件
bool SchedulerStateMachine::ShouldPrepareTiles() const {
// In full-pipeline mode, we need to prepare tiles ASAP to ensure that we
// don't get stuck.
if (settings_.wait_for_all_pipeline_stages_before_draw)
return needs_prepare_tiles_;
// Do not prepare tiles if we've already done so in commit or impl side
// invalidation.
if (did_prepare_tiles_)
return false;
// Limiting to once per-frame is not enough, since we only want to prepare
// tiles _after_ draws.
if (begin_impl_frame_state_ != BeginImplFrameState::INSIDE_DEADLINE)
return false;
return needs_prepare_tiles_;
}
开始准备光栅化:
void ProxyImpl::ScheduledActionPrepareTiles() {
host_impl_->PrepareTiles();
}
bool LayerTreeHostImpl::PrepareTiles() {
if (!tile_priorities_dirty_)
return false;
client_->WillPrepareTiles();
bool did_prepare_tiles = tile_manager_.PrepareTiles(global_tile_state_);
if (did_prepare_tiles)
tile_priorities_dirty_ = false;
client_->DidPrepareTiles();
return did_prepare_tiles;
}
bool TileManager::PrepareTiles(
const GlobalStateThatImpactsTilePriority& state) {
++prepare_tiles_count_;
...
signals_ = Signals();
global_state_ = state;
...
// update completed tasks, useful in updating Task Graph in later scheduling
if (!did_check_for_completed_tasks_since_last_schedule_tasks_) {
tile_task_manager_->CheckForCompletedTasks();
did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
}
...
PrioritizedWorkToSchedule prioritized_work = AssignGpuMemoryToTiles();
client_->SetIsLikelyToRequireADraw(
!prioritized_work.tiles_to_raster.empty() &&
prioritized_work.tiles_to_raster.front().tile()->required_for_draw());
// Schedule tile tasks.
ScheduleTasks(std::move(prioritized_work));
}
TileManager类的成员函数ManageTiles主要是做两件事情:
1. 调用成员函数AssignGpuMemoryToTiles根据优先级对网页中的分块进行排序,根据上述排序以及GPU内存策略获取当前需要执行光栅化操作的分块。
2. 调用成员函数ScheduleTasks对第2步获得的分块执行光栅化操作。
TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() {
...
// build priorities and sort for tiles
std::unique_ptr<RasterTilePriorityQueue> raster_priority_queue(
client_->BuildRasterQueue(global_state_.tree_priority,
RasterTilePriorityQueue::Type::ALL));
std::unique_ptr<EvictionTilePriorityQueue> eviction_priority_queue;
PrioritizedWorkToSchedule work_to_schedule;
for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
const PrioritizedTile& prioritized_tile = raster_priority_queue->Top();
Tile* tile = prioritized_tile.tile();
TilePriority priority = prioritized_tile.priority();
bool tile_is_needed_now = priority.priority_bin == TilePriority::NOW;
if (!tile->is_solid_color_analysis_performed() &&
tile->use_picture_analysis() && kUseColorEstimator) {
tile->set_solid_color_analysis_performed(true);
SkColor color = SK_ColorTRANSPARENT;
bool is_solid_color =
prioritized_tile.raster_source()->PerformSolidColorAnalysis(
tile->enclosing_layer_rect(), &color);
if (is_solid_color) {
tile->draw_info().set_solid_color(color);
client_->NotifyTileStateChanged(tile);
continue;
}
}
// Prepaint tiles that are far away are only processed for images.
if (tile->is_prepaint() && prioritized_tile.is_process_for_images_only()) {
work_to_schedule.tiles_to_process_for_images.push_back(prioritized_tile);
continue;
}
if (!tile->draw_info().NeedsRaster()) {
AddCheckeredImagesToDecodeQueue(
prioritized_tile, raster_color_space.color_space,
CheckerImageTracker::DecodeType::kRaster,
&work_to_schedule.checker_image_decode_queue);
continue;
}
// We won't be able to schedule this tile, so break out early.
if (work_to_schedule.tiles_to_raster.size() >=
scheduled_raster_task_limit_) {
all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
break;
}
MemoryUsage memory_required_by_tile_to_be_scheduled;
if (!tile->raster_task_.get()) {
memory_required_by_tile_to_be_scheduled = MemoryUsage::FromConfig(
tile->desired_texture_size(), DetermineResourceFormat(tile));
}
// This is the memory limit that will be used by this tile. Depending on
// the tile priority, it will be one of hard_memory_limit or
// soft_memory_limit.
MemoryUsage& tile_memory_limit =
tile_is_needed_now ? hard_memory_limit : soft_memory_limit;
const MemoryUsage& scheduled_tile_memory_limit =
tile_memory_limit - memory_required_by_tile_to_be_scheduled;
eviction_priority_queue =
FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
std::move(eviction_priority_queue), scheduled_tile_memory_limit,
priority, &memory_usage);
bool memory_usage_is_within_limit =
!memory_usage.Exceeds(scheduled_tile_memory_limit);
// If we couldn't fit the tile into our current memory limit, then we're
// done.
if (!memory_usage_is_within_limit) {
if (tile_is_needed_now)
had_enough_memory_to_schedule_tiles_needed_now = false;
all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
break;
}
if (tile->HasRasterTask()) {
if (tile->raster_task_scheduled_with_checker_images() &&
prioritized_tile.should_decode_checkered_images_for_tile()) {
AddCheckeredImagesToDecodeQueue(
prioritized_tile, raster_color_space.color_space,
CheckerImageTracker::DecodeType::kRaster,
&work_to_schedule.checker_image_decode_queue);
}
} else {
// Creating the raster task here will acquire resources, but
// this resource usage has already been accounted for above.
auto raster_task = CreateRasterTask(
prioritized_tile, client_->GetRasterColorSpace(), &work_to_schedule);
if (!raster_task) {
continue;
}
tile->raster_task_ = std::move(raster_task);
}
tile->scheduled_priority_ = schedule_priority++;
memory_usage += memory_required_by_tile_to_be_scheduled;
work_to_schedule.tiles_to_raster.push_back(prioritized_tile);
}
eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit(
std::move(eviction_priority_queue), hard_memory_limit, &memory_usage);
if (!had_enough_memory_to_schedule_tiles_needed_now &&
num_of_tiles_with_checker_images_ > 0) {
for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
const PrioritizedTile& prioritized_tile = raster_priority_queue->Top();
if (prioritized_tile.priority().priority_bin > TilePriority::NOW)
break;
if (!prioritized_tile.should_decode_checkered_images_for_tile())
continue;
Tile* tile = prioritized_tile.tile();
if (tile->draw_info().is_checker_imaged() ||
tile->raster_task_scheduled_with_checker_images()) {
AddCheckeredImagesToDecodeQueue(
prioritized_tile, raster_color_space.color_space,
CheckerImageTracker::DecodeType::kRaster,
&work_to_schedule.checker_image_decode_queue);
}
}
}
...
}
首先计算剩余可用的光栅化内存字节数以及光栅化资源个数。CC模块限制了光栅化内存字节数上限和光栅化资源个数(Num Resources Limit)。其中,光栅化内存字节数又分为软限制(Soft Memory Limit)和硬限制(Hard Memory Limit)两种,它们的作用如图2所示:
图2 Soft Memory Limit和Hard Memory Limit
当前可见的分块,也就是处于NOW_BIN中的分块,它们的可用的内存上限是Hard Memory Limit,其余的分块可用的内存上限是Soft Memory Limit。通过这种策略一方面可以最大程度保证可见分块都可以得到光栅化,另一方面又可以在可见分块占用内存不多的情况下,对其它的分块提前进行光栅化,这样等到它们变为可见时,就可以马上显示出来。
Soft Memory Limit、Hard Memory Limit和Num Resources Limit值分别保存在global_state_.soft_memory_limit_in_bytes、global_state_.hard_memory_limit_in_bytes和global_state_.num_resources_limit。其中,global_state_.num_resources_limit的值设置为10000000(没找到,不确定)。
当网页使用硬件加速方式渲染时,global_state_.hard_memory_limit_in_bytes的值可以通过启动选项force-gpu-mem-available-mb指定,但是会被限制在16M到256M之间。当网页使用软件方式渲染时,global_state_.hard_memory_limit_in_bytes的值会被设置为128M。
在当前的实现中,global_state_.soft_memory_limit_in_bytes的值与global_state_.hard_memory_limit_in_bytes是相等的。
目前分块正在使用的光栅化内存字节数可以通过调用TileManager类的成员变量resource_pool_指向的一个ResourcePool::resource_count获得。另外,也可以获得当前正在使用的光栅化资源个数。
有了上述数据之后,本来就可以计算出剩余可用的光栅化内存字节数和光栅化资源个数。例如,剩余可用的Soft类型的光栅化内存字节数就等于(global_state_.soft_memory_limit_in_bytes - resource_pool_->memory_usage_bytes())。不过我们看到,按照前面的方式计算出来的字节数,再加上TileManager类的成员变量bytes_releasable_的值,才是接下来使用的剩余可用的Soft类型的光栅化内存字节数soft_bytes_left。剩余可用的Hard类型的光栅化内存字节数hard_bytes_left和光栅化资源个数resources_left的计算过程