Add support for compositing using Metal on macOS (flutter/engine#26302)

This commit is contained in:
George Wright 2021-06-01 19:49:47 -07:00 committed by GitHub
parent 11c1a8cafb
commit a890879ba2
15 changed files with 357 additions and 118 deletions

View File

@ -1174,6 +1174,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeybo
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMacOSExternalTexture.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRendererTest.mm

View File

@ -178,6 +178,7 @@ executable("flutter_desktop_darwin_unittests") {
"framework/Source/FlutterEngineTest.mm",
"framework/Source/FlutterGLCompositorUnittests.mm",
"framework/Source/FlutterKeyboardManagerUnittests.mm",
"framework/Source/FlutterMetalCompositorUnittests.mm",
"framework/Source/FlutterMetalRendererTest.mm",
"framework/Source/FlutterMetalSurfaceManagerTest.mm",
"framework/Source/FlutterOpenGLRendererTest.mm",

View File

@ -14,14 +14,8 @@
*/
@interface FlutterBackingStoreData : NSObject
- (nullable instancetype)initWithLayerId:(size_t)layerId
fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider
ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder;
/**
* The layer's key value in FlutterGLCompositor's ca_layer_map_.
*/
@property(nonatomic, readonly) size_t layerId;
- (nullable instancetype)initWithFbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider
ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder;
/**
* Provides the fbo for rendering the layer.

View File

@ -11,11 +11,9 @@
@implementation FlutterBackingStoreData
- (nullable instancetype)initWithLayerId:(size_t)layerId
fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider
ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder {
- (nullable instancetype)initWithFbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider
ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder {
if (self = [super init]) {
_layerId = layerId;
_frameBufferProvider = fbProvider;
_ioSurfaceHolder = ioSurfaceHolder;
}

View File

@ -6,6 +6,7 @@
#define FLUTTER_COMPOSITOR_H_
#include <functional>
#include <list>
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
@ -50,11 +51,47 @@ class FlutterCompositor {
// PresentCallback is called at the end of the Present function.
void SetPresentCallback(const PresentCallback& present_callback);
// Denotes the current status of the frame being composited.
// Started: A new frame has begun and we have cleared the old layer tree
// and are now creating backingstore(s) for the embedder to use.
// Presenting: the embedder has finished rendering into the provided
// backingstore(s) and we are creating the layer tree for the
// system compositor to present with.
// Ended: The frame has been presented and we are no longer processing
// it.
typedef enum { kStarted, kPresenting, kEnded } FrameStatus;
protected:
__weak const FlutterViewController* view_controller_;
// Gets and sets the FrameStatus for the current frame.
void SetFrameStatus(FrameStatus frame_status);
FrameStatus GetFrameStatus();
// Clears the previous CALayers and updates the frame status to frame started.
void StartFrame();
// Calls the present callback and ensures the frame status is updated
// to frame ended, returning whether the present was successful or not.
bool EndFrame();
// Creates a CALayer object which is backed by the supplied IOSurface, and
// adds it to the root CALayer for this FlutterViewController's view.
void InsertCALayerForIOSurface(
const IOSurfaceRef& io_surface,
CATransform3D transform = CATransform3DIdentity);
private:
// A list of the active CALayer objects for the frame that need to be removed.
std::list<CALayer*> active_ca_layers_;
// Callback set by the embedder to be called when the layer tree has been
// correctly set up for this frame.
PresentCallback present_callback_;
// Current frame status.
FrameStatus frame_status_ = kEnded;
FML_DISALLOW_COPY_AND_ASSIGN(FlutterCompositor);
};

View File

@ -18,4 +18,41 @@ void FlutterCompositor::SetPresentCallback(
present_callback_ = present_callback;
}
void FlutterCompositor::StartFrame() {
// First remove all CALayers from the superlayer.
for (auto layer : active_ca_layers_) {
[layer removeFromSuperlayer];
}
// Reset active layers.
active_ca_layers_.clear();
SetFrameStatus(FrameStatus::kStarted);
}
bool FlutterCompositor::EndFrame() {
bool status = present_callback_();
SetFrameStatus(FrameStatus::kEnded);
return status;
}
void FlutterCompositor::SetFrameStatus(FlutterCompositor::FrameStatus frame_status) {
frame_status_ = frame_status;
}
FlutterCompositor::FrameStatus FlutterCompositor::GetFrameStatus() {
return frame_status_;
}
void FlutterCompositor::InsertCALayerForIOSurface(const IOSurfaceRef& io_surface,
CATransform3D transform) {
// FlutterCompositor manages the lifecycle of CALayers.
CALayer* content_layer = [[CALayer alloc] init];
content_layer.transform = transform;
content_layer.frame = view_controller_.flutterView.layer.bounds;
[content_layer setContents:(__bridge id)io_surface];
[view_controller_.flutterView.layer addSublayer:content_layer];
active_ca_layers_.push_back(content_layer);
}
} // namespace flutter

View File

@ -12,6 +12,7 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h"
@ -139,9 +140,10 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
// Pointer to the Dart AOT snapshot and instruction data.
_FlutterEngineAOTData* _aotData;
// _macOSGLCompositor is created when the engine is created and
// it's destruction is handled by ARC when the engine is destroyed.
std::unique_ptr<flutter::FlutterGLCompositor> _macOSGLCompositor;
// _macOSCompositor is created when the engine is created and
// its destruction is handled by ARC when the engine is destroyed.
// This is either a FlutterGLCompositor or a FlutterMetalCompositor instance.
std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
// FlutterCompositor is copied and used in embedder.cc.
FlutterCompositor _compositor;
@ -324,40 +326,53 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
}
- (FlutterCompositor*)createFlutterCompositor {
// When rendering with metal do not support platform views.
if ([FlutterRenderingBackend renderUsingMetal]) {
return nil;
}
// TODO(richardjcai): Add support for creating a FlutterGLCompositor
// TODO(richardjcai): Add support for creating a FlutterCompositor
// with a nil _viewController for headless engines.
// https://github.com/flutter/flutter/issues/71606
if (!_viewController) {
return nil;
}
FlutterOpenGLRenderer* openGLRenderer = reinterpret_cast<FlutterOpenGLRenderer*>(_renderer);
[openGLRenderer.openGLContext makeCurrentContext];
__weak FlutterEngine* weakSelf = self;
_macOSGLCompositor =
std::make_unique<flutter::FlutterGLCompositor>(_viewController, openGLRenderer.openGLContext);
if ([FlutterRenderingBackend renderUsingMetal]) {
FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_renderer);
_macOSCompositor =
std::make_unique<flutter::FlutterMetalCompositor>(_viewController, metalRenderer.device);
_macOSCompositor->SetPresentCallback([weakSelf]() {
FlutterMetalRenderer* metalRenderer =
reinterpret_cast<FlutterMetalRenderer*>(weakSelf.renderer);
return [metalRenderer present:0 /*=textureID*/];
});
} else {
FlutterOpenGLRenderer* openGLRenderer = reinterpret_cast<FlutterOpenGLRenderer*>(_renderer);
[openGLRenderer.openGLContext makeCurrentContext];
_macOSCompositor = std::make_unique<flutter::FlutterGLCompositor>(_viewController,
openGLRenderer.openGLContext);
_macOSCompositor->SetPresentCallback([weakSelf]() {
FlutterOpenGLRenderer* openGLRenderer =
reinterpret_cast<FlutterOpenGLRenderer*>(weakSelf.renderer);
return [openGLRenderer glPresent];
});
}
_compositor = {};
_compositor.struct_size = sizeof(FlutterCompositor);
_compositor.user_data = _macOSGLCompositor.get();
_compositor.user_data = _macOSCompositor.get();
_compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, //
FlutterBackingStore* backing_store_out, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->CreateBackingStore(
return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
config, backing_store_out);
};
_compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->CollectBackingStore(
return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CollectBackingStore(
backing_store);
};
@ -365,17 +380,9 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
size_t layers_count, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->Present(layers,
layers_count);
return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->Present(layers, layers_count);
};
__weak FlutterEngine* weakSelf = self;
_macOSGLCompositor->SetPresentCallback([weakSelf]() {
FlutterOpenGLRenderer* openGLRenderer =
reinterpret_cast<FlutterOpenGLRenderer*>(weakSelf.renderer);
return [openGLRenderer glPresent];
});
_compositor.avoid_backing_store_cache = true;
return &_compositor;

View File

@ -14,6 +14,10 @@
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/testing/testing.h"
@interface FlutterEngineTestObjC : NSObject
- (bool)testCompositor;
@end
namespace flutter::testing {
namespace {
@ -330,4 +334,49 @@ TEST(FlutterEngine, ResetsAccessibilityBridgeWhenSetsNewViewController) {
[engine shutDownEngine];
}
TEST(FlutterEngine, Compositor) {
ASSERT_TRUE([[FlutterEngineTestObjC alloc] testCompositor]);
}
} // namespace flutter::testing
@implementation FlutterEngineTestObjC
- (bool)testCompositor {
NSString* fixtures = @(flutter::testing::GetFixturesPath());
FlutterDartProject* project = [[FlutterDartProject alloc]
initWithAssetsPath:fixtures
ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
id engine = [[FlutterEngine alloc] initWithName:@"test" project:project];
FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project];
[viewController loadView];
viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
[engine setViewController:viewController];
EXPECT_TRUE([engine runWithEntrypoint:@"can_composite_platform_views"]);
id mockFlutterView = OCMPartialMock(viewController.flutterView);
OCMExpect([mockFlutterView present]);
@try {
OCMVerifyAllWithDelay( // NOLINT(google-objc-avoid-throwing-exception)
mockFlutterView, 5);
} @catch (...) {
return false;
}
CALayer* rootLayer = viewController.flutterView.layer;
// There are three layers total - the root layer and two sublayers.
// This test will need to be updated when PlatformViews are supported, as
// there are two PlatformView layers in this test.
EXPECT_EQ(rootLayer.sublayers.count, 2u);
// TODO(gw280): add support for screenshot tests in this test harness
[engine shutDownEngine];
return true;
}
@end

View File

@ -9,6 +9,7 @@
#include <memory>
#include "flutter/shell/platform/common/accessibility_bridge.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h"
@interface FlutterEngine ()

View File

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#ifndef FLUTTER_GL_COMPOSITOR_H_
#define FLUTTER_GL_COMPOSITOR_H_
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h"
#include "flutter/shell/platform/embedder/embedder.h"
@ -49,29 +49,9 @@ class FlutterGLCompositor : public FlutterCompositor {
private:
const NSOpenGLContext* open_gl_context_;
// Count for how many CALayers have been created for a frame.
// Resets when a frame is finished.
// ca_layer_count_ is also used as a layerId.
size_t ca_layer_count_ = 0;
// Maps a layer_id (size_t) to a CALayer.
// The layer_id starts at 0 for a given frame
// and increments by 1 for each new CALayer.
std::map<size_t, CALayer*> ca_layer_map_;
// frame_started_ keeps track of if a layer has been
// created for the frame.
bool frame_started_ = false;
// Set frame_started_ to true and reset all layer state.
void StartFrame();
// Creates a CALayer and adds it to ca_layer_map_ and increments
// ca_layer_count_; Returns the key value (size_t) for the layer in
// ca_layer_map_.
size_t CreateCALayer();
FML_DISALLOW_COPY_AND_ASSIGN(FlutterGLCompositor);
};
} // namespace flutter
#endif // FLUTTER_GL_COMPOSITOR_H_

View File

@ -31,7 +31,7 @@ bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* co
CGSize size = CGSizeMake(config->size.width, config->size.height);
if (!frame_started_) {
if (GetFrameStatus() != FrameStatus::kStarted) {
StartFrame();
// If the backing store is for the first layer, return the fbo for the
// FlutterView.
@ -47,13 +47,11 @@ bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* co
GLuint fbo = [fb_provider glFrameBufferId];
GLuint texture = [fb_provider glTextureId];
size_t layer_id = CreateCALayer();
[io_surface_holder bindSurfaceToTexture:texture fbo:fbo size:size];
FlutterBackingStoreData* data =
[[FlutterBackingStoreData alloc] initWithLayerId:layer_id
fbProvider:fb_provider
ioSurfaceHolder:io_surface_holder];
[[FlutterBackingStoreData alloc] initWithFbProvider:fb_provider
ioSurfaceHolder:io_surface_holder];
backing_store_out->open_gl.framebuffer.name = fbo;
backing_store_out->open_gl.framebuffer.user_data = (__bridge_retained void*)data;
@ -64,6 +62,7 @@ bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* co
backing_store_out->open_gl.framebuffer.target = GL_RGBA8;
backing_store_out->open_gl.framebuffer.destruction_callback = [](void* user_data) {
if (user_data != nullptr) {
// This deletes the OpenGL framebuffer object and texture backing it.
CFRelease(user_data);
}
};
@ -76,6 +75,8 @@ bool FlutterGLCompositor::CollectBackingStore(const FlutterBackingStore* backing
}
bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) {
SetFrameStatus(FrameStatus::kPresenting);
for (size_t i = 0; i < layers_count; ++i) {
const auto* layer = layers[i];
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
@ -86,19 +87,11 @@ bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_cou
(__bridge FlutterBackingStoreData*)backing_store->open_gl.framebuffer.user_data;
FlutterIOSurfaceHolder* io_surface_holder = [backing_store_data ioSurfaceHolder];
size_t layer_id = [backing_store_data layerId];
CALayer* content_layer = ca_layer_map_[layer_id];
FML_CHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id;
content_layer.frame = content_layer.superlayer.bounds;
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
// The surface is an OpenGL texture, which means it has origin in bottom left corner
// and needs to be flipped vertically
content_layer.transform = CATransform3DMakeScale(1, -1, 1);
IOSurfaceRef io_surface_contents = [io_surface_holder ioSurface];
[content_layer setContents:(__bridge id)io_surface_contents];
InsertCALayerForIOSurface(io_surface, CATransform3DMakeScale(1, -1, 1));
}
break;
}
@ -108,39 +101,8 @@ bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_cou
break;
};
}
// The frame has been presented, prepare FlutterGLCompositor to
// render a new frame.
frame_started_ = false;
return present_callback_();
}
void FlutterGLCompositor::StartFrame() {
// First reset all the state.
ca_layer_count_ = 0;
// First remove all CALayers from the superlayer.
for (auto const& ca_layer_kvp : ca_layer_map_) {
[ca_layer_kvp.second removeFromSuperlayer];
}
// Reset layer map.
ca_layer_map_.clear();
frame_started_ = true;
}
size_t FlutterGLCompositor::CreateCALayer() {
if (!view_controller_) {
return 0;
}
// FlutterGLCompositor manages the lifecycle of content layers.
// The id for a CALayer starts at 0 and increments by 1 for
// any given frame.
CALayer* content_layer = [[CALayer alloc] init];
[view_controller_.flutterView.layer addSublayer:content_layer];
ca_layer_map_[ca_layer_count_] = content_layer;
return ca_layer_count_++;
return EndFrame();
}
} // namespace flutter

View File

@ -12,7 +12,8 @@ namespace flutter {
class FlutterMetalCompositor : public FlutterCompositor {
public:
explicit FlutterMetalCompositor(FlutterViewController* view_controller);
explicit FlutterMetalCompositor(FlutterViewController* view_controller,
id<MTLDevice> mtl_device);
virtual ~FlutterMetalCompositor() = default;
@ -40,6 +41,9 @@ class FlutterMetalCompositor : public FlutterCompositor {
// frame to the FlutterView(s).
bool Present(const FlutterLayer** layers, size_t layers_count) override;
private:
const id<MTLDevice> mtl_device_;
FML_DISALLOW_COPY_AND_ASSIGN(FlutterMetalCompositor);
};

View File

@ -4,26 +4,100 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"
#include "flutter/fml/logging.h"
namespace flutter {
FlutterMetalCompositor::FlutterMetalCompositor(FlutterViewController* view_controller)
: FlutterCompositor(view_controller) {}
FlutterMetalCompositor::FlutterMetalCompositor(FlutterViewController* view_controller,
id<MTLDevice> mtl_device)
: FlutterCompositor(view_controller), mtl_device_(mtl_device) {}
bool FlutterMetalCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
FML_CHECK(false) << "Not implemented, see issue: https://github.com/flutter/flutter/issues/81320";
return false;
if (!view_controller_) {
return false;
}
CGSize size = CGSizeMake(config->size.width, config->size.height);
backing_store_out->metal.struct_size = sizeof(FlutterMetalBackingStore);
backing_store_out->metal.texture.struct_size = sizeof(FlutterMetalTexture);
if (GetFrameStatus() != FrameStatus::kStarted) {
StartFrame();
// If the backing store is for the first layer, return the MTLTexture for the
// FlutterView.
FlutterMetalRenderBackingStore* backingStore =
reinterpret_cast<FlutterMetalRenderBackingStore*>(
[view_controller_.flutterView backingStoreForSize:size]);
backing_store_out->metal.texture.texture =
(__bridge FlutterMetalTextureHandle)backingStore.texture;
} else {
FlutterIOSurfaceHolder* io_surface_holder = [[FlutterIOSurfaceHolder alloc] init];
[io_surface_holder recreateIOSurfaceWithSize:size];
auto texture_descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:size.width
height:size.height
mipmapped:NO];
texture_descriptor.usage =
MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget | MTLTextureUsageShaderWrite;
backing_store_out->metal.texture.texture = (__bridge_retained FlutterMetalTextureHandle)
[mtl_device_ newTextureWithDescriptor:texture_descriptor
iosurface:[io_surface_holder ioSurface]
plane:0];
backing_store_out->metal.texture.user_data = (__bridge_retained void*)io_surface_holder;
}
backing_store_out->type = kFlutterBackingStoreTypeMetal;
backing_store_out->metal.texture.destruction_callback = [](void* user_data) {
if (user_data != nullptr) {
CFRelease(user_data);
}
};
return true;
}
bool FlutterMetalCompositor::CollectBackingStore(const FlutterBackingStore* backing_store) {
FML_CHECK(false) << "Not implemented, see issue: https://github.com/flutter/flutter/issues/81320";
return false;
// If we allocated this MTLTexture ourselves, user_data is not null, and we will need
// to release it manually.
if (backing_store->metal.texture.user_data != nullptr &&
backing_store->metal.texture.texture != nullptr) {
CFRelease(backing_store->metal.texture.texture);
}
return true;
}
bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_count) {
FML_CHECK(false) << "Not implemented, see issue: https://github.com/flutter/flutter/issues/81320";
return false;
SetFrameStatus(FrameStatus::kPresenting);
for (size_t i = 0; i < layers_count; ++i) {
const auto* layer = layers[i];
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
switch (layer->type) {
case kFlutterLayerContentTypeBackingStore: {
if (backing_store->metal.texture.user_data) {
FlutterIOSurfaceHolder* io_surface_holder =
(__bridge FlutterIOSurfaceHolder*)backing_store->metal.texture.user_data;
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
InsertCALayerForIOSurface(io_surface);
}
break;
}
case kFlutterLayerContentTypePlatformView:
// Add functionality in follow up PR.
FML_LOG(WARNING) << "Presenting PlatformViews not yet supported";
break;
};
}
return EndFrame();
}
} // namespace flutter

View File

@ -0,0 +1,71 @@
// 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.
#import <Foundation/Foundation.h>
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
#import "flutter/testing/testing.h"
namespace flutter::testing {
TEST(FlutterMetalCompositorTest, TestPresent) {
id mockViewController = CreateMockViewController(nil);
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
bool flag = false;
macos_compositor->SetPresentCallback([f = &flag]() {
*f = true;
return true;
});
ASSERT_TRUE(macos_compositor->Present(nil, 0));
ASSERT_TRUE(flag);
}
TEST(FlutterMetalCompositorTest, TestCreate) {
id mockViewController = CreateMockViewController(nil);
[mockViewController loadView];
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
config.struct_size = sizeof(FlutterBackingStoreConfig);
config.size.width = 800;
config.size.height = 600;
macos_compositor->CreateBackingStore(&config, &backing_store);
ASSERT_EQ(backing_store.type, kFlutterBackingStoreTypeMetal);
ASSERT_NE(backing_store.metal.texture.texture, nil);
id<MTLTexture> texture = (__bridge id<MTLTexture>)backing_store.metal.texture.texture;
ASSERT_EQ(texture.width, 800ul);
ASSERT_EQ(texture.height, 600ul);
}
TEST(FlutterMetalCompositorTest, TestCompositing) {
id mockViewController = CreateMockViewController(nil);
[mockViewController loadView];
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
config.struct_size = sizeof(FlutterBackingStoreConfig);
config.size.width = 800;
config.size.height = 600;
macos_compositor->CreateBackingStore(&config, &backing_store);
ASSERT_EQ(backing_store.type, kFlutterBackingStoreTypeMetal);
ASSERT_NE(backing_store.metal.texture.texture, nil);
id<MTLTexture> texture = (__bridge id<MTLTexture>)backing_store.metal.texture.texture;
ASSERT_EQ(texture.width, 800u);
ASSERT_EQ(texture.height, 600u);
}
} // flutter::testing

View File

@ -2,6 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
void main() {
}
Picture CreateSimplePicture() {
Paint blackPaint = Paint();
PictureRecorder baseRecorder = PictureRecorder();
Canvas canvas = Canvas(baseRecorder);
canvas.drawRect(Rect.fromLTRB(0.0, 0.0, 1000.0, 1000.0), blackPaint);
return baseRecorder.endRecording();
}
@pragma('vm:entry-point')
void can_composite_platform_views() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
SceneBuilder builder = SceneBuilder();
builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture());
builder.pushOffset(1.0, 2.0);
builder.addPlatformView(42, width: 123.0, height: 456.0);
builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture());
builder.pop(); // offset
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}