diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index cb3900e392c1f..0e94d9d4d9cec 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/mtl/GrMtlTypes.h" @@ -29,6 +30,9 @@ typedef const void* GPUMTLTextureHandle; struct GPUMTLTextureInfo { int64_t texture_id; GPUMTLTextureHandle texture; + bool partial_repaint_enabled; + SkIRect texture_damage; + SkIRect frame_damage; }; enum class MTLRenderTargetType { kMTLTexture, kCAMetalLayer }; diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index 9ebe7f17c8ed2..7803288a75356 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -209,8 +209,8 @@ return nullptr; } - auto submit_callback = [texture = texture, delegate = delegate_]( - const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool { + auto submit_callback = [texture, delegate = delegate_](const SurfaceFrame& surface_frame, + SkCanvas* canvas) -> bool { TRACE_EVENT0("flutter", "GPUSurfaceMetal::PresentTexture"); if (canvas == nullptr) { FML_DLOG(ERROR) << "Canvas not available."; @@ -222,12 +222,25 @@ canvas->flush(); } - return delegate->PresentTexture(texture); + GPUMTLTextureInfo present_texture = { + .texture_id = texture.texture_id, + .texture = texture.texture, + .partial_repaint_enabled = texture.partial_repaint_enabled, + .texture_damage = *surface_frame.submit_info().buffer_damage, + .frame_damage = *surface_frame.submit_info().frame_damage, + }; + + return delegate->PresentTexture(present_texture); }; SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; + if (texture.partial_repaint_enabled) { + framebuffer_info.supports_partial_repaint = true; + framebuffer_info.existing_damage = texture.texture_damage; + } + return std::make_unique(std::move(surface), std::move(framebuffer_info), submit_callback); } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 97fced2ab3001..f907c15e7b1e3 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -228,7 +228,7 @@ static void* DefaultGLProcResolver(const char* name) { } #endif // FML_OS_LINUX || FML_OS_WIN -#ifdef SHELL_ENABLE_GL +#if defined(SHELL_ENABLE_GL) || defined(SHELL_ENABLE_METAL) // Auxiliary function used to translate rectangles of type SkIRect to // FlutterRect. static FlutterRect SkIRectToFlutterRect(const SkIRect sk_rect) { @@ -465,10 +465,31 @@ InferMetalPlatformViewCreationCallback( std::function metal_present = [ptr = config->metal.present_drawable_callback, user_data](flutter::GPUMTLTextureInfo texture) { + const size_t num_rects = 1; + + std::array texture_damage_rect = { + SkIRectToFlutterRect(texture.texture_damage)}; + std::array frame_damage_rect = { + SkIRectToFlutterRect(texture.frame_damage)}; + + FlutterDamage texture_damage{ + .struct_size = sizeof(FlutterDamage), + .num_rects = texture_damage_rect.size(), + .damage = texture_damage_rect.data(), + }; + + FlutterDamage frame_damage{ + .struct_size = sizeof(FlutterDamage), + .num_rects = frame_damage_rect.size(), + .damage = frame_damage_rect.data(), + }; + FlutterMetalTexture embedder_texture; embedder_texture.struct_size = sizeof(FlutterMetalTexture); embedder_texture.texture = texture.texture; embedder_texture.texture_id = texture.texture_id; + embedder_texture.texture_damage = texture_damage; + embedder_texture.frame_damage = frame_damage; return ptr(user_data, &embedder_texture); }; auto metal_get_texture = @@ -483,6 +504,24 @@ InferMetalPlatformViewCreationCallback( FlutterMetalTexture metal_texture = ptr(user_data, &frame_info); texture_info.texture_id = metal_texture.texture_id; texture_info.texture = metal_texture.texture; + texture_info.partial_repaint_enabled = true; + + // Verify that at least one damage rectangle was provided. + if (metal_texture.texture_damage.num_rects <= 0 || + metal_texture.texture_damage.damage == nullptr) { + FML_LOG(INFO) << "No damage was provided. Forcing full repaint"; + texture_info.partial_repaint_enabled = false; + } else if (metal_texture.texture_damage.num_rects > 1) { + // Log message notifying users that multi-damage is not yet available in + // case they try to make use of it. + FML_LOG(INFO) << "Damage with multiple rectangles not yet supported. " + "Repainting the whole frame."; + texture_info.partial_repaint_enabled = false; + } else { + texture_info.texture_damage = + FlutterRectToSkIRect(*(metal_texture.texture_damage.damage)); + } + return texture_info; }; diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 93e92b7d4c6fa..de9ab7b9aeca4 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -631,6 +631,13 @@ typedef struct { /// The callback invoked by the engine when it no longer needs this backing /// store. VoidCallback destruction_callback; + // Upon request, it represents the existing damage to the present texture. At + // present time, it represents the regions that need to be rendered. + FlutterDamage texture_damage; + // This field is used at present time to represent the regions that have + // changed between this frame and the previous one. This is important so that + // the user can keep track of texture's existing damage. + FlutterDamage frame_damage; } FlutterMetalTexture; /// Callback for when a metal texture is requested. @@ -653,10 +660,16 @@ typedef struct { /// Alias for id. FlutterMetalCommandQueueHandle present_command_queue; /// The callback that gets invoked when the engine requests the embedder for a - /// texture to render to. + /// texture to render to. If using partial repaint, the user must define this + /// callback to fill in the value of texture_damage of the texture being + /// returned with the texture's existing damage. If no texture_damage is given + /// the Embedder will do a full repaint. FlutterMetalTextureCallback get_next_drawable_callback; /// The callback presented to the embedder to present a fully populated metal - /// texture to the user. + /// texture to the user. If using partial repaint, the user must use the + /// texture and frame damages passed through the given FlutterMetalTexture to + /// render only the damage areas of the screen and to keep track of a damage + /// history. FlutterMetalPresentCallback present_drawable_callback; /// When the embedder specifies that a texture has a frame available, the /// engine will call this method (on an internal engine managed thread) so