From 440a5f0f8067fcee7484e80a13854ede5fadf5f8 Mon Sep 17 00:00:00 2001 From: Pierre Champion Date: Fri, 2 Aug 2019 09:34:54 +0200 Subject: [PATCH] Texture support (#217) --- application.go | 20 ++ embedder/embedder.go | 57 +++++- embedder/embedder.h | 230 +++++++++++++---------- embedder/embedder_helper.c | 60 +++--- embedder/embedder_proxy.go | 18 ++ go.mod | 1 + go.sum | 4 +- plugin.go | 18 +- textinput_model.go => textinput-model.go | 0 texture-registry.go | 142 ++++++++++++++ texture.go | 44 +++++ 11 files changed, 453 insertions(+), 141 deletions(-) rename textinput_model.go => textinput-model.go (100%) create mode 100644 texture-registry.go create mode 100644 texture.go diff --git a/application.go b/application.go index 26fa2c00..faed1a4f 100644 --- a/application.go +++ b/application.go @@ -100,6 +100,11 @@ func (a *Application) Run() error { return errors.Errorf("invalid window mode %T", a.config.windowMode) } + glfw.WindowHint(glfw.ContextVersionMajor, 4) + glfw.WindowHint(glfw.ContextVersionMinor, 1) + glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) + glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) + if a.config.windowInitialLocations.xpos != 0 { // To create the window at a specific position, make it initially invisible // using the Visible window hint, set its position and then show it. @@ -217,6 +222,11 @@ func (a *Application) Run() error { a.engine.PlatfromMessage = messenger.handlePlatformMessage + texturer := newRegistry(a.engine, a.window) + a.engine.GLExternalTextureFrameCallback = texturer.handleExternalTexture + + texturer.init() + // Not very nice, but we can only really fix this when there's a pluggable // renderer. defaultTextinputPlugin.keyboardLayout = a.config.keyboardLayout @@ -254,6 +264,16 @@ func (a *Application) Run() error { // platfrom message can be corectly handled by ui.Window.onPlatformMessage. glfw.WaitEvents() + for _, p := range a.config.plugins { + // Extra init call for plugins that satisfy the PluginTexture interface. + if glfwPlugin, ok := p.(PluginTexture); ok { + err = glfwPlugin.InitPluginTexture(texturer) + if err != nil { + return errors.Wrap(err, "failed to initialize texture plugin"+fmt.Sprintf("%T", p)) + } + } + } + a.window.SetKeyCallback( func(window *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) { defaultTextinputPlugin.glfwKeyCallback(window, key, scancode, action, mods) diff --git a/embedder/embedder.go b/embedder/embedder.go index 95045ce8..c4e15fec 100644 --- a/embedder/embedder.go +++ b/embedder/embedder.go @@ -53,6 +53,16 @@ const ( ResultEngineNotRunning Result = -1 ) +// FlutterOpenGLTexture corresponds to the C.FlutterOpenGLTexture struct. +type FlutterOpenGLTexture struct { + // Target texture of the active texture unit (example GL_TEXTURE_2D) + Target uint32 + // The name of the texture + Name uint32 + // The texture format (example GL_RGBA8) + Format uint32 +} + // FlutterEngine corresponds to the C.FlutterEngine with his associated callback's method. type FlutterEngine struct { // Flutter Engine. @@ -65,12 +75,13 @@ type FlutterEngine struct { index int // GL callback functions - GLMakeCurrent func() bool - GLClearCurrent func() bool - GLPresent func() bool - GLFboCallback func() int32 - GLMakeResourceCurrent func() bool - GLProcResolver func(procName string) unsafe.Pointer + GLMakeCurrent func() bool + GLClearCurrent func() bool + GLPresent func() bool + GLFboCallback func() int32 + GLMakeResourceCurrent func() bool + GLProcResolver func(procName string) unsafe.Pointer + GLExternalTextureFrameCallback func(textureID int64, width int, height int) *FlutterOpenGLTexture // platform message callback function PlatfromMessage func(message *PlatformMessage) @@ -299,6 +310,40 @@ func (flu *FlutterEngine) SendPlatformMessageResponse( return (Result)(res) } +// RegisterExternalTexture registers an external texture with a unique identifier. +func (flu *FlutterEngine) RegisterExternalTexture(textureID int64) Result { + flu.sync.Lock() + defer flu.sync.Unlock() + if flu.closed { + return ResultEngineNotRunning + } + res := C.FlutterEngineRegisterExternalTexture(flu.Engine, C.int64_t(textureID)) + return (Result)(res) +} + +// UnregisterExternalTexture unregisters a previous texture registration. +func (flu *FlutterEngine) UnregisterExternalTexture(textureID int64) Result { + flu.sync.Lock() + defer flu.sync.Unlock() + if flu.closed { + return ResultEngineNotRunning + } + res := C.FlutterEngineUnregisterExternalTexture(flu.Engine, C.int64_t(textureID)) + return (Result)(res) +} + +// MarkExternalTextureFrameAvailable marks that a new texture frame is +// available for a given texture identifier. +func (flu *FlutterEngine) MarkExternalTextureFrameAvailable(textureID int64) Result { + flu.sync.Lock() + defer flu.sync.Unlock() + if flu.closed { + return ResultEngineNotRunning + } + res := C.FlutterEngineMarkExternalTextureFrameAvailable(flu.Engine, C.int64_t(textureID)) + return (Result)(res) +} + // FlutterEngineFlushPendingTasksNow flush tasks on a message loop not // controlled by the Flutter engine. // diff --git a/embedder/embedder.h b/embedder/embedder.h index 3a4bc97d..3cb37bfc 100644 --- a/embedder/embedder.h +++ b/embedder/embedder.h @@ -15,7 +15,7 @@ extern "C" { #ifndef FLUTTER_EXPORT #define FLUTTER_EXPORT -#endif // FLUTTER_EXPORT +#endif // FLUTTER_EXPORT #define FLUTTER_ENGINE_VERSION 1 @@ -153,6 +153,10 @@ typedef enum { // |PageView| widget does not have implicit scrolling, so that users don't // navigate to the next page when reaching the end of the current one. kFlutterSemanticsFlagHasImplicitScrolling = 1 << 18, + // Whether the semantic node is read only. + // + // Only applicable when kFlutterSemanticsFlagIsTextField flag is on. + kFlutterSemanticsFlagIsReadOnly = 1 << 20, } FlutterSemanticsFlag; typedef enum { @@ -164,7 +168,7 @@ typedef enum { kFlutterTextDirectionLTR = 2, } FlutterTextDirection; -typedef struct _FlutterEngine* FlutterEngine; +typedef struct _FlutterEngine *FlutterEngine; typedef struct { // horizontal scale factor @@ -187,7 +191,7 @@ typedef struct { double pers2; } FlutterTransformation; -typedef void (*VoidCallback)(void* /* user data */); +typedef void (*VoidCallback)(void * /* user data */); typedef struct { // Target texture of the active texture unit (example GL_TEXTURE_2D). @@ -197,26 +201,25 @@ typedef struct { // The texture format (example GL_RGBA8). uint32_t format; // User data to be returned on the invocation of the destruction callback. - void* user_data; + void *user_data; // Callback invoked (on an engine managed thread) that asks the embedder to // collect the texture. VoidCallback destruction_callback; } FlutterOpenGLTexture; -typedef bool (*BoolCallback)(void* /* user data */); -typedef FlutterTransformation (*TransformationCallback)(void* /* user data */); -typedef uint32_t (*UIntCallback)(void* /* user data */); -typedef bool (*SoftwareSurfacePresentCallback)(void* /* user data */, - const void* /* allocation */, +typedef bool (*BoolCallback)(void * /* user data */); +typedef FlutterTransformation (*TransformationCallback)(void * /* user data */); +typedef uint32_t (*UIntCallback)(void * /* user data */); +typedef bool (*SoftwareSurfacePresentCallback)(void * /* user data */, + const void * /* allocation */, size_t /* row bytes */, size_t /* height */); -typedef void* (*ProcResolver)(void* /* user data */, const char* /* name */); -typedef bool (*TextureFrameCallback)(void* /* user data */, +typedef void *(*ProcResolver)(void * /* user data */, const char * /* name */); +typedef bool (*TextureFrameCallback)(void * /* user data */, int64_t /* texture identifier */, - size_t /* width */, - size_t /* height */, - FlutterOpenGLTexture* /* texture out */); -typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */); + size_t /* width */, size_t /* height */, + FlutterOpenGLTexture * /* texture out */); +typedef void (*VsyncCallback)(void * /* user data */, intptr_t /* baton */); typedef struct { // The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig). @@ -341,7 +344,7 @@ typedef struct { // The size of this struct. Must be sizeof(FlutterPointerEvent). size_t struct_size; FlutterPointerPhase phase; - size_t timestamp; // in microseconds. + size_t timestamp; // in microseconds. double x; double y; // An optional device identifier. If this is not specified, it is assumed that @@ -367,22 +370,23 @@ typedef struct _FlutterPlatformMessageResponseHandle typedef struct { // The size of this struct. Must be sizeof(FlutterPlatformMessage). size_t struct_size; - const char* channel; - const uint8_t* message; - const size_t message_size; + const char *channel; + const uint8_t *message; + size_t message_size; // The response handle on which to invoke - // |FlutterEngineSendPlatformMessageResponse| when the response is ready. This - // field is ignored for messages being sent from the embedder to the - // framework. |FlutterEngineSendPlatformMessageResponse| must be called for - // all messages received by the embedder. Failure to call + // |FlutterEngineSendPlatformMessageResponse| when the response is ready. + // |FlutterEngineSendPlatformMessageResponse| must be called for all messages + // received by the embedder. Failure to call // |FlutterEngineSendPlatformMessageResponse| will cause a memory leak. It is // not safe to send multiple responses on a single response object. - const FlutterPlatformMessageResponseHandle* response_handle; + const FlutterPlatformMessageResponseHandle *response_handle; } FlutterPlatformMessage; typedef void (*FlutterPlatformMessageCallback)( - const FlutterPlatformMessage* /* message*/, - void* /* user data */); + const FlutterPlatformMessage * /* message*/, void * /* user data */); + +typedef void (*FlutterDataCallback)(const uint8_t * /* data */, + size_t /* size */, void * /* user data */); typedef struct { double left; @@ -431,17 +435,17 @@ typedef struct { // Describes how much space the semantics node takes up along the z-axis. double thickness; // A textual description of the node. - const char* label; + const char *label; // A brief description of the result of performing an action on the node. - const char* hint; + const char *hint; // A textual description of the current value of the node. - const char* value; + const char *value; // A value that |value| will have after a kFlutterSemanticsActionIncrease| // action has been performed. - const char* increased_value; + const char *increased_value; // A value that |value| will have after a kFlutterSemanticsActionDecrease| // action has been performed. - const char* decreased_value; + const char *decreased_value; // The reading direction for |label|, |value|, |hint|, |increasedValue|, and // |decreasedValue|. FlutterTextDirection text_direction; @@ -453,14 +457,14 @@ typedef struct { // The number of children this node has. size_t child_count; // Array of child node IDs in traversal order. Has length |child_count|. - const int32_t* children_in_traversal_order; + const int32_t *children_in_traversal_order; // Array of child node IDs in hit test order. Has length |child_count|. - const int32_t* children_in_hit_test_order; + const int32_t *children_in_hit_test_order; // The number of custom accessibility action associated with this node. size_t custom_accessibility_actions_count; // Array of |FlutterSemanticsCustomAction| IDs associated with this node. // Has length |custom_accessibility_actions_count|. - const int32_t* custom_accessibility_actions; + const int32_t *custom_accessibility_actions; } FlutterSemanticsNode; // |FlutterSemanticsCustomAction| ID used as a sentinel to signal the end of a @@ -486,20 +490,19 @@ typedef struct { // |FlutterSemanticsAction| to override. FlutterSemanticsAction override_action; // The user-readable name of this custom semantics action. - const char* label; + const char *label; // The hint description of this custom semantics action. - const char* hint; + const char *hint; } FlutterSemanticsCustomAction; typedef void (*FlutterUpdateSemanticsNodeCallback)( - const FlutterSemanticsNode* /* semantics node */, - void* /* user data */); + const FlutterSemanticsNode * /* semantics node */, void * /* user data */); typedef void (*FlutterUpdateSemanticsCustomActionCallback)( - const FlutterSemanticsCustomAction* /* semantics custom action */, - void* /* user data */); + const FlutterSemanticsCustomAction * /* semantics custom action */, + void * /* user data */); -typedef struct _FlutterTaskRunner* FlutterTaskRunner; +typedef struct _FlutterTaskRunner *FlutterTaskRunner; typedef struct { FlutterTaskRunner runner; @@ -507,9 +510,8 @@ typedef struct { } FlutterTask; typedef void (*FlutterTaskRunnerPostTaskCallback)( - FlutterTask /* task */, - uint64_t /* target time nanos */, - void* /* user data */); + FlutterTask /* task */, uint64_t /* target time nanos */, + void * /* user data */); // An interface used by the Flutter engine to execute tasks at the target time // on a specified thread. There should be a 1-1 relationship between a thread @@ -518,7 +520,7 @@ typedef void (*FlutterTaskRunnerPostTaskCallback)( typedef struct { // The size of this struct. Must be sizeof(FlutterTaskRunnerDescription). size_t struct_size; - void* user_data; + void *user_data; // May be called from any thread. Should return true if tasks posted on the // calling thread will be run on that same thread. // @@ -542,7 +544,7 @@ typedef struct { size_t struct_size; // Specify the task runner for the thread on which the |FlutterEngineRun| call // is made. - const FlutterTaskRunnerDescription* platform_task_runner; + const FlutterTaskRunnerDescription *platform_task_runner; } FlutterCustomTaskRunners; typedef struct { @@ -551,7 +553,7 @@ typedef struct { // The path to the Flutter assets directory containing project assets. The // string can be collected after the call to |FlutterEngineRun| returns. The // string must be NULL terminated. - const char* assets_path; + const char *assets_path; // The path to the Dart file containing the |main| entry point. // The string can be collected after the call to |FlutterEngineRun| returns. // The string must be NULL terminated. @@ -560,7 +562,7 @@ typedef struct { // Dart code should now be compiled to kernel form and will be loaded by from // |kernel_blob.bin| in the assets directory. This struct member is retained // for ABI stability. - const char* main_path__unused__; + const char *main_path__unused__; // The path to the |.packages| for the project. The string can be collected // after the call to |FlutterEngineRun| returns. The string must be NULL // terminated. @@ -569,11 +571,11 @@ typedef struct { // Dart code should now be compiled to kernel form and will be loaded by from // |kernel_blob.bin| in the assets directory. This struct member is retained // for ABI stability. - const char* packages_path__unused__; + const char *packages_path__unused__; // The path to the icudtl.dat file for the project. The string can be // collected after the call to |FlutterEngineRun| returns. The string must // be NULL terminated. - const char* icu_data_path; + const char *icu_data_path; // The command line argument count used to initialize the project. int command_line_argc; // The command line arguments used to initialize the project. The strings can @@ -587,7 +589,7 @@ typedef struct { // they may affect engine stability at runtime in the presence of unsanitized // input. The list of currently recognized engine flags and their descriptions // can be retrieved from the |switches.h| engine source file. - const char* const* command_line_argv; + const char *const *command_line_argv; // The callback invoked by the engine in order to give the embedder the chance // to respond to platform messages from the Dart application. The callback // will be invoked on the thread on which the |FlutterEngineRun| call is made. @@ -596,28 +598,28 @@ typedef struct { // mapped in as read-only. For more information refer to the documentation on // the Wiki at // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode - const uint8_t* vm_snapshot_data; + const uint8_t *vm_snapshot_data; // The size of the VM snapshot data buffer. size_t vm_snapshot_data_size; // The VM snapshot instructions buffer used in AOT operation. This buffer must // be mapped in as read-execute. For more information refer to the // documentation on the Wiki at // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode - const uint8_t* vm_snapshot_instructions; + const uint8_t *vm_snapshot_instructions; // The size of the VM snapshot instructions buffer. size_t vm_snapshot_instructions_size; // The isolate snapshot data buffer used in AOT operation. This buffer must be // mapped in as read-only. For more information refer to the documentation on // the Wiki at // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode - const uint8_t* isolate_snapshot_data; + const uint8_t *isolate_snapshot_data; // The size of the isolate snapshot data buffer. size_t isolate_snapshot_data_size; // The isolate snapshot instructions buffer used in AOT operation. This buffer // must be mapped in as read-execute. For more information refer to the // documentation on the Wiki at // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode - const uint8_t* isolate_snapshot_instructions; + const uint8_t *isolate_snapshot_instructions; // The size of the isolate snapshot instructions buffer. size_t isolate_snapshot_instructions_size; // The callback invoked by the engine in root isolate scope. Called @@ -646,7 +648,10 @@ typedef struct { // Path to a directory used to store data that is cached across runs of a // Flutter application (such as compiled shader programs used by Skia). // This is optional. The string must be NULL terminated. - const char* persistent_cache_path; + // + // This is different from the cache-path-dir argument defined in switches.h, + // which is used in |flutter::Settings| as |temp_directory_path|. + const char *persistent_cache_path; // If true, we'll only read the existing cache, but not write new ones. bool is_persistent_cache_read_only; @@ -669,46 +674,68 @@ typedef struct { // Care must be taken to ensure that the custom entrypoint is not tree-shaken // away. Usually, this is done using the `@pragma('vm:entry-point')` // decoration. - const char* custom_dart_entrypoint; + const char *custom_dart_entrypoint; // Typically the Flutter engine create and manages its internal threads. This // optional argument allows for the specification of task runner interfaces to // event loops managed by the embedder on threads it creates. - const FlutterCustomTaskRunners* custom_task_runners; + const FlutterCustomTaskRunners *custom_task_runners; } FlutterProjectArgs; FLUTTER_EXPORT FlutterEngineResult FlutterEngineRun(size_t version, - const FlutterRendererConfig* config, - const FlutterProjectArgs* args, - void* user_data, - FlutterEngine* engine_out); + const FlutterRendererConfig *config, + const FlutterProjectArgs *args, + void *user_data, + FlutterEngine *engine_out); FLUTTER_EXPORT FlutterEngineResult FlutterEngineShutdown(FlutterEngine engine); FLUTTER_EXPORT -FlutterEngineResult FlutterEngineSendWindowMetricsEvent( - FlutterEngine engine, - const FlutterWindowMetricsEvent* event); +FlutterEngineResult +FlutterEngineSendWindowMetricsEvent(FlutterEngine engine, + const FlutterWindowMetricsEvent *event); + +FLUTTER_EXPORT +FlutterEngineResult +FlutterEngineSendPointerEvent(FlutterEngine engine, + const FlutterPointerEvent *events, + size_t events_count); FLUTTER_EXPORT -FlutterEngineResult FlutterEngineSendPointerEvent( - FlutterEngine engine, - const FlutterPointerEvent* events, - size_t events_count); +FlutterEngineResult +FlutterEngineSendPlatformMessage(FlutterEngine engine, + const FlutterPlatformMessage *message); + +// Creates a platform message response handle that allows the embedder to set a +// native callback for a response to a message. This handle may be set on the +// |response_handle| field of any |FlutterPlatformMessage| sent to the engine. +// +// The handle must be collected via a call to +// |FlutterPlatformMessageReleaseResponseHandle|. This may be done immediately +// after a call to |FlutterEngineSendPlatformMessage| with a platform message +// whose response handle contains the handle created using this call. In case a +// handle is created but never sent in a message, the release call must still be +// made. Not calling release on the handle results in a small memory leak. +// +// The user data baton passed to the data callback is the one specified in this +// call as the third argument. +FLUTTER_EXPORT +FlutterEngineResult FlutterPlatformMessageCreateResponseHandle( + FlutterEngine engine, FlutterDataCallback data_callback, void *user_data, + FlutterPlatformMessageResponseHandle **response_out); +// Collects the handle created using +// |FlutterPlatformMessageCreateResponseHandle|. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineSendPlatformMessage( - FlutterEngine engine, - const FlutterPlatformMessage* message); +FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle( + FlutterEngine engine, FlutterPlatformMessageResponseHandle *response); FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessageResponse( - FlutterEngine engine, - const FlutterPlatformMessageResponseHandle* handle, - const uint8_t* data, - size_t data_length); + FlutterEngine engine, const FlutterPlatformMessageResponseHandle *handle, + const uint8_t *data, size_t data_length); // This API is only meant to be used by platforms that need to flush tasks on a // message loop not controlled by the Flutter engine. This API will be @@ -722,21 +749,21 @@ FlutterEngineResult __FlutterEngineFlushPendingTasksNow(); // mark that a frame is available by calling // |FlutterEngineMarkExternalTextureFrameAvailable|. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineRegisterExternalTexture( - FlutterEngine engine, - int64_t texture_identifier); +FlutterEngineResult +FlutterEngineRegisterExternalTexture(FlutterEngine engine, + int64_t texture_identifier); // Unregister a previous texture registration. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineUnregisterExternalTexture( - FlutterEngine engine, - int64_t texture_identifier); +FlutterEngineResult +FlutterEngineUnregisterExternalTexture(FlutterEngine engine, + int64_t texture_identifier); // Mark that a new texture frame is available for a given texture identifier. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable( - FlutterEngine engine, - int64_t texture_identifier); +FlutterEngineResult +FlutterEngineMarkExternalTextureFrameAvailable(FlutterEngine engine, + int64_t texture_identifier); // Enable or disable accessibility semantics. // @@ -749,18 +776,16 @@ FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(FlutterEngine engine, // Sets additional accessibility features. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures( - FlutterEngine engine, - FlutterAccessibilityFeature features); +FlutterEngineResult +FlutterEngineUpdateAccessibilityFeatures(FlutterEngine engine, + FlutterAccessibilityFeature features); // Dispatch a semantics action to the specified semantics node. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineDispatchSemanticsAction( - FlutterEngine engine, - uint64_t id, - FlutterSemanticsAction action, - const uint8_t* data, - size_t data_length); +FlutterEngineResult +FlutterEngineDispatchSemanticsAction(FlutterEngine engine, uint64_t id, + FlutterSemanticsAction action, + const uint8_t *data, size_t data_length); // Notify the engine that a vsync event occurred. A baton passed to the // platform via the vsync callback must be returned. This call must be made on @@ -779,8 +804,7 @@ FlutterEngineResult FlutterEngineDispatchSemanticsAction( // // That frame timepoints are in nanoseconds. FLUTTER_EXPORT -FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine, - intptr_t baton, +FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine, intptr_t baton, uint64_t frame_start_time_nanos, uint64_t frame_target_time_nanos); @@ -791,7 +815,7 @@ FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine, // Can be called on any thread. Strings passed into the function will NOT be // copied when added to the timeline. Only string literals may be passed in. FLUTTER_EXPORT -void FlutterEngineTraceEventDurationBegin(const char* name); +void FlutterEngineTraceEventDurationBegin(const char *name); // A profiling utility. Logs a trace duration end event to the timeline. If the // timeline is unavailable or disabled, this has no effect. This call must be @@ -801,14 +825,14 @@ void FlutterEngineTraceEventDurationBegin(const char* name); // NOT be copied when added to the timeline. Only string literals may be passed // in. FLUTTER_EXPORT -void FlutterEngineTraceEventDurationEnd(const char* name); +void FlutterEngineTraceEventDurationEnd(const char *name); // A profiling utility. Logs a trace duration instant event to the timeline. If // the timeline is unavailable or disabled, this has no effect. Can be called // on any thread. Strings passed into the function will NOT be copied when added // to the timeline. Only string literals may be passed in. FLUTTER_EXPORT -void FlutterEngineTraceEventInstant(const char* name); +void FlutterEngineTraceEventInstant(const char *name); // Posts a task onto the Flutter render thread. Typically, this may be called // from any thread as long as a |FlutterEngineShutdown| on the specific engine @@ -816,7 +840,7 @@ void FlutterEngineTraceEventInstant(const char* name); FLUTTER_EXPORT FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine, VoidCallback callback, - void* callback_data); + void *callback_data); // Get the current time in nanoseconds from the clock used by the flutter // engine. This is the system monotonic clock. @@ -829,10 +853,10 @@ uint64_t FlutterEngineGetCurrentTime(); // the task before that time is undefined behavior. FLUTTER_EXPORT FlutterEngineResult FlutterEngineRunTask(FlutterEngine engine, - const FlutterTask* task); + const FlutterTask *task); #if defined(__cplusplus) -} // extern "C" +} // extern "C" #endif -#endif // FLUTTER_EMBEDDER_H_ \ No newline at end of file +#endif // FLUTTER_EMBEDDER_H_ diff --git a/embedder/embedder_helper.c b/embedder/embedder_helper.c index 410f0530..c177fa15 100644 --- a/embedder/embedder_helper.c +++ b/embedder/embedder_helper.c @@ -1,4 +1,4 @@ - +#include #include #include "embedder.h" @@ -10,36 +10,38 @@ bool proxy_present(void *user_data); uint32_t proxy_fbo_callback(void *user_data); bool proxy_make_resource_current(void *user_data); void *proxy_gl_proc_resolver(void *user_data, const char *procname); -void proxy_platform_message_callback(const FlutterPlatformMessage *message, void *user_data); +void proxy_platform_message_callback(const FlutterPlatformMessage *message, + void *user_data); +bool proxy_gl_external_texture_frame_callback(void *user_data, + int64_t texture_id, size_t width, + size_t height, + FlutterOpenGLTexture *texture); // C helper -FlutterEngineResult runFlutter(void *user_data, FlutterEngine *engine, FlutterProjectArgs *Args, - const char *const *vmArgs, int nVmAgrs) -{ - FlutterRendererConfig config = {}; - config.type = kOpenGL; - - config.open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig); - config.open_gl.make_current = proxy_make_current; - config.open_gl.clear_current = proxy_clear_current; - config.open_gl.present = proxy_present; - config.open_gl.fbo_callback = proxy_fbo_callback; - config.open_gl.make_resource_current = proxy_make_resource_current; - config.open_gl.gl_proc_resolver = proxy_gl_proc_resolver; - - Args->command_line_argc = nVmAgrs; - Args->command_line_argv = vmArgs; - Args->platform_message_callback = proxy_platform_message_callback; - - return FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, Args, user_data, engine); +FlutterEngineResult runFlutter(void *user_data, FlutterEngine *engine, + FlutterProjectArgs *Args, + const char *const *vmArgs, int nVmAgrs) { + FlutterRendererConfig config = {}; + config.type = kOpenGL; + + config.open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig); + config.open_gl.make_current = proxy_make_current; + config.open_gl.clear_current = proxy_clear_current; + config.open_gl.present = proxy_present; + config.open_gl.fbo_callback = proxy_fbo_callback; + config.open_gl.make_resource_current = proxy_make_resource_current; + config.open_gl.gl_proc_resolver = proxy_gl_proc_resolver; + config.open_gl.gl_external_texture_frame_callback = + proxy_gl_external_texture_frame_callback; + + Args->command_line_argc = nVmAgrs; + Args->command_line_argv = vmArgs; + Args->platform_message_callback = proxy_platform_message_callback; + + return FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, Args, user_data, + engine); } -char **makeCharArray(int size) -{ - return calloc(sizeof(char *), size); -} +char **makeCharArray(int size) { return calloc(sizeof(char *), size); } -void setArrayString(char **a, char *s, int n) -{ - a[n] = s; -} +void setArrayString(char **a, char *s, int n) { a[n] = s; } diff --git a/embedder/embedder_proxy.go b/embedder/embedder_proxy.go index cf0999b9..53dd8c9b 100644 --- a/embedder/embedder_proxy.go +++ b/embedder/embedder_proxy.go @@ -59,3 +59,21 @@ func proxy_gl_proc_resolver(userData unsafe.Pointer, procname *C.char) unsafe.Po flutterEngine := (*FlutterEngine)(unsafe.Pointer(flutterEnginePointer)) return flutterEngine.GLProcResolver(C.GoString(procname)) } + +//export proxy_gl_external_texture_frame_callback +func proxy_gl_external_texture_frame_callback(userData unsafe.Pointer, + textureID int64, + width C.size_t, + height C.size_t, + texture *C.FlutterOpenGLTexture) C.bool { + flutterEnginePointer := *(*uintptr)(userData) + flutterEngine := (*FlutterEngine)(unsafe.Pointer(flutterEnginePointer)) + embedderGLTexture := flutterEngine.GLExternalTextureFrameCallback(textureID, int(width), int(height)) + if embedderGLTexture == nil { + return C.bool(false) + } + texture.target = C.uint32_t(embedderGLTexture.Target) + texture.name = C.uint32_t(embedderGLTexture.Name) + texture.format = C.uint32_t(embedderGLTexture.Format) + return C.bool(true) +} diff --git a/go.mod b/go.mod index caaf77ba..1300521c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/davecgh/go-spew v1.1.1 + github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 github.com/go-gl/glfw v0.0.0-20190519095719-e6da0acd62b1 github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.3.0 diff --git a/go.sum b/go.sum index ade1e344..5e5836b8 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-gl/glfw v0.0.0-20190217072633-93b30450e032 h1:WUDJN6o1AZlnNR0UZ11zsr0Quh44CV7svcg6VzEsySc= -github.com/go-gl/glfw v0.0.0-20190217072633-93b30450e032/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= +github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/go-gl/glfw v0.0.0-20190519095719-e6da0acd62b1 h1:noz9OnjV5PMOZWNOI+y1cS5rnxuJfpY6leIgQEEdBQw= github.com/go-gl/glfw v0.0.0-20190519095719-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= diff --git a/plugin.go b/plugin.go index 0f44731e..c2ed325a 100644 --- a/plugin.go +++ b/plugin.go @@ -22,7 +22,7 @@ type Plugin interface { // PluginGLFW defines the interface for plugins that are GLFW-aware. Plugins may // implement this interface to receive access to the *glfw.Window. Note that // plugins must still implement the Plugin interface. The call to InitPluginGLFW -// is made afther the call to InitPlugin. +// is made after the call to InitPlugin. // // PluginGLFW is separated because not all plugins need to know about glfw, // Adding glfw.Window to the InitPlugin call would add glfw as dependency to @@ -37,3 +37,19 @@ type PluginGLFW interface { // returned it is printend the application is stopped. InitPluginGLFW(window *glfw.Window) error } + +// PluginTexture defines the interface for plugins that needs to create and +// manage backend textures. Plugins may implement this interface to receive +// access to the TextureRegistry. Note that plugins must still implement the +// Plugin interface. The call to PluginTexture is made after the call to +// PluginGLFW. +// +// PluginTexture is separated because not all plugins need to send raw pixel to +// the Flutter scene. +type PluginTexture interface { + // Any type inmplementing PluginTexture must also implement Plugin. + Plugin + // InitPluginTexture is called after the call to InitPlugin. When an error is + // returned it is printend the application is stopped. + InitPluginTexture(registry *TextureRegistry) error +} diff --git a/textinput_model.go b/textinput-model.go similarity index 100% rename from textinput_model.go rename to textinput-model.go diff --git a/texture-registry.go b/texture-registry.go new file mode 100644 index 00000000..d0027cd9 --- /dev/null +++ b/texture-registry.go @@ -0,0 +1,142 @@ +package flutter + +import ( + "fmt" + "sync" + + "github.com/go-flutter-desktop/go-flutter/embedder" + "github.com/go-gl/gl/v4.6-core/gl" + "github.com/go-gl/glfw/v3.2/glfw" + "github.com/pkg/errors" +) + +// TextureRegistry is a registry entry for a managed Texture. +type TextureRegistry struct { + window *glfw.Window + engine *embedder.FlutterEngine + channels map[int64]*externalTextureHanlder + channelsLock sync.RWMutex + + texture int64 + texturesLock sync.Mutex +} + +type externalTextureHanlder struct { + // handle is called when flutter needs the PixelBuffer + handle ExternalTextureHanlderFunc + // gl texture to refer to for this handler + texture uint32 +} + +func newRegistry(engine *embedder.FlutterEngine, window *glfw.Window) *TextureRegistry { + return &TextureRegistry{ + window: window, + engine: engine, + channels: make(map[int64]*externalTextureHanlder), + } +} + +func (t *TextureRegistry) init() error { + t.window.MakeContextCurrent() + // Important! Call gl.Init only under the presence of an active OpenGL context, + // i.e., after MakeContextCurrent. + if err := gl.Init(); err != nil { + return errors.Wrap(err, "TextureRegistry gl init") + } + return nil +} + +// NewTexture creates a new Texture +func (t *TextureRegistry) NewTexture() Texture { + t.texturesLock.Lock() + defer t.texturesLock.Unlock() + t.texture++ + return Texture{ID: t.texture, registry: t} +} + +// ExternalTextureHanlderFunc describes the function that handles external +// Texture on a given ID. +type ExternalTextureHanlderFunc func(width int, height int) (bool, *PixelBuffer) + +// PixelBuffer is an in-memory (RGBA) image. +type PixelBuffer struct { + // Pix holds the image's pixels, in R, G, B, A order. + Pix []uint8 + // Width and Height of the image's bounds + Width, Height int +} + +// setTextureHandler registers a handler to be invoked when the Flutter +// application want to get a PixelBuffer to draw into the scene. +// +// Registration overwrites any previous registration for the same textureID +// name. Use nil as handler to deregister. +func (t *TextureRegistry) setTextureHandler(textureID int64, handler ExternalTextureHanlderFunc) { + t.channelsLock.Lock() + if handler == nil { + texture := t.channels[textureID] + if texture != nil { + gl.DeleteTextures(1, &texture.texture) + } + delete(t.channels, textureID) + } else { + t.channels[textureID] = &externalTextureHanlder{ + handle: handler, + } + } + t.channelsLock.Unlock() +} + +func (t *TextureRegistry) handleExternalTexture(textureID int64, + width int, height int) *embedder.FlutterOpenGLTexture { + + t.channelsLock.RLock() + registration, registrationExists := t.channels[textureID] + t.channelsLock.RUnlock() + + if !registrationExists { + fmt.Printf("go-flutter: no texture handler found for Texture ID: %v\n", textureID) + return nil + } + res, pixelBuffer := registration.handle(width, height) + if !res || pixelBuffer == nil { + return nil + } + + if len(pixelBuffer.Pix) == 0 { + return nil + } + + t.window.MakeContextCurrent() + + if registration.texture == 0 { + gl.GenTextures(1, ®istration.texture) + gl.BindTexture(gl.TEXTURE_2D, registration.texture) + // set the texture wrapping parameters + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER) + // set texture filtering parameters + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + } + + gl.BindTexture(gl.TEXTURE_2D, registration.texture) + // It seems that current flutter/engine can only support RGBA texture. + gl.TexImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + int32(pixelBuffer.Width), + int32(pixelBuffer.Height), + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + gl.Ptr(pixelBuffer.Pix)) + + return &embedder.FlutterOpenGLTexture{ + Target: gl.TEXTURE_2D, + Name: registration.texture, + Format: gl.RGBA8, + } + +} diff --git a/texture.go b/texture.go new file mode 100644 index 00000000..333e7e8e --- /dev/null +++ b/texture.go @@ -0,0 +1,44 @@ +package flutter + +import ( + "errors" + "fmt" + + "github.com/go-flutter-desktop/go-flutter/embedder" +) + +// Texture is an identifier for texture declaration +type Texture struct { + ID int64 + registry *TextureRegistry +} + +// Register registers a textureID with his associated handler +func (t *Texture) Register(handler ExternalTextureHanlderFunc) error { + t.registry.setTextureHandler(t.ID, handler) + result := t.registry.engine.RegisterExternalTexture(t.ID) + if result != embedder.ResultSuccess { + t.registry.setTextureHandler(t.ID, nil) + return errors.New("'go-flutter' couldn't register texture with id: " + fmt.Sprint(t.ID)) + } + return nil +} + +// FrameAvailable mark a texture buffer is ready to be draw in the flutter scene +func (t *Texture) FrameAvailable() error { + result := t.registry.engine.MarkExternalTextureFrameAvailable(t.ID) + if result != embedder.ResultSuccess { + return errors.New("'go-flutter' couldn't mark frame available of texture with id: " + fmt.Sprint(t.ID)) + } + return nil +} + +// UnRegister unregisters a textureID with his associated handler +func (t *Texture) UnRegister() error { + result := t.registry.engine.UnregisterExternalTexture(t.ID) + if result != embedder.ResultSuccess { + return errors.New("'go-flutter' couldn't unregisters texture with id: " + fmt.Sprint(t.ID)) + } + t.registry.setTextureHandler(t.ID, nil) + return nil +}