安卓下多线程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 }

时间: 2024-08-05 07:15:29

安卓下多线程OpenGL共享Context (二)的相关文章

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

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

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

之前的方案假定Java层更新纹理时使用的是RGB或RBGA格式的数据,但是在播放视频这种应用场景下,解码器解码出来的数据如果是YUV格式,渲染起来就比较麻烦了.一种方式是使用CPU进行YUV转RGB,然后再进行渲染,但是这种方式性能极差:另一种方式是使用GPU进行转换,利用GPU的并行计算能力加速转换.我们需要编写Shader来实现.如前文所述,Unity只需要Java层的纹理ID,当使用Shader进行YUV转RGB时,怎么实现更新该纹理的数据呢?答案是Render to Texture (参

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

最近在研究Unity3D开发中使用Java Plugin进行纹理更新,想法很简单,在Java线程更新纹理数据,然后Unity场景中的纹理将自动更新. 首先,创建Java类,定义创建纹理及获取纹理参数的接口,并创建单线程池用于进行加载Bitmap并绑定纹理数据等OpenGL操作.Java代码示例: 1 package com.thornbirds.unity; 2 3 import android.graphics.Bitmap; 4 import android.graphics.BitmapF

phonegap安卓环境下使用BarcodeScanner插件扫描二维码教程

由于一直在使用phoneGap来开发安卓应用,而对于原生Java小白的我最近这几天一直陷入了如何使用phonegap的BarcodeScanner插件这件事情上,可以说查遍了百度和Google,虽然只是一个小小的二维码的功能,但是这里面还是让我学到了许多开发安卓应用的内容,一起共勉吧. 首先,告诉大家的是 1.我使用的phonegap版本是2.9.0,当然对于phonegap版本的东西我也不太了解,不过可以保证的是phonegap2.0.0之后的按照我这种办法来实现二维码是没有问题的. 2.ph

一、掌握Samba服务的配置原理;二、在linux下配置samba共享服务

一.掌握Samba服务的配置原理二.在linux下配置samba共享服务(匿名共享.身份验证.账户映射.访问控制)Samba介绍:Samba是运行在Linux与Unix之上的免费共享服务的软件,是典型服务器/客户端模式,它有两个进程,一个是smbd,主要是用来管理共享文件的进程,另一个是nmbd,用来实现主机名到IP地址的转换(相当于Windows下的NetBIOS的功能),smbd运行在TCP的139.445端口,nmbd则运行在UDP的137.138端口(运行samba服务后可以用如:net

JAVA多线程之间共享数据BlockingQueue介绍

在JAVA的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利.本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景. 一.认识BlockingQueue 阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所起的作用大致如下图所示:  从上图我们可以很清楚看到,通过一个共享的队列,可以使得数据由队列的一端输入,从

安卓笔记2——安卓下的测试和数据存储方式

今天开始介绍安卓的另一个基础知识,安卓下的测试和数据存储的几种方式. 以后后同步发出对应笔记.老规矩,用一张图来介绍今天的内容. 图片看不清的话可以右键新窗口打开. 一.测试 1,分类 黑盒测试: 是以用户的角度,从输入数据与输出数据的对应关系出发进行测试的. 白盒测试: 又称结构测试.透明盒测试.逻辑驱动测试或基于代码的测试. 单元测试: 又称模块测试,是开发者编写的一小段代码,用于检验被测代码的一个很小的.很明确的功能是否正确. 功能测试: 根据产品特性.操作描述和用户方案,测试一个产品的特

linux下多线程编程

最近研究mysql源码,各种锁,各种互斥,好在我去年认真学了<unix环境高级编程>, 虽然已经忘得差不多了,但是学过始终是学过,拿起来也快.写这篇文章的目的就是总结linux 下多线程编程,作为日后的参考资料. 本文将介绍linux系统下多线程编程中,线程同步的各种方法.包括: 互斥量(mutex) 读写锁 条件变量 信号量 文件互斥 在介绍不同的线程同步的方法之前,先简单的介绍一下进程和线程的概念, 它们的优缺点,线程相关的API,读者——写者问题和哲学家就餐问题. 基础知识 1. 进程和

用redis实现tomcat集群下的session共享

上篇实现了 LINUX中NGINX反向代理下的TOMCAT集群(http://www.cnblogs.com/yuanjava/p/6850764.html) 这次我们在上篇的基础上实现session 共享问题 Nginx机器:192.168.1.108 两台tomcat机器分别是:192.168.1.168 192.168.1.178 一:测试session共享问题 在原index.jsp页面添加如下代码 SessionId:<%= session.getId() %> <% Stri