Chromium网页光栅化过程分析
Posted 罗升阳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chromium网页光栅化过程分析相关的知识,希望对你有一定的参考价值。
在前面一篇文章中,我们分析了网页CC Layer Tree同步为CC Pending Layer Tree的过程。同步操作完成后,CC Pending Layer Tree中的每一个Layer都会被划分成一系列的分块,并且每一个分块都会被赋予一个优先级。接下来CC模块会根据优先级对分块进行排序。优先级越高的分块越排在前面,越排在前面的分块就越快得到光栅化。本文接下来就详细分析网页分块的光栅化过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
《android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
CC Pending Layer Tree的光栅化操作是由调度器发起的,它对应于网页渲染过程中的第4个步骤ACTION_MANAGE_TILES,如下所示:
图1 CC Pending Layer Tree光栅化操作的时机
从前面Chromium网页渲染调度器(Scheduler)实现分析一文可以知道,当调度器调用SchedulerStateMachine类的成员函数NextAction询问状态机下一步要执行的操作时,SchedulerStateMachine类的成员函数NextAction会调用另外一个成员函数ShouldManageTiles。当SchedulerStateMachine类的成员函数ShouldManageTiles返回值等于true的时候,状态机就会提示调度器接下来需要执行ACTION_MANAGE_TILES操作,也就是对CC Pending Layer Tree进行光栅化操作,如下所示:
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
......
if (ShouldManageTiles())
return ACTION_MANAGE_TILES;
......
return ACTION_NONE;
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
接下来我们就继续分析SchedulerStateMachine类的成员函数ShouldManageTiles什么情况下会返回true,它的实现如下所示:
bool SchedulerStateMachine::ShouldManageTiles() const {
// ManageTiles only really needs to be called immediately after commit
// and then periodically after that. Use a funnel to make sure we average
// one ManageTiles per BeginImplFrame in the long run.
if (manage_tiles_funnel_ > 0)
return false;
// Limiting to once per-frame is not enough, since we only want to
// manage tiles _after_ draws. Polling for draw triggers and
// begin-frame are mutually exclusive, so we limit to these two cases.
if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
!inside_poll_for_anticipated_draw_triggers_)
return false;
return needs_manage_tiles_;
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
每次执行了一个ACTION_MANAGE_TILES操作时,SchedulerStateMachine类的成员变量manage_tiles_funnel_的值就会增加1。等到下一个VSync信号到来时,SchedulerStateMachine类的成员变量manage_tiles_funnel_的值才会减少1。SchedulerStateMachine类通过这种方式控制在一个VSync周期内,只能执行一次ACTION_MANAGE_TILES操作,也就是当成员变量manage_tiles_funnel_的值大于0时,SchedulerStateMachine类的成员函数ShouldManageTiles的返回值会等于false,表示不能在当前VSync周期内再次执行ACTION_MANAGE_TILES操作。
除了控制一个VSync周期只执行一次ACTION_MANAGE_TILES操作,SchedulerStateMachine类还控制ACTION_MANAGE_TILES操作的执行时间点。在一个VSync周期内,只有当状态机的BeginImplFrameState状态等于BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE时,也就是成员变量begin_impl_frame_state_的值等于BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE时,SchedulerStateMachine类的成员函数ShouldManageTiles才允许执行ACTION_MANAGE_TILES操作。关于状态机的BeginImplFrameState状态,可以参考前面Chromium网页渲染调度器(Scheduler)实现分析一文。
另外,如果调度器启动了Polling机制推进渲染管线,那么在Polling期间,SchedulerStateMachine类的成员变量inside_poll_for_anticipated_draw_triggers_会被设置为true,表示允许执行ACTION_MANAGE_TILES操作的。Polling是一种不依赖于VSync信号的机制,我们在前面Chromium网页渲染调度器(Scheduler)实现分析一文中有提及到。
最后,在以上描述的条件成立的前提下,如果网页的分块发生了变化,例如新创建了分块、分块优先级发生了变化等,那么SchedulerStateMachine类的成员函数ShouldManageTiles的返回值就会等于true,表示要执行一个ACTION_MANAGE_TILES操作。
从前面Chromium网页Layer Tree同步为Pending Layer Tree的过程分析一文可以知道,当网页的分块发生了变化时,SchedulerStateMachine类的成员变量needs_manage_tiles_就会被设置为true,这样就会导致SchedulerStateMachine类的成员函数ShouldManageTiles的返回值为true。
回到SchedulerStateMachine类的成员函数NextAction中,当它调用成员函数ShouldManageTiles得到的返回值等于true,它就会返回一个ACTION_MANAGE_TILES给Scheduler类的成员函数ProcessScheduledActions。这时候Scheduler类的成员函数ProcessScheduledActions就会请求Compositor线程对网页中的分块执行光栅化操作,如下所示:
void Scheduler::ProcessScheduledActions() {
......
SchedulerStateMachine::Action action;
do {
action = state_machine_.NextAction();
......
state_machine_.UpdateState(action);
......
switch (action) {
......
case SchedulerStateMachine::ACTION_MANAGE_TILES:
client_->ScheduledActionManageTiles();
break;
}
} while (action != SchedulerStateMachine::ACTION_NONE);
SetupNextBeginFrameIfNeeded();
......
if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
......
ScheduleBeginImplFrameDeadline(base::TimeTicks());
}
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler.cc中。
Scheduler类的成员函数ProcessScheduledActions的详细分析可以参考前面Chromium网页渲染调度器(Scheduler)实现分析一文。这时候Scheduler类的成员函数ProcessScheduledActions首先调用SchedulerStateMachine类的成员函数UpdateState更新状态机的状态,接着再调用成员变量client_指向的一个ThreadProxy对象的成员函数ScheduledActionManageTiles请求Compositor线程对网页中的分块执行光栅化操作。
SchedulerStateMachine类的成员函数UpdateState的实现如下所示:
void SchedulerStateMachine::UpdateState(Action action) {
switch (action) {
......
case ACTION_MANAGE_TILES:
UpdateStateOnManageTiles();
return;
}
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
SchedulerStateMachine类的成员函数UpdateState调用另外一个成员函数UpdateStateOnManageTiles将成员变量needs_manage_tiles_的值设置为false,表示网页上一次发生的分块变化已经得到了处理,如下所示:
void SchedulerStateMachine::UpdateStateOnManageTiles() {
needs_manage_tiles_ = false;
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
回到Scheduler类的成员函数ProcessScheduledActions中,它修改了状态机的状态之后,接下来调用ThreadProxy类的成员函数ScheduledActionManageTiles请求Compositor线程对网页中的分块执行光栅化操作,如下所示:
void ThreadProxy::ScheduledActionManageTiles() {
......
impl().layer_tree_host_impl->ManageTiles();
}
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。
ThreadProxy类的成员函数ScheduledActionCommit首先调用成员函数impl获得一个CompositorThreadOnly对象。这个CompositorThreadOnly对象的成员变量layer_tree_host_impl指向一个LayerTreeHostImpl对象。这个LayerTreeHostImpl对象负责管理CC Pending Layer Tree和CC Active Layer Tree。有了这个LayerTreeHostImpl对象之后,ThreadProxy类的成员函数ScheduledActionCommit再调用它的成员函数ManageTiles对网页中的分块执行光栅化操作。
LayerTreeHostImpl类的成员函数ManageTiles的实现如下所示:
void LayerTreeHostImpl::ManageTiles() {
if (!tile_manager_)
return;
if (!tile_priorities_dirty_)
return;
tile_priorities_dirty_ = false;
tile_manager_->ManageTiles(global_tile_state_);
......
}
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。
从前面Chromium网页绘图表面(Output Surface)创建过程分析一文可以知道,LayerTreeHostImpl类的成员变量tile_manager_指向的是一个TileManager对象。这个TileManager对象是负责管理网页的分块的。
从前面Chromium网页Layer Tree同步为Pending Layer Tree的过程分析一文又可以知道,当LayerTreeHostImpl类的成员变量tile_priorities_dirty_的值等于true时,就表示网页的分块发生了变化。
只有在负责管理网页分块的TileManager对象已经创建,并且网页分块发生过变化的情况下,LayerTreeHostImpl类的成员函数ManageTiles才会调用TileManager类的成员函数ManageTiles对网页分块执行光栅化操作。
TileManager类的成员函数ManageTiles的实现如下所示:
void TileManager::ManageTiles(const GlobalStateThatImpactsTilePriority& state) {
......
UpdatePrioritizedTileSetIfNeeded();
TileVector tiles_that_need_to_be_rasterized;
AssignGpuMemoryToTiles(&prioritized_tiles_,
&tiles_that_need_to_be_rasterized);
// Finally, schedule rasterizer tasks.
ScheduleTasks(tiles_that_need_to_be_rasterized);
......
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
TileManager类的成员函数ManageTiles主要是做三件事情:
1. 调用成员函数UpdatePrioritizedTileSetIfNeeded根据优先级对网页中的分块进行排序。
2. 调用成员函数AssignGpuMemoryToTiles根据上述排序以及GPU内存策略获取当前需要执行光栅化操作的分块。
3. 调用成员函数ScheduleTasks对第2步获得的分块执行光栅化操作。
接下来我们就分别分析这三个成员函数的实现,以便了解网页分块的光栅化过程。
TileManager类的成员函数UpdatePrioritizedTileSetIfNeeded的实现如下所示:
void TileManager::UpdatePrioritizedTileSetIfNeeded() {
if (!prioritized_tiles_dirty_)
return;
CleanUpReleasedTiles();
prioritized_tiles_.Clear();
GetTilesWithAssignedBins(&prioritized_tiles_);
prioritized_tiles_dirty_ = false;
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
从前面Chromium网页Layer Tree同步为Pending Layer Tree的过程分析一文可以知道,当TileManager类的成员变量prioritized_tiles_dirty_的值等于true时,就表示网页的分块发生了变化,这时候TileManager类的成员函数UpdatePrioritizedTileSetIfNeeded就会对网页的所有分块重新进行排序。
在对分块重新进行排序之前,TileManager类的成员函数UpdatePrioritizedTileSetIfNeeded会先调用成员函数CleanUpReleasedTiles回收那些不再使用了的分块占用的资源,如下所示:
void TileManager::CleanUpReleasedTiles() {
for (std::vector<Tile*>::iterator it = released_tiles_.begin();
it != released_tiles_.end();
++it) {
Tile* tile = *it;
ManagedTileState& mts = tile->managed_state();
for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) {
FreeResourceForTile(tile, static_cast<RasterMode>(mode));
......
}
......
tiles_.erase(tile->id());
......
delete tile;
}
released_tiles_.clear();
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
不再使用的分块都保存在TileManager类的成员变量released_tiles_描述的一个std::vector中。TileManager类的成员函数CleanUpReleasedTiles遍历保存在这个std::vector中的每一个分块。对于每一个分块,首先调用TileManager类的成员函数FreeResourceForTile释放它所占用的资源,接着又将它从TileManager类的成员变量tiles_描述的一个Tile Map中移除,最后删除用来描述该分块的Tile对象。遍历完毕,TileManager类的成员函数CleanUpReleasedTiles就清空成员变量released_tiles_描述的std::vector。
一个分块有可能是按照High Res模式进行光栅化,也有可能按照Low Res模式进行光栅化。这些光栅化模式由一个ManagedTileState对象进行管理。通过调用Tile类的成员函数managed_state可以获得这个ManagedTileState对象,如下所示:
class CC_EXPORT Tile : public RefCountedManaged<Tile> {
......
private:
......
ManagedTileState& managed_state() { return managed_state_; }
......
ManagedTileState managed_state_;
......
};
这个函数定义在文件external/chromium_org/cc/resources/tile.h中。
ManagedTileState类有一个成员变量tile_versions,它是一个大小为2的TileVersion数组,如下所示:
class CC_EXPORT ManagedTileState {
public:
class CC_EXPORT TileVersion {
public:
enum Mode { RESOURCE_MODE, SOLID_COLOR_MODE, PICTURE_PILE_MODE };
......
private:
......
Mode mode_;
SkColor solid_color_;
scoped_ptr<ScopedResource> resource_;
scoped_refptr<RasterTask> raster_task_;
};
......
TileVersion tile_versions[NUM_RASTER_MODES];
RasterMode raster_mode;
TileResolution resolution;
bool required_for_activation;
TilePriority::PriorityBin priority_bin;
float distance_to_visible;
bool visible_and_ready_to_draw;
......
};
这个类定义在文件external/chromium_org/cc/resources/managed_tile_state.h中。
NUM_RASTER_MODES是一个类型为RasterMode的枚举值,它的定义如下所示:
enum RasterMode {
HIGH_QUALITY_RASTER_MODE = 0,
LOW_QUALITY_RASTER_MODE = 1,
NUM_RASTER_MODES = 2
};
这个枚举定义在文件external/chromium_org/cc/resources/raster_mode.h中。
从这里就可以看到,NUM_RASTER_MODES的值定义为2。另外两个枚举值HIGH_QUALITY_RASTER_MODE和LOW_QUALITY_RASTER_MODE分别定义为0和1,分别表示High Res和Low Res两种光栅化模式。
回到ManagedTileState类中,它通过TileVersion类来描述分块的光栅化模式。每一种光栅化模式又有三种光栅化方式:
1. RESOURCE_MODE:分块光栅化在一个独立的纹理资源上,这个纹理资源由成员变量resource_描述。
2. SOLID_COLOR_MODE:分块的光栅化结果是一个单一的颜色,这个颜色值由成员变量solid_color_描述。
3. PICTURE_PILE_MODE:分块也是光栅化在一个纹理资源上,但是这个纹理资源是按需(On Demand)创建的,不是由分块所拥有。
TileVersion类还有另外两个成员变量mode_和raster_task_。其中,成员变量mode_描述一个TileVersion对象是使用High Res光栅化模式,还是Low Res光栅化模式;当成员变量raster_task_的值不等于NULL的时候,表示正在对分块执行光栅化操作。
再回到ManagedTileState类中,它有另外几个重要的成员变量,用来描述分块的光栅化状态:
1. raster_mode:表示分块当前所使用的光栅化模式化,它的值要么等于HIGH_QUALITY_RASTER_MODE,要么等于LOW_QUALITY_RASTER_MODE。
2. bin:表示分块所属的Managed Tile Bin。Tile Manager对分块进行排序时,具有相同Managed Tile Bin属性的分块会归为同一类。
3. resolution:表示分块的Resolution属性,也就是它是以High Res模式进行光栅化,还是以Low Res模式进行光栅化。
4. required_for_activation:表示分块光栅化完成后是否需要将CC Pending Layer Tree激活为CC Active Layer Tree。
5. priority_bin:表示分块的Priority Bin属性,也就是分块的优先级,这个优先级综合考虑了分块在CC Pending Layer Tree和CC Active Layer Tree的优先级。
6. distance_to_visible:表示分块与当前帧的Viewport的距离。
7. visible_and_ready_to_draw:表示分块在当前帧是可见的,并且它已经被分配了纹理资源。
分块的Bin属性通过枚举类型ManagedTileBin描述,它的定义如下所示:
// Tile manager classifying tiles into a few basic bins:
enum ManagedTileBin {
NOW_AND_READY_TO_DRAW_BIN = 0, // Ready to draw and within viewport.
NOW_BIN = 1, // Needed ASAP.
SOON_BIN = 2, // Impl-side version of prepainting.
EVENTUALLY_AND_ACTIVE_BIN = 3, // Nice to have, and has a task or resource.
EVENTUALLY_BIN = 4, // Nice to have, if we've got memory and time.
AT_LAST_AND_ACTIVE_BIN = 5, // Only do this after all other bins.
AT_LAST_BIN = 6, // Only do this after all other bins.
NEVER_BIN = 7, // Dont bother.
NUM_BINS = 8
// NOTE: Be sure to update ManagedTileBinAsValue and kBinPolicyMap when adding
// or reordering fields.
};
这个枚举定义在文件external/chromium_org/cc/resources/managed_tile_state.h中。
ManagedTileBin一共定义了7个Managed Tile Bin,数值越小的Managed Tile Bin,其重要性越高,属于这个Managed Tile Bin的分块就越优先获得光栅化。
分块的Resolution属性通过枚举类型TileResolution描述,它的定义如下所示:
enum TileResolution {
LOW_RESOLUTION = 0 ,
HIGH_RESOLUTION = 1,
NON_IDEAL_RESOLUTION = 2,
};
这个枚举定义在文件external/chromium_org/cc/resources/tile_priority.h中。
TileResolution一共定义了3种Resolution:LOW_RESOLUTION、 HIGH_RESOLUTION和NON_IDEAL_RESOLUTION。其中,Resolution属性等于LOW_RESOLUTION、或者HIGH_RESOLUTION的分块都是当前需要使用的,而Resolution属性等于NON_IDEAL_RESOLUTION的分块不是当前需要使用的。
分块的Priority Bin属性通过枚举类型PriorityBin描述,它的定义如下所示:
struct CC_EXPORT TilePriority {
enum PriorityBin { NOW, SOON, EVENTUALLY };
......
};
这个枚举定义在文件external/chromium_org/cc/resources/tile_priority.h中。
PriorityBin一共定义了3种优先级:NOW、SOON、EVENTUALLY。其中,NOW表示分块在当前帧的Viewport中,需要马上渲染出来,SOON表示分块离当前帧的Viewport很近,很快需要渲染出来,EVENTUALLY表示分块离当前帧的Viewport较远,但是最终可能是需要渲染出来的。
从前面Chromium网页Layer Tree同步为Pending Layer Tree的过程分析一文可以知道,分块在创建或者发生变化时,它的优先级,也就是Priority Bin属性就会被重新计算。接下来Tile Manager会根据分块的Priority Bin属性和其它属性,将它们划分在一系列的Managed Tile Bin中。Managed Tile Bin可以看作是对Priority Bin的进一步细分,这样就可以进一步区分分块的重要程度。
ManagedTileState类的各个成员变量更具体的含义和用法,我们后面分析分块的排序过程再详细分析。
理解了分块的光栅化模式之后,回到TileManager类的成员函数CleanUpReleasedTiles中,我们就可以理解为什么要通过一个for循环释放一个分块所占用的资源。每一次循环释放的是一个光栅化模式所占用的资源。这是通过调用TileManager类的成员函数FreeResourceForTile实现的,如下所示:
void TileManager::FreeResourceForTile(Tile* tile, RasterMode mode) {
ManagedTileState& mts = tile->managed_state();
if (mts.tile_versions[mode].resource_) {
resource_pool_->ReleaseResource(mts.tile_versions[mode].resource_.Pass());
......
bytes_releasable_ -= BytesConsumedIfAllocated(tile);
--resources_releasable_;
}
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
从前面Chromium网页绘图表面(Output Surface)创建过程分析一文可以知道,TileManager类的成员resource_pool_指向的是一个ResourcePool对象,通过调用这个ResourcePool对象的成员函数ReleaseResource可以释放指定的资源,也就是参数tile描述的光栅化模式为mode的分块所占用的资源。
TileManager类的成员变量bytes_releasable_表示当前可释放的资源所占用的总字节数,另外一个成员变量resources_releasable_表示当前可释放的资源个数。当指定的资源被释放后,TileManager类的成员函数FreeResourceForTile需要相应地调整上述两个成员变量的值。
释放了那些不再使用了的分块占用的资源之后, 回到TileManager类的成员函数UpdatePrioritizedTileSetIfNeeded中,它接下来调用另外一个成员函数GetTilesWithAssignedBins对网页中的分块重新进行排序。这些已经排序完成的分块将会保存在TileManager类的成员变量prioritized_tiles_描述的一个PrioritizedTileSet对象中。
TileManager类的成员函数GetTilesWithAssignedBins的实现如下所示:
void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) {
TRACE_EVENT0("cc", "TileManager::GetTilesWithAssignedBins");
const TileMemoryLimitPolicy memory_policy = global_state_.memory_limit_policy;
const TreePriority tree_priority = global_state_.tree_priority;
// For each tree, bin into different categories of tiles.
for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = it->second;
ManagedTileState& mts = tile->managed_state();
const ManagedTileState::TileVersion& tile_version =
tile->GetTileVersionForDrawing();
bool tile_is_ready_to_draw = tile_version.IsReadyToDraw();
bool tile_is_active = tile_is_ready_to_draw ||
mts.tile_versions[mts.raster_mode].raster_task_;
// Get the active priority and bin.
TilePriority active_priority = tile->priority(ACTIVE_TREE);
ManagedTileBin active_bin = BinFromTilePriority(active_priority);
// Get the pending priority and bin.
TilePriority pending_priority = tile->priority(PENDING_TREE);
ManagedTileBin pending_bin = BinFromTilePriority(pending_priority);
bool pending_is_low_res = pending_priority.resolution == LOW_RESOLUTION;
bool pending_is_non_ideal =
pending_priority.resolution == NON_IDEAL_RESOLUTION;
bool active_is_non_ideal =
active_priority.resolution == NON_IDEAL_RESOLUTION;
// Adjust bin state based on if ready to draw.
active_bin = kBinReadyToDrawMap[tile_is_ready_to_draw][active_bin];
pending_bin = kBinReadyToDrawMap[tile_is_ready_to_draw][pending_bin];
// Adjust bin state based on if active.
active_bin = kBinIsActiveMap[tile_is_active][active_bin];
pending_bin = kBinIsActiveMap[tile_is_active][pending_bin];
// We never want to paint new non-ideal tiles, as we always have
// a high-res tile covering that content (paint that instead).
if (!tile_is_ready_to_draw && active_is_non_ideal)
active_bin = NEVER_BIN;
if (!tile_is_ready_to_draw && pending_is_non_ideal)
pending_bin = NEVER_BIN;
ManagedTileBin tree_bin[NUM_TREES];
tree_bin[ACTIVE_TREE] = kBinPolicyMap[memory_policy][active_bin];
tree_bin[PENDING_TREE] = kBinPolicyMap[memory_policy][pending_bin];
// Adjust pending bin state for low res tiles. This prevents pending tree
// low-res tiles from being initialized before high-res tiles.
if (pending_is_low_res)
tree_bin[PENDING_TREE] = std::max(tree_bin[PENDING_TREE], EVENTUALLY_BIN);
TilePriority tile_priority;
switch (tree_priority) {
case SAME_PRIORITY_FOR_BOTH_TREES:
mts.bin = std::min(tree_bin[ACTIVE_TREE], tree_bin[PENDING_TREE]);
tile_priority = tile->combined_priority();
break;
case SMOOTHNESS_TAKES_PRIORITY:
mts.bin = tree_bin[ACTIVE_TREE];
tile_priority = active_priority;
break;
case NEW_CONTENT_TAKES_PRIORITY:
mts.bin = tree_bin[PENDING_TREE];
tile_priority = pending_priority;
break;
}
// Bump up the priority if we determined it's NEVER_BIN on one tree,
// but is still required on the other tree.
bool is_in_never_bin_on_both_trees = tree_bin[ACTIVE_TREE] == NEVER_BIN &&
tree_bin[PENDING_TREE] == NEVER_BIN;
if (mts.bin == NEVER_BIN && !is_in_never_bin_on_both_trees)
mts.bin = tile_is_active ? AT_LAST_AND_ACTIVE_BIN : AT_LAST_BIN;
mts.resolution = tile_priority.resolution;
mts.priority_bin = tile_priority.priority_bin;
mts.distance_to_visible = tile_priority.distance_to_visible;
mts.required_for_activation = tile_priority.required_for_activation;
mts.visible_and_ready_to_draw =
tree_bin[ACTIVE_TREE] == NOW_AND_READY_TO_DRAW_BIN;
// Tiles that are required for activation shouldn't be in NEVER_BIN unless
// smoothness takes priority or memory policy allows nothing to be
// initialized.
DCHECK(!mts.required_for_activation || mts.bin != NEVER_BIN ||
tree_priority == SMOOTHNESS_TAKES_PRIORITY ||
memory_policy == ALLOW_NOTHING);
// If the tile is in NEVER_BIN and it does not have an active task, then we
// can release the resources early. If it does have the task however, we
// should keep it in the prioritized tile set to ensure that AssignGpuMemory
// can visit it.
if (mts.bin == NEVER_BIN &&
!mts.tile_versions[mts.raster_mode].raster_task_) {
FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
continue;
}
// Insert the tile into a priority set.
tiles->InsertTile(tile, mts.bin);
}
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
TileManager类的成员函数GetTilesWithAssignedBins的实现比较长,我们分段来阅读。
第一段代码如下所示:
void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) {
TRACE_EVENT0("cc", "TileManager::GetTilesWithAssignedBins");
const TileMemoryLimitPolicy memory_policy = global_state_.memory_limit_policy;
const TreePriority tree_priority = global_state_.tree_priority;
这段代码分别获得分块的内存使用策略memory_policy和优先级策略tree_priority。
内存使用策略通过枚举类型TileMemoryLimitPolicy描述,它的定义如下所示:
enum TileMemoryLimitPolicy {
// Nothing.
ALLOW_NOTHING = 0,
// You might be made visible, but you're not being interacted with.
ALLOW_ABSOLUTE_MINIMUM = 1, // Tall.
// You're being interacted with, but we're low on memory.
ALLOW_PREPAINT_ONLY = 2, // Grande.
// You're the only thing in town. Go crazy.
ALLOW_ANYTHING = 3, // Venti.
NUM_TILE_MEMORY_LIMIT_POLICIES = 4,
// NOTE: Be sure to update TreePriorityAsValue and kBinPolicyMap when adding
// or reordering fields.
};
这个枚举类型定义在文件external/chromium_org/cc/resources/tile_priority.h中。
TileMemoryLimitPolicy一共定义了4种分块内存使用策略:
1. ALLOW_NOTHING:不给所有的Managed Tile Bin的分块分配内存。
2. ALLOW_ABSOLUTE_MINIMUM:只给类型为NOW_AND_READY_TO_DRAW_BIN和NOW_BIN的Managed Tile Bin的分块分配内存。
3. ALLOW_PREPAINT_ONLY:只给类型为NOW_AND_READY_TO_DRAW_BIN、NOW_BIN和SOON_BIN的Managed Tile Bin的分块分配内存。
4. ALLOW_ANYTHING:给除了类型为NEVER_BIN之外的Managed Tile Bin的分块分配内存。
优先级策略通过枚举类型TreePriority描述,它的定义如下所示:
enum TreePriority {
SAME_PRIORITY_FOR_BOTH_TREES,
SMOOTHNESS_TAKES_PRIORITY,
NEW_CONTENT_TAKES_PRIORITY
// Be sure to update TreePriorityAsValue when adding new fields.
};
这个枚举类型定义在文件external/chromium_org/cc/resources/tile_priority.h中。
TreePriority一共定义了3种分块优先级使用策略:
1. SAME_PRIORITY_FOR_BOTH_TREES:分块在CC Pending Layer Tree和CC Active Layer Tree的优先级同等重要。
2. SMOOTHNESS_TAKES_PRIORITY:分块在CC Active Layer Tree的优先级比在CC Pending Layer Tree的优先级高。
3. NEW_CONTENT_TAKES_PRIORITY:分块在CC Pending Layer Tree的优先级比在CC Active Layer Tree的优先级高。
TileManager类的成员函数GetTilesWithAssignedBins的第二段代码如下所示:
// For each tree, bin into different categories of tiles.
for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
Tile* tile = it->second;
ManagedTileState& mts = tile->managed_state();
const ManagedTileState::TileVersion& tile_version =
tile->GetTileVersionForDrawing();
bool tile_is_ready_to_draw = tile_version.IsReadyToDraw();
bool tile_is_active = tile_is_ready_to_draw ||
mts.tile_versions[mts.raster_mode].raster_task_;
从这段代码开始,TileManager类的成员函数GetTilesWithAssignedBins遍历网页中的所有分块。
对于每一个分块,也就是每一个Tile对象,TileManager类的成员函数GetTilesWithAssignedBins首先通过它的成员函数GetTileVersionForDrawing获得它当前需要渲染的版本,也就是它的光栅化模式,如下所示:
class CC_EXPORT Tile : public RefCountedManaged<Tile> {
public:
......
const ManagedTileState::TileVersion& GetTileVersionForDrawing() const {
for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) {
if (managed_state_.tile_versions[mode].IsReadyToDraw())
return managed_state_.tile_versions[mode];
}
return managed_state_.tile_versions[HIGH_QUALITY_RASTER_MODE];
}
......
};
这个函数定义在文件external/chromium_org/cc/resources/tile.h中。
从前面分析可以知道,每一个分块都有两种光栅化模式。一种是按照Low Res模式进行光栅化,另一种是按照High Res模式进行光栅化。Tile类的成员函数GetTileVersionForDrawing按照以下规则决定分块使用哪一种光栅化模式:
1. 如果分块在High Res光栅化模式中分配了纹理资源,那么优先选择High Res光栅化模式。
2. 如果分块在High Res光栅化模式中没有分配纹理资源,但是在Low Res光栅化模式中分配了纹理资源,那么选择Low Res光栅化模式。
3. 如果分块在High Res和Low Res光栅化模式中均还没有分配纹理资源,那么优先选择High Res光栅化模式。
总结来说,就是优先考虑以高质量模式光栅化分块,但是如果以低质量模式光栅化分块的代价很小,那么优先考虑以低质量模式光栅化分块。
回到TileManager类的成员函数GetTilesWithAssignedBins的第二段代码中,它确定了分块的光栅化模式之后,接下来再确定分块在该光栅化模式下的两个状态:
1. tile_is_ready_to_draw:表示分块是否已经准备就绪进行光栅化?也就是是否已经分配了纹理资源。一个分块只有在分配了纹理资源的前提下,才可以进行光栅化。
2. tile_is_active:表示分块是否已经激活?在两种情况下,一个分块认为当前是激活的。第一种情况是该分块已经Ready To Draw。第二种情况是该分块正在执行光栅化操作。在第二种情况下,分块已经关联了一个光栅化任务。
TileManager类的成员函数GetTilesWithAssignedBins的第三段代码如下所示:
// Get the active priority and bin.
TilePriority active_priority = tile->priority(ACTIVE_TREE);
ManagedTileBin active_bin = BinFromTilePriority(active_priority);
// Get the pending priority and bin.
TilePriority pending_priority = tile->priority(PENDING_TREE);
ManagedTileBin pending_bin = BinFromTilePriority(pending_priority);
这段代码分别获取分块在CC Active Layer Tree和CC Pending Layer Tree中的优先级active_priority和pending_priority,并且通过调用函数BinFromTilePriority获得这两个优先级所对应的Managed Tile Bin,如下所示:
// Determine bin based on three categories of tiles: things we need now,
// things we need soon, and eventually.
inline ManagedTileBin BinFromTilePriority(const TilePriority& prio) {
if (prio.priority_bin == TilePriority::NOW)
return NOW_BIN;
if (prio.priority_bin == TilePriority::SOON)
return SOON_BIN;
if (prio.distance_to_visible == std::numeric_limits<float>::infinity())
return NEVER_BIN;
return EVENTUALLY_BIN;
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
从函数BinFromTilePriority的实现可以知道:
1. 优先级为NOW的分块对应的Managed Tile Bin为NOW_BIN。
2. 优先级为SOON的分块对应的Managed Tile Bin为SOON_BIN。
3. 距离当前帧的Viewport无穷远的分块对应的Managed Tile Bin为NEVER_BIN。
4. 优先级不等于NOW和SOON,并且距离当前帧的Viewport不是无穷远的分块对应的Managed Tile Bin为EVENTUALLY_BIN。
TileManager类的成员函数GetTilesWithAssignedBins的第四段代码如下所示:
bool pending_is_low_res = pending_priority.resolution == LOW_RESOLUTION;
bool pending_is_non_ideal =
pending_priority.resolution == NON_IDEAL_RESOLUTION;
bool active_is_non_ideal =
active_priority.resolution == NON_IDEAL_RESOLUTION;
这段代码检查分块在CC Pending Layer Tree中对应的分辨率是否为LOW_RESOLUTION和NON_IDEAL_RESOLUTION,以及分块在CC Active Layer Tree中对应的分辨率是否为NON_IDEAL_RESOLUTION。
TileManager类的成员函数GetTilesWithAssignedBins的第五段代码如下所示:
// Adjust bin state based on if ready to draw.
active_bin = kBinReadyToDrawMap[tile_is_ready_to_draw][active_bin];
pending_bin = kBinReadyToDrawMap[tile_is_ready_to_draw][pending_bin];
这段代码根据分块是否已经准备就绪光栅化相应地调整它的Acitve Managed Tile Bin和Pending Managed Tile Bin。这个调整过程是通过一个kBinReadyToDrawMap数组进行的,如下所示:
// Ready to draw works by mapping NOW_BIN to NOW_AND_READY_TO_DRAW_BIN.
const ManagedTileBin kBinReadyToDrawMap[2][NUM_BINS] = {
// Not ready
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_BIN, // [NOW_BIN]
SOON_BIN, // [SOON_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
EVENTUALLY_BIN, // [EVENTUALLY_BIN]
AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN]
AT_LAST_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
},
// Ready
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_AND_READY_TO_DRAW_BIN, // [NOW_BIN]
SOON_BIN, // [SOON_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
EVENTUALLY_BIN, // [EVENTUALLY_BIN]
AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN]
AT_LAST_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}};
这个数组定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
从这个数组的定义可以看出,它主要是将那些已经准备就绪光栅化的分块的Managed Tile Bin,从NOW_BIN提升为NOW_AND_READY_TO_DRAW_BIN,其余的都保持不变。
TileManager类的成员函数GetTilesWithAssignedBins的第六段代码如下所示:
// Adjust bin state based on if active.
active_bin = kBinIsActiveMap[tile_is_active][active_bin];
pending_bin = kBinIsActiveMap[tile_is_active][pending_bin];
这段代码根据分块是否激活相应地调整它的Acitve Managed Tile Bin和Pending Managed Tile Bin。这个调整过程是通过一个kBinIsActiveMap数组进行的,如下所示:
// Active works by mapping some bin stats to equivalent _ACTIVE_BIN state.
const ManagedTileBin kBinIsActiveMap[2][NUM_BINS] = {
// Inactive
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_BIN, // [NOW_BIN]
SOON_BIN, // [SOON_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
EVENTUALLY_BIN, // [EVENTUALLY_BIN]
AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN]
AT_LAST_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
},
// Active
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_BIN, // [NOW_BIN]
SOON_BIN, // [SOON_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_BIN]
AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN]
AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}};
这个数组定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
从这个数组的定义可以看出,它主要是将那些已经激活的分块的Managed Tile Bin,从EVENTUALLY_BIN提升为EVENTUALLY_AND_ACTIVE_BIN,以及从AT_LAST_BIN提升为AT_LAST_AND_ACTIVE_BIN,其余的都保持不变。
TileManager类的成员函数GetTilesWithAssignedBins的第七段代码如下所示:
// We never want to paint new non-ideal tiles, as we always have
// a high-res tile covering that content (paint that instead).
if (!tile_is_ready_to_draw && active_is_non_ideal)
active_bin = NEVER_BIN;
if (!tile_is_ready_to_draw && pending_is_non_ideal)
pending_bin = NEVER_BIN;
这段代码检查分块是否是不激活的。如果不是激活的,并且它的分辨率状态又是属于NON_IDEAL_RESOLUTION的,那么它的Managed Tile Bin就会降级为NEVER_BIN。
TileManager类的成员函数GetTilesWithAssignedBins的第八段代码如下所示:
ManagedTileBin tree_bin[NUM_TREES];
tree_bin[ACTIVE_TREE] = kBinPolicyMap[memory_policy][active_bin];
tree_bin[PENDING_TREE] = kBinPolicyMap[memory_policy][pending_bin];
这段代码根据分块内存使用策略调整分块的Acitve Managed Tile Bin和Pending Managed Tile Bin。这个调整过程是通过一个kBinPolicyMap数组进行的,如下所示:
// Memory limit policy works by mapping some bin states to the NEVER bin.
const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = {
// [ALLOW_NOTHING]
{NEVER_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NEVER_BIN, // [NOW_BIN]
NEVER_BIN, // [SOON_BIN]
NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
NEVER_BIN, // [EVENTUALLY_BIN]
NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN]
NEVER_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
},
// [ALLOW_ABSOLUTE_MINIMUM]
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_BIN, // [NOW_BIN]
NEVER_BIN, // [SOON_BIN]
NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
NEVER_BIN, // [EVENTUALLY_BIN]
NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN]
NEVER_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
},
// [ALLOW_PREPAINT_ONLY]
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_BIN, // [NOW_BIN]
SOON_BIN, // [SOON_BIN]
NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
NEVER_BIN, // [EVENTUALLY_BIN]
NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN]
NEVER_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
},
// [ALLOW_ANYTHING]
{NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
NOW_BIN, // [NOW_BIN]
SOON_BIN, // [SOON_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
EVENTUALLY_BIN, // [EVENTUALLY_BIN]
AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN]
AT_LAST_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}};
这个数组定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
从这个数组可以看出:
1. 当分块内存使用策略为ALLOW_NOTHING时,所有的Managed Tile Bin都将降级为NEVER_BIN。
2. 当分块内存使用策略为ALLOW_ABSOLUTE_MINIMUM时,除了类型为NOW_AND_READY_TO_DRAW_BIN和NOW_BIN的Managed Tile Bin,其余的均降级为NEVER_BIN。
3. 当分块内存使用策略为ALLOW_PREPAINT_ONLY时,除了类型为NOW_AND_READY_TO_DRAW_BIN、NOW_BIN和SOON_BIN的Managed Tile Bin,其余的均降级为NEVER_BIN。
4. 当分块内存使用策略为ALLOW_ANYTHING时,分块的Managed Tile Bin保持不变。
TileManager类的成员函数GetTilesWithAssignedBins的第九段代码如下所示:
// Adjust pending bin state for low res tiles. This prevents pending tree
// low-res tiles from being initialized before high-res tiles.
if (pending_is_low_res)
tree_bin[PENDING_TREE] = std::max(tree_bin[PENDING_TREE], EVENTUALLY_BIN);
这段代码检查分块在CC Pending Layer Tree中对应的分辨率是否为LOW_RESOLUTION。如果是的话,就检查它的Pending Managed Tile Bin是否比EVENTUALLY_BIN的优先级高。如果是的话,那么就将它降级为EVENTUALLY_BIN。这样做的目的是降低低分辨率分块的光栅化优先级,使得高分辨率分块可以优先得到光栅化。
TileManager类的成员函数GetTilesWithAssignedBins的第十段代码如下所示:
TilePriority tile_priority;
switch (tree_priority) {
case SAME_PRIORITY_FOR_BOTH_TREES:
mts.bin = std::min(tree_bin[ACTIVE_TREE], tree_bin[PENDING_TREE]);
tile_priority = tile->combined_priority();
break;
case SMOOTHNESS_TAKES_PRIORITY:
mts.bin = tree_bin[ACTIVE_TREE];
tile_priority = active_priority;
break;
case NEW_CONTENT_TAKES_PRIORITY:
mts.bin = tree_bin[PENDING_TREE];
tile_priority = pending_priority;
break;
}
这段代码根据分块优先级策略决定将一个分块划分到哪一个Managed Tile Bin去:
1. 当分块优先级策略等于SAME_PRIORITY_FOR_BOTH_TREES时,在分块的Acitve Managed Tile Bin和Pending Managed Tile Bin之间选择优先级高者作为最终的Managed Tile Bin,同时将分块在CC Active Layer Tree和CC Pending Layer Tree的优先级综合起来得到最终的优先级。这个综合过程是通过调用Tile类的成员函数combined_priority实现的,具体可以参考前面Chromium网页Layer Tree同步为Pending Layer Tree的过程分析一文。
2. 当分块优先级策略等于SMOOTHNESS_TAKES_PRIORITY时,将分块的Acitve Managed Tile Bin作为最终的Managed Tile Bin,同时将分块在CC Active Layer Tree中的优先级设置为最终的优先级。
3. 当分块优先级策略等于NEW_CONTENT_TAKES_PRIORITY时,将分块的Pending Managed Tile Bin作为最终的Managed Tile Bin,同时将分块在CC Pending Layer Tree中的优先级设置为最终的优先级。
TileManager类的成员函数GetTilesWithAssignedBins的第十一段代码如下所示:
// Bump up the priority if we determined it's NEVER_BIN on one tree,
// but is still required on the other tree.
bool is_in_never_bin_on_both_trees = tree_bin[ACTIVE_TREE] == NEVER_BIN &&
tree_bin[PENDING_TREE] == NEVER_BIN;
if (mts.bin == NEVER_BIN && !is_in_never_bin_on_both_trees)
mts.bin = tile_is_active ? AT_LAST_AND_ACTIVE_BIN : AT_LAST_BIN;
这段代码检查分块的Active Managed Tile Bin和Pending Managed Tile Bin是否只有其中一个是等于NEVER_BIN。如果是的话,就将该分块最终的Managed Tile Bin提升为AT_LAST_AND_ACTIVE_BIN或者AT_LAST_BIN,取决于它当前是否是激活的。这意味着只有当分块的Active Managed Tile Bin和Pending Managed Tile Bin均为NEVER_BIN时,它最终的Managed Tile Bin才会被设置为NEVER_BIN。
TileManager类的成员函数GetTilesWithAssignedBins的第十二段代码如下所示:
// If the tile is in NEVER_BIN and it does not have an active task, then we
// can release the resources early. If it does have the task however, we
// should keep it in the prioritized tile set to ensure that AssignGpuMemory
// can visit it.
if (mts.bin == NEVER_BIN &&
!mts.tile_versions[mts.raster_mode].raster_task_) {
FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
continue;
}
这段代码检查分块最终是否被划分在NEVER_BIN中。如果是的话,并且它当前没有关联光栅化任务,那么就会调用TileManager类的成员函数FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw释放它所占用的纹理资源,因为被划分在NEVER_BIN在当前帧中是不需要的。
TileManager类的成员函数FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw最终会调用前面我们已经分析过的成员函数FreeResourceForTile来释放一个分块所占用的纹理资源。
TileManager类的成员函数GetTilesWithAssignedBins的最后一段代码如下所示:
// Insert the tile into a priority set.
tiles->InsertTile(tile, mts.bin);
}
}
这段代码将分块保存在参数tiles指向的一个PrioritizedTileSet对象中。这是通过调用PrioritizedTileSet类的成员函数InsertTile实现的,如下所示:
void PrioritizedTileSet::InsertTile(Tile* tile, ManagedTileBin bin) {
tiles_[bin].push_back(tile);
bin_sorted_[bin] = false;
}
这个函数定义在文件external/chromium_org/cc/resources/prioritized_tile_set.cc中。
PrioritizedTileSet类有两个成员变量tiles_和bin_sorted_,它们的定义如下所示:
class CC_EXPORT PrioritizedTileSet {
......
private:
......
std::vector<Tile*> tiles_[NUM_BINS];
bool bin_sorted_[NUM_BINS];
};
这个类定义在文件external/chromium_org/cc/resources/prioritized_tile_set.h中。
其中,成员变量tiles_是一个大小为NUM_BINS的std::vector数组。也就是说,对于每一个Managed Tile Bin,在PrioritizedTileSet类中都有一个std::vector。保存在同一个std::vector里面的分块都是属于同一个Managed Tile Bin的。
另外一个成员变量bin_sorted_是一个类型为bool的数组,它的大小也是等于NUM_BINS,用来表示成员变量tiles_描述的std::vector哪些是有序的,哪些是无序的。对于无序的std::vector,在遍历它的时候,要先进行排序。
理解了这两个成员变量的含义,回到PrioritizedTileSet类的成员函数InsertTile中,我们就可以很容易理解它的实现,它将参数tile描述的分块保存在参数bin描述的Managed Tile Bin对应的std::vector中,并且将该std::vector标记为无序的。
PrioritizedTileSet类提供了一个Iterator,通过该Iterator可以有序地遍历保存在成员变量tiles_描述的所有std::vector中的分块。遍历规则如下所示:
1. 先遍历优先级高的Manged Tile Bin的分块,再遍历优先级低的Manged Tile Bin的分块。
2. 对于NOW_AND_READY_TO_DRAW_BIN和NEVER_BIN中的分块,它们的遍历顺序是无关重要的。
3. 对于NOW_BIN、SOON_BIN、EVENTUALLY_AND_ACTIVE_BIN、EVENTUALLY_BIN、AT_LAST_AND_ACTIVE_BIN和AT_LAST_BIN中的分块,它们的遍历顺序与它们的优先级、分辨率和到当前帧的Viewport的距离有关。优先级越高、分辨率越高、到当前帧的Viewport的距离越小的分块,就越在前面被遍历。
这一步执行完成后,Tile Manager就对网页中的分块进行排序了,后面Tile Manager就会按照这个顺序对分块进行光栅化。回到TileManager类的成员函数ManageTiles中,它接下来就调用另外一个成员函数AssignGpuMemoryToTiles根据分块的排序和GPU内存限制决定哪些分块需要光栅化,哪些分块不需要光栅化。
TileManager类的成员函数AssignGpuMemoryToTiles的实现如下所示:
void TileManager::AssignGpuMemoryToTiles(
PrioritizedTileSet* tiles,
TileVector* tiles_that_need_to_be_rasterized) {
......
// Cast to prevent overflow.
int64 soft_bytes_available =
static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.soft_memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int64 hard_bytes_available =
static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.hard_memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int resources_available = resources_releasable_ +
global_state_.num_resources_limit -
resource_pool_->acquired_resource_count();
size_t soft_bytes_allocatable =
std::max(static_cast<int64>(0), soft_bytes_available);
size_t hard_bytes_allocatable =
std::max(static_cast<int64>(0), hard_bytes_available);
size_t resources_allocatable = std::max(0, resources_available);
......
size_t soft_bytes_left = soft_bytes_allocatable;
size_t hard_bytes_left = hard_bytes_allocatable;
size_t resources_left = resources_allocatable;
bool oomed_soft = false;
......
for (PrioritizedTileSet::Iterator it(tiles, true); it; ++it) {
Tile* tile = *it;
ManagedTileState& mts = tile->managed_state();
......
mts.raster_mode = tile->DetermineOverallRasterMode();
ManagedTileState::TileVersion& tile_version =
mts.tile_versions[mts.raster_mode];
// If this tile doesn't need a resource, then nothing to do.
if (!tile_version.requires_resource())
continue;
// If the tile is not needed, free it up.
if (mts.bin == NEVER_BIN) {
FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
continue;
}
const bool tile_uses_hard_limit = mts.bin <= NOW_BIN;
const size_t bytes_if_allocated = BytesConsumedIfAllocated(tile);
const size_t tile_bytes_left =
(tile_uses_hard_limit) ? hard_bytes_left : soft_bytes_left;
......
size_t tile_bytes = 0;
size_t tile_resources = 0;
// It costs to maintain a resource.
for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) {
if (mts.tile_versions[mode].resource_) {
tile_bytes += bytes_if_allocated;
tile_resources++;
}
}
// Allow lower priority tiles with initialized resources to keep
// their memory by only assigning memory to new raster tasks if
// they can be scheduled.
bool reached_scheduled_raster_tasks_limit =
tiles_that_need_to_be_rasterized->size() >= kScheduledRasterTasksLimit;
if (!reached_scheduled_raster_tasks_limit) {
// If we don't have the required version, and it's not in flight
// then we'll have to pay to create a new task.
if (!tile_version.resource_ && !tile_version.raster_task_) {
tile_bytes += bytes_if_allocated;
tile_resources++;
}
}
// Tile is OOM.
if (tile_bytes > tile_bytes_left || tile_resources > resources_left) {
......
FreeResourcesForTile(tile);
// This tile was already on screen and now its resources have been
// released. In order to prevent checkerboarding, set this tile as
// rasterize on demand immediately.
if (mts.visible_and_ready_to_draw)
tile_version.set_rasterize_on_demand();
......
oomed_soft = true;
......
} else {
resources_left -= tile_resources;
hard_bytes_left -= tile_bytes;
soft_bytes_left =
(soft_bytes_left > tile_bytes) ? soft_bytes_left - tile_bytes : 0;
if (tile_version.resource_)
continue;
}
......
// Tile shouldn't be rasterized if |tiles_that_need_to_be_rasterized|
// has reached it's limit or we've failed to assign gpu memory to this
// or any higher priority tile. Preventing tiles that fit into memory
// budget to be rasterized when higher priority tile is oom is
// important for two reasons:
// 1. Tile size should not impact raster priority.
// 2. Tiles with existing raster task could otherwise incorrectly
// be added as they are not affected by |bytes_allocatable|.
bool can_schedule_tile =
!oomed_soft && !reached_scheduled_raster_tasks_limit;
if (!can_schedule_tile) {
......
continue;
}
tiles_that_need_to_be_rasterized->push_back(tile);
}
......
}
这个函数定义在文件external/chromium_org/cc/resources/tile_manager.cc中。
TileManager类的成员函数AssignGpuMemoryToTiles的实现也比较长,我们分段来阅读。
第一段代码如下所示:
void TileManager::AssignGpuMemoryToTiles(
PrioritizedTileSet* tiles,
TileVector* tiles_that_need_to_be_rasterized) {
......
// Cast to prevent overflow.
int64 soft_bytes_available =
static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.soft_memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int64 hard_bytes_available =
static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.hard_memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int resources_available = resources_releasable_ +
global_state_.num_resources_limit -
resource_pool_->acquired_resource_count();
size_t soft_bytes_allocatable =
std::max(static_cast<int64>(0), soft_bytes_available);
size_t hard_bytes_allocatable =
std::max(static_cast<int64>(0), hard_bytes_available);
size_t resources_allocatable = std::max(0, resources_available);
......
size_t soft_bytes_left = soft_bytes_allocatable;
size_t hard_bytes_left = hard_bytes_allocatable;
size_t resources_left = resources_allocatable;
bool oomed_soft = false;
bool oomed_hard = false;
这段代码主是计算剩余可用的光栅化内存字节数以及光栅化资源个数。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是相等的。
目前分块正在使用的光栅化内
以上是关于Chromium网页光栅化过程分析的主要内容,如果未能解决你的问题,请参考以下文章