YUVフォーマット(ここではNV12)のDRMバッファのGEM FLINK名がわかっている状態でそこからテクスチャを生成してみる。
GEM FLINKについては
https://qiita.com/maueki/items/8392decdd36c3fa19117#gem-flink
記載するコードについてはエラー処理は省略している。
EGL, GLの初期化
省略
GBMデバイスオープン
int drv_fd = open("/dev/dri/card0", O_RDWR);
struct gbm_device* gbm = gbm_create_device(drv_fd);
PRIME FD取得
GEM FLINK名はuint32_t name
という変数に格納されているものとする。
struct drm_gem_open gem_open = {};
gem_open.name = name;
drmIoctl(gbm_device_get_fd(gbm), DRM_IOCTL_GEM_OPEN, &gem_open));
struct drm_prime_handle prime_handle = {.handle = gem_open.handle, .flags = 0 /* O_CLOSEXEC */ };
drmIoctl(gbm_device_get_fd(gbm), DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_handle);
int fd = prime_handle.fd;
fd
がPRIME FDとなる
eglImageKHR作成
EGL拡張のEGL_EXT_image_dma_buf_importを使用してPRIME FDからeglImageKHRを取得する。
EGLint img_attribs[] = {
EGL_WIDTH, <width>,
EGL_HEIGHT, <height>,
EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV11, /// =0x3231564e,
EGL_DMA_BUF_PLANE0_FD_EXT, fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT, <一行あたりのバイト数(アライメント注意)>,
EGL_DMA_BUF_PLANE1_FD_EXT, fd,
EGL_DMA_BUF_PLANE1_OFFSET_EXT, <PLANE1の開始位置>,
EGL_DMA_BUF_PLANE1_PITCH_EXT, <一行あたりのバイト数(PLANE0と同じはず)>,
EGL_NONE
};
EGLImageKHR eglImage = eglCreateImageKHR(egl_dpy, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, (EGLClientBuffer)0, img_attribs);
PLANEは2まであって、フォーマットに合わせて使い分ける。NV12の場合はYとUVの2面に分かれているのでPLANE0,PLANE1を使用する。
YUVフォーマットについて詳しくは以下参照
http://klabgames.tech.blog.jp.klab.com/archives/1054828175.html
EGL_DMA_BUF_PLANE1_OFFSET_EXT
の値は基本的にEGL_DMA_BUF_PLANE0_PITCH_EXT * EGL_HEIGHT
のはずだが、アライメント調整されることがあるので注意(自分の環境だと32の倍数である必要があってかなりハマった)
テクスチャ作成
GL拡張のGL_OES_EGL_image_externalを使用してテクスチャを作成する。
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
GL_TEXTURE_2D
ではなくGL_TEXTURE_EXTERNAL_OES
を使っていることに注意。
あとは通常のテクスチャ同様に描画すれば内部でNV12 -> RGBの変換を行ってくれる。
描画時の注意
GL_TEXTURE_EXTERNAL_OES
を使っているため、sampler2D
は使用できない。
フラグメントシェーダの先頭に
#extension GL_OES_EGL_image_external: require
を記載し、samplerExternalOES
を使用する。texture2D()
は同様に使用可能。