Skip to content Skip to sidebar Skip to footer

How Compose Multiple Thumbnails Of Gles Scenes Into A Scrolling List On Android?

Our app generates a number of scenes using GLES 2. Making a picker (scrolling list of images) to select which scene to go to. The scenes are not available as pre-made images; using

Solution 1:

NOTE: This is Xamarin C# code. That is why it has different capitalization and other naming differences (java get/set methods replaced by C# properties). It uses Xamarin wrappers for Android java, a java version would be a 1:1 translation of each line of code.

OurGLRenderer is a custom class to 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.

using System;

using Android.Graphics;
using Android.Runtime;

using Javax.Microedition.Khronos.Egl;

namespaceYourAppNameHere
{
    // 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`.publicclassOurGLRenderer
    {
        // Your app supplies this class.publicinterfaceIRenderEngine
        {
            voidEnsureInitialized();
            // The frame buffer or view size.voidEnsureSize(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.objectRenderAsPlatformImage(object model );
        }

        // HACK: ASSUMES Singleton.publicstatic Action OneTimeAfterCreated;
        // Most recent error code.staticint _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 -----staticboolInitializeEGL(out IEGL10 _egl10, out EGLDisplay _display, outbool _display_initialized,
                                   outbool _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)
                returnfalse;

            // EglInitializeint[] _major_minor = newint[ 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)
                returnfalse;

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

        staticboolInitializeEGLConfig( IEGL10 _egl10, EGLDisplay _display,
                                         outbool _choose_config, out EGLConfig _config )
        {
            _config = null;


            // EglChooseConfig -> OpenGL ES 2.0 Configint EGL_OPENGL_ES2_BIT = 4;
            int[] _attribs_config = newint[]{
                        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 = newint[ 1 ];
            _choose_config = _egl10.EglChooseConfig( _display, _attribs_config, _configs, 1, _numconfigs );
            if (!CheckEglError( _egl10, "EglChooseConfig" ) || !_choose_config)
                returnfalse;

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

            return (_config != null);
        }

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

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


            if (_surfaceTexture == null)
                returnfalse;


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

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

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

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

        staticboolCheckEglError( IEGL10 _egl10, string tag )
        {
            _error = _egl10.EglGetError();
            if (_error != EGL10.EglSuccess) {
                Log.e( tag, string.Format( "EGL-Error={0}", _error ) );
                returnfalse;
            }
            returntrue;
        }
        #endregion#region "=== constructor and Dispose ==="publicOurGLRenderer(int ourWidth, int ourHeight )
        {
            Width = ourWidth; Height = ourHeight;
            EnsureOurRenderEngine( ourWidth, ourHeight );
        }

        publicvoidDispose()
        {
            OurRenderEngine = null;
            EndOurGL();
        }
        #endregion#region "=== public fields ==="publicint Width { get; protectedset; }
        publicint Height { get; protectedset; }

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

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

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

            } 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.publicboolMakeCurrent()
        {
            return EglMakeCurrent( _egl10, _display, _surface, _context, out _make_current );
        }

        // ASSUME MakeCurrent already called.publicvoidEnsureOurSize(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)
                thrownew InvalidProgramException( "OurGLRenderer.EnsureOurSize - all images must be same size." );

            OurRenderEngine.EnsureSize( Width, Height );
        }

        publicvoidEndOurGL()
        {
            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 ==="boolEnsureGLAndSurfaceInitialized(int width, int height )
        {
            if (_surface == null) {
                if (!CreateGLAndSurface( width, height ))
                    returnfalse;
            }

            // 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();
                    returnfalse;
                }
            }


            returntrue;
        }

        boolCreateGLAndSurface(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();
                returnfalse;
            }

            returntrue;
        }


        voidEndAndDispose( IEGL10 _egl10 )
        {
            // EglMakeCurrentif (_make_current) {
                _egl10.EglMakeCurrent( _display, EGL10.EglNoSurface, EGL10.EglNoSurface, EGL10.EglNoContext );
                _make_current = false;
            }
            // EglDestroyContextif (_context != null) {
                _egl10.EglDestroyContext( _display, _context );
                _context = null;
            }
            // EglDestroySurfaceif (_surface != null) {
                _egl10.EglDestroySurface( _display, _surface );
                _surface = null;
            }
            //if (_config != null) {
                _config.Dispose();
                _config = null;
            }
            // EglTerminateif (_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.publicvoidEnsureOurRenderEngine(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
    }
}

Post a Comment for "How Compose Multiple Thumbnails Of Gles Scenes Into A Scrolling List On Android?"