#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/compiler_specific.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()>;
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;
float destination_hdr_headroom = 0.f;
};
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;
};
static constexpr int kMinNumberOfSlowPathsForMSAA = 6;
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();
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;
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;
std::vector<uint8_t>& scratch_buffer;
bool crash_dump_on_failure = false;
bool is_privileged = false;
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;
PaintOpBuffer(PaintOpBuffer&& other);
~PaintOpBuffer() override;
PaintOpBuffer& operator=(const PaintOpBuffer&) = delete;
PaintOpBuffer& operator=(PaintOpBuffer&& other);
void Reset();
void Playback(SkCanvas* canvas) const;
void Playback(SkCanvas* canvas,
const PlaybackParams& params,
bool local_ctm = true) const;
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);
static SkRect GetFixedScaleBounds(const SkMatrix& ctm,
const SkRect& bounds,
int max_texture_size = 0);
size_t size() const { return op_count_; }
bool empty() const { return !size(); }
size_t bytes_used() const {
return sizeof(*this) + data_.size() + subrecord_bytes_used_;
}
size_t paint_ops_size() const { return used_ + subrecord_bytes_used_; }
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;
void ShrinkToFit();
PaintRecord ReleaseAsRecord();
PaintRecord DeepCopyAsRecord();
bool EqualsForTesting(const PaintOpBuffer& other) const;
const PaintOp& GetFirstOp() const {
DCHECK(!empty());
return reinterpret_cast<const PaintOp&>(*data_.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 uint8_t*>(&op), data_.data());
size_t result = static_cast<size_t>(reinterpret_cast<const uint8_t*>(&op) -
data_.data());
DCHECK_LT(result, used_);
return result;
}
const uint8_t* DataBufferForTesting() const { return data_.data(); }
const PaintOp& GetOpAtForTesting(size_t index) const;
class Iterator;
class OffsetIterator;
class CompositeIterator;
class PlaybackFoldingIterator;
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 BufferData = base::HeapArray<uint8_t, base::AlignedFreeDeleter>;
bool is_mutable() const { return unique(); }
void DestroyOps();
void Playback(SkCanvas* canvas,
const PlaybackParams& params,
bool local_ctm,
const std::vector<size_t>* offsets) const;
BufferData ReallocBuffer(size_t new_size);
BufferData ReallocIfNeededToFit();
void* AllocatePaintOp(uint16_t aligned_size) {
DCHECK(is_mutable());
if (used_ + aligned_size > data_.size()) {
return AllocatePaintOpSlowPath(aligned_size);
} else {
void* op = &data_[used_];
used_ += aligned_size;
op_count_++;
return op;
}
}
void* AllocatePaintOpSlowPath(uint16_t aligned_size);
void ResetRetainingBuffer();
BufferData data_;
size_t used_ = 0;
size_t op_count_ = 0;
size_t subrecord_bytes_used_ = 0;
size_t subrecord_op_count_ = 0;
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;
};
}
#endif