mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add support for compositing using Metal on macOS (flutter/engine#26302)
This commit is contained in:
parent
11c1a8cafb
commit
a890879ba2
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 ()
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user