GraphicBuffer es un búfer de alto rendimiento diseñado por Android. Tiene algunas características superiores, como:
- Puede pasarse en múltiples procesos
- Se puede compartir entre varios dispositivos de hardware, como CPU, GPU, HWC
- EglImage se puede generar y luego enlazar a Texture o renderBuffer
Las funciones que pueden lograr estas características son:
- Transmitir resultados de renderizado a través de procesos
- Al usar GraphicBuffer para unir texturas, puede reducir la copia de datos entre la CPU y la GPU
Sin embargo, existe una seria limitación al usar GraphicBuffer, y debe usarse en el entorno de código fuente de Android.
Después de Android7, el uso de GraphicBuffer está restringido y GraphicBuffer no se puede usar directamente en NDK.
Entonces, ¿hay alguna manera de usar GraphicBuffer indirectamente a través del NDK?
La respuesta es sí y hay dos opciones:
- Cree un AHardwareBuffer a través del método AHardwareBuffer_allocate de libnativewindow.so del NDK
- Cree un EGLClientBuffer a través de la extensión eglCreateNativeClientBufferANDROID de EGL
Los dos métodos siguientes se presentan por separado
1. Cree AHardwareBuffer a través de NDK AHardwareBuffer_allocate
Primero, debe comprender la estructura AHardwareBuffer_Desc
typedef struct AHardwareBuffer_Desc {
uint32_t width; // width in pixels
uint32_t height; // height in pixels
uint32_t layers; // number of images
uint32_t format; // One of AHARDWAREBUFFER_FORMAT_*
uint64_t usage; // Combination of AHARDWAREBUFFER_USAGE_*
uint32_t stride; // Stride in pixels, ignored for AHardwareBuffer_allocate()
uint32_t rfu0; // Initialize to zero, reserved for future use
uint64_t rfu1; // Initialize to zero, reserved for future use
} AHardwareBuffer_Desc;
El código de implementación específico es el siguiente:
#define LOAD_PROC(NAME, TYPE) \
NAME = reinterpret_cast<TYPE>(eglGetProcAddress(# NAME))
// First, load entry points provided by extensions.
LOAD_PROC(glEGLImageTargetTexture2DOES,
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC);
LOAD_PROC(eglGetNativeClientBufferANDROID,
PFNEGLGETNATIVECLIENTBUFFERANDROID);
LOAD_PROC(eglCreateImageKHR, PFNEGLCREATEIMAGEKHRPROC);
LOAD_PROC(glFramebufferTextureMultiviewOVR,
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC);
LOAD_PROC(glFramebufferTextureMultisampleMultiviewOVR,
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC);
// Try creating a 32x32 AHardwareBuffer and attaching it to a multiview
// framebuffer, with various formats and depths.
AHardwareBuffer_Desc desc = {
};
desc.width = 32;
desc.height = 32;
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
const int layers[] = {
2, 4};
const int formats[] = {
AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
// Do not test AHARDWAREBUFFER_FORMAT_BLOB, it isn't color-renderable.
};
const int samples[] = {
1, 2, 4};
for (int nsamples : samples) {
for (auto nlayers : layers) {
for (auto format : formats) {
desc.layers = nlayers;
desc.format = format;
testEglImageArray(env, desc, nsamples);
}
}
}
}
static void testEglImageArray(JNIEnv* env, AHardwareBuffer_Desc desc,
int nsamples) {
AHardwareBuffer* hwbuffer = nullptr;
int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
// Create EGLClientBuffer from the AHardwareBuffer.
EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
// Create EGLImage from EGLClientBuffer.
EGLint attrs[] = {
EGL_NONE};
EGLImageKHR image =
eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, native_buffer, attrs);
// Create OpenGL texture from the EGLImage.
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D_ARRAY, texid);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D_ARRAY, image);
// Create FBO and add multiview attachment.
GLuint fboid;
glGenFramebuffers(1, &fboid);
glBindFramebuffer(GL_FRAMEBUFFER, fboid);
const GLint miplevel = 0;
const GLint base_view = 0;
const GLint num_views = desc.layers;
if (nsamples == 1) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
texid, miplevel, base_view, num_views);
} else {
glFramebufferTextureMultisampleMultiviewOVR(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texid, miplevel, nsamples,
base_view, num_views);
}
glCheckFramebufferStatus(GL_FRAMEBUFFER);
//do some render
glDeleteTextures(1, &texid);
glDeleteFramebuffers(1, &fboid);
AHardwareBuffer_release(hwbuffer);
}
2. Cree EGLClientBuffer mediante la extensión EGL eglCreateNativeClientBufferANDROID
EGLint attrs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT,10,
EGL_RED_SIZE,8,
EGL_GREEN_SIZE,8,
EGL_BLUE_SIZE 8,
EGL_ALPHA_SIZE,8,
EGL_NATIVE_BUFFER_USAGE_ANDROID,EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID,
EGL_NONE };
EGLClientBuffer native_buffer = eglCreateNativeClientBufferANDROID(attrs);
// Create EGLImage from EGLClientBuffer.
EGLint attrs[] = {
EGL_NONE};
EGLImageKHR image =
eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, native_buffer, attrs);
// Create OpenGL texture from the EGLImage.
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D_ARRAY, texid);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D_ARRAY, image);
3. Las ventajas de ambas implementaciones
- AHardwareBuffer_allocate no depende del entorno EGL
- eglCreateNativeClientBufferANDROID necesita depender del entorno EGL