Is it possible to share the GLES20 context between different GLSurfaceViews (within one Activity)? Alternatively, how would one share a set of texture between different GLSurfaceViews?
On iOS, if you want to conserve memory and reuse (large) textures in different CAEAGLLayer-backed UIViews, you can pass around a EAGLContext object between them or use different EAGLContexts which share a common EAGLSharegroup object.
I wonder how to accomplish this on Android. Is there any equivalent technique?
Edit1
The initial suggestion, to implement your own EGLContextFactory, which will return the same EGLContext, doesn't work since every GLSurfaceViews dispatches the rendering to it's own private gl render thread and sharing the same EGLContext between different threads is not possible.
To rephrase my initial question:
You have several GLSurfaceViews in one screen (one Activity) and you need to access a set of common but large texture in the individual EGLContext of every surfaces, but loading your textures multiple times exceeds the memory of your device. How would you share your textures between GLSurfaceViews then?
The following code works on some of the devices, but not all of them:
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
EGLContext shared = .....;
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext context = egl.eglCreateContext(display, eglConfig, shared == null ? EGL10.EGL_NO_CONTEXT : shared,
attrib_list);
return context;
}
}
GLSurfaceView setEGLContextFactory
GLSurfaceView.java
It seems that setEGLContextFactory enables to use the same GLES20 context between different GLSurfaceViews.
pseudo code:
private class MyEGLContextFactory implements EGLContextFactory {
private static EGLContext mEGLContext;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
/* create EGLContext for GLES20 in first time */
return mEGLContext;
}
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
}
}
Related
I have 2 separate Android app (apk).
App 1 creates SurfaceView inside it and should provide AIDL methods for other apps to obtain an instance of SurfaceHolder for such SurfaceView. So the other apps will be able to draw on that view, displayed inside app number 1.
I was able to transfer Surface itself via aidl easily, since it implements Parcelable interface.
// IMainService.aidl
package com.commonsware.cwac.preso.demo.service;
import android.view.Surface;
interface IMainService {
Surface getSurf();
}
But 3-rd party sdk need SurfaceHolder to draw on. So the question is - how can I create SurfaceHolder for a given Surface instance, or how can I transfer SurfaceHolder via AIDL. Is there any examples of how I can implement Parcelable for SurfaceHolder?
My use-case (if its matter): App 1 starts as a Service and draw UI on Presentation Screen. And I need a way to get another apps display Navigation Data via Nokia Here Mobile SDK inside app 1. I need SurfaceHolder in order to use this api.
Any help will be appreciated.
After a while, finally create a solution that works well with Here OffscreenRender API.
At one app I create SurfaceHolder and this app provide AIDL (see at the post above). With this AIDL I was able to correctly send Surface object and then, on app 2 side, make SurfaceHolder from it.
public class MySurfaceHolder implements SurfaceHolder {
private Surface surface;
private Rect rect;
MySurfaceHolder(Surface surface, Rect rect) {
this.surface = surface;
this.rect = rect;
}
///....
#Override
public Canvas lockCanvas() {
return surface.lockCanvas(this.rect);
}
#Override
public Canvas lockCanvas(Rect rect) {
return surface.lockCanvas(rect);
}
#Override
public void unlockCanvasAndPost(Canvas canvas) {
surface.unlockCanvasAndPost(canvas);
}
#Override
public Rect getSurfaceFrame() {
return rect;
}
#Override
public Surface getSurface() {
return surface;
}
}
Basically, its my own SurfaceHolder, which get initialized by my own, instead of system. This approach need extend AIDL to be able to request Rect sizes, as well.
Hope that will be helpful for someone.
In android I have taken a rotating sphere example given here. It creates a simple app showing a rotating sphere (the earth).
Now, in a class derived from GLSurfaceView I wait for an even (like a touch-screen event) in order to exchange the renderer. I want the current renderer to stop rendering, and the GLSurfaceView should use a different renderer instead (to display some other object).
I have tried to use the following code:
public class MyGLSurfaceView extends GLSurfaceView {
Context mycontext;
MyGLSurfaceView(Context context) {
super(context);
mycontext = context;
}
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
Log.d("onTouchEvent",String.format("keyCode: %d coords: %d %d", event.getActionMasked(), x, y));
GlRenderer renderer = new GlRenderer(mycontext);
setRenderer(renderer);
return super.onTouchEvent(event);
}
}
which gives the following error:
FATAL EXCEPTION: main
Process: com.jimscosmos.opengltexturedsphere, PID: 25708
java.lang.IllegalStateException: setRenderer has already been called for this instance.
I guess I have to stop/remove/destroy the 'old' renderer, but I did not find anything useful for that in the documentation. Maybe my approach is completely wrong? What else to do? How to do it right?
If you insist, technically it can be achieved using a 'mediator' - as suggested here: Change GlSurfaceView renderer.
However - I think this is an overhead you really do not need. Simply prepare various objects and change the various rendering properties in a single renderer.
I have a standard GLSurfaceView class:
public class TestSurfaceView extends GLSurfaceView {
public MainRenderer mRenderer;
public GStreamerSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(2);
mRenderer = new MainRenderer(context);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
I have a Renderer class that implements GLSurfaceView.Renderer:
public class MainRenderer implements GLSurfaceView.Renderer {
private int[] hTex;
private SurfaceTexture mSTexture;
private Context context;
MainRenderer(Context c) {
context = c;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
}
public void onSurfaceCreated(GL10 arg0, javax.microedition.khronos.egl.EGLConfig arg1) {
}
}
In a separate JNI thread, I have some video data (YUV format) uploaded to an OpenGLES texture. I receive in Java the notification that a new texture is available and I have the corresponding texture id.
How can I display the content of this texture in the Renderer class with minimum performance impact?
For cases where the frames are coming from Camera or MediaCodec there are some very efficient solutions. It sounds like you're generating or decoding the video in software, though, which puts a mild spin on things (though you do have a SurfaceTexture declared in your code, which is odd). The trick is the "OpenGL ES texture" part, because the texture is associated with the EGL context, and the EGL context can only be active in one thread at a time.
Because you're using GLSurfaceView, rather than plain SurfaceView, you don't have control over the EGL context in the GLSurfaceView's renderer thread. The easiest way to work around this is to jump through some hoops to create a second EGL context that is shared with the first. Once you've done that, the texture created in the separate JNI thread will be available to the GLSurfaceView renderer thread.
You can find an example of this in Grafika's "show + capture camera" activity. If you look at the onDrawFrame() method in CameraCaptureActivity.java you can see it calling updateSharedContext(), which passes a message to the thread running TextureMovieEncoder to cause it to run handleUpdateSharedContext(), which (re-)creates the surface used to feed a video encoder.
If you use plain SurfaceView instead, and do your own EGL and thread management, the code will be less twisty. You can create both contexts at once, then just pass one to the thread that's producing images. You could also create a single context and use eglMakeCurrent() to shift it between threads, but that could be expensive on some platforms.
Update: the Grafika "show + capture camera" implementation has a race condition; see this bug report for details on the problem and solution. You have to perform some extra steps when creating a texture in one thread and using it in another. It's usually better to do everything in one context on one thread. The other activities in Grafika use a plain SurfaceView and do their own EGL context and thread management.
I am trying to create a context in opengl-es for android and I'm a bit confused about it. I have initialized my context in a completely separate class from my renderer and my launch activity like so.
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL11;
public class Context {
public static GL11 gl;
EGLConfig[] configs = new EGLConfig[1];
EGLConfig config = configs[0];
EGLDisplay dpy;
int attribList;
EGLSurface surf;
static EGLContext glContext;
public Context() {
int attribList [] =
{
EGL11.EGL_DEPTH_SIZE, 15, // z-buffer
EGL11.EGL_NONE
};
dpy = ((EGL11) gl).eglGetDisplay(EGL11.EGL_DEFAULT_DISPLAY);
EGLContext glContext = ((EGL11) gl).eglCreateContext(dpy, config, EGL11.EGL_NO_CONTEXT, attribList);
gl = (GL11)glContext.getGL();
}
}
My app is a simple thing that draws two squares to the screen on launch, one moves in a random direction while the other stays on the spot and spins. In theory the user should be able to touch the screen and the stationary square should move to the location touched on screen. But my app crashes when my UnProject method (which I need to translate coordinates) is called, possibly because my context
GL11 gl
(which is the argument for my UnProject class) returns null (NullPointerException is thrown when my app crashes).
Is my context initialized badly so that it returns null or do you think the problem is elsewhere?
I doubt that the Context constructor gets called anywhere in your code; if it did it would crash at that point:
You cast gl to an EGL11 type, while it doesn't implement that interface.
You use the config member in EGLContext glContext = ((EGL11) gl).eglCreateContext(dpy, config, EGL11.EGL_NO_CONTEXT, attribList);, while it's not initialized (it's null as the entries of your configs member are not constructed)
Where is gl assigned? From outside the Context class?
You use the gl member and then reassign it in gl = (GL11)glContext.getGL() ?
...
In short, I don't think gl was assigned a value in the first place, but it's hard to tell without the rest of the code.
In my openGL game, I draw my scene normally using a GLSurfaceView.Renderer class in the onDrawFrame(). However, when I am displaying a loading screen I would like to force the screen to draw after each item of data is loaded so the loading bar can be displayed.
Is it possible to force a bufferswap during this draw call somehow? My only alternative is to stagger my loading across multiple frames which means a lot of rework..
I guess what I am trying to call is eglSwapBuffers() but I cannot find a way to access the egl context from the GLSurfaceView or GLSurfaceView.Renderer.
Thank you for your time.
You can add also this method to your GLSurfaceView.Renderer class:
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
public void swapBuffers()
{
EGL10 curEgl = (EGL10)EGLContext.getEGL();
EGLDisplay curDisplay = curEgl.eglGetCurrentDisplay();
if (curDisplay == EGL10.EGL_NO_DISPLAY) { Log.e("myApp","No default display"); return; }
EGLSurface curSurface = curEgl.eglGetCurrentSurface(EGL10.EGL_DRAW);
if (curSurface == EGL10.EGL_NO_SURFACE) { Log.e("myApp","No current surface"); return; }
curEgl.eglSwapBuffers(curDisplay, curSurface);
}
Much the same as OpenUserX03's answer, just in Java.
No you can't (or shouldn't) force swapping buffers in the onDraw method of your Renderer.
What you should do is to make the loading of your data in a separate Thread. Your onDraw method will still be called regularly, which will let you ask to the loading thread how many items were loadede to display a progress bar / message accordingly.
It's been awhile since the answer has been accepted but you can (and there is no reason you shouldn't) force swapping the buffers in the onDrawFrame() method of your Renderer.
I had the exact same problem in my OpenGL app - I needed to render a loading screen while data was being loaded. Here is my pseudo-code example of calling eglSwapBuffers() during a load (I use JNI):
public void onDrawFrame(GL10 gl)
{
MyJNILib.render();
}
MyJNILib native pseudo-code:
#include <EGL\egl.h>
...
void render()
{
...
while (loading)
{
// Do loading stuff
...
eglSwapBuffers( eglGetCurrentDisplay(), eglGetCurrentSurface( EGL_DRAW ) );
}
...
}
A solution by force is to make your custom version GLSurfaceView class based on the source code of Android.
In the source, you can find a method called swap:
/**
* Display the current render surface.
* #return the EGL error code from eglSwapBuffers.
*/
public int swap() {
if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
return mEgl.eglGetError();
}
return EGL10.EGL_SUCCESS;
}
This should be what you want. Unfortunately, however, it is a method of private inner class called EglHelper.
/**
* An EGL helper class.
*/
private static class EglHelper {
So in your custom GLSurfaceView class (copied from Google's source), make this EglHelper class public and you can use EglHelper.swap method.
public static class EglHelper {