Skip to content

Commit

Permalink
Add damage_context to determine damage recangle
Browse files Browse the repository at this point in the history
Damage rectangle can be determined by providing optional FrameDamage argument to CompositorContext::ScopedFrame::Render
  • Loading branch information
knopp committed Sep 11, 2020
1 parent c247ac1 commit 6e39c76
Show file tree
Hide file tree
Showing 21 changed files with 530 additions and 18 deletions.
2 changes: 2 additions & 0 deletions flow/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ source_set("flow") {
sources = [
"compositor_context.cc",
"compositor_context.h",
"damage_context.cc",
"damage_context.h",
"embedded_views.cc",
"embedded_views.h",
"gl_context_switch.cc",
Expand Down
47 changes: 43 additions & 4 deletions flow/compositor_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,17 @@ std::unique_ptr<CompositorContext::ScopedFrame> CompositorContext::AcquireFrame(
bool surface_supports_readback,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
return std::make_unique<ScopedFrame>(
*this, gr_context, canvas, view_embedder, root_surface_transformation,
instrumentation_enabled, surface_supports_readback, raster_thread_merger);
*this, gr_context, canvas, view_embedder, &damage_context_,
root_surface_transformation, instrumentation_enabled,
surface_supports_readback, raster_thread_merger);
}

CompositorContext::ScopedFrame::ScopedFrame(
CompositorContext& context,
GrDirectContext* gr_context,
SkCanvas* canvas,
ExternalViewEmbedder* view_embedder,
DamageContext* damage_context,
const SkMatrix& root_surface_transformation,
bool instrumentation_enabled,
bool surface_supports_readback,
Expand All @@ -56,6 +58,7 @@ CompositorContext::ScopedFrame::ScopedFrame(
gr_context_(gr_context),
canvas_(canvas),
view_embedder_(view_embedder),
damage_context_(damage_context),
root_surface_transformation_(root_surface_transformation),
instrumentation_enabled_(instrumentation_enabled),
surface_supports_readback_(surface_supports_readback),
Expand All @@ -69,9 +72,31 @@ CompositorContext::ScopedFrame::~ScopedFrame() {

RasterStatus CompositorContext::ScopedFrame::Raster(
flutter::LayerTree& layer_tree,
bool ignore_raster_cache) {
bool ignore_raster_cache,
FrameDamage* frame_damage) {
TRACE_EVENT0("flutter", "CompositorContext::ScopedFrame::Raster");
bool root_needs_readback = layer_tree.Preroll(*this, ignore_raster_cache);

std::optional<DamageArea> damage_area;

if (frame_damage) {
damage_context()->InitFrame(layer_tree.frame_size(),
frame_damage->previous_frame_description);
layer_tree.Preroll(*this, true);
auto damage_res = damage_context()->FinishFrame();
frame_damage->frame_description = std::move(damage_res.frame_description);

// only bother clipping when less than 80% of screen
if (damage_res.area.bounds().width() * damage_res.area.bounds().height() <
0.8 * layer_tree.frame_size().width() *
layer_tree.frame_size().height()) {
damage_area = std::move(damage_res.area);
}
}

auto cull_rect =
damage_area ? SkRect::Make(damage_area->bounds()) : kGiantRect;
bool root_needs_readback =
layer_tree.Preroll(*this, ignore_raster_cache, cull_rect);
bool needs_save_layer = root_needs_readback && !surface_supports_readback();
PostPrerollResult post_preroll_result = PostPrerollResult::kSuccess;
if (view_embedder_ && raster_thread_merger_) {
Expand All @@ -82,6 +107,12 @@ RasterStatus CompositorContext::ScopedFrame::Raster(
if (post_preroll_result == PostPrerollResult::kResubmitFrame) {
return RasterStatus::kResubmit;
}

if (canvas() && damage_area) {
canvas()->save();
canvas()->clipRect(SkRect::Make(damage_area->bounds()));
}

// Clearing canvas after preroll reduces one render target switch when preroll
// paints some raster cache.
if (canvas()) {
Expand All @@ -98,6 +129,14 @@ RasterStatus CompositorContext::ScopedFrame::Raster(
if (canvas() && needs_save_layer) {
canvas()->restore();
}
if (canvas() && damage_area) {
canvas()->restore();
}

if (frame_damage) {
frame_damage->damage_area = std::move(damage_area);
}

return RasterStatus::kSuccess;
}

Expand Down
21 changes: 20 additions & 1 deletion flow/compositor_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <memory>
#include <string>

#include "flutter/flow/damage_context.h"
#include "flutter/flow/embedded_views.h"
#include "flutter/flow/instrumentation.h"
#include "flutter/flow/raster_cache.h"
Expand Down Expand Up @@ -35,6 +36,18 @@ enum class RasterStatus {
kFailed
};

struct FrameDamage {
// in: description for frame previously rasterized in target framebuffer
const DamageContext::FrameDescription* previous_frame_description;

// out: description for frame being rasterized
std::unique_ptr<DamageContext::FrameDescription> frame_description;

// out: area in framebuffer that has changed between frames;
// if optional is empty, whole frame was repainted
std::optional<DamageArea> damage_area;
};

class CompositorContext {
public:
class ScopedFrame {
Expand All @@ -43,6 +56,7 @@ class CompositorContext {
GrDirectContext* gr_context,
SkCanvas* canvas,
ExternalViewEmbedder* view_embedder,
DamageContext* damage_context,
const SkMatrix& root_surface_transformation,
bool instrumentation_enabled,
bool surface_supports_readback,
Expand All @@ -54,6 +68,8 @@ class CompositorContext {

ExternalViewEmbedder* view_embedder() { return view_embedder_; }

DamageContext* damage_context() { return damage_context_; }

CompositorContext& context() const { return context_; }

const SkMatrix& root_surface_transformation() const {
Expand All @@ -65,13 +81,15 @@ class CompositorContext {
GrDirectContext* gr_context() const { return gr_context_; }

virtual RasterStatus Raster(LayerTree& layer_tree,
bool ignore_raster_cache);
bool ignore_raster_cache,
FrameDamage* frame_damage);

private:
CompositorContext& context_;
GrDirectContext* gr_context_;
SkCanvas* canvas_;
ExternalViewEmbedder* view_embedder_;
DamageContext* damage_context_;
const SkMatrix& root_surface_transformation_;
const bool instrumentation_enabled_;
const bool surface_supports_readback_;
Expand Down Expand Up @@ -110,6 +128,7 @@ class CompositorContext {
private:
RasterCache raster_cache_;
TextureRegistry texture_registry_;
DamageContext damage_context_;
Counter frame_count_;
Stopwatch raster_time_;
Stopwatch ui_time_;
Expand Down
200 changes: 200 additions & 0 deletions flow/damage_context.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#include "flutter/flow/damage_context.h"
#include "flutter/flow/layers/layer.h"
#include "third_party/skia/include/core/SkImageFilter.h"

namespace flutter {

std::size_t DamageContext::LayerContribution::Hash::operator()(
const LayerContribution& e) const noexcept {
size_t res = e.paint_bounds.left();
res = 37 * res + e.paint_bounds.top();
res = 37 * res + e.paint_bounds.width();
res = 37 * res + e.paint_bounds.height();
res = 37 * res + size_t(reinterpret_cast<std::uintptr_t>(e.comparator));
return res;
}

bool DamageContext::LayerContribution::operator==(
const LayerContribution& e) const {
return comparator == e.comparator && paint_bounds == e.paint_bounds &&
std::equal(
mutators.begin(), mutators.end(), e.mutators.begin(),
e.mutators.end(),
[](const std::shared_ptr<Mutator>& m1,
const std::shared_ptr<Mutator>& m2) { return *m1 == *m2; }) &&
(layer.get() == e.layer.get() ||
comparator(layer.get(), e.layer.get()));
}

void DamageContext::InitFrame(const SkISize& tree_size,
const FrameDescription* previous_frame) {
current_layer_tree_size_ = tree_size;
previous_frame_ = previous_frame;
}

void DamageContext::PushLayerContribution(const Layer* layer,
LayerComparator comparator,
const SkMatrix& matrix,
const PrerollContext& preroll_context,
size_t index) {
if (current_layer_tree_size_.isEmpty() || layer->paint_bounds().isEmpty()) {
return;
}

LayerContribution e;
e.layer = layer->shared_from_this();
e.comparator = comparator;

SkRect bounds = layer->paint_bounds();
bounds.intersect(preroll_context.cull_rect);
e.paint_bounds = matrix.mapRect(bounds);

for (auto i = preroll_context.mutators_stack.Begin();
i != preroll_context.mutators_stack.End(); ++i) {
auto type = (*i)->GetType();
// transforms are irrelevant because we compare paint_bounds in
// screen coordinates
if (type != MutatorType::transform) {
e.mutators.push_back(*i);
}
}
if (index == size_t(-1)) {
layer_entries_.push_back(std::move(e));
} else {
layer_entries_.insert(layer_entries_.begin() + index, std::move(e));
}
}

bool DamageContext::ApplyImageFilter(size_t from,
size_t count,
const SkImageFilter* filter,
const SkMatrix& matrix,
const SkRect& bounds) {
SkMatrix inverted;
if (!matrix.invert(&inverted)) {
return false;
}

for (size_t i = from; i < count; ++i) {
auto& entry = layer_entries_[i];
SkRect layer_bounds(entry.paint_bounds);
inverted.mapRect(&layer_bounds);
if (layer_bounds.intersects(bounds)) {
// layer paint bounds after filter can not get bigger than union of
// original paint bounds and filter bounds
SkRect max(layer_bounds);
max.join(bounds);

layer_bounds = filter->computeFastBounds(layer_bounds);
layer_bounds.intersect(max);

matrix.mapRect(&layer_bounds);
entry.paint_bounds = layer_bounds;
}
}

return true;
}

void DamageArea::AddRect(const SkRect& rect) {
SkIRect irect;
rect.roundOut(&irect);
bounds_.join(irect);
}

void DamageArea::AddRect(const SkIRect& rect) {
bounds_.join(rect);
}

std::vector<SkIRect> DamageArea::GetRects() const {
std::vector<SkIRect> res;
res.push_back(bounds_);
return res;
}

DamageContext::DamageResult DamageContext::FinishFrame() {
DamageResult res;
res.frame_description.reset(new FrameDescription());
res.frame_description->layer_tree_size = current_layer_tree_size_;
auto& entries = res.frame_description->entries;

for (size_t i = 0; i < layer_entries_.size(); ++i) {
auto& entry = layer_entries_[i];
entry.paint_order = i;
entries.insert(std::move(entry));
}

if (!previous_frame_ ||
previous_frame_->layer_tree_size != current_layer_tree_size_) {
res.area.AddRect(SkRect::MakeIWH(current_layer_tree_size_.width(),
current_layer_tree_size_.height()));
} else {
// layer entries that are only found in one set (only this frame or only
// previous frame) are for layers that were either added, removed, or
// modified in any way (fail the equality check in LayerContribution) and
// thus contribute to damage area

// matching layer entries from previous frame and this frame; we still need
// to check for paint order to detect reordered layers
std::vector<const LayerContribution*> matching_previous;
std::vector<const LayerContribution*> matching_current;

for (const auto& l : entries) {
auto prev = previous_frame_->entries.find(l);
if (prev == previous_frame_->entries.end()) {
res.area.AddRect(l.paint_bounds);
} else {
matching_current.push_back(&l);
matching_previous.push_back(&*prev);
}
}
for (const auto& l : previous_frame_->entries) {
if (entries.find(l) == entries.end()) {
res.area.AddRect(l.paint_bounds);
}
}

// determine which layers are reordered
auto comparator = [](const LayerContribution* l1,
const LayerContribution* l2) {
return l1->paint_order < l2->paint_order;
};
std::sort(matching_previous.begin(), matching_previous.end(), comparator);
std::sort(matching_current.begin(), matching_current.end(), comparator);

// We have two sets of matching layer entries that possibly differ in paint
// order and we sorted them by paint order, i.e.
// B C D E
// ^-- prev
// C D B E
// ^-- cur
// 1. move cur until match with prev is found (B)
// all layers before cur that intersect with prev are reordered and will
// contribute to damage, as does prev
// 2. remove prev and cur (now both pointing at B)
// 3. repeat until empty
// (except we actually do it in reverse to not erase from beginning)
while (!matching_previous.empty()) {
auto prev = matching_previous.end() - 1;
auto cur = matching_current.end() - 1;

while (*(*prev) != *(*cur)) {
if ((*prev)->paint_bounds.intersects((*cur)->paint_bounds)) {
res.area.AddRect((*prev)->paint_bounds);
res.area.AddRect((*cur)->paint_bounds);
}
--cur;
}
matching_previous.erase(prev);
matching_current.erase(cur);
}
}

previous_frame_ = nullptr;
layer_entries_.clear();
current_layer_tree_size_ = SkISize::MakeEmpty();

return res;
}

} // namespace flutter
Loading

0 comments on commit 6e39c76

Please sign in to comment.