diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index a7591544fd18a..6bb7f08856245 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -176,6 +176,7 @@ ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/test +../../../flutter/impeller/renderer/blit_pass_unittests.cc ../../../flutter/impeller/renderer/capabilities_unittests.cc ../../../flutter/impeller/renderer/compute_subgroup_unittests.cc ../../../flutter/impeller/renderer/compute_unittests.cc diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 22a42063ac196..a04ecd5e7ab0e 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -122,6 +122,7 @@ impeller_component("renderer_unittests") { testonly = true sources = [ + "blit_pass_unittests.cc", "capabilities_unittests.cc", "device_buffer_unittests.cc", "host_buffer_unittests.cc", diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 374da680afd4e..3c0055e6b9a9b 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -12,6 +12,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/paths.h" #include "flutter/fml/synchronization/sync_switch.h" +#include "impeller/core/formats.h" #include "impeller/core/sampler_descriptor.h" #include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" #include "impeller/renderer/backend/metal/sampler_library_mtl.h" diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index b8644d1dcc788..d9441f6b85127 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -8,6 +8,7 @@ #include "impeller/base/strings.h" #include "impeller/base/validation.h" +#include "impeller/core/formats.h" #include "impeller/core/host_buffer.h" #include "impeller/renderer/blit_command.h" @@ -52,6 +53,16 @@ bool BlitPass::AddCopy(std::shared_ptr source, static_cast(destination->GetTextureDescriptor().sample_count)); return false; } + if (source->GetTextureDescriptor().format != + destination->GetTextureDescriptor().format) { + VALIDATION_LOG << SPrintF( + "The source pixel format (%s) must match the destination pixel format " + "(%s) " + "for blits.", + PixelFormatToString(source->GetTextureDescriptor().format), + PixelFormatToString(destination->GetTextureDescriptor().format)); + return false; + } if (!source_region.has_value()) { source_region = IRect::MakeSize(source->GetSize()); diff --git a/impeller/renderer/blit_pass_unittests.cc b/impeller/renderer/blit_pass_unittests.cc new file mode 100644 index 0000000000000..bffa3babdf974 --- /dev/null +++ b/impeller/renderer/blit_pass_unittests.cc @@ -0,0 +1,77 @@ +// 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 "gtest/gtest.h" +#include "impeller/base/validation.h" +#include "impeller/core/formats.h" +#include "impeller/core/texture_descriptor.h" +#include "impeller/playground/playground_test.h" +#include "impeller/renderer/command_buffer.h" + +namespace impeller { +namespace testing { + +using BlitPassTest = PlaygroundTest; +INSTANTIATE_PLAYGROUND_SUITE(BlitPassTest); + +TEST_P(BlitPassTest, BlitAcrossDifferentPixelFormatsFails) { + ScopedValidationDisable scope; // avoid noise in output. + auto context = GetContext(); + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + + TextureDescriptor src_desc; + src_desc.format = PixelFormat::kA8UNormInt; + src_desc.size = {100, 100}; + auto src = context->GetResourceAllocator()->CreateTexture(src_desc); + + TextureDescriptor dst_format; + dst_format.format = PixelFormat::kR8G8B8A8UNormInt; + dst_format.size = {100, 100}; + auto dst = context->GetResourceAllocator()->CreateTexture(dst_format); + + EXPECT_FALSE(blit_pass->AddCopy(src, dst)); +} + +TEST_P(BlitPassTest, BlitAcrossDifferentSampleCountsFails) { + ScopedValidationDisable scope; // avoid noise in output. + auto context = GetContext(); + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + + TextureDescriptor src_desc; + src_desc.format = PixelFormat::kR8G8B8A8UNormInt; + src_desc.sample_count = SampleCount::kCount4; + src_desc.size = {100, 100}; + auto src = context->GetResourceAllocator()->CreateTexture(src_desc); + + TextureDescriptor dst_format; + dst_format.format = PixelFormat::kR8G8B8A8UNormInt; + dst_format.size = {100, 100}; + auto dst = context->GetResourceAllocator()->CreateTexture(dst_format); + + EXPECT_FALSE(blit_pass->AddCopy(src, dst)); +} + +TEST_P(BlitPassTest, BlitPassesForMatchingFormats) { + ScopedValidationDisable scope; // avoid noise in output. + auto context = GetContext(); + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + + TextureDescriptor src_desc; + src_desc.format = PixelFormat::kR8G8B8A8UNormInt; + src_desc.size = {100, 100}; + auto src = context->GetResourceAllocator()->CreateTexture(src_desc); + + TextureDescriptor dst_format; + dst_format.format = PixelFormat::kR8G8B8A8UNormInt; + dst_format.size = {100, 100}; + auto dst = context->GetResourceAllocator()->CreateTexture(dst_format); + + EXPECT_TRUE(blit_pass->AddCopy(src, dst)); +} + +} // namespace testing +} // namespace impeller diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h index e21d5635dc82e..4f9ddd6bbbdf1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h @@ -5,6 +5,7 @@ #ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_OVERLAY_VIEW_H_ #define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_OVERLAY_VIEW_H_ +#include #include #include @@ -12,6 +13,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/shell.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" +#include "fml/platform/darwin/cf_utils.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" @@ -27,12 +29,12 @@ /// views also handle touch propagation and the like for touches that occurs /// either on overlays or otherwise may be intercepted by the platform views. @interface FlutterOverlayView : UIView - - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; - (instancetype)init NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithContentsScale:(CGFloat)contentsScale; +- (instancetype)initWithContentsScale:(CGFloat)contentsScale + pixelFormat:(MTLPixelFormat)pixelFormat; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm index 490efaf5162a1..db1d13d8d37f8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm @@ -3,6 +3,8 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" +#include +#include #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" @@ -18,7 +20,9 @@ // This is mostly a duplication of FlutterView. // TODO(amirh): once GL support is in evaluate if we can merge this with FlutterView. -@implementation FlutterOverlayView +@implementation FlutterOverlayView { + fml::CFRef _colorSpaceRef; +} - (instancetype)initWithFrame:(CGRect)frame { NSAssert(NO, @"FlutterOverlayView must init or initWithContentsScale"); @@ -42,15 +46,24 @@ - (instancetype)init { return self; } -- (instancetype)initWithContentsScale:(CGFloat)contentsScale { +- (instancetype)initWithContentsScale:(CGFloat)contentsScale + pixelFormat:(MTLPixelFormat)pixelFormat { self = [self init]; if ([self.layer isKindOfClass:NSClassFromString(@"CAMetalLayer")]) { self.layer.allowsGroupOpacity = NO; self.layer.contentsScale = contentsScale; self.layer.rasterizationScale = contentsScale; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + CAMetalLayer* layer = (CAMetalLayer*)self.layer; +#pragma clang diagnostic pop + layer.pixelFormat = pixelFormat; + if (pixelFormat == MTLPixelFormatRGBA16Float) { + self->_colorSpaceRef = fml::CFRef(CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB)); + layer.colorspace = self->_colorSpaceRef; + } } - return self; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 004fe7eb31b38..a9fc569bc1165 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #import #include @@ -14,6 +15,7 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -85,13 +87,15 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( GrDirectContext* gr_context, - const std::shared_ptr& ios_context) { + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { if (available_layer_index_ >= layers_.size()) { std::shared_ptr layer; fml::scoped_nsobject overlay_view; fml::scoped_nsobject overlay_view_wrapper; - if (!gr_context) { + bool impeller_enabled = !!ios_context->GetImpellerContext(); + if (!gr_context && !impeller_enabled) { overlay_view.reset([[FlutterOverlayView alloc] init]); overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); @@ -104,8 +108,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::move(surface)); } else { CGFloat screenScale = [UIScreen mainScreen].scale; - overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale]); + overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); auto ca_layer = fml::scoped_nsobject{[[overlay_view.get() layer] retain]}; std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); @@ -735,13 +741,15 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // on the overlay layer. background_canvas->ClipRect(joined_rect, DlCanvas::ClipOp::kDifference); // Get a new host layer. - std::shared_ptr layer = GetLayer(gr_context, // - ios_context, // - slice, // - joined_rect, // - current_platform_view_id, // - overlay_id // - ); + std::shared_ptr layer = + GetLayer(gr_context, // + ios_context, // + slice, // + joined_rect, // + current_platform_view_id, // + overlay_id, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); did_submit &= layer->did_submit_last_frame; platform_view_layers[current_platform_view_id].push_back(layer); overlay_id++; @@ -811,9 +819,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, EmbedderViewSlice* slice, SkRect rect, int64_t view_id, - int64_t overlay_id) { + int64_t overlay_id, + MTLPixelFormat pixel_format) { FML_DCHECK(flutter_view_); - std::shared_ptr layer = layer_pool_->GetLayer(gr_context, ios_context); + std::shared_ptr layer = + layer_pool_->GetLayer(gr_context, ios_context, pixel_format); UIView* overlay_view_wrapper = layer->overlay_view_wrapper.get(); auto screenScale = [UIScreen mainScreen].scale; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index f3a384a89edbf..e754aec279257 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ +#include #include "flutter/flow/embedded_views.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" @@ -167,9 +168,9 @@ class FlutterPlatformViewLayerPool { // Gets a layer from the pool if available, or allocates a new one. // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. - std::shared_ptr GetLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context); + std::shared_ptr GetLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); // Gets the layers in the pool that aren't currently used. // This method doesn't mark the layers as unused. @@ -320,7 +321,8 @@ class FlutterPlatformViewsController { EmbedderViewSlice* slice, SkRect rect, int64_t view_id, - int64_t overlay_id); + int64_t overlay_id, + MTLPixelFormat pixel_format); // Removes overlay views and platform views that aren't needed in the current frame. // Must run on the platform thread. void RemoveUnusedLayers(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.h b/shell/platform/darwin/ios/framework/Source/FlutterView.h index daebc0df5314c..6337580c6887f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -5,6 +5,7 @@ #ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_VIEW_H_ #define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_VIEW_H_ +#include #import #include @@ -47,6 +48,7 @@ enableWideGamut:(BOOL)isWideGamutEnabled NS_DESIGNATED_INITIALIZER; - (UIScreen*)screen; +- (MTLPixelFormat)pixelFormat; // Set by FlutterEngine or FlutterViewController to override software rendering. @property(class, nonatomic) BOOL forceSoftwareRendering; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index dfb25224e0e2a..d1af6ebf5beb1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" +#include #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" @@ -43,6 +44,17 @@ - (UIScreen*)screen { return UIScreen.mainScreen; } +- (MTLPixelFormat)pixelFormat { + if ([self.layer isKindOfClass:NSClassFromString(@"CAMetalLayer")]) { +// It is a known Apple bug that CAMetalLayer incorrectly reports its supported +// SDKs. It is, in fact, available since iOS 8. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + CAMetalLayer* layer = (CAMetalLayer*)self.layer; + return layer.pixelFormat; + } + return MTLPixelFormatBGRA8Unorm; +} - (BOOL)isWideGamutSupported { if (![_delegate isUsingImpeller]) { return NO;