-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] partial repaint for Impeller/iOS. #40959
Changes from all commits
449fce2
ee5b155
c0c227b
18fc13e
4f96864
5477505
503c7e1
3488b59
3732339
5081bb7
b48d948
fc3ef29
00dcdba
04c79db
a76d650
2bde543
bff3803
a1c4caa
ef33292
034036f
353bd2f
38ef3db
c701ac0
4055369
4fb6a98
4c84d0f
1a0de9a
b91f739
0fdbd2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,15 +91,24 @@ class FrameDamage { | |
|
|
||
| // See Damage::buffer_damage. | ||
| std::optional<SkIRect> GetBufferDamage() { | ||
| return damage_ ? std::make_optional(damage_->buffer_damage) : std::nullopt; | ||
| return (damage_ && !ignore_damage_) | ||
| ? std::make_optional(damage_->buffer_damage) | ||
| : std::nullopt; | ||
| } | ||
|
|
||
| // Remove reported buffer_damage to inform clients that a partial repaint | ||
| // should not be performed on this frame. | ||
| // frame_damage is required to correctly track accumulated damage for | ||
| // subsequent frames. | ||
| void Reset() { ignore_damage_ = true; } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if this could simply be
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interestingly if I do this rendering breaks. I wonder if the logic for computing damage requires all frames to have up to date damage calculations? Or if perhaps the std::nullopt signal is incorrect for frame damage and it should instead be treated as if the entire screen were dirty?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. It breaks because in that case glitch.movPossible solution might be something like knopp@30c3d61 . Except this doesn't work because it causes So I'd keep it as it is, maybe add a comment that we need to preserve
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, thanks for the investigation here. I added a comment, but I think it makes sense to keep frame damage because the damage info is still correct a subsequent frame could use this and decide to do a partial repaint. |
||
|
|
||
| private: | ||
| SkIRect additional_damage_ = SkIRect::MakeEmpty(); | ||
| std::optional<Damage> damage_; | ||
| const LayerTree* prev_layer_tree_ = nullptr; | ||
| int vertical_clip_alignment_ = 1; | ||
| int horizontal_clip_alignment_ = 1; | ||
| bool ignore_damage_ = false; | ||
| }; | ||
|
|
||
| class CompositorContext { | ||
|
|
@@ -144,6 +153,15 @@ class CompositorContext { | |
| FrameDamage* frame_damage); | ||
|
|
||
| private: | ||
| void PaintLayerTreeSkia(flutter::LayerTree& layer_tree, | ||
| std::optional<SkRect> clip_rect, | ||
| bool needs_save_layer, | ||
| bool ignore_raster_cache); | ||
|
|
||
| void PaintLayerTreeImpeller(flutter::LayerTree& layer_tree, | ||
| std::optional<SkRect> clip_rect, | ||
| bool ignore_raster_cache); | ||
|
|
||
| CompositorContext& context_; | ||
| GrDirectContext* gr_context_; | ||
| DlCanvas* canvas_; | ||
|
|
@@ -205,6 +223,12 @@ class CompositorContext { | |
|
|
||
| void EndFrame(ScopedFrame& frame, bool enable_instrumentation); | ||
|
|
||
| /// @brief Whether Impeller shouild attempt a partial repaint. | ||
| /// The Impeller backend requires an additional blit pass, which may | ||
| /// not be worthwhile if the damage region is large. | ||
| static bool ShouldPerformPartialRepaint(std::optional<SkRect> damage_rect, | ||
| SkISize layer_tree_size); | ||
|
|
||
| FML_DISALLOW_COPY_AND_ASSIGN(CompositorContext); | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| #include "impeller/renderer/backend/metal/surface_mtl.h" | ||
|
|
||
| #include "flutter/fml/trace_event.h" | ||
| #include "flutter/impeller/renderer/command_buffer.h" | ||
| #include "impeller/base/validation.h" | ||
| #include "impeller/renderer/backend/metal/context_mtl.h" | ||
| #include "impeller/renderer/backend/metal/formats_mtl.h" | ||
|
|
@@ -16,7 +17,7 @@ | |
| #pragma GCC diagnostic push | ||
| #pragma GCC diagnostic ignored "-Wunguarded-availability-new" | ||
|
|
||
| std::unique_ptr<SurfaceMTL> SurfaceMTL::WrapCurrentMetalLayerDrawable( | ||
| id<CAMetalDrawable> SurfaceMTL::GetMetalDrawableAndValidate( | ||
| const std::shared_ptr<Context>& context, | ||
| CAMetalLayer* layer) { | ||
| TRACE_EVENT0("impeller", "SurfaceMTL::WrapCurrentMetalLayerDrawable"); | ||
|
|
@@ -35,23 +36,37 @@ | |
| VALIDATION_LOG << "Could not acquire current drawable."; | ||
| return nullptr; | ||
| } | ||
| return current_drawable; | ||
| } | ||
|
|
||
| const auto color_format = | ||
| FromMTLPixelFormat(current_drawable.texture.pixelFormat); | ||
| std::unique_ptr<SurfaceMTL> SurfaceMTL::WrapCurrentMetalLayerDrawable( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I split this and the above into two functions, so that we can ge the clip rect for DRM before we construct the render target, which is required for conditionally adjusting the strategy. |
||
| const std::shared_ptr<Context>& context, | ||
| id<CAMetalDrawable> drawable, | ||
| std::optional<IRect> clip_rect) { | ||
| bool requires_blit = ShouldPerformPartialRepaint(clip_rect); | ||
| const auto color_format = FromMTLPixelFormat(drawable.texture.pixelFormat); | ||
|
|
||
| if (color_format == PixelFormat::kUnknown) { | ||
| VALIDATION_LOG << "Unknown drawable color format."; | ||
| return nullptr; | ||
| } | ||
| // compositor_context.cc will offset the rendering by the clip origin. Here we | ||
| // shrink to the size of the clip. This has the same effect as clipping the | ||
| // rendering but also creates smaller intermediate passes. | ||
| ISize root_size; | ||
| if (requires_blit) { | ||
| root_size = ISize(clip_rect->size.width, clip_rect->size.height); | ||
| } else { | ||
| root_size = {static_cast<ISize::Type>(drawable.texture.width), | ||
| static_cast<ISize::Type>(drawable.texture.height)}; | ||
| } | ||
|
|
||
| TextureDescriptor msaa_tex_desc; | ||
| msaa_tex_desc.storage_mode = StorageMode::kDeviceTransient; | ||
| msaa_tex_desc.type = TextureType::kTexture2DMultisample; | ||
| msaa_tex_desc.sample_count = SampleCount::kCount4; | ||
| msaa_tex_desc.format = color_format; | ||
| msaa_tex_desc.size = { | ||
| static_cast<ISize::Type>(current_drawable.texture.width), | ||
| static_cast<ISize::Type>(current_drawable.texture.height)}; | ||
| msaa_tex_desc.size = root_size; | ||
| msaa_tex_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget); | ||
|
|
||
| auto msaa_tex = context->GetResourceAllocator()->CreateTexture(msaa_tex_desc); | ||
|
|
@@ -68,8 +83,17 @@ | |
| resolve_tex_desc.sample_count = SampleCount::kCount1; | ||
| resolve_tex_desc.storage_mode = StorageMode::kDevicePrivate; | ||
|
|
||
| std::shared_ptr<Texture> resolve_tex = | ||
| std::make_shared<TextureMTL>(resolve_tex_desc, current_drawable.texture); | ||
| // Create color resolve texture. | ||
| std::shared_ptr<Texture> resolve_tex; | ||
| if (requires_blit) { | ||
| resolve_tex_desc.compression_type = CompressionType::kLossy; | ||
| resolve_tex = | ||
| context->GetResourceAllocator()->CreateTexture(resolve_tex_desc); | ||
| } else { | ||
| resolve_tex = | ||
| std::make_shared<TextureMTL>(resolve_tex_desc, drawable.texture); | ||
| } | ||
|
|
||
| if (!resolve_tex) { | ||
| VALIDATION_LOG << "Could not wrap resolve texture."; | ||
| return nullptr; | ||
|
|
@@ -112,18 +136,42 @@ | |
| render_target_desc.SetStencilAttachment(stencil0); | ||
|
|
||
| // The constructor is private. So make_unique may not be used. | ||
| return std::unique_ptr<SurfaceMTL>(new SurfaceMTL( | ||
| context->weak_from_this(), render_target_desc, current_drawable)); | ||
| return std::unique_ptr<SurfaceMTL>( | ||
| new SurfaceMTL(context->weak_from_this(), render_target_desc, resolve_tex, | ||
| drawable, requires_blit, clip_rect)); | ||
| } | ||
|
|
||
| SurfaceMTL::SurfaceMTL(const std::weak_ptr<Context>& context, | ||
| const RenderTarget& target, | ||
| id<MTLDrawable> drawable) | ||
| : Surface(target), context_(context), drawable_(drawable) {} | ||
| std::shared_ptr<Texture> resolve_texture, | ||
| id<CAMetalDrawable> drawable, | ||
| bool requires_blit, | ||
| std::optional<IRect> clip_rect) | ||
| : Surface(target), | ||
| context_(context), | ||
| resolve_texture_(std::move(resolve_texture)), | ||
| drawable_(drawable), | ||
| requires_blit_(requires_blit), | ||
| clip_rect_(clip_rect) {} | ||
|
|
||
| // |Surface| | ||
| SurfaceMTL::~SurfaceMTL() = default; | ||
|
|
||
| bool SurfaceMTL::ShouldPerformPartialRepaint(std::optional<IRect> damage_rect) { | ||
| // compositor_context.cc will conditionally disable partial repaint if the | ||
| // damage region is large. If that happened, then a nullopt damage rect | ||
| // will be provided here. | ||
| if (!damage_rect.has_value()) { | ||
| return false; | ||
| } | ||
| // If the damage rect is 0 in at least one dimension, partial repaint isn't | ||
| // performed as we skip right to present. | ||
| if (damage_rect->size.width <= 0 || damage_rect->size.height <= 0) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| // |Surface| | ||
| bool SurfaceMTL::Present() const { | ||
| if (drawable_ == nil) { | ||
|
|
@@ -135,6 +183,21 @@ | |
| return false; | ||
| } | ||
|
|
||
| if (requires_blit_) { | ||
| auto blit_command_buffer = context->CreateCommandBuffer(); | ||
| if (!blit_command_buffer) { | ||
| return false; | ||
| } | ||
| auto blit_pass = blit_command_buffer->CreateBlitPass(); | ||
| auto current = TextureMTL::Wrapper({}, drawable_.texture); | ||
| blit_pass->AddCopy(resolve_texture_, current, std::nullopt, | ||
| clip_rect_->origin); | ||
| blit_pass->EncodeCommands(context->GetResourceAllocator()); | ||
| if (!blit_command_buffer->SubmitCommands()) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| // If a transaction is present, `presentDrawable` will present too early. And | ||
| // so we wait on an empty command buffer to get scheduled instead, which | ||
| // forces us to also wait for all of the previous command buffers in the queue | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a comment about why this number and how to figure out if changing it is helping or hurting things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.