Skip to content

Commit

Permalink
[macOS] Merge FlutterSurfaceManager and impls
Browse files Browse the repository at this point in the history
Previously, FlutterSurfaceManager was a protocol with two concrete
implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager.
Most of the implementation was in a shared superclass,
FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific
subclass when backend-specific operations (such as allocating textures)
was required. It did so via a delegate pattern, wherein the subclasses
both implemented the FlutterIOSurfaceManagerDelegate protocol that
exposed the backend-specific functionality.

Now that only the Metal implementation remains, the delegate code can be
inlined into the calling functions, and the class hierarchy can be
squashed into a single concrete implementation class,
FlutterSurfaceManager, similar to how it was originally implemented in
flutter#21525 before we had two backends.

Issue: flutter/flutter#108304
Issue: flutter/flutter#114445
  • Loading branch information
cbracken committed Nov 17, 2022
1 parent 7a5bc91 commit d0522a2
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 133 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2641,6 +2641,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResiz
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManagerTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@ executable("flutter_desktop_darwin_unittests") {
"framework/Source/FlutterKeyboardManagerUnittests.mm",
"framework/Source/FlutterMenuPluginTest.mm",
"framework/Source/FlutterMetalRendererTest.mm",
"framework/Source/FlutterMetalSurfaceManagerTest.mm",
"framework/Source/FlutterPlatformNodeDelegateMacTest.mm",
"framework/Source/FlutterPlatformViewControllerTest.mm",
"framework/Source/FlutterSurfaceManagerTest.mm",
"framework/Source/FlutterTextInputPluginTest.mm",
"framework/Source/FlutterTextInputSemanticsObjectTest.mm",
"framework/Source/FlutterViewControllerTest.mm",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@implementation FlutterMetalResizableBackingStoreProvider {
id<MTLDevice> _device;
id<MTLCommandQueue> _commandQueue;
id<FlutterSurfaceManager> _surfaceManager;
FlutterSurfaceManager* _surfaceManager;
}

- (instancetype)initWithDevice:(id<MTLDevice>)device
Expand All @@ -21,9 +21,9 @@ - (instancetype)initWithDevice:(id<MTLDevice>)device
if (self) {
_device = device;
_commandQueue = commandQueue;
_surfaceManager = [[FlutterMetalSurfaceManager alloc] initWithDevice:device
commandQueue:commandQueue
layer:layer];
_surfaceManager = [[FlutterSurfaceManager alloc] initWithDevice:device
commandQueue:commandQueue
layer:layer];
}
return self;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,36 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"

/**
* Manages the render surfaces and their corresponding backing stores.
*/
@protocol FlutterSurfaceManager

/**
* Updates the backing store size of the managed IOSurfaces to `size`. If the surfaces are already
* of the same size, this is a no-op.
*/
- (void)ensureSurfaceSize:(CGSize)size;

/**
* Swaps the front and the back buffer.
*/
- (void)swapBuffers;

/**
* Returns the backing store for the back buffer.
*/
- (nonnull FlutterRenderBackingStore*)renderBuffer;

@end

/**
* Methods for managing the IOSurfaces held by FlutterIOSurfaceManager.
*/
@protocol FlutterIOSurfaceManagerDelegate

/**
* Tells the delegate that the front and back IOSurfaces are swapped.
*/
- (void)onSwapBuffers;

/**
* Tells the delegate that the IOSurfaces have been resized. `bufferIndex` is to indicate the front
* vs back buffer. `size` is the new size of the IOSurface.
*/
- (void)onUpdateSurface:(nonnull FlutterIOSurfaceHolder*)surface
bufferIndex:(size_t)index
size:(CGSize)size;

/**
* Tells the delegate that IOSurface with given index has been released. Delegate should free
* all resources associated with the surface
*/
- (void)onSurfaceReleased:(size_t)index;

@end

/**
* Manages IOSurfaces for the FlutterEngine to render to.
* Manages render surfaces and corresponding backing stores used by the engine.
*
* The backing store when rendering with on Metal its a Metal texture. There are two IOSurfaces
* The backing store when rendering with on Metal is a Metal texture. There are two IOSurfaces
* created during initialization, FlutterSurfaceManager manages the lifecycle of these.
*/
@interface FlutterIOSurfaceManager : NSObject <FlutterSurfaceManager>

/**
* The object that acts as the delegate for the FlutterIOSurfaceManager. See:
* FlutterIOSurfaceManagerDelegate.
*/
@property(nullable, nonatomic, weak) id<FlutterIOSurfaceManagerDelegate> delegate;
@interface FlutterSurfaceManager : NSObject

/**
* Initializes and returns an IOSurface manager that renders to a child layer (referred to as the
* Initializes and returns a surface manager that renders to a child layer (referred to as the
* content layer) of the containing layer and applies the transform to the contents of the content
* layer.
*/
- (nullable instancetype)initWithLayer:(nonnull CALayer*)containingLayer
contentTransform:(CATransform3D)transform;
- (nullable instancetype)initWithDevice:(nonnull id<MTLDevice>)device
commandQueue:(nonnull id<MTLCommandQueue>)commandQueue
layer:(nonnull CALayer*)containingLayer;

@end
/**
* Updates the backing store size of the managed IOSurfaces the specified size. If the surfaces are
* already of this size, this is a no-op.
*/
- (void)ensureSurfaceSize:(CGSize)size;

/**
* FlutterSurfaceManager implementation where the IOSurfaces managed are backed by a Metal textures.
* Swaps the front and the back buffer.
*/
@interface FlutterMetalSurfaceManager : FlutterIOSurfaceManager <FlutterIOSurfaceManagerDelegate>
- (void)swapBuffers;

/**
* Creates two IOSurfaces backed by Metal textures.
* Returns the backing store for the back buffer.
*/
- (nullable instancetype)initWithDevice:(nonnull id<MTLDevice>)device
commandQueue:(nonnull id<MTLCommandQueue>)commandQueue
layer:(nonnull CALayer*)containingLayer;
- (nonnull FlutterRenderBackingStore*)renderBuffer;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,49 @@
// BackBuffer will be released after kIdleDelay if there is no activity.
static const double kIdleDelay = 1.0;

@implementation FlutterIOSurfaceManager {
@interface FlutterSurfaceManager ()

/**
* Cancels any previously-scheduled onIdle requests.
*/
- (void)cancelIdle;

/**
* Creates a backing textures for the specified surface with the specified size.
*/
- (id<MTLTexture>)createTextureForSurface:(FlutterIOSurfaceHolder*)surface size:(CGSize)size;

@end

@implementation FlutterSurfaceManager {
CALayer* _containingLayer; // provided (parent layer)
CALayer* _contentLayer;
CATransform3D _contentTransform;

CGSize _surfaceSize;
FlutterIOSurfaceHolder* _ioSurfaces[kFlutterSurfaceManagerBufferCount];
BOOL _frameInProgress;

id<MTLDevice> _device;
id<MTLCommandQueue> _commandQueue;
id<MTLTexture> _textures[kFlutterSurfaceManagerBufferCount];
}

- (instancetype)initWithLayer:(CALayer*)containingLayer contentTransform:(CATransform3D)transform {
- (nullable instancetype)initWithDevice:(nonnull id<MTLDevice>)device
commandQueue:(nonnull id<MTLCommandQueue>)commandQueue
layer:(nonnull CALayer*)containingLayer {
self = [super init];
if (self) {
_containingLayer = containingLayer;
_contentTransform = transform;
_contentTransform = CATransform3DIdentity;
_contentLayer = [[CALayer alloc] init];
[_containingLayer addSublayer:_contentLayer];

_ioSurfaces[0] = [[FlutterIOSurfaceHolder alloc] init];
_ioSurfaces[1] = [[FlutterIOSurfaceHolder alloc] init];

_device = device;
_commandQueue = commandQueue;
}
return self;
}
Expand All @@ -51,7 +74,7 @@ - (void)ensureSurfaceSize:(CGSize)size {
for (int i = 0; i < kFlutterSurfaceManagerBufferCount; ++i) {
if (_ioSurfaces[i] != nil) {
[_ioSurfaces[i] recreateIOSurfaceWithSize:size];
[_delegate onUpdateSurface:_ioSurfaces[i] bufferIndex:i size:size];
_textures[i] = [self createTextureForSurface:_ioSurfaces[i] size:size];
}
}
}
Expand All @@ -71,7 +94,8 @@ - (void)swapBuffers {

std::swap(_ioSurfaces[kFlutterSurfaceManagerBackBuffer],
_ioSurfaces[kFlutterSurfaceManagerFrontBuffer]);
[_delegate onSwapBuffers];
std::swap(_textures[kFlutterSurfaceManagerBackBuffer],
_textures[kFlutterSurfaceManagerFrontBuffer]);

// performSelector:withObject:afterDelay needs to be performed on RunLoop thread
[self performSelectorOnMainThread:@selector(reschedule) withObject:nil waitUntilDone:NO];
Expand All @@ -92,7 +116,7 @@ - (void)onIdle {
// Release the back buffer and notify delegate. The buffer will be restored
// on demand in ensureBackBuffer
_ioSurfaces[kFlutterSurfaceManagerBackBuffer] = nil;
[self.delegate onSurfaceReleased:kFlutterSurfaceManagerBackBuffer];
_textures[kFlutterSurfaceManagerBackBuffer] = nil;
}
}
}
Expand All @@ -104,9 +128,9 @@ - (void)ensureBackBuffer {
// Restore previously released backbuffer
_ioSurfaces[kFlutterSurfaceManagerBackBuffer] = [[FlutterIOSurfaceHolder alloc] init];
[_ioSurfaces[kFlutterSurfaceManagerBackBuffer] recreateIOSurfaceWithSize:_surfaceSize];
[_delegate onUpdateSurface:_ioSurfaces[kFlutterSurfaceManagerBackBuffer]
bufferIndex:kFlutterSurfaceManagerBackBuffer
size:_surfaceSize];
_textures[kFlutterSurfaceManagerBackBuffer] =
[self createTextureForSurface:_ioSurfaces[kFlutterSurfaceManagerBackBuffer]
size:_surfaceSize];
}
};
[self performSelectorOnMainThread:@selector(cancelIdle) withObject:nil waitUntilDone:NO];
Expand All @@ -117,47 +141,12 @@ - (void)cancelIdle {
}

- (nonnull FlutterRenderBackingStore*)renderBuffer {
@throw([NSException exceptionWithName:@"Sub-classes FlutterIOSurfaceManager of"
" must override renderBuffer."
reason:nil
userInfo:nil]);
}

@end

@implementation FlutterMetalSurfaceManager {
id<MTLDevice> _device;
id<MTLCommandQueue> _commandQueue;

id<MTLTexture> _textures[kFlutterSurfaceManagerBufferCount];
}

- (nullable instancetype)initWithDevice:(nonnull id<MTLDevice>)device
commandQueue:(nonnull id<MTLCommandQueue>)commandQueue
layer:(nonnull CALayer*)containingLayer {
self = [super initWithLayer:containingLayer contentTransform:CATransform3DIdentity];
if (self) {
super.delegate = self;
_device = device;
_commandQueue = commandQueue;
}
return self;
}

- (FlutterRenderBackingStore*)renderBuffer {
[self ensureBackBuffer];
id<MTLTexture> texture = _textures[kFlutterSurfaceManagerBackBuffer];
return [[FlutterMetalRenderBackingStore alloc] initWithTexture:texture];
}

- (void)onSwapBuffers {
std::swap(_textures[kFlutterSurfaceManagerBackBuffer],
_textures[kFlutterSurfaceManagerFrontBuffer]);
}

- (void)onUpdateSurface:(FlutterIOSurfaceHolder*)surface
bufferIndex:(size_t)index
size:(CGSize)size {
- (id<MTLTexture>)createTextureForSurface:(FlutterIOSurfaceHolder*)surface size:(CGSize)size {
MTLTextureDescriptor* textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:size.width
Expand All @@ -166,13 +155,7 @@ - (void)onUpdateSurface:(FlutterIOSurfaceHolder*)surface
textureDescriptor.usage =
MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget | MTLTextureUsageShaderWrite;
// plane = 0 for BGRA.
_textures[index] = [_device newTextureWithDescriptor:textureDescriptor
iosurface:[surface ioSurface]
plane:0];
}

- (void)onSurfaceReleased:(size_t)index {
_textures[index] = nil;
return [_device newTextureWithDescriptor:textureDescriptor iosurface:[surface ioSurface] plane:0];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ - (instancetype)init {

namespace flutter::testing {

static FlutterMetalSurfaceManager* CreateSurfaceManager() {
static FlutterSurfaceManager* CreateSurfaceManager() {
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
TestMetalView* metalView = [[TestMetalView alloc] init];
CALayer* layer = reinterpret_cast<CALayer*>(metalView.layer);
return [[FlutterMetalSurfaceManager alloc] initWithDevice:device
commandQueue:commandQueue
layer:layer];
return [[FlutterSurfaceManager alloc] initWithDevice:device
commandQueue:commandQueue
layer:layer];
}

TEST(FlutterMetalSurfaceManager, EnsureSizeUpdatesSize) {
FlutterMetalSurfaceManager* surfaceManager = CreateSurfaceManager();
TEST(FlutterSurfaceManager, EnsureSizeUpdatesSize) {
FlutterSurfaceManager* surfaceManager = CreateSurfaceManager();
CGSize size = CGSizeMake(100, 50);
[surfaceManager ensureSurfaceSize:size];
id<MTLTexture> texture =
Expand All @@ -49,8 +49,8 @@ - (instancetype)init {
ASSERT_TRUE(CGSizeEqualToSize(size, textureSize));
}

TEST(FlutterMetalSurfaceManager, EnsureSizeUpdatesSizeForBackBuffer) {
FlutterMetalSurfaceManager* surfaceManager = CreateSurfaceManager();
TEST(FlutterSurfaceManager, EnsureSizeUpdatesSizeForBackBuffer) {
FlutterSurfaceManager* surfaceManager = CreateSurfaceManager();
CGSize size = CGSizeMake(100, 50);
[surfaceManager ensureSurfaceSize:size];
[surfaceManager renderBuffer]; // make sure we have back buffer
Expand Down

0 comments on commit d0522a2

Please sign in to comment.