From 290fc88e3f375a2b5054780bd797be11ad526d90 Mon Sep 17 00:00:00 2001 From: Nicolas Aguirre Date: Fri, 18 Mar 2016 08:56:59 +0100 Subject: [PATCH] evas: add eglfs evas module. EGL Fullscreen is a module intended to support many proprietary GL driver that come with custom API to create framebuffer/window. This one is starting by covering Android with libhybris/hwcomposer. Later on, it should be able to support easily the Raspberry Pi driver. At this moment this does not work properly. Activate it at your own risk ! Do not report bug if you don't know what you are doing :-) A backend for Ecore_Evas will come later on along with a patch for Ecore_FB to use libinput. Finally a few patch should hopefully enable this backend to work and compile more easily (relying on proper header detection and dlopen/dlsym for access to proprietary function). You can read more about the goal of this patch by reading our wiki at : https://phab.enlightenment.org/w/boot2efl/ Signed-off-by: Cedric Bail --- Makefile.am | 4 + configure.ac | 17 + m4/evas_check_engine.m4 | 43 + pc/.gitignore | 1 + pc/evas-eglfs.pc.in | 3 + src/Makefile_Evas.am | 42 + .../evas/engines/eglfs/Evas_Engine_Eglfs.h | 45 + src/modules/evas/engines/eglfs/evas_engine.c | 1254 +++++++++++++++++ src/modules/evas/engines/eglfs/evas_engine.h | 133 ++ src/modules/evas/engines/eglfs/evas_outbuf.c | 703 +++++++++ 10 files changed, 2245 insertions(+) create mode 100644 pc/evas-eglfs.pc.in create mode 100644 src/modules/evas/engines/eglfs/Evas_Engine_Eglfs.h create mode 100644 src/modules/evas/engines/eglfs/evas_engine.c create mode 100644 src/modules/evas/engines/eglfs/evas_engine.h create mode 100644 src/modules/evas/engines/eglfs/evas_outbuf.c diff --git a/Makefile.am b/Makefile.am index 8206b161b8..ef7c8a4292 100644 --- a/Makefile.am +++ b/Makefile.am @@ -188,6 +188,10 @@ if BUILD_ENGINE_FB pkgconfig_DATA += pc/evas-fb.pc endif +if BUILD_ENGINE_EGLFS +pkgconfig_DATA += pc/evas-eglfs.pc +endif + if BUILD_ENGINE_BUFFER pkgconfig_DATA += pc/evas-software-buffer.pc endif diff --git a/configure.ac b/configure.ac index 0768936675..da7e01283b 100644 --- a/configure.ac +++ b/configure.ac @@ -1624,6 +1624,19 @@ AC_ARG_ENABLE([fb], ], [want_fb="no"]) +# Eglfs +AC_ARG_ENABLE([eglfs], + [AS_HELP_STRING([--enable-eglfs],[enable hardware accelerated framebuffer access. @<:@default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + want_eglfs="yes" + want_fb="yes" + else + want_eglfs="no" + fi + ], + [want_eglfs="no"]) + # SDL AC_ARG_ENABLE([sdl], [AS_HELP_STRING([--enable-sdl],[enable SDL support. @<:@default=disabled@:>@])], @@ -2080,6 +2093,7 @@ EVAS_CHECK_ENGINE([wayland-egl], [${want_evas_engine_wayland_egl}], [no], [Wayla EVAS_CHECK_ENGINE([wayland-shm], [${want_wayland}], [no], [Wayland Shm]) EVAS_CHECK_ENGINE([drm], [${want_drm}], [no], [Drm]) EVAS_CHECK_ENGINE([gl-drm], [${want_gl_drm}], [no], [OpenGL Drm]) +EVAS_CHECK_ENGINE([eglfs], [${want_eglfs}], [no], [OpenGL Fb]) # Software XCB @@ -2236,6 +2250,7 @@ if test "x$have_evas_engine_gl_xlib" = "xyes" || \ test "x$have_evas_engine_gl_sdl" = "xyes" || \ test "x$have_evas_engine_gl_cocoa" = "xyes" || \ test "x$have_evas_engine_gl_drm" = "xyes" || \ + test "x$have_evas_engine_eglfs" = "xyes" || \ test "x$have_evas_engine_wayland_egl" = "xyes"; then have_evas_engine_gl_common="yes" fi @@ -2244,6 +2259,7 @@ if test "x$have_evas_engine_gl_xlib" = "xstatic" || \ test "x$have_evas_engine_gl_sdl" = "xstatic" || \ test "x$have_evas_engine_gl_cocoa" = "xstatic" || \ test "x$have_evas_engine_gl_drm" = "xstatic" || \ + test "x$have_evas_engine_eglfs" = "xstatic" || \ test "x$have_evas_engine_wayland_egl" = "xstatic"; then have_evas_engine_gl_common="yes" have_static_evas_engine_gl_common="yes" @@ -5095,6 +5111,7 @@ pc/eo-js.pc pc/efl.pc pc/efl-cxx.pc pc/evas-fb.pc +pc/evas-eglfs.pc pc/evas-opengl-x11.pc pc/evas-opengl-sdl.pc pc/evas-opengl-cocoa.pc diff --git a/m4/evas_check_engine.m4 b/m4/evas_check_engine.m4 index 42ecc892d8..e38589c48a 100644 --- a/m4/evas_check_engine.m4 +++ b/m4/evas_check_engine.m4 @@ -661,6 +661,49 @@ AS_IF([test "x${have_dep}" = "xyes"], [$4], [$5]) ]) +dnl use: EVAS_CHECK_ENGINE_DEP_EGLFS(engine, simple, want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) + +AC_DEFUN([EVAS_CHECK_ENGINE_DEP_EGLFS], +[ + +requirement="" +have_dep="no" +have_hw_dep="no" +evas_engine_[]$1[]_cflags="" +evas_engine_[]$1[]_libs="" + +if test "x${with_opengl}" = "xes" ; then + gl_library="glesv2" +else + AC_MSG_ERROR([We do not support Eglfs without OpenGL ES. Please consider OpenGL ES if you want to use it.]) +fi + +PKG_CHECK_EXISTS([egl >= 7.10 ${gl_library}], + [ + have_dep="yes" + requirement="egl >= 7.10 ${gl_library}" + ], + [have_dep="no"]) + +if test "x${have_dep}" = "xyes" ; then + if test "x$3" = "xstatic" ; then + requirements_pc_evas="${requirement} ${requirements_pc_evas}" + requirements_pc_deps_evas="${requirement} ${requirements_pc_deps_evas}" + else + PKG_CHECK_MODULES([EGLFS], [${requirement}]) + evas_engine_[]$1[]_cflags="${EGLFS_CFLAGS}" + evas_engine_[]$1[]_libs="${EGLFS_LIBS}" + evas_engine_gl_common_libs="$evas_engine_[]$1[]_libdirs -lGLESv2 -lm -lEGL" + fi +fi + +AC_SUBST([evas_engine_$1_cflags]) +AC_SUBST([evas_engine_$1_libs]) + +AS_IF([test "x${have_dep}" = "xyes"], [$4], [$5]) + +]) + dnl use: EVAS_ENGINE(name, want_engine, [DEPENDENCY-CHECK-CODE]) dnl diff --git a/pc/.gitignore b/pc/.gitignore index c3422d5f99..ee97ef9620 100644 --- a/pc/.gitignore +++ b/pc/.gitignore @@ -44,6 +44,7 @@ /ethumb_client.pc /evas-drm.pc /evas-fb.pc +/evas-eglfs.pc /evas-opengl-cocoa.pc /evas-opengl-sdl.pc /evas-opengl-x11.pc diff --git a/pc/evas-eglfs.pc.in b/pc/evas-eglfs.pc.in new file mode 100644 index 0000000000..c8dfc5c8c3 --- /dev/null +++ b/pc/evas-eglfs.pc.in @@ -0,0 +1,3 @@ +Name: evas-eglfs +Description: Evas eglfs engine +Version: @VERSION@ diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 4f5656470b..47642aa300 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -874,6 +874,9 @@ endif if BUILD_ENGINE_GL_DRM modules_evas_engines_gl_common_libevas_engine_gl_common_la_CPPFLAGS += @evas_engine_gl_drm_cflags@ endif +if BUILD_ENGINE_EGLFS +modules_evas_engines_gl_common_libevas_engine_gl_common_la_CPPFLAGS += @evas_engine_eglfs_cflags@ +endif modules_evas_engines_gl_common_libevas_engine_gl_common_la_LIBADD = @USE_EVAS_LIBS@ modules_evas_engines_gl_common_libevas_engine_gl_common_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@ modules_evas_engines_gl_common_libevas_engine_gl_common_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ @@ -1330,6 +1333,45 @@ modules_evas_engines_gl_drm_module_la_LIBTOOLFLAGS = --tag=disable-static endif endif +if BUILD_ENGINE_EGLFS +dist_installed_evasmainheaders_DATA += modules/evas/engines/eglfs/Evas_Engine_Eglfs.h +EGLFS_SOURCES = \ +modules/evas/engines/eglfs/evas_outbuf.c \ +modules/evas/engines/eglfs/evas_engine.c \ +modules/evas/engines/eglfs/evas_engine.h \ +modules/evas/engines/eglfs/Evas_Engine_Eglfs.h +if EVAS_STATIC_BUILD_EGLFS +lib_evas_libevas_la_SOURCES += $(EGLFS_SOURCES) +lib_evas_libevas_la_CPPFLAGS += @evas_engine_eglfs_cflags@ +lib_evas_libevas_la_LIBADD += @evas_engine_eglfs_libs@ +else +engineeglfspkgdir = $(libdir)/evas/modules/engines/eglfs/$(MODULE_ARCH) +engineeglfspkg_LTLIBRARIES = modules/evas/engines/eglfs/module.la + +# Workaround for broken parallel install support in automake (relink issue) +# http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 +install_engineeglfspkgLTLIBRARIES = install-engineeglfspkgLTLIBRARIES +$(install_engineeglfspkgLTLIBRARIES): install-libLTLIBRARIES + +modules_evas_engines_eglfs_module_la_SOURCES = $(EGLFS_SOURCES) +modules_evas_engines_eglfs_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-I$(top_srcdir)/src/lib/evas/include \ +-I$(top_srcdir)/src/lib/evas/cserve2 \ +-I$(top_srcdir)/src/modules/evas/engines/eglfs \ +@EVAS_CFLAGS@ \ +@ECORE_DRM_CFLAGS@ \ +@evas_engine_eglfs_cflags@ +modules_evas_engines_eglfs_module_la_LIBADD = \ +@USE_EVAS_LIBS@ \ +@USE_ECORE_DRM_LIBS@ \ +@evas_engine_eglfs_libs@ \ +@USE_EEZE_INTERNAL_LIBS@ +modules_evas_engines_eglfs_module_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@ @USE_EEZE_INTERNAL_LIBS@ @USE_ECORE_DRM_INTERNAL_LIBS@ +modules_evas_engines_eglfs_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ +modules_evas_engines_eglfs_module_la_LIBTOOLFLAGS = --tag=disable-static +endif +endif + ### Cserve2 binary if EVAS_CSERVE2 diff --git a/src/modules/evas/engines/eglfs/Evas_Engine_Eglfs.h b/src/modules/evas/engines/eglfs/Evas_Engine_Eglfs.h new file mode 100644 index 0000000000..7472c7b306 --- /dev/null +++ b/src/modules/evas/engines/eglfs/Evas_Engine_Eglfs.h @@ -0,0 +1,45 @@ +#ifndef _EVAS_ENGINE_EGLFS_H +# define _EVAS_ENGINE_EGLFS_H + +typedef enum _Evas_Engine_Info_Eglfs_Swap_Mode +{ + EVAS_ENGINE_EGLFS_SWAP_MODE_AUTO = 0, + EVAS_ENGINE_EGLFS_SWAP_MODE_FULL = 1, + EVAS_ENGINE_EGLFS_SWAP_MODE_COPY = 2, + EVAS_ENGINE_EGLFS_SWAP_MODE_DOUBLE = 3, + EVAS_ENGINE_EGLFS_SWAP_MODE_TRIPLE = 4, + EVAS_ENGINE_EGLFS_SWAP_MODE_QUADRUPLE = 5 +} Evas_Engine_Info_Eglfs_Swap_Mode; + +typedef struct _Evas_Engine_Info_Eglfs Evas_Engine_Info_Eglfs; + +struct _Evas_Engine_Info_Eglfs +{ + /* PRIVATE - don't mess with this baby or evas will poke its tongue out */ + /* at you and make nasty noises */ + Evas_Engine_Info magic; + + struct + { + unsigned int rotation, depth; + unsigned int crtc_id, conn_id, buffer_id; + unsigned int format, flags; + + Eina_Bool destination_alpha : 1; + Eina_Bool vsync : 1; + Eina_Bool indirect : 1; + unsigned char swap_mode : 4; + } info; + + struct + { + void (*pre_swap)(void *data, Evas *evas); + void (*post_swap)(void *data, Evas *evas); + void *data; + } callback; + + /* non-blocking or blocking mode */ + Evas_Engine_Render_Mode render_mode; +}; + +#endif diff --git a/src/modules/evas/engines/eglfs/evas_engine.c b/src/modules/evas/engines/eglfs/evas_engine.c new file mode 100644 index 0000000000..c408375c96 --- /dev/null +++ b/src/modules/evas/engines/eglfs/evas_engine.c @@ -0,0 +1,1254 @@ +#include "config.h" +#include "evas_engine.h" +#include + +#ifdef HAVE_DLSYM +# include /* dlopen,dlclose,etc */ +#else +# error eglfs should not get compiled if dlsym is not found on the system! +#endif + +#ifdef EVAS_CSERVE2 +# include "evas_cs2_private.h" +#endif + +#define EVAS_GL_NO_GL_H_CHECK 1 +#include "Evas_GL.h" + +#define EVAS_GL_UPDATE_TILE_SIZE 16 + +#ifndef EGL_NATIVE_PIXMAP_KHR +# define EGL_NATIVE_PIXMAP_KHR 0x30b0 +#endif + +/* external variables */ +int _evas_engine_eglfs_log_dom = -1; +int _extn_have_buffer_age = 1; + +/* local variables */ +static Eina_Bool initted = EINA_FALSE; +static int gl_wins = 0; + +/* local structures */ +typedef struct _Render_Engine Render_Engine; +struct _Render_Engine +{ + Render_Engine_GL_Generic generic; +}; + +typedef struct _Native Native; +struct _Native +{ + Evas_Native_Surface ns; + struct wl_buffer *wl_buf; + void *egl_surface; +}; + +/* local function prototype types */ +typedef void (*_eng_fn)(void); +typedef _eng_fn (*glsym_func_eng_fn)(); +typedef void (*glsym_func_void)(); +typedef void *(*glsym_func_void_ptr)(); +typedef int (*glsym_func_int)(); +typedef unsigned int (*glsym_func_uint)(); +typedef const char *(*glsym_func_const_char_ptr)(); + +/* external dynamic loaded Evas_GL function pointers */ +Evas_GL_Common_Image_Call glsym_evas_gl_common_image_ref = NULL; +Evas_GL_Common_Image_Call glsym_evas_gl_common_image_unref = NULL; +Evas_GL_Common_Image_Call glsym_evas_gl_common_image_free = NULL; +Evas_GL_Common_Image_Call glsym_evas_gl_common_image_native_disable = NULL; +Evas_GL_Common_Image_Call glsym_evas_gl_common_image_native_enable = NULL; +Evas_GL_Common_Image_New_From_Data glsym_evas_gl_common_image_new_from_data = NULL; +Evas_GL_Common_Context_Call glsym_evas_gl_common_image_all_unload = NULL; +Evas_GL_Preload glsym_evas_gl_preload_init = NULL; +Evas_GL_Preload glsym_evas_gl_preload_shutdown = NULL; +EVGL_Engine_Call glsym_evgl_engine_shutdown = NULL; +EVGL_Current_Native_Context_Get_Call glsym_evgl_current_native_context_get = NULL; +Evas_Gl_Symbols glsym_evas_gl_symbols = NULL; + +Evas_GL_Common_Context_New glsym_evas_gl_common_context_new = NULL; +Evas_GL_Common_Context_Call glsym_evas_gl_common_context_flush = NULL; +Evas_GL_Common_Context_Call glsym_evas_gl_common_context_free = NULL; +Evas_GL_Common_Context_Call glsym_evas_gl_common_context_use = NULL; +Evas_GL_Common_Context_Call glsym_evas_gl_common_context_newframe = NULL; +Evas_GL_Common_Context_Call glsym_evas_gl_common_context_done = NULL; +Evas_GL_Common_Context_Resize_Call glsym_evas_gl_common_context_resize = NULL; +Evas_GL_Common_Buffer_Dump_Call glsym_evas_gl_common_buffer_dump = NULL; +Evas_GL_Preload_Render_Call glsym_evas_gl_preload_render_lock = NULL; +Evas_GL_Preload_Render_Call glsym_evas_gl_preload_render_unlock = NULL; +Evas_GL_Preload_Render_Call glsym_evas_gl_preload_render_relax = NULL; + +glsym_func_void_ptr glsym_evas_gl_common_current_context_get = NULL; + +/* dynamic loaded local egl function pointers */ +_eng_fn (*glsym_eglGetProcAddress)(const char *a) = NULL; +void *(*glsym_eglCreateImage)(EGLDisplay a, EGLContext b, EGLenum c, EGLClientBuffer d, const int *e) = NULL; +void (*glsym_eglDestroyImage)(EGLDisplay a, void *b) = NULL; +void (*glsym_glEGLImageTargetTexture2DOES)(int a, void *b) = NULL; +unsigned int (*glsym_eglSwapBuffersWithDamage)(EGLDisplay a, void *b, const EGLint *d, EGLint c) = NULL; +unsigned int (*glsym_eglQueryWaylandBufferWL)(EGLDisplay a, struct wl_resource *b, EGLint c, EGLint *d) = NULL; + +/* local function prototypes */ +static void gl_symbols(void); +static void gl_extn_veto(Render_Engine *re); + +static void *evgl_eng_display_get(void *data); +static void *evgl_eng_evas_surface_get(void *data); +static int evgl_eng_make_current(void *data, void *surface, void *context, int flush); +static void *evgl_eng_native_window_create(void *data); +static int evgl_eng_native_window_destroy(void *data, void *native_window); +static void *evgl_eng_window_surface_create(void *data, void *native_window); +static int evgl_eng_window_surface_destroy(void *data, void *surface); +static void *evgl_eng_context_create(void *data, void *share_ctx, Evas_GL_Context_Version version); +static int evgl_eng_context_destroy(void *data, void *context); +static const char *evgl_eng_string_get(void *data); +static void *evgl_eng_proc_address_get(const char *name); +static int evgl_eng_rotation_angle_get(void *data); + +/* function tables - filled in later (func and parent func) */ +static Evas_Func func, pfunc; +static const EVGL_Interface evgl_funcs = +{ + evgl_eng_display_get, + evgl_eng_evas_surface_get, + evgl_eng_native_window_create, + evgl_eng_native_window_destroy, + evgl_eng_window_surface_create, + evgl_eng_window_surface_destroy, + evgl_eng_context_create, + evgl_eng_context_destroy, + evgl_eng_make_current, + evgl_eng_proc_address_get, + evgl_eng_string_get, + evgl_eng_rotation_angle_get, + NULL, // PBuffer + NULL, // PBuffer + NULL, // OpenGL-ES 1 + NULL, // OpenGL-ES 1 + NULL, // OpenGL-ES 1 + NULL, // native_win_surface_config_get +}; + + +/* local inline functions */ +static inline Outbuf * +eng_get_ob(Render_Engine *re) +{ + return re->generic.software.ob; +} + +/* local functions */ +static void +gl_symbols(void) +{ + static Eina_Bool done = EINA_FALSE; + + if (done) return; + +#define LINK2GENERIC(sym) \ + glsym_##sym = dlsym(RTLD_DEFAULT, #sym); + + // Get function pointer to evas_gl_common that is now provided through the link of GL_Generic. + LINK2GENERIC(evas_gl_common_image_all_unload); + LINK2GENERIC(evas_gl_common_image_ref); + LINK2GENERIC(evas_gl_common_image_unref); + LINK2GENERIC(evas_gl_common_image_new_from_data); + LINK2GENERIC(evas_gl_common_image_native_disable); + LINK2GENERIC(evas_gl_common_image_free); + LINK2GENERIC(evas_gl_common_image_native_enable); + LINK2GENERIC(evas_gl_common_context_new); + LINK2GENERIC(evas_gl_common_context_flush); + LINK2GENERIC(evas_gl_common_context_free); + LINK2GENERIC(evas_gl_common_context_use); + LINK2GENERIC(evas_gl_common_context_newframe); + LINK2GENERIC(evas_gl_common_context_done); + LINK2GENERIC(evas_gl_common_context_resize); + LINK2GENERIC(evas_gl_common_buffer_dump); + LINK2GENERIC(evas_gl_preload_render_lock); + LINK2GENERIC(evas_gl_preload_render_unlock); + LINK2GENERIC(evas_gl_preload_render_relax); + LINK2GENERIC(evas_gl_preload_init); + LINK2GENERIC(evas_gl_preload_shutdown); + LINK2GENERIC(evgl_engine_shutdown); + LINK2GENERIC(evas_gl_symbols); + +#define FINDSYM(dst, sym, typ) \ + if (glsym_eglGetProcAddress) { \ + if (!dst) dst = (typ)glsym_eglGetProcAddress(sym); \ + } else { \ + if (!dst) dst = (typ)dlsym(RTLD_DEFAULT, sym); \ + } + + FINDSYM(glsym_eglGetProcAddress, "eglGetProcAddressKHR", glsym_func_eng_fn); + FINDSYM(glsym_eglGetProcAddress, "eglGetProcAddressEXT", glsym_func_eng_fn); + FINDSYM(glsym_eglGetProcAddress, "eglGetProcAddressARB", glsym_func_eng_fn); + FINDSYM(glsym_eglGetProcAddress, "eglGetProcAddress", glsym_func_eng_fn); + + glsym_evas_gl_symbols((void*)glsym_eglGetProcAddress); + + FINDSYM(glsym_eglCreateImage, "eglCreateImageKHR", glsym_func_void_ptr); + FINDSYM(glsym_eglCreateImage, "eglCreateImageEXT", glsym_func_void_ptr); + FINDSYM(glsym_eglCreateImage, "eglCreateImageARB", glsym_func_void_ptr); + FINDSYM(glsym_eglCreateImage, "eglCreateImage", glsym_func_void_ptr); + + FINDSYM(glsym_eglDestroyImage, "eglDestroyImageKHR", glsym_func_void); + FINDSYM(glsym_eglDestroyImage, "eglDestroyImageEXT", glsym_func_void); + FINDSYM(glsym_eglDestroyImage, "eglDestroyImageARB", glsym_func_void); + FINDSYM(glsym_eglDestroyImage, "eglDestroyImage", glsym_func_void); + + FINDSYM(glsym_glEGLImageTargetTexture2DOES, + "glEGLImageTargetTexture2DOES", glsym_func_void); + + FINDSYM(glsym_eglSwapBuffersWithDamage, "eglSwapBuffersWithDamageEXT", + glsym_func_uint); + FINDSYM(glsym_eglSwapBuffersWithDamage, "eglSwapBuffersWithDamageINTEL", + glsym_func_uint); + FINDSYM(glsym_eglSwapBuffersWithDamage, "eglSwapBuffersWithDamage", + glsym_func_uint); + + FINDSYM(glsym_eglQueryWaylandBufferWL, "eglQueryWaylandBufferWL", + glsym_func_uint); + + done = EINA_TRUE; +} + +static void +gl_extn_veto(Render_Engine *re) +{ + const char *str = NULL; + + str = eglQueryString(eng_get_ob(re)->egl.disp, EGL_EXTENSIONS); + if (str) + { + const char *s = NULL; + + if (getenv("EVAS_GL_INFO")) printf("EGL EXTN:\n%s\n", str); + + // Disable Partial Rendering + s = getenv("EVAS_GL_PARTIAL_DISABLE"); + if ((s) && (atoi(s))) + { + _extn_have_buffer_age = 0; + glsym_eglSwapBuffersWithDamage = NULL; + } + if (!strstr(str, "EGL_EXT_buffer_age")) _extn_have_buffer_age = 0; + if (!strstr(str, "EGL_EXT_swap_buffers_with_damage")) + glsym_eglSwapBuffersWithDamage = NULL; + } + else + { + if (getenv("EVAS_GL_INFO")) printf("NO EGL EXTN!\n"); + _extn_have_buffer_age = 0; + } +} + +static void * +evgl_eng_display_get(void *data) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + if (eng_get_ob(re)) + return (void *)eng_get_ob(re)->egl.disp; + else + return NULL; +} + +static void * +evgl_eng_evas_surface_get(void *data) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + if (eng_get_ob(re)) + return (void *)eng_get_ob(re)->egl.surface[0]; + else + return NULL; +} + +static int +evgl_eng_make_current(void *data, void *surface, void *context, int flush) +{ + Render_Engine *re; + EGLContext ctx; + EGLSurface sfc; + EGLDisplay dpy; + int ret = 0; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return 0; + } + + dpy = eng_get_ob(re)->egl.disp; + ctx = (EGLContext)context; + sfc = (EGLSurface)surface; + + if ((!context) && (!surface)) + { + ret = eglMakeCurrent(dpy, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!ret) + { + ERR("eglMakeCurrent() failed! Error Code=%#x", eglGetError()); + return 0; + } + + return 1; + } + + if ((eglGetCurrentContext() != ctx) || + (eglGetCurrentSurface(EGL_READ) != sfc) || + (eglGetCurrentSurface(EGL_DRAW) != sfc) ) + { + if (flush) evas_outbuf_use(NULL); + + ret = eglMakeCurrent(dpy, sfc, sfc, ctx); + if (!ret) + { + ERR("eglMakeCurrent() failed! Error Code=%#x", eglGetError()); + return 0; + } + } + + return 1; +} + +static void _hwc_present_cb(void *user_data, struct ANativeWindow *window, + struct ANativeWindowBuffer *buffer) +{ + +} + +static void * +evgl_eng_native_window_create(void *data) +{ + Render_Engine *re; + Evas_Engine_Info_Eglfs *info; + struct ANativeWindow *native_window; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + info = eng_get_ob(re)->info; + if (!info) + { + ERR("Invalid Evas Engine Eglfs Info!"); + return NULL; + } + EGLNativeWindowType win; + win = create_hwcomposernativewindow(); + return (void *)win; +} + +static int +evgl_eng_native_window_destroy(void *data, void *native_window) +{ + Render_Engine *re = (Render_Engine *)data; + + if (!re) + { + ERR("Invalid Render Engine Data!"); + return 0; + } + + if (!native_window) + { + ERR("Invalid native surface."); + return 0; + } + + HWCNativeWindowDestroy(native_window); + + return 1; +} + +static void * +evgl_eng_window_surface_create(void *data, void *native_window) +{ + Render_Engine *re; + EGLSurface surface = EGL_NO_SURFACE; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + // Create resource surface for EGL + surface = eglCreateWindowSurface(eng_get_ob(re)->egl.disp, + eng_get_ob(re)->egl.config, + (EGLNativeWindowType)native_window, + NULL); + if (!surface) + { + ERR("Creating window surface failed. Error: %#x.", eglGetError()); + return NULL; + } + + return (void *)surface; +} + +static int +evgl_eng_window_surface_destroy(void *data, void *surface) +{ + Render_Engine *re; + EGLBoolean ret = EGL_FALSE; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return 0; + } + + if (!surface) + { + ERR("Invalid surface."); + return 0; + } + + ret = eglDestroySurface(eng_get_ob(re)->egl.disp, (EGLSurface)surface); + if (ret == EGL_TRUE) return 1; + + return 0; +} + +static void * +evgl_eng_context_create(void *data, void *share_ctx, Evas_GL_Context_Version version) +{ + Render_Engine *re; + EGLContext context = EGL_NO_CONTEXT; + int context_attrs[3]; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + if (version != EVAS_GL_GLES_2_X) + { + ERR("This engine only supports OpenGL-ES 2.0 contexts for now!"); + return NULL; + } + + context_attrs[0] = EGL_CONTEXT_CLIENT_VERSION; + context_attrs[1] = 2; + context_attrs[2] = EGL_NONE; + + // Share context already assumes that it's sharing with evas' context + if (share_ctx) + { + context = eglCreateContext(eng_get_ob(re)->egl.disp, + eng_get_ob(re)->egl.config, + (EGLContext)share_ctx, + context_attrs); + } + else + { + context = eglCreateContext(eng_get_ob(re)->egl.disp, + eng_get_ob(re)->egl.config, + eng_get_ob(re)->egl.context[0], // Evas' GL Context + context_attrs); + } + + if (!context) + { + ERR("eglMakeCurrent() failed! Error Code=%#x", eglGetError()); + return NULL; + } + + return (void *)context; +} + +static int +evgl_eng_context_destroy(void *data, void *context) +{ + Render_Engine *re; + EGLBoolean ret = EGL_FALSE; + + re = (Render_Engine *)data; + if ((!re) || (!context)) + { + ERR("Invalid Render Input Data. Engine: %p, Context: %p", + data, context); + return 0; + } + + ret = eglDestroyContext(eng_get_ob(re)->egl.disp, (EGLContext)context); + if (ret == EGL_TRUE) return 1; + + return 0; +} + +static const char * +evgl_eng_string_get(void *data) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + return eglQueryString(eng_get_ob(re)->egl.disp, EGL_EXTENSIONS); +} + +static void * +evgl_eng_proc_address_get(const char *name) +{ + if (glsym_eglGetProcAddress) return glsym_eglGetProcAddress(name); + return dlsym(RTLD_DEFAULT, name); +} + +static int +evgl_eng_rotation_angle_get(void *data) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (!re) + { + ERR("Invalid Render Engine Data!"); + return 0; + } + + if ((eng_get_ob(re)) && (eng_get_ob(re)->gl_context)) + return eng_get_ob(re)->gl_context->rot; + else + { + ERR("Unable to retrieve rotation angle."); + return 0; + } +} + +static Eina_Bool +eng_preload_make_current(void *data, void *doit) +{ + Outbuf *ob; + + ob = (Outbuf *)data; + if (!ob) return EINA_FALSE; + + if (doit) + { + if (!eglMakeCurrent(ob->egl.disp, ob->egl.surface[0], + ob->egl.surface[0], ob->egl.context[0])) + return EINA_FALSE; + } + else + { + if (!eglMakeCurrent(ob->egl.disp, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT)) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static void +_re_winfree(Render_Engine *re) +{ + if (!re) return; + if (!eng_get_ob(re)->surf) return; + glsym_evas_gl_preload_render_relax(eng_preload_make_current, eng_get_ob(re)); + evas_outbuf_unsurf(eng_get_ob(re)); +} + +static void +_native_cb_bind(void *data EINA_UNUSED, void *image) +{ + Evas_GL_Image *img; + Native *n; + + if (!(img = image)) return; + if (!(n = img->native.data)) return; + + if (n->ns.type == EVAS_NATIVE_SURFACE_WL) + { + if (n->egl_surface) + { + if (glsym_glEGLImageTargetTexture2DOES) + { + glsym_glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, n->egl_surface); + if (eglGetError() != EGL_SUCCESS) + ERR("glEGLImageTargetTexture2DOES() failed."); + } + else + ERR("Try glEGLImageTargetTexture2DOES on EGL with no support"); + } + } + else if (n->ns.type == EVAS_NATIVE_SURFACE_OPENGL) + glBindTexture(GL_TEXTURE_2D, n->ns.data.opengl.texture_id); + + /* TODO: NATIVE_SURFACE_TBM and NATIVE_SURFACE_EVASGL */ +} + +static void +_native_cb_unbind(void *data EINA_UNUSED, void *image) +{ + Evas_GL_Image *img; + Native *n; + + if (!(img = image)) return; + if (!(n = img->native.data)) return; + + else if (n->ns.type == EVAS_NATIVE_SURFACE_OPENGL) + glBindTexture(GL_TEXTURE_2D, 0); + + /* TODO: NATIVE_SURFACE_TBM and NATIVE_SURFACE_EVASGL */ +} + +static void +_native_cb_free(void *data, void *image) +{ + Render_Engine *re; + Outbuf *ob; + Evas_GL_Image *img; + Native *n; + uint32_t texid; + void *wlid; + + if (!(re = (Render_Engine *)data)) return; + if (!(img = image)) return; + if (!(n = img->native.data)) return; + if (!(ob = eng_get_ob(re))) return; + + if (n->ns.type == EVAS_NATIVE_SURFACE_WL) + { + wlid = (void*)n->wl_buf; + eina_hash_del(ob->gl_context->shared->native_wl_hash, &wlid, img); + if (n->egl_surface) + { + if (glsym_eglDestroyImage) + { + glsym_eglDestroyImage(ob->egl.disp, n->egl_surface); + if (eglGetError() != EGL_SUCCESS) + ERR("eglDestroyImage() failed."); + } + else + ERR("Try eglDestroyImage on EGL with no support"); + } + } + else if (n->ns.type == EVAS_NATIVE_SURFACE_OPENGL) + { + texid = n->ns.data.opengl.texture_id; + eina_hash_del(ob->gl_context->shared->native_tex_hash, &texid, img); + } + + img->native.data = NULL; + img->native.func.data = NULL; + img->native.func.bind = NULL; + img->native.func.unbind = NULL; + img->native.func.free = NULL; + + free(n); +} + +/* engine specific override functions */ +static void * +eng_info(Evas *eo_e EINA_UNUSED) +{ + Evas_Engine_Info_Eglfs *info; + + /* try to allocate space for our engine info */ + if (!(info = calloc(1, sizeof(Evas_Engine_Info_Eglfs)))) + return NULL; + + info->magic.magic = rand(); + info->render_mode = EVAS_RENDER_MODE_BLOCKING; + + return info; +} + +static void +eng_info_free(Evas *eo_e EINA_UNUSED, void *in) +{ + Evas_Engine_Info_Eglfs *info; + + if ((info = (Evas_Engine_Info_Eglfs *)in)) + free(info); +} + +static int +eng_setup(Evas *evas, void *in) +{ + Evas_Engine_Info_Eglfs *info; + Evas_Public_Data *epd; + Render_Engine *re; + Render_Engine_Swap_Mode swap_mode = MODE_FULL; + const char *s = NULL; + + /* try to cast to our engine info structure */ + if (!(info = (Evas_Engine_Info_Eglfs *)in)) return 0; + + /* try to get the evas public data */ + if (!(epd = eo_data_scope_get(evas, EVAS_CANVAS_CLASS))) return 0; + + s = getenv("EVAS_GL_SWAP_MODE"); + if (s) + { + if ((!strcasecmp(s, "full")) || (!strcasecmp(s, "f"))) + swap_mode = MODE_FULL; + else if ((!strcasecmp(s, "copy")) || (!strcasecmp(s, "c"))) + swap_mode = MODE_COPY; + else if ((!strcasecmp(s, "double")) || + (!strcasecmp(s, "d")) || (!strcasecmp(s, "2"))) + swap_mode = MODE_DOUBLE; + else if ((!strcasecmp(s, "triple")) || + (!strcasecmp(s, "t")) || (!strcasecmp(s, "3"))) + swap_mode = MODE_TRIPLE; + else if ((!strcasecmp(s, "quadruple")) || + (!strcasecmp(s, "q")) || (!strcasecmp(s, "4"))) + swap_mode = MODE_QUADRUPLE; + } + else + { +// in most gl implementations - egl and glx here that we care about the TEND +// to either swap or copy backbuffer and front buffer, but strictly that is +// not true. technically backbuffer content is totally undefined after a swap +// and thus you MUST re-render all of it, thus MODE_FULL + swap_mode = MODE_FULL; +// BUT... reality is that lmost every implementation copies or swaps so +// triple buffer mode can be used as it is a superset of double buffer and +// copy (though using those explicitly is more efficient). so let's play with +// triple buffer mdoe as a default and see. +// re->mode = MODE_TRIPLE; +// XXX: note - the above seems to break on some older intel chipsets and +// drivers. it seems we CANT depend on backbuffer staying around. bugger! + switch (info->info.swap_mode) + { + case EVAS_ENGINE_EGLFS_SWAP_MODE_FULL: + swap_mode = MODE_FULL; + break; + case EVAS_ENGINE_EGLFS_SWAP_MODE_COPY: + swap_mode = MODE_COPY; + break; + case EVAS_ENGINE_EGLFS_SWAP_MODE_DOUBLE: + swap_mode = MODE_DOUBLE; + break; + case EVAS_ENGINE_EGLFS_SWAP_MODE_TRIPLE: + swap_mode = MODE_TRIPLE; + break; + case EVAS_ENGINE_EGLFS_SWAP_MODE_QUADRUPLE: + swap_mode = MODE_QUADRUPLE; + break; + default: + swap_mode = MODE_AUTO; + break; + } + } + + if (!(re = epd->engine.data.output)) + { + Outbuf *ob; + Render_Engine_Merge_Mode merge_mode = MERGE_BOUNDING; + + if (!initted) + { + evas_common_init(); + glsym_evas_gl_preload_init(); + } + + if (!(re = calloc(1, sizeof(Render_Engine)))) return 0; + + /* try to create new outbuf */ + ob = evas_outbuf_new(info, epd->output.w, epd->output.h, swap_mode); + if (!ob) + { + free(re); + return 0; + } + + ob->evas = evas; + + if (!evas_render_engine_gl_generic_init(&re->generic, ob, + evas_outbuf_buffer_state_get, + evas_outbuf_rot_get, + evas_outbuf_reconfigure, + evas_outbuf_update_region_first_rect, + evas_outbuf_update_region_new, + evas_outbuf_update_region_push, + evas_outbuf_update_region_free, + NULL, + evas_outbuf_flush, + evas_outbuf_free, + evas_outbuf_use, + evas_outbuf_gl_context_get, + evas_outbuf_egl_display_get, + evas_outbuf_gl_context_new, + evas_outbuf_gl_context_use, + &evgl_funcs, ob->w, ob->h)) + { + /* free outbuf */ + + evas_outbuf_free(ob); + free(re); + return 0; + } + + epd->engine.data.output = re; + gl_wins++; + + s = getenv("EVAS_GL_PARTIAL_MERGE"); + if (s) + { + if ((!strcmp(s, "bounding")) || (!strcmp(s, "b"))) + merge_mode = MERGE_BOUNDING; + else if ((!strcmp(s, "full")) || (!strcmp(s, "f"))) + merge_mode = MERGE_FULL; + } + + evas_render_engine_software_generic_merge_mode_set(&re->generic.software, merge_mode); + + if (!initted) + { + gl_extn_veto(re); + initted = EINA_TRUE; + } + } + else + { + if (eng_get_ob(re) && _re_wincheck(eng_get_ob(re))) + { + if ((info->info.depth != eng_get_ob(re)->depth) || + (info->info.destination_alpha != eng_get_ob(re)->destination_alpha)) + { + Outbuf *ob, *ob_old; + + ob_old = re->generic.software.ob; + re->generic.software.ob = NULL; + gl_wins--; + + ob = evas_outbuf_new(info, epd->output.w, epd->output.h, swap_mode); + if (!ob) + { + if (ob_old) evas_outbuf_free(ob_old); + free(re); + return 0; + } + + evas_outbuf_use(ob); + if (ob_old) evas_outbuf_free(ob_old); + + ob->evas = evas; + + evas_render_engine_software_generic_update(&re->generic.software, ob, + epd->output.w, epd->output.h); + + gl_wins++; + } + else if ((eng_get_ob(re)->w != epd->output.w) || + (eng_get_ob(re)->h != epd->output.h) || + (info->info.rotation != eng_get_ob(re)->rotation)) + { + evas_outbuf_reconfigure(eng_get_ob(re), + epd->output.w, epd->output.h, + info->info.rotation, + info->info.depth); + } + } + } + + if (!eng_get_ob(re)) + { + free(re); + return 0; + } + + if (!epd->engine.data.output) + { + if (eng_get_ob(re)) + { + evas_outbuf_free(eng_get_ob(re)); + gl_wins--; + } + free(re); + return 0; + } + + if (re->generic.software.tb) + evas_common_tilebuf_free(re->generic.software.tb); + re->generic.software.tb = + evas_common_tilebuf_new(epd->output.w, epd->output.h); + if (re->generic.software.tb) + evas_common_tilebuf_set_tile_size(re->generic.software.tb, + TILESIZE, TILESIZE); + + if (re->generic.software.tb) + evas_render_engine_software_generic_tile_strict_set(&re->generic.software, EINA_TRUE); + + if (!epd->engine.data.context) + { + epd->engine.data.context = + epd->engine.func->context_new(epd->engine.data.output); + } + + evas_outbuf_use(eng_get_ob(re)); + + return 1; +} + +static void +eng_output_free(void *data) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (re) + { + glsym_evas_gl_preload_render_relax(eng_preload_make_current, eng_get_ob(re)); + + if (gl_wins == 1) glsym_evgl_engine_shutdown(re); + + /* NB: evas_render_engine_software_generic_clean() frees ob */ + evas_render_engine_software_generic_clean(&re->generic.software); + + gl_wins--; + + free(re); + } + + if ((initted == EINA_TRUE) && (gl_wins == 0)) + { + glsym_evas_gl_preload_shutdown(); + evas_common_shutdown(); + initted = EINA_FALSE; + } +} + +static Eina_Bool +eng_canvas_alpha_get(void *data, void *info EINA_UNUSED) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (!re) return EINA_FALSE; + + return eng_get_ob(re)->destination_alpha; +} + +static void +eng_output_dump(void *data) +{ + Render_Engine *re; + + re = (Render_Engine *)data; + if (!re) return; + + evas_common_image_image_all_unload(); + evas_common_font_font_all_unload(); + glsym_evas_gl_common_image_all_unload(eng_get_ob(re)->gl_context); + _re_winfree(re); +} + +static void * +eng_image_native_set(void *data, void *image, void *native) +{ + Render_Engine *re; + Outbuf *ob; + Native *n; + Evas_Native_Surface *ns; + Evas_GL_Image *img, *img2; + unsigned int tex = 0, fbo = 0; + uint32_t texid; + void *wlid, *wl_buf = NULL; + + re = (Render_Engine *)data; + if (!re) return NULL; + + ob = eng_get_ob(re); + if (!ob) return NULL; + + ns = native; + + if (!(img = image)) + { + if ((ns) && (ns->type == EVAS_NATIVE_SURFACE_OPENGL)) + { + img = + glsym_evas_gl_common_image_new_from_data(ob->gl_context, + ns->data.opengl.w, + ns->data.opengl.h, + NULL, 1, + EVAS_COLORSPACE_ARGB8888); + } + else + return NULL; + } + + if (ns) + { + if (ns->type == EVAS_NATIVE_SURFACE_WL) + { + wl_buf = ns->data.wl.legacy_buffer; + if (img->native.data) + { + Evas_Native_Surface *ens; + + ens = img->native.data; + if (ens->data.wl.legacy_buffer == wl_buf) + return img; + } + } + else if (ns->type == EVAS_NATIVE_SURFACE_OPENGL) + { + tex = ns->data.opengl.texture_id; + fbo = ns->data.opengl.framebuffer_id; + if (img->native.data) + { + Evas_Native_Surface *ens; + + ens = img->native.data; + if ((ens->data.opengl.texture_id == tex) && + (ens->data.opengl.framebuffer_id == fbo)) + return img; + } + } + } + + if ((!ns) && (!img->native.data)) return img; + + evas_outbuf_use(ob); + + if (img->native.data) + { + if (img->native.func.free) + img->native.func.free(img->native.func.data, img); + glsym_evas_gl_common_image_native_disable(img); + } + + if (!ns) return img; + + if (ns->type == EVAS_NATIVE_SURFACE_WL) + { + wlid = wl_buf; + img2 = eina_hash_find(ob->gl_context->shared->native_wl_hash, &wlid); + if (img2 == img) return img; + if (img2) + { + if((n = img2->native.data)) + { + glsym_evas_gl_common_image_ref(img2); + glsym_evas_gl_common_image_free(img); + return img2; + } + } + } + else if (ns->type == EVAS_NATIVE_SURFACE_OPENGL) + { + texid = tex; + img2 = eina_hash_find(ob->gl_context->shared->native_tex_hash, &texid); + if (img2 == img) return img; + if (img2) + { + if ((n = img2->native.data)) + { + glsym_evas_gl_common_image_ref(img2); + glsym_evas_gl_common_image_free(img); + return img2; + } + } + } + + img2 = glsym_evas_gl_common_image_new_from_data(ob->gl_context, img->w, + img->h, NULL, img->alpha, + EVAS_COLORSPACE_ARGB8888); + glsym_evas_gl_common_image_free(img); + + if (!(img = img2)) return NULL; + + if (ns->type == EVAS_NATIVE_SURFACE_WL) + { + if (native) + { + if ((n = calloc(1, sizeof(Native)))) + { + EGLint attribs[3]; + int format, yinvert = 1; + + glsym_eglQueryWaylandBufferWL(ob->egl.disp, wl_buf, + EGL_TEXTURE_FORMAT, &format); + if ((format != EGL_TEXTURE_RGB) && + (format != EGL_TEXTURE_RGBA)) + { + ERR("eglQueryWaylandBufferWL() %d format is not supported ", format); + glsym_evas_gl_common_image_free(img); + free(n); + return NULL; + } + + attribs[0] = EGL_WAYLAND_PLANE_WL; + attribs[1] = 0; //if plane is 1 then 0, if plane is 2 then 1 + attribs[2] = EGL_NONE; + + memcpy(&(n->ns), ns, sizeof(Evas_Native_Surface)); + glsym_eglQueryWaylandBufferWL(ob->egl.disp, wl_buf, + EGL_WAYLAND_Y_INVERTED_WL, + &yinvert); + eina_hash_add(ob->gl_context->shared->native_wl_hash, + &wlid, img); + + n->wl_buf = wl_buf; + if (glsym_eglCreateImage) + n->egl_surface = glsym_eglCreateImage(ob->egl.disp, + NULL, + EGL_WAYLAND_BUFFER_WL, + wl_buf, attribs); + else + { + ERR("Try eglCreateImage on EGL with no support"); + eina_hash_del(ob->gl_context->shared->native_wl_hash, + &wlid, img); + glsym_evas_gl_common_image_free(img); + free(n); + return NULL; + } + + if (!n->egl_surface) + { + ERR("eglCreatePixmapSurface() for %p failed", wl_buf); + eina_hash_del(ob->gl_context->shared->native_wl_hash, + &wlid, img); + glsym_evas_gl_common_image_free(img); + free(n); + return NULL; + } + + //XXX: workaround for mesa-10.2.8 + // mesa's eglQueryWaylandBufferWL() with EGL_WAYLAND_Y_INVERTED_WL works incorrect. + //img->native.yinvert = yinvert; + img->native.yinvert = 1; + img->native.loose = 0; + img->native.data = n; + img->native.func.data = re; + img->native.func.bind = _native_cb_bind; + img->native.func.unbind = _native_cb_unbind; + img->native.func.free = _native_cb_free; + img->native.target = GL_TEXTURE_2D; + img->native.mipmap = 0; + + glsym_evas_gl_common_image_native_enable(img); + } + } + } + else if (ns->type == EVAS_NATIVE_SURFACE_OPENGL) + { + if (native) + { + if ((n = calloc(1, sizeof(Native)))) + { + memcpy(&(n->ns), ns, sizeof(Evas_Native_Surface)); + eina_hash_add(ob->gl_context->shared->native_tex_hash, + &texid, img); + + n->egl_surface = 0; + + img->native.yinvert = 0; + img->native.loose = 0; + img->native.data = n; + img->native.func.data = re; + img->native.func.bind = _native_cb_bind; + img->native.func.unbind = _native_cb_unbind; + img->native.func.free = _native_cb_free; + img->native.target = GL_TEXTURE_2D; + img->native.mipmap = 0; + + glsym_evas_gl_common_image_native_enable(img); + } + } + } + + /* TODO: NATIVE_SURFACE_TBM and NATIVE_SURFACE_EVASGL */ + + return img; +} + +/* module api functions */ +static int +module_open(Evas_Module *em) +{ + /* check for valid evas module */ + if (!em) return 0; + + /* get whatever engine module we inherit from */ + if (!_evas_module_engine_inherit(&pfunc, "gl_generic")) return 0; + + /* try to create eina logging domain */ + if (_evas_engine_eglfs_log_dom < 0) + { + _evas_engine_eglfs_log_dom = + eina_log_domain_register("evas-eglfs", EVAS_DEFAULT_LOG_COLOR); + } + + /* if we could not create a logging domain, error out */ + if (_evas_engine_eglfs_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + + /* store it for later use */ + func = pfunc; + + /* now to override methods */ + EVAS_API_OVERRIDE(info, &func, eng_); + EVAS_API_OVERRIDE(info_free, &func, eng_); + EVAS_API_OVERRIDE(setup, &func, eng_); + EVAS_API_OVERRIDE(canvas_alpha_get, &func, eng_); + EVAS_API_OVERRIDE(output_free, &func, eng_); + EVAS_API_OVERRIDE(output_dump, &func, eng_); + EVAS_API_OVERRIDE(image_native_set, &func, eng_); + + setenv("EGL_PLATFORM", "fbdev", 1); + + gl_symbols(); + + /* now advertise out own api */ + em->functions = (void *)(&func); + + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + /* unregister the eina log domain for this engine */ + eina_log_domain_unregister(_evas_engine_eglfs_log_dom); + _evas_engine_eglfs_log_dom = -1; +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, "eglfs", "none", { module_open, module_close } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_ENGINE, engine, eglfs); + +#ifndef EVAS_STATIC_BUILD_EGLFS +EVAS_EINA_MODULE_DEFINE(engine, eglfs); +#endif diff --git a/src/modules/evas/engines/eglfs/evas_engine.h b/src/modules/evas/engines/eglfs/evas_engine.h new file mode 100644 index 0000000000..e02acb0d03 --- /dev/null +++ b/src/modules/evas/engines/eglfs/evas_engine.h @@ -0,0 +1,133 @@ +#ifndef EVAS_ENGINE_H +# define EVAS_ENGINE_H + +# include "evas_common_private.h" +# include "evas_macros.h" +# include "evas_private.h" +# include "Evas.h" +# include "Evas_Engine_Eglfs.h" + +# define EGL_EGLEXT_PROTOTYPES +# define GL_GLEXT_PROTOTYPES + +# include +# include +# include +# include +# include +# include +# include +# include +# include "../gl_generic/Evas_Engine_GL_Generic.h" + +extern int _evas_engine_eglfs_log_dom; +extern int _extn_have_buffer_age; + +# ifdef ERR +# undef ERR +# endif +# define ERR(...) EINA_LOG_DOM_ERR(_evas_engine_eglfs_log_dom, __VA_ARGS__) + +# ifdef DBG +# undef DBG +# endif +# define DBG(...) EINA_LOG_DOM_DBG(_evas_engine_eglfs_log_dom, __VA_ARGS__) + +# ifdef INF +# undef INF +# endif +# define INF(...) EINA_LOG_DOM_INFO(_evas_engine_eglfs_log_dom, __VA_ARGS__) + +# ifdef WRN +# undef WRN +# endif +# define WRN(...) EINA_LOG_DOM_WARN(_evas_engine_eglfs_log_dom, __VA_ARGS__) + +# ifdef CRI +# undef CRI +# endif +# define CRI(...) EINA_LOG_DOM_CRIT(_evas_engine_eglfs_log_dom, __VA_ARGS__) + +extern Evas_GL_Common_Context_New glsym_evas_gl_common_context_new; +extern Evas_GL_Common_Context_Call glsym_evas_gl_common_context_flush; +extern Evas_GL_Common_Context_Call glsym_evas_gl_common_context_free; +extern Evas_GL_Common_Context_Call glsym_evas_gl_common_context_use; +extern Evas_GL_Common_Context_Call glsym_evas_gl_common_context_newframe; +extern Evas_GL_Common_Context_Call glsym_evas_gl_common_context_done; +extern Evas_GL_Common_Context_Resize_Call glsym_evas_gl_common_context_resize; +extern Evas_GL_Common_Buffer_Dump_Call glsym_evas_gl_common_buffer_dump; +extern Evas_GL_Preload_Render_Call glsym_evas_gl_preload_render_lock; +extern Evas_GL_Preload_Render_Call glsym_evas_gl_preload_render_unlock; + +struct _Context_3D +{ + EGLDisplay display; + EGLContext context; + EGLSurface surface; +}; + +struct _Outbuf +{ + Evas_Engine_Info_Eglfs *info; + Evas_Engine_GL_Context *gl_context; + + Evas *evas; // used for pre_swap, post_swap + + int w, h; + unsigned int rotation, depth; + Render_Engine_Swap_Mode swap_mode; + + struct + { + EGLContext context[1]; + EGLSurface surface[1]; + EGLConfig config; + EGLDisplay disp; + } egl; + + struct + { + int prev_age, frame_cnt; + int curr, last, num; + Eina_List *pending_writes; + } priv; + + Eina_Bool destination_alpha : 1; + Eina_Bool vsync : 1; + Eina_Bool lost_back : 1; + Eina_Bool surf : 1; + Eina_Bool drew : 1; +}; + +Outbuf *evas_outbuf_new(Evas_Engine_Info_Eglfs *info, int w, int h, Render_Engine_Swap_Mode swap_mode); +void evas_outbuf_free(Outbuf *ob); +void evas_outbuf_use(Outbuf *ob); +void evas_outbuf_resurf(Outbuf *ob); +void evas_outbuf_unsurf(Outbuf *ob); +void evas_outbuf_reconfigure(Outbuf *ob, int w, int h, int rot, Outbuf_Depth depth); +Render_Engine_Swap_Mode evas_outbuf_buffer_state_get(Outbuf *ob); +int evas_outbuf_rot_get(Outbuf *ob); +Eina_Bool evas_outbuf_update_region_first_rect(Outbuf *ob); +void *evas_outbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch); +void evas_outbuf_update_region_push(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h); +void evas_outbuf_update_region_free(Outbuf *ob, RGBA_Image *update); +void evas_outbuf_flush(Outbuf *ob, Tilebuf_Rect *rects, Evas_Render_Mode render_mode); +Evas_Engine_GL_Context* evas_outbuf_gl_context_get(Outbuf *ob); +void *evas_outbuf_egl_display_get(Outbuf *ob); +Context_3D *evas_outbuf_gl_context_new(Outbuf *ob); +void evas_outbuf_gl_context_use(Context_3D *ctx); +EGLNativeWindowType create_hwcomposernativewindow(void); + +static inline Eina_Bool +_re_wincheck(Outbuf *ob) +{ + if (ob->surf) return EINA_TRUE; + evas_outbuf_resurf(ob); + ob->lost_back = 1; + if (!ob->surf) ERR("GL engine can't re-create window surface!"); + return EINA_FALSE; +} + +extern unsigned int (*glsym_eglSwapBuffersWithDamage)(EGLDisplay a, void *b, const EGLint *d, EGLint c); + +#endif diff --git a/src/modules/evas/engines/eglfs/evas_outbuf.c b/src/modules/evas/engines/eglfs/evas_outbuf.c new file mode 100644 index 0000000000..aa3cd8a1b2 --- /dev/null +++ b/src/modules/evas/engines/eglfs/evas_outbuf.c @@ -0,0 +1,703 @@ +#include "evas_engine.h" + +#include +#include +#include +#include + +static hwc_layer_1_t *fblayer; +static hwc_composer_device_1_t *hwcDevicePtr; +static hwc_display_contents_1_t **mList; + +void present(void *user_data, struct ANativeWindow *window, + struct ANativeWindowBuffer *buffer) +{ + + int oldretire = mList[0]->retireFenceFd; + mList[0]->retireFenceFd = -1; + fblayer->handle = buffer->handle; + fblayer->acquireFenceFd = HWCNativeBufferGetFence(buffer); + fblayer->releaseFenceFd = -1; + int err = hwcDevicePtr->prepare(hwcDevicePtr, HWC_NUM_DISPLAY_TYPES, mList); + //assert(err == 0); + + err = hwcDevicePtr->set(hwcDevicePtr, HWC_NUM_DISPLAY_TYPES, mList); + //assert(err == 0); + HWCNativeBufferSetFence(buffer, fblayer->releaseFenceFd); + + if (oldretire != -1) + { + sync_wait(oldretire, -1); + close(oldretire); + } +} + + +EGLNativeWindowType create_hwcomposernativewindow(void) +{ + int err; + hw_module_t *hwcModule = 0; + hwcDevicePtr = 0; + + err = hw_get_module(HWC_HARDWARE_MODULE_ID, (const hw_module_t **) &hwcModule); + //assert(err == 0); + + err = hwc_open_1(hwcModule, &hwcDevicePtr); + //assert(err == 0); + + hwcDevicePtr->blank(hwcDevicePtr, 0, 0); + + uint32_t configs[5]; + size_t numConfigs = 5; + + err = hwcDevicePtr->getDisplayConfigs(hwcDevicePtr, 0, configs, &numConfigs); + //assert (err == 0); + + int32_t attr_values[2]; + uint32_t attributes[] = { HWC_DISPLAY_WIDTH, HWC_DISPLAY_HEIGHT, HWC_DISPLAY_NO_ATTRIBUTE }; + + hwcDevicePtr->getDisplayAttributes(hwcDevicePtr, 0, + configs[0], attributes, attr_values); + + size_t size = sizeof(hwc_display_contents_1_t) + 2 * sizeof(hwc_layer_1_t); + hwc_display_contents_1_t *list = (hwc_display_contents_1_t *) malloc(size); + mList = (hwc_display_contents_1_t **) malloc(HWC_NUM_DISPLAY_TYPES * sizeof(hwc_display_contents_1_t *)); + const hwc_rect_t r = { 0, 0, attr_values[0], attr_values[1] }; + + int counter = 0; + for (; counter < HWC_NUM_DISPLAY_TYPES; counter++) + mList[counter] = NULL; + // Assign buffer only to the first item, otherwise you get tearing + // if passed the same to multiple places + mList[0] = list; + + fblayer = &list->hwLayers[0]; + memset(fblayer, 0, sizeof(hwc_layer_1_t)); + fblayer->compositionType = HWC_FRAMEBUFFER; + fblayer->hints = 0; + fblayer->flags = 0; + fblayer->handle = 0; + fblayer->transform = 0; + fblayer->blending = HWC_BLENDING_NONE; + fblayer->sourceCrop = r; + fblayer->displayFrame = r; + fblayer->visibleRegionScreen.numRects = 1; + fblayer->visibleRegionScreen.rects = &fblayer->displayFrame; + fblayer->acquireFenceFd = -1; + fblayer->releaseFenceFd = -1; + fblayer = &list->hwLayers[1]; + memset(fblayer, 0, sizeof(hwc_layer_1_t)); + fblayer->compositionType = HWC_FRAMEBUFFER_TARGET; + fblayer->hints = 0; + fblayer->flags = 0; + fblayer->handle = 0; + fblayer->transform = 0; + fblayer->blending = HWC_BLENDING_NONE; + fblayer->sourceCrop = r; + fblayer->displayFrame = r; + fblayer->visibleRegionScreen.numRects = 1; + fblayer->visibleRegionScreen.rects = &fblayer->displayFrame; + fblayer->acquireFenceFd = -1; + fblayer->releaseFenceFd = -1; + + list->retireFenceFd = -1; + list->flags = HWC_GEOMETRY_CHANGED; + list->numHwLayers = 2; + + EGLNativeWindowType win = NULL; + win = (EGLNativeWindowType)HWCNativeWindowCreate(attr_values[0], attr_values[1], HAL_PIXEL_FORMAT_RGBA_8888, present, NULL); + return win; +} + +/* local variables */ +static Outbuf *_evas_eglfs_window = NULL; +static EGLContext context = EGL_NO_CONTEXT; +static int win_count = 0; + +static Eina_Bool +_evas_outbuf_make_current(void *data, void *doit) +{ + Outbuf *ob; + + if (!(ob = data)) return EINA_FALSE; + + if (doit) + { + if (!eglMakeCurrent(ob->egl.disp, ob->egl.surface[0], + ob->egl.surface[0], ob->egl.context[0])) + return EINA_FALSE; + } + else + { + if (!eglMakeCurrent(ob->egl.disp, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT)) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +void _hwcomposer_present_cb(void *user_data, struct ANativeWindow *window, struct ANativeWindowBuffer *buffer) +{ +} + +static Eina_Bool +_evas_outbuf_egl_setup(Outbuf *ob) +{ + int ctx_attr[3]; + int cfg_attr[40]; + int maj = 0, min = 0, n = 0, i = 0; + EGLint ncfg; + EGLConfig *cfgs; + const GLubyte *vendor, *renderer, *version, *glslversion; + Eina_Bool blacklist = EINA_FALSE; + + /* setup egl surface */ + ctx_attr[0] = EGL_CONTEXT_CLIENT_VERSION; + ctx_attr[1] = 2; + ctx_attr[2] = EGL_NONE; + + cfg_attr[n++] = EGL_BUFFER_SIZE; + cfg_attr[n++] = 32; + cfg_attr[n++] = EGL_DEPTH_SIZE; + cfg_attr[n++] = EGL_DONT_CARE; + cfg_attr[n++] = EGL_STENCIL_SIZE; + cfg_attr[n++] = EGL_DONT_CARE; + cfg_attr[n++] = EGL_RENDERABLE_TYPE; + cfg_attr[n++] = EGL_OPENGL_ES2_BIT; + cfg_attr[n++] = EGL_SURFACE_TYPE; + cfg_attr[n++] = EGL_WINDOW_BIT; + + cfg_attr[n++] = EGL_ALPHA_SIZE; + if (ob->destination_alpha) cfg_attr[n++] = 1; + else cfg_attr[n++] = 0; + cfg_attr[n++] = EGL_NONE; + + int err; + hw_module_t *hwcModule = 0; + hwc_composer_device_1_t *hwcDevicePtr = 0; + + err = hw_get_module(HWC_HARDWARE_MODULE_ID, (const hw_module_t **) &hwcModule); + if (err != 0) + { + ERR("hw_get_module() fail. code=%d", err); + return EINA_FALSE; + } + err = hwc_open_1(hwcModule, &hwcDevicePtr); + if (err != 0) + { + ERR("hwc_open_1 fail. code=%d", err); + return EINA_FALSE; + } + hwcDevicePtr->blank(hwcDevicePtr, 0, 0); + + uint32_t configs[5]; + size_t numConfigs = 5; + + err = hwcDevicePtr->getDisplayConfigs(hwcDevicePtr, 0, configs, &numConfigs); + if (err != 0) + { + ERR("getDisplayConfig. code=%d", err); + return EINA_FALSE; + } + + int32_t attr_values[2]; + uint32_t attributes[] = { HWC_DISPLAY_WIDTH, HWC_DISPLAY_HEIGHT, HWC_DISPLAY_NO_ATTRIBUTE }; + + hwcDevicePtr->getDisplayAttributes(hwcDevicePtr, 0, + configs[0], attributes, attr_values); + + DBG("width: %i height: %i\n", attr_values[0], attr_values[1]); + + size_t size = sizeof(hwc_display_contents_1_t) + 2 * sizeof(hwc_layer_1_t); + hwc_display_contents_1_t *list = (hwc_display_contents_1_t *) malloc(size); + hwc_display_contents_1_t **mList = (hwc_display_contents_1_t **) malloc(HWC_NUM_DISPLAY_TYPES * sizeof(hwc_display_contents_1_t *)); + const hwc_rect_t r = { 0, 0, attr_values[0], attr_values[1] }; + + int counter = 0; + for (; counter < HWC_NUM_DISPLAY_TYPES; counter++) + mList[counter] = NULL; + mList[0] = list; + + hwc_layer_1_t *layer = &list->hwLayers[0]; + memset(layer, 0, sizeof(hwc_layer_1_t)); + layer->compositionType = HWC_FRAMEBUFFER; + layer->hints = 0; + layer->flags = 0; + layer->handle = 0; + layer->transform = 0; + layer->blending = HWC_BLENDING_NONE; + layer->sourceCrop = r; + layer->displayFrame = r; + layer->visibleRegionScreen.numRects = 1; + layer->visibleRegionScreen.rects = &layer->displayFrame; + layer->acquireFenceFd = -1; + layer->releaseFenceFd = -1; + layer = &list->hwLayers[1]; + memset(layer, 0, sizeof(hwc_layer_1_t)); + layer->compositionType = HWC_FRAMEBUFFER_TARGET; + layer->hints = 0; + layer->flags = 0; + layer->handle = 0; + layer->transform = 0; + layer->blending = HWC_BLENDING_NONE; + layer->sourceCrop = r; + layer->displayFrame = r; + layer->visibleRegionScreen.numRects = 1; + layer->visibleRegionScreen.rects = &layer->displayFrame; + layer->acquireFenceFd = -1; + layer->releaseFenceFd = -1; + + list->retireFenceFd = -1; + list->flags = HWC_GEOMETRY_CHANGED; + list->numHwLayers = 2; + + ob->egl.disp = eglGetDisplay(NULL); + if (ob->egl.disp == EGL_NO_DISPLAY) + { + ERR("eglGetDisplay() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + if (!eglInitialize(ob->egl.disp, &maj, &min)) + { + ERR("eglInitialize() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + eglBindAPI(EGL_OPENGL_ES_API); + if (eglGetError() != EGL_SUCCESS) + { + ERR("eglBindAPI() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + if (!eglGetConfigs(ob->egl.disp, NULL, 0, &ncfg) || (ncfg == 0)) + { + ERR("eglGetConfigs() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + cfgs = malloc(ncfg * sizeof(EGLConfig)); + if (!cfgs) + { + ERR("Failed to malloc space for egl configs"); + return EINA_FALSE; + } + + if (!eglChooseConfig(ob->egl.disp, cfg_attr, cfgs, + ncfg, &ncfg) || (ncfg == 0)) + { + ERR("eglChooseConfig() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + // First is always best... + ob->egl.config = cfgs[0]; + + EGLNativeWindowType win = create_hwcomposernativewindow(); + ob->egl.surface[0] = + eglCreateWindowSurface(ob->egl.disp, ob->egl.config, + (EGLNativeWindowType)win, NULL); + + if (ob->egl.surface[0] == EGL_NO_SURFACE) + { + ERR("eglCreateWindowSurface() fail for %p. code=%#x", + NULL, eglGetError()); + return EINA_FALSE; + } + + ob->egl.context[0] = + eglCreateContext(ob->egl.disp, ob->egl.config, EGL_NO_CONTEXT, ctx_attr); + if (ob->egl.context[0] == EGL_NO_CONTEXT) + { + ERR("eglCreateContext() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + if (context == EGL_NO_CONTEXT) context = ob->egl.context[0]; + + if (eglMakeCurrent(ob->egl.disp, ob->egl.surface[0], + ob->egl.surface[0], ob->egl.context[0]) == EGL_FALSE) + { + ERR("eglMakeCurrent() fail. code=%#x", eglGetError()); + return EINA_FALSE; + } + + vendor = glGetString(GL_VENDOR); + renderer = glGetString(GL_RENDERER); + version = glGetString(GL_VERSION); + glslversion = glGetString(GL_SHADING_LANGUAGE_VERSION); + if (!vendor) vendor = (unsigned char *)"-UNKNOWN-"; + if (!renderer) renderer = (unsigned char *)"-UNKNOWN-"; + if (!version) version = (unsigned char *)"-UNKNOWN-"; + if (!glslversion) glslversion = (unsigned char *)"-UNKNOWN-"; + if (getenv("EVAS_GL_INFO")) + { + fprintf(stderr, "vendor : %s\n", vendor); + fprintf(stderr, "renderer: %s\n", renderer); + fprintf(stderr, "version : %s\n", version); + fprintf(stderr, "glsl ver: %s\n", glslversion); + } + + if (strstr((const char *)vendor, "Mesa Project")) + { + if (strstr((const char *)renderer, "Software Rasterizer")) + blacklist = EINA_TRUE; + } + if (strstr((const char *)renderer, "softpipe")) + blacklist = EINA_TRUE; + if (strstr((const char *)renderer, "llvmpipe")) + blacklist = EINA_TRUE; + + if ((blacklist) && (!getenv("EVAS_GL_NO_BLACKLIST"))) + { + ERR("OpenGL Driver blacklisted:"); + ERR("Vendor: %s", (const char *)vendor); + ERR("Renderer: %s", (const char *)renderer); + ERR("Version: %s", (const char *)version); + return EINA_FALSE; + } + + ob->gl_context = glsym_evas_gl_common_context_new(); + if (!ob->gl_context) return EINA_FALSE; + +#ifdef GL_GLES + ob->gl_context->egldisp = ob->egl.disp; + ob->gl_context->eglctxt = ob->egl.context[0]; +#endif + + evas_outbuf_use(ob); + glsym_evas_gl_common_context_resize(ob->gl_context, + ob->w, ob->h, ob->rotation); + + ob->surf = EINA_TRUE; + + return EINA_TRUE; +} + +Outbuf * +evas_outbuf_new(Evas_Engine_Info_Eglfs *info, int w, int h, Render_Engine_Swap_Mode swap_mode) +{ + Outbuf *ob; + char *num; + + if (!info) return NULL; + + /* try to allocate space for outbuf */ + if (!(ob = calloc(1, sizeof(Outbuf)))) return NULL; + + win_count++; + + ob->w = w; + ob->h = h; + ob->info = info; + ob->depth = info->info.depth; + ob->rotation = info->info.rotation; + ob->destination_alpha = info->info.destination_alpha; + ob->swap_mode = swap_mode; + ob->priv.num = 2; + + if ((num = getenv("EVAS_EGLFS_BUFFERS"))) + { + ob->priv.num = atoi(num); + if (ob->priv.num <= 0) ob->priv.num = 1; + else if (ob->priv.num > 4) ob->priv.num = 4; + } + + if ((num = getenv("EVAS_EGLFS_VSYNC"))) + ob->vsync = atoi(num); + + if (!_evas_outbuf_egl_setup(ob)) + { + evas_outbuf_free(ob); + return NULL; + } + + return ob; +} + +void +evas_outbuf_free(Outbuf *ob) +{ + int ref = 0; + + win_count--; + evas_outbuf_use(ob); + + if (ob == _evas_eglfs_window) _evas_eglfs_window = NULL; + + if (ob->gl_context) + { + ref = ob->gl_context->references - 1; + glsym_evas_gl_common_context_free(ob->gl_context); + } + + eglMakeCurrent(ob->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (ob->egl.context[0] != context) + eglDestroyContext(ob->egl.disp, ob->egl.context[0]); + + if (ob->egl.surface[0] != EGL_NO_SURFACE) + eglDestroySurface(ob->egl.disp, ob->egl.surface[0]); + + if (ref == 0) + { + if (context) eglDestroyContext(ob->egl.disp, context); + eglTerminate(ob->egl.disp); + eglReleaseThread(); + context = EGL_NO_CONTEXT; + } + + free(ob); +} + +void +evas_outbuf_use(Outbuf *ob) +{ + Eina_Bool force = EINA_FALSE; + + glsym_evas_gl_preload_render_lock(_evas_outbuf_make_current, ob); + + if (_evas_eglfs_window) + { + if (eglGetCurrentContext() != _evas_eglfs_window->egl.context[0]) + force = EINA_TRUE; + } + + if ((_evas_eglfs_window != ob) || (force)) + { + if (_evas_eglfs_window) + { + glsym_evas_gl_common_context_use(_evas_eglfs_window->gl_context); + glsym_evas_gl_common_context_flush(_evas_eglfs_window->gl_context); + } + + _evas_eglfs_window = ob; + + if (ob) + { + if (ob->egl.surface[0] != EGL_NO_SURFACE) + { + if (eglMakeCurrent(ob->egl.disp, ob->egl.surface[0], + ob->egl.surface[0], + ob->egl.context[0]) == EGL_FALSE) + ERR("eglMakeCurrent() failed!"); + } + } + } + + if (ob) glsym_evas_gl_common_context_use(ob->gl_context); +} + +void +evas_outbuf_resurf(Outbuf *ob) +{ + if (ob->surf) return; + if (getenv("EVAS_GL_INFO")) printf("resurf %p\n", ob); + + ob->egl.surface[0] = + eglCreateWindowSurface(ob->egl.disp, ob->egl.config, + NULL, NULL); + + if (ob->egl.surface[0] == EGL_NO_SURFACE) + { + ERR("eglCreateWindowSurface() fail for %p. code=%#x", + NULL, eglGetError()); + return; + } + + if (eglMakeCurrent(ob->egl.disp, ob->egl.surface[0], ob->egl.surface[0], + ob->egl.context[0]) == EGL_FALSE) + ERR("eglMakeCurrent() failed!"); + + ob->surf = EINA_TRUE; +} + +void +evas_outbuf_unsurf(Outbuf *ob) +{ + if (!ob->surf) return; + if (!getenv("EVAS_GL_WIN_RESURF")) return; + if (getenv("EVAS_GL_INFO")) printf("unsurf %p\n", ob); + + if (_evas_eglfs_window) + glsym_evas_gl_common_context_flush(_evas_eglfs_window->gl_context); + if (_evas_eglfs_window == ob) + { + eglMakeCurrent(ob->egl.disp, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (ob->egl.surface[0] != EGL_NO_SURFACE) + eglDestroySurface(ob->egl.disp, ob->egl.surface[0]); + ob->egl.surface[0] = EGL_NO_SURFACE; + + _evas_eglfs_window = NULL; + } + + ob->surf = EINA_FALSE; +} + +void +evas_outbuf_reconfigure(Outbuf *ob, int w, int h, int rot, Outbuf_Depth depth) +{ + if (depth == OUTBUF_DEPTH_INHERIT) depth = ob->depth; + + ob->w = w; + ob->h = h; + ob->depth = depth; + ob->rotation = rot; + + evas_outbuf_use(ob); + glsym_evas_gl_common_context_resize(ob->gl_context, w, h, rot); +} + +Render_Engine_Swap_Mode +evas_outbuf_buffer_state_get(Outbuf *ob) +{ + return MODE_FULL; + // Forces re-rendering all the screen, that is bad for performance. However + // partial rendering makes black area. We should try to find a better solution. +} + +int +evas_outbuf_rot_get(Outbuf *ob) +{ + return ob->rotation; +} + +Eina_Bool +evas_outbuf_update_region_first_rect(Outbuf *ob) +{ + glsym_evas_gl_preload_render_lock(_evas_outbuf_make_current, ob); + evas_outbuf_use(ob); + + if (!_re_wincheck(ob)) return EINA_TRUE; + + glsym_evas_gl_common_context_flush(ob->gl_context); + glsym_evas_gl_common_context_newframe(ob->gl_context); + + return EINA_FALSE; +} + +void * +evas_outbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx EINA_UNUSED, int *cy EINA_UNUSED, int *cw EINA_UNUSED, int *ch EINA_UNUSED) +{ + if ((w == ob->w) && (h == ob->h)) + ob->gl_context->master_clip.enabled = EINA_FALSE; + else + { + ob->gl_context->master_clip.enabled = EINA_TRUE; + ob->gl_context->master_clip.x = x; + ob->gl_context->master_clip.y = y; + ob->gl_context->master_clip.w = w; + ob->gl_context->master_clip.h = h; + } + + return ob->gl_context->def_surface; +} + +void +evas_outbuf_update_region_push(Outbuf *ob, RGBA_Image *update EINA_UNUSED, int x EINA_UNUSED, int y EINA_UNUSED, int w EINA_UNUSED, int h EINA_UNUSED) +{ + /* Is it really necessary to flush per region ? Shouldn't we be able to + still do that for the full canvas when doing partial update */ + if (!_re_wincheck(ob)) return; + ob->drew = EINA_TRUE; + glsym_evas_gl_common_context_flush(ob->gl_context); +} + +void +evas_outbuf_update_region_free(Outbuf *ob EINA_UNUSED, RGBA_Image *update EINA_UNUSED) +{ + /* Nothing to do here as we don't really create an image per area */ +} + +void +evas_outbuf_flush(Outbuf *ob, Tilebuf_Rect *rects EINA_UNUSED, Evas_Render_Mode render_mode) +{ + if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) goto end; + + if (!_re_wincheck(ob)) goto end; + if (!ob->drew) goto end; + + ob->drew = EINA_FALSE; + evas_outbuf_use(ob); + glsym_evas_gl_common_context_done(ob->gl_context); + + if (!ob->vsync) + { + if (ob->info->info.vsync) eglSwapInterval(ob->egl.disp, 1); + else eglSwapInterval(ob->egl.disp, 0); + ob->vsync = 1; + } + + if (ob->info->callback.pre_swap) + ob->info->callback.pre_swap(ob->info->callback.data, ob->evas); + + eglSwapBuffers(ob->egl.disp, ob->egl.surface[0]); + + if (ob->info->callback.post_swap) + ob->info->callback.post_swap(ob->info->callback.data, ob->evas); + + ob->priv.frame_cnt++; + +end: + glsym_evas_gl_preload_render_unlock(_evas_outbuf_make_current, ob); +} + +Evas_Engine_GL_Context * +evas_outbuf_gl_context_get(Outbuf *ob) +{ + return ob->gl_context; +} + +void * +evas_outbuf_egl_display_get(Outbuf *ob) +{ + return ob->egl.disp; +} + +Context_3D * +evas_outbuf_gl_context_new(Outbuf *ob) +{ + Context_3D *ctx; + int context_attrs[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + if (!ob) return NULL; + + ctx = calloc(1, sizeof(Context_3D)); + if (!ctx) return NULL; + + ctx->context = eglCreateContext(ob->egl.disp, ob->egl.config, + ob->egl.context[0], context_attrs); + + if (!ctx->context) + { + ERR("EGL context creation failed."); + goto error; + } + + ctx->display = ob->egl.disp; + ctx->surface = ob->egl.surface[0]; + + return ctx; + +error: + free(ctx); + return NULL; +} + +void +evas_outbuf_gl_context_free(Context_3D *ctx) +{ + eglDestroyContext(ctx->display, ctx->context); + free(ctx); +} + +void +evas_outbuf_gl_context_use(Context_3D *ctx) +{ + if (eglMakeCurrent(ctx->display, ctx->surface, + ctx->surface, ctx->context) == EGL_FALSE) + ERR("eglMakeCurrent() failed."); +}