forked from firka/flutter
Resolves https://github.com/flutter/flutter/issues/134748.
This was a really fun experiment. I learned a lot from it, and it
genuinely helped me solve some coverage-related problems, but the
reality is it was too little too late -- by the time we had this
capture system, we had already solved most of the problems that would
have benefitted from this.
It's been a few months since I've used or extended the capabilities of
this capture system for something, and I don't have the spare
time/energy to give it the love it needs to realize the vision I had
for it. I still almost exclusively use a combination of native frame
captures and print debugging to solve problems.
RIP in peace.
This reverts commit 104eb98e62. (https://github.com/flutter/engine/pull/52680)
164 lines
5.6 KiB
C++
164 lines
5.6 KiB
C++
// 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 "impeller/entity/entity_pass_clip_stack.h"
|
|
#include "impeller/entity/contents/clip_contents.h"
|
|
#include "impeller/entity/contents/content_context.h"
|
|
#include "impeller/entity/entity.h"
|
|
|
|
namespace impeller {
|
|
|
|
EntityPassClipStack::EntityPassClipStack(const Rect& initial_coverage_rect) {
|
|
subpass_state_.push_back(SubpassState{
|
|
.clip_coverage =
|
|
{
|
|
{ClipCoverageLayer{
|
|
.coverage = initial_coverage_rect,
|
|
.clip_height = 0,
|
|
}},
|
|
},
|
|
});
|
|
}
|
|
|
|
std::optional<Rect> EntityPassClipStack::CurrentClipCoverage() const {
|
|
return subpass_state_.back().clip_coverage.back().coverage;
|
|
}
|
|
|
|
bool EntityPassClipStack::HasCoverage() const {
|
|
return !subpass_state_.back().clip_coverage.empty();
|
|
}
|
|
|
|
void EntityPassClipStack::PushSubpass(std::optional<Rect> subpass_coverage,
|
|
size_t clip_height) {
|
|
subpass_state_.push_back(SubpassState{
|
|
.clip_coverage =
|
|
{
|
|
ClipCoverageLayer{.coverage = subpass_coverage,
|
|
.clip_height = clip_height},
|
|
},
|
|
});
|
|
}
|
|
|
|
void EntityPassClipStack::PopSubpass() {
|
|
subpass_state_.pop_back();
|
|
}
|
|
|
|
const std::vector<ClipCoverageLayer>
|
|
EntityPassClipStack::GetClipCoverageLayers() const {
|
|
return subpass_state_.back().clip_coverage;
|
|
}
|
|
|
|
EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState(
|
|
Contents::ClipCoverage global_clip_coverage,
|
|
Entity& entity,
|
|
size_t clip_height_floor,
|
|
Point global_pass_position) {
|
|
ClipStateResult result = {.should_render = false, .clip_did_change = false};
|
|
|
|
auto& subpass_state = GetCurrentSubpassState();
|
|
switch (global_clip_coverage.type) {
|
|
case Contents::ClipCoverage::Type::kNoChange:
|
|
break;
|
|
case Contents::ClipCoverage::Type::kAppend: {
|
|
auto op = CurrentClipCoverage();
|
|
|
|
// Compute the previous clip height.
|
|
size_t previous_clip_height = 0;
|
|
if (!subpass_state.clip_coverage.empty()) {
|
|
previous_clip_height = subpass_state.clip_coverage.back().clip_height;
|
|
} else {
|
|
// If there is no clip coverage, then the previous clip height is the
|
|
// clip height floor.
|
|
previous_clip_height = clip_height_floor;
|
|
}
|
|
|
|
subpass_state.clip_coverage.push_back(
|
|
ClipCoverageLayer{.coverage = global_clip_coverage.coverage,
|
|
.clip_height = previous_clip_height + 1});
|
|
result.clip_did_change = true;
|
|
|
|
FML_DCHECK(subpass_state.clip_coverage.back().clip_height ==
|
|
subpass_state.clip_coverage.front().clip_height +
|
|
subpass_state.clip_coverage.size() - 1);
|
|
|
|
if (!op.has_value()) {
|
|
// Running this append op won't impact the clip buffer because the
|
|
// whole screen is already being clipped, so skip it.
|
|
return result;
|
|
}
|
|
} break;
|
|
case Contents::ClipCoverage::Type::kRestore: {
|
|
ClipRestoreContents* restore_contents =
|
|
reinterpret_cast<ClipRestoreContents*>(entity.GetContents().get());
|
|
size_t restore_height = restore_contents->GetRestoreHeight();
|
|
|
|
if (subpass_state.clip_coverage.back().clip_height <= restore_height) {
|
|
// Drop clip restores that will do nothing.
|
|
return result;
|
|
}
|
|
|
|
auto restoration_index =
|
|
restore_height - subpass_state.clip_coverage.front().clip_height;
|
|
FML_DCHECK(restoration_index < subpass_state.clip_coverage.size());
|
|
|
|
// We only need to restore the area that covers the coverage of the
|
|
// clip rect at target height + 1.
|
|
std::optional<Rect> restore_coverage =
|
|
(restoration_index + 1 < subpass_state.clip_coverage.size())
|
|
? subpass_state.clip_coverage[restoration_index + 1].coverage
|
|
: std::nullopt;
|
|
if (restore_coverage.has_value()) {
|
|
// Make the coverage rectangle relative to the current pass.
|
|
restore_coverage = restore_coverage->Shift(-global_pass_position);
|
|
}
|
|
subpass_state.clip_coverage.resize(restoration_index + 1);
|
|
result.clip_did_change = true;
|
|
|
|
// Skip all clip restores when stencil-then-cover is enabled.
|
|
if (subpass_state.clip_coverage.back().coverage.has_value()) {
|
|
RecordEntity(entity, global_clip_coverage.type, Rect());
|
|
}
|
|
return result;
|
|
|
|
} break;
|
|
}
|
|
|
|
RecordEntity(entity, global_clip_coverage.type,
|
|
subpass_state.clip_coverage.back().coverage);
|
|
|
|
result.should_render = true;
|
|
return result;
|
|
}
|
|
|
|
void EntityPassClipStack::RecordEntity(const Entity& entity,
|
|
Contents::ClipCoverage::Type type,
|
|
std::optional<Rect> clip_coverage) {
|
|
auto& subpass_state = GetCurrentSubpassState();
|
|
switch (type) {
|
|
case Contents::ClipCoverage::Type::kNoChange:
|
|
return;
|
|
case Contents::ClipCoverage::Type::kAppend:
|
|
subpass_state.rendered_clip_entities.push_back(
|
|
{.entity = entity.Clone(), .clip_coverage = clip_coverage});
|
|
break;
|
|
case Contents::ClipCoverage::Type::kRestore:
|
|
if (!subpass_state.rendered_clip_entities.empty()) {
|
|
subpass_state.rendered_clip_entities.pop_back();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
EntityPassClipStack::SubpassState&
|
|
EntityPassClipStack::GetCurrentSubpassState() {
|
|
return subpass_state_.back();
|
|
}
|
|
|
|
const std::vector<EntityPassClipStack::ReplayResult>&
|
|
EntityPassClipStack::GetReplayEntities() const {
|
|
return subpass_state_.back().rendered_clip_entities;
|
|
}
|
|
|
|
} // namespace impeller
|