blob: 779aaddeff5178174fe496a41cfc88cc7ec10036 [file] [log] [blame]
// Copyright 2014 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.
#ifndef CC_PAINT_DISPLAY_ITEM_LIST_H_
#define CC_PAINT_DISPLAY_ITEM_LIST_H_
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/rtree.h"
#include "cc/paint/discardable_image_map.h"
#include "cc/paint/image_id.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_op_buffer.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
class SkCanvas;
namespace gpu {
namespace raster {
class RasterImplementation;
class RasterImplementationGLES;
} // namespace raster
} // namespace gpu
namespace base {
namespace trace_event {
class TracedValue;
}
}
namespace cc {
// DisplayItemList is a container of paint operations. One can populate the list
// using StartPaint, followed by push{,_with_data,_with_array} functions
// specialized with ops coming from paint_op_buffer.h. Internally, the
// DisplayItemList contains a PaintOpBuffer and defers op saving to it.
// Additionally, it store some meta information about the paint operations.
// Specifically, it creates an rtree to assist in rasterization: when
// rasterizing a rect, it queries the rtree to extract only the byte offsets of
// the ops required and replays those into a canvas.
class CC_PAINT_EXPORT DisplayItemList
: public base::RefCountedThreadSafe<DisplayItemList> {
public:
// TODO(vmpstr): It would be cool if we didn't need this, and instead used
// PaintOpBuffer directly when we needed to release this as a paint op buffer.
enum UsageHint { kTopLevelDisplayItemList, kToBeReleasedAsPaintOpBuffer };
explicit DisplayItemList(UsageHint = kTopLevelDisplayItemList);
DisplayItemList(const DisplayItemList&) = delete;
DisplayItemList& operator=(const DisplayItemList&) = delete;
void Raster(SkCanvas* canvas, ImageProvider* image_provider = nullptr) const;
// Captures |DrawTextBlobOp|s intersecting |rect| and returns the associated
// |NodeId|s in |content|.
void CaptureContent(const gfx::Rect& rect,
std::vector<NodeInfo>* content) const;
// Returns the approximate total area covered by |DrawTextBlobOp|s
// intersecting |rect|, used for statistics purpose.
double AreaOfDrawText(const gfx::Rect& rect) const;
void StartPaint() {
#if DCHECK_IS_ON()
DCHECK(!IsPainting());
current_range_start_ = paint_op_buffer_.size();
#endif
}
// Push functions construct a new op on the paint op buffer, while maintaining
// bookkeeping information. Must be called after invoking StartPaint().
// Returns the id (which is an opaque value) of the operation that can be used
// in UpdateSaveLayerBounds().
template <typename T, typename... Args>
size_t push(Args&&... args) {
#if DCHECK_IS_ON()
DCHECK(IsPainting());
#endif
size_t offset = paint_op_buffer_.next_op_offset();
if (usage_hint_ == kTopLevelDisplayItemList)
offsets_.push_back(offset);
const T* op = paint_op_buffer_.push<T>(std::forward<Args>(args)...);
DCHECK(op->IsValid());
return offset;
}
// Called by blink::PaintChunksToCcLayer when an effect ends, to update the
// bounds of a SaveLayer[Alpha]Op which was emitted when the effect started.
// This is needed because blink doesn't know the bounds when an effect starts.
// Don't add other mutation methods like this if there is better alternative.
void UpdateSaveLayerBounds(size_t id, const SkRect& bounds) {
paint_op_buffer_.UpdateSaveLayerBounds(id, bounds);
}
void EndPaintOfUnpaired(const gfx::Rect& visual_rect) {
#if DCHECK_IS_ON()
DCHECK(IsPainting());
current_range_start_ = kNotPainting;
#endif
if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
return;
visual_rects_.resize(paint_op_buffer_.size(), visual_rect);
GrowCurrentBeginItemVisualRect(visual_rect);
}
void EndPaintOfPairedBegin() {
#if DCHECK_IS_ON()
DCHECK(IsPainting());
DCHECK_LT(current_range_start_, paint_op_buffer_.size());
current_range_start_ = kNotPainting;
#endif
if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
return;
DCHECK_LT(visual_rects_.size(), paint_op_buffer_.size());
size_t count = paint_op_buffer_.size() - visual_rects_.size();
paired_begin_stack_.push_back({visual_rects_.size(), count});
visual_rects_.resize(paint_op_buffer_.size());
}
void EndPaintOfPairedEnd();
// Called after all items are appended, to process the items.
void Finalize();
struct DirectlyCompositedImageResult {
gfx::Size intrinsic_image_size;
bool nearest_neighbor;
};
// If this list represents an image that should be directly composited (i.e.
// rasterized at the intrinsic size of the image), return the intrinsic size
// of the image and whether or not to use nearest neighbor filtering when
// scaling the layer.
base::Optional<DirectlyCompositedImageResult>
GetDirectlyCompositedImageResult(gfx::Size containing_layer_bounds) const;
int NumSlowPaths() const { return paint_op_buffer_.numSlowPaths(); }
bool HasNonAAPaint() const { return paint_op_buffer_.HasNonAAPaint(); }
// This gives the total number of PaintOps.
size_t TotalOpCount() const { return paint_op_buffer_.total_op_count(); }
size_t BytesUsed() const;
const DiscardableImageMap& discardable_image_map() const {
return image_map_;
}
base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
TakeDecodingModeMap() {
return image_map_.TakeDecodingModeMap();
}
void EmitTraceSnapshot() const;
void GenerateDiscardableImagesMetadata();
gfx::Rect VisualRectForTesting(int index) { return visual_rects_[index]; }
// Generate a PaintRecord from this DisplayItemList, leaving |this| in
// an empty state.
sk_sp<PaintRecord> ReleaseAsRecord();
// If a rectangle is solid color, returns that color. |max_ops_to_analyze|
// indicates the maximum number of draw ops we consider when determining if a
// rectangle is solid color.
bool GetColorIfSolidInRect(const gfx::Rect& rect,
SkColor* color,
int max_ops_to_analyze = 1);
std::string ToString() const;
bool has_draw_ops() const { return paint_op_buffer_.has_draw_ops(); }
bool has_draw_text_ops() const {
return paint_op_buffer_.has_draw_text_ops();
}
// Ops with nested paint ops are considered as a single op.
size_t num_paint_ops() const { return paint_op_buffer_.size(); }
bool NeedsAdditionalInvalidationForLCDText(
const DisplayItemList& old_list) const {
return paint_op_buffer_.NeedsAdditionalInvalidationForLCDText(
old_list.paint_op_buffer_);
}
private:
friend class DisplayItemListTest;
friend gpu::raster::RasterImplementation;
friend gpu::raster::RasterImplementationGLES;
~DisplayItemList();
void Reset();
std::unique_ptr<base::trace_event::TracedValue> CreateTracedValue(
bool include_items) const;
void AddToValue(base::trace_event::TracedValue*, bool include_items) const;
// If we're currently within a paired display item block, unions the
// given visual rect with the begin display item's visual rect.
void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect) {
DCHECK_EQ(usage_hint_, kTopLevelDisplayItemList);
if (!paired_begin_stack_.empty())
visual_rects_[paired_begin_stack_.back().first_index].Union(visual_rect);
}
// RTree stores indices into the paint op buffer.
// TODO(vmpstr): Update the rtree to store offsets instead.
RTree<size_t> rtree_;
DiscardableImageMap image_map_;
PaintOpBuffer paint_op_buffer_;
// The visual rects associated with each of the display items in the
// display item list. These rects are intentionally kept separate because they
// are used to decide which ops to walk for raster.
std::vector<gfx::Rect> visual_rects_;
// Byte offsets associated with each of the ops.
std::vector<size_t> offsets_;
// A stack of paired begin sequences that haven't been closed.
struct PairedBeginInfo {
// Index (into virual_rects_ and offsets_) of the first operation in the
// paired begin sequence.
size_t first_index;
// Number of operations in the paired begin sequence.
size_t count;
};
std::vector<PairedBeginInfo> paired_begin_stack_;
#if DCHECK_IS_ON()
// While recording a range of ops, this is the position in the PaintOpBuffer
// where the recording started.
bool IsPainting() const { return current_range_start_ != kNotPainting; }
const size_t kNotPainting = static_cast<size_t>(-1);
size_t current_range_start_ = kNotPainting;
#endif
UsageHint usage_hint_;
friend class base::RefCountedThreadSafe<DisplayItemList>;
FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, BytesUsed);
};
} // namespace cc
#endif // CC_PAINT_DISPLAY_ITEM_LIST_H_