我如何在 android 中的单独线程上 运行 GLES20.glReadPixels?
How can i run GLES20.glReadPixels on separate thread in android?
我目前正在使用 ARCore 对图像进行分类并将对象放在图像上。但似乎 ARCore 相机没有提供任何获取像素缓冲区的方法。然后我遇到了 How to take picture with camera using ARCore,根据这个我们可以使用 GLES20.glReadPixels 从 OpenGL 复制帧。如果我一次传递每一帧,我的分类器工作正常,但是当我将 GLES20.glReadPixels 放在单独的线程中获取像素缓冲区时,我得到的都是零。所以基本上它给了我黑色图像。
那么有没有办法在单独的线程上 运行 GLES20.glReadPixels 。
OpenGL,对于 Android 平台,OpenGL ES 被设计为一个面向单线程的库。这并不意味着您不能使用 OpenGL ES 使用多线程,但这不是使用 OpenGL 的标准方式。
根据我的经验,我需要异步加载纹理。为此,需要在另一个线程中创建 OpenGL 上下文。请注意,并非每个设备都支持创建两个 OpenGL 上下文的功能。我创建以下 class 来管理异步纹理加载。我认为您可以轻松地将其转换为您的需求。
public class AsyncOperationManager {
public boolean asyncMode;
public interface AsyncTextureInfoLoader {
TextureInfo load(Texture texture);
}
public class TextureLoaderThread extends Thread {
public TextureLoaderThread() {
super("GLThread-AsyncOperation");
}
public Handler handler;
@SuppressLint("HandlerLeak")
public void run() {
Looper.prepare();
int pbufferAttribs[] = { EGL10.EGL_WIDTH, 1, EGL10.EGL_HEIGHT, 1, EGL_TEXTURE_TARGET, EGL_NO_TEXTURE, EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE, EGL10.EGL_NONE };
surfaceForTextureLoad = egl.eglCreatePbufferSurface(display, eglConfig, pbufferAttribs);
egl.eglMakeCurrent(display, surfaceForTextureLoad, surfaceForTextureLoad, textureContext);
handler = new Handler() {
public void handleMessage(Message msg) {
MessageContent content = (MessageContent) msg.obj;
long start2 = Clock.now();
Logger.debug("context switch for async texture load stopped ");
long start1 = Clock.now();
Logger.debug("async texture load stopped ");
content.texture.updateInfo(content.execute.load(content.texture));
Logger.debug("async texture load ended in %s ms", (Clock.now() - start1));
Logger.debug("context switch for async texture load ended in %s ms", (Clock.now() - start2));
if (content.listener != null)
content.listener.onTextureReady(content.texture);
}
};
Looper.loop();
}
}
final static int EGL_TEXTURE_TARGET = 12417;
final static int EGL_NO_TEXTURE = 12380;
final static int EGL_TEXTURE_FORMAT = 12416;
private static AsyncOperationManager instance = new AsyncOperationManager();
public static AsyncOperationManager instance() {
return instance;
}
private EGLContext textureContext;
private EGL10 egl;
private EGLDisplay display;
private EGLConfig eglConfig;
protected EGLSurface surfaceForTextureLoad;
private TextureLoaderThread textureLoaderThread;
public AsyncOperationManager() {
}
public void init(EGL10 egl, EGLContext renderContext, EGLDisplay display, EGLConfig eglConfig) {
// la versione usata è la 2!
int[] attrib_list = { XenonEGL.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
this.egl = egl;
this.display = display;
this.eglConfig = eglConfig;
textureContext = egl.eglCreateContext(display, eglConfig, renderContext, attrib_list);
if (textureContext != EGL10.EGL_NO_CONTEXT) {
Logger.info("Context for async operation asyncMode.");
asyncMode = true;
// creiamo il thread per le operazioni async su opengl
textureLoaderThread = new TextureLoaderThread();
textureLoaderThread.start();
} else {
asyncMode = false;
Logger.fatal("Try to enable context for async operation, but failed.");
}
}
public int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public void init(android.opengl.EGLContext renderContext, android.opengl.EGLDisplay display, android.opengl.EGLConfig eglConfig) {
// la versione usata è la 2!
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
if (textureContext != EGL10.EGL_NO_CONTEXT) {
Logger.info("Context for async operation asyncMode.");
asyncMode = true;
textureLoaderThread = new TextureLoaderThread();
textureLoaderThread.start();
} else {
asyncMode = false;
Logger.fatal("Try to enable context for async operation, but failed.");
}
}
public boolean destroy(EGL10 egl) {
return egl.eglDestroyContext(display, textureContext);
}
public boolean destroy() {
return false;
}
public class MessageContent {
public MessageContent(Texture textureValue, AsyncTextureInfoLoader executeValue, TextureAsyncLoaderListener listenerValue) {
texture = textureValue;
execute = executeValue;
listener = listenerValue;
}
public Texture texture;
public AsyncTextureInfoLoader execute;
public TextureAsyncLoaderListener listener;
}
public boolean isEnabled() {
return asyncMode;
}
public TextureInfo load(final Texture texture, final AsyncTextureInfoLoader execute, final TextureAsyncLoaderListener listener) {
if (asyncMode) {
MessageContent content = new MessageContent(texture, execute, listener);
Message msg = textureLoaderThread.handler.obtainMessage(25, content);
textureLoaderThread.handler.sendMessage(msg);
return null;
} else {
Logger.error("async operations on textures are disabled! This device support multiple opengl context?");
Logger.warn("run texture update in single thread!");
execute.load(texture);
if (listener != null)
listener.onTextureReady(texture);
return texture.info;
}
}
public void init() {
asyncMode = false;
}
}
关于这个论点的更多信息,建议您阅读:
- Loading-textures-in-a-background-thread-on-Android
- 5843-Texture-Sharing
- why-is-eglmakecurrent-failing-with-egl-bad-match
我目前正在使用 ARCore 对图像进行分类并将对象放在图像上。但似乎 ARCore 相机没有提供任何获取像素缓冲区的方法。然后我遇到了 How to take picture with camera using ARCore,根据这个我们可以使用 GLES20.glReadPixels 从 OpenGL 复制帧。如果我一次传递每一帧,我的分类器工作正常,但是当我将 GLES20.glReadPixels 放在单独的线程中获取像素缓冲区时,我得到的都是零。所以基本上它给了我黑色图像。 那么有没有办法在单独的线程上 运行 GLES20.glReadPixels 。
OpenGL,对于 Android 平台,OpenGL ES 被设计为一个面向单线程的库。这并不意味着您不能使用 OpenGL ES 使用多线程,但这不是使用 OpenGL 的标准方式。
根据我的经验,我需要异步加载纹理。为此,需要在另一个线程中创建 OpenGL 上下文。请注意,并非每个设备都支持创建两个 OpenGL 上下文的功能。我创建以下 class 来管理异步纹理加载。我认为您可以轻松地将其转换为您的需求。
public class AsyncOperationManager {
public boolean asyncMode;
public interface AsyncTextureInfoLoader {
TextureInfo load(Texture texture);
}
public class TextureLoaderThread extends Thread {
public TextureLoaderThread() {
super("GLThread-AsyncOperation");
}
public Handler handler;
@SuppressLint("HandlerLeak")
public void run() {
Looper.prepare();
int pbufferAttribs[] = { EGL10.EGL_WIDTH, 1, EGL10.EGL_HEIGHT, 1, EGL_TEXTURE_TARGET, EGL_NO_TEXTURE, EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE, EGL10.EGL_NONE };
surfaceForTextureLoad = egl.eglCreatePbufferSurface(display, eglConfig, pbufferAttribs);
egl.eglMakeCurrent(display, surfaceForTextureLoad, surfaceForTextureLoad, textureContext);
handler = new Handler() {
public void handleMessage(Message msg) {
MessageContent content = (MessageContent) msg.obj;
long start2 = Clock.now();
Logger.debug("context switch for async texture load stopped ");
long start1 = Clock.now();
Logger.debug("async texture load stopped ");
content.texture.updateInfo(content.execute.load(content.texture));
Logger.debug("async texture load ended in %s ms", (Clock.now() - start1));
Logger.debug("context switch for async texture load ended in %s ms", (Clock.now() - start2));
if (content.listener != null)
content.listener.onTextureReady(content.texture);
}
};
Looper.loop();
}
}
final static int EGL_TEXTURE_TARGET = 12417;
final static int EGL_NO_TEXTURE = 12380;
final static int EGL_TEXTURE_FORMAT = 12416;
private static AsyncOperationManager instance = new AsyncOperationManager();
public static AsyncOperationManager instance() {
return instance;
}
private EGLContext textureContext;
private EGL10 egl;
private EGLDisplay display;
private EGLConfig eglConfig;
protected EGLSurface surfaceForTextureLoad;
private TextureLoaderThread textureLoaderThread;
public AsyncOperationManager() {
}
public void init(EGL10 egl, EGLContext renderContext, EGLDisplay display, EGLConfig eglConfig) {
// la versione usata è la 2!
int[] attrib_list = { XenonEGL.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
this.egl = egl;
this.display = display;
this.eglConfig = eglConfig;
textureContext = egl.eglCreateContext(display, eglConfig, renderContext, attrib_list);
if (textureContext != EGL10.EGL_NO_CONTEXT) {
Logger.info("Context for async operation asyncMode.");
asyncMode = true;
// creiamo il thread per le operazioni async su opengl
textureLoaderThread = new TextureLoaderThread();
textureLoaderThread.start();
} else {
asyncMode = false;
Logger.fatal("Try to enable context for async operation, but failed.");
}
}
public int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public void init(android.opengl.EGLContext renderContext, android.opengl.EGLDisplay display, android.opengl.EGLConfig eglConfig) {
// la versione usata è la 2!
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
if (textureContext != EGL10.EGL_NO_CONTEXT) {
Logger.info("Context for async operation asyncMode.");
asyncMode = true;
textureLoaderThread = new TextureLoaderThread();
textureLoaderThread.start();
} else {
asyncMode = false;
Logger.fatal("Try to enable context for async operation, but failed.");
}
}
public boolean destroy(EGL10 egl) {
return egl.eglDestroyContext(display, textureContext);
}
public boolean destroy() {
return false;
}
public class MessageContent {
public MessageContent(Texture textureValue, AsyncTextureInfoLoader executeValue, TextureAsyncLoaderListener listenerValue) {
texture = textureValue;
execute = executeValue;
listener = listenerValue;
}
public Texture texture;
public AsyncTextureInfoLoader execute;
public TextureAsyncLoaderListener listener;
}
public boolean isEnabled() {
return asyncMode;
}
public TextureInfo load(final Texture texture, final AsyncTextureInfoLoader execute, final TextureAsyncLoaderListener listener) {
if (asyncMode) {
MessageContent content = new MessageContent(texture, execute, listener);
Message msg = textureLoaderThread.handler.obtainMessage(25, content);
textureLoaderThread.handler.sendMessage(msg);
return null;
} else {
Logger.error("async operations on textures are disabled! This device support multiple opengl context?");
Logger.warn("run texture update in single thread!");
execute.load(texture);
if (listener != null)
listener.onTextureReady(texture);
return texture.info;
}
}
public void init() {
asyncMode = false;
}
}
关于这个论点的更多信息,建议您阅读:
- Loading-textures-in-a-background-thread-on-Android
- 5843-Texture-Sharing
- why-is-eglmakecurrent-failing-with-egl-bad-match