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
cc / paint / paint_op_buffer.h [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#ifndef CC_PAINT_PAINT_OP_BUFFER_H_
#define CC_PAINT_PAINT_OP_BUFFER_H_
#include <limits>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "base/bits.h"
#include "base/check_op.h"
#include "base/functional/callback.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/stack_allocated.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/scroll_offset_map.h"
#include "third_party/skia/include/core/SkM44.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/display_color_spaces.h"
class SkCanvas;
class SkColorSpace;
class SkImage;
class SkStrikeClient;
class SkStrikeServer;
namespace gpu {
struct Mailbox;
}
namespace cc {
class ClientPaintCache;
class ImageProvider;
class PaintOp;
class PaintRecord;
class ServicePaintCache;
class SkottieSerializationHistory;
class TransferCacheDeserializeHelper;
class TransferCacheSerializeHelper;
enum class PaintOpType : uint8_t;
struct CC_PAINT_EXPORT PlaybackCallbacks {
STACK_ALLOCATED();
public:
PlaybackCallbacks();
~PlaybackCallbacks();
PlaybackCallbacks(const PlaybackCallbacks&);
PlaybackCallbacks& operator=(const PlaybackCallbacks&);
using CustomDataRasterCallback =
base::RepeatingCallback<void(SkCanvas* canvas, uint32_t id)>;
using DidDrawOpCallback = base::RepeatingCallback<void()>;
// This callback returns
// - &op if no conversion;
// - a pointer (owned by the callback) to a new op;
// - null if the op should be discarded.
using ConvertOpCallback =
base::RepeatingCallback<const PaintOp*(const PaintOp& op)>;
CustomDataRasterCallback custom_callback;
DidDrawOpCallback did_draw_op_callback;
ConvertOpCallback convert_op_callback;
};
struct CC_PAINT_EXPORT PlaybackParams {
STACK_ALLOCATED();
public:
explicit PlaybackParams(
ImageProvider* image_provider = nullptr,
const SkM44& original_ctm = SkM44(),
const PlaybackCallbacks& callbacks = PlaybackCallbacks());
~PlaybackParams();
ImageProvider* image_provider = nullptr;
SkM44 original_ctm;
PlaybackCallbacks callbacks;
std::optional<bool> save_layer_alpha_should_preserve_lcd_text;
const ScrollOffsetMap* raster_inducing_scroll_offsets = nullptr;
bool is_analyzing = false;
};
class CC_PAINT_EXPORT SharedImageProvider {
public:
enum class Error {
kNoError,
kUnknownMailbox,
kNoAccess,
kSkImageCreationFailed,
};
virtual ~SharedImageProvider() = default;
virtual sk_sp<SkImage> OpenSharedImageForRead(const gpu::Mailbox& mailbox,
Error& error) = 0;
};
// Defined outside of the class as this const is used in multiple files.
static constexpr int kMinNumberOfSlowPathsForMSAA = 6;
// PaintOpBuffer is a reimplementation of SkLiteDL.
// See: third_party/skia/src/core/SkLiteDL.h.
class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
public:
struct CC_PAINT_EXPORT SerializeOptions {
STACK_ALLOCATED();
public:
SerializeOptions();
SerializeOptions(
ImageProvider* image_provider,
TransferCacheSerializeHelper* transfer_cache,
ClientPaintCache* paint_cache,
SkStrikeServer* strike_server,
sk_sp<SkColorSpace> color_space,
SkottieSerializationHistory* skottie_serialization_history,
bool can_use_lcd_text,
bool context_supports_distance_field_text,
int max_texture_size,
const ScrollOffsetMap* raster_inducing_scroll_offsets = nullptr);
SerializeOptions(const SerializeOptions&);
SerializeOptions& operator=(const SerializeOptions&);
~SerializeOptions();
// Required.
ImageProvider* image_provider = nullptr;
TransferCacheSerializeHelper* transfer_cache = nullptr;
ClientPaintCache* paint_cache = nullptr;
SkStrikeServer* strike_server = nullptr;
sk_sp<SkColorSpace> color_space;
SkottieSerializationHistory* skottie_serialization_history = nullptr;
bool can_use_lcd_text = false;
bool context_supports_distance_field_text = true;
int max_texture_size = 0;
const ScrollOffsetMap* raster_inducing_scroll_offsets = nullptr;
// TODO(crbug.com/40136055): Cleanup after study completion.
//
// If true, perform serializaion in a way that avoids serializing transient
// members, such as IDs, so that a stable digest can be calculated. This
// means that serialized output can't be deserialized correctly.
bool for_identifiability_study = false;
};
struct CC_PAINT_EXPORT DeserializeOptions {
STACK_ALLOCATED();
public:
TransferCacheDeserializeHelper* transfer_cache = nullptr;
ServicePaintCache* paint_cache = nullptr;
SkStrikeClient* strike_client = nullptr;
// Used to memcpy Skia flattenables into to avoid TOCTOU issues.
std::vector<uint8_t>& scratch_buffer;
// Do a DumpWithoutCrashing when serialization fails.
bool crash_dump_on_failure = false;
// True if the deserialization is happening on a privileged gpu channel.
// e.g. in the case of UI.
bool is_privileged = false;
// The HDR headroom to apply when deserializing.
// TODO(crbug.com/40281980): Move this to playback instead of
// deserialization.
float hdr_headroom = 1.f;
SharedImageProvider* shared_image_provider = nullptr;
};
enum { kInitialBufferSize = 4096 };
static constexpr size_t kPaintOpAlign = 8;
template <typename Op>
static constexpr uint16_t ComputeOpAlignedSize() {
constexpr size_t size = base::bits::AlignUp(sizeof(Op), kPaintOpAlign);
static_assert(size <= std::numeric_limits<uint16_t>::max());
return static_cast<uint16_t>(size);
}
PaintOpBuffer();
PaintOpBuffer(const PaintOpBuffer&) = delete;
// `other` will be reset to the initial state.
PaintOpBuffer(PaintOpBuffer&& other);
~PaintOpBuffer() override;
PaintOpBuffer& operator=(const PaintOpBuffer&) = delete;
// `other` will be reset in the initial state.
PaintOpBuffer& operator=(PaintOpBuffer&& other);
// Resets the PaintOpBuffer to the initial state, except that the current
// data buffer is retained.
void Reset();
// Replays the paint op buffer into the canvas.
void Playback(SkCanvas* canvas) const;
void Playback(SkCanvas* canvas,
const PlaybackParams& params,
bool local_ctm = true) const;
// Deserialize PaintOps from |input|. The original content will be
// overwritten.
bool Deserialize(const volatile void* input,
size_t input_size,
const DeserializeOptions& options);
static sk_sp<PaintOpBuffer> MakeFromMemory(const volatile void* input,
size_t input_size,
const DeserializeOptions& options);
// Given the |bounds| of a PaintOpBuffer that would be transformed by |ctm|
// when rendered, compute the bounds needed to raster the buffer at a fixed
// scale into an auxiliary image instead of rasterizing at scale dynamically.
// This is used to enforce scaling decisions made pre-serialization when
// rasterizing after deserializing the buffer.
static SkRect GetFixedScaleBounds(const SkMatrix& ctm,
const SkRect& bounds,
int max_texture_size = 0);
// Returns the size of the paint op buffer. That is, the number of ops
// contained in it.
size_t size() const { return op_count_; }
bool empty() const { return !size(); }
// Returns the number of bytes used by the paint op buffer.
size_t bytes_used() const {
return sizeof(*this) + reserved_ + subrecord_bytes_used_;
}
// Returns the number of bytes used by paint ops.
size_t paint_ops_size() const { return used_ + subrecord_bytes_used_; }
// Returns the total number of ops including sub-records.
size_t total_op_count() const { return op_count_ + subrecord_op_count_; }
size_t next_op_offset() const { return used_; }
int num_slow_paths_up_to_min_for_MSAA() const {
return num_slow_paths_up_to_min_for_MSAA_;
}
bool has_non_aa_paint() const { return has_non_aa_paint_; }
bool has_draw_ops() const { return has_draw_ops_; }
bool has_draw_text_ops() const { return has_draw_text_ops_; }
bool has_save_layer_ops() const { return has_save_layer_ops_; }
bool has_save_layer_alpha_ops() const { return has_save_layer_alpha_ops_; }
bool has_effects_preventing_lcd_text_for_save_layer_alpha() const {
return has_effects_preventing_lcd_text_for_save_layer_alpha_;
}
bool has_discardable_images() const { return has_discardable_images_; }
gfx::ContentColorUsage content_color_usage() const {
return content_color_usage_;
}
bool NeedsAdditionalInvalidationForLCDText(
const PaintOpBuffer& old_buffer) const;
// Resize the PaintOpBuffer to exactly fit the current amount of used space.
void ShrinkToFit();
// Takes the contents of this as a PaintRecord. The result is shrunk to fit.
// If the shrinking-to-fit allocates a new data buffer, this PaintOpBuffer
// retains the original data buffer for future use.
PaintRecord ReleaseAsRecord();
PaintRecord DeepCopyAsRecord();
bool EqualsForTesting(const PaintOpBuffer& other) const;
const PaintOp& GetFirstOp() const {
DCHECK(!empty());
return reinterpret_cast<const PaintOp&>(*data_);
}
template <typename T, typename... Args>
const T& push(Args&&... args) {
DCHECK(is_mutable());
static_assert(std::is_base_of<PaintOp, T>::value, "T not a PaintOp.");
static_assert(alignof(T) <= kPaintOpAlign, "");
uint16_t aligned_size = ComputeOpAlignedSize<T>();
T* op = reinterpret_cast<T*>(AllocatePaintOp(aligned_size));
new (op) T{std::forward<Args>(args)...};
DCHECK_EQ(op->type, static_cast<uint8_t>(T::kType));
DCHECK_EQ(aligned_size, op->AlignedSize());
AnalyzeAddedOp(op);
return *op;
}
void UpdateSaveLayerBounds(size_t offset, const SkRect& bounds);
template <typename T>
void AnalyzeAddedOp(const T* op) {
static_assert(!std::is_same<T, PaintOp>::value,
"AnalyzeAddedOp needs a subtype of PaintOp");
DCHECK(is_mutable());
DCHECK(op->IsValid());
if (num_slow_paths_up_to_min_for_MSAA_ < kMinNumberOfSlowPathsForMSAA) {
num_slow_paths_up_to_min_for_MSAA_ += op->CountSlowPathsFromFlags();
num_slow_paths_up_to_min_for_MSAA_ += op->CountSlowPaths();
}
has_non_aa_paint_ |= op->HasNonAAPaint();
subrecord_bytes_used_ += op->AdditionalBytesUsed();
subrecord_op_count_ += op->AdditionalOpCount();
has_draw_ops_ |= op->IsDrawOp();
has_draw_text_ops_ |= op->HasDrawTextOps();
has_save_layer_ops_ |= op->HasSaveLayerOps();
has_save_layer_alpha_ops_ |= op->HasSaveLayerAlphaOps();
has_effects_preventing_lcd_text_for_save_layer_alpha_ |=
op->HasEffectsPreventingLCDTextForSaveLayerAlpha();
has_discardable_images_ |= op->HasDiscardableImages(&content_color_usage_);
has_discardable_images_ |=
op->HasDiscardableImagesFromFlags(&content_color_usage_);
}
size_t GetOpOffsetForTracing(const PaintOp& op) const {
DCHECK_GE(reinterpret_cast<const char*>(&op), data_.get());
size_t result =
static_cast<size_t>(reinterpret_cast<const char*>(&op) - data_.get());
DCHECK_LT(result, used_);
return result;
}
const char* DataBufferForTesting() const { return data_.get(); }
const PaintOp& GetOpAtForTesting(size_t index) const;
class Iterator;
class OffsetIterator;
class CompositeIterator;
class PlaybackFoldingIterator;
// STL-like container support:
using value_type = PaintOp;
using const_iterator = Iterator;
Iterator begin() const;
Iterator end() const;
private:
friend class DisplayItemList;
friend class PaintOp;
friend class PaintOpBufferOffsetsTest;
friend class SolidColorAnalyzer;
using BufferDataPtr = std::unique_ptr<char, base::AlignedFreeDeleter>;
bool is_mutable() const { return unique(); }
void DestroyOps();
// Replays the paint op buffer into the canvas. If |indices| is specified, it
// contains indices in an increasing order and only the indices specified in
// the vector will be replayed.
void Playback(SkCanvas* canvas,
const PlaybackParams& params,
bool local_ctm,
const std::vector<size_t>* offsets) const;
// Creates a new buffer sized to `new_size`, copying the old to the new (if
// the old exists). Returns the old buffer.
BufferDataPtr ReallocBuffer(size_t new_size);
// Shrinks the buffer to fit `used_`. Returns the old buffer if this
// allocated a new buffer, or nullptr.
BufferDataPtr ReallocIfNeededToFit();
// Returns the allocated op.
void* AllocatePaintOp(uint16_t aligned_size) {
DCHECK(is_mutable());
if (used_ + aligned_size > reserved_) {
return AllocatePaintOpSlowPath(aligned_size);
} else {
void* op = data_.get() + used_;
used_ += aligned_size;
op_count_++;
return op;
}
}
void* AllocatePaintOpSlowPath(uint16_t aligned_size);
void ResetRetainingBuffer();
BufferDataPtr data_;
size_t used_ = 0;
size_t reserved_ = 0;
size_t op_count_ = 0;
// Record additional bytes used by referenced sub-records and display lists.
size_t subrecord_bytes_used_ = 0;
// Record total op count of referenced sub-record and display lists.
size_t subrecord_op_count_ = 0;
// Record paths for veto-to-msaa for gpu raster. Counting slow paths can be
// very expensive, we stop counting them once reaching the minimum number
// required for an MSAA sample count for raster.
int num_slow_paths_up_to_min_for_MSAA_ = 0;
bool has_non_aa_paint_ : 1 = false;
bool has_draw_ops_ : 1 = false;
bool has_draw_text_ops_ : 1 = false;
bool has_save_layer_ops_ : 1 = false;
bool has_save_layer_alpha_ops_ : 1 = false;
bool has_effects_preventing_lcd_text_for_save_layer_alpha_ : 1 = false;
bool has_discardable_images_ : 1 = false;
gfx::ContentColorUsage content_color_usage_ = gfx::ContentColorUsage::kSRGB;
};
} // namespace cc
#endif // CC_PAINT_PAINT_OP_BUFFER_H_