1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_H_
#include <memory>
#include <optional>
#include <utility>
#include "base/check_op.h"
#include "base/dcheck_is_on.h"
#include "base/memory/ptr_util.h"
#include "cc/input/hit_test_opaqueness.h"
#include "cc/input/layer_selection_bound.h"
#include "cc/paint/element_id.h"
#include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_under_invalidation_checker.h"
#include "third_party/blink/renderer/platform/graphics/paint/region_capture_data.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/geometry/rect.h"
class SkTextBlob;
namespace blink {
enum class PaintBenchmarkMode {
kNormal,
kForceRasterInvalidationAndConvert,
kForcePaintArtifactCompositorUpdate,
// Tests PaintController performance of moving cached subsequences.
kForcePaint,
// Tests performance of core paint tree walk and moving cached display items.
kSubsequenceCachingDisabled,
// Tests performance of full repaint.
kCachingDisabled,
};
// FrameFirstPaint stores first-paint, text or image painted for the
// corresponding frame. They are never reset to false. First-paint is defined in
// https://github.com/WICG/paint-timing. It excludes default background paint.
struct FrameFirstPaint {
DISALLOW_NEW();
explicit FrameFirstPaint(const void* frame)
: frame(frame),
first_painted(false),
text_painted(false),
image_painted(false) {}
const void* frame = nullptr;
bool first_painted : 1;
bool text_painted : 1;
bool image_painted : 1;
};
struct SubsequenceMarkers {
DISALLOW_NEW();
DisplayItemClientId client_id = kInvalidDisplayItemClientId;
// The start and end (not included) index of paint chunks in this
// subsequence.
wtf_size_t start_chunk_index = 0;
wtf_size_t end_chunk_index = 0;
bool is_moved_from_cached_subsequence = false;
};
struct SubsequencesData {
DISALLOW_NEW();
// Map a client to the index into |tree|.
HashMap<DisplayItemClientId, wtf_size_t> map;
// A pre-order list of the subsequence tree.
Vector<SubsequenceMarkers> tree;
};
class PaintController;
class PLATFORM_EXPORT PaintControllerPersistentData
: public GarbageCollected<PaintControllerPersistentData> {
public:
void Trace(Visitor* visitor) const {
visitor->Trace(current_paint_artifact_);
}
// Returns the approximate memory usage owned by by this data.
size_t ApproximateUnsharedMemoryUsage() const;
// Get the artifact generated after the last commit.
const PaintArtifact& GetPaintArtifact() const {
return *current_paint_artifact_;
}
const DisplayItemList& GetDisplayItemList() const {
return GetPaintArtifact().GetDisplayItemList();
}
const PaintChunks& GetPaintChunks() const {
return GetPaintArtifact().GetPaintChunks();
}
void InvalidateAllForTesting();
bool ClientCacheIsValid(const DisplayItemClient&) const;
wtf_size_t GetSubsequenceIndex(DisplayItemClientId) const;
const SubsequenceMarkers* GetSubsequenceMarkers(DisplayItemClientId) const;
private:
friend class PaintController;
void CommitNewDisplayItems(PaintArtifact& new_paint_artifact,
SubsequencesData new_subsequences);
Member<PaintArtifact> current_paint_artifact_ =
MakeGarbageCollected<PaintArtifact>();
SubsequencesData current_subsequences_;
bool cache_is_all_invalid_ = true;
};
// Responsible for processing display items as they are produced, and producing
// a final paint artifact when complete. This class includes logic for caching,
// cache invalidation, and merging.
class PLATFORM_EXPORT PaintController {
STACK_ALLOCATED();
public:
explicit PaintController(bool record_debug_info = false,
PaintControllerPersistentData* = nullptr,
PaintBenchmarkMode = PaintBenchmarkMode::kNormal);
PaintController(const PaintController&) = delete;
PaintController& operator=(const PaintController&) = delete;
~PaintController();
bool HasPersistentData() const { return persistent_data_; }
void SetRecordDebugInfo(bool record_debug_info) {
record_debug_info_ = record_debug_info;
}
// Provide a new set of paint chunk properties to apply to recorded display
// items. If id is nullptr, the id of the first display item will be used as
// the id of the paint chunk if needed.
void UpdateCurrentPaintChunkProperties(const PaintChunk::Id&,
const DisplayItemClient&,
const PropertyTreeStateOrAlias&);
void UpdateCurrentPaintChunkProperties(const PropertyTreeStateOrAlias&);
const PropertyTreeStateOrAlias& CurrentPaintChunkProperties() const {
return paint_chunker_.CurrentPaintChunkProperties();
}
void SetCurrentEffectivelyInvisible(bool invisible) {
paint_chunker_.SetCurrentEffectivelyInvisible(invisible);
}
bool CurrentEffectivelyInvisible() const {
return paint_chunker_.CurrentEffectivelyInvisible();
}
void EnsureChunk();
bool CurrentChunkIsNonEmptyAndTransparentToHitTest() const {
return paint_chunker_.CurrentChunkIsNonEmptyAndTransparentToHitTest();
}
void RecordHitTestData(const DisplayItemClient&,
const gfx::Rect&,
TouchAction,
bool blocking_wheel,
cc::HitTestOpaqueness,
DisplayItem::Type type = DisplayItem::kHitTest);
void RecordRegionCaptureData(const DisplayItemClient& client,
const RegionCaptureCropId& crop_id,
const gfx::Rect& rect);
void RecordScrollHitTestData(
const DisplayItemClient&,
DisplayItem::Type,
const TransformPaintPropertyNode* scroll_translation,
const gfx::Rect& scroll_hit_test_rect,
cc::HitTestOpaqueness,
const gfx::Rect& scrolling_contents_cull_rect = InfiniteIntRect());
void RecordSelection(std::optional<PaintedSelectionBound> start,
std::optional<PaintedSelectionBound> end,
String debug_info);
void RecordAnySelectionWasPainted() {
paint_chunker_.RecordAnySelectionWasPainted();
}
wtf_size_t NumNewChunks() const {
return new_paint_artifact_->GetPaintChunks().size();
}
const gfx::Rect& LastChunkBounds() const {
return new_paint_artifact_->GetPaintChunks().back().bounds;
}
const TraceablePropertyTreeStateOrAlias& LastChunkProperties() const {
return new_paint_artifact_->GetPaintChunks().back().properties;
}
void MarkClientForValidation(const DisplayItemClient& client);
template <typename DisplayItemClass, typename... Args>
void CreateAndAppend(const DisplayItemClient& client, Args&&... args) {
MarkClientForValidation(client);
DisplayItemClass& display_item =
new_paint_artifact_->GetDisplayItemList()
.AllocateAndConstruct<DisplayItemClass>(
client.Id(), std::forward<Args>(args)...);
display_item.SetFragment(current_fragment_);
ProcessNewItem(client, display_item);
}
// Tries to find the cached display item corresponding to the given
// parameters. If found, appends the cached display item to the new display
// list and returns true. Otherwise returns false.
bool UseCachedItemIfPossible(const DisplayItemClient&, DisplayItem::Type);
#if DCHECK_IS_ON()
void AssertLastCheckedCachedItem(const DisplayItemClient&, DisplayItem::Type);
#endif
// Returns the SkTextBlob in DrawTextBlobOp in
// MatchingCachedItemToBeRepainted() if it exists and can be reused for
// repainting.
sk_sp<SkTextBlob> CachedTextBlob() const;
// Tries to find the cached subsequence corresponding to the given parameters.
// If found, copies the cache subsequence to the new display list and returns
// true. Otherwise returns false.
bool UseCachedSubsequenceIfPossible(const DisplayItemClient&);
// Returns the index of the new subsequence.
wtf_size_t BeginSubsequence(const DisplayItemClient&);
// The |subsequence_index| parameter should be the return value of the
// corresponding BeginSubsequence().
void EndSubsequence(wtf_size_t subsequence_index);
void BeginSkippingCache() {
if (persistent_data_) {
++skipping_cache_count_;
}
}
void EndSkippingCache() {
if (persistent_data_) {
CHECK_GT(skipping_cache_count_, 0u);
--skipping_cache_count_;
}
}
bool IsSkippingCache() const {
return !persistent_data_ || skipping_cache_count_;
}
// Must be called when a painting is finished. If associated with persistent
// data, updates the current paint artifact in the persistent data with the
// new paintings. Returns the new paint artifact (which is mainly for
// transient (i.e. no persistent data) usages).
const PaintArtifact& CommitNewDisplayItems();
bool IsCheckingUnderInvalidationForTesting() const;
void SetFirstPainted();
void SetTextPainted();
void SetImagePainted();
#if DCHECK_IS_ON()
void ShowCompactDebugData() const;
String DebugDataAsString(
DisplayItemList::JsonOption = DisplayItemList::kDefault) const;
void ShowDebugData() const;
void ShowDebugDataWithPaintRecords() const;
#endif
void BeginFrame(const void* frame);
FrameFirstPaint EndFrame(const void* frame);
// The current fragment will be part of the ids of all display items and
// paint chunks, to uniquely identify display items in different fragments
// for the same client and type.
wtf_size_t CurrentFragment() const { return current_fragment_; }
void SetCurrentFragment(wtf_size_t fragment) { current_fragment_ = fragment; }
class CounterForTesting {
STACK_ALLOCATED();
public:
CounterForTesting() {
DCHECK(!PaintController::counter_for_testing_);
PaintController::counter_for_testing_ = this;
}
~CounterForTesting() {
DCHECK_EQ(this, PaintController::counter_for_testing_);
PaintController::counter_for_testing_ = nullptr;
}
void Reset() { num_cached_items = num_cached_subsequences = 0; }
size_t num_cached_items = 0;
size_t num_cached_subsequences = 0;
};
private:
friend class PaintControllerTestBase;
friend class PaintControllerPaintTestBase;
friend class PaintControllerPersistentData;
friend class PaintUnderInvalidationChecker;
const PaintArtifact& CurrentPaintArtifact() const {
DCHECK(persistent_data_);
return *persistent_data_->current_paint_artifact_;
}
PaintArtifact& CurrentPaintArtifact() {
DCHECK(persistent_data_);
return *persistent_data_->current_paint_artifact_;
}
const DisplayItemList& CurrentDisplayItemList() const {
return CurrentPaintArtifact().GetDisplayItemList();
}
DisplayItemList& CurrentDisplayItemList() {
return CurrentPaintArtifact().GetDisplayItemList();
}
const PaintChunks& CurrentPaintChunks() const {
return CurrentPaintArtifact().GetPaintChunks();
}
PaintChunks& CurrentPaintChunks() {
return CurrentPaintArtifact().GetPaintChunks();
}
const SubsequencesData& CurrentSubsequences() const {
DCHECK(persistent_data_);
return persistent_data_->current_subsequences_;
}
SubsequencesData& CurrentSubsequences() {
DCHECK(persistent_data_);
return persistent_data_->current_subsequences_;
}
void RecordDebugInfo(const DisplayItemClient&);
// True if all display items associated with the client are validly cached.
// However, the current algorithm allows the following situations even if
// ClientCacheIsValid() is true for a client during painting:
// 1. The client paints a new display item that is not cached:
// UseCachedItemIfPossible() returns false for the display item and the
// newly painted display item will be added into the cache. This situation
// has slight performance hit (see FindOutOfOrderCachedItemForward()) so we
// print a warning in the situation and should keep it rare.
// 2. the client no longer paints a display item that is cached: the cached
// display item will be removed. This doesn't affect performance.
bool ClientCacheIsValid(const DisplayItemClient&) const;
// Set new item state (cache skipping, etc) for the last new display item.
void ProcessNewItem(const DisplayItemClient&, DisplayItem&);
// This can only be called if the previous UseCachedItemIfPossible() returned
// false. Returns the cached display item that was matched in the previous
// UseCachedItemIfPossible() for an invalidated DisplayItemClient for
// non-layout reason, or nullptr if there is no such item.
const DisplayItem* MatchingCachedItemToBeRepainted() const;
void CheckNewItem(DisplayItem&);
void CheckNewChunkId(const PaintChunk::Id&);
void CheckNewChunk();
// Maps a display item id to the index of the display item or the paint chunk.
using IdIndexMap = HashMap<DisplayItem::Id::HashKey, wtf_size_t>;
static wtf_size_t FindItemFromIdIndexMap(const DisplayItem::Id&,
const IdIndexMap&,
const DisplayItemList&);
static void AddToIdIndexMap(const DisplayItem::Id&,
wtf_size_t index,
IdIndexMap&);
wtf_size_t FindCachedItem(const DisplayItemClient&, const DisplayItem::Id&);
wtf_size_t FindOutOfOrderCachedItemForward(const DisplayItemClient&,
const DisplayItem::Id&);
void AppendSubsequenceByMoving(const DisplayItemClient&,
wtf_size_t subsequence_index,
wtf_size_t start_chunk_index,
wtf_size_t end_chunk_index);
wtf_size_t GetSubsequenceIndex(DisplayItemClientId id) const {
return persistent_data_->GetSubsequenceIndex(id);
}
const SubsequenceMarkers* GetSubsequenceMarkers(
DisplayItemClientId id) const {
return persistent_data_->GetSubsequenceMarkers(id);
}
void ValidateNewChunkClient(const DisplayItemClient&);
PaintUnderInvalidationChecker& EnsureUnderInvalidationChecker();
ALWAYS_INLINE bool IsCheckingUnderInvalidation() const;
#if DCHECK_IS_ON()
void ShowDebugDataInternal(DisplayItemList::JsonOption) const;
#endif
PaintControllerPersistentData* const persistent_data_;
// Data being used to build the next paint artifact. It's not null until
// CommitNewDisplayItems().
PaintArtifact* new_paint_artifact_ = MakeGarbageCollected<PaintArtifact>();
PaintChunker paint_chunker_;
HeapVector<Member<const DisplayItemClient>> clients_to_validate_;
// Saves the original persistent_data_->current_paint_artifact_ after calling
// CommitNewDisplayItems. The data will be cleared when this PaintController
// is destructed to reduce GC overhead.
PaintArtifact* old_paint_artifact_ = nullptr;
// A stack recording current frames' first paints.
Vector<FrameFirstPaint> frame_first_paints_;
bool record_debug_info_ = false;
unsigned skipping_cache_count_ = 0;
wtf_size_t num_cached_new_items_ = 0;
wtf_size_t num_cached_new_subsequences_ = 0;
// Maps from ids to indices of valid cacheable display items in
// current_paint_artifact_.GetDisplayItemList() that have not been matched by
// requests of cached display items (using UseCachedItemIfPossible() and
// UseCachedSubsequenceIfPossible()) during sequential matching. The indexed
// items will be matched by later out-of-order requests of cached display
// items. This ensures that when out-of-order cached display items are
// requested, we only traverse at most once over the current display list
// looking for potential matches. Thus we can ensure that the algorithm runs
// in linear time.
IdIndexMap out_of_order_item_id_index_map_;
wtf_size_t current_fragment_ = 0;
// The next item in the current list for sequential match.
wtf_size_t next_item_to_match_ = 0;
// The next item in the current list to be indexed for out-of-order cache
// requests.
wtf_size_t next_item_to_index_ = 0;
wtf_size_t last_matching_item_ = kNotFound;
PaintInvalidationReason last_matching_client_invalidation_reason_ =
PaintInvalidationReason::kNone;
#if DCHECK_IS_ON()
wtf_size_t num_indexed_items_ = 0;
wtf_size_t num_sequential_matches_ = 0;
wtf_size_t num_out_of_order_matches_ = 0;
// This is used to check duplicated ids during CreateAndAppend().
IdIndexMap new_display_item_id_index_map_;
// This is used to check duplicated ids for new paint chunks.
IdIndexMap new_paint_chunk_id_index_map_;
DisplayItem::Id::HashKey last_checked_cached_item_id_;
#endif
const PaintBenchmarkMode benchmark_mode_;
std::optional<PaintUnderInvalidationChecker> under_invalidation_checker_;
SubsequencesData new_subsequences_;
static CounterForTesting* counter_for_testing_;
class PaintArtifactAsJSON;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_H_
|