blob: 1cb1a412af7694ff079a55ee51baa466fdd1e808 [file] [log] [blame]
// 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/FlutterCompositor.h"
#include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"
namespace flutter {
FlutterCompositor::FlutterCompositor(id<FlutterViewProvider> view_provider,
FlutterPlatformViewController* platform_view_controller,
id<MTLDevice> mtl_device)
: view_provider_(view_provider),
platform_view_controller_(platform_view_controller),
mtl_device_(mtl_device) {
FML_CHECK(view_provider != nullptr) << "view_provider cannot be nullptr";
}
bool FlutterCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
// TODO(dkwingsmt): This class only supports single-view for now. As more
// classes are gradually converted to multi-view, it should get the view ID
// from somewhere.
FlutterView* view = GetView(kFlutterDefaultViewId);
if (!view) {
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 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 FlutterCompositor::CollectBackingStore(const FlutterBackingStore* backing_store) {
// 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 FlutterCompositor::Present(uint64_t view_id,
const FlutterLayer** layers,
size_t layers_count) {
FlutterView* view = GetView(view_id);
if (!view) {
return false;
}
SetFrameStatus(FrameStatus::kPresenting);
bool has_flutter_content = false;
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(view, io_surface);
}
has_flutter_content = true;
break;
}
case kFlutterLayerContentTypePlatformView: {
PresentPlatformView(view, layer, i);
break;
}
};
}
return EndFrame(has_flutter_content);
}
void FlutterCompositor::PresentPlatformView(FlutterView* default_base_view,
const FlutterLayer* layer,
size_t layer_position) {
// TODO (https://github.com/flutter/flutter/issues/96668)
// once the issue is fixed, this check will pass.
FML_DCHECK([[NSThread currentThread] isMainThread])
<< "Must be on the main thread to present platform views";
int64_t platform_view_id = layer->platform_view->identifier;
NSView* platform_view = [platform_view_controller_ platformViewWithID:platform_view_id];
FML_DCHECK(platform_view) << "Platform view not found for id: " << platform_view_id;
CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
layer->size.width / scale, layer->size.height / scale);
if (platform_view.superview == nil) {
[default_base_view addSubview:platform_view];
}
platform_view.layer.zPosition = layer_position;
}
void FlutterCompositor::SetPresentCallback(
const FlutterCompositor::PresentCallback& present_callback) {
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 has_flutter_content) {
bool status = present_callback_(has_flutter_content);
SetFrameStatus(FrameStatus::kEnded);
return status;
}
FlutterView* FlutterCompositor::GetView(uint64_t view_id) {
return [view_provider_ getView:view_id];
}
void FlutterCompositor::SetFrameStatus(FlutterCompositor::FrameStatus frame_status) {
frame_status_ = frame_status;
}
FlutterCompositor::FrameStatus FlutterCompositor::GetFrameStatus() {
return frame_status_;
}
void FlutterCompositor::InsertCALayerForIOSurface(FlutterView* view,
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.layer.bounds;
[content_layer setContents:(__bridge id)io_surface];
[view.layer addSublayer:content_layer];
active_ca_layers_.push_back(content_layer);
}
} // namespace flutter