diff --git a/library/macos/FLEExternalTexture.h b/library/macos/FLEExternalTexture.h new file mode 100644 index 000000000..ceaa25e97 --- /dev/null +++ b/library/macos/FLEExternalTexture.h @@ -0,0 +1,46 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "FLETexture.h" + +/** + * Used to bridge external FLETexture objects, + * so the implementer only needs to return the CVPixelBufferRef object, + * which will make the interface consistent with the FlutterTexture. + */ +@interface FLEExternalTexture : NSObject + +/** + * + */ +- (nonnull instancetype)initWithExternalTexture:(nonnull id)external_texture; + +/** + * Accept texture rendering notifications from the flutter engine. + */ +- (BOOL)populateTextureWidth:(size_t)width + height:(size_t)height + texture:(FlutterOpenGLTexture *)texture; + +/** + * + */ +- (int64_t)textureId; + +@property(nullable, weak) id external_texture; + +@end diff --git a/library/macos/FLEExternalTexture.m b/library/macos/FLEExternalTexture.m new file mode 100644 index 000000000..6d9cca26f --- /dev/null +++ b/library/macos/FLEExternalTexture.m @@ -0,0 +1,96 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FLEExternalTexture.h" + +#import +#import +#import +#import + +@implementation FLEExternalTexture { + CVOpenGLTextureCacheRef _textureCache; + CVPixelBufferRef _pixelBuffer; +} + +@synthesize external_texture; + +- (instancetype)initWithExternalTexture:(id)external_texture { + self = [super init]; + + if (self) { + self.external_texture = external_texture; + } + + return self; +} + +static void OnGLTextureRelease(CVPixelBufferRef pixelBuffer) { + CVPixelBufferRelease(pixelBuffer); +} + +- (int64_t)textureId { + return (NSInteger)(self); +} + +- (BOOL)populateTextureWidth:(size_t)width + height:(size_t)height + texture:(FlutterOpenGLTexture *)texture { + + if(external_texture == NULL) { + return NO; + } + + // Copy image buffer from external texture. + _pixelBuffer = [external_texture copyPixelBuffer]; + + if(_pixelBuffer == NULL) { + return NO; + } + + // Create the texture cache if necessary. + if (_textureCache == NULL) { + CGLContextObj context = [NSOpenGLContext currentContext].CGLContextObj; + CGLPixelFormatObj format = CGLGetPixelFormat(context); + if (CVOpenGLTextureCacheCreate(kCFAllocatorDefault, NULL, context, format, NULL, &_textureCache) != kCVReturnSuccess) { + NSLog(@"Could not create texture cache."); + CVPixelBufferRelease(_pixelBuffer); + return NO; + } + } + + // Try to clear the cache of OpenGL textures to save memory. + CVOpenGLTextureCacheFlush(_textureCache, 0); + + CVOpenGLTextureRef openGLTexture = NULL; + if (CVOpenGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, _pixelBuffer, NULL, &openGLTexture) != kCVReturnSuccess) { + CVPixelBufferRelease(_pixelBuffer); + return NO; + } + + texture->target = CVOpenGLTextureGetTarget(openGLTexture); + texture->name = CVOpenGLTextureGetName(openGLTexture); + texture->format = GL_RGBA8; + texture->destruction_callback = (VoidCallback)&OnGLTextureRelease; + texture->user_data = openGLTexture; + + CVPixelBufferRelease(_pixelBuffer); + return YES; +} + +- (void)dealloc { + CVOpenGLTextureCacheRelease(_textureCache); +} + +@end diff --git a/library/macos/FLEPluginRegistrar.h b/library/macos/FLEPluginRegistrar.h index 20401863e..f5660549d 100644 --- a/library/macos/FLEPluginRegistrar.h +++ b/library/macos/FLEPluginRegistrar.h @@ -16,6 +16,7 @@ #import "FLEBinaryMessenger.h" #import "FLEMethodChannel.h" +#import "FLETexture.h" /** * The protocol for an object managing registration for a plugin. It provides access to application @@ -31,6 +32,11 @@ */ @property(nonnull, readonly) id messenger; +/** + * Returns a `FLETextureRegistry` for registering textures provided by the plugin. + */ +@property(nonnull, readonly) id textures; + /** * The view displaying Flutter content. * diff --git a/library/macos/FLETexture.h b/library/macos/FLETexture.h new file mode 100644 index 000000000..5808c86a3 --- /dev/null +++ b/library/macos/FLETexture.h @@ -0,0 +1,52 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + * Implement a texture object for the FLE plugin side. + */ +@protocol FLETexture + +/** + * When the texture on the platform side is ready, + * the flutter engine will copy the texture buffer + * using copyPixelBuffer. + */ +- (nullable CVPixelBufferRef)copyPixelBuffer; + +@end + +/** + * The protocol for an object managing registration for texture. + */ +@protocol FLETextureRegistrar + +/** + * Register a |texture| object and return a textureId. + */ +- (int64_t)registerTexture:(nonnull id)texture; + +/** + * Mark a texture buffer is ready. + */ +- (void)textureFrameAvailable:(int64_t)textureId; + +/** + * Unregister an existing Texture object. + */ +- (void)unregisterTexture:(int64_t)textureId; + +@end diff --git a/library/macos/FLEViewController.h b/library/macos/FLEViewController.h index 10eb0ea5b..5d2af4d64 100644 --- a/library/macos/FLEViewController.h +++ b/library/macos/FLEViewController.h @@ -20,6 +20,7 @@ #import "FLEPlugin.h" #import "FLEPluginRegistrar.h" #import "FLEReshapeListener.h" +#import "FLETexture.h" /** * Controls embedder plugins and communication with the underlying Flutter engine, managing a view @@ -29,7 +30,7 @@ * Flutter engine in non-interactive mode, or with a drawable Flutter canvas. */ @interface FLEViewController - : NSViewController + : NSViewController /** * The view this controller manages when launched in interactive mode (headless set to false). Must diff --git a/library/macos/FLEViewController.mm b/library/macos/FLEViewController.mm index 254e82294..7c909ed5a 100644 --- a/library/macos/FLEViewController.mm +++ b/library/macos/FLEViewController.mm @@ -22,6 +22,7 @@ #import "FLEReshapeListener.h" #import "FLETextInputPlugin.h" #import "FLEView.h" +#import "FLEExternalTexture.h" static NSString *const kXcodeExtraArgumentOne = @"-NSDocumentRevisionsDebugMode"; static NSString *const kXcodeExtraArgumentTwo = @"YES"; @@ -95,6 +96,10 @@ - (void)dispatchMouseEvent:(nonnull NSEvent *)event phase:(FlutterPointerPhase)p */ - (void)dispatchKeyEvent:(NSEvent *)event ofType:(NSString *)type; +-(BOOL) populateTextureWithIdentifier:(int64_t)texture_identifier + width:(size_t)width + height:(size_t)height + texture:(FlutterOpenGLTexture *)texture; @end #pragma mark - Static methods provided to engine configuration @@ -144,6 +149,14 @@ static bool OnMakeResourceCurrent(FLEViewController *controller) { return true; } +static bool OnAcquireExternalTexture(FLEViewController *controller, + int64_t texture_identifier, + size_t width, + size_t height, + FlutterOpenGLTexture *texture) { + return [controller populateTextureWithIdentifier:texture_identifier width:width height:height texture:texture]; +} + #pragma mark Static methods provided for headless engine configuration static bool HeadlessOnMakeCurrent(FLEViewController *controller) { return false; } @@ -173,6 +186,9 @@ @implementation FLEViewController { // A message channel for passing key events to the Flutter engine. This should be replaced with // an embedding API; see Issue #47. FLEBasicMessageChannel *_keyEventChannel; + + // A mapping of external textures. + NSMutableDictionary *_textures; } @dynamic view; @@ -183,6 +199,7 @@ @implementation FLEViewController { static void CommonInit(FLEViewController *controller) { controller->_messageHandlers = [[NSMutableDictionary alloc] init]; controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init]; + controller->_textures = [[NSMutableDictionary alloc] init]; } - (instancetype)initWithCoder:(NSCoder *)coder { @@ -327,7 +344,8 @@ + (FlutterRendererConfig)createRenderConfigHeadless:(BOOL)headless { .open_gl.clear_current = (BoolCallback)OnClearCurrent, .open_gl.present = (BoolCallback)OnPresent, .open_gl.fbo_callback = (UIntCallback)OnFBO, - .open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent}; + .open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent, + .open_gl.gl_external_texture_frame_callback = (TextureFrameCallback)OnAcquireExternalTexture}; return config; } } @@ -342,6 +360,30 @@ - (void)makeResourceContextCurrent { [_resourceContext makeCurrentContext]; } +- (BOOL)populateTextureWithIdentifier:(int64_t)textureId + width:(size_t)width + height:(size_t) height + texture:(FlutterOpenGLTexture *)texture { + return [_textures[@(textureId)] populateTextureWidth:width height:height texture:texture]; +} + +- (int64_t)registerTexture:(id)texture { + FLEExternalTexture *fleTexture = [[FLEExternalTexture alloc] initWithExternalTexture:texture]; + int64_t textureId = [fleTexture textureId]; + FlutterEngineRegisterExternalTexture(_engine, textureId); + _textures[@(textureId)] = fleTexture; + return textureId; +} + +- (void)textureFrameAvailable:(int64_t)textureId { + FlutterEngineMarkExternalTextureFrameAvailable(_engine, textureId); +} + +- (void)unregisterTexture:(int64_t)textureId { + FlutterEngineUnregisterExternalTexture(_engine, textureId); + [_textures removeObjectForKey:@(textureId)]; +} + - (void)handlePlatformMessage:(const FlutterPlatformMessage *)message { NSData *messageData = [NSData dataWithBytesNoCopy:(void *)message->message length:message->message_size @@ -440,6 +482,10 @@ - (void)setMessageHandlerOnChannel:(nonnull NSString *)channel return self; } +- (id)textures { + return self; +} + - (void)addMethodCallDelegate:(nonnull id)delegate channel:(nonnull FLEMethodChannel *)channel { [channel setMethodCallHandler:^(FLEMethodCall *call, FLEMethodResult result) { diff --git a/library/macos/FlutterEmbedderMac.h b/library/macos/FlutterEmbedderMac.h index 8d5ed477b..7fc61f091 100644 --- a/library/macos/FlutterEmbedderMac.h +++ b/library/macos/FlutterEmbedderMac.h @@ -26,5 +26,6 @@ #import "FLEPlugin.h" #import "FLEPluginRegistrar.h" #import "FLEReshapeListener.h" +#import "FLETexture.h" #import "FLEView.h" #import "FLEViewController.h" diff --git a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj index bbc1a68f4..c842660ee 100644 --- a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj +++ b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj @@ -30,6 +30,9 @@ 1E2492411FCF50BE00DD3BBB /* FlutterEmbedderMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E2492371FCF50BE00DD3BBB /* FlutterEmbedderMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EEF8E071FD1F0C300DD563C /* FLEView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEF8E051FD1F0C300DD563C /* FLEView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EEF8E081FD1F0C300DD563C /* FLEView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EEF8E061FD1F0C300DD563C /* FLEView.m */; }; + 2CC5E74022033D53000EF5F5 /* FLETexture.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CC5E73F22033D53000EF5F5 /* FLETexture.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2CC5E7432203444A000EF5F5 /* FLEExternalTexture.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CC5E7412203444A000EF5F5 /* FLEExternalTexture.h */; }; + 2CC5E7442203444A000EF5F5 /* FLEExternalTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CC5E7422203444A000EF5F5 /* FLEExternalTexture.m */; }; 2CCADF9A21C74690001B4026 /* FLEEventChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CCADF9821C74690001B4026 /* FLEEventChannel.m */; }; 2CCADF9B21C74690001B4026 /* FLEEventChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CCADF9921C74690001B4026 /* FLEEventChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3389A6872159359200A27898 /* FLEBinaryMessenger.h in Headers */ = {isa = PBXBuildFile; fileRef = 3389A6862159359200A27898 /* FLEBinaryMessenger.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -92,6 +95,9 @@ 1E2492381FCF50BE00DD3BBB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 1EEF8E051FD1F0C300DD563C /* FLEView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEView.h; sourceTree = ""; }; 1EEF8E061FD1F0C300DD563C /* FLEView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEView.m; sourceTree = ""; }; + 2CC5E73F22033D53000EF5F5 /* FLETexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLETexture.h; sourceTree = ""; }; + 2CC5E7412203444A000EF5F5 /* FLEExternalTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEExternalTexture.h; sourceTree = ""; }; + 2CC5E7422203444A000EF5F5 /* FLEExternalTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEExternalTexture.m; sourceTree = ""; }; 2CCADF9821C74690001B4026 /* FLEEventChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEEventChannel.m; sourceTree = ""; }; 2CCADF9921C74690001B4026 /* FLEEventChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEEventChannel.h; sourceTree = ""; }; 3389A6862159359200A27898 /* FLEBinaryMessenger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEBinaryMessenger.h; sourceTree = ""; }; @@ -132,6 +138,8 @@ 1E24921A1FCF504200DD3BBB = { isa = PBXGroup; children = ( + 2CC5E7412203444A000EF5F5 /* FLEExternalTexture.h */, + 2CC5E7422203444A000EF5F5 /* FLEExternalTexture.m */, C4C1FE431FD74F6400691968 /* FlutterEmbedder.framework */, 1E2492371FCF50BE00DD3BBB /* FlutterEmbedderMac.h */, 1E2492451FCF536200DD3BBB /* Public */, @@ -169,6 +177,7 @@ 33C0FA1921B845F0008F8959 /* FLEBasicMessageChannel.h */, 3389A6862159359200A27898 /* FLEBinaryMessenger.h */, 2CCADF9921C74690001B4026 /* FLEEventChannel.h */, + 2CC5E73F22033D53000EF5F5 /* FLETexture.h */, 33C0FA1E21B84810008F8959 /* FLEJSONMessageCodec.h */, 33C0FA1D21B84810008F8959 /* FLEJSONMethodCodec.h */, 33C0FA1F21B84810008F8959 /* FLEMessageCodec.h */, @@ -202,8 +211,10 @@ buildActionMask = 2147483647; files = ( 33C0FA2621B84AA4008F8959 /* FLEMethodCall.h in Headers */, + 2CC5E7432203444A000EF5F5 /* FLEExternalTexture.h in Headers */, 1E24923B1FCF50BE00DD3BBB /* FLEPlugin.h in Headers */, 2CCADF9B21C74690001B4026 /* FLEEventChannel.h in Headers */, + 2CC5E74022033D53000EF5F5 /* FLETexture.h in Headers */, 3389A6872159359200A27898 /* FLEBinaryMessenger.h in Headers */, 33D7B59920A4F54400296EFC /* FLEMethodChannel.h in Headers */, 1E24923F1FCF50BE00DD3BBB /* FLEViewController.h in Headers */, @@ -325,6 +336,7 @@ 1EEF8E081FD1F0C300DD563C /* FLEView.m in Sources */, 33A87EB720F6BCDB0086D21D /* FLEJSONMethodCodec.m in Sources */, 2CCADF9A21C74690001B4026 /* FLEEventChannel.m in Sources */, + 2CC5E7442203444A000EF5F5 /* FLEExternalTexture.m in Sources */, AA8AE8BA1FD948BA00B6FB31 /* FLETextInputModel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0;