zoukankan      html  css  js  c++  java
  • 安卓下多线程OpenGL共享Context (二)

          为了在Java线程进行OpenGL调用,需要为java线程初始化OpenGL环境,initOpenGL函数展示了初始化OpenGL环境的过程。在setupOpenGL方法中,在线程上先执行该调用即可。Java代码示例如下:

     1 package com.thornbirds.unity;
     2 
     3 public class PluginTexture {
     4 
     5     private EGLDisplay mEGLDisplay;
     6     private EGLConfig mEglConfig;
     7     private EGLContext mEglContext;
     8     private EGLSurface mEglSurface;
     9 
    10     private void glLogE(String msg) {
    11         Log.e(TAG, msg + ", err=" + GLES10.glGetError());
    12     }
    13 
    14     private void initOpenGL() {
    15         mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    16         if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
    17             glLogE("eglGetDisplay failed");
    18             return;
    19         }
    20 
    21         int[] version = new int[2];
    22         if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
    23             mEGLDisplay = null;
    24             glLogE("eglInitialize failed");
    25             return;
    26         }
    27 
    28         int[] eglConfigAttribList = new int[]{
    29                 EGL14.EGL_RED_SIZE, 8,
    30                 EGL14.EGL_GREEN_SIZE, 8,
    31                 EGL14.EGL_BLUE_SIZE, 8,
    32                 EGL14.EGL_ALPHA_SIZE, 8,
    33                 EGL14.EGL_NONE
    34         };
    35         int[] numEglConfigs = new int[1];
    36         EGLConfig[] eglConfigs = new EGLConfig[1];
    37         if (!EGL14.eglChooseConfig(mEGLDisplay, eglConfigAttribList, 0, eglConfigs, 0,
    38                 eglConfigs.length, numEglConfigs, 0)) {
    39             glLogE("eglGetConfigs failed");
    40             return;
    41         }
    42         mEglConfig = eglConfigs[0];
    43 
    44         int[] eglContextAttribList = new int[]{
    45                 EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
    46                 EGL14.EGL_NONE
    47         };
    48         mEglContext = EGL14.eglCreateContext(mEGLDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
    49                 eglContextAttribList, 0);
    50         if (mEglContext == EGL14.EGL_NO_CONTEXT) {
    51             glLogE("eglCreateContext failed");
    52             return;
    53         }
    54 
    55         int[] surfaceAttribList = {
    56                 EGL14.EGL_WIDTH, 64,
    57                 EGL14.EGL_HEIGHT, 64,
    58                 EGL14.EGL_NONE
    59         };
    60         // Java线程不进行实际绘制,因此创建PbufferSurface而非WindowSurface
    61         mEglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEglConfig, surfaceAttribList, 0);
    62         if (mEglSurface == EGL14.EGL_NO_SURFACE) {
    63             glLogE("eglCreatePbufferSurface failed");
    64             return;
    65         }
    66 
    67         if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEglContext)) {
    68             glLogE("eglMakeCurrent failed");
    69             return;
    70         }
    71         GLES20.glFlush();
    72     }
    73 
    74     public void setupOpenGL() {
    75         mRenderThread.execute(new Runnable() {
    76             @Override
    77             public void run() {
    78                 // 初始化OpenGL环境
    79                 initOpenGL();
    80                 // ...
    81             }
    82         });
    83     }
    84 }

          初始化完OpenGL环境之后,就可以在Java线程中愉快地进行OpenGL调用了。我们在OpenGL线程中调用glGenTextures生成纹理ID(见上一节),然后将纹理ID传递给C#,并与Unity场景中的GameObject绑定。但是,由于OpenGL执行环境是线程独立的,Java线程生成的纹理ID并不能被应用到Unity的渲染线程。所以需要让两个线程共享上下文。

          首先,需要获取到Unity线程的EGLContext,因为setupOpenGL是从Unity线程调用过来的,因此我们在该调用中获取当前线程的EGLContext即可。然后,在创建Java线程的EGLContext时,将Unity线程的EGLContext作为参数传递给eglCreateContext即可。Java示例如下:

     1 package com.thornbirds.unity;
     2 
     3 import java.util.concurrent.ExecutorService;
     4 import java.util.concurrent.Executors;
     5 
     6 public class PluginTexture {
     7 
     8     private volatile EGLContext mSharedEglContext;
     9     private volatile EGLConfig mSharedEglConfig;
    10 
    11     private EGLDisplay mEGLDisplay;
    12     private EGLContext mEglContext;
    13     private EGLSurface mEglSurface;
    14 
    15     private void initOpenGL() {
    16         mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    17         if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
    18             glLogE("eglGetDisplay failed");
    19             return;
    20         }
    21 
    22         int[] version = new int[2];
    23         if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
    24             mEGLDisplay = null;
    25             glLogE("eglInitialize failed");
    26             return;
    27         }
    28 
    29         int[] eglContextAttribList = new int[]{
    30                 EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, // 注意:该值需与Unity绘制线程使用的一致,否则eglCreateContext调用会失败,EGL_BAD_MATCH
    31                 EGL14.EGL_NONE
    32         };
    33         // 注意:创建Java线程的EGLContext时,将Unity线程的EGLContext和EGLConfig作为参数传递给eglCreateContext,
    34         // 从而实现两个线程共享EGLContext
    35         mEglContext = EGL14.eglCreateContext(mEGLDisplay, mSharedEglConfig, mSharedEglContext,
    36                 eglContextAttribList, 0);
    37         if (mEglContext == EGL14.EGL_NO_CONTEXT) {
    38             glLogE("eglCreateContext failed");
    39             return;
    40         }
    41 
    42         int[] surfaceAttribList = {
    43                 EGL14.EGL_WIDTH, 64,
    44                 EGL14.EGL_HEIGHT, 64,
    45                 EGL14.EGL_NONE
    46         };
    47         // Java线程不进行实际绘制,因此创建PbufferSurface而非WindowSurface
    48         // 注意:创建Java线程的EGLSurface时,将Unity线程的EGLConfig作为参数传递给eglCreatePbufferSurface
    49         mEglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mSharedEglConfig, surfaceAttribList, 0);
    50         if (mEglSurface == EGL14.EGL_NO_SURFACE) {
    51             glLogE("eglCreatePbufferSurface failed");
    52             return;
    53         }
    54 
    55         // 由于Java线程只初始化了一个OpenGL执行环境,所以此步是非必需的
    56         if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEglContext)) {
    57             glLogE("eglMakeCurrent failed");
    58             return;
    59         }
    60         GLES20.glFlush();
    61     }
    62 
    63     public void setupOpenGL() {
    64         // 注意:该调用一定是从Unity绘制线程发起
    65         // 获取Unity绘制线程的EGLContext
    66         mSharedEglContext = EGL14.eglGetCurrentContext();
    67         if (mSharedEglContext == EGL14.EGL_NO_CONTEXT) {
    68             glLogE("eglGetCurrentContext failed");
    69             return;
    70         }
    71         EGLDisplay sharedEglDisplay = EGL14.eglGetCurrentDisplay();
    72         if (sharedEglDisplay == EGL14.EGL_NO_DISPLAY) {
    73             glLogE("sharedEglDisplay failed");
    74             return;
    75         }
    76         // 获取Unity绘制线程的EGLConfig
    77         int[] numEglConfigs = new int[1];
    78         EGLConfig[] eglConfigs = new EGLConfig[1];
    79         if (!EGL14.eglGetConfigs(sharedEglDisplay, eglConfigs, 0, eglConfigs.length,
    80                 numEglConfigs, 0)) {
    81             glLogE("eglGetConfigs failed");
    82             return;
    83         }
    84         mSharedEglConfig = eglConfigs[0];
    85         mRenderThread.execute(new Runnable() {
    86             @Override
    87             public void run() {
    88                 // 初始化OpenGL环境
    89                 initOpenGL();
    90                 // ... 
    91             }
    92         });
    93     }
    94 }

          共享上下文之后,两个线程就可以共享纹理了。将Java线程生成的纹理返回给C#线程即可。不过,此方案只适用在Java线程加载纹理,然后给到Unity线程使用。如果需要在Java线程不断修改纹理数据,会由于并发访问导致Unity线程出现访问非法内存而崩溃。所以,如果需要不断更新纹理内容,多线程OpenGL并不可行,至少以笔者目前的OpenGL水平是不可行的。下回继续。

          如果在使用EGL过程中执行调用失败,可以在该网址查看错误码的描述:https://www.khronos.org/registry/EGL/sdk/docs/man/html/

    Java完整代码如下:

      1 package com.thornbirds.unity;
      2 
      3 import android.graphics.Bitmap;
      4 import android.graphics.BitmapFactory;
      5 import android.opengl.EGL14;
      6 import android.opengl.EGLConfig;
      7 import android.opengl.EGLContext;
      8 import android.opengl.EGLDisplay;
      9 import android.opengl.EGLSurface;
     10 import android.opengl.GLES10;
     11 import android.opengl.GLES11Ext;
     12 import android.opengl.GLES20;
     13 import android.opengl.GLUtils;
     14 import android.util.Log;
     15 
     16 import java.util.concurrent.ExecutorService;
     17 import java.util.concurrent.Executors;
     18 
     19 public class PluginTexture {
     20     private static final String TAG = "PluginTexture";
     21     private int mTextureID = 0;
     22     private int mTextureWidth = 0;
     23     private int mTextureHeight = 0;
     24 
     25     // 创建单线程池,用于处理OpenGL纹理
     26     private final ExecutorService mRenderThread = Executors.newSingleThreadExecutor();
     27 
     28     public int getStreamTextureWidth() {
     29         return mTextureWidth;
     30     }
     31 
     32     public int getStreamTextureHeight() {
     33         return mTextureHeight;
     34     }
     35 
     36     public int getStreamTextureID() {
     37         return mTextureID;
     38     }
     39 
     40     public PluginTexture() {
     41     }
     42 
     43     private volatile EGLContext mSharedEglContext;
     44     private volatile EGLConfig mSharedEglConfig;
     45 
     46     private EGLDisplay mEGLDisplay;
     47     private EGLContext mEglContext;
     48     private EGLSurface mEglSurface;
     49 
     50     private void glLogE(String msg) {
     51         Log.e(TAG, msg + ", err=" + GLES10.glGetError());
     52     }
     53 
     54     private void initOpenGL() {
     55         mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
     56         if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
     57             glLogE("eglGetDisplay failed");
     58             return;
     59         }
     60 
     61         int[] version = new int[2];
     62         if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
     63             mEGLDisplay = null;
     64             glLogE("eglInitialize failed");
     65             return;
     66         }
     67 
     68         int[] eglContextAttribList = new int[]{
     69                 EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, // 注意:该值需与Unity绘制线程使用的一致,否则eglCreateContext调用会失败,EGL_BAD_MATCH
     70                 EGL14.EGL_NONE
     71         };
     72         // 注意:创建Java线程的EGLContext时,将Unity线程的EGLContext和EGLConfig作为参数传递给eglCreateContext,
     73         // 从而实现两个线程共享EGLContext
     74         mEglContext = EGL14.eglCreateContext(mEGLDisplay, mSharedEglConfig, mSharedEglContext,
     75                 eglContextAttribList, 0);
     76         if (mEglContext == EGL14.EGL_NO_CONTEXT) {
     77             glLogE("eglCreateContext failed");
     78             return;
     79         }
     80 
     81         int[] surfaceAttribList = {
     82                 EGL14.EGL_WIDTH, 64,
     83                 EGL14.EGL_HEIGHT, 64,
     84                 EGL14.EGL_NONE
     85         };
     86         // Java线程不进行实际绘制,因此创建PbufferSurface而非WindowSurface
     87         // 注意:创建Java线程的EGLSurface时,将Unity线程的EGLConfig作为参数传递给eglCreatePbufferSurface
     88         mEglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mSharedEglConfig, surfaceAttribList, 0);
     89         if (mEglSurface == EGL14.EGL_NO_SURFACE) {
     90             glLogE("eglCreatePbufferSurface failed");
     91             return;
     92         }
     93 
     94         // 由于Java线程只初始化了一个OpenGL执行环境,所以此步是非必需的
     95         if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEglContext)) {
     96             glLogE("eglMakeCurrent failed");
     97             return;
     98         }
     99         GLES20.glFlush();
    100     }
    101 
    102     public void setupOpenGL() {
    103         // 注意:该调用一定是从Unity绘制线程发起
    104         // 获取Unity绘制线程的EGLContext
    105         mSharedEglContext = EGL14.eglGetCurrentContext();
    106         if (mSharedEglContext == EGL14.EGL_NO_CONTEXT) {
    107             glLogE("eglGetCurrentContext failed");
    108             return;
    109         }
    110         EGLDisplay sharedEglDisplay = EGL14.eglGetCurrentDisplay();
    111         if (sharedEglDisplay == EGL14.EGL_NO_DISPLAY) {
    112             glLogE("sharedEglDisplay failed");
    113             return;
    114         }
    115         // 获取Unity绘制线程的EGLConfig
    116         int[] numEglConfigs = new int[1];
    117         EGLConfig[] eglConfigs = new EGLConfig[1];
    118         if (!EGL14.eglGetConfigs(sharedEglDisplay, eglConfigs, 0, eglConfigs.length,
    119                 numEglConfigs, 0)) {
    120             glLogE("eglGetConfigs failed");
    121             return;
    122         }
    123         mSharedEglConfig = eglConfigs[0];
    124         mRenderThread.execute(new Runnable() {
    125             @Override
    126             public void run() {
    127                 // 初始化OpenGL环境
    128                 initOpenGL();
    129 
    130                 // 生成OpenGL纹理ID
    131                 int textures[] = new int[1];
    132                 GLES20.glGenTextures(1, textures, 0);
    133                 if (textures[0] == 0) {
    134                     glLogE("glGenTextures failed");
    135                     return;
    136                 }
    137                 mTextureID = textures[0];
    138                 mTextureWidth = 640;
    139                 mTextureHeight = 360;
    140             }
    141         });
    142     }
    143 
    144     public void updateTexture() {
    145         mRenderThread.execute(new Runnable() {
    146             @Override
    147             public void run() {
    148                 String imageFilePath = "/sdcard/test/image.png";
    149                 final Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
    150 
    151                 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
    152                 GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    153                 GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    154                 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    155                 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    156 
    157                 bitmap.recycle();
    158             }
    159         });
    160     }
    161 
    162     public void destroy() {
    163         mRenderThread.shutdownNow();
    164     }
    165 }
    View Code
  • 相关阅读:
    Asp.Net.Core 系列-中间件和依赖注入Hosting篇
    Asp.Net.Core 系列-中间件和依赖注入进阶篇
    Asp.Net.Core 系列-中间件和依赖注入基础篇
    修饰符总结
    CSS3边框border知识点
    浅谈CSS中的居中
    c#中的委托和事件
    c#基础知识复习-static
    c#基础知识复习
    Bfc的理解
  • 原文地址:https://www.cnblogs.com/moderate-fish/p/6893392.html
Copyright © 2011-2022 走看看