如何不使用Java编写Android应用?C/C++帧缓冲访问实现求助
在Android Studio中用C/C++实现光线追踪图像输出(帧缓冲访问替代方案)
由于Android原生禁止直接访问系统帧缓冲(高版本无权限,低版本需root),且这种方式无法利用GPU合成能力,推荐用EGL + OpenGL ES的方案,在C/C++层完成光线追踪计算后,通过OpenGL将像素数据渲染到屏幕,完全绕开Java层的性能损耗。
步骤1:配置Android Studio NDK环境
在Module级build.gradle中开启NDK和CMake支持,同时开启最高优化:
android { defaultConfig { externalNativeBuild { cmake { cppFlags "-std=c++17 -O3 -mfpu=neon" // 开启C++17、O3优化和NEON指令 arguments "-DANDROID_STL=c++_shared" } } } externalNativeBuild { cmake { path "CMakeLists.txt" } } }
步骤2:C/C++层实现EGL初始化与图像渲染
核心是通过EGL创建OpenGL上下文,将光线追踪生成的像素缓冲区作为纹理渲染到屏幕:
#include <EGL/egl.h> #include <GLES3/gl3.h> #include <android/native_window.h> #include <cstdlib> // 全局资源 EGLDisplay eglDisplay; EGLContext eglContext; EGLSurface eglSurface; GLuint frameTexture; int screenW, screenH; GLuint shaderProgram; GLuint vao; // 编译着色器辅助函数(实际开发需补充错误检查) static GLuint compileShader(GLenum type, const char* source) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); return shader; } // 初始化EGL与OpenGL extern "C" JNIEXPORT jboolean JNICALL Java_com_example_raytrace_MainActivity_initNative(JNIEnv* env, jobject thiz, jobject surface) { ANativeWindow* window = ANativeWindow_fromSurface(env, surface); // 初始化EGL eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(eglDisplay, nullptr, nullptr); const EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_NONE }; EGLConfig config; EGLint numConfigs; eglChooseConfig(eglDisplay, configAttribs, &config, 1, &numConfigs); eglSurface = eglCreateWindowSurface(eglDisplay, config, window, nullptr); ANativeWindow_release(window); const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; eglContext = eglCreateContext(eglDisplay, config, EGL_NO_CONTEXT, contextAttribs); eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); // 获取屏幕尺寸 eglQuerySurface(eglDisplay, eglSurface, EGL_WIDTH, &screenW); eglQuerySurface(eglDisplay, eglSurface, EGL_HEIGHT, &screenH); // 创建纹理存储光线追踪像素 glGenTextures(1, &frameTexture); glBindTexture(GL_TEXTURE_2D, frameTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenW, screenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); // 编译全屏着色器 const char* vertexShader = "#version 300 es\nin vec2 aPos;out vec2 texCoord;void main(){gl_Position=vec4(aPos,0.0,1.0);texCoord=(aPos+1.0)/2.0;}"; const char* fragmentShader = "#version 300 es\nprecision mediump float;in vec2 texCoord;uniform sampler2D frameTex;out vec4 fragColor;void main(){fragColor=texture(frameTex,texCoord);}"; GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShader); GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragmentShader); shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vs); glAttachShader(shaderProgram, fs); glLinkProgram(shaderProgram); glUseProgram(shaderProgram); glDeleteShader(vs); glDeleteShader(fs); // 设置全屏顶点数据 float vertices[] = {-1.0f,-1.0f, 1.0f,-1.0f, -1.0f,1.0f, 1.0f,1.0f}; GLuint vbo; glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), nullptr); glEnableVertexAttribArray(0); return JNI_TRUE; } // 更新纹理并渲染(替换随机像素为你的光线追踪计算逻辑) extern "C" JNIEXPORT void JNICALL Java_com_example_raytrace_MainActivity_renderNative(JNIEnv* env, jobject thiz) { // 1. 生成RGBA像素数据(示例用随机值) uint8_t* pixelData = new uint8_t[screenW * screenH * 4]; for(int i=0; i<screenW*screenH*4; i+=4){ pixelData[i] = rand()%255; // R pixelData[i+1] = rand()%255; // G pixelData[i+2] = rand()%255; // B pixelData[i+3] = 255; // A } // 2. 更新纹理数据 glBindTexture(GL_TEXTURE_2D, frameTexture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenW, screenH, GL_RGBA, GL_UNSIGNED_BYTE, pixelData); delete[] pixelData; // 3. 渲染到屏幕 glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); eglSwapBuffers(eglDisplay, eglSurface); } // 释放资源 extern "C" JNIEXPORT void JNICALL Java_com_example_raytrace_MainActivity_cleanupNative(JNIEnv* env, jobject thiz) { eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(eglDisplay, eglSurface); eglDestroyContext(eglDisplay, eglContext); eglTerminate(eglDisplay); glDeleteTextures(1, &frameTexture); glDeleteVertexArrays(1, &vao); }
步骤3:Java层传递Surface到Native层
用SurfaceView获取渲染表面,将其传给C/C++层初始化EGL:
import android.app.Activity; import android.os.Bundle; import android.view.SurfaceHolder; import android.view.SurfaceView; public class MainActivity extends Activity { static { System.loadLibrary("raytrace"); } private native boolean initNative(Object surface); private native void renderNative(); private native void cleanupNative(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SurfaceView surfaceView = new SurfaceView(this); setContentView(surfaceView); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { initNative(holder.getSurface()); // 启动渲染线程,避免阻塞UI new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { renderNative(); try { Thread.sleep(16); // 控制帧率 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(SurfaceHolder holder) { cleanupNative(); } }); } }
性能优化要点
- 多线程并行计算:用C++11
std::thread或OpenMP(CMake加-fopenmp编译选项)拆分光线追踪任务 - 内存对齐:用
aligned_alloc分配像素缓冲区,对齐到16字节适配NEON指令 - 避免跨层拷贝:直接在Native堆生成像素数据,不要通过Java层传递
内容的提问来源于stack exchange,提问作者niazlv
相关产品推荐
相关产品推荐

