Loading textures when coming in from a JNI Call

I’m running into an issue of something going wrong with my OpenGL context when coming “into” my C++ code from Java.

I have this function which gets called fine from the Java.

void Java_org_cocos2dx_lib_Cocos2dxActivity_nativeShowQuitOverlay() {
    CCScene* scene = CCDirector::sharedDirector()->getRunningScene();
    scene->addChild(QuitOverlay::node());
}

And it makes it into the init().

bool QuitOverlay::init(void) {    
    if (!CCLayer::init()) {
        return false;
    }
    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sheetQuit.plist");
    return true;
}

But it crashes on the line:

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sheetQuit.plist");

With this error in LogCat:

04-05 02:34:07.846: E/libEGL(4935): call to OpenGL ES API with no current context (logged once per thread)

It seems odd that I can utilize all of my app’s other functions from JNI methods except when loading textures. If anyone knows a way around this or a solution to this issue it would be much appreciated. Thanks.

1 Like

Can not invoke opengl es functions in new thread.

You can mot invoke OpenGL calls on a thread that does not have an OpenGL context.
You could create a shared OpenGL context on this thread if you really have to do the calls there. However, there is no magic Android API function call that will do that, but you have to take the hard way and use the EGL methods to create the context.
Also not all gpu’s support multiple contexts (Althoug I guess that only were Adreno 130 and similar older chips).

A far simpler solution would be to schedule an method on CCScheduler which then makes your GL calls during the next frame.

is CCScheduler thread-safe?

you can instead invoke your function from a gl thread , like that :

mGLView.queueEvent
{
@Override
public void run
{
nativeShowQuitOverlay;
}
});

when calling in java nativeShowQuitOverlay;

sry meant

@ mGLView.queueEvent(new Runnable()
{
Override public void run() { nativePassShakeOccurred(); } });

Any other way to do this? I am creating a sprite during a native c++ code call from Java and need to add it to a particular layer.

problem also for me, Cocos2d-x 2.1.1

I use:

@ private static void sendNativeResponseOnOpenGLThread (final int responseCode, final int errorCode) {

// queue native call on openGL thread
Cocos2dxGLSurfaceView.getInstance().queueEvent(new Runnable() {
Override public void run() { Activity.nativeSetResponse(responseCode, errorCode); } }); }

This method is called after a response from In-App Billing Android.
The variable errorCode tell me if in-app item was purchased or not.

I show a MessageBox (CCLayer) based from this response, but too early.
Infact, Surface is not yet created.
How can I resolve? I have to wait MainActivity.OnResume?
I hoped that Cocos2dxGLSurfaceView.queueEvent() was waiting until GLSurface was active.

The log show an error:

06-30 20:17:02.062: D/Billing(4503): Request 7610932992334889426 of type REQUEST_PURCHASE sent

06-30 20:17:02.062: D/PhoneWindow(4503): couldn’t save which view has focus because the focused view org.cocos2dx.lib.Cocos2dxGLSurfaceView-4054b320 has no id.
06-30 20:17:02.212: I/GLThread(4503): onPause tid=10
06-30 20:17:02.212: I/Main thread(4503): onPause waiting for mPaused.
06-30 20:17:02.232: D/cocos2d-x debug info: ———> EnterBackground
06-30 20:17:02.442: I/GLThread(4503): mPaused is now true tid=10
06-30 20:17:02.442: I/GLThread(4503): releasing EGL surface because paused tid=10
06-30 20:17:02.442: W/EglHelper(4503): destroySurface() tid=10
06-30 20:17:02.452: W/EglHelper(4503): finish() tid=10
06-30 20:17:02.482: I/GLThread(4503): releasing EGL context because paused tid=10
06-30 20:17:02.482: D/cocos2d-x debug: ———> ON_PAUSE

06-30 20:17:41.981: D/inApp(4503): PURCHASED
06-30 20:17:41.981: D/cocos2d-x debug info(4503): setNativeResponse idCall: 2, responseCode: 0, errorCode: 0

06-30 20:17:42.001: I/GLThread(4503): onResume tid=10
06-30 20:17:42.001: I/Main thread(4503): onResume waiting for !mPaused.

06-30 20:17:42.011: E/libEGL: call to OpenGL ES API with no current context
06-30 20:17:42.011: E/cocos2d-x assert: /Users/xxxxxx/Documents/lib/cocos2d-2.1beta3-x-2.1.1/cocos2dx/misc_nodes/CCRenderTexture.cpp function:initWithWidthAndHeight line:332

06-30 20:17:42.051: I/GLThread(4503): mPaused is now false tid=10
06-30 20:17:42.051: W/EglHelper(4503): start() tid=10
06-30 20:17:42.061: W/EglHelper(4503): createContext com.google.android.gles_jni.EGLContextImpl-405f0f48 tid=10
06-30 20:17:42.061: D/cocos2d-x debug: ———> ON_RESUME
06-30 20:17:42.071: W/GLThread(4503): egl createSurface
06-30 20:17:42.071: W/EglHelper(4503): createSurface() tid=10
06-30 20:17:42.071: E/libEGL(4503): eglCreateWindowSurface format=4
06-30 20:17:42.071: W/GLThread(4503): onSurfaceCreated

06-30 20:17:42.261: D/cocos2d-x debug info(4503): reload all texture

06-30 20:17:42.732: W/GLThread(4503): onSurfaceChanged(480, 800)
06-30 20:17:42.732: D/cocos2d-x debug info: ———> EnterForeground

I have same issue. My work around is like this, but it’s ugly.

mHandler.postDelayed(
new Runnable() {
Override public void run() { Cocos2dxGLSurfaceView.getInstance().queueEvent(new Runnable() {Override
public void run() {
GL2JNILib.callbackSelectImageResult(path);
}
});
}
}, 100);

Any progress on this? When coming back from an activity I start (open web browser and close it) in a Runnable using runOnUiThread or queueEvent, i get the same error.

02-21 15:47:54.917: E/libEGL(17561): call to OpenGL ES API with no current context (logged once per thread)
02-21 15:47:54.928: D/cocos2d-x debug info(17561): (INFO): (SceneManager) replaced scene: 4
02-21 15:47:55.068: D/cocos2d-x debug info(17561): cocos2d: CCTextureCache: texture: assets/pp-assets/fonts/defaultBlack.png
02-21 15:47:55.068: D/cocos2d-x debug info(17561): cocos2d: CCTextureCache: texture: assets/pp-assets/fonts/defaultWhite.png
02-21 15:47:55.068: W/Adreno200-ES20(17561): <core_glBufferSubData:805>: GL_INVALID_OPERATION
02-21 15:47:55.068: D/cocos2d-x debug info(17561): OpenGL error 0x0502 in /Users/Justin/Desktop/tools/cocos2d-x-2.2.1/projects/MyGame/proj.android/../../../cocos2dx/textures/CCTextureAtlas.cpp drawNumberOfQuads 686

The last two lines just keep building up into the thousands.

I encountered this problem too, but the problem seems to be around for long time ago… I don’t know if it has been fixed or not.

@Jgod
@siauw
What’s the engine version?
Could you give some codes to reproduce it?

jinhuanghust solution is a good workaround, i was thinking the same thing. also you may want to save the gl context.

I don’t know how to apply @jinhuanghust solution on 3.0beta2. Could you give more details on it?

The idea is basically that EGL context is a limited resource esp. on older devices but to newer devices it is possible to have multiple context so switching is smooth without reinitializing (if you save your context). Now when your code loses the focus or went to background or on pause, your GLSurfaceView will release that context and all the resources that you need and so when you get the focus back you don’t have a context to start with so you need to reinitialize your EGL context but if you don’t have that reinitialization implemented and go straight issue an openGL call then you’ll get that error.
There is a setEGLcontextPreserveOnPause on GLSurfaceView, try to set that to true and check if that resolves the issue.
Note: what i said is just a general concept from an android sdk and opengl perspectives. i can’t point you out to a specific method call in cocos2dx for i haven’t tried tinkering with it, still waiting for the final stable release though, i believe it’s only weeks from now.
am i right zhangxm?

did it fix it?

With cocos2d-x 3, if your activity extends Cocos2dxActivity, you can call the native method like this:
(me = instance of the activity)

me.runOnGLThread(new Runnable() {
  @Override
  public void run() {
    // TODO Auto-generated method stub
    nativeMethodInCpp("aa", true);
  }
});
2 Likes