问题
I'm trying to use the UpdateExternalTexture
and CreateExternalTexture
to pass a simple bitmap image from an android plugin into unity. On the documentation it says the latter is meant to be used with low level plugins. I'm a layer above that with java code.
I'm getting a context error when I do any GLES20 call:
call to OpenGL ES API with no current context (logged once per thread)
According to this excellent question this is because the context isn't right (or has none). I followed the instructions on that page and I managed to create a context. However, now they seem to be referring to different resource "areas". The pointer I'm getting is "1" which unity understands as the "current frame on the screen" instead of my bitmap. So looks like this:
Here's a snippet of my code. First the Unity piece. This is attached to a game object in the scene.
// This function is called shortly after Start
private void TestingTransfer() {
// get the current activity from unity
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
androidActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
// create a link to the android plugin
AndroidJavaObject obj = new AndroidJavaObject("com.test.pc.sample.SampleClass", androidActivity);
// Make the call to put the bitmap in the graphic memory and get it's PTR
Int32 texPtr = obj.Call<Int32>("PutBitmapInGraphicsCard");
Debug.Log("texture pointer == " + texPtr);
// Stage the texture in unity
Texture2D nativeTexture = Texture2D.CreateExternalTexture(100, 100, TextureFormat.ARGB32, false, false, (IntPtr)texPtr);
// Update it with the new PTR
nativeTexture.UpdateExternalTexture(nativeTexture.GetNativeTexturePtr());
// Notify other things in unity that this updated (delegate)
// I'm using this from another game object to pickup the new texture
if (OnNewImageReported != null) OnNewImageReported(nativeTexture);
}
Now for the Java side. This code is being used by unity through an AAR file.
private int PutBitmapInGraphicsCard() {
final int[] textureHandle = new int[1];
final Bitmap bitmap = GenerateTestImage(); // this creates a simple bitmap, blank with a blue circle in the center, works fine in android.
CreateContext(); // look below, it's defined there
GLES20.glGenTextures(1, textureHandle, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
return textureHandle[0];
}
// This function is a (almost) direct copy from the question linked above
public void CreateContext(){
// gets hold of the display
EGLDisplay dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
EGL14.eglInitialize(dpy, vers, 0, vers, 1);
// get some basic configs going
int[] configAttr = {
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
EGL14.eglChooseConfig(dpy, configAttr, 0,
configs, 0, 1, numConfig, 0);
if (numConfig[0] == 0) {
Log.e("GLEDB-Error", "Could not find/create config!!");
}
EGLConfig config = configs[0];
// creating an offscreen (PBuffer) surface to render stuff
int[] surfAttr = {
EGL14.EGL_WIDTH, 100,
EGL14.EGL_HEIGHT, 100,
EGL14.EGL_NONE
};
EGLSurface surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0);
// create the context
int[] ctxAttrib = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
EGLContext ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0);
// connect all these things together
EGL14.eglMakeCurrent(dpy, surf, surf, ctx);
}
So, if I'm correct in understanding the problem what I need is to refer to the same context that unity is using to draw it's scene. Is there a way to get/use this?
SOLUTION:
As noted per the accepted answer, the main issue was that I'm using multi-threaded rendering. For my use case this makes no difference.
This didn't work straight away from the code above. A few things to note:
- If you do this don't create your own context. It messes up with unity and prevents any updating of the surface (freezes any drawing)
- The code is slightly different, not completely sure why the one above didn't work but the following does.
[ line intentionally added since StackOverflow can't render code after bullet points]
public int PutBitmapInGraphicsCard() {
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
final Bitmap bitmap = GenerateTestImage();
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0, bitmap, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureHandle[0];
}
回答1:
Based on this Manual:
Rendering in Unity can be multithreaded if the platform and number of available CPUs will allow for it. When multithreaded rendering is used, the rendering API commands happen on a thread which is completely separate from the one that runs MonoBehaviour scripts.
You probably have multithreaded rendering turned on, so it might be the case. You could try to turn it off and see if it makes a difference.
来源:https://stackoverflow.com/questions/47779970/get-bitmap-to-unity-through-android-plugin-using-gles20