diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 9a9a7d4866c65..543fd754a0a0c 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -25,7 +25,14 @@ void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { // // We have to write this flaky test because there is no reliable way to test // whether a variable is initialized or not in C++. - FML_CHECK(transform_.isFinite()); + + //fix crash:https://github.com/flutter/flutter/issues/31650 + // FML_CHECK(transform_.isFinite()); + FML_DCHECK(transform_.isFinite()); + if (!transform_.isFinite()) { + FML_LOG(ERROR) << "TransformLayer is constructed with an invalid matrix."; + transform_.setIdentity(); + } SkMatrix child_matrix; child_matrix.setConcat(matrix, transform_); diff --git a/fml/message_loop_impl.cc b/fml/message_loop_impl.cc index 94474fd1af9e0..ef1a5c48c16f6 100644 --- a/fml/message_loop_impl.cc +++ b/fml/message_loop_impl.cc @@ -39,7 +39,8 @@ fml::RefPtr MessageLoopImpl::Create() { #endif } -MessageLoopImpl::MessageLoopImpl() : order_(0), terminated_(false) {} +MessageLoopImpl::MessageLoopImpl() + : order_(0), terminated_(false), task_limit_per_looprun_(10000) {} MessageLoopImpl::~MessageLoopImpl() = default; @@ -112,28 +113,39 @@ void MessageLoopImpl::FlushTasks(FlushType type) { TRACE_EVENT0("fml", "MessageLoop::FlushTasks"); std::vector invocations; + int i_tasks_cnt = 0; + bool need_call_run_again = false; + auto now = fml::TimePoint::Now(); + { std::lock_guard lock(delayed_tasks_mutex_); - if (delayed_tasks_.empty()) { + if (delayed_tasks_.empty() || !IsMessageLoopEnabled()) { return; } - auto now = fml::TimePoint::Now(); - while (!delayed_tasks_.empty()) { + is_runninging_expired_tasks_ = true; + while (!delayed_tasks_.empty() && i_tasks_cnt < task_limit_per_looprun_) { const auto& top = delayed_tasks_.top(); if (top.target_time > now) { break; } invocations.emplace_back(std::move(top.task)); delayed_tasks_.pop(); + i_tasks_cnt++; if (type == FlushType::kSingle) { break; } } - WakeUp(delayed_tasks_.empty() ? fml::TimePoint::Max() - : delayed_tasks_.top().target_time); + // WakeUp(delayed_tasks_.empty() ? fml::TimePoint::Max() + // : delayed_tasks_.top().target_time); + if (i_tasks_cnt >= task_limit_per_looprun_ && !delayed_tasks_.empty()) { + need_call_run_again = true; + } else { + WakeUp(delayed_tasks_.empty() ? fml::TimePoint::Max() + : delayed_tasks_.top().target_time); + } } for (const auto& invocation : invocations) { @@ -142,6 +154,10 @@ void MessageLoopImpl::FlushTasks(FlushType type) { observer.second(); } } + is_runninging_expired_tasks_ = false; + if (need_call_run_again) { + FlushTasks(FlushType::kAll); + } } void MessageLoopImpl::RunExpiredTasksNow() { diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h index 4248061ad0d8d..bb95284dd078b 100644 --- a/fml/message_loop_impl.h +++ b/fml/message_loop_impl.h @@ -43,6 +43,19 @@ class MessageLoopImpl : public fml::RefCountedThreadSafe { void DoTerminate(); + void EnableMessageLoop(bool isEnable) { is_loop_enabled_ = isEnable; } + + void SetTaskLimitPerLoopRun(int task_limit_per_looprun) { + task_limit_per_looprun_ = task_limit_per_looprun; + } + + inline bool IsMessageLoopEnabled() { return is_loop_enabled_; } + + inline bool IsRunningingExpiredTasks() { + return is_runninging_expired_tasks_; + } + + protected: // Exposed for the embedder shell which allows clients to poll for events // instead of dedicating a thread to the message loop. @@ -80,12 +93,16 @@ class MessageLoopImpl : public fml::RefCountedThreadSafe { using DelayedTaskQueue = std:: priority_queue, DelayedTaskCompare>; + bool is_loop_enabled_ = true; std::map task_observers_; std::mutex delayed_tasks_mutex_; DelayedTaskQueue delayed_tasks_ FML_GUARDED_BY(delayed_tasks_mutex_); size_t order_ FML_GUARDED_BY(delayed_tasks_mutex_); std::atomic_bool terminated_; + bool is_runninging_expired_tasks_; + int task_limit_per_looprun_; + void RegisterTask(fml::closure task, fml::TimePoint target_time); enum class FlushType { diff --git a/fml/task_runner.cc b/fml/task_runner.cc index 2c4cfe4b638a2..58b1bad4638ee 100644 --- a/fml/task_runner.cc +++ b/fml/task_runner.cc @@ -39,6 +39,14 @@ bool TaskRunner::RunsTasksOnCurrentThread() { return MessageLoop::GetCurrent().GetLoopImpl() == loop_; } +void TaskRunner::EnableMessageLoop(bool isEnable) { + loop_->EnableMessageLoop(isEnable); +} + +MessageLoopImpl* TaskRunner::getMessageLoop() { + return loop_.get(); +} + void TaskRunner::RunNowOrPostTask(fml::RefPtr runner, fml::closure task) { FML_DCHECK(runner); diff --git a/fml/task_runner.h b/fml/task_runner.h index 72c4219029f34..eabeef01d18db 100644 --- a/fml/task_runner.h +++ b/fml/task_runner.h @@ -27,6 +27,9 @@ class TaskRunner : public fml::RefCountedThreadSafe { virtual bool RunsTasksOnCurrentThread(); + void EnableMessageLoop(bool isEnable); + MessageLoopImpl* getMessageLoop(); + static void RunNowOrPostTask(fml::RefPtr runner, fml::closure task); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 5a33f0d388eed..92a8be0c0b09f 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1109,6 +1109,14 @@ Rasterizer::Screenshot Shell::Screenshot( Rasterizer::ScreenshotType screenshot_type, bool base64_encode) { TRACE_EVENT0("flutter", "Shell::Screenshot"); + fml::TaskRunner* taskRunner = + (fml::TaskRunner*)this->GetTaskRunners().GetUITaskRunner().get(); + taskRunner->EnableMessageLoop(true); + + taskRunner = + (fml::TaskRunner*)this->GetTaskRunners().GetGPUTaskRunner().get(); + taskRunner->EnableMessageLoop(true); + fml::AutoResetWaitableEvent latch; Rasterizer::Screenshot screenshot; fml::TaskRunner::RunNowOrPostTask( diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index d3fd715a38abc..d29cb630f6697 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -65,6 +65,12 @@ static EGLResult CreateContext(EGLDisplay display, EGLint attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; EGLContext context = eglCreateContext(display, config, share, attributes); + if (context == EGL_NO_CONTEXT) { + EGLint last_error = eglGetError(); + if (last_error == EGL_BAD_MATCH && share != EGL_NO_CONTEXT) { + context = eglCreateContext(display, config, EGL_NO_CONTEXT, attributes); + } + } return {context != EGL_NO_CONTEXT, context}; } @@ -143,6 +149,10 @@ bool AndroidContextGL::CreatePBufferSurface() { return surface_ != EGL_NO_SURFACE; } +void* AndroidContextGL::GetContext() { + return context_; +} + AndroidContextGL::AndroidContextGL(fml::RefPtr env, const AndroidContextGL* share_context) : environment_(env), diff --git a/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index 50e15aba2ed4f..cdd91bcd2cfff 100644 --- a/shell/platform/android/android_context_gl.h +++ b/shell/platform/android/android_context_gl.h @@ -35,6 +35,8 @@ class AndroidContextGL : public fml::RefCountedThreadSafe { bool Resize(const SkISize& size); + void* GetContext(); + private: fml::RefPtr environment_; fml::RefPtr window_; diff --git a/shell/platform/android/android_external_texture_gl.cc b/shell/platform/android/android_external_texture_gl.cc index 0acd0c8337d25..53243d2f03782 100644 --- a/shell/platform/android/android_external_texture_gl.cc +++ b/shell/platform/android/android_external_texture_gl.cc @@ -15,12 +15,19 @@ AndroidExternalTextureGL::AndroidExternalTextureGL( int64_t id, const fml::jni::JavaObjectWeakGlobalRef& surfaceTexture) : Texture(id), surface_texture_(surfaceTexture), transform(SkMatrix::I()) {} + +AndroidExternalTextureGL::AndroidExternalTextureGL(int64_t id, + int64_t texture_id) +: Texture(id), texture_name_(texture_id) { + use_out_texture_ = true; +} AndroidExternalTextureGL::~AndroidExternalTextureGL() { - if (state_ == AttachmentState::attached) { - glDeleteTextures(1, &texture_name_); - } -} + if (texture_name_ != 0 && !use_out_texture_) { + glDeleteTextures(1, &texture_name_); + texture_name_ = 0; + } +}; void AndroidExternalTextureGL::OnGrContextCreated() { state_ = AttachmentState::uninitialized; @@ -38,16 +45,25 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, return; } if (state_ == AttachmentState::uninitialized) { - glGenTextures(1, &texture_name_); - Attach(static_cast(texture_name_)); + if (!use_out_texture_) { + glGenTextures(1, &texture_name_); + Attach(static_cast(texture_name_)); + } state_ = AttachmentState::attached; } if (!freeze && new_frame_ready_) { - Update(); + if (!use_out_texture_) { + Update(); + } new_frame_ready_ = false; } GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_, GL_RGBA8_OES}; + if (use_out_texture_) { + textureInfo.fTarget = GL_TEXTURE_2D; + transform.setIdentity(); + } + GrBackendTexture backendTexture(1, 1, GrMipMapped::kNo, textureInfo); sk_sp image = SkImage::MakeFromTexture( canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin, diff --git a/shell/platform/android/android_external_texture_gl.h b/shell/platform/android/android_external_texture_gl.h index 9ce87b3255bd1..fae18536d30cb 100644 --- a/shell/platform/android/android_external_texture_gl.h +++ b/shell/platform/android/android_external_texture_gl.h @@ -16,6 +16,7 @@ class AndroidExternalTextureGL : public flutter::Texture { AndroidExternalTextureGL( int64_t id, const fml::jni::JavaObjectWeakGlobalRef& surfaceTexture); + AndroidExternalTextureGL(int64_t id, int64_t texture_id); ~AndroidExternalTextureGL() override; @@ -47,6 +48,8 @@ class AndroidExternalTextureGL : public flutter::Texture { bool new_frame_ready_ = false; + bool use_out_texture_ = false; + GLuint texture_name_ = 0; SkMatrix transform; diff --git a/shell/platform/android/android_surface.h b/shell/platform/android/android_surface.h index 43f4cf33d0d50..4c238ce2e3c2a 100644 --- a/shell/platform/android/android_surface.h +++ b/shell/platform/android/android_surface.h @@ -36,6 +36,8 @@ class AndroidSurface { virtual bool ResourceContextClearCurrent() = 0; virtual bool SetNativeWindow(fml::RefPtr window) = 0; + + virtual void* GetContext() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index ff0e45fcef73c..26ba70fd9fbd3 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -75,6 +75,10 @@ bool AndroidSurfaceGL::ResourceContextClearCurrent() { return offscreen_context_->ClearCurrent(); } +void* AndroidSurfaceGL::GetContext() { + return offscreen_context_->GetContext(); +} + bool AndroidSurfaceGL::SetNativeWindow( fml::RefPtr window) { // In any case, we want to get rid of our current onscreen context. diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 7592ad5f8df3e..02606adc86dc6 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -58,6 +58,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| intptr_t GLContextFBO() const override; + void* GetContext() override; private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index 633b6091f0c15..551e40156a4bc 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -140,6 +140,10 @@ bool AndroidSurfaceSoftware::OnScreenSurfaceResize(const SkISize& size) const { return true; } +void* AndroidSurfaceSoftware::GetContext() { + return 0; +} + bool AndroidSurfaceSoftware::SetNativeWindow( fml::RefPtr window) { native_window_ = std::move(window); diff --git a/shell/platform/android/android_surface_software.h b/shell/platform/android/android_surface_software.h index cf7fcb3e20f8e..f430a99e5b6e1 100644 --- a/shell/platform/android/android_surface_software.h +++ b/shell/platform/android/android_surface_software.h @@ -47,6 +47,8 @@ class AndroidSurfaceSoftware final : public AndroidSurface, // |GPUSurfaceSoftwareDelegate| bool PresentBackingStore(sk_sp backing_store) override; + void* GetContext() override; + private: sk_sp sk_surface_; fml::RefPtr native_window_; diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 66e98087c4e5c..0a233c95380e8 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -12,6 +12,7 @@ import android.support.annotation.UiThread; import android.util.Log; import android.view.Surface; +import android.opengl.EGLContext; import java.nio.ByteBuffer; import java.util.HashSet; @@ -409,7 +410,25 @@ public void unregisterTexture(long textureId) { } private native void nativeUnregisterTexture(long nativePlatformViewId, long textureId); - //------- End from FlutterView ----- + + @UiThread + public void registerGLTexture(long texIndex, long textureId) { + ensureAttachedToNative(); + nativeGLRegisterTexture(nativePlatformViewId, texIndex, textureId); + } + + private native void nativeGLRegisterTexture(long nativePlatformViewId, long texIndex, long textureId); + + @UiThread + public EGLContext getGLContext() { + ensureAttachedToNative(); + return nativeGetContext(nativePlatformViewId); + } + + private native EGLContext nativeGetContext(long nativePlatformViewId); + + + //------- End from FlutterView ----- // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) //------ Start from FlutterNativeView ---- diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 2dfac4c67e826..ee9421eebdfa6 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -11,6 +11,7 @@ import android.os.Handler; import android.support.annotation.NonNull; import android.view.Surface; +import android.opengl.EGLContext; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicLong; @@ -75,6 +76,32 @@ public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedList //------ START TextureRegistry IMPLEMENTATION ----- // TODO(mattcarroll): detachFromGLContext requires API 16. Create solution for earlier APIs. @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public long registerGLTexture(long textureID) { + long texIndex = nextTextureId.getAndIncrement(); + registerGLTexture(texIndex,textureID); + return texIndex; + } + + @Override + public void onGLFrameAvaliable(int textureIndex) { + markTextureFrameAvailable(textureIndex); + } + + @Override + public TextureRegistry.GLTextureEntry createGLTexture(long textureID) { + long texIndex = nextTextureId.getAndIncrement(); + final GLTextureRegistryEntry entry = new GLTextureRegistryEntry(texIndex,textureID); + registerGLTexture(texIndex,textureID); + return entry; + } + + @Override + public EGLContext getGLContext() { + return flutterJNI.getGLContext(); + } + + @Override public SurfaceTextureEntry createSurfaceTexture() { final SurfaceTexture surfaceTexture = new SurfaceTexture(0); @@ -89,9 +116,10 @@ public SurfaceTextureEntry createSurfaceTexture() { final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { private final long id; - private final SurfaceTexture surfaceTexture; + private SurfaceTexture surfaceTexture; private boolean released; - + private boolean suspended; + SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) { this.id = id; this.surfaceTexture = surfaceTexture; @@ -141,7 +169,98 @@ public void release() { unregisterTexture(id); released = true; } + + @Override + public void suspend() { + if (released) { + return; + } + if (suspended) { + return; + } + + suspended = true; + unregisterTexture(id); + // Otherwise onFrameAvailableListener might be called after mNativeView==null + // (https://github.com/flutter/flutter/issues/20951). See also the check in + // onFrameAvailable. + surfaceTexture.setOnFrameAvailableListener(null); + surfaceTexture.release(); + surfaceTexture = null; + } + + @Override + public void resume() { + if(released) return; + if(!suspended) return; + suspended = false; + if(surfaceTexture == null){ + surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // The callback relies on being executed on the UI thread (unsynchronised read of mNativeView + // and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable), + // so we explicitly pass a Handler for the current thread. + surfaceTexture.setOnFrameAvailableListener(onFrameListener, new Handler()); + } else { + // Android documentation states that the listener can be called on an arbitrary thread. + // But in practice, versions of Android that predate the newer API will call the listener + // on the thread where the SurfaceTexture was constructed. + surfaceTexture.setOnFrameAvailableListener(onFrameListener); + } + } + registerTexture(id, surfaceTexture); + } + } + + final class GLTextureRegistryEntry implements TextureRegistry.GLTextureEntry { + private final long id; + private final long textureID; + private boolean released; + private boolean suspended; + GLTextureRegistryEntry(long id, long textureID) { + this.id = id; + this.textureID = textureID; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + if (suspended) { + return; + } + released = true; + registerGLTexture(id,textureID); + } + + @Override + public void suspend() { + if (released) { + return; + } + if (suspended) { + return; + } + suspended = true; + unregisterTexture(id); + } + + @Override + public void resume(long textureID) { + if(released) return; + if(!suspended) return; + suspended = false; + registerGLTexture(id,textureID); + } } + //------ END TextureRegistry IMPLEMENTATION ---- // TODO(mattcarroll): describe the native behavior that this invokes @@ -201,6 +320,10 @@ private void unregisterTexture(long textureId) { flutterJNI.unregisterTexture(textureId); } + private void registerGLTexture(long texIndex, long textureId) { + flutterJNI.registerGLTexture(texIndex, textureId); + } + // TODO(mattcarroll): describe the native behavior that this invokes public boolean isSoftwareRenderingEnabled() { return FlutterJNI.nativeGetIsSoftwareRenderingEnabled(); diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index a589f54d4ca5d..c91c0c43e50a9 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -506,7 +506,9 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { // For platform views we delegate the node creation to the accessibility view embedder. View embeddedView = platformViewsAccessibilityDelegate.getPlatformViewById(semanticsNode.platformViewId); Rect bounds = semanticsNode.getGlobalRect(); - return accessibilityViewEmbedder.getRootNode(embeddedView, semanticsNode.id, bounds); + if (embeddedView!=null && bounds!=null) { + return accessibilityViewEmbedder.getRootNode(embeddedView, semanticsNode.id, bounds); + } } AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(rootAccessibilityView, virtualViewId); @@ -1116,11 +1118,14 @@ public boolean onAccessibilityHoverEvent(MotionEvent event) { return false; } - SemanticsNode semanticsNodeUnderCursor = getRootSemanticsNode().hitTest(new float[] {event.getX(), event.getY(), 0, 1}); - if (semanticsNodeUnderCursor.platformViewId != -1) { - return accessibilityViewEmbedder.onAccessibilityHoverEvent(semanticsNodeUnderCursor.id, event); + if (getRootSemanticsNode()!=null){ + SemanticsNode semanticsNodeUnderCursor = getRootSemanticsNode().hitTest(new float[] {event.getX(), event.getY(), 0, 1}); + if (semanticsNodeUnderCursor.platformViewId != -1) { + return accessibilityViewEmbedder.onAccessibilityHoverEvent(semanticsNodeUnderCursor.id, event); + } } + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { handleTouchExploration(event.getX(), event.getY()); } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { @@ -1441,7 +1446,9 @@ private void sendAccessibilityEvent(@NonNull AccessibilityEvent event) { return; } // TODO(mattcarroll): why are we explicitly talking to the root view's parent? - rootAccessibilityView.getParent().requestSendAccessibilityEvent(rootAccessibilityView, event); + if (rootAccessibilityView.getParent()!=null) { + rootAccessibilityView.getParent().requestSendAccessibilityEvent(rootAccessibilityView, event); + } } /** @@ -2015,8 +2022,9 @@ private void updateRecursively(float[] ancestorTransform, Set vis if (globalTransform == null) { globalTransform = new float[16]; } - Matrix.multiplyMM(globalTransform, 0, ancestorTransform, 0, transform, 0); - + if (globalTransform!=null && ancestorTransform!=null && transform!=null) { + Matrix.multiplyMM(globalTransform, 0, ancestorTransform, 0, transform, 0); + } final float[] sample = new float[4]; sample[2] = 0; sample[3] = 1; diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 075a828f2e294..740bf695a84d6 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -26,6 +26,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.opengl.EGLContext; import io.flutter.app.FlutterPluginRegistry; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.android.AndroidKeyProcessor; @@ -751,77 +752,188 @@ public interface FirstFrameListener { void onFirstFrame(); } - @Override - public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() { - final SurfaceTexture surfaceTexture = new SurfaceTexture(0); - surfaceTexture.detachFromGLContext(); - final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), - surfaceTexture); - mNativeView.getFlutterJNI().registerTexture(entry.id(), surfaceTexture); - return entry; - } - - final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { - private final long id; - private final SurfaceTexture surfaceTexture; - private boolean released; - - SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) { - this.id = id; - this.surfaceTexture = surfaceTexture; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // The callback relies on being executed on the UI thread (unsynchronised read of mNativeView - // and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable), - // so we explicitly pass a Handler for the current thread. - this.surfaceTexture.setOnFrameAvailableListener(onFrameListener, new Handler()); - } else { - // Android documentation states that the listener can be called on an arbitrary thread. - // But in practice, versions of Android that predate the newer API will call the listener - // on the thread where the SurfaceTexture was constructed. - this.surfaceTexture.setOnFrameAvailableListener(onFrameListener); - } + @Override + public long registerGLTexture(long textureID) { + long texIndex = nextTextureId.getAndIncrement(); + mNativeView.getFlutterJNI().registerGLTexture(texIndex,textureID); + return texIndex; + } + + @Override + public void onGLFrameAvaliable(int textureIndex) { + mNativeView.getFlutterJNI().markTextureFrameAvailable(textureIndex); + } + + @Override + public TextureRegistry.GLTextureEntry createGLTexture(long textureID) { + long texIndex = nextTextureId.getAndIncrement(); + final GLTextureRegistryEntry entry = new GLTextureRegistryEntry(texIndex,textureID); + mNativeView.getFlutterJNI().registerGLTexture(texIndex,textureID); + return entry; + } + + @Override + public EGLContext getGLContext() { + return mNativeView.getFlutterJNI().getGLContext(); + } + + + @Override + public SurfaceTextureEntry createSurfaceTexture() { + final SurfaceTexture surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry( + nextTextureId.getAndIncrement(), + surfaceTexture + ); + mNativeView.getFlutterJNI().registerTexture(entry.id(), surfaceTexture); + return entry; + } + + final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { + private final long id; + private SurfaceTexture surfaceTexture; + private boolean released; + private boolean suspended; + + SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) { + this.id = id; + this.surfaceTexture = surfaceTexture; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // The callback relies on being executed on the UI thread (unsynchronised read of mNativeView + // and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable), + // so we explicitly pass a Handler for the current thread. + this.surfaceTexture.setOnFrameAvailableListener(onFrameListener, new Handler()); + } else { + // Android documentation states that the listener can be called on an arbitrary thread. + // But in practice, versions of Android that predate the newer API will call the listener + // on the thread where the SurfaceTexture was constructed. + this.surfaceTexture.setOnFrameAvailableListener(onFrameListener); + } + } + + private SurfaceTexture.OnFrameAvailableListener onFrameListener = new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture texture) { + if (released) { + // Even though we make sure to unregister the callback before releasing, as of Android O + // SurfaceTexture has a data race when accessing the callback, so the callback may + // still be called by a stale reference after released==true and mNativeView==null. + return; } + mNativeView.getFlutterJNI().markTextureFrameAvailable(id); + } + }; - private SurfaceTexture.OnFrameAvailableListener onFrameListener = new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture texture) { - if (released || mNativeView == null) { - // Even though we make sure to unregister the callback before releasing, as of Android O - // SurfaceTexture has a data race when accessing the callback, so the callback may - // still be called by a stale reference after released==true and mNativeView==null. - return; - } - mNativeView.getFlutterJNI().markTextureFrameAvailable(SurfaceTextureRegistryEntry.this.id); - } - }; - - @Override - public SurfaceTexture surfaceTexture() { - return surfaceTexture; - } + @Override + public SurfaceTexture surfaceTexture() { + return surfaceTexture; + } - @Override - public long id() { - return id; - } + @Override + public long id() { + return id; + } - @Override - public void release() { - if (released) { - return; - } - released = true; + @Override + public void release() { + if (released) { + return; + } + surfaceTexture.release(); + mNativeView.getFlutterJNI().unregisterTexture(id); + released = true; + } - // The ordering of the next 3 calls is important: - // First we remove the frame listener, then we release the SurfaceTexture, and only after we unregister - // the texture which actually deletes the GL texture. + @Override + public void suspend() { + if (released) { + return; + } + if (suspended) { + return; + } + + suspended = true; + mNativeView.getFlutterJNI().unregisterTexture(id); + // Otherwise onFrameAvailableListener might be called after mNativeView==null + // (https://github.com/flutter/flutter/issues/20951). See also the check in + // onFrameAvailable. + surfaceTexture.setOnFrameAvailableListener(null); + surfaceTexture.release(); + surfaceTexture = null; + } - // Otherwise onFrameAvailableListener might be called after mNativeView==null - // (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable. - surfaceTexture.setOnFrameAvailableListener(null); - surfaceTexture.release(); - mNativeView.getFlutterJNI().unregisterTexture(id); + @Override + public void resume() { + if(released) return; + if(!suspended) return; + suspended = false; + if(surfaceTexture == null){ + surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // The callback relies on being executed on the UI thread (unsynchronised read of mNativeView + // and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable), + // so we explicitly pass a Handler for the current thread. + surfaceTexture.setOnFrameAvailableListener(onFrameListener, new Handler()); + } else { + // Android documentation states that the listener can be called on an arbitrary thread. + // But in practice, versions of Android that predate the newer API will call the listener + // on the thread where the SurfaceTexture was constructed. + surfaceTexture.setOnFrameAvailableListener(onFrameListener); } + } + mNativeView.getFlutterJNI().registerTexture(id, surfaceTexture); + } + } + + final class GLTextureRegistryEntry implements TextureRegistry.GLTextureEntry { + private final long id; + private final long textureID; + private boolean released; + private boolean suspended; + GLTextureRegistryEntry(long id, long textureID) { + this.id = id; + this.textureID = textureID; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + if (suspended) { + return; + } + released = true; + mNativeView.getFlutterJNI().registerGLTexture(id,textureID); + } + + @Override + public void suspend() { + if (released) { + return; + } + if (suspended) { + return; + } + suspended = true; + mNativeView.getFlutterJNI().unregisterTexture(id); + } + + @Override + public void resume(long textureID) { + if(released) return; + if(!suspended) return; + suspended = false; + mNativeView.getFlutterJNI().registerGLTexture(id,textureID); } + } } diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index ed3134bb035dd..1b1045f01a4d9 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -5,6 +5,7 @@ package io.flutter.view; import android.graphics.SurfaceTexture; +import android.opengl.EGLContext; /** * Registry of backend textures used with a single {@link FlutterView} instance. @@ -19,6 +20,42 @@ public interface TextureRegistry { * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); + + void onGLFrameAvaliable(int index); + + long registerGLTexture(long textureID); + + EGLContext getGLContext(); + + GLTextureEntry createGLTexture(long textureID); + + /** + * A registry entry for a managed SurfaceTexture. + */ + interface GLTextureEntry { + + /** + * @return The identity of this SurfaceTexture. + */ + long id(); + + /** + * Deregisters and releases this SurfaceTexture. + */ + void release(); + + /** + * suspend the SurfaceTexture + * this will unregister the texture and release the existed surfaceeTexture,but + * the id will be reserved + */ + void suspend(); + + /** + * this will create a new SurfaceTexture, and register texture + */ + void resume(long textureID); + } /** * A registry entry for a managed SurfaceTexture. @@ -38,5 +75,18 @@ interface SurfaceTextureEntry { * Deregisters and releases this SurfaceTexture. */ void release(); + + /** + * suspend the SurfaceTexture + * this will unregister the texture and release the existed surfaceeTexture,but + * the id will be reserved + */ + void suspend(); + + /** + * this will create a new SurfaceTexture, and register texture + */ + void resume(); + } } diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 449a7e4571fba..3e1f773b02da7 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -375,6 +375,17 @@ void PlatformViewAndroid::RegisterExternalTexture( RegisterTexture( std::make_shared(texture_id, surface_texture)); } + +void PlatformViewAndroid::RegisterGLExternalTexture(int64_t texture_index, + int64_t texture_id) { + RegisterTexture( + std::make_shared(texture_index, texture_id)); +} + +void* PlatformViewAndroid::GetContext() { + return android_surface_->GetContext(); +} + // |PlatformView| std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 74c0bbe6c26e7..572b021d515e2 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -73,6 +73,10 @@ class PlatformViewAndroid final : public PlatformView { int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture); + void RegisterGLExternalTexture(int64_t texture_index, int64_t texture_id); + + void* GetContext(); + private: const fml::jni::JavaObjectWeakGlobalRef java_object_; const std::unique_ptr android_surface_; diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index 38db3453cef00..28e9e13c552e1 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -23,6 +23,9 @@ #include "flutter/shell/platform/android/android_shell_holder.h" #include "flutter/shell/platform/android/apk_asset_provider.h" #include "flutter/shell/platform/android/flutter_main.h" +#include +#include +#include #define ANDROID_SHELL_HOLDER \ (reinterpret_cast(shell_holder)) @@ -494,6 +497,49 @@ static void RegisterTexture(JNIEnv* env, ); } +static void RegisterGLTexture(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jlong texture_index, + jlong texture_id) { + ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterGLExternalTexture( + static_cast(texture_index), static_cast(texture_id)); +} + +static jobject GetContext(JNIEnv* env, jobject jcaller, jlong shell_holder) { + + void* cxt = ANDROID_SHELL_HOLDER->GetPlatformView()->GetContext(); + + + jclass versionClass = env->FindClass("android/os/Build$VERSION" ); + jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I" ); + int sdkInt = env->GetStaticIntField(versionClass, sdkIntFieldID ); + __android_log_print(ANDROID_LOG_ERROR, "andymao", "sdkInt %d",sdkInt); + jclass eglcontextClassLocal = env->FindClass("android/opengl/EGLContext"); + jmethodID eglcontextConstructor; + jobject eglContext; + if (sdkInt >= 21) { + //5.0and above + eglcontextConstructor=env->GetMethodID(eglcontextClassLocal, "", "(J)V"); + if ((EGLContext)cxt == EGL_NO_CONTEXT) { + return env->NewObject(eglcontextClassLocal, eglcontextConstructor, + reinterpret_cast(EGL_NO_CONTEXT)); + } + eglContext = env->NewObject(eglcontextClassLocal, eglcontextConstructor, + reinterpret_cast(jlong(cxt))); + }else{ + eglcontextConstructor=env->GetMethodID(eglcontextClassLocal, "", "(I)V"); + if ((EGLContext)cxt == EGL_NO_CONTEXT) { + return env->NewObject(eglcontextClassLocal, eglcontextConstructor, + reinterpret_cast(EGL_NO_CONTEXT)); + } + eglContext = env->NewObject(eglcontextClassLocal, eglcontextConstructor, + reinterpret_cast(jint(cxt))); + } + + return eglContext; +} + static void MarkTextureFrameAvailable(JNIEnv* env, jobject jcaller, jlong shell_holder, @@ -637,6 +683,11 @@ bool RegisterApi(JNIEnv* env) { .signature = "(JJLandroid/graphics/SurfaceTexture;)V", .fnPtr = reinterpret_cast(&RegisterTexture), }, + { + .name = "nativeGLRegisterTexture", + .signature = "(JJJ)V", + .fnPtr = reinterpret_cast(&RegisterGLTexture), + }, { .name = "nativeMarkTextureFrameAvailable", .signature = "(JJ)V", @@ -647,6 +698,11 @@ bool RegisterApi(JNIEnv* env) { .signature = "(JJ)V", .fnPtr = reinterpret_cast(&UnregisterTexture), }, + { + .name = "nativeGetContext", + .signature = "(J)Landroid/opengl/EGLContext;", + .fnPtr = reinterpret_cast(&GetContext), + }, }; if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h b/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h index e7cd01337deb9..4abccee96d3da 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h @@ -7,6 +7,7 @@ #import #import +#import #include "FlutterMacros.h" @@ -15,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN FLUTTER_EXPORT @protocol FlutterTexture - (CVPixelBufferRef _Nullable)copyPixelBuffer; +- (GLuint)copyTextureID; @end FLUTTER_EXPORT @@ -22,6 +24,7 @@ FLUTTER_EXPORT - (int64_t)registerTexture:(NSObject*)texture; - (void)textureFrameAvailable:(int64_t)textureId; - (void)unregisterTexture:(int64_t)textureId; +- (EAGLSharegroup*)glShareGroup; @end NS_ASSUME_NONNULL_END diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 50208e65a08d4..b2d2ff7c9459c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -527,6 +527,9 @@ - (void)setMessageHandlerOnChannel:(NSString*)channel } #pragma mark - FlutterTextureRegistry +- (EAGLSharegroup*)glShareGroup { + return (EAGLSharegroup*)(self.iosPlatformView -> GetGLShareGroup()); +} - (int64_t)registerTexture:(NSObject*)texture { int64_t textureId = _nextTextureId++; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 61b5db9c56f75..7c29b4d7ee6df 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -64,11 +64,11 @@ - (void)layoutSubviews { } + (Class)layerClass { -#if TARGET_IPHONE_SIMULATOR - return [CALayer class]; -#else // TARGET_IPHONE_SIMULATOR +// #if TARGET_IPHONE_SIMULATOR +// return [CALayer class]; +// #else // TARGET_IPHONE_SIMULATOR return [CAEAGLLayer class]; -#endif // TARGET_IPHONE_SIMULATOR +// #endif // TARGET_IPHONE_SIMULATOR } - (std::unique_ptr)createSurface: diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 0f987d42e40f7..4facc68807c66 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -8,9 +8,10 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include - +#include "flutter/common/task_runners.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/message_loop.h" +#include "flutter/fml/message_loop_impl.h" #include "flutter/fml/platform/darwin/platform_version.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/thread_host.h" @@ -23,6 +24,7 @@ #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemanticsUpdate"; +static double kTouchTrackerCheckInterval = 1.f; @implementation FlutterViewController { std::unique_ptr> _weakFactory; @@ -40,6 +42,8 @@ @implementation FlutterViewController { BOOL _viewOpaque; BOOL _engineNeedsLaunch; NSMutableSet* _ongoingTouches; + NSMutableSet* _touchTrackerSet; + NSMutableDictionary* _touchTrackerDict; } #pragma mark - Manage and override all designated initializers @@ -119,6 +123,15 @@ - (void)performCommonViewControllerInitialization { _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; + _touchTrackerSet = [[NSMutableSet set] retain]; + _touchTrackerDict = [[NSMutableDictionary dictionary] retain]; + + fml::MessageLoopImpl* gpuLoop = + ((fml::TaskRunner*)[self getTaskRunners].GetGPUTaskRunner().get())->getMessageLoop(); + gpuLoop->SetTaskLimitPerLoopRun(10); + fml::MessageLoopImpl* ioLoop = + ((fml::TaskRunner*)[self getTaskRunners].GetIOTaskRunner().get())->getMessageLoop(); + ioLoop->SetTaskLimitPerLoopRun(10); [self setupNotificationCenterObservers]; } @@ -379,6 +392,7 @@ - (void)setFlutterViewDidRenderCallback:(void (^)(void))callback { - (void)surfaceUpdated:(BOOL)appeared { // NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and GPU thread. + [self setEnableForRunnersBatch:YES]; if (appeared) { [self installSplashScreenViewCallback]; [_engine.get() platformViewsController] -> SetFlutterView(_flutterView.get()); @@ -386,7 +400,59 @@ - (void)surfaceUpdated:(BOOL)appeared { } else { [_engine.get() platformView] -> NotifyDestroyed(); [_engine.get() platformViewsController] -> SetFlutterView(nullptr); + [self disableGPUOperation]; + } +} + + +- (flutter::TaskRunners)getTaskRunners { + return ([_engine.get() shell].GetTaskRunners()); +} + +- (void)disableGPUOperation { + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; + //暂时通过延时来等待GL操作结束(否则进入后台后的GL操作会闪退) + NSDate* date = [NSDate date]; + double delayMax = 8; //最多等8S + fml::MessageLoopImpl* gpuLoop = + ((fml::TaskRunner*)[self getTaskRunners].GetGPUTaskRunner().get())->getMessageLoop(); + fml::MessageLoopImpl* ioLoop = + ((fml::TaskRunner*)[self getTaskRunners].GetIOTaskRunner().get())->getMessageLoop(); + while (true) { + //两个TaskRunner没内容了,好,可以退出 + if (!gpuLoop->IsRunningingExpiredTasks() && !ioLoop->IsRunningingExpiredTasks()) + break; + //超时退出 + if ([[NSDate date] timeIntervalSinceDate:date] > delayMax) + break; + [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.2]]; } + [self setEnableForRunnersBatch:FALSE]; +} + +- (void)enableMessageLoop:(bool)isEnable forTaskRunner:(NSString*)aTaskRunnerId { + if ([@"io.flutter.io" caseInsensitiveCompare:aTaskRunnerId] == NSOrderedSame) { + fml::TaskRunner* taskRunner = (fml::TaskRunner*)[self getTaskRunners].GetIOTaskRunner().get(); + taskRunner->EnableMessageLoop(isEnable); + } + if ([@"io.flutter.ui" caseInsensitiveCompare:aTaskRunnerId] == NSOrderedSame) { + fml::TaskRunner* taskRunner = (fml::TaskRunner*)[self getTaskRunners].GetUITaskRunner().get(); + taskRunner->EnableMessageLoop(isEnable); + } + if ([@"io.flutter.gpu" caseInsensitiveCompare:aTaskRunnerId] == NSOrderedSame) { + fml::TaskRunner* taskRunner = (fml::TaskRunner*)[self getTaskRunners].GetGPUTaskRunner().get(); + taskRunner->EnableMessageLoop(isEnable); + } + if ([@"io.flutter.platform" caseInsensitiveCompare:aTaskRunnerId] == NSOrderedSame) { + fml::TaskRunner* taskRunner = + (fml::TaskRunner*)[self getTaskRunners].GetPlatformTaskRunner().get(); + taskRunner->EnableMessageLoop(isEnable); + } +} + +- (void)setEnableForRunnersBatch:(BOOL)enable { + [self enableMessageLoop:enable forTaskRunner:@"io.flutter.gpu"]; + [self enableMessageLoop:enable forTaskRunner:@"io.flutter.io"]; } #pragma mark - UIViewController lifecycle notifications @@ -471,6 +537,8 @@ - (void)flushOngoingTouches { - (void)dealloc { [_engine.get() notifyViewControllerDeallocated]; [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_touchTrackerSet release]; + [_touchTrackerDict release]; [super dealloc]; } @@ -499,6 +567,8 @@ - (void)applicationWillEnterForeground:(NSNotification*)notification { [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; } + + #pragma mark - Touch event handling static flutter::PointerData::Change PointerDataChangeFromUITouchPhase(UITouchPhase phase) { @@ -540,21 +610,44 @@ - (void)applicationWillEnterForeground:(NSNotification*)notification { // in the status bar area are available to framework code. The change type (optional) of the faked // touch is specified in the second argument. - (void)dispatchTouches:(NSSet*)touches - pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change { + pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change + trackTouches:(BOOL)bTrack { const CGFloat scale = [UIScreen mainScreen].scale; auto packet = std::make_unique(touches.count); - + NSTimeInterval tsNow = [[NSDate date] timeIntervalSinceReferenceDate]; size_t pointer_index = 0; for (UITouch* touch in touches) { CGPoint windowCoordinates = [touch locationInView:self.view]; + UITouchPhase phase = touch.phase; + NSValue* key = [NSValue valueWithPointer:(void*)touch]; flutter::PointerData pointer_data; pointer_data.Clear(); + switch (phase) { + case UITouchPhaseBegan: + case UITouchPhaseMoved: + case UITouchPhaseStationary: + if (bTrack) { + [_touchTrackerSet addObject:touch]; + _touchTrackerDict[key] = @(tsNow + kTouchTrackerCheckInterval); + } + break; + case UITouchPhaseEnded: + case UITouchPhaseCancelled: + if (bTrack) { + [_touchTrackerDict removeObjectForKey:key]; + [_touchTrackerSet removeObject:touch]; + } + break; + default: + break; + } + constexpr int kMicrosecondsPerSecond = 1000 * 1000; pointer_data.time_stamp = touch.timestamp * kMicrosecondsPerSecond; - + pointer_data.change = overridden_change != nullptr ? *overridden_change : PointerDataChangeFromUITouchPhase(touch.phase); @@ -647,19 +740,45 @@ - (void)dispatchTouches:(NSSet*)touches } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches pointerDataChangeOverride:nullptr]; + [self dispatchTouches:touches pointerDataChangeOverride:nullptr trackTouches:TRUE]; + [self checkIfCompleteTouches]; } - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches pointerDataChangeOverride:nullptr]; + [self dispatchTouches:touches pointerDataChangeOverride:nullptr trackTouches:TRUE]; } - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches pointerDataChangeOverride:nullptr]; + [self dispatchTouches:touches pointerDataChangeOverride:nullptr trackTouches:TRUE]; } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches pointerDataChangeOverride:nullptr]; + [self dispatchTouches:touches pointerDataChangeOverride:nullptr trackTouches:TRUE]; +} + +- (BOOL)checkIfCompleteTouches { + NSInteger cnt = _touchTrackerSet.count; + if (cnt <= 0) + return FALSE; + NSTimeInterval tsNow = [[NSDate date] timeIntervalSinceReferenceDate]; + NSSet* tmpTrackingTouches = [_touchTrackerSet copy]; + NSMutableSet* set = [NSMutableSet set]; + for (UITouch* touch in tmpTrackingTouches) { + NSValue* key = [NSValue valueWithPointer:(void*)touch]; + NSNumber* expiredTime = [_touchTrackerDict objectForKey:key]; + if (expiredTime.doubleValue <= tsNow) { + [touch setValue:@(UITouchPhaseEnded) forKey:@"phase"]; + [set addObject:touch]; + [_touchTrackerDict removeObjectForKey:key]; + [_touchTrackerSet removeObject:touch]; + } + } + if (set.count > 0) { + [self dispatchTouches:set pointerDataChangeOverride:nullptr trackTouches:FALSE]; + [self dispatchTouches:set pointerDataChangeOverride:nullptr trackTouches:FALSE]; + return TRUE; + } + return FALSE; } #pragma mark - Handle view resizing @@ -955,9 +1074,9 @@ - (void)handleStatusBarTouches:(UIEvent*)event { NSSet* statusbarTouches = [NSSet setWithObject:touch]; flutter::PointerData::Change change = flutter::PointerData::Change::kDown; - [self dispatchTouches:statusbarTouches pointerDataChangeOverride:&change]; + [self dispatchTouches:statusbarTouches pointerDataChangeOverride:&change trackTouches:TRUE]; change = flutter::PointerData::Change::kUp; - [self dispatchTouches:statusbarTouches pointerDataChangeOverride:&change]; + [self dispatchTouches:statusbarTouches pointerDataChangeOverride:&change trackTouches:TRUE]; return; } } @@ -1016,6 +1135,9 @@ - (void)setMessageHandlerOnChannel:(NSString*)channel } #pragma mark - FlutterTextureRegistry +- (EAGLSharegroup*)glShareGroup { + return [_engine.get() glShareGroup]; +} - (int64_t)registerTexture:(NSObject*)texture { return [_engine.get() registerTexture:texture]; diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.mm b/shell/platform/darwin/ios/ios_external_texture_gl.mm index ff0be31447d40..067817e8681d4 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -26,39 +26,48 @@ const SkRect& bounds, bool freeze, GrContext* context) { - if (!cache_ref_) { - CVOpenGLESTextureCacheRef cache; - CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, - [EAGLContext currentContext], NULL, &cache); - if (err == noErr) { - cache_ref_.Reset(cache); + GrGLTextureInfo textureInfo; + if ([external_texture_ respondsToSelector:@selector(copyTextureID)]) { + textureInfo.fFormat = GL_RGBA8_OES; + textureInfo.fID = [external_texture_ copyTextureID]; + textureInfo.fTarget = GL_TEXTURE_2D; } else { - FML_LOG(WARNING) << "Failed to create GLES texture cache: " << err; - return; + if (!cache_ref_) { + CVOpenGLESTextureCacheRef cache; + CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, + [EAGLContext currentContext], NULL, &cache); + if (err == noErr) { + cache_ref_.Reset(cache); + } else { + FML_LOG(WARNING) << "Failed to create GLES texture cache: " << err; + return; + } + } + if (!freeze) { + fml::CFRef bufferRef; + bufferRef.Reset([external_texture_ copyPixelBuffer]); + if (bufferRef != nullptr) { + CVOpenGLESTextureRef texture; + CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage( + kCFAllocatorDefault, cache_ref_, bufferRef, nullptr, GL_TEXTURE_2D, GL_RGBA, + static_cast(CVPixelBufferGetWidth(bufferRef)), + static_cast(CVPixelBufferGetHeight(bufferRef)), GL_BGRA, GL_UNSIGNED_BYTE, 0, + &texture); + texture_ref_.Reset(texture); + if (err != noErr) { + FML_LOG(WARNING) << "Could not create texture from pixel buffer: " << err; + return; + } + } + } + + if (!texture_ref_) { + return; + } + textureInfo.fFormat = GL_RGBA8_OES; + textureInfo.fID = CVOpenGLESTextureGetName(texture_ref_); + textureInfo.fTarget = CVOpenGLESTextureGetTarget(texture_ref_); } - } - fml::CFRef bufferRef; - if (!freeze) { - bufferRef.Reset([external_texture_ copyPixelBuffer]); - if (bufferRef != nullptr) { - CVOpenGLESTextureRef texture; - CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage( - kCFAllocatorDefault, cache_ref_, bufferRef, nullptr, GL_TEXTURE_2D, GL_RGBA, - static_cast(CVPixelBufferGetWidth(bufferRef)), - static_cast(CVPixelBufferGetHeight(bufferRef)), GL_BGRA, GL_UNSIGNED_BYTE, 0, - &texture); - texture_ref_.Reset(texture); - if (err != noErr) { - FML_LOG(WARNING) << "Could not create texture from pixel buffer: " << err; - return; - } - } - } - if (!texture_ref_) { - return; - } - GrGLTextureInfo textureInfo = {CVOpenGLESTextureGetTarget(texture_ref_), - CVOpenGLESTextureGetName(texture_ref_), GL_RGBA8_OES}; GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, textureInfo); sk_sp image = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 232645d9c8592..f8f60af8f57ce 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -30,6 +30,8 @@ class IOSGLContext { bool ResourceMakeCurrent(); + void* GetGLShareGroup(); + sk_sp ColorSpace() const { return color_space_; } private: diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 52fb85f8f19a9..be60021de82f9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,9 +13,9 @@ namespace flutter { IOSGLContext::IOSGLContext() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:resource_context_.get().sharegroup]); } else { resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); @@ -59,5 +59,10 @@ bool IOSGLContext::ResourceMakeCurrent() { return [EAGLContext setCurrentContext:resource_context_.get()]; } + +void* IOSGLContext::GetGLShareGroup() { + EAGLSharegroup* shareGroup = context_.get().sharegroup; + return (void*)shareGroup; +} } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 8b065bb9ef83e..867891e99454a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -33,6 +33,8 @@ class IOSSurface { virtual std::unique_ptr CreateGPUSurface() = 0; + virtual void* GetGLShareGroup() = 0; + protected: FlutterPlatformViewsController* GetPlatformViewsController(); diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index e0a2f52e2d927..d5f178dac7999 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -40,6 +40,8 @@ class IOSSurfaceGL : public IOSSurface, bool GLContextMakeCurrent() override; + void* GetGLShareGroup() override; + bool GLContextClearCurrent() override; bool GLContextPresent() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index f9279cc5116fe..4c103f1ee8c93 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -50,6 +50,10 @@ return IsValid() ? render_target_->framebuffer() : GL_NONE; } +void* IOSSurfaceGL::GetGLShareGroup() { + return context_.get()->GetGLShareGroup(); +} + bool IOSSurfaceGL::UseOffscreenSurface() const { // The onscreen surface wraps a GL renderbuffer, which is extremely slow to read. // Certain filter effects require making a copy of the current destination, so we diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index bcb5b6210bf6b..567c876b4c075 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -35,7 +35,10 @@ class IOSSurfaceSoftware final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface() override; - + + // |IOSSurface| + void* GetGLShareGroup() override; + // |GPUSurfaceSoftwareDelegate| sk_sp AcquireBackingStore(const SkISize& size) override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 32cb5a2bbeef1..9598dd05a9abe 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -52,6 +52,10 @@ return surface; } +void* IOSSurfaceSoftware::GetGLShareGroup() { + return nullptr; +} + sk_sp IOSSurfaceSoftware::AcquireBackingStore(const SkISize& size) { TRACE_EVENT0("flutter", "IOSSurfaceSoftware::AcquireBackingStore"); if (!IsValid()) { diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index 38fc2a9f30b28..43ab6327e5750 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -37,6 +37,8 @@ class PlatformViewIOS final : public PlatformView { void RegisterExternalTexture(int64_t id, NSObject* texture); + void* GetGLShareGroup(); + fml::scoped_nsprotocol GetTextInputPlugin() const; void SetTextInputPlugin(fml::scoped_nsprotocol plugin); diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index c80ae377c03a1..b249884d2c915 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -70,6 +70,12 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), NSObject* texture) { RegisterTexture(std::make_shared(texture_id, texture)); } + +void* PlatformViewIOS::GetGLShareGroup() { + if (ios_surface_.get() == NULL) + return NULL; + return ios_surface_->GetGLShareGroup(); +} // |PlatformView| std::unique_ptr PlatformViewIOS::CreateRenderingSurface() {