首页 > 代码库 > 安卓下多线程OpenGL共享Context (三)

安卓下多线程OpenGL共享Context (三)

      在上一节中我们介绍了多线程OpenGL绘制方案,但是如果需要在Java线程不断修改纹理数据,会由于并发访问导致Unity线程出现访问非法内存而崩溃。因此,考虑在Java线程加载数据,然后在unity线程调用OpenGL操作更新纹理。这样所有的OpenGL操作都在Unity绘制线程完成,从而避免了多线程OpenGL引入的各种问题。为了能够从Java线程切换到Unity线程执行,我们获取到Unity线程的Looper,然后使用该Looper实例化一个Handler,这样就可以通过往上发送消息或者Runnable在Unity线程执行任务了。Java代码如下:

 1 package com.thornbirds.unity;
 2 
 3 import android.graphics.Bitmap;
 4 import android.graphics.BitmapFactory;
 5 import android.opengl.GLES10;
 6 import android.opengl.GLES11Ext;
 7 import android.opengl.GLES20;
 8 import android.opengl.GLUtils;
 9 import android.os.Handler;
10 import android.os.Looper;
11 import android.util.Log;
12 
13 import java.util.concurrent.ExecutorService;
14 import java.util.concurrent.Executors;
15 
16 public class PluginTexture {
17     private static final String TAG = "PluginTexture";
18 
19     private int mTextureID = 0;
20     private int mTextureWidth = 0;
21     private int mTextureHeight = 0;
22 
23     // 创建单线程池,用于加载图片资源
24     private final ExecutorService mJavaThread = Executors.newSingleThreadExecutor();
25     // 使用Unity线程Looper的Handler,用于执行Java层的OpenGL操作
26     private Handler mUnityRenderHandler;
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 void glLogE(String msg) {
44         Log.e(TAG, msg + ", err=" + GLES10.glGetError());
45     }
46 
47     public void setupOpenGL() {
48         // 注意:该调用一定是从Unity绘制线程发起
49         if (Looper.myLooper() == null) {
50             Looper.prepare();
51         }
52         mUnityRenderHandler = new Handler(Looper.myLooper());
53         // 生成OpenGL纹理ID
54         int textures[] = new int[1];
55         GLES20.glGenTextures(1, textures, 0);
56         if (textures[0] == 0) {
57             glLogE("glGenTextures failed");
58             return;
59         }
60         mTextureID = textures[0];
61         mTextureWidth = 640;
62         mTextureHeight = 360;
63     }
64 
65     public void updateTexture() {
66         mJavaThread.execute(new Runnable() {
67             @Override
68             public void run() {
69                 // 加载图片资源
70                 String imageFilePath = "/sdcard/test/image.png";
71                 final Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
72                 // 切换到Unity绘制线程更新纹理
73                 mUnityRenderHandler.post(new Runnable() {
74                     @Override
75                     public void run() {
76                         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
77                         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
78                         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
79                         GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
80                         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
81                         bitmap.recycle();
82                     }
83                 });
84             }
85         });
86     }
87 
88     public void destroy() {
89         mJavaThread.shutdownNow();
90         mUnityRenderHandler.removeCallbacksAndMessages(null);
91     }
92 }

     至此,我们完整介绍了Unity3D开发中在Android Java Plugin中进行纹理更新的方案和实现方法。基于相同的原理,我们可以很方便地给出iOS Object-C Plugin的实现,此处不在赘述。

安卓下多线程OpenGL共享Context (三)