GraphicBufferは、Androidによって設計された高性能バッファで、次のような優れた機能を備えています。
- 複数のプロセスで渡すことができます
- CPU、GPU、HWCなどの複数のハードウェアデバイス間で共有可能
- EglImageを生成して、TextureまたはrenderBufferにバインドできます
これらの機能で実現できる機能は次のとおりです。
- プロセス間でレンダリング結果を渡す
- GraphicBufferを使用してテクスチャをバインドする場合、CPUとGPU間のデータコピーを減らすことができます
ただし、GraphicBufferを使用する場合は重大な制限があり、Androidソースコード環境で使用する必要があります。
Android7以降、GraphicBufferの使用は制限され、GraphicBufferをNDKで直接使用することはできません。
NDKを介して間接的にGraphicBufferを使用する方法はありますか?
答えは「はい」で、2つのオプションがあります。
- NDKのlibnativewindow.soのAHardwareBuffer_allocateメソッドを使用してAHardwareBufferを作成します。
- EGLのeglCreateNativeClientBufferANDROID拡張機能を使用してEGLClientBufferを作成します
次の2つの方法は別々に導入されます
1. NDK AHardwareBuffer_allocateを使用してAHardwareBufferを作成します
まず、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;
具体的な実装コードは次のとおりです。
#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. EGL eglCreateNativeClientBufferANDROID拡張機能を使用してEGLClientBufferを作成します
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.両方の実装の利点
- AHardwareBuffer_allocateはEGL環境に依存しません
- eglCreateNativeClientBufferANDROIDはEGL環境に依存する必要があります