2017-01-05 4 views
0

私たちのアプリは、GLES 2を使用していくつかのシーンを生成します。移動するシーンを選択するためのピッカー(画像のスクロールリスト)を作成します。シーンは既成のイメージとして利用できません。 GLレンダリングが必要なときに使用します。 iOSでは、オフスクリーンのpbufferでシーンを作成しました。リストがスクロールしている間に見えるようになりました。GLESシーンの複数のサムネイルをAndroidのスクロールリストにどのように合成するのですか?

試み#1:アンドロイドで 、電流EGLContextがないため、私たちのiOSコードとコードと同等の、GLエラーを生成(または現在のコンテキストにレンダリングするための有効な時間またはスレッドではありません)。 iOSでは、GLビューとの関連付けに依存しないオフスクリーンレンダリングの例が見つかりました。 Androidの場合、ほとんどの例は、あなたがGLSurfaceViewの描画コードの中にいると仮定しているようですので、正しいEGLContextでGLスレッドを使用しています。 フレームワークの内部EGLContextが明らかにアプリケーションに公開されていたので、Lollipopの前例であっただったので、スレッドセーフではない(たとえ最終的には問題を引き起こしても)。

試行#2: GLSurfaceView per thumbnail。 1つのサムネイルに最適です。 2番目のサムネイルを追加すると、エミュレータがグラフィックドライバのエラーでクラッシュします。 GLビューによって管理される2つのGLスレッドのためだと思います。

試行#3:Grafika as described by faddenで使用 アプローチ:複数surfaceviewsないGLSurfaceViews)、それらsurfaceviewsにレンダリングする独自GLスレッドを管理します。これはおそらくスクロールリストでは機能しないかもしれませんが、スクロールでうまくいくようにするには、textureviewsに変更することができます(ただし、GLスレッドを共有するか、共有グループ内のEGLContextを使う必要があります - Grafika参照)。サムネイルは、N個のサーフェスに対するコンポーザーの制限に達するとパフォーマンス・クリフについてのfaddenのコメントに従って表示されます。

試行番号4: GLには全く表面がない。独自のGLスレッドを管理する(Grafika)。サムネイルが描画されたら、GLをFBOまたはpbufferにレンダリングします。ビットマップに変換します。ビットマップをサムネイル表示で表示します。ビューが作成された時点でビットマップが存在しないため、これは私が見た例とは異なります。ビューが描画されるときのみ存在します。各描画で再利用できるようにビットマップをキャッシュします。

  1. アンEGLContextとGLのスレッド:ここでは第4位の場合

    は、サブタスクがあります。

  2. 正しいサイズのFBOを作成するか、すでにiOSで動作するpbufferコードを使用してください。 (おそらく古代の例からコピーされた;私はFBOがより効率的な解決策であると理解する)。
  3. GLをバッファにレンダリングする。 [このコードはすでに動作しています。アプリ固有]
  4. バッファをAndroidビットマップにキャプチャします。 [私はこれの例を持っています。ここには表示されません。]
  5. ビットマップを表示して動的に表示します。たとえば、ビットマップをImageViewにアタッチします。

(1.)はサブタスクです。


NOTE#1:、全体のスクロールビューシングルGLの表面を作ることによって、すべてこれを避けるリストのスクロールをシミュレートし、適切な長方形に各シーンのレンダリングでした - しかし、それは私たちのクロスプラットフォームのコードが複雑になる(Xamarin )、柔軟性を排除します。 Androidのビュー階層に適したソリューションを探して、テキストキャプションやその他の変更を加えることができます。それから、私たちが望む場所にGLをレンダリングし、Androidで提供されているGL以外の機能を使ってGLをレンダリングする汎用的な機能があります。

注2:Grafikaとは異なり、すべてのフレームを変更するにはGLレンダリングは必要ありません。だからこそ私たちはではない複数のsurfaceviews - ビットマップが存在すると、それらを再利用することができます。したがって、の解は、 GLビューではありません。はありません。あなたがそれを求めているならば、ビューの数を制限し、GrafikaのアプローチをいくつかのSurfaceViews(またはそれらを移動する必要がある場合はTextureViews)で使用してください。

答えて

1

注:これはXamarin C#コードです。そのため、異なる大文字とその他の命名の相違点があります(Javaのget/setメソッドがC#のプロパティに置き換えられました)。それはAndroidのJava用のXamarinラッパーを使用し、Javaバージョンは各コード行の1:1の翻訳になります。

OurGLRendererは、EGLContextを管理するカスタムクラスです。これにより、GLレンダリングは、なしでGLSurfaceViewまたはTextureViewとなります。

このクラスの中心は「MakeCurrent」です:アクティブなEGLContextがあるため、この呼び出し後、GL呼び出しを行うことができます。 GL呼び出しは、でCreateOffscreenBufferを介して以前に作成されたのオフスクリーンバッファにレンダリングします。

代わりにTextureView(またはSurfaceView?)にレンダリングするには、CreateOffscreenBufferの代わりにCreateWindowSurfaceを使用します。

using System; 

using Android.Graphics; 
using Android.Runtime; 

using Javax.Microedition.Khronos.Egl; 

namespace YourAppNameHere 
{ 
    // Manage an EGLContext. This allows GL rendering without a GLSurfaceView or TextureView. 
    // The heart of this class is "MakeCurrent": after calling that, you can make GL calls, 
    // because you have an active EGLContext. 
    // The GL calls render to an offscreen buffer, previously created in CreateGLAndSurface via CreateOffscreenBuffer. 
    // To instead render to a `TextureView` (or `SurfaceView`?), then use `CreateWindowSurface` instead of `CreateOffscreenBuffer`. 
    public class OurGLRenderer 
    { 
     // Your app supplies this class. 
     public interface IRenderEngine 
     { 
      void EnsureInitialized(); 
      // The frame buffer or view size. 
      void EnsureSize(int width, int height); 
      // Our client calls our MakeCurrent, then calls this to render. 
      // "model" should be a class in your app. 
      // On Android, this could return a Bitmap, which you then place in an ImageView. 
      object RenderAsPlatformImage(object model); 
     } 

     // HACK: ASSUMES Singleton. 
     public static Action OneTimeAfterCreated; 
     // Most recent error code. 
     static int _error = 0; 


     #region "=== static methods - could be in a utility class ===" 
     // These are static, so that they can be used independently. Could be "public". 
     // ----- Based on https://forums.xamarin.com/discussion/3406/xamarin-android-textureview-sample-render-an-opengl-scene-to-a-view ----- 
     static bool InitializeEGL(out IEGL10 _egl10, out EGLDisplay _display, out bool _display_initialized, 
            out bool _choose_config, out EGLConfig _config) 
     { 
      _display_initialized = false; 
      _choose_config = false; 
      _config = null; 

      //FAIL Javax.Microedition.Khronos.Egl.IEGL10 t_egl10 = (Javax.Microedition.Khronos.Egl.IEGL10)Javax.Microedition.Khronos.Egl.EGLContext.EGL; 
      _egl10 = EGLContext.EGL.JavaCast<IEGL10>(); 

      // _display 
      _display = _egl10.EglGetDisplay(EGL10.EglDefaultDisplay); // EglGetCurrentDisplay returns NULL ! 
      if (_display == null) 
       return false; 

      // EglInitialize 
      int[] _major_minor = new int[ 2 ]; 
      _display_initialized = _egl10.EglInitialize(_display, _major_minor); 
      Console.WriteLine(string.Format("EglInitialize -> {0}, version={1}.{2}", _display_initialized, _major_minor[ 0 ], _major_minor[ 1 ])); 
      if (!CheckEglError(_egl10, "EglInitialize") || !_display_initialized) 
       return false; 

      return InitializeEGLConfig(_egl10, _display, out _choose_config, out _config); 
     } 

     static bool InitializeEGLConfig(IEGL10 _egl10, EGLDisplay _display, 
             out bool _choose_config, out EGLConfig _config) 
     { 
      _config = null; 


      // EglChooseConfig -> OpenGL ES 2.0 Config 
      int EGL_OPENGL_ES2_BIT = 4; 
      int[] _attribs_config = new int[]{ 
         EGL10.EglRenderableType, EGL_OPENGL_ES2_BIT, // IMPORTANT 
         EGL10.EglRedSize, 8, 
         EGL10.EglGreenSize, 8, 
         EGL10.EglBlueSize, 8, 
         EGL10.EglAlphaSize, 8, 
         EGL10.EglDepthSize, 0, 
         EGL10.EglStencilSize, 0, 
         EGL10.EglNone 
        }; 
      EGLConfig[] _configs = null; 
      _configs = new EGLConfig[ 1 ]; 
      int[] _numconfigs = new int[ 1 ]; 
      _choose_config = _egl10.EglChooseConfig(_display, _attribs_config, _configs, 1, _numconfigs); 
      if (!CheckEglError(_egl10, "EglChooseConfig") || !_choose_config) 
       return false; 

      _config = _configs[ 0 ]; 
      // Why? (I guess so not holding another reference.) 
      _configs[ 0 ] = null; _configs = null; 

      return (_config != null); 
     } 

     static bool EglCreateContext(IEGL10 _egl10, EGLDisplay _display, EGLConfig _config, 
             out EGLContext _context) 
     { 
      // EglCreateContext -> OpenGL ES 2.0 Context 
      int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 
      int _version = EGL10.EglVersion; 
      int[] _attribs_config = new int[]{ 
         EGL_CONTEXT_CLIENT_VERSION, 2, // IMPORTANT 
         EGL10.EglNone 
        }; 
      _context = _egl10.EglCreateContext(_display, _config, EGL10.EglNoContext, _attribs_config); 
      return CheckEglError(_egl10, "EglCreateContext") && (_context != null); 
     } 

     static bool CreateWindowSurface(IEGL10 _egl10, EGLDisplay _display, EGLConfig _config, SurfaceTexture _surfaceTexture, 
             out EGLSurface _surface) 
     { 
      _surface = null; 


      if (_surfaceTexture == null) 
       return false; 


      // EglCreateWindowSurface 
      int[] _attribs_config = new int[]{ 
         EGL10.EglNone 
        }; 
      _surface = _egl10.EglCreateWindowSurface(_display, _config, _surfaceTexture, _attribs_config); 
      return CheckEglError(_egl10, "EglCreateWindowSurface") && (_surface != null); 
     } 

     static bool CreateOffscreenBuffer(IEGL10 _egl10, EGLDisplay _display, EGLConfig _config, int width, int height, 
              out EGLSurface _surface) 
     { 
      int[] _attribs_config = new int[]{ 
         EGL10.EglWidth, width, 
         EGL10.EglHeight, height, 
         EGL10.EglNone 
        }; 
      _surface = _egl10.EglCreatePbufferSurface(_display, _config, _attribs_config); 
      return CheckEglError(_egl10, "EglCreatePbufferSurface") && (_surface != null); 
     } 

     static bool EglMakeCurrent(IEGL10 _egl10, EGLDisplay _display, EGLSurface _surface, EGLContext _context, 
            out bool _make_current) 
     { 
      _make_current = _egl10.EglMakeCurrent(_display, _surface, _surface, _context); 
      return (CheckEglError(_egl10, "EglMakeCurrent") && _make_current); 
     } 

     static bool EglSwapBuffers(IEGL10 _egl10, EGLDisplay _display, EGLSurface _surface) 
     { 
      bool _ok = _egl10.EglSwapBuffers(_display, _surface); 
      return (CheckEglError(_egl10, "EglSwapBuffers") && _ok); 
     } 

     static bool CheckEglError(IEGL10 _egl10, string tag) 
     { 
      _error = _egl10.EglGetError(); 
      if (_error != EGL10.EglSuccess) { 
       Log.e(tag, string.Format("EGL-Error={0}", _error)); 
       return false; 
      } 
      return true; 
     } 
     #endregion 


     #region "=== constructor and Dispose ===" 
     public OurGLRenderer(int ourWidth, int ourHeight) 
     { 
      Width = ourWidth; Height = ourHeight; 
      EnsureOurRenderEngine(ourWidth, ourHeight); 
     } 

     public void Dispose() 
     { 
      OurRenderEngine = null; 
      EndOurGL(); 
     } 
     #endregion 


     #region "=== public fields ===" 
     public int Width { get; protected set; } 
     public int Height { get; protected set; } 

     public IRenderEngine OurRenderEngine; 
     #endregion 


     #region "=== public methods ===" 
     // NOTE: call EndOurGL when leave fragment. 
     public bool BeginOurGL(int width, int height) 
     { 
      if (alreadyBeginningOurGL) 
       // CAUTION: Caller must not call EndOurGLThread - might be another view starting it! 
       return false; 

      alreadyBeginningOurGL = true; 
      try { 
       if (!EnsureGLAndSurfaceInitialized(width, height)) { 
        return false; 
       } 

       //TEST_TextureView(_egl10, null); // tmstest 

       return true; 

      } finally { 
       alreadyBeginningOurGL = false; 
      } 
     } 

     // Client must call this before any GL calls. 
     // Before first GL call, and whenever Android may have done drawing in its own EGLContext. 
     public bool MakeCurrent() 
     { 
      return EglMakeCurrent(_egl10, _display, _surface, _context, out _make_current); 
     } 

     // ASSUME MakeCurrent already called. 
     public void EnsureOurSize(int ourWidth, int ourHeight) 
     { 
      // In our app, we create an OurGLRenderer, then use it to render multiple images of the same size - 
      // our IRenderEngine is set up once for that size. 
      // You might not have this constraint; in which case, comment this out. 
      if (Width != ourWidth || Height != ourHeight) 
       throw new InvalidProgramException("OurGLRenderer.EnsureOurSize - all images must be same size."); 

      OurRenderEngine.EnsureSize(Width, Height); 
     } 

     public void EndOurGL() 
     { 
      EndAndDispose(_egl10); _egl10 = null; 
     } 
     #endregion 


     #region "=== private fields ===" 
     IEGL10 _egl10 = null; 
     EGLDisplay _display = null; 
     bool _display_initialized = false; 
     bool _choose_config = false; 
     EGLConfig _config = null; 
     EGLSurface _surface = null; 
     EGLContext _context = null; 
     bool _make_current = false; 

     bool alreadyBeginningOurGL = false; 
     #endregion 


     #region "=== private methods ===" 
     bool EnsureGLAndSurfaceInitialized(int width, int height) 
     { 
      if (_surface == null) { 
       if (!CreateGLAndSurface(width, height)) 
        return false; 
      } 

      // TODO: Try this, once NOT on PaintingView. 
      if (false) { 
       // Make current. We aren't rendering yet, but confirm that this succeeds. 
       if (!MakeCurrent()) { 
        // Failed; undo any work that was done. 
        EndOurGL(); 
        return false; 
       } 
      } 


      return true; 
     } 

     bool CreateGLAndSurface(int width, int height) 
     { 
      if (!InitializeEGL(out _egl10, out _display, out _display_initialized, out _choose_config, out _config) || 
       !EglCreateContext(_egl10, _display, _config, out _context) || 
       !CreateOffscreenBuffer(_egl10, _display, _config, width, height, out _surface)) { 
       // Failed; undo any work that was done. 
       EndOurGL(); 
       return false; 
      } 

      return true; 
     } 


     void EndAndDispose(IEGL10 _egl10) 
     { 
      // EglMakeCurrent 
      if (_make_current) { 
       _egl10.EglMakeCurrent(_display, EGL10.EglNoSurface, EGL10.EglNoSurface, EGL10.EglNoContext); 
       _make_current = false; 
      } 
      // EglDestroyContext 
      if (_context != null) { 
       _egl10.EglDestroyContext(_display, _context); 
       _context = null; 
      } 
      // EglDestroySurface 
      if (_surface != null) { 
       _egl10.EglDestroySurface(_display, _surface); 
       _surface = null; 
      } 
      // 
      if (_config != null) { 
       _config.Dispose(); 
       _config = null; 
      } 
      // EglTerminate 
      if (_display_initialized) { 
       _egl10.EglTerminate(_display); 
       _display_initialized = false; 
      } 
      // 
      if (_display != null) { 
       _display.Dispose(); 
       _display = null; 
      } 
     } 
     #endregion 


     #region "=== specific to our app ===" 
     // Shows that OurRenderEngine must be created, and BeginOurGL called. 
     // Also shows a call to MakeCurrent, and OurRenderEngine.EnsureInitialized. 
     public void EnsureOurRenderEngine(int ourWidth, int ourHeight) 
     { 
     // if ((OurRenderEngine == null) || !ReferenceEquals(AppState.ActiveRenderEngine, OurRenderEngine)) { 
     //  AppState.ReleaseRenderEngine(); 
     //  // NOTE: We can't pass a reDrawDelegate because multiple views are sharing this engine. 
     //  //AppState.ActiveRenderEngine = OurRenderEngine = AppState.CreateRenderEngine(MainActivity.OurAppType, ourWidth, ourHeight, null); 
     //  AppState.ActiveRenderEngine = OurRenderEngine = (OffscreenRenderEngine)AppState.CreateRenderEngine(AppState.AppType.Offscreen, ourWidth, ourHeight, null); 
     // 
     //  if (Width != ourWidth || Height != ourHeight) 
     //   throw new InvalidProgramException("OurGLRenderer.EnsureOurRenderEngine - all images must be same size."); 
     // 
     //  // BEFORE ActiveRenderEngine.EnsureInitialized. 
     //  // TODO: GL Program fails to compile, when called in OnDraw. Conflict with framework's GL context? 
     //  BeginOurGL(ourWidth, ourHeight); 
     // 
     //  // Before any GL calls. 
     //  if (!MakeCurrent()) 
     //   return; 
     //  // Needed because FragmentMain.OnCreateView runs before this, so its initialization is skipped (no ActiveRenderEngine yet).. 
     //  OurRenderEngine.EnsureInitialized(); 
     // 
     //  if (OneTimeAfterCreated != null) { 
     //   OneTimeAfterCreated(); 
     //   OneTimeAfterCreated = null; 
     //  } 
     // } 
     } 
     #endregion 
    } 
} 
関連する問題