// Copyright 2011 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/layers/tiled_layer_impl.h" #include "base/basictypes.h" #include "base/debug/trace_event_argument.h" #include "base/strings/stringprintf.h" #include "cc/base/math_util.h" #include "cc/base/simple_enclosed_region.h" #include "cc/debug/debug_colors.h" #include "cc/layers/append_quads_data.h" #include "cc/quads/checkerboard_draw_quad.h" #include "cc/quads/debug_border_draw_quad.h" #include "cc/quads/solid_color_draw_quad.h" #include "cc/quads/tile_draw_quad.h" #include "cc/resources/layer_tiling_data.h" #include "cc/trees/occlusion_tracker.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/quad_f.h" namespace cc { class DrawableTile : public LayerTilingData::Tile { public: static scoped_ptr<DrawableTile> Create() { return make_scoped_ptr(new DrawableTile()); } ResourceProvider::ResourceId resource_id() const { return resource_id_; } void set_resource_id(ResourceProvider::ResourceId resource_id) { resource_id_ = resource_id; } bool contents_swizzled() { return contents_swizzled_; } void set_contents_swizzled(bool contents_swizzled) { contents_swizzled_ = contents_swizzled; } private: DrawableTile() : resource_id_(0), contents_swizzled_(false) {} ResourceProvider::ResourceId resource_id_; bool contents_swizzled_; DISALLOW_COPY_AND_ASSIGN(DrawableTile); }; TiledLayerImpl::TiledLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), skips_draw_(true) {} TiledLayerImpl::~TiledLayerImpl() { } ResourceProvider::ResourceId TiledLayerImpl::ContentsResourceId() const { // This function is only valid for single texture layers, e.g. masks. DCHECK(tiler_); // It's possible the mask layer is created but has no size or otherwise // can't draw. if (tiler_->num_tiles_x() == 0 || tiler_->num_tiles_y() == 0) return 0; // Any other number of tiles other than 0 or 1 is incorrect for masks. DCHECK_EQ(tiler_->num_tiles_x(), 1); DCHECK_EQ(tiler_->num_tiles_y(), 1); DrawableTile* tile = TileAt(0, 0); ResourceProvider::ResourceId resource_id = tile ? tile->resource_id() : 0; return resource_id; } bool TiledLayerImpl::HasTileAt(int i, int j) const { return !!tiler_->TileAt(i, j); } bool TiledLayerImpl::HasResourceIdForTileAt(int i, int j) const { return HasTileAt(i, j) && TileAt(i, j)->resource_id(); } DrawableTile* TiledLayerImpl::TileAt(int i, int j) const { return static_cast<DrawableTile*>(tiler_->TileAt(i, j)); } DrawableTile* TiledLayerImpl::CreateTile(int i, int j) { scoped_ptr<DrawableTile> tile(DrawableTile::Create()); DrawableTile* added_tile = tile.get(); tiler_->AddTile(tile.PassAs<LayerTilingData::Tile>(), i, j); return added_tile; } void TiledLayerImpl::GetDebugBorderProperties(SkColor* color, float* width) const { *color = DebugColors::TiledContentLayerBorderColor(); *width = DebugColors::TiledContentLayerBorderWidth(layer_tree_impl()); } scoped_ptr<LayerImpl> TiledLayerImpl::CreateLayerImpl( LayerTreeImpl* tree_impl) { return TiledLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>(); } void TiledLayerImpl::AsValueInto(base::debug::TracedValue* state) const { LayerImpl::AsValueInto(state); state->BeginArray("invalidation"); MathUtil::AddToTracedValue(update_rect(), state); state->EndArray(); } size_t TiledLayerImpl::GPUMemoryUsageInBytes() const { size_t amount = 0; const size_t kMemoryUsagePerTileInBytes = 4 * tiler_->tile_size().width() * tiler_->tile_size().height(); for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin(); iter != tiler_->tiles().end(); ++iter) { const DrawableTile* tile = static_cast<DrawableTile*>(iter->second); DCHECK(tile); if (!tile->resource_id()) continue; amount += kMemoryUsagePerTileInBytes; } return amount; } void TiledLayerImpl::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); TiledLayerImpl* tiled_layer = static_cast<TiledLayerImpl*>(layer); tiled_layer->set_skips_draw(skips_draw_); tiled_layer->SetTilingData(*tiler_); for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin(); iter != tiler_->tiles().end(); ++iter) { int i = iter->first.first; int j = iter->first.second; DrawableTile* tile = static_cast<DrawableTile*>(iter->second); DCHECK(tile); tiled_layer->PushTileProperties(i, j, tile->resource_id(), tile->contents_swizzled()); } } bool TiledLayerImpl::WillDraw(DrawMode draw_mode, ResourceProvider* resource_provider) { if (!tiler_ || tiler_->has_empty_bounds() || visible_content_rect().IsEmpty() || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) return false; return LayerImpl::WillDraw(draw_mode, resource_provider); } void TiledLayerImpl::AppendQuads( RenderPass* render_pass, const OcclusionTracker<LayerImpl>& occlusion_tracker, AppendQuadsData* append_quads_data) { DCHECK(tiler_); DCHECK(!tiler_->has_empty_bounds()); DCHECK(!visible_content_rect().IsEmpty()); gfx::Rect content_rect = visible_content_rect(); SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); PopulateSharedQuadState(shared_quad_state); AppendDebugBorderQuad( render_pass, content_bounds(), shared_quad_state, append_quads_data); int left, top, right, bottom; tiler_->ContentRectToTileIndices(content_rect, &left, &top, &right, &bottom); if (ShowDebugBorders()) { for (int j = top; j <= bottom; ++j) { for (int i = left; i <= right; ++i) { DrawableTile* tile = TileAt(i, j); gfx::Rect tile_rect = tiler_->tile_bounds(i, j); gfx::Rect visible_tile_rect = tile_rect; SkColor border_color; float border_width; if (skips_draw_ || !tile || !tile->resource_id()) { border_color = DebugColors::MissingTileBorderColor(); border_width = DebugColors::MissingTileBorderWidth(layer_tree_impl()); } else { border_color = DebugColors::HighResTileBorderColor(); border_width = DebugColors::HighResTileBorderWidth(layer_tree_impl()); } DebugBorderDrawQuad* debug_border_quad = render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>(); debug_border_quad->SetNew(shared_quad_state, tile_rect, visible_tile_rect, border_color, border_width); } } } if (skips_draw_) return; Occlusion occlusion = occlusion_tracker.GetCurrentOcclusionForLayer(draw_transform()); for (int j = top; j <= bottom; ++j) { for (int i = left; i <= right; ++i) { DrawableTile* tile = TileAt(i, j); gfx::Rect tile_rect = tiler_->tile_bounds(i, j); gfx::Rect display_rect = tile_rect; tile_rect.Intersect(content_rect); // Skip empty tiles. if (tile_rect.IsEmpty()) continue; gfx::Rect visible_tile_rect = occlusion.GetUnoccludedContentRect(tile_rect); if (visible_tile_rect.IsEmpty()) continue; if (!tile || !tile->resource_id()) { SkColor checker_color; if (ShowDebugBorders()) { checker_color = tile ? DebugColors::InvalidatedTileCheckerboardColor() : DebugColors::EvictedTileCheckerboardColor(); } else { checker_color = DebugColors::DefaultCheckerboardColor(); } CheckerboardDrawQuad* checkerboard_quad = render_pass->CreateAndAppendDrawQuad<CheckerboardDrawQuad>(); checkerboard_quad->SetNew( shared_quad_state, tile_rect, visible_tile_rect, checker_color); append_quads_data->num_missing_tiles++; continue; } gfx::Rect tile_opaque_rect = contents_opaque() ? tile_rect : gfx::Rect(); // Keep track of how the top left has moved, so the texture can be // offset the same amount. gfx::Vector2d display_offset = tile_rect.origin() - display_rect.origin(); gfx::Vector2d texture_offset = tiler_->texture_offset(i, j) + display_offset; gfx::RectF tex_coord_rect = gfx::RectF(tile_rect.size()) + texture_offset; float tile_width = static_cast<float>(tiler_->tile_size().width()); float tile_height = static_cast<float>(tiler_->tile_size().height()); gfx::Size texture_size(tile_width, tile_height); TileDrawQuad* quad = render_pass->CreateAndAppendDrawQuad<TileDrawQuad>(); quad->SetNew(shared_quad_state, tile_rect, tile_opaque_rect, visible_tile_rect, tile->resource_id(), tex_coord_rect, texture_size, tile->contents_swizzled()); } } } void TiledLayerImpl::SetTilingData(const LayerTilingData& tiler) { if (tiler_) { tiler_->reset(); } else { tiler_ = LayerTilingData::Create(tiler.tile_size(), tiler.has_border_texels() ? LayerTilingData::HAS_BORDER_TEXELS : LayerTilingData::NO_BORDER_TEXELS); } *tiler_ = tiler; } void TiledLayerImpl::PushTileProperties( int i, int j, ResourceProvider::ResourceId resource_id, bool contents_swizzled) { DrawableTile* tile = TileAt(i, j); if (!tile) tile = CreateTile(i, j); tile->set_resource_id(resource_id); tile->set_contents_swizzled(contents_swizzled); } void TiledLayerImpl::PushInvalidTile(int i, int j) { DrawableTile* tile = TileAt(i, j); if (!tile) tile = CreateTile(i, j); tile->set_resource_id(0); tile->set_contents_swizzled(false); } SimpleEnclosedRegion TiledLayerImpl::VisibleContentOpaqueRegion() const { if (skips_draw_) return SimpleEnclosedRegion(); return LayerImpl::VisibleContentOpaqueRegion(); } void TiledLayerImpl::ReleaseResources() { tiler_->reset(); } const char* TiledLayerImpl::LayerTypeAsString() const { return "cc::TiledLayerImpl"; } } // namespace cc