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?"