mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Revert "[DisplayList] Allow random access to ops through indexing" (flutter/engine#54661)
Reverts flutter/engine#54484 Fixes https://github.com/flutter/flutter/issues/153737
This commit is contained in:
parent
d109230b49
commit
28f75263ff
@ -220,60 +220,6 @@ static void BM_DisplayListDispatchDefault(
|
||||
}
|
||||
}
|
||||
|
||||
static void BM_DisplayListDispatchByIndexDefault(
|
||||
benchmark::State& state,
|
||||
DisplayListDispatchBenchmarkType type) {
|
||||
bool prepare_rtree = NeedPrepareRTree(type);
|
||||
DisplayListBuilder builder(prepare_rtree);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
InvokeAllOps(builder);
|
||||
}
|
||||
auto display_list = builder.Build();
|
||||
DlOpReceiverIgnore receiver;
|
||||
while (state.KeepRunning()) {
|
||||
DlIndex end = display_list->GetRecordCount();
|
||||
for (DlIndex i = 0u; i < end; i++) {
|
||||
display_list->Dispatch(receiver, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BM_DisplayListDispatchByIteratorDefault(
|
||||
benchmark::State& state,
|
||||
DisplayListDispatchBenchmarkType type) {
|
||||
bool prepare_rtree = NeedPrepareRTree(type);
|
||||
DisplayListBuilder builder(prepare_rtree);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
InvokeAllOps(builder);
|
||||
}
|
||||
auto display_list = builder.Build();
|
||||
DlOpReceiverIgnore receiver;
|
||||
while (state.KeepRunning()) {
|
||||
for (DlIndex i : *display_list) {
|
||||
display_list->Dispatch(receiver, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BM_DisplayListDispatchByVectorDefault(
|
||||
benchmark::State& state,
|
||||
DisplayListDispatchBenchmarkType type) {
|
||||
bool prepare_rtree = NeedPrepareRTree(type);
|
||||
DisplayListBuilder builder(prepare_rtree);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
InvokeAllOps(builder);
|
||||
}
|
||||
auto display_list = builder.Build();
|
||||
DlOpReceiverIgnore receiver;
|
||||
while (state.KeepRunning()) {
|
||||
std::vector<DlIndex> indices =
|
||||
display_list->GetCulledIndices(display_list->bounds());
|
||||
for (DlIndex index : indices) {
|
||||
display_list->Dispatch(receiver, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BM_DisplayListDispatchCull(benchmark::State& state,
|
||||
DisplayListDispatchBenchmarkType type) {
|
||||
bool prepare_rtree = NeedPrepareRTree(type);
|
||||
@ -290,26 +236,6 @@ static void BM_DisplayListDispatchCull(benchmark::State& state,
|
||||
}
|
||||
}
|
||||
|
||||
static void BM_DisplayListDispatchByVectorCull(
|
||||
benchmark::State& state,
|
||||
DisplayListDispatchBenchmarkType type) {
|
||||
bool prepare_rtree = NeedPrepareRTree(type);
|
||||
DisplayListBuilder builder(prepare_rtree);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
InvokeAllOps(builder);
|
||||
}
|
||||
auto display_list = builder.Build();
|
||||
SkRect rect = SkRect::MakeLTRB(0, 0, 100, 100);
|
||||
EXPECT_FALSE(rect.contains(display_list->bounds()));
|
||||
DlOpReceiverIgnore receiver;
|
||||
while (state.KeepRunning()) {
|
||||
std::vector<DlIndex> indices = display_list->GetCulledIndices(rect);
|
||||
for (DlIndex index : indices) {
|
||||
display_list->Dispatch(receiver, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_CAPTURE(BM_DisplayListBuilderDefault,
|
||||
kDefault,
|
||||
DisplayListBuilderBenchmarkType::kDefault)
|
||||
@ -444,24 +370,4 @@ BENCHMARK_CAPTURE(BM_DisplayListDispatchCull,
|
||||
DisplayListDispatchBenchmarkType::kCulledWithRtree)
|
||||
->Unit(benchmark::kMicrosecond);
|
||||
|
||||
BENCHMARK_CAPTURE(BM_DisplayListDispatchByIndexDefault,
|
||||
kDefaultNoRtree,
|
||||
DisplayListDispatchBenchmarkType::kDefaultNoRtree)
|
||||
->Unit(benchmark::kMicrosecond);
|
||||
|
||||
BENCHMARK_CAPTURE(BM_DisplayListDispatchByIteratorDefault,
|
||||
kDefaultNoRtree,
|
||||
DisplayListDispatchBenchmarkType::kDefaultNoRtree)
|
||||
->Unit(benchmark::kMicrosecond);
|
||||
|
||||
BENCHMARK_CAPTURE(BM_DisplayListDispatchByVectorDefault,
|
||||
kDefaultNoRtree,
|
||||
DisplayListDispatchBenchmarkType::kDefaultNoRtree)
|
||||
->Unit(benchmark::kMicrosecond);
|
||||
|
||||
BENCHMARK_CAPTURE(BM_DisplayListDispatchByVectorCull,
|
||||
kCulledWithRtree,
|
||||
DisplayListDispatchBenchmarkType::kCulledWithRtree)
|
||||
->Unit(benchmark::kMicrosecond);
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -29,21 +29,6 @@ DisplayList::DisplayList()
|
||||
root_is_unbounded_(false),
|
||||
max_root_blend_mode_(DlBlendMode::kClear) {}
|
||||
|
||||
// Eventually we should rework DisplayListBuilder to compute these and
|
||||
// deliver the vector alongside the storage.
|
||||
static std::vector<size_t> MakeOffsets(const DisplayListStorage& storage,
|
||||
size_t byte_count) {
|
||||
std::vector<size_t> offsets;
|
||||
const uint8_t* start = storage.get();
|
||||
const uint8_t* end = start + byte_count;
|
||||
const uint8_t* ptr = start;
|
||||
while (ptr < end) {
|
||||
offsets.push_back(ptr - start);
|
||||
ptr += reinterpret_cast<const DLOp*>(ptr)->size;
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
DisplayList::DisplayList(DisplayListStorage&& storage,
|
||||
size_t byte_count,
|
||||
uint32_t op_count,
|
||||
@ -59,7 +44,6 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
|
||||
bool root_is_unbounded,
|
||||
sk_sp<const DlRTree> rtree)
|
||||
: storage_(std::move(storage)),
|
||||
offsets_(MakeOffsets(storage_, byte_count)),
|
||||
byte_count_(byte_count),
|
||||
op_count_(op_count),
|
||||
nested_byte_count_(nested_byte_count),
|
||||
@ -89,114 +73,86 @@ uint32_t DisplayList::next_unique_id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
struct SaveInfo {
|
||||
SaveInfo(DlIndex previous_restore_index, bool save_was_needed)
|
||||
: previous_restore_index(previous_restore_index),
|
||||
save_was_needed(save_was_needed) {}
|
||||
class Culler {
|
||||
public:
|
||||
virtual ~Culler() = default;
|
||||
virtual bool init(DispatchContext& context) = 0;
|
||||
virtual void update(DispatchContext& context) = 0;
|
||||
};
|
||||
class NopCuller final : public Culler {
|
||||
public:
|
||||
static NopCuller instance;
|
||||
|
||||
DlIndex previous_restore_index;
|
||||
bool save_was_needed;
|
||||
~NopCuller() = default;
|
||||
|
||||
bool init(DispatchContext& context) override {
|
||||
// Setting next_render_index to 0 means that
|
||||
// all rendering ops will be at or after that
|
||||
// index so they will execute and all restore
|
||||
// indices will be after it as well so all
|
||||
// clip and transform operations will execute.
|
||||
context.next_render_index = 0;
|
||||
return true;
|
||||
}
|
||||
void update(DispatchContext& context) override {}
|
||||
};
|
||||
NopCuller NopCuller::instance = NopCuller();
|
||||
class VectorCuller final : public Culler {
|
||||
public:
|
||||
VectorCuller(const DlRTree* rtree, const std::vector<int>& rect_indices)
|
||||
: rtree_(rtree), cur_(rect_indices.begin()), end_(rect_indices.end()) {}
|
||||
|
||||
~VectorCuller() = default;
|
||||
|
||||
bool init(DispatchContext& context) override {
|
||||
if (cur_ < end_) {
|
||||
context.next_render_index = rtree_->id(*cur_++);
|
||||
return true;
|
||||
} else {
|
||||
// Setting next_render_index to MAX_INT means that
|
||||
// all rendering ops will be "before" that index and
|
||||
// they will skip themselves and all clip and transform
|
||||
// ops will see that the next render index is not
|
||||
// before the next restore index (even if both are MAX_INT)
|
||||
// and so they will also not execute.
|
||||
// None of this really matters because returning false
|
||||
// here should cause the Dispatch operation to abort,
|
||||
// but this value is conceptually correct if that short
|
||||
// circuit optimization isn't used.
|
||||
context.next_render_index = std::numeric_limits<int>::max();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void update(DispatchContext& context) override {
|
||||
if (++context.cur_index > context.next_render_index) {
|
||||
while (cur_ < end_) {
|
||||
context.next_render_index = rtree_->id(*cur_++);
|
||||
if (context.next_render_index >= context.cur_index) {
|
||||
// It should be rare that we have duplicate indices
|
||||
// but if we do, then having a while loop is a cheap
|
||||
// insurance for those cases.
|
||||
// The main cause of duplicate indices is when a
|
||||
// DrawDisplayListOp was added to this DisplayList and
|
||||
// both are computing an R-Tree, in which case the
|
||||
// builder method will forward all of the child
|
||||
// DisplayList's rects to this R-Tree with the same
|
||||
// op_index.
|
||||
return;
|
||||
}
|
||||
}
|
||||
context.next_render_index = std::numeric_limits<int>::max();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const DlRTree* rtree_;
|
||||
std::vector<int>::const_iterator cur_;
|
||||
std::vector<int>::const_iterator end_;
|
||||
};
|
||||
|
||||
void DisplayList::RTreeResultsToIndexVector(
|
||||
std::vector<DlIndex>& indices,
|
||||
const std::vector<int>& rtree_results) const {
|
||||
FML_DCHECK(rtree_);
|
||||
auto cur_rect = rtree_results.begin();
|
||||
auto end_rect = rtree_results.end();
|
||||
if (cur_rect >= end_rect) {
|
||||
return;
|
||||
}
|
||||
DlIndex next_render_index = rtree_->id(*cur_rect++);
|
||||
DlIndex next_restore_index = std::numeric_limits<DlIndex>::max();
|
||||
std::vector<SaveInfo> save_infos;
|
||||
for (DlIndex index = 0u; index < offsets_.size(); index++) {
|
||||
while (index > next_render_index) {
|
||||
if (cur_rect < end_rect) {
|
||||
next_render_index = rtree_->id(*cur_rect++);
|
||||
} else {
|
||||
// Nothing left to render.
|
||||
// Nothing left to do, but match our restores from the stack.
|
||||
while (!save_infos.empty()) {
|
||||
SaveInfo& info = save_infos.back();
|
||||
// stack top boolean tells us whether the local variable
|
||||
// next_restore_index should be executed. The local variable
|
||||
// then gets reset to the value stored in the stack top
|
||||
if (info.save_was_needed) {
|
||||
FML_DCHECK(next_restore_index < offsets_.size());
|
||||
indices.push_back(next_restore_index);
|
||||
}
|
||||
next_restore_index = info.previous_restore_index;
|
||||
save_infos.pop_back();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
const uint8_t* ptr = storage_.get() + offsets_[index];
|
||||
const DLOp* op = reinterpret_cast<const DLOp*>(ptr);
|
||||
switch (GetOpCategory(op->type)) {
|
||||
case DisplayListOpCategory::kAttribute:
|
||||
// Attributes are always needed
|
||||
indices.push_back(index);
|
||||
break;
|
||||
|
||||
case DisplayListOpCategory::kTransform:
|
||||
case DisplayListOpCategory::kClip:
|
||||
if (next_render_index < next_restore_index) {
|
||||
indices.push_back(index);
|
||||
}
|
||||
break;
|
||||
|
||||
case DisplayListOpCategory::kRendering:
|
||||
case DisplayListOpCategory::kSubDisplayList:
|
||||
if (index == next_render_index) {
|
||||
indices.push_back(index);
|
||||
}
|
||||
break;
|
||||
|
||||
case DisplayListOpCategory::kSave:
|
||||
case DisplayListOpCategory::kSaveLayer: {
|
||||
bool needed = (index < next_restore_index);
|
||||
save_infos.emplace_back(next_restore_index, needed);
|
||||
switch (op->type) {
|
||||
case DisplayListOpType::kSave:
|
||||
case DisplayListOpType::kSaveLayer:
|
||||
case DisplayListOpType::kSaveLayerBackdrop:
|
||||
next_restore_index =
|
||||
static_cast<const SaveOpBase*>(op)->restore_index;
|
||||
break;
|
||||
default:
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
if (needed) {
|
||||
indices.push_back(index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DisplayListOpCategory::kRestore: {
|
||||
FML_DCHECK(!save_infos.empty());
|
||||
FML_DCHECK(index == next_restore_index);
|
||||
SaveInfo& info = save_infos.back();
|
||||
next_restore_index = info.previous_restore_index;
|
||||
if (info.save_was_needed) {
|
||||
indices.push_back(index);
|
||||
}
|
||||
save_infos.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
case DisplayListOpCategory::kInvalidCategory:
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayList::Dispatch(DlOpReceiver& receiver) const {
|
||||
const uint8_t* base = storage_.get();
|
||||
for (size_t offset : offsets_) {
|
||||
DispatchOneOp(receiver, base + offset);
|
||||
}
|
||||
const uint8_t* ptr = storage_.get();
|
||||
Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance);
|
||||
}
|
||||
|
||||
void DisplayList::Dispatch(DlOpReceiver& receiver,
|
||||
@ -211,36 +167,52 @@ void DisplayList::Dispatch(DlOpReceiver& receiver,
|
||||
}
|
||||
if (!has_rtree() || cull_rect.contains(bounds())) {
|
||||
Dispatch(receiver);
|
||||
} else {
|
||||
auto op_indices = GetCulledIndices(cull_rect);
|
||||
const uint8_t* base = storage_.get();
|
||||
for (DlIndex index : op_indices) {
|
||||
DispatchOneOp(receiver, base + offsets_[index]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const DlRTree* rtree = this->rtree().get();
|
||||
FML_DCHECK(rtree != nullptr);
|
||||
const uint8_t* ptr = storage_.get();
|
||||
std::vector<int> rect_indices;
|
||||
rtree->search(cull_rect, &rect_indices);
|
||||
VectorCuller culler(rtree, rect_indices);
|
||||
Dispatch(receiver, ptr, ptr + byte_count_, culler);
|
||||
}
|
||||
|
||||
void DisplayList::DispatchOneOp(DlOpReceiver& receiver,
|
||||
const uint8_t* ptr) const {
|
||||
auto op = reinterpret_cast<const DLOp*>(ptr);
|
||||
switch (op->type) {
|
||||
#define DL_OP_DISPATCH(name) \
|
||||
case DisplayListOpType::k##name: \
|
||||
static_cast<const name##Op*>(op)->dispatch(receiver); \
|
||||
void DisplayList::Dispatch(DlOpReceiver& receiver,
|
||||
const uint8_t* ptr,
|
||||
const uint8_t* end,
|
||||
Culler& culler) const {
|
||||
DispatchContext context = {
|
||||
.receiver = receiver,
|
||||
.cur_index = 0,
|
||||
// next_render_index will be initialized by culler.init()
|
||||
.next_restore_index = std::numeric_limits<int>::max(),
|
||||
};
|
||||
if (!culler.init(context)) {
|
||||
return;
|
||||
}
|
||||
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(context); \
|
||||
break;
|
||||
|
||||
FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH)
|
||||
|
||||
FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH)
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
DL_OP_DISPATCH(SetSceneColorSource)
|
||||
DL_OP_DISPATCH(SetSceneColorSource)
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
|
||||
#undef DL_OP_DISPATCH
|
||||
|
||||
case DisplayListOpType::kInvalidOp:
|
||||
default:
|
||||
FML_DCHECK(false) << "Unrecognized op type: "
|
||||
<< static_cast<int>(op->type);
|
||||
default:
|
||||
FML_DCHECK(false);
|
||||
return;
|
||||
}
|
||||
culler.update(context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,7 +230,6 @@ void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) {
|
||||
break;
|
||||
|
||||
FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE)
|
||||
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
DL_OP_DISPOSE(SetSceneColorSource)
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
@ -271,154 +242,6 @@ void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) {
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListOpCategory DisplayList::GetOpCategory(DlIndex index) const {
|
||||
return GetOpCategory(GetOpType(index));
|
||||
}
|
||||
|
||||
DisplayListOpCategory DisplayList::GetOpCategory(DisplayListOpType type) {
|
||||
switch (type) {
|
||||
case DisplayListOpType::kSetAntiAlias:
|
||||
case DisplayListOpType::kSetInvertColors:
|
||||
case DisplayListOpType::kSetStrokeCap:
|
||||
case DisplayListOpType::kSetStrokeJoin:
|
||||
case DisplayListOpType::kSetStyle:
|
||||
case DisplayListOpType::kSetStrokeWidth:
|
||||
case DisplayListOpType::kSetStrokeMiter:
|
||||
case DisplayListOpType::kSetColor:
|
||||
case DisplayListOpType::kSetBlendMode:
|
||||
case DisplayListOpType::kClearColorFilter:
|
||||
case DisplayListOpType::kSetPodColorFilter:
|
||||
case DisplayListOpType::kClearColorSource:
|
||||
case DisplayListOpType::kSetPodColorSource:
|
||||
case DisplayListOpType::kSetImageColorSource:
|
||||
case DisplayListOpType::kSetRuntimeEffectColorSource:
|
||||
case DisplayListOpType::kClearImageFilter:
|
||||
case DisplayListOpType::kSetPodImageFilter:
|
||||
case DisplayListOpType::kSetSharedImageFilter:
|
||||
case DisplayListOpType::kClearMaskFilter:
|
||||
case DisplayListOpType::kSetPodMaskFilter:
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
case DisplayListOpType::kSetSceneColorSource:
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
return DisplayListOpCategory::kAttribute;
|
||||
|
||||
case DisplayListOpType::kSave:
|
||||
return DisplayListOpCategory::kSave;
|
||||
case DisplayListOpType::kSaveLayer:
|
||||
case DisplayListOpType::kSaveLayerBackdrop:
|
||||
return DisplayListOpCategory::kSaveLayer;
|
||||
case DisplayListOpType::kRestore:
|
||||
return DisplayListOpCategory::kRestore;
|
||||
|
||||
case DisplayListOpType::kTranslate:
|
||||
case DisplayListOpType::kScale:
|
||||
case DisplayListOpType::kRotate:
|
||||
case DisplayListOpType::kSkew:
|
||||
case DisplayListOpType::kTransform2DAffine:
|
||||
case DisplayListOpType::kTransformFullPerspective:
|
||||
case DisplayListOpType::kTransformReset:
|
||||
return DisplayListOpCategory::kTransform;
|
||||
|
||||
case DisplayListOpType::kClipIntersectRect:
|
||||
case DisplayListOpType::kClipIntersectOval:
|
||||
case DisplayListOpType::kClipIntersectRRect:
|
||||
case DisplayListOpType::kClipIntersectPath:
|
||||
case DisplayListOpType::kClipDifferenceRect:
|
||||
case DisplayListOpType::kClipDifferenceOval:
|
||||
case DisplayListOpType::kClipDifferenceRRect:
|
||||
case DisplayListOpType::kClipDifferencePath:
|
||||
return DisplayListOpCategory::kClip;
|
||||
|
||||
case DisplayListOpType::kDrawPaint:
|
||||
case DisplayListOpType::kDrawColor:
|
||||
case DisplayListOpType::kDrawLine:
|
||||
case DisplayListOpType::kDrawDashedLine:
|
||||
case DisplayListOpType::kDrawRect:
|
||||
case DisplayListOpType::kDrawOval:
|
||||
case DisplayListOpType::kDrawCircle:
|
||||
case DisplayListOpType::kDrawRRect:
|
||||
case DisplayListOpType::kDrawDRRect:
|
||||
case DisplayListOpType::kDrawArc:
|
||||
case DisplayListOpType::kDrawPath:
|
||||
case DisplayListOpType::kDrawPoints:
|
||||
case DisplayListOpType::kDrawLines:
|
||||
case DisplayListOpType::kDrawPolygon:
|
||||
case DisplayListOpType::kDrawVertices:
|
||||
case DisplayListOpType::kDrawImage:
|
||||
case DisplayListOpType::kDrawImageWithAttr:
|
||||
case DisplayListOpType::kDrawImageRect:
|
||||
case DisplayListOpType::kDrawImageNine:
|
||||
case DisplayListOpType::kDrawImageNineWithAttr:
|
||||
case DisplayListOpType::kDrawAtlas:
|
||||
case DisplayListOpType::kDrawAtlasCulled:
|
||||
case DisplayListOpType::kDrawTextBlob:
|
||||
case DisplayListOpType::kDrawTextFrame:
|
||||
case DisplayListOpType::kDrawShadow:
|
||||
case DisplayListOpType::kDrawShadowTransparentOccluder:
|
||||
return DisplayListOpCategory::kRendering;
|
||||
|
||||
case DisplayListOpType::kDrawDisplayList:
|
||||
return DisplayListOpCategory::kSubDisplayList;
|
||||
|
||||
case DisplayListOpType::kInvalidOp:
|
||||
return DisplayListOpCategory::kInvalidCategory;
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListOpType DisplayList::GetOpType(DlIndex index) const {
|
||||
// Assert unsigned type so we can eliminate >= 0 comparison
|
||||
static_assert(std::is_unsigned_v<DlIndex>);
|
||||
if (index >= offsets_.size()) {
|
||||
return DisplayListOpType::kInvalidOp;
|
||||
}
|
||||
|
||||
size_t offset = offsets_[index];
|
||||
FML_DCHECK(offset < byte_count_);
|
||||
auto ptr = storage_.get() + offset;
|
||||
auto op = reinterpret_cast<const DLOp*>(ptr);
|
||||
FML_DCHECK(ptr + op->size <= storage_.get() + byte_count_);
|
||||
return op->type;
|
||||
}
|
||||
|
||||
static void FillAllIndices(std::vector<DlIndex>& indices, DlIndex size) {
|
||||
indices.reserve(size);
|
||||
for (DlIndex i = 0u; i < size; i++) {
|
||||
indices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DlIndex> DisplayList::GetCulledIndices(
|
||||
const SkRect& cull_rect) const {
|
||||
std::vector<DlIndex> indices;
|
||||
if (!cull_rect.isEmpty()) {
|
||||
if (rtree_) {
|
||||
std::vector<int> rect_indices;
|
||||
rtree_->search(cull_rect, &rect_indices);
|
||||
RTreeResultsToIndexVector(indices, rect_indices);
|
||||
} else {
|
||||
FillAllIndices(indices, offsets_.size());
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
bool DisplayList::Dispatch(DlOpReceiver& receiver, DlIndex index) const {
|
||||
// Assert unsigned type so we can eliminate >= 0 comparison
|
||||
static_assert(std::is_unsigned_v<DlIndex>);
|
||||
if (index >= offsets_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = offsets_[index];
|
||||
FML_DCHECK(offset < byte_count_);
|
||||
auto ptr = storage_.get() + offset;
|
||||
FML_DCHECK(offset + reinterpret_cast<const DLOp*>(ptr)->size <= byte_count_);
|
||||
|
||||
DispatchOneOp(receiver, ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CompareOps(const uint8_t* ptrA,
|
||||
const uint8_t* endA,
|
||||
const uint8_t* ptrB,
|
||||
@ -447,7 +270,6 @@ static bool CompareOps(const uint8_t* ptrA,
|
||||
break;
|
||||
|
||||
FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS)
|
||||
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
DL_OP_EQUALS(SetSceneColorSource)
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
|
||||
@ -142,30 +142,12 @@ namespace flutter {
|
||||
#define DL_OP_TO_ENUM_VALUE(name) k##name,
|
||||
enum class DisplayListOpType {
|
||||
FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE)
|
||||
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
DL_OP_TO_ENUM_VALUE(SetSceneColorSource)
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
|
||||
// empty comment to make formatter happy
|
||||
kInvalidOp,
|
||||
kMaxOp = kInvalidOp,
|
||||
};
|
||||
#undef DL_OP_TO_ENUM_VALUE
|
||||
|
||||
enum class DisplayListOpCategory {
|
||||
kAttribute,
|
||||
kTransform,
|
||||
kClip,
|
||||
kSave,
|
||||
kSaveLayer,
|
||||
kRestore,
|
||||
kRendering,
|
||||
kSubDisplayList,
|
||||
kInvalidCategory,
|
||||
kMaxCategory = kInvalidCategory,
|
||||
};
|
||||
|
||||
class DlOpReceiver;
|
||||
class DisplayListBuilder;
|
||||
|
||||
@ -288,7 +270,7 @@ class DisplayListStorage {
|
||||
std::unique_ptr<uint8_t, FreeDeleter> ptr_;
|
||||
};
|
||||
|
||||
using DlIndex = uint32_t;
|
||||
class Culler;
|
||||
|
||||
// The base class that contains a sequence of rendering operations
|
||||
// for dispatch to a DlOpReceiver. These objects must be instantiated
|
||||
@ -378,158 +360,6 @@ class DisplayList : public SkRefCnt {
|
||||
/// be required for the indicated blend mode to do its work.
|
||||
DlBlendMode max_root_blend_mode() const { return max_root_blend_mode_; }
|
||||
|
||||
/// @brief Iterator utility class used for the |DisplayList::begin|
|
||||
/// and |DisplayList::end| methods. It implements just the
|
||||
/// basic methods to enable iteration-style for loops.
|
||||
class Iterator {
|
||||
public:
|
||||
DlIndex operator*() const { return value_; }
|
||||
bool operator!=(const Iterator& other) { return value_ != other.value_; }
|
||||
Iterator& operator++() {
|
||||
value_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Iterator(DlIndex value) : value_(value) {}
|
||||
|
||||
DlIndex value_;
|
||||
|
||||
friend class DisplayList;
|
||||
};
|
||||
|
||||
/// @brief Return the number of stored records in the DisplayList.
|
||||
///
|
||||
/// Each stored record represents a dispatchable operation that will be
|
||||
/// sent to a |DlOpReceiver| by the |Dispatch| method. You can directly
|
||||
/// simulate the |Dispatch| method using a simple for loop on the indices:
|
||||
///
|
||||
/// {
|
||||
/// for (DlIndex i = 0u; i < display_list->GetRecordCount(); i++) {
|
||||
/// display_list->Dispatch(my_receiver, i);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @see |Dispatch(receiver, index)|
|
||||
/// @see |begin|
|
||||
/// @see |end|
|
||||
/// @see |GetCulledIndices|
|
||||
DlIndex GetRecordCount() const { return offsets_.size(); }
|
||||
|
||||
/// @brief Return an iterator to the start of the stored records,
|
||||
/// enabling the iteration form of a for loop.
|
||||
///
|
||||
/// Each stored record represents a dispatchable operation that will be
|
||||
/// sent to a |DlOpReceiver| by the |Dispatch| method. You can directly
|
||||
/// simulate the |Dispatch| method using a simple for loop on the indices:
|
||||
///
|
||||
/// {
|
||||
/// for (DlIndex i : *display_list) {
|
||||
/// display_list->Dispatch(my_receiver, i);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @see |end|
|
||||
/// @see |GetCulledIndices|
|
||||
Iterator begin() const { return Iterator(0u); }
|
||||
|
||||
/// @brief Return an iterator to the end of the stored records,
|
||||
/// enabling the iteration form of a for loop.
|
||||
///
|
||||
/// Each stored record represents a dispatchable operation that will be
|
||||
/// sent to a |DlOpReceiver| by the |Dispatch| method. You can directly
|
||||
/// simulate the |Dispatch| method using a simple for loop on the indices:
|
||||
///
|
||||
/// {
|
||||
/// for (DlIndex i : *display_list) {
|
||||
/// display_list->Dispatch(my_receiver, i);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @see |begin|
|
||||
/// @see |GetCulledIndices|
|
||||
Iterator end() const { return Iterator(offsets_.size()); }
|
||||
|
||||
/// @brief Dispatch a single stored operation by its index.
|
||||
///
|
||||
/// Each stored record represents a dispatchable operation that will be
|
||||
/// sent to a |DlOpReceiver| by the |Dispatch| method. You can use this
|
||||
/// method to dispatch a single operation to your receiver with an index
|
||||
/// between |0u| (inclusive) and |GetRecordCount()| (exclusive), as in:
|
||||
///
|
||||
/// {
|
||||
/// for (DlIndex i = 0u; i < display_list->GetRecordCount(); i++) {
|
||||
/// display_list->Dispatch(my_receiver, i);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// If the index is out of the range of the stored records, this method
|
||||
/// will not call any methods on the receiver and return false. You can
|
||||
/// check the return value for true if you want to make sure you are
|
||||
/// using valid indices.
|
||||
///
|
||||
/// @see |GetRecordCount|
|
||||
/// @see |begin|
|
||||
/// @see |end|
|
||||
/// @see |GetCulledIndices|
|
||||
bool Dispatch(DlOpReceiver& receiver, DlIndex index) const;
|
||||
|
||||
/// @brief Return an enum describing the specific op type stored at
|
||||
/// the indicated index.
|
||||
///
|
||||
/// The specific types of the records are subject to change without notice
|
||||
/// as the DisplayList code is developed and optimized. These values are
|
||||
/// useful mostly for debugging purposes and should not be used in
|
||||
/// production code.
|
||||
///
|
||||
/// @see |GetOpCategory| for a more stable description of the records
|
||||
DisplayListOpType GetOpType(DlIndex index) const;
|
||||
|
||||
/// @brief Return an enum describing the general category of the
|
||||
/// operation record stored at the indicated index.
|
||||
///
|
||||
/// The categories are general and stable and can be used fairly safely
|
||||
/// in production code to plan how to dispatch or reorder ops during
|
||||
/// final rendering.
|
||||
///
|
||||
/// @see |GetOpType| for a more detailed description of the records
|
||||
/// primarily for debugging use
|
||||
DisplayListOpCategory GetOpCategory(DlIndex index) const;
|
||||
|
||||
/// @brief Return an enum describing the general category of the
|
||||
/// operation record with the given type.
|
||||
///
|
||||
/// @see |GetOpType| for a more detailed description of the records
|
||||
/// primarily for debugging use
|
||||
static DisplayListOpCategory GetOpCategory(DisplayListOpType type);
|
||||
|
||||
/// @brief Return a vector of valid indices for records stored in
|
||||
/// the DisplayList that must be dispatched if you are
|
||||
/// restricted to the indicated cull_rect.
|
||||
///
|
||||
/// This method can be used along with indexed dispatching to implement
|
||||
/// RTree culling while still maintaining control over planning of
|
||||
/// operations to be rendered, as in:
|
||||
///
|
||||
/// {
|
||||
/// std::vector<DlIndex> indices =
|
||||
/// display_list->GetCulledIndices(cull-rect);
|
||||
/// for (DlIndex i : indices) {
|
||||
/// display_list->Dispatch(my_receiver, i);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// The indices returned in the vector will automatically deal with
|
||||
/// including or culling related operations such as attributes, clips
|
||||
/// and transforms that will provide state for any rendering operations
|
||||
/// selected by the culling checks.
|
||||
///
|
||||
/// @see |GetOpType| for a more detailed description of the records
|
||||
/// primarily for debugging use
|
||||
///
|
||||
/// @see |Dispatch(receiver, index)|
|
||||
std::vector<DlIndex> GetCulledIndices(const SkRect& cull_rect) const;
|
||||
|
||||
private:
|
||||
DisplayList(DisplayListStorage&& ptr,
|
||||
size_t byte_count,
|
||||
@ -551,7 +381,6 @@ class DisplayList : public SkRefCnt {
|
||||
static void DisposeOps(const uint8_t* ptr, const uint8_t* end);
|
||||
|
||||
const DisplayListStorage storage_;
|
||||
const std::vector<size_t> offsets_;
|
||||
const size_t byte_count_;
|
||||
const uint32_t op_count_;
|
||||
|
||||
@ -572,10 +401,10 @@ class DisplayList : public SkRefCnt {
|
||||
|
||||
const sk_sp<const DlRTree> rtree_;
|
||||
|
||||
void DispatchOneOp(DlOpReceiver& receiver, const uint8_t* ptr) const;
|
||||
|
||||
void RTreeResultsToIndexVector(std::vector<DlIndex>& indices,
|
||||
const std::vector<int>& rtree_results) const;
|
||||
void Dispatch(DlOpReceiver& ctx,
|
||||
const uint8_t* ptr,
|
||||
const uint8_t* end,
|
||||
Culler& culler) const;
|
||||
|
||||
friend class DisplayListBuilder;
|
||||
};
|
||||
|
||||
@ -253,70 +253,6 @@ TEST_F(DisplayListTest, EmptyRebuild) {
|
||||
ASSERT_TRUE(dl2->Equals(dl3));
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, GeneralReceiverInitialValues) {
|
||||
DisplayListGeneralReceiver receiver;
|
||||
|
||||
EXPECT_EQ(receiver.GetOpsReceived(), 0u);
|
||||
|
||||
auto max_type = static_cast<int>(DisplayListOpType::kMaxOp);
|
||||
for (int i = 0; i <= max_type; i++) {
|
||||
DisplayListOpType type = static_cast<DisplayListOpType>(i);
|
||||
EXPECT_EQ(receiver.GetOpsReceived(type), 0u) << type;
|
||||
}
|
||||
|
||||
auto max_category = static_cast<int>(DisplayListOpCategory::kMaxCategory);
|
||||
for (int i = 0; i <= max_category; i++) {
|
||||
DisplayListOpCategory category = static_cast<DisplayListOpCategory>(i);
|
||||
EXPECT_EQ(receiver.GetOpsReceived(category), 0u) << category;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, Iteration) {
|
||||
DisplayListBuilder builder;
|
||||
builder.DrawRect({10, 10, 20, 20}, DlPaint());
|
||||
auto dl = builder.Build();
|
||||
for (DlIndex i : *dl) {
|
||||
EXPECT_EQ(dl->GetOpType(i), DisplayListOpType::kDrawRect) //
|
||||
<< "at " << i;
|
||||
EXPECT_EQ(dl->GetOpCategory(i), DisplayListOpCategory::kRendering)
|
||||
<< "at " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, InvalidIndices) {
|
||||
DisplayListBuilder builder;
|
||||
builder.DrawRect(kTestBounds, DlPaint());
|
||||
auto dl = builder.Build();
|
||||
DisplayListGeneralReceiver receiver;
|
||||
|
||||
EXPECT_FALSE(dl->Dispatch(receiver, -1));
|
||||
EXPECT_FALSE(dl->Dispatch(receiver, dl->GetRecordCount()));
|
||||
EXPECT_EQ(dl->GetOpType(-1), DisplayListOpType::kInvalidOp);
|
||||
EXPECT_EQ(dl->GetOpType(dl->GetRecordCount()), DisplayListOpType::kInvalidOp);
|
||||
EXPECT_EQ(dl->GetOpCategory(-1), DisplayListOpCategory::kInvalidCategory);
|
||||
EXPECT_EQ(dl->GetOpCategory(dl->GetRecordCount()),
|
||||
DisplayListOpCategory::kInvalidCategory);
|
||||
EXPECT_EQ(dl->GetOpCategory(-1), DisplayListOpCategory::kInvalidCategory);
|
||||
EXPECT_EQ(dl->GetOpCategory(DisplayListOpType::kInvalidOp),
|
||||
DisplayListOpCategory::kInvalidCategory);
|
||||
EXPECT_EQ(dl->GetOpCategory(DisplayListOpType::kMaxOp),
|
||||
DisplayListOpCategory::kInvalidCategory);
|
||||
EXPECT_EQ(receiver.GetOpsReceived(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, ValidIndices) {
|
||||
DisplayListBuilder builder;
|
||||
builder.DrawRect(kTestBounds, DlPaint());
|
||||
auto dl = builder.Build();
|
||||
DisplayListGeneralReceiver receiver;
|
||||
|
||||
EXPECT_EQ(dl->GetRecordCount(), 1u);
|
||||
EXPECT_TRUE(dl->Dispatch(receiver, 0u));
|
||||
EXPECT_EQ(dl->GetOpType(0u), DisplayListOpType::kDrawRect);
|
||||
EXPECT_EQ(dl->GetOpCategory(0u), DisplayListOpCategory::kRendering);
|
||||
EXPECT_EQ(receiver.GetOpsReceived(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, BuilderCanBeReused) {
|
||||
DisplayListBuilder builder(kTestBounds);
|
||||
builder.DrawRect(kTestBounds, DlPaint());
|
||||
@ -742,38 +678,6 @@ TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedAreEqual) {
|
||||
group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
|
||||
ASSERT_TRUE(DisplayListsEQ_Verbose(dl, copy));
|
||||
ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc;
|
||||
ASSERT_EQ(copy->GetRecordCount(), dl->GetRecordCount());
|
||||
ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc;
|
||||
ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc;
|
||||
ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc;
|
||||
ASSERT_EQ(copy->total_depth(), dl->total_depth()) << desc;
|
||||
ASSERT_EQ(copy->bounds(), dl->bounds()) << desc;
|
||||
ASSERT_TRUE(copy->Equals(*dl)) << desc;
|
||||
ASSERT_TRUE(dl->Equals(*copy)) << desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedByIndexAreEqual) {
|
||||
for (auto& group : allGroups) {
|
||||
for (size_t i = 0; i < group.variants.size(); i++) {
|
||||
sk_sp<DisplayList> dl = Build(group.variants[i]);
|
||||
// Verify recapturing the replay of the display list is Equals()
|
||||
// when dispatching directly from the DL to another builder
|
||||
DisplayListBuilder copy_builder;
|
||||
DlOpReceiver& r = ToReceiver(copy_builder);
|
||||
for (DlIndex i = 0; i < dl->GetRecordCount(); i++) {
|
||||
EXPECT_NE(dl->GetOpType(i), DisplayListOpType::kInvalidOp);
|
||||
EXPECT_NE(dl->GetOpCategory(i),
|
||||
DisplayListOpCategory::kInvalidCategory);
|
||||
EXPECT_TRUE(dl->Dispatch(r, i));
|
||||
}
|
||||
sk_sp<DisplayList> copy = copy_builder.Build();
|
||||
auto desc =
|
||||
group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
|
||||
ASSERT_TRUE(DisplayListsEQ_Verbose(dl, copy));
|
||||
ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc;
|
||||
ASSERT_EQ(copy->GetRecordCount(), dl->GetRecordCount());
|
||||
ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc;
|
||||
ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc;
|
||||
ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc;
|
||||
@ -3236,55 +3140,27 @@ TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) {
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, RTreeRenderCulling) {
|
||||
SkRect rect1 = SkRect::MakeLTRB(0, 0, 10, 10);
|
||||
SkRect rect2 = SkRect::MakeLTRB(20, 0, 30, 10);
|
||||
SkRect rect3 = SkRect::MakeLTRB(0, 20, 10, 30);
|
||||
SkRect rect4 = SkRect::MakeLTRB(20, 20, 30, 30);
|
||||
DlPaint paint1 = DlPaint().setColor(DlColor::kRed());
|
||||
DlPaint paint2 = DlPaint().setColor(DlColor::kGreen());
|
||||
DlPaint paint3 = DlPaint().setColor(DlColor::kBlue());
|
||||
DlPaint paint4 = DlPaint().setColor(DlColor::kMagenta());
|
||||
|
||||
DisplayListBuilder main_builder(true);
|
||||
main_builder.DrawRect(rect1, paint1);
|
||||
main_builder.DrawRect(rect2, paint2);
|
||||
main_builder.DrawRect(rect3, paint3);
|
||||
main_builder.DrawRect(rect4, paint4);
|
||||
DlOpReceiver& main_receiver = ToReceiver(main_builder);
|
||||
main_receiver.drawRect({0, 0, 10, 10});
|
||||
main_receiver.drawRect({20, 0, 30, 10});
|
||||
main_receiver.drawRect({0, 20, 10, 30});
|
||||
main_receiver.drawRect({20, 20, 30, 30});
|
||||
auto main = main_builder.Build();
|
||||
|
||||
auto test = [main](SkIRect cull_rect, const sk_sp<DisplayList>& expected,
|
||||
const std::string& label) {
|
||||
SkRect cull_rectf = SkRect::Make(cull_rect);
|
||||
|
||||
auto test = [main](SkIRect cull_rect, const sk_sp<DisplayList>& expected) {
|
||||
{ // Test SkIRect culling
|
||||
DisplayListBuilder culling_builder;
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected))
|
||||
<< "using cull rect " << cull_rect //
|
||||
<< " where " << label;
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
}
|
||||
|
||||
{ // Test SkRect culling
|
||||
DisplayListBuilder culling_builder;
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rectf);
|
||||
main->Dispatch(ToReceiver(culling_builder), SkRect::Make(cull_rect));
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected))
|
||||
<< "using cull rect " << cull_rectf //
|
||||
<< " where " << label;
|
||||
}
|
||||
|
||||
{ // Test using vector of culled indices
|
||||
DisplayListBuilder culling_builder;
|
||||
DlOpReceiver& receiver = ToReceiver(culling_builder);
|
||||
auto indices = main->GetCulledIndices(cull_rectf);
|
||||
for (DlIndex i : indices) {
|
||||
EXPECT_TRUE(main->Dispatch(receiver, i));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected))
|
||||
<< "using culled indices on cull rect " << cull_rectf //
|
||||
<< " where " << label;
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
}
|
||||
};
|
||||
|
||||
@ -3294,62 +3170,57 @@ TEST_F(DisplayListTest, RTreeRenderCulling) {
|
||||
DisplayListBuilder expected_builder;
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
test(cull_rect, expected, "no rects intersect");
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 1
|
||||
SkIRect cull_rect = {9, 9, 19, 19};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
expected_builder.DrawRect(rect1, paint1);
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({0, 0, 10, 10});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
test(cull_rect, expected, "rect 1 intersects");
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 2
|
||||
SkIRect cull_rect = {11, 9, 21, 19};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
// Unfortunately we don't cull attribute records (yet?) until the last op
|
||||
ToReceiver(expected_builder).setColor(paint1.getColor());
|
||||
expected_builder.DrawRect(rect2, paint2);
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({20, 0, 30, 10});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
test(cull_rect, expected, "rect 2 intersects");
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 3
|
||||
SkIRect cull_rect = {9, 11, 19, 21};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
// Unfortunately we don't cull attribute records (yet?) until the last op
|
||||
ToReceiver(expected_builder).setColor(paint1.getColor());
|
||||
ToReceiver(expected_builder).setColor(paint2.getColor());
|
||||
expected_builder.DrawRect(rect3, paint3);
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({0, 20, 10, 30});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
test(cull_rect, expected, "rect 3 intersects");
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 4
|
||||
SkIRect cull_rect = {11, 11, 21, 21};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
// Unfortunately we don't cull attribute records (yet?) until the last op
|
||||
ToReceiver(expected_builder).setColor(paint1.getColor());
|
||||
ToReceiver(expected_builder).setColor(paint2.getColor());
|
||||
ToReceiver(expected_builder).setColor(paint3.getColor());
|
||||
expected_builder.DrawRect(rect4, paint4);
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({20, 20, 30, 30});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
test(cull_rect, expected, "rect 4 intersects");
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // All 4 rects
|
||||
SkIRect cull_rect = {9, 9, 21, 21};
|
||||
|
||||
test(cull_rect, main, "all rects intersect");
|
||||
test(cull_rect, main);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -510,7 +510,7 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
// Most rendering ops will use 1 depth value, but some attributes may
|
||||
// require an additional depth value (due to implicit saveLayers)
|
||||
uint32_t render_op_depth_cost_ = 1u;
|
||||
DlIndex op_index_ = 0;
|
||||
int op_index_ = 0;
|
||||
|
||||
// bytes and ops from |drawPicture| and |drawDisplayList|
|
||||
size_t nested_bytes_ = 0;
|
||||
|
||||
@ -18,6 +18,47 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// Structure holding the information necessary to dispatch and
|
||||
// potentially cull the DLOps during playback.
|
||||
//
|
||||
// Generally drawing ops will execute as long as |cur_index|
|
||||
// is at or after |next_render_index|, so setting the latter
|
||||
// to 0 will render all primitives and setting it to MAX_INT
|
||||
// will skip all remaining rendering primitives.
|
||||
//
|
||||
// Save and saveLayer ops will execute as long as the next
|
||||
// rendering index is before their closing restore index.
|
||||
// They will also store their own restore index into the
|
||||
// |next_restore_index| field for use by clip and transform ops.
|
||||
//
|
||||
// Clip and transform ops will only execute if the next
|
||||
// render index is before the next restore index. Otherwise
|
||||
// their modified state will not be used before it gets
|
||||
// restored.
|
||||
//
|
||||
// Attribute ops always execute as they are too numerous and
|
||||
// cheap to deal with a complicated "lifetime" tracking to
|
||||
// determine if they will be used.
|
||||
struct DispatchContext {
|
||||
DlOpReceiver& receiver;
|
||||
|
||||
int cur_index;
|
||||
int next_render_index;
|
||||
|
||||
int next_restore_index;
|
||||
|
||||
struct SaveInfo {
|
||||
SaveInfo(int previous_restore_index, bool save_was_needed)
|
||||
: previous_restore_index(previous_restore_index),
|
||||
save_was_needed(save_was_needed) {}
|
||||
|
||||
int previous_restore_index;
|
||||
bool save_was_needed;
|
||||
};
|
||||
|
||||
std::vector<SaveInfo> save_infos;
|
||||
};
|
||||
|
||||
// Most Ops can be bulk compared using memcmp because they contain
|
||||
// only numeric values or constructs that are constructed from numeric
|
||||
// values.
|
||||
@ -75,8 +116,8 @@ struct DLOp {
|
||||
\
|
||||
const bool value; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.set##name(value); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
ctx.receiver.set##name(value); \
|
||||
} \
|
||||
};
|
||||
DEFINE_SET_BOOL_OP(AntiAlias)
|
||||
@ -92,8 +133,8 @@ DEFINE_SET_BOOL_OP(InvertColors)
|
||||
\
|
||||
const DlStroke##name value; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.setStroke##name(value); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
ctx.receiver.setStroke##name(value); \
|
||||
} \
|
||||
};
|
||||
DEFINE_SET_ENUM_OP(Cap)
|
||||
@ -108,8 +149,8 @@ struct SetStyleOp final : DLOp {
|
||||
|
||||
const DlDrawStyle style;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.setDrawStyle(style);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setDrawStyle(style);
|
||||
}
|
||||
};
|
||||
// 4 byte header + 4 byte payload packs into minimum 8 bytes
|
||||
@ -120,8 +161,8 @@ struct SetStrokeWidthOp final : DLOp {
|
||||
|
||||
const float width;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.setStrokeWidth(width);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setStrokeWidth(width);
|
||||
}
|
||||
};
|
||||
// 4 byte header + 4 byte payload packs into minimum 8 bytes
|
||||
@ -132,8 +173,8 @@ struct SetStrokeMiterOp final : DLOp {
|
||||
|
||||
const float limit;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.setStrokeMiter(limit);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setStrokeMiter(limit);
|
||||
}
|
||||
};
|
||||
|
||||
@ -145,7 +186,7 @@ struct SetColorOp final : DLOp {
|
||||
|
||||
const DlColor color;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { receiver.setColor(color); }
|
||||
void dispatch(DispatchContext& ctx) const { ctx.receiver.setColor(color); }
|
||||
};
|
||||
// 4 byte header + 4 byte payload packs into minimum 8 bytes
|
||||
struct SetBlendModeOp final : DLOp {
|
||||
@ -155,8 +196,8 @@ struct SetBlendModeOp final : DLOp {
|
||||
|
||||
const DlBlendMode mode;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.setBlendMode(mode);
|
||||
void dispatch(DispatchContext& ctx) const { //
|
||||
ctx.receiver.setBlendMode(mode);
|
||||
}
|
||||
};
|
||||
|
||||
@ -172,8 +213,8 @@ struct SetBlendModeOp final : DLOp {
|
||||
\
|
||||
Clear##name##Op() {} \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.set##name(nullptr); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
ctx.receiver.set##name(nullptr); \
|
||||
} \
|
||||
}; \
|
||||
struct SetPod##name##Op final : DLOp { \
|
||||
@ -181,9 +222,9 @@ struct SetBlendModeOp final : DLOp {
|
||||
\
|
||||
SetPod##name##Op() {} \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
const Dl##name* filter = reinterpret_cast<const Dl##name*>(this + 1); \
|
||||
receiver.set##name(filter); \
|
||||
ctx.receiver.set##name(filter); \
|
||||
} \
|
||||
};
|
||||
DEFINE_SET_CLEAR_DLATTR_OP(ColorFilter, ColorFilter, filter)
|
||||
@ -206,8 +247,8 @@ struct SetImageColorSourceOp : DLOp {
|
||||
|
||||
const DlImageColorSource source;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.setColorSource(&source);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setColorSource(&source);
|
||||
}
|
||||
};
|
||||
|
||||
@ -224,8 +265,8 @@ struct SetRuntimeEffectColorSourceOp : DLOp {
|
||||
|
||||
const DlRuntimeEffectColorSource source;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.setColorSource(&source);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setColorSource(&source);
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const SetRuntimeEffectColorSourceOp* other) const {
|
||||
@ -243,8 +284,8 @@ struct SetSceneColorSourceOp : DLOp {
|
||||
|
||||
const DlSceneColorSource source;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.setColorSource(&source);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setColorSource(&source);
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const SetSceneColorSourceOp* other) const {
|
||||
@ -263,8 +304,8 @@ struct SetSharedImageFilterOp : DLOp {
|
||||
|
||||
const std::shared_ptr<DlImageFilter> filter;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.setImageFilter(filter.get());
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
ctx.receiver.setImageFilter(filter.get());
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const SetSharedImageFilterOp* other) const {
|
||||
@ -289,8 +330,15 @@ struct SaveOpBase : DLOp {
|
||||
// of the data here, it can be stored for free and defaulted to 0 for
|
||||
// save operations.
|
||||
SaveLayerOptions options;
|
||||
DlIndex restore_index;
|
||||
int restore_index;
|
||||
uint32_t total_content_depth;
|
||||
|
||||
inline bool save_needed(DispatchContext& ctx) const {
|
||||
bool needed = ctx.next_render_index <= restore_index;
|
||||
ctx.save_infos.emplace_back(ctx.next_restore_index, needed);
|
||||
ctx.next_restore_index = restore_index;
|
||||
return needed;
|
||||
}
|
||||
};
|
||||
// 16 byte SaveOpBase with no additional data (options is unsed here)
|
||||
struct SaveOp final : SaveOpBase {
|
||||
@ -298,8 +346,10 @@ struct SaveOp final : SaveOpBase {
|
||||
|
||||
SaveOp() : SaveOpBase() {}
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.save(total_content_depth);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (save_needed(ctx)) {
|
||||
ctx.receiver.save(total_content_depth);
|
||||
}
|
||||
}
|
||||
};
|
||||
// The base struct for all saveLayer() ops
|
||||
@ -319,8 +369,11 @@ struct SaveLayerOp final : SaveLayerOpBase {
|
||||
SaveLayerOp(const SaveLayerOptions& options, const SkRect& rect)
|
||||
: SaveLayerOpBase(options, rect) {}
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.saveLayer(rect, options, total_content_depth, max_blend_mode);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (save_needed(ctx)) {
|
||||
ctx.receiver.saveLayer(rect, options, total_content_depth,
|
||||
max_blend_mode);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 36 byte SaveLayerOpBase + 4 bytes for alignment + 16 byte payload packs
|
||||
@ -335,9 +388,11 @@ struct SaveLayerBackdropOp final : SaveLayerOpBase {
|
||||
|
||||
const std::shared_ptr<DlImageFilter> backdrop;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.saveLayer(rect, options, total_content_depth, max_blend_mode,
|
||||
backdrop.get());
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (save_needed(ctx)) {
|
||||
ctx.receiver.saveLayer(rect, options, total_content_depth, max_blend_mode,
|
||||
backdrop.get());
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const SaveLayerBackdropOp* other) const {
|
||||
@ -355,14 +410,23 @@ struct RestoreOp final : DLOp {
|
||||
|
||||
RestoreOp() {}
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.restore();
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
DispatchContext::SaveInfo& info = ctx.save_infos.back();
|
||||
if (info.save_was_needed) {
|
||||
ctx.receiver.restore();
|
||||
}
|
||||
ctx.next_restore_index = info.previous_restore_index;
|
||||
ctx.save_infos.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
struct TransformClipOpBase : DLOp {
|
||||
static constexpr uint32_t kDepthInc = 0;
|
||||
static constexpr uint32_t kRenderOpInc = 1;
|
||||
|
||||
inline bool op_needed(const DispatchContext& context) const {
|
||||
return context.next_render_index <= context.next_restore_index;
|
||||
}
|
||||
};
|
||||
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
|
||||
// (4 bytes unused)
|
||||
@ -374,8 +438,10 @@ struct TranslateOp final : TransformClipOpBase {
|
||||
const SkScalar tx;
|
||||
const SkScalar ty;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.translate(tx, ty);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.translate(tx, ty);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
|
||||
@ -388,8 +454,10 @@ struct ScaleOp final : TransformClipOpBase {
|
||||
const SkScalar sx;
|
||||
const SkScalar sy;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.scale(sx, sy);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.scale(sx, sy);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 4 byte header + 4 byte payload packs into minimum 8 bytes
|
||||
@ -400,8 +468,10 @@ struct RotateOp final : TransformClipOpBase {
|
||||
|
||||
const SkScalar degrees;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.rotate(degrees);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.rotate(degrees);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
|
||||
@ -414,8 +484,10 @@ struct SkewOp final : TransformClipOpBase {
|
||||
const SkScalar sx;
|
||||
const SkScalar sy;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.skew(sx, sy);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.skew(sx, sy);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes
|
||||
@ -432,9 +504,11 @@ struct Transform2DAffineOp final : TransformClipOpBase {
|
||||
const SkScalar mxx, mxy, mxt;
|
||||
const SkScalar myx, myy, myt;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.transform2DAffine(mxx, mxy, mxt, //
|
||||
myx, myy, myt);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.transform2DAffine(mxx, mxy, mxt, //
|
||||
myx, myy, myt);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 4 byte header + 64 byte payload uses 68 bytes which is rounded up to 72 bytes
|
||||
@ -459,11 +533,13 @@ struct TransformFullPerspectiveOp final : TransformClipOpBase {
|
||||
const SkScalar mzx, mzy, mzz, mzt;
|
||||
const SkScalar mwx, mwy, mwz, mwt;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.transformFullPerspective(mxx, mxy, mxz, mxt, //
|
||||
myx, myy, myz, myt, //
|
||||
mzx, mzy, mzz, mzt, //
|
||||
mwx, mwy, mwz, mwt);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.transformFullPerspective(mxx, mxy, mxz, mxt, //
|
||||
myx, myy, myz, myt, //
|
||||
mzx, mzy, mzz, mzt, //
|
||||
mwx, mwy, mwz, mwt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -473,8 +549,10 @@ struct TransformResetOp final : TransformClipOpBase {
|
||||
|
||||
TransformResetOp() = default;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.transformReset();
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.transformReset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -498,8 +576,11 @@ struct TransformResetOp final : TransformClipOpBase {
|
||||
const bool is_aa; \
|
||||
const Sk##shapetype shape; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.clip##shapename(shape, DlCanvas::ClipOp::k##clipop, is_aa); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
ctx.receiver.clip##shapename(shape, DlCanvas::ClipOp::k##clipop, \
|
||||
is_aa); \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
DEFINE_CLIP_SHAPE_OP(Rect, Rect, Intersect)
|
||||
@ -510,30 +591,33 @@ DEFINE_CLIP_SHAPE_OP(Oval, Rect, Difference)
|
||||
DEFINE_CLIP_SHAPE_OP(RRect, RRect, Difference)
|
||||
#undef DEFINE_CLIP_SHAPE_OP
|
||||
|
||||
#define DEFINE_CLIP_PATH_OP(clipop) \
|
||||
struct Clip##clipop##PathOp final : TransformClipOpBase { \
|
||||
static constexpr auto kType = DisplayListOpType::kClip##clipop##Path; \
|
||||
\
|
||||
Clip##clipop##PathOp(const SkPath& path, bool is_aa) \
|
||||
: is_aa(is_aa), cached_path(path) {} \
|
||||
\
|
||||
const bool is_aa; \
|
||||
const DlOpReceiver::CacheablePath cached_path; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
if (receiver.PrefersImpellerPaths()) { \
|
||||
receiver.clipPath(cached_path, DlCanvas::ClipOp::k##clipop, is_aa); \
|
||||
} else { \
|
||||
receiver.clipPath(cached_path.sk_path, DlCanvas::ClipOp::k##clipop, \
|
||||
is_aa); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \
|
||||
return is_aa == other->is_aa && cached_path == other->cached_path \
|
||||
? DisplayListCompare::kEqual \
|
||||
: DisplayListCompare::kNotEqual; \
|
||||
} \
|
||||
#define DEFINE_CLIP_PATH_OP(clipop) \
|
||||
struct Clip##clipop##PathOp final : TransformClipOpBase { \
|
||||
static constexpr auto kType = DisplayListOpType::kClip##clipop##Path; \
|
||||
\
|
||||
Clip##clipop##PathOp(const SkPath& path, bool is_aa) \
|
||||
: is_aa(is_aa), cached_path(path) {} \
|
||||
\
|
||||
const bool is_aa; \
|
||||
const DlOpReceiver::CacheablePath cached_path; \
|
||||
\
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
if (ctx.receiver.PrefersImpellerPaths()) { \
|
||||
ctx.receiver.clipPath(cached_path, DlCanvas::ClipOp::k##clipop, \
|
||||
is_aa); \
|
||||
} else { \
|
||||
ctx.receiver.clipPath(cached_path.sk_path, \
|
||||
DlCanvas::ClipOp::k##clipop, is_aa); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \
|
||||
return is_aa == other->is_aa && cached_path == other->cached_path \
|
||||
? DisplayListCompare::kEqual \
|
||||
: DisplayListCompare::kNotEqual; \
|
||||
} \
|
||||
};
|
||||
DEFINE_CLIP_PATH_OP(Intersect)
|
||||
DEFINE_CLIP_PATH_OP(Difference)
|
||||
@ -542,6 +626,10 @@ DEFINE_CLIP_PATH_OP(Difference)
|
||||
struct DrawOpBase : DLOp {
|
||||
static constexpr uint32_t kDepthInc = 1;
|
||||
static constexpr uint32_t kRenderOpInc = 1;
|
||||
|
||||
inline bool op_needed(const DispatchContext& ctx) const {
|
||||
return ctx.cur_index >= ctx.next_render_index;
|
||||
}
|
||||
};
|
||||
|
||||
// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused)
|
||||
@ -550,8 +638,10 @@ struct DrawPaintOp final : DrawOpBase {
|
||||
|
||||
DrawPaintOp() {}
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const { //
|
||||
receiver.drawPaint();
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawPaint();
|
||||
}
|
||||
}
|
||||
};
|
||||
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
|
||||
@ -564,8 +654,10 @@ struct DrawColorOp final : DrawOpBase {
|
||||
const DlColor color;
|
||||
const DlBlendMode mode;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawColor(color, mode);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawColor(color, mode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -582,8 +674,10 @@ struct DrawColorOp final : DrawOpBase {
|
||||
\
|
||||
const arg_type arg_name; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.draw##op_name(arg_name); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
ctx.receiver.draw##op_name(arg_name); \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect)
|
||||
@ -600,11 +694,13 @@ struct DrawPathOp final : DrawOpBase {
|
||||
|
||||
const DlOpReceiver::CacheablePath cached_path;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
if (receiver.PrefersImpellerPaths()) {
|
||||
receiver.drawPath(cached_path);
|
||||
} else {
|
||||
receiver.drawPath(cached_path.sk_path);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
if (ctx.receiver.PrefersImpellerPaths()) {
|
||||
ctx.receiver.drawPath(cached_path);
|
||||
} else {
|
||||
ctx.receiver.drawPath(cached_path.sk_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,8 +726,10 @@ struct DrawPathOp final : DrawOpBase {
|
||||
const type1 name1; \
|
||||
const type2 name2; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.draw##op_name(name1, name2); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
ctx.receiver.draw##op_name(name1, name2); \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
DEFINE_DRAW_2ARG_OP(Line, SkPoint, p0, SkPoint, p1)
|
||||
@ -654,8 +752,10 @@ struct DrawDashedLineOp final : DrawOpBase {
|
||||
const SkScalar on_length;
|
||||
const SkScalar off_length;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawDashedLine(p0, p1, on_length, off_length);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawDashedLine(p0, p1, on_length, off_length);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -671,8 +771,10 @@ struct DrawArcOp final : DrawOpBase {
|
||||
const SkScalar sweep;
|
||||
const bool center;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawArc(bounds, start, sweep, center);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawArc(bounds, start, sweep, center);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -682,18 +784,20 @@ struct DrawArcOp final : DrawOpBase {
|
||||
// so this op will always pack efficiently
|
||||
// The point type is packed into 3 different OpTypes to avoid expanding
|
||||
// the fixed payload beyond the 8 bytes
|
||||
#define DEFINE_DRAW_POINTS_OP(name, mode) \
|
||||
struct Draw##name##Op final : DrawOpBase { \
|
||||
static constexpr auto kType = DisplayListOpType::kDraw##name; \
|
||||
\
|
||||
explicit Draw##name##Op(uint32_t count) : count(count) {} \
|
||||
\
|
||||
const uint32_t count; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
const SkPoint* pts = reinterpret_cast<const SkPoint*>(this + 1); \
|
||||
receiver.drawPoints(DlCanvas::PointMode::mode, count, pts); \
|
||||
} \
|
||||
#define DEFINE_DRAW_POINTS_OP(name, mode) \
|
||||
struct Draw##name##Op final : DrawOpBase { \
|
||||
static constexpr auto kType = DisplayListOpType::kDraw##name; \
|
||||
\
|
||||
explicit Draw##name##Op(uint32_t count) : count(count) {} \
|
||||
\
|
||||
const uint32_t count; \
|
||||
\
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
const SkPoint* pts = reinterpret_cast<const SkPoint*>(this + 1); \
|
||||
ctx.receiver.drawPoints(DlCanvas::PointMode::mode, count, pts); \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
DEFINE_DRAW_POINTS_OP(Points, kPoints);
|
||||
DEFINE_DRAW_POINTS_OP(Lines, kLines);
|
||||
@ -711,36 +815,40 @@ struct DrawVerticesOp final : DrawOpBase {
|
||||
const DlBlendMode mode;
|
||||
const std::shared_ptr<DlVertices> vertices;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawVertices(vertices, mode);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawVertices(vertices, mode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 4 byte header + 40 byte payload uses 44 bytes but is rounded up to 48 bytes
|
||||
// (4 bytes unused)
|
||||
#define DEFINE_DRAW_IMAGE_OP(name, with_attributes) \
|
||||
struct name##Op final : DrawOpBase { \
|
||||
static constexpr auto kType = DisplayListOpType::k##name; \
|
||||
\
|
||||
name##Op(const sk_sp<DlImage>& image, \
|
||||
const SkPoint& point, \
|
||||
DlImageSampling sampling) \
|
||||
: point(point), sampling(sampling), image(std::move(image)) {} \
|
||||
\
|
||||
const SkPoint point; \
|
||||
const DlImageSampling sampling; \
|
||||
const sk_sp<DlImage> image; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.drawImage(image, point, sampling, with_attributes); \
|
||||
} \
|
||||
\
|
||||
DisplayListCompare equals(const name##Op* other) const { \
|
||||
return (point == other->point && sampling == other->sampling && \
|
||||
image->Equals(other->image)) \
|
||||
? DisplayListCompare::kEqual \
|
||||
: DisplayListCompare::kNotEqual; \
|
||||
} \
|
||||
#define DEFINE_DRAW_IMAGE_OP(name, with_attributes) \
|
||||
struct name##Op final : DrawOpBase { \
|
||||
static constexpr auto kType = DisplayListOpType::k##name; \
|
||||
\
|
||||
name##Op(const sk_sp<DlImage>& image, \
|
||||
const SkPoint& point, \
|
||||
DlImageSampling sampling) \
|
||||
: point(point), sampling(sampling), image(std::move(image)) {} \
|
||||
\
|
||||
const SkPoint point; \
|
||||
const DlImageSampling sampling; \
|
||||
const sk_sp<DlImage> image; \
|
||||
\
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
ctx.receiver.drawImage(image, point, sampling, with_attributes); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
DisplayListCompare equals(const name##Op* other) const { \
|
||||
return (point == other->point && sampling == other->sampling && \
|
||||
image->Equals(other->image)) \
|
||||
? DisplayListCompare::kEqual \
|
||||
: DisplayListCompare::kNotEqual; \
|
||||
} \
|
||||
};
|
||||
DEFINE_DRAW_IMAGE_OP(DrawImage, false)
|
||||
DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true)
|
||||
@ -771,9 +879,11 @@ struct DrawImageRectOp final : DrawOpBase {
|
||||
const DlCanvas::SrcRectConstraint constraint;
|
||||
const sk_sp<DlImage> image;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawImageRect(image, src, dst, sampling, render_with_attributes,
|
||||
constraint);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawImageRect(image, src, dst, sampling,
|
||||
render_with_attributes, constraint);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const DrawImageRectOp* other) const {
|
||||
@ -802,9 +912,11 @@ struct DrawImageRectOp final : DrawOpBase {
|
||||
const DlFilterMode mode; \
|
||||
const sk_sp<DlImage> image; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
receiver.drawImageNine(image, center, dst, mode, \
|
||||
render_with_attributes); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
ctx.receiver.drawImageNine(image, center, dst, mode, \
|
||||
render_with_attributes); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
DisplayListCompare equals(const name##Op* other) const { \
|
||||
@ -882,14 +994,16 @@ struct DrawAtlasOp final : DrawAtlasBaseOp {
|
||||
has_colors,
|
||||
render_with_attributes) {}
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
|
||||
const SkRect* tex = reinterpret_cast<const SkRect*>(xform + count);
|
||||
const DlColor* colors =
|
||||
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
|
||||
const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
|
||||
receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
|
||||
nullptr, render_with_attributes);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
|
||||
const SkRect* tex = reinterpret_cast<const SkRect*>(xform + count);
|
||||
const DlColor* colors =
|
||||
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
|
||||
const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
|
||||
ctx.receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
|
||||
nullptr, render_with_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const DrawAtlasOp* other) const {
|
||||
@ -925,14 +1039,16 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp {
|
||||
|
||||
const SkRect cull_rect;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
|
||||
const SkRect* tex = reinterpret_cast<const SkRect*>(xform + count);
|
||||
const DlColor* colors =
|
||||
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
|
||||
const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
|
||||
receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
|
||||
&cull_rect, render_with_attributes);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
|
||||
const SkRect* tex = reinterpret_cast<const SkRect*>(xform + count);
|
||||
const DlColor* colors =
|
||||
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
|
||||
const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
|
||||
ctx.receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
|
||||
&cull_rect, render_with_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const DrawAtlasCulledOp* other) const {
|
||||
@ -957,8 +1073,10 @@ struct DrawDisplayListOp final : DrawOpBase {
|
||||
SkScalar opacity;
|
||||
const sk_sp<DisplayList> display_list;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawDisplayList(display_list, opacity);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawDisplayList(display_list, opacity);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const DrawDisplayListOp* other) const {
|
||||
@ -981,8 +1099,10 @@ struct DrawTextBlobOp final : DrawOpBase {
|
||||
const SkScalar y;
|
||||
const sk_sp<SkTextBlob> blob;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawTextBlob(blob, x, y);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawTextBlob(blob, x, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -998,8 +1118,10 @@ struct DrawTextFrameOp final : DrawOpBase {
|
||||
const SkScalar y;
|
||||
const std::shared_ptr<impeller::TextFrame> text_frame;
|
||||
|
||||
void dispatch(DlOpReceiver& receiver) const {
|
||||
receiver.drawTextFrame(text_frame, x, y);
|
||||
void dispatch(DispatchContext& ctx) const {
|
||||
if (op_needed(ctx)) {
|
||||
ctx.receiver.drawTextFrame(text_frame, x, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1019,13 +1141,15 @@ struct DrawTextFrameOp final : DrawOpBase {
|
||||
const SkScalar dpr; \
|
||||
const DlOpReceiver::CacheablePath cached_path; \
|
||||
\
|
||||
void dispatch(DlOpReceiver& receiver) const { \
|
||||
if (receiver.PrefersImpellerPaths()) { \
|
||||
receiver.drawShadow(cached_path, color, elevation, \
|
||||
transparent_occluder, dpr); \
|
||||
} else { \
|
||||
receiver.drawShadow(cached_path.sk_path, color, elevation, \
|
||||
transparent_occluder, dpr); \
|
||||
void dispatch(DispatchContext& ctx) const { \
|
||||
if (op_needed(ctx)) { \
|
||||
if (ctx.receiver.PrefersImpellerPaths()) { \
|
||||
ctx.receiver.drawShadow(cached_path, color, elevation, \
|
||||
transparent_occluder, dpr); \
|
||||
} else { \
|
||||
ctx.receiver.drawShadow(cached_path.sk_path, color, elevation, \
|
||||
transparent_occluder, dpr); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
|
||||
@ -55,8 +55,6 @@ using DlVertexMode = flutter::DlVertexMode;
|
||||
using DlTileMode = flutter::DlTileMode;
|
||||
using DlImageSampling = flutter::DlImageSampling;
|
||||
using SaveLayerOptions = flutter::SaveLayerOptions;
|
||||
using DisplayListOpType = flutter::DisplayListOpType;
|
||||
using DisplayListOpCategory = flutter::DisplayListOpCategory;
|
||||
|
||||
using DisplayListStreamDispatcher = flutter::testing::DisplayListStreamDispatcher;
|
||||
|
||||
@ -101,84 +99,44 @@ std::ostream& operator<<(std::ostream& os, const DlPaint& paint) {
|
||||
return os << ")";
|
||||
}
|
||||
|
||||
#define DLT_OSTREAM_CASE(enum_name, value_name) \
|
||||
case enum_name::k##value_name: return os << #enum_name "::k" #value_name
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DlBlendMode& mode) {
|
||||
switch (mode) {
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Clear);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Src);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Dst);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, SrcOver);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, DstOver);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, SrcIn);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, DstIn);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, SrcOut);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, DstOut);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, SrcATop);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, DstATop);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Xor);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Plus);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Modulate);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Screen);
|
||||
case DlBlendMode::kClear: return os << "BlendMode::kClear";
|
||||
case DlBlendMode::kSrc: return os << "BlendMode::kSrc";
|
||||
case DlBlendMode::kDst: return os << "BlendMode::kDst";
|
||||
case DlBlendMode::kSrcOver: return os << "BlendMode::kSrcOver";
|
||||
case DlBlendMode::kDstOver: return os << "BlendMode::kDstOver";
|
||||
case DlBlendMode::kSrcIn: return os << "BlendMode::kSrcIn";
|
||||
case DlBlendMode::kDstIn: return os << "BlendMode::kDstIn";
|
||||
case DlBlendMode::kSrcOut: return os << "BlendMode::kSrcOut";
|
||||
case DlBlendMode::kDstOut: return os << "BlendMode::kDstOut";
|
||||
case DlBlendMode::kSrcATop: return os << "BlendMode::kSrcATop";
|
||||
case DlBlendMode::kDstATop: return os << "BlendMode::kDstATop";
|
||||
case DlBlendMode::kXor: return os << "BlendMode::kXor";
|
||||
case DlBlendMode::kPlus: return os << "BlendMode::kPlus";
|
||||
case DlBlendMode::kModulate: return os << "BlendMode::kModulate";
|
||||
case DlBlendMode::kScreen: return os << "BlendMode::kScreen";
|
||||
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Overlay);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Darken);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Lighten);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, ColorDodge);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, ColorBurn);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, HardLight);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, SoftLight);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Difference);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Exclusion);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Multiply);
|
||||
case DlBlendMode::kOverlay: return os << "BlendMode::kOverlay";
|
||||
case DlBlendMode::kDarken: return os << "BlendMode::kDarken";
|
||||
case DlBlendMode::kLighten: return os << "BlendMode::kLighten";
|
||||
case DlBlendMode::kColorDodge: return os << "BlendMode::kColorDodge";
|
||||
case DlBlendMode::kColorBurn: return os << "BlendMode::kColorBurn";
|
||||
case DlBlendMode::kHardLight: return os << "BlendMode::kHardLight";
|
||||
case DlBlendMode::kSoftLight: return os << "BlendMode::kSoftLight";
|
||||
case DlBlendMode::kDifference: return os << "BlendMode::kDifference";
|
||||
case DlBlendMode::kExclusion: return os << "BlendMode::kExclusion";
|
||||
case DlBlendMode::kMultiply: return os << "BlendMode::kMultiply";
|
||||
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Hue);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Saturation);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Color);
|
||||
DLT_OSTREAM_CASE(DlBlendMode, Luminosity);
|
||||
case DlBlendMode::kHue: return os << "BlendMode::kHue";
|
||||
case DlBlendMode::kSaturation: return os << "BlendMode::kSaturation";
|
||||
case DlBlendMode::kColor: return os << "BlendMode::kColor";
|
||||
case DlBlendMode::kLuminosity: return os << "BlendMode::kLuminosity";
|
||||
|
||||
default: return os << "BlendMode::????";
|
||||
}
|
||||
// Not a valid enum, should never happen, but in case we encounter bad data.
|
||||
return os << "DlBlendMode::????";
|
||||
}
|
||||
|
||||
extern std::ostream& operator<<(std::ostream& os,
|
||||
const flutter::DisplayListOpType& type) {
|
||||
switch (type) {
|
||||
#define DLT_OP_TYPE_CASE(V) DLT_OSTREAM_CASE(DisplayListOpType, V);
|
||||
FOR_EACH_DISPLAY_LIST_OP(DLT_OP_TYPE_CASE)
|
||||
DLT_OP_TYPE_CASE(InvalidOp)
|
||||
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
DLT_OP_TYPE_CASE(SetSceneColorSource)
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
|
||||
|
||||
#undef DLT_OP_TYPE_CASE
|
||||
}
|
||||
// Not a valid enum, should never happen, but in case we encounter bad data.
|
||||
return os << "DisplayListOpType::???";
|
||||
}
|
||||
|
||||
extern std::ostream& operator<<(
|
||||
std::ostream& os, const flutter::DisplayListOpCategory& category) {
|
||||
switch (category) {
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, Attribute);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, Transform);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, Clip);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, Save);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, SaveLayer);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, Restore);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, Rendering);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, SubDisplayList);
|
||||
DLT_OSTREAM_CASE(DisplayListOpCategory, InvalidCategory);
|
||||
}
|
||||
// Not a valid enum, should never happen, but in case we encounter bad data.
|
||||
return os << "DisplayListOpCategory::???";
|
||||
}
|
||||
|
||||
#undef DLT_OSTREAM_CASE
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const SaveLayerOptions& options) {
|
||||
return os << "SaveLayerOptions("
|
||||
<< "renders_with_attributes: " << options.renders_with_attributes()
|
||||
@ -539,12 +497,6 @@ void DisplayListStreamDispatcher::setColorSource(const DlColorSource* source) {
|
||||
<< sweep_src->tile_mode() << ", " << sweep_src->matrix_ptr() << ")";
|
||||
break;
|
||||
}
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
case DlColorSourceType::kScene: {
|
||||
os_ << "DlSceneColorSource()";
|
||||
break;
|
||||
}
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
default:
|
||||
os_ << "?DlUnknownColorSource?()";
|
||||
break;
|
||||
|
||||
@ -75,10 +75,6 @@ extern std::ostream& operator<<(std::ostream& os,
|
||||
const flutter::DlImage* image);
|
||||
extern std::ostream& operator<<(std::ostream& os,
|
||||
const flutter::SaveLayerOptions& image);
|
||||
extern std::ostream& operator<<(std::ostream& os,
|
||||
const flutter::DisplayListOpType& type);
|
||||
extern std::ostream& operator<<(std::ostream& os,
|
||||
const flutter::DisplayListOpCategory& category);
|
||||
|
||||
} // namespace std
|
||||
|
||||
@ -213,398 +209,6 @@ class DisplayListStreamDispatcher final : public DlOpReceiver {
|
||||
void out(const DlImageFilter* filter);
|
||||
};
|
||||
|
||||
class DisplayListGeneralReceiver : public DlOpReceiver {
|
||||
public:
|
||||
DisplayListGeneralReceiver() {
|
||||
type_counts_.fill(0u);
|
||||
category_counts_.fill(0u);
|
||||
}
|
||||
|
||||
void setAntiAlias(bool aa) override {
|
||||
RecordByType(DisplayListOpType::kSetAntiAlias);
|
||||
}
|
||||
void setInvertColors(bool invert) override {
|
||||
RecordByType(DisplayListOpType::kSetInvertColors);
|
||||
}
|
||||
void setStrokeCap(DlStrokeCap cap) override {
|
||||
RecordByType(DisplayListOpType::kSetStrokeCap);
|
||||
}
|
||||
void setStrokeJoin(DlStrokeJoin join) override {
|
||||
RecordByType(DisplayListOpType::kSetStrokeJoin);
|
||||
}
|
||||
void setDrawStyle(DlDrawStyle style) override {
|
||||
RecordByType(DisplayListOpType::kSetStyle);
|
||||
}
|
||||
void setStrokeWidth(float width) override {
|
||||
RecordByType(DisplayListOpType::kSetStrokeWidth);
|
||||
}
|
||||
void setStrokeMiter(float limit) override {
|
||||
RecordByType(DisplayListOpType::kSetStrokeMiter);
|
||||
}
|
||||
void setColor(DlColor color) override {
|
||||
RecordByType(DisplayListOpType::kSetColor);
|
||||
}
|
||||
void setBlendMode(DlBlendMode mode) override {
|
||||
RecordByType(DisplayListOpType::kSetBlendMode);
|
||||
}
|
||||
void setColorSource(const DlColorSource* source) override {
|
||||
if (source) {
|
||||
switch (source->type()) {
|
||||
case DlColorSourceType::kImage:
|
||||
RecordByType(DisplayListOpType::kSetImageColorSource);
|
||||
break;
|
||||
case DlColorSourceType::kRuntimeEffect:
|
||||
RecordByType(DisplayListOpType::kSetRuntimeEffectColorSource);
|
||||
break;
|
||||
case DlColorSourceType::kColor:
|
||||
case DlColorSourceType::kLinearGradient:
|
||||
case DlColorSourceType::kRadialGradient:
|
||||
case DlColorSourceType::kConicalGradient:
|
||||
case DlColorSourceType::kSweepGradient:
|
||||
RecordByType(DisplayListOpType::kSetPodColorSource);
|
||||
break;
|
||||
#ifdef IMPELLER_ENABLE_3D
|
||||
case DlColorSourceType::kScene:
|
||||
RecordByType(DisplayListOpType::kSetSceneColorSource);
|
||||
break;
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
}
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kClearColorSource);
|
||||
}
|
||||
}
|
||||
void setImageFilter(const DlImageFilter* filter) override {
|
||||
if (filter) {
|
||||
switch (filter->type()) {
|
||||
case DlImageFilterType::kBlur:
|
||||
case DlImageFilterType::kDilate:
|
||||
case DlImageFilterType::kErode:
|
||||
case DlImageFilterType::kMatrix:
|
||||
RecordByType(DisplayListOpType::kSetPodImageFilter);
|
||||
break;
|
||||
case DlImageFilterType::kCompose:
|
||||
case DlImageFilterType::kLocalMatrix:
|
||||
case DlImageFilterType::kColorFilter:
|
||||
RecordByType(DisplayListOpType::kSetSharedImageFilter);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kClearImageFilter);
|
||||
}
|
||||
}
|
||||
void setColorFilter(const DlColorFilter* filter) override {
|
||||
if (filter) {
|
||||
switch (filter->type()) {
|
||||
case DlColorFilterType::kBlend:
|
||||
case DlColorFilterType::kMatrix:
|
||||
case DlColorFilterType::kLinearToSrgbGamma:
|
||||
case DlColorFilterType::kSrgbToLinearGamma:
|
||||
RecordByType(DisplayListOpType::kSetPodColorFilter);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kClearColorFilter);
|
||||
}
|
||||
}
|
||||
void setMaskFilter(const DlMaskFilter* filter) override {
|
||||
if (filter) {
|
||||
switch (filter->type()) {
|
||||
case DlMaskFilterType::kBlur:
|
||||
RecordByType(DisplayListOpType::kSetPodMaskFilter);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kClearMaskFilter);
|
||||
}
|
||||
}
|
||||
|
||||
void translate(SkScalar tx, SkScalar ty) override {
|
||||
RecordByType(DisplayListOpType::kTranslate);
|
||||
}
|
||||
void scale(SkScalar sx, SkScalar sy) override {
|
||||
RecordByType(DisplayListOpType::kScale);
|
||||
}
|
||||
void rotate(SkScalar degrees) override {
|
||||
RecordByType(DisplayListOpType::kRotate);
|
||||
}
|
||||
void skew(SkScalar sx, SkScalar sy) override {
|
||||
RecordByType(DisplayListOpType::kSkew);
|
||||
}
|
||||
// clang-format off
|
||||
// 2x3 2D affine subset of a 4x4 transform in row major order
|
||||
void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
|
||||
SkScalar myx, SkScalar myy, SkScalar myt) override {
|
||||
RecordByType(DisplayListOpType::kTransform2DAffine);
|
||||
}
|
||||
// full 4x4 transform in row major order
|
||||
void transformFullPerspective(
|
||||
SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
|
||||
SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
|
||||
SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
|
||||
SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override {
|
||||
RecordByType(DisplayListOpType::kTransformFullPerspective);
|
||||
}
|
||||
// clang-format on
|
||||
void transformReset() override {
|
||||
RecordByType(DisplayListOpType::kTransformReset);
|
||||
}
|
||||
|
||||
void clipRect(const SkRect& rect,
|
||||
DlCanvas::ClipOp clip_op,
|
||||
bool is_aa) override {
|
||||
switch (clip_op) {
|
||||
case DlCanvas::ClipOp::kIntersect:
|
||||
RecordByType(DisplayListOpType::kClipIntersectRect);
|
||||
break;
|
||||
case DlCanvas::ClipOp::kDifference:
|
||||
RecordByType(DisplayListOpType::kClipDifferenceRect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void clipOval(const SkRect& bounds,
|
||||
DlCanvas::ClipOp clip_op,
|
||||
bool is_aa) override {
|
||||
switch (clip_op) {
|
||||
case DlCanvas::ClipOp::kIntersect:
|
||||
RecordByType(DisplayListOpType::kClipIntersectOval);
|
||||
break;
|
||||
case DlCanvas::ClipOp::kDifference:
|
||||
RecordByType(DisplayListOpType::kClipDifferenceOval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void clipRRect(const SkRRect& rrect,
|
||||
DlCanvas::ClipOp clip_op,
|
||||
bool is_aa) override {
|
||||
switch (clip_op) {
|
||||
case DlCanvas::ClipOp::kIntersect:
|
||||
RecordByType(DisplayListOpType::kClipIntersectRRect);
|
||||
break;
|
||||
case DlCanvas::ClipOp::kDifference:
|
||||
RecordByType(DisplayListOpType::kClipDifferenceRRect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void clipPath(const SkPath& path,
|
||||
DlCanvas::ClipOp clip_op,
|
||||
bool is_aa) override {
|
||||
switch (clip_op) {
|
||||
case DlCanvas::ClipOp::kIntersect:
|
||||
RecordByType(DisplayListOpType::kClipIntersectPath);
|
||||
break;
|
||||
case DlCanvas::ClipOp::kDifference:
|
||||
RecordByType(DisplayListOpType::kClipDifferencePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void save() override { RecordByType(DisplayListOpType::kSave); }
|
||||
void saveLayer(const SkRect& bounds,
|
||||
const SaveLayerOptions options,
|
||||
const DlImageFilter* backdrop) override {
|
||||
if (backdrop) {
|
||||
RecordByType(DisplayListOpType::kSaveLayerBackdrop);
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kSaveLayer);
|
||||
}
|
||||
}
|
||||
void restore() override { RecordByType(DisplayListOpType::kRestore); }
|
||||
|
||||
void drawColor(DlColor color, DlBlendMode mode) override {
|
||||
RecordByType(DisplayListOpType::kDrawColor);
|
||||
}
|
||||
void drawPaint() override { RecordByType(DisplayListOpType::kDrawPaint); }
|
||||
void drawLine(const SkPoint& p0, const SkPoint& p1) override {
|
||||
RecordByType(DisplayListOpType::kDrawLine);
|
||||
}
|
||||
void drawDashedLine(const DlPoint& p0,
|
||||
const DlPoint& p1,
|
||||
DlScalar on_length,
|
||||
DlScalar off_length) override {
|
||||
RecordByType(DisplayListOpType::kDrawDashedLine);
|
||||
}
|
||||
void drawRect(const SkRect& rect) override {
|
||||
RecordByType(DisplayListOpType::kDrawRect);
|
||||
}
|
||||
void drawOval(const SkRect& bounds) override {
|
||||
RecordByType(DisplayListOpType::kDrawOval);
|
||||
}
|
||||
void drawCircle(const SkPoint& center, SkScalar radius) override {
|
||||
RecordByType(DisplayListOpType::kDrawCircle);
|
||||
}
|
||||
void drawRRect(const SkRRect& rrect) override {
|
||||
RecordByType(DisplayListOpType::kDrawRRect);
|
||||
}
|
||||
void drawDRRect(const SkRRect& outer, const SkRRect& inner) override {
|
||||
RecordByType(DisplayListOpType::kDrawDRRect);
|
||||
}
|
||||
void drawPath(const SkPath& path) override {
|
||||
RecordByType(DisplayListOpType::kDrawPath);
|
||||
}
|
||||
void drawArc(const SkRect& oval_bounds,
|
||||
SkScalar start_degrees,
|
||||
SkScalar sweep_degrees,
|
||||
bool use_center) override {
|
||||
RecordByType(DisplayListOpType::kDrawArc);
|
||||
}
|
||||
void drawPoints(DlCanvas::PointMode mode,
|
||||
uint32_t count,
|
||||
const SkPoint points[]) override {
|
||||
switch (mode) {
|
||||
case DlCanvas::PointMode::kPoints:
|
||||
RecordByType(DisplayListOpType::kDrawPoints);
|
||||
break;
|
||||
case DlCanvas::PointMode::kLines:
|
||||
RecordByType(DisplayListOpType::kDrawLines);
|
||||
break;
|
||||
case DlCanvas::PointMode::kPolygon:
|
||||
RecordByType(DisplayListOpType::kDrawPolygon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void drawVertices(const std::shared_ptr<DlVertices>& vertices,
|
||||
DlBlendMode mode) override {
|
||||
RecordByType(DisplayListOpType::kDrawVertices);
|
||||
}
|
||||
void drawImage(const sk_sp<DlImage> image,
|
||||
const SkPoint point,
|
||||
DlImageSampling sampling,
|
||||
bool render_with_attributes) override {
|
||||
if (render_with_attributes) {
|
||||
RecordByType(DisplayListOpType::kDrawImageWithAttr);
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kDrawImage);
|
||||
}
|
||||
}
|
||||
void drawImageRect(const sk_sp<DlImage> image,
|
||||
const SkRect& src,
|
||||
const SkRect& dst,
|
||||
DlImageSampling sampling,
|
||||
bool render_with_attributes,
|
||||
SrcRectConstraint constraint) override {
|
||||
RecordByType(DisplayListOpType::kDrawImageRect);
|
||||
}
|
||||
void drawImageNine(const sk_sp<DlImage> image,
|
||||
const SkIRect& center,
|
||||
const SkRect& dst,
|
||||
DlFilterMode filter,
|
||||
bool render_with_attributes) override {
|
||||
if (render_with_attributes) {
|
||||
RecordByType(DisplayListOpType::kDrawImageNineWithAttr);
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kDrawImageNine);
|
||||
}
|
||||
}
|
||||
void drawAtlas(const sk_sp<DlImage> atlas,
|
||||
const SkRSXform xform[],
|
||||
const SkRect tex[],
|
||||
const DlColor colors[],
|
||||
int count,
|
||||
DlBlendMode mode,
|
||||
DlImageSampling sampling,
|
||||
const SkRect* cull_rect,
|
||||
bool render_with_attributes) override {
|
||||
if (cull_rect) {
|
||||
RecordByType(DisplayListOpType::kDrawAtlasCulled);
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kDrawAtlas);
|
||||
}
|
||||
}
|
||||
void drawDisplayList(const sk_sp<DisplayList> display_list,
|
||||
SkScalar opacity) override {
|
||||
RecordByType(DisplayListOpType::kDrawDisplayList);
|
||||
}
|
||||
void drawTextBlob(const sk_sp<SkTextBlob> blob,
|
||||
SkScalar x,
|
||||
SkScalar y) override {
|
||||
RecordByType(DisplayListOpType::kDrawTextBlob);
|
||||
}
|
||||
void drawTextFrame(const std::shared_ptr<impeller::TextFrame>& text_frame,
|
||||
SkScalar x,
|
||||
SkScalar y) override {
|
||||
RecordByType(DisplayListOpType::kDrawTextFrame);
|
||||
}
|
||||
void drawShadow(const SkPath& path,
|
||||
const DlColor color,
|
||||
const SkScalar elevation,
|
||||
bool transparent_occluder,
|
||||
SkScalar dpr) override {
|
||||
if (transparent_occluder) {
|
||||
RecordByType(DisplayListOpType::kDrawShadowTransparentOccluder);
|
||||
} else {
|
||||
RecordByType(DisplayListOpType::kDrawShadow);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetOpsReceived() { return op_count_; }
|
||||
uint32_t GetOpsReceived(DisplayListOpCategory category) {
|
||||
return category_counts_[static_cast<int>(category)];
|
||||
}
|
||||
uint32_t GetOpsReceived(DisplayListOpType type) {
|
||||
return type_counts_[static_cast<int>(type)];
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void RecordByType(DisplayListOpType type) {
|
||||
type_counts_[static_cast<int>(type)]++;
|
||||
RecordByCategory(DisplayList::GetOpCategory(type));
|
||||
}
|
||||
|
||||
virtual void RecordByCategory(DisplayListOpCategory category) {
|
||||
category_counts_[static_cast<int>(category)]++;
|
||||
switch (category) {
|
||||
case DisplayListOpCategory::kAttribute:
|
||||
RecordAttribute();
|
||||
break;
|
||||
case DisplayListOpCategory::kTransform:
|
||||
RecordTransform();
|
||||
break;
|
||||
case DisplayListOpCategory::kClip:
|
||||
RecordClip();
|
||||
break;
|
||||
case DisplayListOpCategory::kSave:
|
||||
RecordSave();
|
||||
break;
|
||||
case DisplayListOpCategory::kSaveLayer:
|
||||
RecordSaveLayer();
|
||||
break;
|
||||
case DisplayListOpCategory::kRestore:
|
||||
RecordRestore();
|
||||
break;
|
||||
case DisplayListOpCategory::kRendering:
|
||||
RecordRendering();
|
||||
break;
|
||||
case DisplayListOpCategory::kSubDisplayList:
|
||||
RecordSubDisplayList();
|
||||
break;
|
||||
case DisplayListOpCategory::kInvalidCategory:
|
||||
RecordInvalid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void RecordAttribute() { RecordOp(); }
|
||||
virtual void RecordTransform() { RecordOp(); }
|
||||
virtual void RecordClip() { RecordOp(); }
|
||||
virtual void RecordSave() { RecordOp(); }
|
||||
virtual void RecordSaveLayer() { RecordOp(); }
|
||||
virtual void RecordRestore() { RecordOp(); }
|
||||
virtual void RecordRendering() { RecordOp(); }
|
||||
virtual void RecordSubDisplayList() { RecordOp(); }
|
||||
virtual void RecordInvalid() { RecordOp(); }
|
||||
|
||||
virtual void RecordOp() { op_count_++; }
|
||||
|
||||
static constexpr size_t kTypeCount =
|
||||
static_cast<size_t>(DisplayListOpType::kMaxOp) + 1;
|
||||
static constexpr size_t kCategoryCount =
|
||||
static_cast<size_t>(DisplayListOpCategory::kMaxCategory) + 1;
|
||||
|
||||
std::array<uint32_t, kTypeCount> type_counts_;
|
||||
std::array<uint32_t, kCategoryCount> category_counts_;
|
||||
uint32_t op_count_ = 0u;
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user