【问题标题】:Initializing OpenGL context in background thread在后台线程中初始化 OpenGL 上下文
【发布时间】:2017-02-21 09:20:13
【问题描述】:

所以我在解决 android 中的离屏渲染问题时遇到了一些困难,并且找不到解决方案。请记住,我在所有与 OpenGL 相关的事情上都是新手,所以如果我做出任何虚假陈述,我提前道歉。

这个 android 进程的目标是在后台线程中将内容渲染到位图上,而用户不会看到它(因此是屏幕外渲染)。我用来渲染这些东西的库需要我手动设置 OpenGL 上下文,然后才能使用实用程序渲染方法。在初始化 OpenGL 上下文并将其绑定到线程后,我需要将位图加载到 OpenGL 纹理(由 GLES20.glGenTextures(..) 创建)中。不过,我无法详细了解图书馆。

问题:我不知道如何在不使用 GLSurfaceView 的情况下在 android 中设置 OpenGL 上下文,并且每次搜索它都会将我重定向到基于此 SurfaceView 的某种解决方案。所以我需要一个起点来弄清楚如何在后台线程中使对 GLES20 的调用有效。

我知道的是,我需要使用 EGL14 提供的方法来设置它:

eglCreateContext(...);
eglMakeCurrent(...);
eglInitialize(...);

但是由于文档不存在,我很难弄清楚要使用哪些参数,甚至无法确定使用它们的组合/顺序。

非常感谢任何帮助。

编辑:澄清一下,我并不反对基于 GLSurfaceView 的解决方案,但据我了解,它们都需要在屏幕上显示,而我绝对不能这样做。


编辑 2:

所以在挖掘了更多之后,我偶然发现了一些看起来相当不错的东西:

mEgl = (EGL10) EGLContext.getEGL();

mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

if (mEglDisplay == EGL10.EGL_NO_DISPLAY)
    throw new RuntimeException("Error: eglGetDisplay() Failed "
            + GLUtils.getEGLErrorString(mEgl.eglGetError()));

int[] version = new int[2];

if (!mEgl.eglInitialize(mEglDisplay, version))
    throw new RuntimeException("Error: eglInitialize() Failed "
            + GLUtils.getEGLErrorString(mEgl.eglGetError()));

maEGLconfigs = new EGLConfig[1];

int[] configsCount = new int[1];
int[] configSpec = new int[]
        {
                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 0,
                EGL10.EGL_STENCIL_SIZE, 0,
                EGL10.EGL_NONE
        };
if ((!mEgl.eglChooseConfig(mEglDisplay, configSpec, maEGLconfigs, 1, configsCount)) || (configsCount[0] == 0))
    throw new IllegalArgumentException("Error: eglChooseConfig() Failed "
            + GLUtils.getEGLErrorString(mEgl.eglGetError()));

if (maEGLconfigs[0] == null)
    throw new RuntimeException("Error: eglConfig() not Initialized");

int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};

mEglContext = mEgl.eglCreateContext(mEglDisplay, maEGLconfigs[0], EGL10.EGL_NO_CONTEXT, attrib_list);

那么下一个调用将是:

final int[] textureHandle = {0};
GLES20.glGenTextures(1, textureHandle, 0);

但是 textureHandle[0] 仍然是 0。它还会向控制台打印一个错误:

E/libEGL: call to OpenGL ES API with no current context

所以如果我们忽略这个问题,即代码使用旧的 EGL10 方法,它仍然不能正常工作。我在这里错过了什么?

【问题讨论】:

  • 我不确定是否应该在 EGL14 之类的类中创建上下文。上下文通常使用指示使用哪个版本的属性生成。之后,您使用相应的 API。你能从另一个问题 egl.eglCreateContext 中检查一下吗? stackoverflow.com/questions/5930274/…
  • 您缺少对 eglCreateSurface 和 eglMakeCurrent() 函数的调用。在创建渲染上下文之前,您必须创建一个表面,然后在创建渲染上下文之后,您必须使其成为当前的。
  • @MaticOblak 我不确定我明白你在说什么。在我的例子的最底部,我完全按照你所说的去做(至少我认为你提供的链接是这样的)。啊,我现在看到您写的是未经编辑的原始帖子。是的,你是对的。我只是使用示例 EGL14.egl....() 来显示该方法属于哪个类。我并不是要暗示它们是使用的静态方法。我将编辑我的问题以避免误解。

标签: java android opengl-es rendering


【解决方案1】:

下面的函数设置 OpenGL ES 1.x 兼容的渲染上下文。您可以修改 create context 函数的属性列表以创建 OpenGL ES 2.x 兼容的渲染上下文

void initializeEGL() {
    //get access to the EGL object, so that we can
    //initialize EGL
    mEGL = (EGL10) EGLContext.getEGL();

    //initialize display
    mDisplay = mEGL.eglGetDisplay(EGL11.EGL_DEFAULT_DISPLAY);
    if(mDisplay == EGL11.EGL_NO_DISPLAY)
    {
        Log.e("SimpleGLView", "Unable to get access to Native Display");
    }

    int[] version = new int[2];
    boolean success = mEGL.eglInitialize(mDisplay, version);
    if(!success) {
        int error = mEGL.eglGetError();
        switch(error) {
        case EGL11.EGL_NOT_INITIALIZED:
            Log.e("SimpleGLView", "Unable to initialize the EGL sub-system");
            break;
        case EGL11.EGL_BAD_DISPLAY:
            Log.e("SimpleGLView", "Display not valid");
            break;
        }
        return;
        }

    int[] mConfigSpec = {   EGL11.EGL_RED_SIZE, 8,
                                EGL11.EGL_GREEN_SIZE, 8,
                                EGL11.EGL_BLUE_SIZE, 8,
                                EGL11.EGL_DEPTH_SIZE, 16,
                                EGL11.EGL_STENCIL_SIZE, 8,
                                EGL11.EGL_NONE };
    EGLConfig[] configs = new EGLConfig[1];
    int[] num_config = new int[1];

    success = mEGL.eglChooseConfig(mDisplay, mConfigSpec, configs, 1, num_config);
    if(success) {
        Log.i("SimpleGLView", "Successfully acquired a surface configuration");
        return;
    }

    mConfig = configs[0];

    mSurface = mEGL.eglCreateWindowSurface(mDisplay,mConfig, holder, null);
    if(mSurface == EGL11.EGL_NO_SURFACE) {
        Log.e("SimpleGLView", "Unable to create surface");
        int error = mEGL.eglGetError();
        switch(error) {
        case EGL11.EGL_BAD_CONFIG:
            Log.e("SimpleGLView", "Invalid configuration selected");
            break;
        case EGL11.EGL_BAD_NATIVE_WINDOW:
            Log.e("SimpleGLView", "Bad native window used");
            break;
        case EGL11.EGL_BAD_ALLOC:
            Log.e("SimpleGLView", "Not enough resources to create a surface");
            break;
        }
        return;
    }

    mContext = mEGL.eglCreateContext(mDisplay, mConfig, EGL11.EGL_NO_CONTEXT, null);
    if(mContext == null) {
        Log.i("SimpleGLView", "Create Context failed");
        return;
    }

    success = mEGL.eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
    if(success) {
        Log.i("SimpleGLView", "Made current");
    }
}

【讨论】:

  • 从代码第 35 行开始的 sn-p 对我来说似乎有点奇怪。如果我成功选择了配置,为什么还要调用 return?这样做不会为线程创建有效的上下文,因为 mEGL.eglMakeCurrent(...) 不会被调用,或者我在这里遗漏了什么?
  • 不管怎样,我找到了一种让它为我的目的工作的方法,你的代码对我帮助很大,谢谢!我会接受你的回答,并在我完成并满意后立即发布我的编辑版本作为答案
  • 你说得对,不需要退货。不知道那是怎么进来的:)。感谢您纠正错误。
  • 小更新:遗憾的是我无法升级到 EGL20,因为我必须使用的库显然不支持它。好吧,它有效,就是这样。再次感谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-29
  • 2013-05-26
  • 1970-01-01
  • 2017-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多