blob: 171d935d58ef8635119efd964bde6533dc7d5401 [file] [log] [blame]
// Copyright 2013 The Flutter 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 <type_traits>
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/display_list_canvas_dispatcher.h"
#include "flutter/display_list/display_list_ops.h"
#include "flutter/display_list/display_list_utils.h"
#include "flutter/fml/trace_event.h"
namespace flutter {
const SaveLayerOptions SaveLayerOptions::kNoAttributes = SaveLayerOptions();
const SaveLayerOptions SaveLayerOptions::kWithAttributes =
kNoAttributes.with_renders_with_attributes();
DisplayList::DisplayList()
: byte_count_(0),
op_count_(0),
nested_byte_count_(0),
nested_op_count_(0),
unique_id_(0),
bounds_({0, 0, 0, 0}),
bounds_cull_({0, 0, 0, 0}),
can_apply_group_opacity_(true) {}
DisplayList::DisplayList(uint8_t* ptr,
size_t byte_count,
unsigned int op_count,
size_t nested_byte_count,
unsigned int nested_op_count,
const SkRect& cull_rect,
bool can_apply_group_opacity)
: storage_(ptr),
byte_count_(byte_count),
op_count_(op_count),
nested_byte_count_(nested_byte_count),
nested_op_count_(nested_op_count),
bounds_({0, 0, -1, -1}),
bounds_cull_(cull_rect),
can_apply_group_opacity_(can_apply_group_opacity) {
static std::atomic<uint32_t> nextID{1};
do {
unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed);
} while (unique_id_ == 0);
}
DisplayList::~DisplayList() {
uint8_t* ptr = storage_.get();
DisposeOps(ptr, ptr + byte_count_);
}
void DisplayList::ComputeBounds() {
RectBoundsAccumulator accumulator;
DisplayListBoundsCalculator calculator(accumulator, &bounds_cull_);
Dispatch(calculator);
if (calculator.is_unbounded()) {
FML_LOG(INFO) << "returning partial bounds for unbounded DisplayList";
}
bounds_ = accumulator.bounds();
}
void DisplayList::ComputeRTree() {
RTreeBoundsAccumulator accumulator;
DisplayListBoundsCalculator calculator(accumulator, &bounds_cull_);
Dispatch(calculator);
if (calculator.is_unbounded()) {
FML_LOG(INFO) << "returning partial rtree for unbounded DisplayList";
}
rtree_ = accumulator.rtree();
}
void DisplayList::Dispatch(Dispatcher& dispatcher,
uint8_t* ptr,
uint8_t* end) const {
TRACE_EVENT0("flutter", "DisplayList::Dispatch");
while (ptr < end) {
auto op = reinterpret_cast<const DLOp*>(ptr);
ptr += op->size;
FML_DCHECK(ptr <= end);
switch (op->type) {
#define DL_OP_DISPATCH(name) \
case DisplayListOpType::k##name: \
static_cast<const name##Op*>(op)->dispatch(dispatcher); \
break;
FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH)
#undef DL_OP_DISPATCH
default:
FML_DCHECK(false);
return;
}
}
}
void DisplayList::DisposeOps(uint8_t* ptr, uint8_t* end) {
while (ptr < end) {
auto op = reinterpret_cast<const DLOp*>(ptr);
ptr += op->size;
FML_DCHECK(ptr <= end);
switch (op->type) {
#define DL_OP_DISPOSE(name) \
case DisplayListOpType::k##name: \
if (!std::is_trivially_destructible_v<name##Op>) { \
static_cast<const name##Op*>(op)->~name##Op(); \
} \
break;
FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE)
#undef DL_OP_DISPOSE
default:
FML_DCHECK(false);
return;
}
}
}
static bool CompareOps(uint8_t* ptrA,
uint8_t* endA,
uint8_t* ptrB,
uint8_t* endB) {
// These conditions are checked by the caller...
FML_DCHECK((endA - ptrA) == (endB - ptrB));
FML_DCHECK(ptrA != ptrB);
uint8_t* bulkStartA = ptrA;
uint8_t* bulkStartB = ptrB;
while (ptrA < endA && ptrB < endB) {
auto opA = reinterpret_cast<const DLOp*>(ptrA);
auto opB = reinterpret_cast<const DLOp*>(ptrB);
if (opA->type != opB->type || opA->size != opB->size) {
return false;
}
ptrA += opA->size;
ptrB += opB->size;
FML_DCHECK(ptrA <= endA);
FML_DCHECK(ptrB <= endB);
DisplayListCompare result;
switch (opA->type) {
#define DL_OP_EQUALS(name) \
case DisplayListOpType::k##name: \
result = static_cast<const name##Op*>(opA)->equals( \
static_cast<const name##Op*>(opB)); \
break;
FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS)
#undef DL_OP_EQUALS
default:
FML_DCHECK(false);
return false;
}
switch (result) {
case DisplayListCompare::kNotEqual:
return false;
case DisplayListCompare::kUseBulkCompare:
break;
case DisplayListCompare::kEqual:
// Check if we have a backlog of bytes to bulk compare and then
// reset the bulk compare pointers to the address following this op
auto bulkBytes = reinterpret_cast<const uint8_t*>(opA) - bulkStartA;
if (bulkBytes > 0) {
if (memcmp(bulkStartA, bulkStartB, bulkBytes) != 0) {
return false;
}
}
bulkStartA = ptrA;
bulkStartB = ptrB;
break;
}
}
if (ptrA != endA || ptrB != endB) {
return false;
}
if (bulkStartA < ptrA) {
// Perform a final bulk compare if we have remaining bytes waiting
if (memcmp(bulkStartA, bulkStartB, ptrA - bulkStartA) != 0) {
return false;
}
}
return true;
}
void DisplayList::RenderTo(DisplayListBuilder* builder,
SkScalar opacity) const {
// TODO(100983): Opacity is not respected and attributes are not reset.
if (!builder) {
return;
}
Dispatch(*builder);
}
void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const {
DisplayListCanvasDispatcher dispatcher(canvas, opacity);
Dispatch(dispatcher);
}
bool DisplayList::Equals(const DisplayList* other) const {
if (this == other) {
return true;
}
if (byte_count_ != other->byte_count_ || op_count_ != other->op_count_) {
return false;
}
uint8_t* ptr = storage_.get();
uint8_t* o_ptr = other->storage_.get();
if (ptr == o_ptr) {
return true;
}
return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other->byte_count_);
}
} // namespace flutter