(MacOS) Add FlutterGLCompositor with support for rendering multiple layers (flutter/engine#22782)

* Create FlutterGLCompositor.
* Add additional state to manage frame status and CALayers to FlutterGLCompositor

FlutterGLCompositor supports rendering multiple layers. The first layer is rendered using the FlutterView. Additional CALayers are created if there is more than one layer.

Platform view support will be added in following PR.
This commit is contained in:
Richard Cai 2020-12-03 11:57:15 -05:00 committed by GitHub
parent 7e51be2917
commit cb35e23e34
15 changed files with 441 additions and 28 deletions

View File

@ -1053,6 +1053,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlug
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Info.plist
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
@ -1062,6 +1064,9 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExter
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h
@ -1078,6 +1083,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm

View File

@ -46,6 +46,8 @@ source_set("flutter_framework_source") {
sources = [
"framework/Source/FlutterAppDelegate.mm",
"framework/Source/FlutterBackingStoreData.h",
"framework/Source/FlutterBackingStoreData.mm",
"framework/Source/FlutterDartProject.mm",
"framework/Source/FlutterDartProject_Internal.h",
"framework/Source/FlutterEngine.mm",
@ -54,6 +56,8 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterExternalTextureGL.mm",
"framework/Source/FlutterFrameBufferProvider.h",
"framework/Source/FlutterFrameBufferProvider.mm",
"framework/Source/FlutterGLCompositor.h",
"framework/Source/FlutterGLCompositor.mm",
"framework/Source/FlutterIOSurfaceHolder.h",
"framework/Source/FlutterIOSurfaceHolder.mm",
"framework/Source/FlutterMouseCursorPlugin.h",
@ -77,9 +81,12 @@ source_set("flutter_framework_source") {
sources += _flutter_framework_headers
deps = [
"//flutter/flow:flow",
"//flutter/fml",
"//flutter/shell/platform/common/cpp:common_cpp_switches",
"//flutter/shell/platform/darwin/common:framework_shared",
"//flutter/shell/platform/embedder:embedder_as_internal_library",
"//third_party/skia",
]
public_configs = [ "//flutter:config" ]
@ -119,7 +126,10 @@ executable("flutter_desktop_darwin_unittests") {
sources = [
"framework/Source/FlutterEngineTest.mm",
"framework/Source/FlutterGLCompositorUnittests.mm",
"framework/Source/FlutterViewControllerTest.mm",
"framework/Source/FlutterViewControllerTestUtils.h",
"framework/Source/FlutterViewControllerTestUtils.mm",
]
cflags_objcc = [ "-fobjc-arc" ]

View File

@ -0,0 +1,36 @@
// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
/**
* FlutterBackingStoreData holds data to be stored in the
* BackingStore's user_data.
*/
@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;
/**
* Provides the fbo for rendering the layer.
*/
@property(nonnull, nonatomic, readonly) FlutterFrameBufferProvider* frameBufferProvider;
/**
* Contains the IOSurfaceRef with the layer contents.
*/
@property(nonnull, nonatomic, readonly) FlutterIOSurfaceHolder* ioSurfaceHolder;
@end

View File

@ -0,0 +1,24 @@
// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"
#include <OpenGL/gl.h>
@implementation FlutterBackingStoreData
- (nullable instancetype)initWithLayerId:(size_t)layerId
fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider
ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder {
if (self = [super init]) {
_layerId = layerId;
_frameBufferProvider = fbProvider;
_ioSurfaceHolder = ioSurfaceHolder;
}
return self;
}
@end

View File

@ -10,7 +10,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/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h"
#import "flutter/shell/platform/embedder/embedder.h"
/**
@ -197,6 +197,13 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine,
// 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;
// FlutterCompositor is copied and used in embedder.cc.
FlutterCompositor _compositor;
}
- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
@ -306,6 +313,8 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine,
flutterArguments.aot_data = _aotData;
}
flutterArguments.compositor = [self createFlutterCompositor];
FlutterEngineResult result = _embedderAPI.Initialize(
FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
if (result != kSuccess) {
@ -360,6 +369,54 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine,
}
}
- (FlutterCompositor*)createFlutterCompositor {
// TODO(richardjcai): Add support for creating a FlutterGLCompositor
// with a nil _viewController for headless engines.
// https://github.com/flutter/flutter/issues/71606
if (_viewController == nullptr) {
return nullptr;
}
[_mainOpenGLContext makeCurrentContext];
_macOSGLCompositor = std::make_unique<flutter::FlutterGLCompositor>(_viewController);
_compositor = {};
_compositor.struct_size = sizeof(FlutterCompositor);
_compositor.user_data = _macOSGLCompositor.get();
_compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, //
FlutterBackingStore* backing_store_out, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterGLCompositor*>(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(
backing_store);
};
_compositor.present_layers_callback = [](const FlutterLayer** layers, //
size_t layers_count, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->Present(layers,
layers_count);
};
__weak FlutterEngine* weak_self = self;
_macOSGLCompositor->SetPresentCallback(
[weak_self]() { return [weak_self engineCallbackOnPresent]; });
_compositor.avoid_backing_store_cache = true;
return &_compositor;
}
- (id<FlutterBinaryMessenger>)binaryMessenger {
// TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins
// keeping the engine alive.

View File

@ -9,7 +9,7 @@
*/
@interface FlutterFrameBufferProvider : NSObject
- (nullable instancetype)initWithOpenGLContext:(nonnull NSOpenGLContext*)opengLContext;
- (nullable instancetype)initWithOpenGLContext:(nonnull const NSOpenGLContext*)opengLContext;
/**
* Returns the id of the framebuffer.

View File

@ -15,7 +15,7 @@
@end
@implementation FlutterFrameBufferProvider
- (instancetype)initWithOpenGLContext:(NSOpenGLContext*)openGLContext {
- (instancetype)initWithOpenGLContext:(const NSOpenGLContext*)openGLContext {
if (self = [super init]) {
MacOSGLContextSwitch context_switch(openGLContext);

View File

@ -0,0 +1,80 @@
// 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 <map>
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
namespace flutter {
// FlutterGLCompositor creates and manages the backing stores used for
// rendering Flutter content and presents Flutter content and Platform views.
// Platform views are not yet supported.
// FlutterGLCompositor is created and destroyed by FlutterEngine.
class FlutterGLCompositor {
public:
FlutterGLCompositor(FlutterViewController* view_controller);
// Creates a BackingStore and saves updates the backing_store_out
// data with the new BackingStore data.
// If the backing store is being requested for the first time
// for a given frame, we do not create a new backing store but
// rather return the backing store associated with the
// FlutterView's FlutterSurfaceManager.
//
// Any additional state allocated for the backing store and
// saved as user_data in the backing store must be collected
// in the backing_store's desctruction_callback field which will
// be called when the embedder collects the backing store.
bool CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out);
// Releases the memory for any state used by the backing store.
bool CollectBackingStore(const FlutterBackingStore* backing_store);
// Presents the FlutterLayers by updating FlutterView(s) using the
// layer content.
// Present sets frame_started_ to false.
bool Present(const FlutterLayer** layers, size_t layers_count);
using PresentCallback = std::function<bool()>;
// PresentCallback is called at the end of the Present function.
void SetPresentCallback(const PresentCallback& present_callback);
private:
const FlutterViewController* view_controller_;
const NSOpenGLContext* open_gl_context_;
PresentCallback present_callback_;
// 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

View File

@ -0,0 +1,142 @@
// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h"
#import <OpenGL/gl.h>
#import "flutter/fml/logging.h"
#import "flutter/fml/platform/darwin/cf_utils.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"
#import "third_party/skia/include/core/SkCanvas.h"
#import "third_party/skia/include/core/SkSurface.h"
#import "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
#import "third_party/skia/include/utils/mac/SkCGUtils.h"
namespace flutter {
FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller)
: open_gl_context_(view_controller.flutterView.openGLContext) {
FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr";
view_controller_ = view_controller;
}
bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
CGSize size = CGSizeMake(config->size.width, config->size.height);
if (!frame_started_) {
StartFrame();
// If the backing store is for the first layer, return the fbo for the
// FlutterView.
auto fbo = [view_controller_.flutterView frameBufferIDForSize:size];
backing_store_out->open_gl.framebuffer.name = fbo;
} else {
FlutterFrameBufferProvider* fb_provider =
[[FlutterFrameBufferProvider alloc] initWithOpenGLContext:open_gl_context_];
FlutterIOSurfaceHolder* io_surface_holder = [FlutterIOSurfaceHolder alloc];
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];
backing_store_out->open_gl.framebuffer.name = fbo;
backing_store_out->open_gl.framebuffer.user_data = (__bridge_retained void*)data;
}
backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
backing_store_out->open_gl.framebuffer.target = GL_RGBA8;
backing_store_out->open_gl.framebuffer.destruction_callback = [](void* user_data) {
if (user_data != nullptr) {
CFRelease(user_data);
}
};
return true;
}
bool FlutterGLCompositor::CollectBackingStore(const FlutterBackingStore* backing_store) {
return true;
}
bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) {
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->open_gl.framebuffer.user_data) {
FlutterBackingStoreData* backing_store_data =
(__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;
// 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];
}
break;
}
case kFlutterLayerContentTypePlatformView:
// Add functionality in follow up PR.
FML_LOG(WARNING) << "Presenting PlatformViews not yet supported";
break;
};
}
// The frame has been presented, prepare FlutterGLCompositor to
// render a new frame.
frame_started_ = false;
return present_callback_();
}
void FlutterGLCompositor::SetPresentCallback(
const FlutterGLCompositor::PresentCallback& present_callback) {
present_callback_ = 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() {
// 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_++;
}
} // namespace flutter

View File

@ -0,0 +1,29 @@
// 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/FlutterGLCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
#import "flutter/testing/testing.h"
namespace flutter::testing {
TEST(FlutterGLCompositorTest, TestPresent) {
id mockViewController = CreateMockViewController(nil);
std::unique_ptr<flutter::FlutterGLCompositor> macos_compositor =
std::make_unique<FlutterGLCompositor>(mockViewController);
bool flag = false;
macos_compositor->SetPresentCallback([f = &flag]() {
*f = true;
return true;
});
ASSERT_TRUE(macos_compositor->Present(nil, 0));
ASSERT_TRUE(flag);
}
} // flutter::testing

View File

@ -9,34 +9,14 @@
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
#include "flutter/testing/testing.h"
namespace flutter::testing {
// Returns a mock FlutterViewController that is able to work in environments
// without a real pasteboard.
id mockViewController(NSString* pasteboardString) {
NSString* fixtures = @(testing::GetFixturesPath());
FlutterDartProject* project = [[FlutterDartProject alloc]
initWithAssetsPath:fixtures
ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project];
// Mock pasteboard so that this test will work in environments without a
// real pasteboard.
id pasteboardMock = OCMClassMock([NSPasteboard class]);
OCMExpect([pasteboardMock stringForType:[OCMArg any]]).andDo(^(NSInvocation* invocation) {
NSString* returnValue = pasteboardString.length > 0 ? pasteboardString : nil;
[invocation setReturnValue:&returnValue];
});
id viewControllerMock = OCMPartialMock(viewController);
OCMStub([viewControllerMock pasteboard]).andReturn(pasteboardMock);
return viewControllerMock;
}
TEST(FlutterViewController, HasStringsWhenPasteboardEmpty) {
// Mock FlutterViewController so that it behaves like the pasteboard is empty.
id viewControllerMock = mockViewController(nil);
id viewControllerMock = CreateMockViewController(nil);
// Call hasStrings and expect it to be false.
__block bool calledAfterClear = false;
@ -56,7 +36,7 @@ TEST(FlutterViewController, HasStringsWhenPasteboardEmpty) {
TEST(FlutterViewController, HasStringsWhenPasteboardFull) {
// Mock FlutterViewController so that it behaves like the pasteboard has a
// valid string.
id viewControllerMock = mockViewController(@"some string");
id viewControllerMock = CreateMockViewController(@"some string");
// Call hasStrings and expect it to be true.
__block bool called = false;

View File

@ -0,0 +1,18 @@
// 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/NSString.h>
#import <OCMock/OCMock.h>
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
#import "flutter/testing/testing.h"
namespace flutter::testing {
// Returns a mock FlutterViewController that is able to work in environments
// without a real pasteboard.
id CreateMockViewController(NSString* pasteboardString);
}

View File

@ -0,0 +1,30 @@
// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
namespace flutter::testing {
id CreateMockViewController(NSString* pasteboardString) {
{
NSString* fixtures = @(testing::GetFixturesPath());
FlutterDartProject* project = [[FlutterDartProject alloc]
initWithAssetsPath:fixtures
ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project];
// Mock pasteboard so that this test will work in environments without a
// real pasteboard.
id pasteboardMock = OCMClassMock([NSPasteboard class]);
OCMExpect([pasteboardMock stringForType:[OCMArg any]]).andDo(^(NSInvocation* invocation) {
NSString* returnValue = pasteboardString.length > 0 ? pasteboardString : nil;
[invocation setReturnValue:&returnValue];
});
id viewControllerMock = OCMPartialMock(viewController);
OCMStub([viewControllerMock pasteboard]).andReturn(pasteboardMock);
return viewControllerMock;
}
}
}

View File

@ -10,7 +10,7 @@
*/
class MacOSGLContextSwitch {
public:
explicit MacOSGLContextSwitch(NSOpenGLContext* context);
explicit MacOSGLContextSwitch(const NSOpenGLContext* context);
~MacOSGLContextSwitch();
MacOSGLContextSwitch(const MacOSGLContextSwitch&) = delete;

View File

@ -4,7 +4,7 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h"
MacOSGLContextSwitch::MacOSGLContextSwitch(NSOpenGLContext* context) {
MacOSGLContextSwitch::MacOSGLContextSwitch(const NSOpenGLContext* context) {
previous_ = [NSOpenGLContext currentContext];
[context makeCurrentContext];
}