上面说明了EGL的大致流程,接下来,看看如何在NDK中使用它。
首先创建一个SurfaceView.并为该SurfaceView设置一个回调监听,即SurfaceHolder.Callback.如下:
private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
native_surfaceCreate(surfaceHolder.getSurface());
Timber.v("surfaceCreated");
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int width, int height) {
native_surfaceChange(width, height);
Timber.v("surfaceChanged");
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Timber.v("surfaceDestroyed");
native_surfaceDestroy();
native_surfaceCreated,将会在c++ 层面创建一个绘制线程,在此不表,不过可以使用的api较多,如c++的std::thread,或者pthread,或者android独有的Android::Thread(只有系统编程人员可用)。在线程创建初期,进行EGL的初始化如下:
try {
getDisplay();
initilize();
chooseConfig();
createContext();
lostContext = false;
catch (const std::runtime_error error) {
ALOGE("error %s", error.what());
throw error;
初始化完成之后,接下来将SurfaceView创建的surface,传递给NDK中使用。
注意注意:前面说过SurfaceHolder.Callback的回调surfaceCreated,就已经创建好了surface,那么应该怎么传递给EGL使用呢?
这里就不得不说到,android的SurfaceFlinger和SurfaceView了。
1.SurfaceFlinger提供了一种机制,让别人能够调用SurfaceFlinger,创建一种图形产生和消耗的数据结构BufferQueue.这种机制之一叫做SurfaceControl.
2. SurfaceView使用SurfaceControl,让SurfaceFlinger产生一个BufferQueue.这个BufferQueue一头是SurfaceFlinger(消耗方).一头是SurfaceView(生产方). 这个BufferQueue创建出来的绘制区域都可以称为Surface。
3. 因此SurfaceHolder.Callback中的回调surfaceCreated,表示SurfaceView(生产方)使用的绘制区域已经创建完成。
4. 这个绘制区域在C层的实现就是使用的EGLNativeWindowType。因此调用eglCreateWindowSurface,就能创建对应的能在NDK中使用的绘制区域的代理对象了
从surface的java对象中获取,EGLNativeWindowType对象,如下:
JNIEXPORT void JNICALL
Java_cn_xxxx_native_surfaceCreate(JNIEnv *env,jobject thiz,jobject surface) {
ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
if (window == nullptr) {
ALOGE("can't get native window from surface object");
return;
if (window != nullptr) {
ANativeWindow_release(window);
window = nullptr;
其中ANativeWindow就是EGLNativeWindowType
故下面的代码直接创建EGLSurface
void xxxEgl::createSurface(EGLNativeWindowType win) {
EGLint attr_none[] = {EGL_NONE};
eglSurface = eglCreateWindowSurface(display, firstConfig, win, attr_none);
if (eglSurface == NULL && eglGetError() != EGL_SUCCESS) {
wrapMessageOrException(string("create surface error reason:") + to_string(eglGetError()));
if (eglMakeCurrent(display, eglSurface, eglSurface, context) == EGL_FALSE ||
eglGetError() != EGL_SUCCESS) {
wrapMessageOrException(
string("create new surface context error reason:") + to_string(eglGetError()));
接下来就是native_surfaceChange了。在这里面,将初始化skia库,并进行绘制我们想要的图片。
初始化skia的步骤如下
void xxxSkia::initEnvironment(int32_t width, int32_t height) {
sk_sp<const GrGLInterface> interface(GrGLCreateNativeInterface());
SkASSERT(NULL != interface);
sk_sp<GrContext> grContext(
GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) interface.get()));
SkASSERT(grContext);
GrGLint buffer;
GR_GL_GetIntegerv(interface.get(), GR_GL_FRAMEBUFFER_BINDING, &buffer);
GrGLFramebufferInfo info;
info.fFBOID = (GrGLuint) buffer;
GrBackendRenderTarget target(width, height, sampleCount, stencilBit,
kSkia8888_GrPixelConfig, info);
SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
surface = SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
kBottomLeft_GrSurfaceOrigin,
nullptr, &props).release();
mSurfaceHeight = height;
mSurfaceWidth = width;
canvas = surface->getCanvas();
我们将使用skia的gpu后端,因此我们使用SkSurface::MakeFromBackendRenderTarget来创建对应的SkSurface对象。
在创建SkSurface对象时,需要传递,上下文接口,即grContext对象代表的逻辑。还需要传递目标绘制区域的一些信息,即target对象代表的逻辑。
创建完成之后,就可以使用skia的api进行绘制了。
那么需要在native_surfaceDestroy中,销毁必要的对象,除了EGL提及的,surface,context,display需要销毁以外,还需要销毁skia库中对应的SkSurface对象。请注意,请依照后创建先销毁的顺序,进行销毁
特别要注意的一点是:skia库,使用GPU后端进行渲染时。skia库可能创建一个gpu渲染上下文,因此有必要在绘制的地方,进行线程同步。比如,在我的例子中,进行了如下的同步:
void xxxSkia::draw() {
std::lock_guard<std::mutex> _l(mutex);
ATRACE_CALL();
canvas->drawColor(SK_ColorWHITE);
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(40);
paint.setTextSize(40);
paint.setColor(0xfffe938c);
canvas->drawString("please set view", 100, 100, paint);
ALOGD("this %p,context %p,surface %p",this,eglGetCurrentContext(),eglGetCurrentSurface(EGL_DRAW));
canvas->flush();
使用上面的例子,成功将javacanvas的绘制,传递到了ndk中实现。附上一张实现结果图:

Android NDK-EGL 初级在最近的工作中,发现很少有资料直接介绍android EGL的。在翻越GLSurfaceView和Skia的源码之后,将我自己的NDK-EGL编程整理如下,供有需要的开发者取用什么是EGLEGL at a glanceEGL provides mechanisms for creating rendering surfaces onto which client APIs like OpenGL ES and OpenVG can draw, creates g
本文介绍在Android中与图像架构相关的概念或类Surface、SurfaceHolder、EGLSurface、SurfaceView、GLSurfaceView、SurfaceTexture、TextureView、SurfaceFlinger 和 Vulkan 的知识。
本页将介绍 Android 系统级图形架构的基本元素,以及应用框架和多媒体系统如何使用这些元素。我们会重点介绍图形数据的缓冲区是如何在系统中移动的。 如果您想了解 SurfaceView 和 TextureView .
EGL :是渲染 API(如 OpenGL ES)和原生窗口系统之间的接口, 与 opengl 对接操作GPU 能力,android使用的是 openGL ES
EGL10 11 14
OpenGL:是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态,但是当涉及到与本地窗口系...
指定显示连接,默认连接为EGL_DEFAULT_DISPLAY
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
打开连接之后,需要初始化EGL
major 指定EGL实现返回的主版本号,可能为NULL
minor 指定EGL实现返回的次版本号,可能为NULL
EGLint major, minor;
if (!eglInitialize(display, &major, &minor)){
OpenGl是一套跨平台的接口,它与各个平台本地窗口系统之间的交互,是借助于一个中间控制层,这个中间控制层就是EGL。EGL也有自己的一套标准API,由各个平台的系统来完成其具体实现。
EGL是OpenGL和本地窗口体系进行联系的桥梁,负责管理OpenGL的运行状态、渲染图像到本地窗口或缓冲区等功能。
在Android中,OpenGL的每一步处理,都需要依赖于EGL提供的这些相关功能支持,所以必须先创建EGL环境,才能正常进行OpenGL处理...
./android-ndk-r25c/sources/android/native_app_glue/Android.mk ./android-ndk-r25c/sources/android/support/Android.mk ./android-ndk-r25c/sources/android/ndk_helper/Android.mk ./android-ndk-r25c/sources/android/cpufeatures/Android.mk ./android-ndk-r25c/sources/cxx-stl/llvm-libc++abi/Android.mk ./android-ndk-r25c/sources/cxx-stl/llvm-libc++/Android.mk ./android-ndk-r25c/sources/third_party/googletest/Android.mk ./android-ndk-r25c/sources/third_party/shaderc/libshaderc_util/Android.mk ./android-ndk-r25c/sources/third_party/shaderc/third_party/glslang/Android.mk ./android-ndk-r25c/sources/third_party/shaderc/third_party/spirv-tools/Android.mk ./android-ndk-r25c/sources/third_party/shaderc/third_party/Android.mk ./android-ndk-r25c/sources/third_party/shaderc/libshaderc/Android.mk ./android-ndk-r25c/sources/third_party/shaderc/Android.mk ./android-ndk-r25c/sources/third_party/vulkan/src/build-android/jni/Android.mk哪个是NDK编译的mk
这些都是 Android NDK 内部的 `Android.mk` 文件。其中,`./android-ndk-r25c/sources/android/native_app_glue/Android.mk` 是用于编译 Native Activity 示例应用程序的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/support/Android.mk` 是包含一些 Android 支持库的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/ndk_helper/Android.mk` 是包含一些辅助函数和类的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/cpufeatures/Android.mk` 是用于编译 `cpufeatures` 库的 `Android.mk` 文件,该库提供了一些 CPU 相关的信息和功能;`./android-ndk-r25c/sources/cxx-stl/llvm-libc++abi/Android.mk` 和 `./android-ndk-r25c/sources/cxx-stl/llvm-libc++/Android.mk` 是用于编译 C++ STL 库的 `Android.mk` 文件,分别对应 libc++abi 和 libc++ 两个 STL 库;`./android-ndk-r25c/sources/third_party/googletest/Android.mk` 是用于编译 Google Test 测试框架的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/Android.mk` 是用于编译 Shaderc 编译器的 `Android.mk` 文件,该编译器可以将 GLSL 代码编译成 SPIR-V 代码;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc/Android.mk` 是用于编译 Shaderc 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc_util/Android.mk` 是用于编译 Shaderc Util 库的 `Android.mk` 文件,该库提供了一些辅助函数和类;`./android-ndk-r25c/sources/third_party/shaderc/third_party/Android.mk` 是用于编译 Shaderc 编译器依赖的第三方库的 `Android.mk` 文件,包括 glslang 和 spirv-tools 两个库;`./android-ndk-r25c/sources/third_party/shaderc/third_party/glslang/Android.mk` 是用于编译 glslang 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/third_party/spirv-tools/Android.mk` 是用于编译 spirv-tools 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/vulkan/src/build-android/jni/Android.mk` 是用于编译 Vulkan 库的 `Android.mk` 文件。
如果您要在 Android NDK 中编写自己的 `Android.mk` 文件,可以参考这些示例文件。