Как я могу запустить GLES20.glReadPixels в отдельном потоке в Android?

В настоящее время я работаю с ARCore для классификации изображений и размещения объектов на изображениях. Но похоже, что камера ARCore не предоставляет никакого способа получить пиксельный буфер. затем я наткнулся на Как сделать снимок с помощью камеры с помощью ARCore и в соответствии с этим мы можем скопировать кадр из OpenGL, используя GLES20.glReadPixels. если я передаю каждый кадр за раз, мой классификатор работает нормально, но когда я помещаю GLES20.glReadPixels, чтобы получить пиксельный буфер в отдельном потоке, я получаю все нули. так что в основном это дает мне черное изображение. Итак, есть ли способ запустить GLES20.glReadPixels в отдельном потоке?


person deeppandya    schedule 13.02.2018    source источник


Ответы (1)


OpenGL, а для платформы Android OpenGL ES был разработан как библиотека, ориентированная на один поток. Это не означает, что вы не можете работать с несколькими потоками с OpenGL ES, но это не стандартный способ работы с OpenGL.

По моему опыту, мне нужно было загружать текстуры асинхронно. Для этого необходимо создать контекст OpenGL в другом потоке. Обратите внимание, что не каждое устройство поддерживает возможность создания двух контекстов OpenGL. Я создаю следующий класс для управления асинхронной загрузкой текстур. Я думаю, вы можете легко преобразовать это в свои нужды.

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;
    }
}

Для получения дополнительной информации об этом аргументе я предлагаю вам прочитать:

person xcesco    schedule 13.02.2018
comment
Пожалуйста, не забудьте выбрать мой ответ, если он поможет вам решить вашу проблему. - person xcesco; 15.02.2018