[Impeller] Update partial repaint to use a fullsize onscreen. (#161626)

The existing technique of offsetting a smaller texture is very vunerable
to bugs in the renderer. Rather than this approach, we can allocate a
new offscreen that is full sized and then blit a smaller region. To
reduce the allocation costs, we can also set up a transients cache which
will reuse this texture. In total, this should be more performant than
the existing partial repaint (due to lack of continual re-allocation) at
the cost of higher peak memory usage.

Fixes https://github.com/flutter/flutter/issues/140877
Fixes https://github.com/flutter/flutter/issues/160588
Fixes https://github.com/flutter/flutter/issues/156113
This commit is contained in:
Jonah Williams 2025-01-16 11:29:36 -08:00 committed by GitHub
parent 5517cc9b3b
commit 90f926edc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 309 additions and 69 deletions

View File

@ -185,6 +185,7 @@
../../../flutter/impeller/renderer/backend/gles/test
../../../flutter/impeller/renderer/backend/gles/unique_handle_gles_unittests.cc
../../../flutter/impeller/renderer/backend/metal/allocator_mtl_unittests.mm
../../../flutter/impeller/renderer/backend/metal/swapchain_transients_mtl_unittests.mm
../../../flutter/impeller/renderer/backend/metal/texture_mtl_unittests.mm
../../../flutter/impeller/renderer/backend/vulkan/allocator_vk_unittests.cc
../../../flutter/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc

View File

@ -42639,6 +42639,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/metal/shader_library_mtl.h +
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/shader_library_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/surface_mtl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/surface_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/swapchain_transients_mtl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/swapchain_transients_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/texture_mtl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/texture_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/texture_wrapper_mtl.h + ../../../flutter/LICENSE
@ -45584,6 +45586,8 @@ FILE: ../../../flutter/impeller/renderer/backend/metal/shader_library_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/shader_library_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/surface_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/surface_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/swapchain_transients_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/swapchain_transients_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/texture_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/texture_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/texture_wrapper_mtl.h

View File

@ -185,10 +185,15 @@ void CompositorContext::ScopedFrame::PaintLayerTreeImpeller(
flutter::LayerTree& layer_tree,
std::optional<DlRect> clip_rect,
bool ignore_raster_cache) {
if (canvas() && clip_rect) {
canvas()->Translate(-clip_rect->GetX(), -clip_rect->GetY());
DlAutoCanvasRestore restore(canvas(), clip_rect.has_value());
if (canvas()) {
if (clip_rect) {
canvas()->ClipRect(clip_rect.value());
}
}
// The canvas()->Restore() is taken care of by the DlAutoCanvasRestore
layer_tree.Paint(*this, ignore_raster_cache);
}

View File

@ -10,6 +10,7 @@
#include "flutter/fml/concurrent_message_loop.h"
#include "flutter/fml/synchronization/sync_switch.h"
#include "impeller/playground/playground_impl.h"
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
namespace impeller {
@ -36,6 +37,7 @@ class PlaygroundImplMTL final : public PlaygroundImpl {
std::unique_ptr<Data> data_;
std::shared_ptr<ContextMTL> context_;
std::shared_ptr<fml::ConcurrentMessageLoop> concurrent_loop_;
std::shared_ptr<SwapchainTransientsMTL> swapchain_transients_;
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch_;
// |PlaygroundImpl|

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "impeller/playground/backend/metal/playground_impl_mtl.h"
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#define GLFW_INCLUDE_NONE
#import "third_party/glfw/include/GLFW/glfw3.h"
@ -98,6 +99,8 @@ PlaygroundImplMTL::PlaygroundImplMTL(PlaygroundSwitches switches)
handle_.reset(window);
context_ = std::move(context);
swapchain_transients_ = std::make_shared<SwapchainTransientsMTL>(
context_->GetResourceAllocator());
}
PlaygroundImplMTL::~PlaygroundImplMTL() = default;
@ -125,7 +128,8 @@ std::unique_ptr<Surface> PlaygroundImplMTL::AcquireSurfaceFrame(
auto drawable =
SurfaceMTL::GetMetalDrawableAndValidate(context, data_->metal_layer);
return SurfaceMTL::MakeFromMetalLayerDrawable(context, drawable);
return SurfaceMTL::MakeFromMetalLayerDrawable(context, drawable,
swapchain_transients_);
}
fml::Status PlaygroundImplMTL::SetCapabilities(

View File

@ -46,6 +46,8 @@ impeller_component("metal") {
"shader_library_mtl.mm",
"surface_mtl.h",
"surface_mtl.mm",
"swapchain_transients_mtl.h",
"swapchain_transients_mtl.mm",
"texture_mtl.h",
"texture_mtl.mm",
"texture_wrapper_mtl.h",
@ -70,6 +72,7 @@ impeller_component("metal_unittests") {
sources = [
"allocator_mtl_unittests.mm",
"swapchain_transients_mtl_unittests.mm",
"texture_mtl_unittests.mm",
]

View File

@ -9,6 +9,7 @@
#include <memory>
#include "impeller/geometry/rect.h"
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/surface.h"
@ -41,11 +42,13 @@ class SurfaceMTL final : public Surface {
static std::unique_ptr<SurfaceMTL> MakeFromMetalLayerDrawable(
const std::shared_ptr<Context>& context,
id<CAMetalDrawable> drawable,
const std::shared_ptr<SwapchainTransientsMTL>& transients,
std::optional<IRect> clip_rect = std::nullopt);
static std::unique_ptr<SurfaceMTL> MakeFromTexture(
const std::shared_ptr<Context>& context,
id<MTLTexture> texture,
const std::shared_ptr<SwapchainTransientsMTL>& transients,
std::optional<IRect> clip_rect,
id<CAMetalDrawable> drawable = nil);
#pragma GCC diagnostic pop

View File

@ -7,9 +7,11 @@
#include "flutter/fml/trace_event.h"
#include "flutter/impeller/renderer/command_buffer.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
#include "impeller/renderer/backend/metal/context_mtl.h"
#include "impeller/renderer/backend/metal/formats_mtl.h"
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#include "impeller/renderer/backend/metal/texture_mtl.h"
#include "impeller/renderer/render_target.h"
@ -47,25 +49,20 @@ id<CAMetalDrawable> SurfaceMTL::GetMetalDrawableAndValidate(
}
static std::optional<RenderTarget> WrapTextureWithRenderTarget(
Allocator& allocator,
const std::shared_ptr<SwapchainTransientsMTL>& transients,
id<MTLTexture> texture,
bool requires_blit,
std::optional<IRect> clip_rect) {
// 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) {
if (!clip_rect.has_value()) {
VALIDATION_LOG << "Missing clip rectangle.";
return std::nullopt;
}
root_size = ISize(clip_rect->GetWidth(), clip_rect->GetHeight());
} else {
root_size = {static_cast<ISize::Type>(texture.width),
static_cast<ISize::Type>(texture.height)};
ISize root_size = {static_cast<ISize::Type>(texture.width),
static_cast<ISize::Type>(texture.height)};
PixelFormat format = FromMTLPixelFormat(texture.pixelFormat);
if (format == PixelFormat::kUnknown) {
VALIDATION_LOG << "Unknown drawable color format.";
return std::nullopt;
}
transients->SetSizeAndFormat(root_size, format);
TextureDescriptor resolve_tex_desc;
resolve_tex_desc.format = FromMTLPixelFormat(texture.pixelFormat);
resolve_tex_desc.size = root_size;
@ -74,65 +71,58 @@ static std::optional<RenderTarget> WrapTextureWithRenderTarget(
resolve_tex_desc.sample_count = SampleCount::kCount1;
resolve_tex_desc.storage_mode = StorageMode::kDevicePrivate;
if (resolve_tex_desc.format == PixelFormat::kUnknown) {
VALIDATION_LOG << "Unknown drawable color format.";
return std::nullopt;
}
// Create color resolve texture.
std::shared_ptr<Texture> resolve_tex;
if (requires_blit) {
resolve_tex_desc.compression_type = CompressionType::kLossy;
resolve_tex = allocator.CreateTexture(resolve_tex_desc);
resolve_tex = transients->GetResolveTexture();
} else {
resolve_tex = TextureMTL::Create(resolve_tex_desc, texture);
}
if (!resolve_tex) {
VALIDATION_LOG << "Could not wrap resolve texture.";
return std::nullopt;
}
resolve_tex->SetLabel("ImpellerOnscreenResolve");
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 = resolve_tex->GetTextureDescriptor().format;
msaa_tex_desc.size = resolve_tex->GetSize();
msaa_tex_desc.usage = TextureUsage::kRenderTarget;
auto msaa_tex = allocator.CreateTexture(msaa_tex_desc);
if (!msaa_tex) {
VALIDATION_LOG << "Could not allocate MSAA color texture.";
return std::nullopt;
}
msaa_tex->SetLabel("ImpellerOnscreenColorMSAA");
ColorAttachment color0;
color0.texture = msaa_tex;
color0.texture = transients->GetMSAATexture();
color0.clear_color = Color::DarkSlateGray();
color0.load_action = LoadAction::kClear;
color0.store_action = StoreAction::kMultisampleResolve;
color0.resolve_texture = std::move(resolve_tex);
auto render_target_desc = std::make_optional<RenderTarget>();
render_target_desc->SetColorAttachment(color0, 0u);
DepthAttachment depth0;
depth0.load_action =
RenderTarget::kDefaultStencilAttachmentConfig.load_action;
depth0.store_action =
RenderTarget::kDefaultStencilAttachmentConfig.store_action;
depth0.clear_depth = 0u;
depth0.texture = transients->GetDepthStencilTexture();
return render_target_desc;
StencilAttachment stencil0;
stencil0.load_action =
RenderTarget::kDefaultStencilAttachmentConfig.load_action;
stencil0.store_action =
RenderTarget::kDefaultStencilAttachmentConfig.store_action;
stencil0.clear_stencil = 0u;
stencil0.texture = transients->GetDepthStencilTexture();
RenderTarget render_target;
render_target.SetColorAttachment(color0, 0u);
render_target.SetDepthAttachment(std::move(depth0));
render_target.SetStencilAttachment(std::move(stencil0));
return render_target;
}
std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromMetalLayerDrawable(
const std::shared_ptr<Context>& context,
id<CAMetalDrawable> drawable,
const std::shared_ptr<SwapchainTransientsMTL>& transients,
std::optional<IRect> clip_rect) {
return SurfaceMTL::MakeFromTexture(context, drawable.texture, clip_rect,
drawable);
return SurfaceMTL::MakeFromTexture(context, drawable.texture, transients,
clip_rect, drawable);
}
std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromTexture(
const std::shared_ptr<Context>& context,
id<MTLTexture> texture,
const std::shared_ptr<SwapchainTransientsMTL>& transients,
std::optional<IRect> clip_rect,
id<CAMetalDrawable> drawable) {
bool partial_repaint_blit_required = ShouldPerformPartialRepaint(clip_rect);
@ -140,9 +130,8 @@ std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromTexture(
// The returned render target is the texture that Impeller will render the
// root pass to. If partial repaint is in use, this may be a new texture which
// is smaller than the given MTLTexture.
auto render_target =
WrapTextureWithRenderTarget(*context->GetResourceAllocator(), texture,
partial_repaint_blit_required, clip_rect);
auto render_target = WrapTextureWithRenderTarget(
transients, texture, partial_repaint_blit_required, clip_rect);
if (!render_target) {
return nullptr;
}
@ -251,7 +240,7 @@ bool SurfaceMTL::PreparePresent() const {
VALIDATION_LOG << "Missing clip rectangle.";
return false;
}
blit_pass->AddCopy(source_texture_, destination_texture_, std::nullopt,
blit_pass->AddCopy(source_texture_, destination_texture_, clip_rect_,
clip_rect_->GetOrigin());
blit_pass->EncodeCommands();
if (!context->GetCommandQueue()->Submit({blit_command_buffer}).ok()) {

View File

@ -0,0 +1,56 @@
// 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.
#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_METAL_SWAPCHAIN_TRANSIENTS_MTL_H_
#define FLUTTER_IMPELLER_RENDERER_BACKEND_METAL_SWAPCHAIN_TRANSIENTS_MTL_H_
#include <memory>
#include "impeller/core/formats.h"
#include "impeller/geometry/rect.h"
#include "impeller/geometry/size.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/surface.h"
namespace impeller {
/// @brief A cache for the onscreen texture attachments used in surface_mtl.
///
/// Typically the onscreen resolve texture is created from a Metal drawable and
/// this cache is only used for the MSAA texture and the depth+stencil
/// attachment. When partial repaint is active, this class also provides a cache
/// for an offscreen resolve texture that is blitted to the real onscreen during
/// present.
class SwapchainTransientsMTL {
public:
explicit SwapchainTransientsMTL(const std::shared_ptr<Allocator>& allocator);
~SwapchainTransientsMTL();
/// @brief Update the size and pixel format of the onscreens.
///
/// Note: this will invalidate any cached textures if either property changes.
void SetSizeAndFormat(ISize size, PixelFormat format);
/// @brief Retrieve the resolve texture, creating one if needed.
std::shared_ptr<Texture> GetResolveTexture();
/// @brief Retrieve the MSAA texture, creating one if needed.
std::shared_ptr<Texture> GetMSAATexture();
/// @brief Retrieve the depth+stencil texture, creating one if needed.
std::shared_ptr<Texture> GetDepthStencilTexture();
private:
std::shared_ptr<Allocator> allocator_;
ISize size_ = {0, 0};
PixelFormat format_ = PixelFormat::kUnknown;
std::shared_ptr<Texture> resolve_tex_;
std::shared_ptr<Texture> msaa_tex_;
std::shared_ptr<Texture> depth_stencil_tex_;
};
} // namespace impeller
#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_METAL_SWAPCHAIN_TRANSIENTS_MTL_H_

View File

@ -0,0 +1,92 @@
// 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/renderer/backend/metal/swapchain_transients_mtl.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
namespace impeller {
SwapchainTransientsMTL::SwapchainTransientsMTL(
const std::shared_ptr<Allocator>& allocator)
: allocator_(allocator) {}
SwapchainTransientsMTL::~SwapchainTransientsMTL() = default;
void SwapchainTransientsMTL::SetSizeAndFormat(ISize size, PixelFormat format) {
if (size != size_ || format != format_) {
resolve_tex_ = nullptr;
msaa_tex_ = nullptr;
depth_stencil_tex_ = nullptr;
}
size_ = size;
format_ = format;
}
std::shared_ptr<Texture> SwapchainTransientsMTL::GetResolveTexture() {
if (!resolve_tex_) {
TextureDescriptor desc;
desc.size = size_;
desc.sample_count = SampleCount::kCount1;
desc.format = format_;
desc.storage_mode = StorageMode::kDevicePrivate;
desc.usage = TextureUsage::kShaderRead | TextureUsage::kRenderTarget;
desc.compression_type = CompressionType::kLossy;
desc.type = TextureType::kTexture2D;
resolve_tex_ = allocator_->CreateTexture(desc);
if (!resolve_tex_) {
VALIDATION_LOG << "Failed to allocate resolve texture.";
return nullptr;
}
resolve_tex_->SetLabel("ImpellerOnscreenResolve");
}
return resolve_tex_;
}
std::shared_ptr<Texture> SwapchainTransientsMTL::GetMSAATexture() {
if (!msaa_tex_) {
TextureDescriptor desc;
desc.size = size_;
desc.sample_count = SampleCount::kCount4;
desc.format = format_;
desc.storage_mode = StorageMode::kDeviceTransient;
desc.usage = TextureUsage::kRenderTarget;
desc.type = TextureType::kTexture2DMultisample;
msaa_tex_ = allocator_->CreateTexture(desc);
if (!msaa_tex_) {
VALIDATION_LOG << "Failed to allocate MSAA texture.";
return nullptr;
}
msaa_tex_->SetLabel("ImpellerOnscreenMSAA");
}
return msaa_tex_;
}
std::shared_ptr<Texture> SwapchainTransientsMTL::GetDepthStencilTexture() {
if (!depth_stencil_tex_) {
TextureDescriptor desc;
desc.size = size_;
desc.sample_count = SampleCount::kCount4;
desc.format = PixelFormat::kD32FloatS8UInt;
desc.storage_mode = StorageMode::kDeviceTransient;
desc.usage = TextureUsage::kRenderTarget;
desc.type = TextureType::kTexture2DMultisample;
depth_stencil_tex_ = allocator_->CreateTexture(desc);
if (!depth_stencil_tex_) {
VALIDATION_LOG << "Failed to allocate depth-stencil texture.";
return nullptr;
}
depth_stencil_tex_->SetLabel("ImpellerOnscreenDepth+Stencil");
}
return depth_stencil_tex_;
}
} // namespace impeller

View File

@ -0,0 +1,79 @@
// 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 "flutter/testing/testing.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
#include "impeller/playground/playground_test.h"
#include "impeller/renderer/backend/metal/allocator_mtl.h"
#include "impeller/renderer/backend/metal/context_mtl.h"
#include "impeller/renderer/backend/metal/formats_mtl.h"
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#include "impeller/renderer/backend/metal/texture_mtl.h"
#include "impeller/renderer/capabilities.h"
#include <memory>
#include <thread>
#include "gtest/gtest.h"
namespace impeller {
namespace testing {
using SwapchainTransientsMTLTest = PlaygroundTest;
INSTANTIATE_METAL_PLAYGROUND_SUITE(SwapchainTransientsMTLTest);
TEST_P(SwapchainTransientsMTLTest, CanAllocateSwapchainTextures) {
const auto& transients = std::make_shared<SwapchainTransientsMTL>(
GetContext()->GetResourceAllocator());
transients->SetSizeAndFormat({1, 1}, PixelFormat::kB8G8R8A8UNormInt);
auto resolve = transients->GetResolveTexture();
EXPECT_NE(resolve, nullptr);
EXPECT_NE(transients->GetMSAATexture(), nullptr);
EXPECT_NE(transients->GetDepthStencilTexture(), nullptr);
// Texture properties are correct for resolve.
EXPECT_EQ(resolve->GetTextureDescriptor().size, ISize(1, 1));
EXPECT_EQ(resolve->GetTextureDescriptor().format,
PixelFormat::kB8G8R8A8UNormInt);
EXPECT_EQ(resolve->GetTextureDescriptor().sample_count, SampleCount::kCount1);
EXPECT_EQ(resolve->GetTextureDescriptor().storage_mode,
StorageMode::kDevicePrivate);
// Texture properties are correct for MSAA.
auto msaa = transients->GetMSAATexture();
EXPECT_EQ(msaa->GetTextureDescriptor().size, ISize(1, 1));
EXPECT_EQ(msaa->GetTextureDescriptor().format,
PixelFormat::kB8G8R8A8UNormInt);
EXPECT_EQ(msaa->GetTextureDescriptor().sample_count, SampleCount::kCount4);
EXPECT_EQ(msaa->GetTextureDescriptor().storage_mode,
StorageMode::kDeviceTransient);
// Texture properties are correct for Depth+Stencil.
auto depth_stencil = transients->GetDepthStencilTexture();
EXPECT_EQ(depth_stencil->GetTextureDescriptor().size, ISize(1, 1));
EXPECT_EQ(depth_stencil->GetTextureDescriptor().format,
PixelFormat::kD32FloatS8UInt);
EXPECT_EQ(depth_stencil->GetTextureDescriptor().sample_count,
SampleCount::kCount4);
EXPECT_EQ(depth_stencil->GetTextureDescriptor().storage_mode,
StorageMode::kDeviceTransient);
// Textures are cached.
EXPECT_EQ(transients->GetResolveTexture(), resolve);
// Texture cache is invalidated when size changes.
transients->SetSizeAndFormat({2, 2}, PixelFormat::kB8G8R8A8UNormInt);
EXPECT_NE(resolve, transients->GetResolveTexture());
resolve = transients->GetResolveTexture();
// Texture cache is invalidated when pixel format changes.
transients->SetSizeAndFormat({2, 2}, PixelFormat::kB10G10R10A10XR);
EXPECT_NE(resolve, transients->GetResolveTexture());
}
} // namespace testing
} // namespace impeller

View File

@ -68,13 +68,6 @@ bool BlitPass::AddCopy(std::shared_ptr<Texture> source,
return true; // Nothing to blit.
}
// Clip the destination image.
source_region = source_region->Intersection(
IRect::MakeOriginSize(-destination_origin, destination->GetSize()));
if (!source_region.has_value()) {
return true; // Nothing to blit.
}
return OnCopyTextureToTextureCommand(
std::move(source), std::move(destination), source_region.value(),
destination_origin, label);

View File

@ -11,6 +11,7 @@
#include "flutter/fml/macros.h"
#include "flutter/impeller/display_list/aiks_context.h"
#include "flutter/impeller/renderer/backend/metal/context_mtl.h"
#include "flutter/impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
#include "third_party/skia/include/gpu/ganesh/mtl/GrMtlTypes.h"
@ -46,6 +47,7 @@ class IMPELLER_CA_METAL_LAYER_AVAILABLE GPUSurfaceMetalImpeller
// MTLTexture for each drawable
std::shared_ptr<std::map<void*, SkIRect>> damage_ =
std::make_shared<std::map<void*, SkIRect>>();
std::shared_ptr<impeller::SwapchainTransientsMTL> swapchain_transients_;
// |Surface|
std::unique_ptr<SurfaceFrame> AcquireFrame(

View File

@ -9,6 +9,7 @@
#include "flow/surface.h"
#include "flow/surface_frame.h"
#include "impeller/display_list/aiks_context.h"
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#include "flutter/common/settings.h"
#include "flutter/fml/make_copyable.h"
@ -36,6 +37,10 @@ GPUSurfaceMetalImpeller::GPUSurfaceMetalImpeller(
if (disablePartialRepaint != nil) {
disable_partial_repaint_ = disablePartialRepaint.boolValue;
}
if (aiks_context_) {
swapchain_transients_ = std::make_shared<impeller::SwapchainTransientsMTL>(
aiks_context_->GetContext()->GetResourceAllocator());
}
}
GPUSurfaceMetalImpeller::~GPUSurfaceMetalImpeller() = default;
@ -107,7 +112,8 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
aiks_context = aiks_context_, //
drawable, //
weak_last_texture, //
weak_layer //
weak_layer, //
swapchain_transients = swapchain_transients_ //
](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
id<MTLTexture> strong_last_texture = weak_last_texture;
CAMetalLayer* strong_layer = weak_layer;
@ -146,8 +152,8 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
buffer_damage->width(), buffer_damage->height());
}
auto surface = impeller::SurfaceMTL::MakeFromMetalLayerDrawable(aiks_context->GetContext(),
drawable, clip_rect);
auto surface = impeller::SurfaceMTL::MakeFromMetalLayerDrawable(
aiks_context->GetContext(), drawable, swapchain_transients, clip_rect);
// The surface may be null if we failed to allocate the onscreen render target
// due to running out of memory.
@ -232,8 +238,9 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur
SurfaceFrame::EncodeCallback encode_callback =
fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
damage = damage_,
aiks_context = aiks_context_, //
weak_texture //
aiks_context = aiks_context_, //
weak_texture, //
swapchain_transients = swapchain_transients_ //
](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
id<MTLTexture> strong_texture = weak_texture;
if (!strong_texture) {
@ -269,8 +276,8 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur
buffer_damage->width(), buffer_damage->height());
}
auto surface = impeller::SurfaceMTL::MakeFromTexture(aiks_context->GetContext(),
strong_texture, clip_rect);
auto surface = impeller::SurfaceMTL::MakeFromTexture(
aiks_context->GetContext(), strong_texture, swapchain_transients, clip_rect);
surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction);