diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 40c9989d69c1d..252f6a4c71fd8 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -540,6 +540,8 @@ FILE: ../../../flutter/shell/platform/android/android_environment_gl.h FILE: ../../../flutter/shell/platform/android/android_exports.lst FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.cc FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.h +FILE: ../../../flutter/shell/platform/android/android_external_texture_gl_share_context.cc +FILE: ../../../flutter/shell/platform/android/android_external_texture_gl_share_context.h FILE: ../../../flutter/shell/platform/android/android_native_window.cc FILE: ../../../flutter/shell/platform/android/android_native_window.h FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc @@ -780,6 +782,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_i FILE: ../../../flutter/shell/platform/darwin/ios/framework/module.modulemap FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl_share_context.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl_share_context.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 9151457d23100..61a627d1661b2 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -28,6 +28,8 @@ shared_library("flutter_shell_native") { "android_environment_gl.h", "android_external_texture_gl.cc", "android_external_texture_gl.h", + "android_external_texture_gl_share_context.cc", + "android_external_texture_gl_share_context.h", "android_native_window.cc", "android_native_window.h", "android_shell_holder.cc", diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index d3fd715a38abc..ece127988371e 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -272,4 +272,8 @@ bool AndroidContextGL::Resize(const SkISize& size) { return true; } +EGLContext AndroidContextGL::GetShareContext() { + return context_; +} + } // namespace flutter diff --git a/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index 50e15aba2ed4f..3c389b370ee0c 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); + EGLContext GetShareContext(); + private: fml::RefPtr environment_; fml::RefPtr window_; diff --git a/shell/platform/android/android_external_texture_gl_share_context.cc b/shell/platform/android/android_external_texture_gl_share_context.cc new file mode 100644 index 0000000000000..909a1534bad0f --- /dev/null +++ b/shell/platform/android/android_external_texture_gl_share_context.cc @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/android/android_external_texture_gl_share_context.h" + +#include + +#include "flutter/shell/platform/android/platform_view_android_jni.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" + +#include "flutter/fml/trace_event.h" + +namespace flutter { +AndroidExternalTextureShareContext::AndroidExternalTextureShareContext( + int64_t id, + int64_t shareTextureID) + : Texture(id), texture_id_(shareTextureID) {} + +AndroidExternalTextureShareContext::~AndroidExternalTextureShareContext() {} + +void AndroidExternalTextureShareContext::OnGrContextCreated() {} + +void AndroidExternalTextureShareContext::MarkNewFrameAvailable() {} + +void AndroidExternalTextureShareContext::Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) { + GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_id_, + GL_RGBA8_OES}; + + textureInfo.fTarget = GL_TEXTURE_2D; + + GrBackendTexture backendTexture(bounds.width(), bounds.height(), + GrMipMapped::kNo, textureInfo); + sk_sp image = SkImage::MakeFromTexture( + canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); + FML_DCHECK(image) << "Failed to create SkImage from Texture."; + if (image) { + canvas.drawImage(image, bounds.x(), bounds.y()); + } +} + +void AndroidExternalTextureShareContext::OnGrContextDestroyed() {} +} // namespace flutter diff --git a/shell/platform/android/android_external_texture_gl_share_context.h b/shell/platform/android/android_external_texture_gl_share_context.h new file mode 100644 index 0000000000000..26f3ef8b0c1e4 --- /dev/null +++ b/shell/platform/android/android_external_texture_gl_share_context.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_TEXTURE_SHARE_CONTEXT_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_TEXTURE_SHARE_CONTEXT_H_ + +#include +#include "flutter/flow/texture.h" +#include "flutter/fml/platform/android/jni_weak_ref.h" + +namespace flutter { + +// This is another solution for Flutter's ExternalTexture. +// The original ExternalTexture uses SurfaceTexture to update the frame data +// that native video object produces to an OpenGL texture. In this scheme, we +// directly pass an OpenGL texture ID to the ExternalTexture object, and avoid +// the performance consumption of data writing to SurfaceTexture +class AndroidExternalTextureShareContext : public flutter::Texture { + public: + AndroidExternalTextureShareContext(int64_t id, int64_t shareTextureID); + + ~AndroidExternalTextureShareContext() override; + + void Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) override; + + void OnGrContextCreated() override; + + void OnGrContextDestroyed() override; + + void MarkNewFrameAvailable() override; + + private: + fml::jni::JavaObjectWeakGlobalRef surface_texture_; + + GLuint texture_id_ = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(AndroidExternalTextureShareContext); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_TEXTURE_GL_H_ diff --git a/shell/platform/android/android_surface.h b/shell/platform/android/android_surface.h index 43f4cf33d0d50..bdef5043cd665 100644 --- a/shell/platform/android/android_surface.h +++ b/shell/platform/android/android_surface.h @@ -7,6 +7,7 @@ #include +#include #include "flutter/fml/macros.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/jni_weak_ref.h" @@ -36,6 +37,8 @@ class AndroidSurface { virtual bool ResourceContextClearCurrent() = 0; virtual bool SetNativeWindow(fml::RefPtr window) = 0; + + virtual EGLContext GetShareContext() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 737d9f293a518..4232ee7c65b01 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -130,4 +130,7 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } +EGLContext AndroidSurfaceGL::GetShareContext() { + return offscreen_context_->GetShareContext(); +} } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index d59302ad66509..d41e721f3c1b4 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -46,6 +46,9 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |AndroidSurface| bool SetNativeWindow(fml::RefPtr window) override; + // |AndroidSurface| + EGLContext GetShareContext() override; + // |GPUSurfaceGLDelegate| bool GLContextMakeCurrent() override; diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index 620e15301a581..f656212a849d9 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -159,4 +159,8 @@ bool AndroidSurfaceSoftware::SetNativeWindow( return true; } +EGLContext AndroidSurfaceSoftware::GetShareContext() { + return 0; +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_software.h b/shell/platform/android/android_surface_software.h index 30888091fe0d5..4af224d6be6eb 100644 --- a/shell/platform/android/android_surface_software.h +++ b/shell/platform/android/android_surface_software.h @@ -41,6 +41,9 @@ class AndroidSurfaceSoftware final : public AndroidSurface, // |AndroidSurface| bool SetNativeWindow(fml::RefPtr window) override; + // |AndroidSurface| + EGLContext GetShareContext() override; + // |GPUSurfaceSoftwareDelegate| sk_sp AcquireBackingStore(const SkISize& size) override; diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 3ab1745d44c9b..f2e5f656bfb11 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -16,6 +16,7 @@ import android.support.annotation.VisibleForTesting; import android.view.Surface; import android.view.SurfaceHolder; +import android.opengl.EGLContext; import java.nio.ByteBuffer; import java.util.Set; @@ -544,6 +545,23 @@ public void registerTexture(long textureId, @NonNull SurfaceTexture surfaceTextu private native void nativeRegisterTexture(long nativePlatformViewId, long textureId, @NonNull SurfaceTexture surfaceTexture); + + @UiThread + public void registerShareTexture(long texIndex, long shareTextureId) { + ensureAttachedToNative(); + nativeRegisterShareTexture(nativePlatformViewId, texIndex, shareTextureId); + } + + private native void nativeRegisterShareTexture(long nativePlatformViewId, long texIndex, long shareTextureId); + + @UiThread + public EGLContext getShareContext(long sdkInt) { + ensureAttachedToNative(); + return nativeGetShareContext(nativePlatformViewId, sdkInt); + } + + private native EGLContext nativeGetShareContext(long nativePlatformViewId, long sdkInt); + /** * Call this method to inform Flutter that a texture previously registered with * {@link #registerTexture(long, SurfaceTexture)} has a new frame available. 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 306535a547a26..56c9711ee99ff 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -12,6 +12,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.Surface; +import android.opengl.EGLContext; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicLong; @@ -110,6 +111,27 @@ public SurfaceTextureEntry createSurfaceTexture() { return entry; } + @Override + public TextureRegistry.ShareTextureEntry createShareTexture(long shareTextureID) { + final ShareTextureRegistryEntry entry = new ShareTextureRegistryEntry( + nextTextureId.getAndIncrement(), + shareTextureID + ); + Log.v(TAG, "New ShareTexture ID: " + entry.id()); + registerShareTexture(entry.id(), shareTextureID); + return entry; + } + + @Override + public void onShareFrameAvaliable(long textureId) { + markTextureFrameAvailable(textureId); + } + + @Override + public EGLContext getShareContext(long sdkInt) { + return flutterJNI.getShareContext(sdkInt); + } + final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { private final long id; @NonNull @@ -168,7 +190,33 @@ public void release() { released = true; } } - //------ END TextureRegistry IMPLEMENTATION ---- + + final class ShareTextureRegistryEntry implements TextureRegistry.ShareTextureEntry { + private final long id; + private final long textureID; + private boolean released; + ShareTextureRegistryEntry(long id, long shareTextureID) { + this.id = id; + this.textureID = shareTextureID; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + released = true; + unregisterTexture(id); + } + } + +//------ END TextureRegistry IMPLEMENTATION ---- + /** * Notifies Flutter that the given {@code surface} was created and is available for Flutter @@ -269,6 +317,10 @@ private void registerTexture(long textureId, @NonNull SurfaceTexture surfaceText flutterJNI.registerTexture(textureId, surfaceTexture); } + private void registerShareTexture(long textureId, long shareTextureID) { + flutterJNI.registerShareTexture(textureId, shareTextureID); + } + // TODO(mattcarroll): describe the native behavior that this invokes private void markTextureFrameAvailable(long textureId) { flutterJNI.markTextureFrameAvailable(textureId); diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index d1037170e1911..ae0831508181a 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -36,6 +36,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.opengl.EGLContext; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -779,6 +780,23 @@ public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() { return entry; } + @Override + public void onShareFrameAvaliable(long textureId) { + mNativeView.getFlutterJNI().markTextureFrameAvailable(textureId); + } + + @Override + public TextureRegistry.ShareTextureEntry createShareTexture(long shareTextureID) { + final ShareTextureRegistryEntry entry = new ShareTextureRegistryEntry(nextTextureId.getAndIncrement(), shareTextureID); + mNativeView.getFlutterJNI().registerShareTexture(entry.id(), shareTextureID); + return entry; + } + + @Override + public EGLContext getShareContext(long sdkInt) { + return mNativeView.getFlutterJNI().getShareContext(sdkInt); + } + final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { private final long id; private final SurfaceTexture surfaceTexture; @@ -842,4 +860,28 @@ public void release() { mNativeView.getFlutterJNI().unregisterTexture(id); } } + + final class ShareTextureRegistryEntry implements TextureRegistry.ShareTextureEntry { + private final long id; + private final long textureID; + private boolean released; + ShareTextureRegistryEntry(long id, long shareTextureID) { + this.id = id; + this.textureID = shareTextureID; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + released = true; + mNativeView.getFlutterJNI().unregisterTexture(id); + } + } } diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index 01c76e0335a97..f3d6f11a0bd44 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; // TODO(mattcarroll): re-evalute docs in this class and add nullability annotations. /** @@ -40,4 +41,43 @@ interface SurfaceTextureEntry { */ void release(); } + + /** + * Informs the the Flutter Engine that the external texture has been updated, and to start a new rendering pipeline. + * The paramater is the id of the ShareTextureEntry (Not OpenGL Texture ID) + */ + void onShareFrameAvaliable(long textureId); + + /** + * The OpenGL context created in the Flutter Engine, which is safe to user for sharing + * Param: Android SDK version code + */ + EGLContext getShareContext(long sdkInt); + + /** + * Create a ShareTextureEntry, and registers a External Texture in Flutter Engine + * The paramater is a OpenGL Texture ID + * Generally, this is used for users who process image frame with OpenGL(such as filter with GPUImage), and display the image frame using Flutter external texture + * Unlike SurfaceTexture, this can directly send the processed textures from OpenGL to flutter rendering pipeline. + * Avoid the performance consumption of data writing to SurfaceTexture + * Requirement: The OpenGL Texture should created in the EGLContext which is created using getShareContext(sdkInt) as a ShareContext + * @return A ShareTextureEntry. + */ + ShareTextureEntry createShareTexture(long shareTextureID); + + /** + * A registry entry for a managed ShareTexture. + */ + interface ShareTextureEntry { + + /** + * @return The identity of this ShareTexture. + */ + long id(); + + /** + * Deregisters and releases this ShareTexture. + */ + void release(); + } } diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 856fd9748ce90..32ae5582f7d3c 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -11,6 +11,7 @@ #include "flutter/shell/common/shell_io_manager.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" +#include "flutter/shell/platform/android/android_external_texture_gl_share_context.h" #include "flutter/shell/platform/android/android_surface_gl.h" #include "flutter/shell/platform/android/platform_message_response_android.h" #include "flutter/shell/platform/android/platform_view_android_jni.h" @@ -378,6 +379,17 @@ void PlatformViewAndroid::RegisterExternalTexture( std::make_shared(texture_id, surface_texture)); } +void PlatformViewAndroid::RegisterExternalShareTexture( + int64_t texture_id, + int64_t share_texture_id) { + RegisterTexture(std::make_shared( + texture_id, share_texture_id)); +} + +EGLContext PlatformViewAndroid::GetShareContext() { + return android_surface_->GetShareContext(); +} + // |PlatformView| std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { return std::make_unique(task_runners_); diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 74c0bbe6c26e7..7cf1a709b527b 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -10,6 +10,7 @@ #include #include +#include #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/platform/android/jni_weak_ref.h" #include "flutter/fml/platform/android/scoped_java_ref.h" @@ -73,6 +74,11 @@ class PlatformViewAndroid final : public PlatformView { int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture); + void RegisterExternalShareTexture(int64_t texture_id, + int64_t share_texture_id); + + EGLContext GetShareContext(); + 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 8b70d145dc439..d6eeefc798021 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -8,6 +8,8 @@ #include +#include +#include #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" #include "flutter/fml/file.h" @@ -444,6 +446,55 @@ static void RegisterTexture(JNIEnv* env, ); } +static void RegisterShareTexture(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jlong texture_id, + jlong share_texture_id) { + ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterExternalShareTexture( + static_cast(texture_id), // + static_cast(share_texture_id) // + ); +} + +static jobject GetShareContext(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jlong sdk_int) { + EGLContext cxt = ANDROID_SHELL_HOLDER->GetPlatformView()->GetShareContext(); + + jclass eglcontextClassLocal = env->FindClass("android/opengl/EGLContext"); + if (eglcontextClassLocal == nullptr) { + return nullptr; + } + jmethodID eglcontextConstructor; + jobject eglContext; + if (sdk_int >= 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"); + int contextAddress = (int)(size_t)cxt; + if ((EGLContext)cxt == EGL_NO_CONTEXT) { + contextAddress = (int)(size_t)EGL_NO_CONTEXT; + return env->NewObject(eglcontextClassLocal, eglcontextConstructor, + reinterpret_cast(jint(contextAddress))); + } + eglContext = env->NewObject(eglcontextClassLocal, eglcontextConstructor, + reinterpret_cast(jint(contextAddress))); + } + + return eglContext; +} + static void MarkTextureFrameAvailable(JNIEnv* env, jobject jcaller, jlong shell_holder, @@ -592,7 +643,16 @@ bool RegisterApi(JNIEnv* env) { .signature = "(JJ)V", .fnPtr = reinterpret_cast(&UnregisterTexture), }, - + { + .name = "nativeRegisterShareTexture", + .signature = "(JJJ)V", + .fnPtr = reinterpret_cast(&RegisterShareTexture), + }, + { + .name = "nativeGetShareContext", + .signature = "(JJ)Landroid/opengl/EGLContext;", + .fnPtr = reinterpret_cast(&GetShareContext), + }, // Methods for Dart callback functionality. { .name = "nativeLookupCallbackInformation", diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 6e00a098ee4ed..fe2fd44512e53 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -81,6 +81,8 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/vsync_waiter_ios.mm", "ios_external_texture_gl.h", "ios_external_texture_gl.mm", + "ios_external_texture_gl_share_context.h", + "ios_external_texture_gl_share_context.mm", "ios_gl_context.h", "ios_gl_context.mm", "ios_gl_render_target.h", diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h b/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h index e7cd01337deb9..5aec0fe2a80ee 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h @@ -8,11 +8,21 @@ #import #import +#import #include "FlutterMacros.h" NS_ASSUME_NONNULL_BEGIN FLUTTER_EXPORT +@protocol FlutterShareTexture +/* + *This is for users who implements this protocol to return the texture they create for image + *processing(such as GPUImage) to flutter rendering pipe line for display. The returned texture + *format should be GL_RGBA The returned texture target should be GL_TEXTURE_2D + */ +- (GLuint)copyShareTexture; +@end + @protocol FlutterTexture - (CVPixelBufferRef _Nullable)copyPixelBuffer; @end @@ -20,8 +30,10 @@ FLUTTER_EXPORT FLUTTER_EXPORT @protocol FlutterTextureRegistry - (int64_t)registerTexture:(NSObject*)texture; +- (int64_t)registerShareTexture:(NSObject*)texture; - (void)textureFrameAvailable:(int64_t)textureId; - (void)unregisterTexture:(int64_t)textureId; +- (EAGLSharegroup*)getShareGroup; @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 b2374fa0d147c..257d1dfa68916 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -564,6 +564,12 @@ - (int64_t)registerTexture:(NSObject*)texture { return textureId; } +- (int64_t)registerShareTexture:(NSObject*)texture { + int64_t textureId = _nextTextureId++; + self.iosPlatformView->RegisterExternalShareTexture(textureId, texture); + return textureId; +} + - (void)unregisterTexture:(int64_t)textureId { _shell->GetPlatformView()->UnregisterTexture(textureId); } @@ -572,6 +578,10 @@ - (void)textureFrameAvailable:(int64_t)textureId { _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); } +- (EAGLSharegroup*)getShareGroup { + return self.iosPlatformView->GetGLShareGroup(); +} + - (NSString*)lookupKeyForAsset:(NSString*)asset { return [FlutterDartProject lookupKeyForAsset:asset]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 87e605e97fd48..bf8e7a568bebc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1131,6 +1131,10 @@ - (int64_t)registerTexture:(NSObject*)texture { return [_engine.get() registerTexture:texture]; } +- (int64_t)registerShareTexture:(NSObject*)texture { + return [_engine.get() registerShareTexture:texture]; +} + - (void)unregisterTexture:(int64_t)textureId { [_engine.get() unregisterTexture:textureId]; } @@ -1139,6 +1143,10 @@ - (void)textureFrameAvailable:(int64_t)textureId { [_engine.get() textureFrameAvailable:textureId]; } +- (id)getShareGroup { + return [_engine.get() getShareGroup]; +} + - (NSString*)lookupKeyForAsset:(NSString*)asset { return [FlutterDartProject lookupKeyForAsset:asset]; } diff --git a/shell/platform/darwin/ios/ios_external_texture_gl_share_context.h b/shell/platform/darwin/ios/ios_external_texture_gl_share_context.h new file mode 100644 index 0000000000000..eb3925df05052 --- /dev/null +++ b/shell/platform/darwin/ios/ios_external_texture_gl_share_context.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_IOS_EXTERNAL_TEXTURE_SHARE_CONTEXT_H_ +#define FLUTTER_SHELL_PLATFORM_IOS_EXTERNAL_TEXTURE_SHARE_CONTEXT_H_ + +#include "flutter/flow/texture.h" +#include "flutter/fml/platform/darwin/cf_utils.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h" + +namespace flutter { +// This is another solution for Flutter's external texture. +// Unlike the original external texture which copies pixel buffer from an object that implements +// FlutterTexture protocol, and creates a texture reference using the pixel buffer, this solution +// will copy the OpenGL texture directly from the object implementing FlutterShareTexture protocol. +class IOSExternalTextureShareContext : public flutter::Texture { + public: + IOSExternalTextureShareContext(int64_t textureId, NSObject* externalTexture); + + ~IOSExternalTextureShareContext() override; + + // Called from GPU thread. + void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrContext* context) override; + + void OnGrContextCreated() override; + + void OnGrContextDestroyed() override; + + void MarkNewFrameAvailable() override; + + private: + NSObject* external_texture_; + + FML_DISALLOW_COPY_AND_ASSIGN(IOSExternalTextureShareContext); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_IOS_EXTERNAL_TEXTURE_SHARE_CONTEXT_H_ diff --git a/shell/platform/darwin/ios/ios_external_texture_gl_share_context.mm b/shell/platform/darwin/ios/ios_external_texture_gl_share_context.mm new file mode 100644 index 0000000000000..79b2249cb2a07 --- /dev/null +++ b/shell/platform/darwin/ios/ios_external_texture_gl_share_context.mm @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl_share_context.h" + +#import +#import +#import + +#include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" + +namespace flutter { + +IOSExternalTextureShareContext::IOSExternalTextureShareContext( + int64_t textureId, + NSObject* externalTexture) + : Texture(textureId), external_texture_(externalTexture) { + FML_DCHECK(external_texture_); +} + +IOSExternalTextureShareContext::~IOSExternalTextureShareContext() = default; + +void IOSExternalTextureShareContext::Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) { + GLuint texture_id = [external_texture_ copyShareTexture]; + if (texture_id == 0) { + FML_LOG(WARNING) << "Paint nothing when texture_id is 0"; + return; + } + GrGLTextureInfo textureInfo; + textureInfo.fFormat = GL_RGBA8_OES; + textureInfo.fID = texture_id; + textureInfo.fTarget = GL_TEXTURE_2D; + + GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, textureInfo); + sk_sp image = + SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); + FML_DCHECK(image) << "Failed to create SkImage from Texture."; + if (image) { + canvas.drawImage(image, bounds.x(), bounds.y()); + } +} + +void IOSExternalTextureShareContext::OnGrContextCreated() {} + +void IOSExternalTextureShareContext::OnGrContextDestroyed() {} + +void IOSExternalTextureShareContext::MarkNewFrameAvailable() {} + +} // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 1f1bebcfb85a3..4553d5551f09a 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -29,6 +29,8 @@ class IOSGLContext { sk_sp ColorSpace() const { return color_space_; } + EAGLSharegroup* GetGLShareGroup(); + std::unique_ptr MakeSharedContext(); fml::WeakPtr GetWeakPtr(); diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 8398ce0a5efd6..da3b9808869a8 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -63,4 +63,9 @@ return std::make_unique(context_.get().sharegroup); } +EAGLSharegroup* IOSGLContext::GetGLShareGroup() { + EAGLSharegroup* shareGroup = context_.get().sharegroup; + return shareGroup; +} + } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 49f40f9eec76a..34f1a13fa5bc5 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -39,6 +39,8 @@ class IOSSurface { virtual std::unique_ptr CreateGPUSurface( GrContext* gr_context = nullptr) = 0; + virtual EAGLSharegroup* 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 7dc815dbba879..d821a647d9566 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -53,6 +53,8 @@ class IOSSurfaceGL final : public IOSSurface, bool UseOffscreenSurface() const override; + EAGLSharegroup* GetGLShareGroup() override; + // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index b278e8170cd89..d7bea95929e1e 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -81,6 +81,10 @@ return IsValid() && render_target_->PresentRenderBuffer(); } +EAGLSharegroup* IOSSurfaceGL::GetGLShareGroup() { + return onscreen_gl_context_.get()->GetGLShareGroup(); +} + // |ExternalViewEmbedder| sk_sp IOSSurfaceGL::GetRootSurface() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index d2ff61efdc4bf..4bbf755ae1233 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -36,6 +36,9 @@ class IOSSurfaceSoftware final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; + // |IOSSurface| + EAGLSharegroup* 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 253c8645d2308..37ab370aa810b 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -38,6 +38,10 @@ // Android oddities. } +EAGLSharegroup* IOSSurfaceSoftware::GetGLShareGroup() { + return nullptr; +} + std::unique_ptr IOSSurfaceSoftware::CreateGPUSurface(GrContext* gr_context) { if (!IsValid()) { return nullptr; diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index eeaca3dd9df50..01c2019379c5e 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 RegisterExternalShareTexture(int64_t id, NSObject* texture); + fml::scoped_nsprotocol GetTextInputPlugin() const; void SetTextInputPlugin(fml::scoped_nsprotocol plugin); @@ -44,6 +46,8 @@ class PlatformViewIOS final : public PlatformView { // |PlatformView| void SetSemanticsEnabled(bool enabled) override; + EAGLSharegroup* GetGLShareGroup(); + private: fml::WeakPtr owner_controller_; std::unique_ptr ios_surface_; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 778823493977a..4daa5e06e67b7 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -16,6 +16,7 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" +#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl_share_context.h" namespace flutter { @@ -103,6 +104,18 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), RegisterTexture(std::make_shared(texture_id, texture)); } +void PlatformViewIOS::RegisterExternalShareTexture(int64_t texture_id, + NSObject* texture) { + RegisterTexture(std::make_shared(texture_id, texture)); +} + +EAGLSharegroup* PlatformViewIOS::GetGLShareGroup() { + if (ios_surface_.get() == nullptr) { + return nullptr; + } + return ios_surface_->GetGLShareGroup(); +} + // |PlatformView| std::unique_ptr PlatformViewIOS::CreateRenderingSurface() { if (!ios_surface_) { diff --git a/shell/platform/fuchsia/runtime/dart/utils/BUILD.gn b/shell/platform/fuchsia/runtime/dart/utils/BUILD.gn index b2324ec25536a..9e523ea264bcc 100644 --- a/shell/platform/fuchsia/runtime/dart/utils/BUILD.gn +++ b/shell/platform/fuchsia/runtime/dart/utils/BUILD.gn @@ -33,10 +33,10 @@ source_set("utils") { "$fuchsia_sdk_root/pkg:async-loop-cpp", "$fuchsia_sdk_root/pkg:fdio", "$fuchsia_sdk_root/pkg:memfs", - "$fuchsia_sdk_root/pkg:syslog", - "$fuchsia_sdk_root/pkg:zx", "$fuchsia_sdk_root/pkg:sys_cpp", + "$fuchsia_sdk_root/pkg:syslog", "$fuchsia_sdk_root/pkg:vfs_cpp", + "$fuchsia_sdk_root/pkg:zx", "//third_party/tonic", ] diff --git a/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenarios/ExampleInstrumentedTest.java b/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenarios/ExampleInstrumentedTest.java index edcfc22fee06b..eee9a1dc7687a 100644 --- a/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenarios/ExampleInstrumentedTest.java +++ b/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenarios/ExampleInstrumentedTest.java @@ -1,6 +1,7 @@ package dev.flutter.scenarios; import android.content.Context; +import android.content.Intent; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; @@ -23,4 +24,14 @@ public void useAppContext() { assertEquals("dev.flutter.scenarios", appContext.getPackageName()); } + + @Test + public void testExternalTexture(){ + + Context appContext = InstrumentationRegistry.getTargetContext(); + Intent intent = new Intent(appContext,MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction("com.google.intent.action.TEST_EXTERNAL_TEXTURE"); + appContext.startActivity(intent); + } } diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java index 2d237bdbb10cc..0fa11e1249339 100644 --- a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java @@ -7,20 +7,28 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; -import io.flutter.Log; -import io.flutter.embedding.android.FlutterActivity; +import io.flutter.app.FlutterPluginRegistry; import io.flutter.embedding.android.FlutterFragment; import io.flutter.embedding.android.FlutterView; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryCodec; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugin.common.StringCodec; +import io.flutter.plugins.GeneratedPluginRegistrant; +import io.flutter.view.TextureRegistry; public class MainActivity extends FlutterActivity { final static String TAG = "Scenarios"; @@ -37,39 +45,20 @@ protected void onCreate(Bundle savedInstanceState) { final Uri logFileUri = launchIntent.getData(); new Handler().postDelayed(() -> writeTimelineData(logFileUri), 20000); } + else if("com.google.intent.action.TEST_EXTERNAL_TEXTURE".equals(launchIntent.getAction())){ + startExternalTexture(); + } + else if("com.google.intent.action.TEST_PLATFORM_VIEW".equals(launchIntent.getAction())){ + new Handler().postDelayed(() -> startPlatformView(), 20000); + } } - @Override - @NonNull - public FlutterShellArgs getFlutterShellArgs() { - FlutterShellArgs args = FlutterShellArgs.fromIntent(getIntent()); - args.add(FlutterShellArgs.ARG_TRACE_STARTUP); - args.add(FlutterShellArgs.ARG_ENABLE_DART_PROFILING); - args.add(FlutterShellArgs.ARG_VERBOSE_LOGGING); - - return args; - } - - @Override - public void configureFlutterEngine(FlutterEngine flutterEngine) { - flutterEngine.getPlatformViewsController() - .getRegistry() - .registerViewFactory( - "scenarios/textPlatformView", - new TextPlatformViewFactory()); - } - - private void writeTimelineData(Uri logFile) { if (logFile == null) { throw new IllegalArgumentException(); } - if (getFlutterEngine() == null) { - Log.e(TAG, "Could not write timeline data - no engine."); - return; - } - final BasicMessageChannel channel = new BasicMessageChannel<>( - getFlutterEngine().getDartExecutor(), "write_timeline", BinaryCodec.INSTANCE); + final BasicMessageChannel channel = new BasicMessageChannel( + getFlutterEngine().getDartExecutor(), "write_timeline", BinaryCodec.INSTANCE); channel.send(null, (ByteBuffer reply) -> { try { final FileDescriptor fd = getContentResolver() @@ -84,4 +73,36 @@ private void writeTimelineData(Uri logFile) { finish(); }); } + + + private void startPlatformView(){ + getFlutterEngine().getPlatformViewsController().getRegistry() + .registerViewFactory( + "scenarios/textPlatformView", + new TextPlatformViewFactory()); + } + + private void startExternalTexture(){ + + getFlutterEngine().getDartExecutor().setMessageHandler("scenario_status", (byteBuffer, binaryReply) -> { + final BasicMessageChannel channel = new BasicMessageChannel<>( + getFlutterEngine().getDartExecutor(), "set_scenario", StringCodec.INSTANCE); + channel.send("external_texture", null); + }); + + getFlutterEngine().getDartExecutor().setMessageHandler("create_external_texture", (byteBuffer, binaryReply) -> { + + TestExternalTexture testExternalTexture = new TestExternalTexture(getFlutterEngine()); + + testExternalTexture.createTexture((int textureId)->{ + TextureRegistry.ShareTextureEntry entry = getFlutterEngine().getRenderer().createShareTexture(textureId); + long textureIndex = entry.id(); + final BasicMessageChannel channel = new BasicMessageChannel<>( + getFlutterEngine().getDartExecutor(), "update_data", StringCodec.INSTANCE); + channel.send(""+textureIndex, null); + + testExternalTexture.startWithId(textureIndex,getApplicationContext()); + }); + }); + } } diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestExternalTexture.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestExternalTexture.java new file mode 100644 index 0000000000000..53c8849b9cab5 --- /dev/null +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestExternalTexture.java @@ -0,0 +1,175 @@ +package dev.flutter.scenarios; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLSurface; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import java.util.HashMap; + +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.PluginRegistry; + +public class TestExternalTexture { + private volatile Handler mGLHandler; + private volatile Handler mMainHandler; + private final FlutterEngine flutterEngine; + private int imageTexture; + /** + * Plugin registration. + */ + @SuppressLint("UseSparseArrays") + public TestExternalTexture(FlutterEngine engine) { + this.flutterEngine = engine; + startThread(); + } + + public void createTexture(TextureComplete complete){ + if(mGLHandler != null){ + mGLHandler.post(()->{ + final int[] textures = new int[1]; + GLES20.glGenTextures(1, textures, 0); + imageTexture = textures[0]; + if(mMainHandler == null){ + mMainHandler = new Handler(); + } + mMainHandler.post(()->{ + complete.onTextureCreated(imageTexture); + }); + }); + } + else{ + if(mMainHandler == null){ + mMainHandler = new Handler(); + } + mMainHandler.postDelayed(()->{ + this.createTexture(complete); + },200); + } + } + + void startWithId(long textureIndex, Context context){ + mGLHandler.post(()->{ + + int identifier = context.getResources().getIdentifier("flutter","mipmap",context.getPackageName()); + + final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),identifier); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTexture); + + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, + GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, + GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, + GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, + GLES20.GL_CLAMP_TO_EDGE); + try { + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,bitmap,0); + } + catch (Exception e){ + } + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + mMainHandler.post(() -> flutterEngine.getRenderer().onShareFrameAvaliable(textureIndex)); + }); + } + + + private void startThread(){ + Thread glThread = new Thread(new Runnable() { + @Override + public void run() { + Looper.prepare(); + mGLHandler = new Handler(); + initOpenGL(); + Looper.loop(); + } + }); + glThread.setName("external.testqueue"); + glThread.start(); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void initOpenGL(){ + EGLContext sharedContext = flutterEngine.getRenderer().getShareContext(Build.VERSION.SDK_INT); + EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (eglDisplay == EGL14.EGL_NO_DISPLAY) { + throw new RuntimeException("unable to get EGL14 display"); + } + int[] version = new int[2]; + if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) { + throw new RuntimeException("unable to initialize EGL14"); + } + EGLConfig config = null; + config = TestExternalTexture.getConfig2(eglDisplay); + if (config == null) { + throw new RuntimeException("Unable to find a suitable EGLConfig"); + } + int[] attrib2_list = { + EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, + EGL14.EGL_NONE + }; + EGLContext myEGLContext = EGL14.eglCreateContext(eglDisplay, config, sharedContext, + attrib2_list, 0); + + // Confirm with query. + int[] values = new int[1]; + EGL14.eglQueryContext(eglDisplay, myEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION, + values, 0); + + int[] surfaceAttribs = { + EGL14.EGL_WIDTH, 16, + EGL14.EGL_HEIGHT, 16, + EGL14.EGL_NONE + }; + EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, config, + surfaceAttribs, 0); + EGL14.eglMakeCurrent(eglDisplay,eglSurface,eglSurface,myEGLContext); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + static EGLConfig getConfig2(EGLDisplay display) { + + // The actual surface is generally RGBA or RGBX, so situationally omitting alpha + // doesn't really help. It can also lead to a huge performance hit on glReadPixels() + // when reading into a GL_RGBA buffer. + int[] attribList = { + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + //EGL14.EGL_DEPTH_SIZE, 16, + //EGL14.EGL_STENCIL_SIZE, 8, + EGL14.EGL_RENDERABLE_TYPE,EGL14.EGL_OPENGL_ES2_BIT, + EGL14.EGL_NONE + }; + + EGLConfig[] configs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + if (!EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.length, + numConfigs, 0)) { + return null; + } + return configs[0]; + } + +} diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextureComplete.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextureComplete.java new file mode 100644 index 0000000000000..e0ffc217eb3bb --- /dev/null +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextureComplete.java @@ -0,0 +1,5 @@ +package dev.flutter.scenarios; + +public interface TextureComplete { + void onTextureCreated(int textureID); +} diff --git a/testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java new file mode 100644 index 0000000000000..d007606a44d83 --- /dev/null +++ b/testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -0,0 +1,23 @@ +package io.flutter.plugins; + +import io.flutter.plugin.common.PluginRegistry; + +/** + * Generated file. Do not edit. + */ +public final class GeneratedPluginRegistrant { + public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; + } +} diff --git a/testing/scenario_app/android/app/src/main/res/mipmap-xxhdpi/flutter.png b/testing/scenario_app/android/app/src/main/res/mipmap-xxhdpi/flutter.png new file mode 100644 index 0000000000000..8dcbcebf02237 Binary files /dev/null and b/testing/scenario_app/android/app/src/main/res/mipmap-xxhdpi/flutter.png differ diff --git a/testing/scenario_app/ios/Flutter/Generated.xcconfig b/testing/scenario_app/ios/Flutter/Generated.xcconfig new file mode 100644 index 0000000000000..0ba7bfe608990 --- /dev/null +++ b/testing/scenario_app/ios/Flutter/Generated.xcconfig @@ -0,0 +1,7 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/ljc/Documents/third_party/engine/flutter +FLUTTER_APPLICATION_PATH=/Users/ljc/Documents/third_party/engine/src/flutter/testing/scenario_app +FLUTTER_TARGET=lib/main.dart +FLUTTER_BUILD_DIR=build +SYMROOT=${SOURCE_ROOT}/../build/ios +FLUTTER_FRAMEWORK_DIR=/Users/ljc/Documents/third_party/engine/flutter/bin/cache/artifacts/engine/ios diff --git a/testing/scenario_app/ios/Flutter/flutter_export_environment.sh b/testing/scenario_app/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 0000000000000..0f266c8ff412a --- /dev/null +++ b/testing/scenario_app/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/ljc/Documents/third_party/engine/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/ljc/Documents/third_party/engine/src/flutter/testing/scenario_app" +export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_BUILD_DIR=build" +export "SYMROOT=${SOURCE_ROOT}/../build/ios" +export "FLUTTER_FRAMEWORK_DIR=/Users/ljc/Documents/third_party/engine/flutter/bin/cache/artifacts/engine/ios" diff --git a/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 0000000000000..3b700eb481958 --- /dev/null +++ b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +#endif /* GeneratedPluginRegistrant_h */ diff --git a/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 0000000000000..60dfa42b328db --- /dev/null +++ b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +#import "GeneratedPluginRegistrant.h" + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index b6c77bab8781d..53c157b25657b 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -17,6 +17,11 @@ 0DB7820022EA2C9D00E9B371 /* App.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 246B4E4122E3B5F700073EBF /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0DB7820122EA2CA500E9B371 /* App.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 246B4E4122E3B5F700073EBF /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0DB7820222EA493B00E9B371 /* FlutterViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB781FC22EA2C0300E9B371 /* FlutterViewControllerTest.m */; }; + 221C7253231FBE3800F2FCD1 /* golden_external_texture_D10AP.png in Resources */ = {isa = PBXBuildFile; fileRef = 221C7252231FBE3800F2FCD1 /* golden_external_texture_D10AP.png */; }; + 223563E823191864009EC45A /* TestExternalTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 223563E723191864009EC45A /* TestExternalTexture.m */; }; + 228708AC231D100800F7CA59 /* flutter.png in Resources */ = {isa = PBXBuildFile; fileRef = 228708AB231D100800F7CA59 /* flutter.png */; }; + 228708B3231F93B800F7CA59 /* ExternalTextureUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 228708B2231F93B800F7CA59 /* ExternalTextureUITests.m */; }; + 22E1E7202320AB3800577639 /* golden_external_texture_N69AP.png in Resources */ = {isa = PBXBuildFile; fileRef = 22E1E71F2320AB3800577639 /* golden_external_texture_N69AP.png */; }; 242F37A222E636DE001E83D4 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 242F37A322E636DE001E83D4 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4122E3B5F700073EBF /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 244EA6D0230DBE8900B2D26E /* golden_platform_view_D21AP.png in Resources */ = {isa = PBXBuildFile; fileRef = 244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */; }; @@ -93,6 +98,12 @@ 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FlutterEngine+ScenariosTest.h"; sourceTree = ""; }; 0A57B3C12323D2D700DD9521 /* AppLifecycleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppLifecycleTests.m; sourceTree = ""; }; 0DB781FC22EA2C0300E9B371 /* FlutterViewControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlutterViewControllerTest.m; sourceTree = ""; }; + 221C7252231FBE3800F2FCD1 /* golden_external_texture_D10AP.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = golden_external_texture_D10AP.png; sourceTree = ""; }; + 223563E623191864009EC45A /* TestExternalTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestExternalTexture.h; sourceTree = ""; }; + 223563E723191864009EC45A /* TestExternalTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestExternalTexture.m; sourceTree = ""; }; + 228708AB231D100800F7CA59 /* flutter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = flutter.png; sourceTree = ""; }; + 228708B2231F93B800F7CA59 /* ExternalTextureUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExternalTextureUITests.m; sourceTree = ""; }; + 22E1E71F2320AB3800577639 /* golden_external_texture_N69AP.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = golden_external_texture_N69AP.png; sourceTree = ""; }; 244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = golden_platform_view_D21AP.png; sourceTree = ""; }; 246B4E4122E3B5F700073EBF /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = App.framework; sourceTree = ""; }; 246B4E4522E3B61000073EBF /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Flutter.framework; sourceTree = ""; }; @@ -168,8 +179,11 @@ 248D76C922E388370012F0C1 /* Scenarios */ = { isa = PBXGroup; children = ( + 228708AB231D100800F7CA59 /* flutter.png */, 24F1FB88230B4579005ACE7C /* TextPlatformView.h */, 24F1FB87230B4579005ACE7C /* TextPlatformView.m */, + 223563E623191864009EC45A /* TestExternalTexture.h */, + 223563E723191864009EC45A /* TestExternalTexture.m */, 248D76CA22E388370012F0C1 /* AppDelegate.h */, 248D76CB22E388370012F0C1 /* AppDelegate.m */, 248D76D322E388380012F0C1 /* Assets.xcassets */, @@ -197,10 +211,13 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( + 22E1E71F2320AB3800577639 /* golden_external_texture_N69AP.png */, + 221C7252231FBE3800F2FCD1 /* golden_external_texture_D10AP.png */, 244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */, 24D47D1C230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png */, 24D47D1A230C79840069DD5E /* golden_platform_view_D211AP.png */, 248D76EE22E388380012F0C1 /* PlatformViewUITests.m */, + 228708B2231F93B800F7CA59 /* ExternalTextureUITests.m */, 248D76F022E388380012F0C1 /* Info.plist */, 24D47D1E230CA4480069DD5E /* README.md */, ); @@ -325,6 +342,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 228708AC231D100800F7CA59 /* flutter.png in Resources */, 248D76D422E388380012F0C1 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -340,6 +358,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 221C7253231FBE3800F2FCD1 /* golden_external_texture_D10AP.png in Resources */, + 22E1E7202320AB3800577639 /* golden_external_texture_N69AP.png in Resources */, 24D47D1B230C79840069DD5E /* golden_platform_view_D211AP.png in Resources */, 24D47D1D230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png in Resources */, 244EA6D0230DBE8900B2D26E /* golden_platform_view_D21AP.png in Resources */, @@ -353,6 +373,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 223563E823191864009EC45A /* TestExternalTexture.m in Sources */, 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, @@ -375,6 +396,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 228708B3231F93B800F7CA59 /* ExternalTextureUITests.m in Sources */, 248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -514,17 +536,23 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 7MSDM3RKPG; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + GLES_SILENCE_DEPRECATION, + "$(inherited)", + ); INFOPLIST_FILE = Scenarios/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.Scenarios; + PRODUCT_BUNDLE_IDENTIFIER = com.taobao.ScenariosAA; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; @@ -537,17 +565,19 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 7MSDM3RKPG; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); + GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1"; INFOPLIST_FILE = Scenarios/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.Scenarios; + PRODUCT_BUNDLE_IDENTIFIER = com.taobao.ScenariosAA; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; @@ -558,13 +588,15 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 7MSDM3RKPG; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = ScenariosTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -576,6 +608,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.ScenariosTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Scenarios.app/Scenarios"; }; @@ -585,13 +618,15 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 7MSDM3RKPG; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = ScenariosTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -603,6 +638,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.ScenariosTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Scenarios.app/Scenarios"; }; @@ -611,13 +647,15 @@ 248D76FA22E388380012F0C1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 7MSDM3RKPG; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = ScenariosUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -627,8 +665,9 @@ "-framework", Flutter, ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.ScenariosUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.taobao.ScenariosUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Scenarios; }; @@ -637,13 +676,15 @@ 248D76FB22E388380012F0C1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 7MSDM3RKPG; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = ScenariosUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -653,8 +694,9 @@ "-framework", Flutter, ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.ScenariosUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.taobao.ScenariosUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Scenarios; }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 6a419e99a7bc3..174d3cacf9814 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -5,10 +5,10 @@ #include "AppDelegate.h" #import "FlutterEngine+ScenariosTest.h" #import "ScreenBeforeFlutter.h" +#import "TestExternalTexture.h" #import "TextPlatformView.h" @interface NoStatusBarFlutterViewController : FlutterViewController - @end @implementation NoStatusBarFlutterViewController @@ -17,12 +17,15 @@ - (BOOL)prefersStatusBarHidden { } @end +@interface AppDelegate () +@property(nonatomic, strong) TestExternalTexture* externalTexture; +@end + @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - // This argument is used by the XCUITest for Platform Views so that the app // under test will create platform views. if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--platform-view"]) { @@ -37,6 +40,30 @@ - (BOOL)application:(UIApplication*)application NSObject* registrar = [flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"]; [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"]; + self.window.rootViewController = flutterViewController; + } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--external-texture"]) { + FlutterEngine* engine = [[FlutterEngine alloc] initWithScenario:@"external_texture" + withCompletion:nil]; + [engine runWithEntrypoint:nil]; + + FlutterViewController* flutterViewController = + [[NoStatusBarFlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; + + [engine.binaryMessenger + setMessageHandlerOnChannel:@"create_external_texture" + binaryMessageHandler:^(NSData* _Nullable message, FlutterBinaryReply _Nonnull reply) { + NSObject* registrar = [flutterViewController.engine + registrarForPlugin:@"scenarios/ExternalTexturePlugin"]; + self.externalTexture = + [[TestExternalTexture alloc] initWithWithRegistrar:registrar]; + int64_t textureID = [registrar.textures registerShareTexture:self.externalTexture]; + [self.externalTexture startWithID:textureID]; + [engine.binaryMessenger + sendOnChannel:@"update_data" + message:[[NSString stringWithFormat:@"%lld", textureID] + dataUsingEncoding:NSUTF8StringEncoding]]; + }]; + self.window.rootViewController = flutterViewController; } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TestExternalTexture.h b/testing/scenario_app/ios/Scenarios/Scenarios/TestExternalTexture.h new file mode 100644 index 0000000000000..bb27bff77cc9b --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TestExternalTexture.h @@ -0,0 +1,19 @@ +// +// TestExternalTexture.h +// Scenarios +// +// Created by lujunchen on 2019/8/30. +// Copyright © 2019 flutter. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TestExternalTexture : NSObject +- (instancetype)initWithWithRegistrar:(NSObject*)registrar; +- (void)startWithID:(int64_t)textureID; +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TestExternalTexture.m b/testing/scenario_app/ios/Scenarios/Scenarios/TestExternalTexture.m new file mode 100644 index 0000000000000..5e61ffed23135 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TestExternalTexture.m @@ -0,0 +1,334 @@ +// +// TestExternalTexture.m +// Scenarios +// +// Created by lujunchen on 2019/8/30. +// Copyright © 2019 flutter. All rights reserved. +// + +#import "TestExternalTexture.h" +#import +#import + +#define SHADER_STRING(text) @ #text + +NSString* const kIFGLGeneralVertexShaderString = + SHADER_STRING(attribute vec4 position; attribute vec4 inputTextureCoordinate; + + varying vec2 textureCoordinate; + + void main() { + gl_Position = position; + textureCoordinate = inputTextureCoordinate.xy; + }); + +NSString* const kIFGLGeneralFragmentShaderString = + SHADER_STRING(varying highp vec2 textureCoordinate; + + uniform sampler2D inputImageTexture; + + void main() { gl_FragColor = texture2D(inputImageTexture, textureCoordinate); }); + +const GLfloat vetex1[] = { + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, +}; + +const GLfloat vetex2[] = { + -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, +}; + +const GLfloat vetex3[] = { + 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, +}; + +const GLfloat vetex4[] = { + 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, +}; + +const GLfloat vetex[] = { + -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, +}; + +const GLfloat textureGeneralTexCoord[] = { + 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, +}; + +@interface TestExternalTexture () +@property(nonatomic, assign) GLuint program; +@property(nonatomic, assign) GLuint vertShader; +@property(nonatomic, assign) GLuint fragShader; +@property(nonatomic, strong) NSMutableArray* attributes; +@property(nonatomic, strong) NSMutableDictionary* uniforms; +@property(nonatomic, assign) GLuint positionAttributeLocation; +@property(nonatomic, assign) GLuint texCoordAttributeLocation; + +@property(nonatomic, strong) dispatch_queue_t displayQueue; +@property(nonatomic, strong) EAGLContext* glContext; + +@property(nonatomic, assign) GLuint frameBuffer; +@property(nonatomic, assign) GLuint frameTexture; +@property(nonatomic, assign) GLuint materialTexture; + +@property(strong, nonatomic) NSObject* registry; +@property(strong, nonatomic) NSObject* messenger; +@end + +@implementation TestExternalTexture +- (instancetype)initWithWithRegistrar:(NSObject*)registrar { + if ((self = [super init])) { + self.registry = [registrar textures]; + self.messenger = [registrar messenger]; + + self.attributes = [[NSMutableArray alloc] init]; + self.uniforms = [[NSMutableDictionary alloc] init]; + + self.displayQueue = dispatch_queue_create("external.testqueue", nil); + } + + return self; +} + +- (GLuint)copyShareTexture { + if (self.frameTexture != 0) { + return self.frameTexture; + } + return 0; +} + +- (void)initProgramm { + self.program = glCreateProgram(); + + if (![self compileShader:&_vertShader + type:GL_VERTEX_SHADER + string:kIFGLGeneralVertexShaderString]) { + NSLog(@"FMAVEffect FMAVEffectGLProgram Failed to compile vertex shader"); + } + + if (![self compileShader:&_fragShader + type:GL_FRAGMENT_SHADER + string:kIFGLGeneralFragmentShaderString]) { + NSLog(@"FMAVEffect FMAVEffectGLProgram Failed to compile fragment shader"); + } + + glAttachShader(self.program, _vertShader); + glAttachShader(self.program, _fragShader); + + // called before program link + [self addAttribute:@"position"]; + [self addAttribute:@"inputTextureCoordinate"]; + + if (![self link]) { + NSLog(@"FMAVEffect FMAVEffectGLProgram link failed"); + } + // 4 + self.positionAttributeLocation = [self attributeIndex:@"position"]; + self.texCoordAttributeLocation = [self attributeIndex:@"inputTextureCoordinate"]; +} + +- (void)startWithID:(int64_t)textureID { + int width = [UIScreen mainScreen].bounds.size.width; + int height = [UIScreen mainScreen].bounds.size.height; + + dispatch_async(self.displayQueue, ^{ + if (self.glContext == NULL) { + EAGLSharegroup* flutterShareGroup = [self.registry getShareGroup]; + if (flutterShareGroup != NULL) { + self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:flutterShareGroup]; + } else { + self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + } + } + [EAGLContext setCurrentContext:self.glContext]; + if (self.program == 0) { + [self initProgramm]; + } + if (self.frameTexture == 0) { + self.frameTexture = [self createTextureWithWidth:width andHeight:height]; + } + if (self.frameBuffer == 0) { + glGenFramebuffers(1, &self->_frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer); + } + if (self.materialTexture == 0) { + glGenTextures(1, &self->_materialTexture); + UIImage* materialImage = [UIImage imageNamed:@"flutter.png"]; + if (materialImage != NULL) { + [self convertCGImage:materialImage.CGImage + toTexture:self.materialTexture + inSize:materialImage.size]; + } + } + + glViewport(0, 0, (int)width, (int)height); + + glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.frameTexture, + 0); + + [self useProgramm]; + + [self renderTexture:self.materialTexture withVertex:(GLvoid*)vetex]; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + [self.registry textureFrameAvailable:textureID]; + }); +} + +- (void)convertCGImage:(CGImageRef)image toTexture:(GLuint)textureID inSize:(CGSize)size { + CGImageRef cgImageRef = image; + GLuint width = size.width; + GLuint height = size.height; + CGRect rect = CGRectMake(0, 0, width, height); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + void* imageData = malloc(width * height * 4); + CGContextRef context = + CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, + kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + if (context == NULL) { + return; + } + CGContextTranslateCTM(context, 0, 0); + CGContextScaleCTM(context, 1.0f, 1.0f); + CGColorSpaceRelease(colorSpace); + CGContextClearRect(context, rect); + CGContextDrawImage(context, rect, cgImageRef); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); + glBindTexture(GL_TEXTURE_2D, 0); + CGContextRelease(context); + free(imageData); +} + +- (void)renderTexture:(GLuint)srcTexture withVertex:(GLvoid*)vertex { + glVertexAttribPointer(self.positionAttributeLocation, 2, GL_FLOAT, 0, 0, vertex); + glEnableVertexAttribArray(self.positionAttributeLocation); + + glVertexAttribPointer(self.texCoordAttributeLocation, 2, GL_FLOAT, 0, 0, textureGeneralTexCoord); + glEnableVertexAttribArray(self.texCoordAttributeLocation); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, srcTexture); + glUniform1i([self uniformIndex:@"inputImageTexture"], 0); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glFlush(); + glBindTexture(GL_TEXTURE_2D, 0); +} + +- (GLuint)createTextureWithWidth:(size_t)width andHeight:(size_t)height { + GLuint textureID = -1; + glActiveTexture(GL_TEXTURE1); + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, 0); + return textureID; +} + +#pragma mark OPENGL +- (BOOL)compileShader:(GLuint*)shader type:(GLenum)type string:(NSString*)shaderString { + GLint status; + const GLchar* source; + + source = (GLchar*)[shaderString UTF8String]; + if (!source) { + NSLog(@"FMAVEffect FMAVEffectGLProgram Failed to load shader source"); + return NO; + } + + *shader = glCreateShader(type); + glShaderSource(*shader, 1, &source, NULL); + glCompileShader(*shader); + + glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) { + GLint logLength; + glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + GLchar* log = (GLchar*)malloc(logLength); + glGetShaderInfoLog(*shader, logLength, &logLength, log); + if (shader == &_vertShader) { + NSLog(@"FMAVEffect FMAVEffectGLProgram compile vertext shader error: %s", log); + } else { + NSLog(@"FMAVEffect FMAVEffectGLProgram compile fragment shader error: %s", log); + } + + free(log); + } + } + + return status == GL_TRUE; +} + +- (void)addAttribute:(NSString*)attributeName { + if (![self.attributes containsObject:attributeName]) { + [self.attributes addObject:attributeName]; + glBindAttribLocation(self.program, (GLuint)[self.attributes indexOfObject:attributeName], + [attributeName UTF8String]); + } +} + +- (GLuint)attributeIndex:(NSString*)attributeName { + return (GLuint)[self.attributes indexOfObject:attributeName]; +} + +- (GLuint)uniformIndex:(NSString*)uniformName { + if ([self.uniforms.allKeys containsObject:uniformName]) { + return (GLuint)[self.uniforms[uniformName] unsignedIntValue]; + } + int loc = glGetUniformLocation(self.program, [uniformName UTF8String]); + _uniforms[uniformName] = [NSNumber numberWithInt:loc]; + return loc; +} + +- (BOOL)link { + GLint status; + + glLinkProgram(self.program); + + glGetProgramiv(self.program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + return NO; + + if (self.vertShader) { + glDeleteShader(self.vertShader); + self.vertShader = 0; + } + if (self.fragShader) { + glDeleteShader(self.fragShader); + self.fragShader = 0; + } + return YES; +} + +- (void)useProgramm { + glUseProgram(self.program); +} + +- (void)dealloc { + if (_vertShader != 0) + glDeleteShader(_vertShader); + + if (_fragShader != 0) + glDeleteShader(_fragShader); + + if (_program != 0) + glDeleteProgram(_program); +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/flutter.png b/testing/scenario_app/ios/Scenarios/Scenarios/flutter.png new file mode 100644 index 0000000000000..8dcbcebf02237 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/Scenarios/flutter.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/ExternalTextureUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/ExternalTextureUITests.m new file mode 100644 index 0000000000000..6f79e4bd397bb --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/ExternalTextureUITests.m @@ -0,0 +1,186 @@ +// +// ExternalTextureUITests.m +// ScenariosUITests +// +// Created by lujunchen on 2019/9/4. +// Copyright © 2019 flutter. All rights reserved. +// + +#import +#import +#include + +@interface ExternalTextureUITests : XCTestCase +@property(nonatomic, strong) XCUIApplication* application; +@end + +@implementation ExternalTextureUITests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the + // class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + + // UI tests must launch the application that they test. Doing this in setup will make sure it + // happens for each test method. + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--external-texture" ]; + [self.application launch]; + + // In UI tests it’s important to set the initial state - such as interface orientation - required + // for your tests before they run. The setUp method is a good place to do this. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the + // class. +} + +- (void)testExample { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + NSBundle* bundle = [NSBundle bundleForClass:[self class]]; + NSString* goldenName = + [NSString stringWithFormat:@"golden_external_texture_%@", [self platformName]]; + NSString* path = [bundle pathForResource:goldenName ofType:@"png"]; + UIImage* golden = [[UIImage alloc] initWithContentsOfFile:path]; + + XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot]; + XCTAttachment* attachment = [XCTAttachment attachmentWithScreenshot:screenshot]; + attachment.lifetime = XCTAttachmentLifetimeKeepAlways; + [self addAttachment:attachment]; + + if (golden) { + XCTAttachment* goldenAttachment = [XCTAttachment attachmentWithImage:golden]; + goldenAttachment.lifetime = XCTAttachmentLifetimeKeepAlways; + [self addAttachment:goldenAttachment]; + } else { + NSString* folder = + [NSString stringWithFormat:@"%@/", NSSearchPathForDirectoriesInDomains( + NSCachesDirectory, NSUserDomainMask, YES)[0]]; + NSFileManager* fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:folder]) { + NSError* error = nil; + [fileManager createDirectoryAtPath:folder + withIntermediateDirectories:YES + attributes:nil + error:&error]; + if (error) { + } + } + NSString* imgPath = + [[folder stringByAppendingPathComponent:goldenName] stringByAppendingPathExtension:@"png"]; + [self writeImageToFile:screenshot.image atPath:imgPath]; + + XCTFail(@"This test will fail - no golden named %@ found. Follow the steps in the " + @"README to add a new golden.", + goldenName); + } + + XCTAssertTrue([self compareImage:golden toOther:screenshot.image]); +} + +- (NSString*)platformName { + NSString* simulatorName = + [[NSProcessInfo processInfo].environment objectForKey:@"SIMULATOR_DEVICE_NAME"]; + if (simulatorName) { + return [NSString stringWithFormat:@"%@_simulator", simulatorName]; + } + + size_t size; + sysctlbyname("hw.model", NULL, &size, NULL, 0); + char* answer = malloc(size); + sysctlbyname("hw.model", answer, &size, NULL, 0); + + NSString* results = [NSString stringWithCString:answer encoding:NSUTF8StringEncoding]; + free(answer); + return results; +} + +- (BOOL)writeImageToFile:(UIImage*)image atPath:(NSString*)aPath { + if ((image == nil) || (aPath == nil) || ([aPath isEqualToString:@""])) { + return NO; + } + + NSFileManager* fileManager = [NSFileManager defaultManager]; + BOOL isDirectory; + NSString* directory = [aPath stringByDeletingLastPathComponent]; + BOOL exist = [fileManager fileExistsAtPath:directory isDirectory:&isDirectory]; + if (!exist) { + [fileManager createDirectoryAtPath:directory + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } + + @try { + NSData* imageData = nil; + NSString* ext = [aPath pathExtension]; + if ([ext isEqualToString:@"png"]) { + imageData = UIImagePNGRepresentation(image); + } else { + imageData = UIImageJPEGRepresentation(image, 1.0); + } + + if ((imageData == nil) || ([imageData length] <= 0)) { + return NO; + } + + BOOL success = [imageData writeToFile:aPath atomically:YES]; + return success; + } @catch (NSException* e) { + // NSLog(@"create thumbnail exception."); + } + return NO; +} + +- (BOOL)compareImage:(UIImage*)a toOther:(UIImage*)b { + CGImageRef imageRefA = [a CGImage]; + CGImageRef imageRefB = [b CGImage]; + + NSUInteger widthA = CGImageGetWidth(imageRefA); + NSUInteger heightA = CGImageGetHeight(imageRefA); + NSUInteger widthB = CGImageGetWidth(imageRefB); + NSUInteger heightB = CGImageGetHeight(imageRefB); + + if (widthA != widthB || heightA != heightB) { + return NO; + } + NSUInteger bytesPerPixel = 4; + NSUInteger size = widthA * heightA * bytesPerPixel; + NSMutableData* rawA = [NSMutableData dataWithLength:size]; + NSMutableData* rawB = [NSMutableData dataWithLength:size]; + + if (!rawA || !rawB) { + return NO; + } + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + NSUInteger bytesPerRow = bytesPerPixel * widthA; + NSUInteger bitsPerComponent = 8; + CGContextRef contextA = + CGBitmapContextCreate(rawA.mutableBytes, widthA, heightA, bitsPerComponent, bytesPerRow, + colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + + CGContextDrawImage(contextA, CGRectMake(0, 0, widthA, heightA), imageRefA); + CGContextRelease(contextA); + + CGContextRef contextB = + CGBitmapContextCreate(rawB.mutableBytes, widthA, heightA, bitsPerComponent, bytesPerRow, + colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + + CGContextDrawImage(contextB, CGRectMake(0, 0, widthA, heightA), imageRefB); + CGContextRelease(contextB); + + if (memcmp(rawA.bytes, rawB.bytes, rawA.length)) { + return NO; + } + + return YES; +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_external_texture_D10AP.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_external_texture_D10AP.png new file mode 100644 index 0000000000000..018ec7177bf02 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_external_texture_D10AP.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_external_texture_N69AP.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_external_texture_N69AP.png new file mode 100644 index 0000000000000..7a6448e22ae4a Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_external_texture_N69AP.png differ diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 11126afe69427..a26070f98d4b3 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -10,12 +10,14 @@ import 'dart:typed_data'; import 'dart:ui'; import 'src/animated_color_square.dart'; +import 'src/external_texture_view.dart'; import 'src/platform_view.dart'; import 'src/poppable_screen.dart'; import 'src/scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), + 'external_texture': ExternalTextureScenario(window), 'text_platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)'), 'poppable_screen': PoppableScreenScenario(window), }; @@ -30,8 +32,9 @@ void main() { ..onMetricsChanged = _onMetricsChanged ..onPointerDataPacket = _onPointerDataPacket ..scheduleFrame(); - final ByteData data = ByteData(1); - data.setUint8(0, 1); + final ByteData data = ByteData(2); + data.setUint8(0, 128); + data.setUint8(1, 2); window.sendPlatformMessage('scenario_status', data, null); } @@ -54,7 +57,10 @@ Future _handlePlatformMessage( } else if (name == 'write_timeline') { final String timelineData = await _getTimelineData(); callback(Uint8List.fromList(utf8.encode(timelineData)).buffer.asByteData()); - } else { + } else if(name == 'update_data'){ + _currentScenario.onUpdateData(data); + } + else { _currentScenario?.onPlatformMessage(name, data, callback); } } diff --git a/testing/scenario_app/lib/src/external_texture_view.dart b/testing/scenario_app/lib/src/external_texture_view.dart new file mode 100644 index 0000000000000..f669505b54795 --- /dev/null +++ b/testing/scenario_app/lib/src/external_texture_view.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'scenario.dart'; + + +class ExternalTextureScenario extends Scenario { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + ExternalTextureScenario(Window window) + : assert(window != null), + super(window) { + final ByteData data = ByteData(1); + data.setUint8(0, 1); + window.sendPlatformMessage( + 'create_external_texture', + data, + null, + ); + } + + int _textureId; + + @override + void onBeginFrame(Duration duration) { + + print('begin frame 0000'); + + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + if (_textureId != null) { + print('begin frame 1111'); + + builder.addTexture(_textureId, offset: const Offset(0, 0), width: 480, height: 480); + } + + + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } + + @override + void onUpdateData(ByteData data) { + super.onUpdateData(data); + if (data != null) { + String string = utf8.decode(data.buffer.asUint8List()); + _textureId = int.parse(string); + print('update textureid $_textureId'); + + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + if (_textureId != null) { + print('begin frame 1111'); + + builder.addTexture(_textureId, offset: const Offset(0, 0), width: 480, height: 480); + } + + + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } + } +} diff --git a/testing/scenario_app/lib/src/scenario.dart b/testing/scenario_app/lib/src/scenario.dart index db7b5890c0d51..79d167abd1a0d 100644 --- a/testing/scenario_app/lib/src/scenario.dart +++ b/testing/scenario_app/lib/src/scenario.dart @@ -29,6 +29,11 @@ abstract class Scenario { /// See [Window.onMetricsChanged]. void onMetricsChanged() {} + /// Called by the program when Scenario's data updated + /// + /// + void onUpdateData(ByteData data){} + /// Called by the program when a pointer event is received. /// /// See [Window.onPointerDataPacket]. @@ -39,8 +44,8 @@ abstract class Scenario { /// /// See [Window.onPlatformMessage]. void onPlatformMessage( - String name, - ByteData data, - PlatformMessageResponseCallback callback, - ) {} + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) {} }