Commit 4c259455 authored by Julien Isorce's avatar Julien Isorce Committed by Commit Bot

Can export a gl texture as dmabuf

First create an EGLImage from a given gl texture.
Then export the EGLImage as dmabuf.

In addition to the new unit test included in this CL,
this will be useful for the subsequent CL,
https://chromium-review.googlesource.com/c/chromium/src/+/766787
which will enable existing VaapiDrmPicture when passing --use-gl=egl.
While existing VaapiTFPPicture(X11) will be left for --use-gl=desktop.

Added unit test ui/gl/gl_image_native_pixmap_unittest.cc
The test is enabled on non-ozone but should work on ozone too.
If any of the pre-conditions fail then the test is skipped.
Specifically it requires the extension EGL_MESA_image_dma_buf_export.
So it works on Intel, AMDGPU and nouveau drivers but skipped on NVIDIA.

Bug: 785201
Test: gl_unittests --gtest_filter=*NativePixmap*

Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: Id9b4e56aaaaf5fd9994f4b144ce74a234d848253
Reviewed-on: https://chromium-review.googlesource.com/766687
Commit-Queue: Julien Isorce <julien.isorce@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#522052}
parent a302a709
...@@ -59,7 +59,8 @@ GpuMemoryBufferFactoryAndroidHardwareBuffer::CreateImageForGpuMemoryBuffer( ...@@ -59,7 +59,8 @@ GpuMemoryBufferFactoryAndroidHardwareBuffer::CreateImageForGpuMemoryBuffer(
scoped_refptr<gl::GLImageEGL> image(new gl::GLImageAHardwareBuffer(size)); scoped_refptr<gl::GLImageEGL> image(new gl::GLImageAHardwareBuffer(size));
EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer); EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer);
if (!image->Initialize(EGL_NATIVE_BUFFER_ANDROID, client_buffer, attribs)) { if (!image->Initialize(EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
client_buffer, attribs)) {
DLOG(ERROR) << "Failed to create GLImage " << size.ToString(); DLOG(ERROR) << "Failed to create GLImage " << size.ToString();
image = nullptr; image = nullptr;
} }
......
...@@ -433,6 +433,10 @@ test("gl_unittests") { ...@@ -433,6 +433,10 @@ test("gl_unittests") {
sources += [ "gl_image_ahardwarebuffer_unittest.cc" ] sources += [ "gl_image_ahardwarebuffer_unittest.cc" ]
} }
if (is_desktop_linux) {
sources += [ "gl_image_native_pixmap_unittest.cc" ]
}
include_dirs = [ "//third_party/khronos" ] include_dirs = [ "//third_party/khronos" ]
deps = [ deps = [
......
...@@ -1941,6 +1941,16 @@ EGL_FUNCTIONS = [ ...@@ -1941,6 +1941,16 @@ EGL_FUNCTIONS = [
'GL_CHROMIUM_egl_android_native_fence_sync_hack']}], 'GL_CHROMIUM_egl_android_native_fence_sync_hack']}],
'arguments': 'arguments':
'EGLDisplay dpy, EGLSyncKHR sync' }, 'EGLDisplay dpy, EGLSyncKHR sync' },
{ 'return_type': 'EGLBoolean',
'versions': [{ 'name': 'eglExportDMABUFImageMESA',
'extensions': ['EGL_MESA_image_dma_buf_export'] }],
'arguments': 'EGLDisplay dpy, EGLImageKHR image, int* fds, EGLint* strides, '
'EGLint* offsets', },
{ 'return_type': 'EGLBoolean',
'versions': [{ 'name': 'eglExportDMABUFImageQueryMESA',
'extensions': ['EGL_MESA_image_dma_buf_export'] }],
'arguments': 'EGLDisplay dpy, EGLImageKHR image, int* fourcc, '
'int* num_planes, EGLuint64KHR* modifiers', },
{ 'return_type': 'EGLBoolean', { 'return_type': 'EGLBoolean',
'versions': [{ 'name': 'eglGetCompositorTimingANDROID', 'versions': [{ 'name': 'eglGetCompositorTimingANDROID',
'extensions': [ 'extensions': [
......
...@@ -65,6 +65,16 @@ EGLBoolean eglDestroyStreamKHRFn(EGLDisplay dpy, EGLStreamKHR stream) override; ...@@ -65,6 +65,16 @@ EGLBoolean eglDestroyStreamKHRFn(EGLDisplay dpy, EGLStreamKHR stream) override;
EGLBoolean eglDestroySurfaceFn(EGLDisplay dpy, EGLSurface surface) override; EGLBoolean eglDestroySurfaceFn(EGLDisplay dpy, EGLSurface surface) override;
EGLBoolean eglDestroySyncKHRFn(EGLDisplay dpy, EGLSyncKHR sync) override; EGLBoolean eglDestroySyncKHRFn(EGLDisplay dpy, EGLSyncKHR sync) override;
EGLint eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy, EGLSyncKHR sync) override; EGLint eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy, EGLSyncKHR sync) override;
EGLBoolean eglExportDMABUFImageMESAFn(EGLDisplay dpy,
EGLImageKHR image,
int* fds,
EGLint* strides,
EGLint* offsets) override;
EGLBoolean eglExportDMABUFImageQueryMESAFn(EGLDisplay dpy,
EGLImageKHR image,
int* fourcc,
int* num_planes,
EGLuint64KHR* modifiers) override;
EGLBoolean eglGetCompositorTimingANDROIDFn(EGLDisplay dpy, EGLBoolean eglGetCompositorTimingANDROIDFn(EGLDisplay dpy,
EGLSurface surface, EGLSurface surface,
EGLint numTimestamps, EGLint numTimestamps,
......
...@@ -161,6 +161,8 @@ void DriverEGL::InitializeExtensionBindings() { ...@@ -161,6 +161,8 @@ void DriverEGL::InitializeExtensionBindings() {
ext.b_EGL_KHR_swap_buffers_with_damage = ext.b_EGL_KHR_swap_buffers_with_damage =
HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage"); HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage");
ext.b_EGL_KHR_wait_sync = HasExtension(extensions, "EGL_KHR_wait_sync"); ext.b_EGL_KHR_wait_sync = HasExtension(extensions, "EGL_KHR_wait_sync");
ext.b_EGL_MESA_image_dma_buf_export =
HasExtension(extensions, "EGL_MESA_image_dma_buf_export");
ext.b_EGL_NV_post_sub_buffer = ext.b_EGL_NV_post_sub_buffer =
HasExtension(extensions, "EGL_NV_post_sub_buffer"); HasExtension(extensions, "EGL_NV_post_sub_buffer");
ext.b_EGL_NV_stream_consumer_gltexture_yuv = ext.b_EGL_NV_stream_consumer_gltexture_yuv =
...@@ -197,6 +199,18 @@ void DriverEGL::InitializeExtensionBindings() { ...@@ -197,6 +199,18 @@ void DriverEGL::InitializeExtensionBindings() {
GetGLProcAddress("eglDestroyStreamKHR")); GetGLProcAddress("eglDestroyStreamKHR"));
} }
if (ext.b_EGL_MESA_image_dma_buf_export) {
fn.eglExportDMABUFImageMESAFn =
reinterpret_cast<eglExportDMABUFImageMESAProc>(
GetGLProcAddress("eglExportDMABUFImageMESA"));
}
if (ext.b_EGL_MESA_image_dma_buf_export) {
fn.eglExportDMABUFImageQueryMESAFn =
reinterpret_cast<eglExportDMABUFImageQueryMESAProc>(
GetGLProcAddress("eglExportDMABUFImageQueryMESA"));
}
if (ext.b_EGL_ANDROID_get_frame_timestamps) { if (ext.b_EGL_ANDROID_get_frame_timestamps) {
fn.eglGetCompositorTimingANDROIDFn = fn.eglGetCompositorTimingANDROIDFn =
reinterpret_cast<eglGetCompositorTimingANDROIDProc>( reinterpret_cast<eglGetCompositorTimingANDROIDProc>(
...@@ -464,6 +478,25 @@ EGLint EGLApiBase::eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy, ...@@ -464,6 +478,25 @@ EGLint EGLApiBase::eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy,
return driver_->fn.eglDupNativeFenceFDANDROIDFn(dpy, sync); return driver_->fn.eglDupNativeFenceFDANDROIDFn(dpy, sync);
} }
EGLBoolean EGLApiBase::eglExportDMABUFImageMESAFn(EGLDisplay dpy,
EGLImageKHR image,
int* fds,
EGLint* strides,
EGLint* offsets) {
return driver_->fn.eglExportDMABUFImageMESAFn(dpy, image, fds, strides,
offsets);
}
EGLBoolean EGLApiBase::eglExportDMABUFImageQueryMESAFn(
EGLDisplay dpy,
EGLImageKHR image,
int* fourcc,
int* num_planes,
EGLuint64KHR* modifiers) {
return driver_->fn.eglExportDMABUFImageQueryMESAFn(dpy, image, fourcc,
num_planes, modifiers);
}
EGLBoolean EGLApiBase::eglGetCompositorTimingANDROIDFn( EGLBoolean EGLApiBase::eglGetCompositorTimingANDROIDFn(
EGLDisplay dpy, EGLDisplay dpy,
EGLSurface surface, EGLSurface surface,
...@@ -916,6 +949,28 @@ EGLint TraceEGLApi::eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy, ...@@ -916,6 +949,28 @@ EGLint TraceEGLApi::eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy,
return egl_api_->eglDupNativeFenceFDANDROIDFn(dpy, sync); return egl_api_->eglDupNativeFenceFDANDROIDFn(dpy, sync);
} }
EGLBoolean TraceEGLApi::eglExportDMABUFImageMESAFn(EGLDisplay dpy,
EGLImageKHR image,
int* fds,
EGLint* strides,
EGLint* offsets) {
TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::eglExportDMABUFImageMESA")
return egl_api_->eglExportDMABUFImageMESAFn(dpy, image, fds, strides,
offsets);
}
EGLBoolean TraceEGLApi::eglExportDMABUFImageQueryMESAFn(
EGLDisplay dpy,
EGLImageKHR image,
int* fourcc,
int* num_planes,
EGLuint64KHR* modifiers) {
TRACE_EVENT_BINARY_EFFICIENT0("gpu",
"TraceGLAPI::eglExportDMABUFImageQueryMESA")
return egl_api_->eglExportDMABUFImageQueryMESAFn(dpy, image, fourcc,
num_planes, modifiers);
}
EGLBoolean TraceEGLApi::eglGetCompositorTimingANDROIDFn( EGLBoolean TraceEGLApi::eglGetCompositorTimingANDROIDFn(
EGLDisplay dpy, EGLDisplay dpy,
EGLSurface surface, EGLSurface surface,
...@@ -1509,6 +1564,39 @@ EGLint DebugEGLApi::eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy, ...@@ -1509,6 +1564,39 @@ EGLint DebugEGLApi::eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy,
return result; return result;
} }
EGLBoolean DebugEGLApi::eglExportDMABUFImageMESAFn(EGLDisplay dpy,
EGLImageKHR image,
int* fds,
EGLint* strides,
EGLint* offsets) {
GL_SERVICE_LOG("eglExportDMABUFImageMESA"
<< "(" << dpy << ", " << image << ", "
<< static_cast<const void*>(fds) << ", "
<< static_cast<const void*>(strides) << ", "
<< static_cast<const void*>(offsets) << ")");
EGLBoolean result =
egl_api_->eglExportDMABUFImageMESAFn(dpy, image, fds, strides, offsets);
GL_SERVICE_LOG("GL_RESULT: " << result);
return result;
}
EGLBoolean DebugEGLApi::eglExportDMABUFImageQueryMESAFn(
EGLDisplay dpy,
EGLImageKHR image,
int* fourcc,
int* num_planes,
EGLuint64KHR* modifiers) {
GL_SERVICE_LOG("eglExportDMABUFImageQueryMESA"
<< "(" << dpy << ", " << image << ", "
<< static_cast<const void*>(fourcc) << ", "
<< static_cast<const void*>(num_planes) << ", "
<< static_cast<const void*>(modifiers) << ")");
EGLBoolean result = egl_api_->eglExportDMABUFImageQueryMESAFn(
dpy, image, fourcc, num_planes, modifiers);
GL_SERVICE_LOG("GL_RESULT: " << result);
return result;
}
EGLBoolean DebugEGLApi::eglGetCompositorTimingANDROIDFn( EGLBoolean DebugEGLApi::eglGetCompositorTimingANDROIDFn(
EGLDisplay dpy, EGLDisplay dpy,
EGLSurface surface, EGLSurface surface,
......
...@@ -91,6 +91,18 @@ typedef EGLBoolean(GL_BINDING_CALL* eglDestroySyncKHRProc)(EGLDisplay dpy, ...@@ -91,6 +91,18 @@ typedef EGLBoolean(GL_BINDING_CALL* eglDestroySyncKHRProc)(EGLDisplay dpy,
typedef EGLint(GL_BINDING_CALL* eglDupNativeFenceFDANDROIDProc)( typedef EGLint(GL_BINDING_CALL* eglDupNativeFenceFDANDROIDProc)(
EGLDisplay dpy, EGLDisplay dpy,
EGLSyncKHR sync); EGLSyncKHR sync);
typedef EGLBoolean(GL_BINDING_CALL* eglExportDMABUFImageMESAProc)(
EGLDisplay dpy,
EGLImageKHR image,
int* fds,
EGLint* strides,
EGLint* offsets);
typedef EGLBoolean(GL_BINDING_CALL* eglExportDMABUFImageQueryMESAProc)(
EGLDisplay dpy,
EGLImageKHR image,
int* fourcc,
int* num_planes,
EGLuint64KHR* modifiers);
typedef EGLBoolean(GL_BINDING_CALL* eglGetCompositorTimingANDROIDProc)( typedef EGLBoolean(GL_BINDING_CALL* eglGetCompositorTimingANDROIDProc)(
EGLDisplay dpy, EGLDisplay dpy,
EGLSurface surface, EGLSurface surface,
...@@ -277,6 +289,7 @@ struct ExtensionsEGL { ...@@ -277,6 +289,7 @@ struct ExtensionsEGL {
bool b_EGL_KHR_stream_consumer_gltexture; bool b_EGL_KHR_stream_consumer_gltexture;
bool b_EGL_KHR_swap_buffers_with_damage; bool b_EGL_KHR_swap_buffers_with_damage;
bool b_EGL_KHR_wait_sync; bool b_EGL_KHR_wait_sync;
bool b_EGL_MESA_image_dma_buf_export;
bool b_EGL_NV_post_sub_buffer; bool b_EGL_NV_post_sub_buffer;
bool b_EGL_NV_stream_consumer_gltexture_yuv; bool b_EGL_NV_stream_consumer_gltexture_yuv;
bool b_GL_CHROMIUM_egl_android_native_fence_sync_hack; bool b_GL_CHROMIUM_egl_android_native_fence_sync_hack;
...@@ -305,6 +318,8 @@ struct ProcsEGL { ...@@ -305,6 +318,8 @@ struct ProcsEGL {
eglDestroySurfaceProc eglDestroySurfaceFn; eglDestroySurfaceProc eglDestroySurfaceFn;
eglDestroySyncKHRProc eglDestroySyncKHRFn; eglDestroySyncKHRProc eglDestroySyncKHRFn;
eglDupNativeFenceFDANDROIDProc eglDupNativeFenceFDANDROIDFn; eglDupNativeFenceFDANDROIDProc eglDupNativeFenceFDANDROIDFn;
eglExportDMABUFImageMESAProc eglExportDMABUFImageMESAFn;
eglExportDMABUFImageQueryMESAProc eglExportDMABUFImageQueryMESAFn;
eglGetCompositorTimingANDROIDProc eglGetCompositorTimingANDROIDFn; eglGetCompositorTimingANDROIDProc eglGetCompositorTimingANDROIDFn;
eglGetCompositorTimingSupportedANDROIDProc eglGetCompositorTimingSupportedANDROIDProc
eglGetCompositorTimingSupportedANDROIDFn; eglGetCompositorTimingSupportedANDROIDFn;
...@@ -428,6 +443,17 @@ class GL_EXPORT EGLApi { ...@@ -428,6 +443,17 @@ class GL_EXPORT EGLApi {
virtual EGLBoolean eglDestroySyncKHRFn(EGLDisplay dpy, EGLSyncKHR sync) = 0; virtual EGLBoolean eglDestroySyncKHRFn(EGLDisplay dpy, EGLSyncKHR sync) = 0;
virtual EGLint eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy, virtual EGLint eglDupNativeFenceFDANDROIDFn(EGLDisplay dpy,
EGLSyncKHR sync) = 0; EGLSyncKHR sync) = 0;
virtual EGLBoolean eglExportDMABUFImageMESAFn(EGLDisplay dpy,
EGLImageKHR image,
int* fds,
EGLint* strides,
EGLint* offsets) = 0;
virtual EGLBoolean eglExportDMABUFImageQueryMESAFn(
EGLDisplay dpy,
EGLImageKHR image,
int* fourcc,
int* num_planes,
EGLuint64KHR* modifiers) = 0;
virtual EGLBoolean eglGetCompositorTimingANDROIDFn( virtual EGLBoolean eglGetCompositorTimingANDROIDFn(
EGLDisplay dpy, EGLDisplay dpy,
EGLSurface surface, EGLSurface surface,
...@@ -607,6 +633,10 @@ class GL_EXPORT EGLApi { ...@@ -607,6 +633,10 @@ class GL_EXPORT EGLApi {
#define eglDestroySyncKHR ::gl::g_current_egl_context->eglDestroySyncKHRFn #define eglDestroySyncKHR ::gl::g_current_egl_context->eglDestroySyncKHRFn
#define eglDupNativeFenceFDANDROID \ #define eglDupNativeFenceFDANDROID \
::gl::g_current_egl_context->eglDupNativeFenceFDANDROIDFn ::gl::g_current_egl_context->eglDupNativeFenceFDANDROIDFn
#define eglExportDMABUFImageMESA \
::gl::g_current_egl_context->eglExportDMABUFImageMESAFn
#define eglExportDMABUFImageQueryMESA \
::gl::g_current_egl_context->eglExportDMABUFImageQueryMESAFn
#define eglGetCompositorTimingANDROID \ #define eglGetCompositorTimingANDROID \
::gl::g_current_egl_context->eglGetCompositorTimingANDROIDFn ::gl::g_current_egl_context->eglGetCompositorTimingANDROIDFn
#define eglGetCompositorTimingSupportedANDROID \ #define eglGetCompositorTimingSupportedANDROID \
......
...@@ -78,8 +78,8 @@ class GLImageAHardwareBufferTestDelegate { ...@@ -78,8 +78,8 @@ class GLImageAHardwareBufferTestDelegate {
auto image = base::MakeRefCounted<GLImageAHardwareBuffer>(size); auto image = base::MakeRefCounted<GLImageAHardwareBuffer>(size);
EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer); EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer);
bool rv = bool rv = image->Initialize(EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
image->Initialize(EGL_NATIVE_BUFFER_ANDROID, client_buffer, attribs); client_buffer, attribs);
EXPECT_TRUE(rv); EXPECT_TRUE(rv);
return image; return image;
} }
......
...@@ -24,13 +24,14 @@ GLImageEGL::~GLImageEGL() { ...@@ -24,13 +24,14 @@ GLImageEGL::~GLImageEGL() {
} }
} }
bool GLImageEGL::Initialize(EGLenum target, bool GLImageEGL::Initialize(EGLContext context,
EGLenum target,
EGLClientBuffer buffer, EGLClientBuffer buffer,
const EGLint* attrs) { const EGLint* attrs) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(EGL_NO_IMAGE_KHR, egl_image_); DCHECK_EQ(EGL_NO_IMAGE_KHR, egl_image_);
egl_image_ = eglCreateImageKHR(GLSurfaceEGL::GetHardwareDisplay(), egl_image_ = eglCreateImageKHR(GLSurfaceEGL::GetHardwareDisplay(), context,
EGL_NO_CONTEXT, target, buffer, attrs); target, buffer, attrs);
if (egl_image_ == EGL_NO_IMAGE_KHR) { if (egl_image_ == EGL_NO_IMAGE_KHR) {
LOG(ERROR) << "Error creating EGLImage: " << ui::GetLastEGLErrorString(); LOG(ERROR) << "Error creating EGLImage: " << ui::GetLastEGLErrorString();
return false; return false;
......
...@@ -18,7 +18,19 @@ class GL_EXPORT GLImageEGL : public GLImage { ...@@ -18,7 +18,19 @@ class GL_EXPORT GLImageEGL : public GLImage {
public: public:
explicit GLImageEGL(const gfx::Size& size); explicit GLImageEGL(const gfx::Size& size);
bool Initialize(EGLenum target, EGLClientBuffer buffer, const EGLint* attrs); // Same semantic as specified for eglCreateImageKHR. There two main usages:
// 1- When using the |target| EGL_GL_TEXTURE_2D_KHR it is required to pass
// a valid |context|. This allows to create an EGLImage from a GL texture.
// Then this EGLImage can be converted to an external resource to be shared
// with other client APIs.
// 2- When using the |target| EGL_NATIVE_PIXMAP_KHR or EGL_LINUX_DMA_BUF_EXT
// it is required to pass EGL_NO_CONTEXT. This allows to create an EGLImage
// from an external resource. Then this EGLImage can be converted to a GL
// texture.
bool Initialize(EGLContext context,
EGLenum target,
EGLClientBuffer buffer,
const EGLint* attrs);
// Overridden from GLImage: // Overridden from GLImage:
gfx::Size GetSize() override; gfx::Size GetSize() override;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <vector> #include <vector>
#include "ui/gfx/buffer_format_util.h" #include "ui/gfx/buffer_format_util.h"
#include "ui/gl/egl_util.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_egl.h"
#define FOURCC(a, b, c, d) \ #define FOURCC(a, b, c, d) \
...@@ -123,6 +125,34 @@ EGLint FourCC(gfx::BufferFormat format) { ...@@ -123,6 +125,34 @@ EGLint FourCC(gfx::BufferFormat format) {
return 0; return 0;
} }
gfx::BufferFormat GetBufferFormatFromFourCCFormat(int format) {
switch (format) {
case DRM_FORMAT_R8:
return gfx::BufferFormat::R_8;
case DRM_FORMAT_GR88:
return gfx::BufferFormat::RG_88;
case DRM_FORMAT_ABGR8888:
return gfx::BufferFormat::RGBA_8888;
case DRM_FORMAT_XBGR8888:
return gfx::BufferFormat::RGBX_8888;
case DRM_FORMAT_ARGB8888:
return gfx::BufferFormat::BGRA_8888;
case DRM_FORMAT_XRGB8888:
return gfx::BufferFormat::BGRX_8888;
case DRM_FORMAT_XRGB2101010:
return gfx::BufferFormat::BGRX_1010102;
case DRM_FORMAT_RGB565:
return gfx::BufferFormat::BGR_565;
case DRM_FORMAT_NV12:
return gfx::BufferFormat::YUV_420_BIPLANAR;
case DRM_FORMAT_YVU420:
return gfx::BufferFormat::YVU_420;
default:
NOTREACHED();
return gfx::BufferFormat::BGRA_8888;
}
}
} // namespace } // namespace
GLImageNativePixmap::GLImageNativePixmap(const gfx::Size& size, GLImageNativePixmap::GLImageNativePixmap(const gfx::Size& size,
...@@ -130,7 +160,9 @@ GLImageNativePixmap::GLImageNativePixmap(const gfx::Size& size, ...@@ -130,7 +160,9 @@ GLImageNativePixmap::GLImageNativePixmap(const gfx::Size& size,
: GLImageEGL(size), : GLImageEGL(size),
internalformat_(internalformat), internalformat_(internalformat),
has_image_flush_external_( has_image_flush_external_(
gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_image_flush_external")) {} gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_image_flush_external")),
has_image_dma_buf_export_(
gl::GLSurfaceEGL::HasEGLExtension("EGL_MESA_image_dma_buf_export")) {}
GLImageNativePixmap::~GLImageNativePixmap() {} GLImageNativePixmap::~GLImageNativePixmap() {}
...@@ -139,7 +171,7 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap, ...@@ -139,7 +171,7 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap,
DCHECK(!pixmap_); DCHECK(!pixmap_);
if (pixmap->GetEGLClientBuffer()) { if (pixmap->GetEGLClientBuffer()) {
EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
if (!GLImageEGL::Initialize(EGL_NATIVE_PIXMAP_KHR, if (!GLImageEGL::Initialize(EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
pixmap->GetEGLClientBuffer(), attrs)) { pixmap->GetEGLClientBuffer(), attrs)) {
return false; return false;
} }
...@@ -196,7 +228,7 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap, ...@@ -196,7 +228,7 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap,
} }
attrs.push_back(EGL_NONE); attrs.push_back(EGL_NONE);
if (!GLImageEGL::Initialize(EGL_LINUX_DMA_BUF_EXT, if (!GLImageEGL::Initialize(EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
static_cast<EGLClientBuffer>(nullptr), static_cast<EGLClientBuffer>(nullptr),
&attrs[0])) { &attrs[0])) {
return false; return false;
...@@ -207,6 +239,93 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap, ...@@ -207,6 +239,93 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap,
return true; return true;
} }
gfx::NativePixmapHandle GLImageNativePixmap::ExportHandle() {
DCHECK(!pixmap_);
DCHECK(thread_checker_.CalledOnValidThread());
// Must use GLImageEGL::Initialize.
if (egl_image_ == EGL_NO_IMAGE_KHR) {
LOG(ERROR) << "GLImageEGL is not initialized";
return gfx::NativePixmapHandle();
}
if (!has_image_dma_buf_export_) {
LOG(ERROR) << "Error no extension EGL_MESA_image_dma_buf_export";
return gfx::NativePixmapHandle();
}
int fourcc = 0;
int num_planes = 0;
EGLuint64KHR modifiers = 0;
if (!eglExportDMABUFImageQueryMESA(GLSurfaceEGL::GetHardwareDisplay(),
egl_image_, &fourcc, &num_planes,
&modifiers)) {
LOG(ERROR) << "Error querying EGLImage: " << ui::GetLastEGLErrorString();
return gfx::NativePixmapHandle();
}
if (num_planes <= 0 || num_planes > 4) {
LOG(ERROR) << "Invalid number of planes: " << num_planes;
return gfx::NativePixmapHandle();
}
gfx::BufferFormat format = GetBufferFormatFromFourCCFormat(fourcc);
if (num_planes > 0 && static_cast<size_t>(num_planes) !=
gfx::NumberOfPlanesForBufferFormat(format)) {
LOG(ERROR) << "Invalid number of planes: " << num_planes
<< " for format: " << static_cast<int>(format);
return gfx::NativePixmapHandle();
}
if (!ValidInternalFormat(internalformat_, format)) {
// A driver has returned a format different than what has been requested.
// This can happen if RGBX is implemented using RGBA. Otherwise there is
// a real mistake from the user and we have to fail.
if (internalformat_ == GL_RGB && format != gfx::BufferFormat::RGBA_8888) {
LOG(ERROR) << "Invalid internalformat: 0x" << std::hex << internalformat_
<< " for format: " << static_cast<int>(format);
return gfx::NativePixmapHandle();
}
}
std::vector<int> fds(num_planes);
std::vector<EGLint> strides(num_planes);
std::vector<EGLint> offsets(num_planes);
// It is specified for eglExportDMABUFImageMESA that the app is responsible
// for closing any fds retrieved.
if (!eglExportDMABUFImageMESA(GLSurfaceEGL::GetHardwareDisplay(), egl_image_,
&fds[0], &strides[0], &offsets[0])) {
LOG(ERROR) << "Error exporting EGLImage: " << ui::GetLastEGLErrorString();
return gfx::NativePixmapHandle();
}
gfx::NativePixmapHandle handle;
for (int i = 0; i < num_planes; ++i) {
// Sanity check. In principle all the fds are meant to be valid when
// eglExportDMABUFImageMESA succeeds.
base::ScopedFD scoped_fd(fds[i]);
if (!scoped_fd.is_valid()) {
LOG(ERROR) << "Invalid dmabuf";
return gfx::NativePixmapHandle();
}
// scoped_fd.release() transfers ownership to the caller so it will not
// call close when going out of scope. base::FileDescriptor never closes
// the fd when going out of scope. The auto_close flag is just a hint for
// the user. When true it means the user has ownership of it so he is
// responsible for closing the fd.
handle.fds.emplace_back(
base::FileDescriptor(scoped_fd.release(), true /* auto_close */));
handle.planes.emplace_back(strides[i], offsets[i], 0 /* size opaque */,
modifiers);
}
return handle;
}
unsigned GLImageNativePixmap::GetInternalFormat() { unsigned GLImageNativePixmap::GetInternalFormat() {
return internalformat_; return internalformat_;
} }
......
...@@ -20,6 +20,7 @@ class GL_EXPORT GLImageNativePixmap : public gl::GLImageEGL { ...@@ -20,6 +20,7 @@ class GL_EXPORT GLImageNativePixmap : public gl::GLImageEGL {
GLImageNativePixmap(const gfx::Size& size, unsigned internalformat); GLImageNativePixmap(const gfx::Size& size, unsigned internalformat);
bool Initialize(gfx::NativePixmap* pixmap, gfx::BufferFormat format); bool Initialize(gfx::NativePixmap* pixmap, gfx::BufferFormat format);
gfx::NativePixmapHandle ExportHandle();
// Overridden from GLImage: // Overridden from GLImage:
unsigned GetInternalFormat() override; unsigned GetInternalFormat() override;
...@@ -47,6 +48,7 @@ class GL_EXPORT GLImageNativePixmap : public gl::GLImageEGL { ...@@ -47,6 +48,7 @@ class GL_EXPORT GLImageNativePixmap : public gl::GLImageEGL {
unsigned internalformat_; unsigned internalformat_;
scoped_refptr<gfx::NativePixmap> pixmap_; scoped_refptr<gfx::NativePixmap> pixmap_;
bool has_image_flush_external_; bool has_image_flush_external_;
bool has_image_dma_buf_export_;
}; };
} // namespace gl } // namespace gl
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gl/gl_image_native_pixmap.h"
#include "base/files/file_util.h"
#include "base/files/platform_file.h"
#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/gl/test/gl_surface_test_support.h"
namespace gl {
namespace {
class GLImageNativePixmapTest : public testing::Test {
protected:
// Overridden from testing::Test:
void SetUp() override {
std::vector<GLImplementation> allowed_impls =
init::GetAllowedGLImplementations();
GLImplementation gl_impl = kGLImplementationEGLGLES2;
bool found = false;
for (auto impl : allowed_impls) {
if (impl == gl_impl) {
found = true;
break;
}
}
if (!found) {
LOG(WARNING) << "Skip test, egl is required";
return;
}
GLSurfaceTestSupport::InitializeOneOffImplementation(
gl_impl, /* fallback_to_osmesa */ false);
const std::string dmabuf_import_ext = "EGL_MESA_image_dma_buf_export";
std::string platform_extensions(DriverEGL::GetPlatformExtensions());
ExtensionSet extensions(MakeExtensionSet(platform_extensions));
if (!HasExtension(extensions, dmabuf_import_ext)) {
LOG(WARNING) << "Skip test, missing extension " << dmabuf_import_ext;
return;
}
surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
context_ =
gl::init::CreateGLContext(nullptr, surface_.get(), GLContextAttribs());
context_->MakeCurrent(surface_.get());
skip_test_ = false;
}
void TearDown() override {
if (context_)
context_->ReleaseCurrent(surface_.get());
context_ = nullptr;
surface_ = nullptr;
init::ShutdownGL(false);
}
bool skip_test_ = true;
scoped_refptr<GLSurface> surface_;
scoped_refptr<GLContext> context_;
};
void GLTexture2DToDmabuf(gfx::BufferFormat image_format,
GLint tex_internal_format,
GLenum tex_format) {
const gfx::Size image_size(64, 64);
EXPECT_NE(nullptr, GLContext::GetCurrent());
EGLContext context =
reinterpret_cast<EGLContext>(GLContext::GetCurrent()->GetHandle());
EXPECT_NE(EGL_NO_CONTEXT, context);
scoped_refptr<gl::GLImageNativePixmap> image(new gl::GLImageNativePixmap(
image_size,
gl::GLImageNativePixmap::GetInternalFormatForTesting(image_format)));
GLuint texture_id = 0;
glGenTextures(1, &texture_id);
EXPECT_NE(0u, texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, tex_internal_format, image_size.width(),
image_size.height(), 0, tex_format, GL_UNSIGNED_BYTE, nullptr);
scoped_refptr<gl::GLImageEGL> base_image = image;
EXPECT_TRUE(base_image->Initialize(
context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(texture_id), nullptr));
gfx::NativePixmapHandle native_pixmap_handle = image->ExportHandle();
size_t num_planes = gfx::NumberOfPlanesForBufferFormat(image_format);
EXPECT_EQ(num_planes, native_pixmap_handle.planes.size());
std::vector<base::ScopedFD> scoped_fds;
for (auto& fd : native_pixmap_handle.fds) {
EXPECT_TRUE(fd.auto_close);
scoped_fds.emplace_back(fd.fd);
EXPECT_TRUE(scoped_fds.back().is_valid());
}
// Close all fds.
scoped_fds.clear();
image = nullptr;
glDeleteTextures(1, &texture_id);
}
TEST_F(GLImageNativePixmapTest, GLTexture2DToDmabuf) {
if (skip_test_)
return;
// Add more cases if needed.
GLTexture2DToDmabuf(gfx::BufferFormat::RGBA_8888, GL_RGBA, GL_RGBA);
GLTexture2DToDmabuf(gfx::BufferFormat::BGRA_8888, GL_RGBA, GL_RGBA);
GLTexture2DToDmabuf(gfx::BufferFormat::RGBX_8888, GL_RGBA, GL_RGBA);
GLTexture2DToDmabuf(gfx::BufferFormat::BGRX_8888, GL_RGBA, GL_RGBA);
}
} // namespace
} // namespace gl
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment