diff --git a/configure.ac b/configure.ac index f85074daff..dbd24ccbc2 100644 --- a/configure.ac +++ b/configure.ac @@ -1335,6 +1335,17 @@ AC_ARG_ENABLE([drm-hw-accel], [want_drm_hw_accel="no"]) +AC_ARG_ENABLE([gl-drm], + [AC_HELP_STRING([--enable-gl-drm], + [enable gl drm engine. @<:@default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + want_gl_drm="yes" + else + want_gl_drm="no" + fi + ], + [want_gl_drm="no"]) # Fontconfig AC_ARG_ENABLE([fontconfig], [AS_HELP_STRING([--disable-fontconfig],[disable fontconfig for finding fonts. @<:@default=enabled@:>@])], @@ -1622,6 +1633,7 @@ EVAS_CHECK_ENGINE([software-ddraw], [${want_evas_engine_software_ddraw}], [no], EVAS_CHECK_ENGINE([wayland-egl], [${want_evas_engine_wayland_egl}], [no], [Wayland Egl]) 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]) # Software XCB @@ -1755,6 +1767,7 @@ if test "x$have_evas_engine_gl_xlib" = "xyes" || \ test "x$have_evas_engine_gl_xcb" = "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_wayland_egl" = "xyes"; then have_evas_engine_gl_common="yes" fi @@ -1762,6 +1775,7 @@ if test "x$have_evas_engine_gl_xlib" = "xstatic" || \ test "x$have_evas_engine_gl_xcb" = "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_wayland_egl" = "xstatic"; then have_evas_engine_gl_common="yes" have_static_evas_engine_gl_common="yes" @@ -2813,7 +2827,7 @@ EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [ecore-input]) EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [eo]) EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [eina]) -EFL_DEPEND_PKG([ECORE_DRM], [DRM], [libudev >= 148 libdrm >= 2.4 xkbcommon >= 0.3.0 libsystemd-login >= 192 dbus-1]) +EFL_DEPEND_PKG([ECORE_DRM], [DRM], [libudev >= 148 libdrm >= 2.4 xkbcommon >= 0.3.0 libsystemd-login >= 192 dbus-1 gbm]) EFL_EVAL_PKGS([ECORE_DRM]) @@ -3623,8 +3637,9 @@ ECORE_EVAS_MODULE([extn], [${want_ecore_evas_extn}]) ECORE_EVAS_MODULE([ews], [yes]) ECORE_EVAS_MODULE([fb], [${want_fb}]) ECORE_EVAS_MODULE([drm], [${want_drm}], - [EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_drm}], [ecore-drm])] -) + [EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_drm}], [ecore-drm],[gbm])]) +ECORE_EVAS_MODULE([gl-drm], [${want_gl_drm}], + [EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_drm}], [ecore-drm],[gbm])]) ECORE_EVAS_MODULE([psl1ght], [${have_ps3}]) ECORE_EVAS_MODULE([opengl-cocoa], [${want_ecore_evas_gl_cocoa}]) @@ -3731,6 +3746,8 @@ if test "x$have_ecore_evas_software_x11" = "xyes" || \ fi AM_CONDITIONAL([BUILD_ECORE_EVAS_X11], [test "${build_ecore_evas_x11}" = "yes"]) +AC_DEFINE([BUILD_ECORE_EVAS_OPENGL_DRM], [1], [Build support for DRM based OpenGL]) + EFL_EVAL_PKGS([ECORE_EVAS]) ### Checks for header files diff --git a/m4/evas_check_engine.m4 b/m4/evas_check_engine.m4 index fd29393096..6b2ca60335 100644 --- a/m4/evas_check_engine.m4 +++ b/m4/evas_check_engine.m4 @@ -634,6 +634,45 @@ if test "x${have_hw_dep}" = "xyes" ; then 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_CHECK_ENGINE_DEP_GL_DRM(engine, simple, want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) + +AC_DEFUN([EVAS_CHECK_ENGINE_DEP_GL_DRM], +[ + +requirement="" +have_dep="no" +have_hw_dep="no" +evas_engine_[]$1[]_cflags="" +evas_engine_[]$1[]_libs="" + +gl_library="glesv2" + +PKG_CHECK_EXISTS([egl >= 7.10 ${gl_library} gbm], + [ + have_dep="yes" + requirement="egl >= 7.10 ${gl_library} gbm" + ], + [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([GL_DRM], [${requirement}]) + evas_engine_[]$1[]_cflags="${GL_DRM_CFLAGS}" + evas_engine_[]$1[]_libs="${GL_DRM_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]) diff --git a/src/Makefile_Ecore_Evas.am b/src/Makefile_Ecore_Evas.am index 236cae1666..b1c8cd0161 100644 --- a/src/Makefile_Ecore_Evas.am +++ b/src/Makefile_Ecore_Evas.am @@ -213,7 +213,9 @@ ecoreevasenginedrmpkg_LTLIBRARIES = modules/ecore_evas/engines/drm/module.la modules_ecore_evas_engines_drm_module_la_SOURCES = $(DRMSOURCES) modules_ecore_evas_engines_drm_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ @ECORE_EVAS_CFLAGS@ \ --I$(top_srcdir)/src/modules/evas/engines/drm +@ECORE_DRM_CFLAGS@ \ +-I$(top_srcdir)/src/modules/evas/engines/drm \ +-I$(top_srcdir)/src/modules/evas/engines/gl_drm modules_ecore_evas_engines_drm_module_la_LIBADD = @USE_ECORE_EVAS_LIBS@ modules_ecore_evas_engines_drm_module_la_DEPENDENCIES = @USE_ECORE_EVAS_INTERNAL_LIBS@ modules_ecore_evas_engines_drm_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index ceb5f6c9d2..f370b74e64 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -886,6 +886,9 @@ endif if BUILD_ENGINE_WAYLAND_EGL modules_evas_engines_gl_common_libevas_engine_gl_common_la_CPPFLAGS += @evas_engine_wayland_egl_cflags@ endif +if BUILD_ENGINE_GL_DRM +modules_evas_engines_gl_common_libevas_engine_gl_common_la_CPPFLAGS += @evas_engine_gl_drm_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@ @@ -1239,6 +1242,38 @@ modules_evas_engines_drm_module_la_LIBTOOLFLAGS = --tag=disable-static endif endif +if BUILD_ENGINE_GL_DRM +dist_installed_evasmainheaders_DATA += modules/evas/engines/gl_drm/Evas_Engine_GL_Drm.h +GL_DRM_SOURCES = \ +modules/evas/engines/gl_drm/evas_drm.c \ +modules/evas/engines/gl_drm/evas_drm_main.c \ +modules/evas/engines/gl_drm/evas_engine.c \ +modules/evas/engines/gl_drm/evas_engine.h \ +modules/evas/engines/gl_drm/Evas_Engine_GL_Drm.h +if EVAS_STATIC_BUILD_GL_DRM +lib_evas_libevas_la_SOURCES += $(GL_DRM_SOURCES) +lib_evas_libevas_la_CPPFLAGS += @evas_engine_gl_drm_cflags@ +lib_evas_libevas_la_LIBADD += @evas_engine_gl_drm_libs@ +else +enginegl_drmpkgdir = $(libdir)/evas/modules/engines/gl_drm/$(MODULE_ARCH) +enginegl_drmpkg_LTLIBRARIES = modules/evas/engines/gl_drm/module.la +modules_evas_engines_gl_drm_module_la_SOURCES = $(GL_DRM_SOURCES) +modules_evas_engines_gl_drm_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/gl_drm \ +@EVAS_CFLAGS@ \ +@ECORE_DRM_CFLAGS@ \ +@evas_engine_gl_drm_cflags@ +modules_evas_engines_gl_drm_module_la_LIBADD = \ +@USE_EVAS_LIBS@ \ +@USE_ECORE_DRM_LIBS@ \ +@evas_engine_gl_drm_libs@ +modules_evas_engines_gl_drm_module_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@ +modules_evas_engines_gl_drm_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ +modules_evas_engines_gl_drm_module_la_LIBTOOLFLAGS = --tag=disable-static +endif +endif ### Cserve2 binary diff --git a/src/lib/ecore_evas/Ecore_Evas.h b/src/lib/ecore_evas/Ecore_Evas.h index c70dff3d59..4c97211c56 100644 --- a/src/lib/ecore_evas/Ecore_Evas.h +++ b/src/lib/ecore_evas/Ecore_Evas.h @@ -103,6 +103,7 @@ extern "C" { #define HAVE_ECORE_EVAS_WAYLAND_SHM 1 #define HAVE_ECORE_EVAS_WAYLAND_EGL 1 #define HAVE_ECORE_EVAS_DRM 1 +#define HAVE_ECORE_EVAS_DRM_GL 1 typedef enum _Ecore_Evas_Engine_Type { @@ -129,7 +130,8 @@ typedef enum _Ecore_Evas_Engine_Type ECORE_EVAS_ENGINE_PSL1GHT, ECORE_EVAS_ENGINE_WAYLAND_SHM, ECORE_EVAS_ENGINE_WAYLAND_EGL, - ECORE_EVAS_ENGINE_DRM + ECORE_EVAS_ENGINE_DRM, + ECORE_EVAS_ENGINE_OPENGL_DRM } Ecore_Evas_Engine_Type; typedef enum _Ecore_Evas_Avoid_Damage_Type @@ -1190,6 +1192,7 @@ EAPI void ecore_evas_wayland_type_set(Ecore_Evas *ee, int type); EAPI Ecore_Wl_Window *ecore_evas_wayland_window_get(const Ecore_Evas *ee); EAPI Ecore_Evas *ecore_evas_drm_new(const char *device, unsigned int parent, int x, int y, int w, int h); +EAPI Ecore_Evas *ecore_evas_gl_drm_new(const char *device, unsigned int parent, int x, int y, int w, int h); /** * @brief Create a new @c Ecore_Evas canvas bound to the Evas diff --git a/src/lib/ecore_evas/ecore_evas.c b/src/lib/ecore_evas/ecore_evas.c index c5be4aa9fa..16966f8d57 100644 --- a/src/lib/ecore_evas/ecore_evas.c +++ b/src/lib/ecore_evas/ecore_evas.c @@ -327,6 +327,12 @@ ecore_evas_engine_type_supported_get(Ecore_Evas_Engine_Type engine) #else return EINA_FALSE; #endif + case ECORE_EVAS_ENGINE_OPENGL_DRM: +#ifdef BUILD_ECORE_EVAS_OPENGL_DRM + return EINA_TRUE; +#else + return EINA_FALSE; +#endif default: return EINA_FALSE; @@ -695,6 +701,21 @@ _ecore_evas_constructor_drm(int x, int y, int w, int h, const char *extra_option return ee; } +static Ecore_Evas * +_ecore_evas_constructor_opengl_drm(int x, int y, int w, int h, const char *extra_options) +{ + char *device = NULL; + unsigned int parent = 0; + Ecore_Evas *ee; + + _ecore_evas_parse_extra_options_str(extra_options, "device=", &device); + _ecore_evas_parse_extra_options_uint(extra_options, "parent=", &parent); + ee = ecore_evas_gl_drm_new(device, parent, x, y, w, h); + free(device); + + return ee; +} + static Ecore_Evas * _ecore_evas_constructor_software_gdi(int x, int y, int w, int h, const char *extra_options EINA_UNUSED) @@ -752,6 +773,7 @@ static const struct ecore_evas_engine _engines[] = { {"wayland_shm", _ecore_evas_constructor_wayland_shm}, {"wayland_egl", _ecore_evas_constructor_wayland_egl}, {"drm", _ecore_evas_constructor_drm}, + {"opengl_drm", _ecore_evas_constructor_opengl_drm}, {"opengl_sdl", _ecore_evas_constructor_opengl_sdl}, {"sdl", _ecore_evas_constructor_sdl}, {"buffer", _ecore_evas_constructor_buffer}, @@ -4027,6 +4049,20 @@ ecore_evas_drm_new(const char *disp_name, unsigned int parent, return new(disp_name, parent, x, y, w, h); } +EAPI Ecore_Evas * +ecore_evas_gl_drm_new(const char *disp_name, unsigned int parent, + int x, int y, int w, int h) +{ + Ecore_Evas *(*new)(const char *, unsigned int, int, int, int, int); + Eina_Module *m = _ecore_evas_engine_load("drm"); + EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL); + + new = eina_module_symbol_get(m, "ecore_evas_gl_drm_new_internal"); + EINA_SAFETY_ON_NULL_RETURN_VAL(new, NULL); + + return new(disp_name, parent, x, y, w, h); +} + EAPI Ecore_Evas * ecore_evas_software_gdi_new(Ecore_Win32_Window *parent, int x, diff --git a/src/modules/ecore_evas/engines/drm/ecore_evas_drm.c b/src/modules/ecore_evas/engines/drm/ecore_evas_drm.c index 2b601ae27a..cb7fb615a9 100644 --- a/src/modules/ecore_evas/engines/drm/ecore_evas_drm.c +++ b/src/modules/ecore_evas/engines/drm/ecore_evas_drm.c @@ -4,7 +4,6 @@ #include #include - #include #include #include "ecore_private.h" @@ -13,11 +12,14 @@ #include #include "ecore_evas_private.h" #include "ecore_evas_drm.h" +#include +#include -//#ifdef BUILD_ECORE_EVAS_DRM -# include -# include -//#endif +#ifdef BUILD_ECORE_EVAS_OPENGL_DRM +# include +# include +# include +#endif typedef struct _Ecore_Evas_Engine_Drm_Data Ecore_Evas_Engine_Drm_Data; @@ -30,7 +32,6 @@ struct _Ecore_Evas_Engine_Drm_Data static int _ecore_evas_drm_init(const char *device); static int _ecore_evas_drm_shutdown(void); static Ecore_Evas_Interface_Drm *_ecore_evas_drm_interface_new(void); - static void _ecore_evas_drm_free(Ecore_Evas *ee); static void _ecore_evas_drm_callback_resize_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func); static void _ecore_evas_drm_callback_move_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func); @@ -62,6 +63,7 @@ static void _ecore_evas_drm_ignore_events_set(Ecore_Evas *ee, int ignore); static void _ecore_evas_drm_alpha_set(Ecore_Evas *ee, int alpha); static void _ecore_evas_drm_transparent_set(Ecore_Evas *ee, int transparent); static void _ecore_evas_drm_aspect_set(Ecore_Evas *ee, double aspect); + static int _ecore_evas_drm_render(Ecore_Evas *ee); static void _ecore_evas_drm_render_updates(void *data, Evas *evas EINA_UNUSED, void *event); static int _ecore_evas_drm_render_updates_process(Ecore_Evas *ee, Eina_List *updates); @@ -70,7 +72,7 @@ static int _ecore_evas_drm_render_updates_process(Ecore_Evas *ee, Eina_List *upd static int _ecore_evas_init_count = 0; static Ecore_Drm_Device *dev = NULL; -static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func = +static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func = { _ecore_evas_drm_free, _ecore_evas_drm_callback_resize_set, @@ -120,14 +122,14 @@ static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func = _ecore_evas_drm_transparent_set, NULL, //void (*fn_profiles_set) (Ecore_Evas *ee, const char **profiles, int count); NULL, //void (*fn_profile_set) (Ecore_Evas *ee, const char *profile); - + NULL, //void (*fn_window_group_set) (Ecore_Evas *ee, const Ecore_Evas *ee_group); _ecore_evas_drm_aspect_set, NULL, //void (*fn_urgent_set) (Ecore_Evas *ee, Eina_Bool on); NULL, //void (*fn_modal_set) (Ecore_Evas *ee, Eina_Bool on); NULL, //void (*fn_demands_attention_set) (Ecore_Evas *ee, Eina_Bool on); NULL, //void (*fn_focus_skip_set) (Ecore_Evas *ee, Eina_Bool on); - + _ecore_evas_drm_render, NULL, //void (*fn_screen_geometry_get) (const Ecore_Evas *ee, int *x, int *y, int *w, int *h); @@ -223,7 +225,7 @@ ecore_evas_drm_new_internal(const char *device, unsigned int parent EINA_UNUSED, evas_output_viewport_set(ee->evas, 0, 0, w, h); if (ee->can_async_render) - evas_event_callback_add(ee->evas, EVAS_CALLBACK_RENDER_POST, + evas_event_callback_add(ee->evas, EVAS_CALLBACK_RENDER_POST, _ecore_evas_drm_render_updates, ee); if ((einfo = (Evas_Engine_Info_Drm *)evas_engine_info_get(ee->evas))) @@ -260,9 +262,9 @@ ecore_evas_drm_new_internal(const char *device, unsigned int parent EINA_UNUSED, (Ecore_Event_Multi_Down_Cb)_ecore_evas_mouse_multi_down_process, (Ecore_Event_Multi_Up_Cb)_ecore_evas_mouse_multi_up_process); - evas_event_feed_mouse_in(ee->evas, + evas_event_feed_mouse_in(ee->evas, (unsigned int)((unsigned long long) - (ecore_time_get() * 1000.0) & + (ecore_time_get() * 1000.0) & 0xffffffff), NULL); return ee; @@ -274,8 +276,168 @@ ee_err: return NULL; } +#ifdef BUILD_ECORE_EVAS_OPENGL_DRM +EAPI Ecore_Evas * +ecore_evas_gl_drm_new_internal(const char *device, unsigned int parent EINA_UNUSED, int x, int y, int w, int h) +{ + Ecore_Evas *ee; + Evas_Engine_Info_GL_Drm *einfo; + Ecore_Evas_Interface_Drm *iface; + Ecore_Evas_Engine_Drm_Data *edata; + int method; + uint32_t format = GBM_FORMAT_ARGB8888; + uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + char *num; + + /* try to find the evas drm engine */ + if (!(method = evas_render_method_lookup("gl_drm"))) + { + ERR("Render method lookup failed for GL Drm"); + return NULL; + } + + /* try to init drm */ + if (_ecore_evas_drm_init(device) < 1) return NULL; + + /* try to load gl libary, gbm libary */ + /* Typically, gbm loads the dri driver However some versions of Mesa + * do not have libglapi symbols linked in the driver. Because of this, + * using hardware accel for our drm code Could fail with a + * message that the driver could not load. Let's be proactive and + * work around this for the user by preloading the glapi library */ + dlopen("libglapi.so.0", (RTLD_LAZY | RTLD_GLOBAL)); + if (dlerror()) + { + _ecore_evas_drm_shutdown(); + return NULL; + } + + /* try to allocate space for new ecore_evas */ + if (!(ee = calloc(1, sizeof(Ecore_Evas)))) + { + ERR("Failed to allocate space for new Ecore_Evas"); + goto ee_err; + } + + if (!(edata = calloc(1, sizeof(Ecore_Evas_Engine_Drm_Data)))) + { + ERR("Failed to allocate space for new Ecore_Evas_Engine_Data"); + free(ee); + goto ee_err; + } + + ee->engine.data = edata; + + ECORE_MAGIC_SET(ee, ECORE_MAGIC_EVAS); + + ee->engine.func = (Ecore_Evas_Engine_Func *)&_ecore_evas_drm_engine_func; + + iface = _ecore_evas_drm_interface_new(); + ee->engine.ifaces = eina_list_append(ee->engine.ifaces, iface); + + /* set some engine properties */ + ee->driver = "gl_drm"; + if (device) ee->name = strdup(device); + else + ee->name = strdup(ecore_drm_device_name_get(dev)); + + if (w < 1) w = 1; + if (h < 1) h = 1; + + ee->x = ee->req.x = x; + ee->y = ee->req.y = y; + ee->w = ee->req.w = w; + ee->h = ee->req.h = h; + + ee->prop.max.w = 32767; + ee->prop.max.h = 32767; + ee->prop.layer = 4; + ee->prop.request_pos = 0; + ee->prop.sticky = 0; + ee->alpha = EINA_FALSE; + + ee->can_async_render = 1; + if (getenv("ECORE_EVAS_FORCE_SYNC_RENDER")) + ee->can_async_render = 0; + + /* try to initialize evas */ + ee->evas = evas_new(); + evas_data_attach_set(ee->evas, ee); + evas_output_method_set(ee->evas, method); + + /* FIXME: Support initial rotation ?? */ + evas_output_size_set(ee->evas, w, h); + evas_output_viewport_set(ee->evas, 0, 0, w, h); + + if (ee->can_async_render) + evas_event_callback_add(ee->evas, EVAS_CALLBACK_RENDER_POST, + _ecore_evas_drm_render_updates, ee); + + if ((einfo = (Evas_Engine_Info_GL_Drm *)evas_engine_info_get(ee->evas))) + { + einfo->info.depth = 32; + einfo->info.destination_alpha = ee->alpha; + einfo->info.rotation = ee->rotation; + + if ((num = getenv("EVAS_DRM_VSYNC"))) + { + if (!atoi(num)) einfo->vsync = EINA_FALSE; + else einfo->vsync = EINA_TRUE; + } + else einfo->vsync = EINA_TRUE; + + einfo->info.fd = ecore_drm_device_fd_get(dev); + einfo->info.format = format; + einfo->info.flags = flags; + if (einfo->info.fd) einfo->info.gbm = gbm_create_device(einfo->info.fd); + if (einfo->info.gbm) + { + einfo->info.surface = gbm_surface_create(einfo->info.gbm, w, h, + format, flags); + } + + if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo)) + { + ERR("evas_engine_info_set() for engine '%s' failed.", ee->driver); + goto eng_err; + } + } + else + { + ERR("Failed to get Evas Engine Info for '%s'", ee->driver); + goto eng_err; + } + + ee->prop.window = einfo->info.output; + + _ecore_evas_register(ee); + ecore_evas_input_event_register(ee); + + ecore_drm_device_window_set(dev, ee->prop.window); + ecore_event_window_register(ee->prop.window, ee, ee->evas, + (Ecore_Event_Mouse_Move_Cb)_ecore_evas_mouse_move_process, + (Ecore_Event_Multi_Move_Cb)_ecore_evas_mouse_multi_move_process, + (Ecore_Event_Multi_Down_Cb)_ecore_evas_mouse_multi_down_process, + (Ecore_Event_Multi_Up_Cb)_ecore_evas_mouse_multi_up_process); + + evas_event_feed_mouse_in(ee->evas, + (unsigned int)((unsigned long long) + (ecore_time_get() * 1000.0) & + 0xffffffff), NULL); + + return ee; + +eng_err: + ecore_evas_free(ee); +ee_err: + _ecore_evas_drm_shutdown(); + + return NULL; +} +#endif + /* local functions */ -static int +static int _ecore_evas_drm_init(const char *device) { if (++_ecore_evas_init_count != 1) return _ecore_evas_init_count; @@ -318,7 +480,7 @@ _ecore_evas_drm_init(const char *device) goto sprite_err; } - /* NB: We don't need to create outputs here. Evas will create the + /* NB: We don't need to create outputs here. Evas will create the * framebuffers it needs */ /* try to create outputs */ /* if (!ecore_drm_outputs_create(dev)) */ @@ -351,7 +513,7 @@ dev_err: return --_ecore_evas_init_count; } -static int +static int _ecore_evas_drm_shutdown(void) { if (--_ecore_evas_init_count != 0) return _ecore_evas_init_count; @@ -369,6 +531,7 @@ _ecore_evas_drm_shutdown(void) return _ecore_evas_init_count; } +#ifdef BUILD_ECORE_EVAS_OPENGL_DRM static Ecore_Evas_Interface_Drm * _ecore_evas_drm_interface_new(void) { @@ -386,9 +549,10 @@ _ecore_evas_drm_interface_new(void) return iface; } +#endif /* local ecore_evas functions */ -static void +static void _ecore_evas_drm_free(Ecore_Evas *ee) { Ecore_Evas_Engine_Drm_Data *data; @@ -399,49 +563,49 @@ _ecore_evas_drm_free(Ecore_Evas *ee) _ecore_evas_drm_shutdown(); } -static void +static void _ecore_evas_drm_callback_resize_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_resize = func; } -static void +static void _ecore_evas_drm_callback_move_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_move = func; } -static void +static void _ecore_evas_drm_callback_focus_in_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_focus_in = func; } -static void +static void _ecore_evas_drm_callback_focus_out_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_focus_out = func; } -static void +static void _ecore_evas_drm_callback_mouse_in_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_mouse_in = func; } -static void +static void _ecore_evas_drm_callback_mouse_out_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_mouse_out = func; } -static void +static void _ecore_evas_drm_delete_request_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func) { ee->func.fn_delete_request = func; } -static void +static void _ecore_evas_drm_move(Ecore_Evas *ee, int x, int y) { ee->req.x = x; @@ -452,7 +616,7 @@ _ecore_evas_drm_move(Ecore_Evas *ee, int x, int y) if (ee->func.fn_move) ee->func.fn_move(ee); } -static void +static void _ecore_evas_drm_resize(Ecore_Evas *ee, int w, int h) { ee->req.w = w; @@ -465,7 +629,7 @@ _ecore_evas_drm_resize(Ecore_Evas *ee, int w, int h) if (ee->func.fn_resize) ee->func.fn_resize(ee); } -static void +static void _ecore_evas_drm_move_resize(Ecore_Evas *ee, int x, int y, int w, int h) { if ((ee->x != x) || (ee->y != y)) @@ -474,7 +638,7 @@ _ecore_evas_drm_move_resize(Ecore_Evas *ee, int x, int y, int w, int h) _ecore_evas_drm_resize(ee, w, h); } -static void +static void _ecore_evas_drm_rotation_set(Ecore_Evas *ee, int rotation, int resize EINA_UNUSED) { Evas_Engine_Info_Drm *einfo; @@ -487,7 +651,7 @@ _ecore_evas_drm_rotation_set(Ecore_Evas *ee, int rotation, int resize EINA_UNUSE ERR("evas_engine_info_set() for engine '%s' failed.", ee->driver); } -static void +static void _ecore_evas_drm_show(Ecore_Evas *ee) { if ((!ee) || (ee->visible)) return; @@ -496,7 +660,7 @@ _ecore_evas_drm_show(Ecore_Evas *ee) if (ee->func.fn_show) ee->func.fn_show(ee); } -static void +static void _ecore_evas_drm_hide(Ecore_Evas *ee) { if ((!ee) || (!ee->visible)) return; @@ -506,7 +670,7 @@ _ecore_evas_drm_hide(Ecore_Evas *ee) if (ee->func.fn_hide) ee->func.fn_hide(ee); } -static void +static void _ecore_evas_drm_title_set(Ecore_Evas *ee, const char *title) { if (ee->prop.title) free(ee->prop.title); @@ -514,7 +678,7 @@ _ecore_evas_drm_title_set(Ecore_Evas *ee, const char *title) if (title) ee->prop.title = strdup(title); } -static void +static void _ecore_evas_drm_name_class_set(Ecore_Evas *ee, const char *n, const char *c) { if (ee->prop.name) free(ee->prop.name); @@ -525,7 +689,7 @@ _ecore_evas_drm_name_class_set(Ecore_Evas *ee, const char *n, const char *c) if (c) ee->prop.clas = strdup(c); } -static void +static void _ecore_evas_drm_size_min_set(Ecore_Evas *ee, int w, int h) { if (w < 0) w = 0; @@ -535,7 +699,7 @@ _ecore_evas_drm_size_min_set(Ecore_Evas *ee, int w, int h) ee->prop.min.h = h; } -static void +static void _ecore_evas_drm_size_max_set(Ecore_Evas *ee, int w, int h) { if (w < 0) w = 0; @@ -545,7 +709,7 @@ _ecore_evas_drm_size_max_set(Ecore_Evas *ee, int w, int h) ee->prop.max.h = h; } -static void +static void _ecore_evas_drm_size_base_set(Ecore_Evas *ee, int w, int h) { if (w < 0) w = 0; @@ -555,7 +719,7 @@ _ecore_evas_drm_size_base_set(Ecore_Evas *ee, int w, int h) ee->prop.base.h = h; } -static void +static void _ecore_evas_drm_size_step_set(Ecore_Evas *ee, int w, int h) { if (w < 0) w = 0; @@ -565,7 +729,7 @@ _ecore_evas_drm_size_step_set(Ecore_Evas *ee, int w, int h) ee->prop.step.h = h; } -static void +static void _ecore_evas_drm_object_cursor_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Ecore_Evas *ee; @@ -573,8 +737,8 @@ _ecore_evas_drm_object_cursor_del(void *data, Evas *e EINA_UNUSED, Evas_Object * if ((ee = data)) ee->prop.cursor.object = NULL; } -static void -_ecore_evas_drm_object_cursor_set(Ecore_Evas *ee, Evas_Object *obj, int layer, int hot_x, int hot_y) +static void +_ecore_evas_drm_object_cursor_set(Ecore_Evas *ee, Evas_Object *obj, int layer, int hot_x, int hot_y) { int x, y; Evas_Object *old; @@ -618,7 +782,7 @@ end: } } -static void +static void _ecore_evas_drm_layer_set(Ecore_Evas *ee, int layer) { if (layer < 1) layer = 1; @@ -627,28 +791,28 @@ _ecore_evas_drm_layer_set(Ecore_Evas *ee, int layer) ee->prop.layer = layer; } -static void +static void _ecore_evas_drm_iconified_set(Ecore_Evas *ee, Eina_Bool on) { if (ee->prop.iconified == on) return; ee->prop.iconified = on; } -static void +static void _ecore_evas_drm_borderless_set(Ecore_Evas *ee, Eina_Bool on) { if (ee->prop.borderless == on) return; ee->prop.borderless = on; } -static void +static void _ecore_evas_drm_maximized_set(Ecore_Evas *ee, Eina_Bool on) { if (ee->prop.maximized == on) return; ee->prop.maximized = on; } -static void +static void _ecore_evas_drm_fullscreen_set(Ecore_Evas *ee, Eina_Bool on) { Eina_Bool resized = EINA_FALSE; @@ -695,7 +859,7 @@ _ecore_evas_drm_fullscreen_set(Ecore_Evas *ee, Eina_Bool on) } } -static void +static void _ecore_evas_drm_withdrawn_set(Ecore_Evas *ee, Eina_Bool on) { if (ee->prop.withdrawn == on) return; @@ -704,14 +868,14 @@ _ecore_evas_drm_withdrawn_set(Ecore_Evas *ee, Eina_Bool on) else ecore_evas_show(ee); } -static void +static void _ecore_evas_drm_ignore_events_set(Ecore_Evas *ee, int ignore) { if (ee->ignore_events == ignore) return; ee->ignore_events = ignore; } -static void +static void _ecore_evas_drm_alpha_set(Ecore_Evas *ee, int alpha) { if (ee->in_async_render) @@ -724,7 +888,7 @@ _ecore_evas_drm_alpha_set(Ecore_Evas *ee, int alpha) /* FIXME: TODO: Finish */ } -static void +static void _ecore_evas_drm_transparent_set(Ecore_Evas *ee, int transparent) { if (ee->in_async_render) @@ -737,14 +901,14 @@ _ecore_evas_drm_transparent_set(Ecore_Evas *ee, int transparent) /* FIXME: TODO: Finish */ } -static void +static void _ecore_evas_drm_aspect_set(Ecore_Evas *ee, double aspect) { if (ee->prop.aspect == aspect) return; ee->prop.aspect = aspect; } -static int +static int _ecore_evas_drm_render(Ecore_Evas *ee) { int rend = 0; @@ -786,7 +950,7 @@ _ecore_evas_drm_render(Ecore_Evas *ee) return rend; } -static void +static void _ecore_evas_drm_render_updates(void *data, Evas *evas EINA_UNUSED, void *event) { Evas_Event_Render_Post *ev; @@ -802,7 +966,7 @@ _ecore_evas_drm_render_updates(void *data, Evas *evas EINA_UNUSED, void *event) /* TODO: handle delayed changes */ } -static int +static int _ecore_evas_drm_render_updates_process(Ecore_Evas *ee, Eina_List *updates) { int rend = 0; diff --git a/src/modules/evas/engines/gl_drm/Evas_Engine_GL_Drm.h b/src/modules/evas/engines/gl_drm/Evas_Engine_GL_Drm.h new file mode 100644 index 0000000000..3f41beeca9 --- /dev/null +++ b/src/modules/evas/engines/gl_drm/Evas_Engine_GL_Drm.h @@ -0,0 +1,44 @@ +#ifndef _EVAS_ENGINE_GL_DRM_H +# define _EVAS_ENGINE_GL_DRM_H + +# include + +typedef struct _Evas_Engine_Info_GL_Drm Evas_Engine_Info_GL_Drm; + +struct _Evas_Engine_Info_GL_Drm +{ + /* PRIVATE - don't mess with this baby or evas will poke its tongue out + * at you and make nasty noises */ + Evas_Engine_Info magic; + + /* engine specific data & parameters it needs to set up */ + struct + { + struct gbm_device *gbm; + struct gbm_surface *surface; + uint32_t format; + uint32_t flags; + int depth, screen, rotation; + unsigned char destination_alpha : 1; + int fd, tty; + Eina_Bool own_fd : 1; + Eina_Bool own_tty : 1; + int output, plane; + } 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; + + unsigned char vsync : 1; + unsigned char indirect : 1; + unsigned char swap_mode : 4; +}; + +#endif diff --git a/src/modules/evas/engines/gl_drm/evas_drm.c b/src/modules/evas/engines/gl_drm/evas_drm.c new file mode 100644 index 0000000000..18c77935f4 --- /dev/null +++ b/src/modules/evas/engines/gl_drm/evas_drm.c @@ -0,0 +1,602 @@ +#include "evas_engine.h" +#include +#include +#include + +static Evas_Engine_Info_GL_Drm *siginfo; + +static void +_evas_drm_fb_destroy_callback(struct gbm_bo *bo, void *data) +{ + Buffer *buffer = data; + struct gbm_device *gbm = gbm_bo_get_device(bo); + + if (buffer->fb) + drmModeRmFB(gbm_device_get_fd(gbm), buffer->fb); +} + +static int +_evas_drm_tty_open(Evas_Engine_Info_GL_Drm *info) +{ + int tty = STDIN_FILENO; + + /* check if the current stdin is a valid tty */ + if (!isatty(tty)) + { + /* if not, try to open the curren tty */ + if ((tty = open("/dev/tty", (O_RDWR | O_CLOEXEC))) < 0) + { + int tty0 = -1, num = -1; + char name[16]; + + /* if that fails, try tty0 */ + if ((tty0 = open("/dev/tty0", (O_WRONLY | O_CLOEXEC))) < 0) + { + CRI("Could not open tty0: %m"); + return -1; + } + + /* try to find a non-opened tty */ + if ((ioctl(tty0, VT_OPENQRY, &num) < 0) || (num < 0)) + { + CRI("Could not find a non-opened tty"); + close(tty0); + return -1; + } + + snprintf(name, sizeof(name), "/dev/tty%d", num); + + /* try to open this tty */ + if ((tty = open(name, (O_RDWR | O_CLOEXEC))) < 0) + { + CRI("Could not open tty: %s", name); + close(tty0); + return -1; + } + + /* set flag that evas should close this tty */ + info->info.own_tty = EINA_TRUE; + + /* close /dev/tty0 */ + close(tty0); + } + } + else + { + /* set flag that evas should close this tty */ + info->info.own_tty = EINA_TRUE; + } + + DBG("Opened Tty %d", tty); + + return tty; +} + +static int +_evas_drm_crtc_find(int fd, drmModeRes *res, drmModeConnector *conn) +{ + int crtc = -1; + drmModeEncoder *enc = NULL; + + /* if this connector already has an encoder, get it */ + if (conn->encoder_id) enc = drmModeGetEncoder(fd, conn->encoder_id); + + /* if this encoder already has a crtc, lets try to use that */ + if ((enc) && (enc->crtc_id)) crtc = enc->crtc_id; + + if (crtc < 0) + { + int i = 0, c = 0; + + /* if this connector has no encoder, we need to find one */ + for (; i < conn->count_encoders; ++i) + { + /* try to get this encoder */ + if (!(enc = drmModeGetEncoder(fd, conn->encoders[i]))) + continue; + + /* loop global crtcs */ + for (; c < res->count_crtcs; ++c) + { + /* does this crtc work with this encoder ? */ + if (!(enc->possible_crtcs & (1 << c))) continue; + + /* FIXME: We could be more proactive here and check that + * nobody else is using this crtc */ + + /* if it works, let's use it */ + crtc = res->crtcs[c]; + break; + } + + if (crtc >= 0) break; + } + } + + /* free the encoder */ + if (enc) drmModeFreeEncoder(enc); + + return crtc; +} + +static unsigned int +_evas_drm_crtc_buffer_get(int fd, int crtc_id) +{ + drmModeCrtc *crtc; + unsigned int id; + + if (!(crtc = drmModeGetCrtc(fd, crtc_id))) return 0; + id = crtc->buffer_id; + drmModeFreeCrtc(crtc); + return id; +} + +static void +_evas_drm_tty_sigusr1(int x EINA_UNUSED, siginfo_t *info EINA_UNUSED, void *data EINA_UNUSED) +{ + Evas_Engine_Info_GL_Drm *einfo; + + DBG("Caught SIGUSR1"); + + if (!(einfo = siginfo)) return; + + /* TODO: set canvas to not render */ + + DBG("\tDrop Master & Release VT"); + + /* drop drm master */ + if (einfo->info.own_fd) + { + if (drmDropMaster(einfo->info.fd) != 0) + WRN("Could not drop drm master: %m"); + } + + /* release vt */ + if (einfo->info.own_tty) + { + if (ioctl(einfo->info.tty, VT_RELDISP, 1) < 0) + WRN("Could not release vt: %m"); + } +} + +static void +_evas_drm_tty_sigusr2(int x EINA_UNUSED, siginfo_t *info EINA_UNUSED, void *data EINA_UNUSED) +{ + Evas_Engine_Info_GL_Drm *einfo; + + DBG("Caught SIGUSR2"); + + if (!(einfo = siginfo)) return; + + /* TODO: set canvas to render again */ + + DBG("\tAcquire VT & Set Master"); + + /* acquire vt */ + if (einfo->info.own_tty) + { + if (ioctl(einfo->info.tty, VT_RELDISP, VT_ACKACQ) < 0) + WRN("Could not acquire vt: %m"); + } + + /* set master */ + if (einfo->info.own_fd) + { + if (drmSetMaster(einfo->info.fd) != 0) + WRN("Could not set drm master: %m"); + } +} + +static Eina_Bool +_evas_drm_tty_setup(Evas_Engine_Info_GL_Drm *info) +{ + struct vt_mode vtmode = { 0 }; + struct sigaction sig; + + /* check for valid tty */ + if (info->info.tty < 0) return EINA_FALSE; + + /* setup tty rel/acq signals */ + vtmode.mode = VT_PROCESS; + vtmode.waitv = 0; + vtmode.relsig = SIGUSR1; + vtmode.acqsig = SIGUSR2; + if (ioctl(info->info.tty, VT_SETMODE, &vtmode) < 0) + { + CRI("Could not set tty mode: %m"); + return EINA_FALSE; + } + + /* store info struct + * + * NB: REALLY hate to store this here, but sigaction signal handlers cannot + * pass any 'user data' to the signal handlers :( + */ + siginfo = info; + + /* setup signal handlers for above signals */ + sig.sa_sigaction = _evas_drm_tty_sigusr1; + sig.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESTART); + sigemptyset(&sig.sa_mask); + sigaction(SIGUSR1, &sig, NULL); + + sig.sa_sigaction = _evas_drm_tty_sigusr2; + sig.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESTART); + sigemptyset(&sig.sa_mask); + sigaction(SIGUSR2, &sig, NULL); + + return EINA_TRUE; +} + +static void +_evas_drm_outbuf_page_flip(int fd EINA_UNUSED, unsigned int seq EINA_UNUSED, unsigned int tv_sec EINA_UNUSED, unsigned int tv_usec EINA_UNUSED, void *data) +{ + Outbuf *ob; + Buffer *buff; + + /* get the output buffer from data */ + if (!(ob = data)) return; + + buff = &(ob->priv.buffer[ob->priv.curr]); + gbm_surface_release_buffer(ob->surface, buff->bo); + + ob->priv.pending_flip = EINA_FALSE; + ob->priv.curr = (ob->priv.curr + 1) % ob->priv.num; +} + +static Eina_Bool +_evas_drm_outbuf_planes_setup(Outbuf *ob, drmModePlaneResPtr pres) +{ + drmModePlanePtr dplane; + Plane *oplane; + unsigned int p = 0; + unsigned int f = 0; + + for (p = 0; p < pres->count_planes; p++) + { + /* try to get this plane */ + if (!(dplane = drmModeGetPlane(ob->priv.fd, pres->planes[p]))) + continue; + + /* try to allocate space for our plane */ + if (!(oplane = + malloc(sizeof(Plane) + + ((sizeof(unsigned int)) * dplane->count_formats)))) + { + drmModeFreePlane(dplane); + continue; + } + + oplane->crtcs = dplane->possible_crtcs; + oplane->id = dplane->plane_id; + oplane->num_formats = dplane->count_formats; + memcpy(oplane->formats, dplane->formats, + dplane->count_formats * sizeof(dplane->formats[0])); + + DBG("Plane %d, %d %d", p, dplane->x, dplane->y); + DBG("\tFB: %d", dplane->fb_id); + DBG("\tCrtc: %d, %d %d", dplane->crtc_id, + dplane->crtc_x, dplane->crtc_y); + + DBG("\tSupported Formats"); + for (f = 0; f < dplane->count_formats; f++) + { + DBG("\t\t%C%C%C%C", (dplane->formats[f] & 0xFF), + ((dplane->formats[f] >> 8) & 0xFF), + ((dplane->formats[f] >> 16) & 0xFF), + ((dplane->formats[f] >> 24) & 0xFF)); + } + + /* free this plane */ + drmModeFreePlane(dplane); + + /* append this plane */ + ob->priv.planes = eina_list_append(ob->priv.planes, oplane); + } + + if (eina_list_count(ob->priv.planes) < 1) return EINA_FALSE; + return EINA_TRUE; +} + +Eina_Bool +evas_drm_init(Evas_Engine_Info_GL_Drm *info) +{ + /* check for valid engine info */ + if (!info) return EINA_FALSE; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* check if we already opened the tty */ + if (info->info.tty < 0) + { + /* try to open the current tty */ + if ((info->info.tty = _evas_drm_tty_open(info)) < 0) + { + /* check if we already opened the card. if so, close it */ + if ((info->info.fd >= 0) && (info->info.own_fd)) + { + close(info->info.fd); + info->info.fd = -1; + } + + return EINA_FALSE; + } + } + + /* with the tty opened, we need to set it up */ + if (!_evas_drm_tty_setup(info)) + { + /* setup of tty failed, close it */ + if ((info->info.tty >= 0) && (info->info.own_tty)) + close(info->info.tty); + + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +evas_drm_shutdown(Evas_Engine_Info_GL_Drm *info) +{ + /* check for valid engine info */ + if (!info) return EINA_TRUE; + + /* check if we already opened the tty. if so, close it */ + if ((info->info.tty >= 0) && (info->info.own_tty)) + { + close(info->info.tty); + info->info.tty = -1; + } + + return EINA_TRUE; +} + +Eina_Bool +evas_drm_gbm_init(Evas_Engine_Info_GL_Drm *info, int w, int h) +{ + if (!info) return EINA_FALSE; + if (info->info.fd < 0) return EINA_FALSE; + + if (!(info->info.gbm = gbm_create_device(info->info.fd))) + { + return EINA_FALSE; + } + + if (!(info->info.surface = gbm_surface_create(info->info.gbm, w, h, + info->info.format, + info->info.flags))) + { + gbm_device_destroy(info->info.gbm); + info->info.gbm = NULL; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +evas_drm_gbm_shutdown(Evas_Engine_Info_GL_Drm *info) +{ + if (!info) return EINA_TRUE; + + if (info->info.surface) + { + gbm_surface_destroy(info->info.surface); + info->info.surface = NULL; + } + if (info->info.gbm) + { + gbm_device_destroy(info->info.gbm); + info->info.gbm = NULL; + } + + return EINA_TRUE; +} + +Eina_Bool +evas_drm_outbuf_setup(Outbuf *ob) +{ + drmModeRes *res; + drmModeConnector *conn; + drmModePlaneResPtr pres; + int i = 0; + + /* check for valid Output buffer */ + if ((!ob) || (ob->priv.fd < 0)) return EINA_FALSE; + + /* setup drmHandleEvent context */ + memset(&ob->priv.ctx, 0, sizeof(ob->priv.ctx)); + ob->priv.ctx.version = DRM_EVENT_CONTEXT_VERSION; + ob->priv.ctx.page_flip_handler = _evas_drm_outbuf_page_flip; + + /* try to get drm resources */ + if (!(res = drmModeGetResources(ob->priv.fd))) + { + CRI("Could not get drm resources: %m"); + return EINA_FALSE; + } + + /* loop the connectors */ + for (; i < res->count_connectors; ++i) + { + int crtc = -1; + int m = 0; + + /* try to get this connector */ + if (!(conn = drmModeGetConnector(ob->priv.fd, res->connectors[i]))) + { + WRN("Could not get drm connector %d: %m", i); + continue; + } + + /* make sure this connector is actually connected */ + if (conn->connection != DRM_MODE_CONNECTED) + { + /* free connector resources */ + drmModeFreeConnector(conn); + continue; + } + + /* make sure it has modes */ + if (conn->count_modes == 0) + { + /* free connector resources */ + drmModeFreeConnector(conn); + continue; + } + + /* try to find a crtc for this connector */ + if ((crtc = _evas_drm_crtc_find(ob->priv.fd, res, conn)) < 0) + { + /* free connector resources */ + drmModeFreeConnector(conn); + continue; + } + + /* record the connector id */ + ob->priv.conn = conn->connector_id; + + /* record the crtc id */ + ob->priv.crtc = crtc; + + /* get the current framebuffer */ + ob->priv.fb = _evas_drm_crtc_buffer_get(ob->priv.fd, crtc); + + /* record the current mode */ + memcpy(&ob->priv.mode, &conn->modes[0], sizeof(ob->priv.mode)); + + for (m = 0; m < conn->count_modes; m++) + { + DBG("Output Available Mode: %d: %d %d %d", ob->priv.conn, + conn->modes[m].hdisplay, conn->modes[m].vdisplay, + conn->modes[m].vrefresh); + + /* try to find a mode which matches the requested size */ + if ((conn->modes[m].hdisplay == ob->w) && + (conn->modes[m].vdisplay == ob->h) && + (conn->modes[m].vrefresh == 60)) + { + memcpy(&ob->priv.mode, &conn->modes[m], + sizeof(ob->priv.mode)); + } + } + + DBG("Output Current Mode: %d: %d %d", ob->priv.conn, + ob->priv.mode.hdisplay, ob->priv.mode.vdisplay); + + if ((ob->priv.mode.hdisplay != conn->modes[0].hdisplay) || + (ob->priv.mode.vdisplay != conn->modes[0].vdisplay)) + { + /* set new crtc mode */ + drmModeSetCrtc(ob->priv.fd, ob->priv.crtc, ob->priv.fb, 0, 0, + &ob->priv.conn, 1, &ob->priv.mode); + } + + /* free connector resources */ + drmModeFreeConnector(conn); + + break; + } + + /* get any plane resource from the card */ + pres = drmModeGetPlaneResources(ob->priv.fd); + + /* if we have at least one plane, set it up */ + if (pres->count_planes > 0) + { + if (!_evas_drm_outbuf_planes_setup(ob, pres)) + WRN("Could not setup hardware planes"); + } + + /* free plane resources */ + drmModeFreePlaneResources(pres); + + /* free drm resources */ + drmModeFreeResources(res); + + return EINA_TRUE; +} + +void +evas_drm_outbuf_framebuffer_set(Outbuf *ob, Buffer *buffer) +{ + int ret; + uint32_t handles[4], pitches[4], offsets[4]; + uint32_t width, height; + uint32_t format; + + /* validate params */ + if ((!ob) || (!buffer)) return; + + if (buffer->valid) return; + + width = gbm_bo_get_width(buffer->bo); + height = gbm_bo_get_height(buffer->bo); + buffer->stride = gbm_bo_get_stride(buffer->bo); + buffer->handle = gbm_bo_get_handle(buffer->bo).u32; + buffer->size = buffer->stride * height; + format = gbm_bo_get_format(buffer->bo); + + handles[0] = buffer->handle; + pitches[0] = buffer->stride; + offsets[0] = 0; + + ret = drmModeAddFB2(ob->priv.fd, width, height, format, handles, + pitches, offsets, &(buffer->fb), 0); + + if (ret) + { + ret = drmModeAddFB(ob->priv.fd, width, height, 24, 32, + buffer->stride, buffer->handle, &(buffer->fb)); + } + if (ret) ERR("Failed to AddFB: %m"); + + ret = drmModeSetCrtc(ob->priv.fd, ob->priv.crtc, buffer->fb, 0, 0, + &ob->priv.conn, 1, &ob->priv.mode); + + if (ret) ERR("Failed to set crtc: %m"); + + gbm_bo_set_user_data(buffer->bo, buffer, _evas_drm_fb_destroy_callback); + + buffer->valid = EINA_TRUE; +} + +Eina_Bool +evas_drm_framebuffer_send(Outbuf *ob, Buffer *buffer) +{ + /* check for valid Output buffer */ + if ((!ob) || (ob->priv.fd < 0)) return EINA_FALSE; + + /* check for valid buffer */ + if (!buffer) return EINA_FALSE; + + if (ob->vsync) + { + if (drmModePageFlip(ob->priv.fd, ob->priv.crtc, + buffer->fb, DRM_MODE_PAGE_FLIP_EVENT, ob) < 0) + { + ERR("Cannot flip crtc for connector %u: %m", ob->priv.conn); + return EINA_FALSE; + } + + ob->priv.pending_flip = EINA_TRUE; + + while (ob->priv.pending_flip) + drmHandleEvent(ob->priv.fd, &ob->priv.ctx); + } + else + { + /* NB: We don't actually need to do this if we are not vsync + * because we are drawing directly to the buffer anyway. + * If we enable the sending of buffer to crtc, it causes vsync */ + + /* send this buffer to the crtc */ + /* evas_drm_outbuf_framebuffer_set(ob, buffer); */ + + /* increment buffer we are using */ + ob->priv.curr = (ob->priv.curr + 1) % ob->priv.num; + } + + return EINA_TRUE; +} diff --git a/src/modules/evas/engines/gl_drm/evas_drm_main.c b/src/modules/evas/engines/gl_drm/evas_drm_main.c new file mode 100644 index 0000000000..84a0b54068 --- /dev/null +++ b/src/modules/evas/engines/gl_drm/evas_drm_main.c @@ -0,0 +1,638 @@ +#include "evas_engine.h" + +/* local variables */ +static Outbuf *_evas_gl_drm_window = NULL; +static EGLContext context = EGL_NO_CONTEXT; +static int win_count = 0; + +/* local function prototypes */ +static void _outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects, unsigned int count); +static void _outbuf_flush_famebuffer(Outbuf *ob); + +/* local functions */ +static void +_outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects EINA_UNUSED, unsigned int count EINA_UNUSED) +{ + Buffer *buff; + + buff = &(ob->priv.buffer[ob->priv.curr]); + + buff->bo = gbm_surface_lock_front_buffer(ob->surface); + + /* if this buffer is not valid, we need to set it */ + if (!buff->valid) evas_drm_outbuf_framebuffer_set(ob, buff); + + /* send this buffer to the crtc */ + evas_drm_framebuffer_send(ob, buff); +} + +static void +_outbuf_flush_famebuffer(Outbuf *ob) +{ + Eina_Rectangle *rects = NULL; + unsigned int n = 0; + //TODO: add region flush routine for SwapBuffersWithDamage + + /* force a buffer swap */ + _outbuf_buffer_swap(ob, rects, n); +} + +Outbuf *eng_window_new(Evas_Engine_Info_GL_Drm *info, Evas *e, struct gbm_device *gbm, struct gbm_surface *surface, int screen, int depth, int w, int h, int indirect EINA_UNUSED, int alpha, int rot, Render_Engine_Swap_Mode swap_mode) +{ + Outbuf *gw; + int context_attrs[3]; + int config_attrs[40]; + int major_version, minor_version; + int num_config, n = 0; + const GLubyte *vendor, *renderer, *version; + Eina_Bool blacklist = EINA_FALSE; + char *num; + + /* try to allocate space for outbuf */ + gw = calloc(1, sizeof(Outbuf)); + if (!gw) return NULL; + + /* set properties of outbuf */ + win_count++; + gw->gbm = gbm; + gw->surface = surface; + gw->screen = screen; + gw->depth = depth; + gw->w = w; + gw->h = h; + gw->alpha = alpha; + gw->rot = rot; + gw->swap_mode = swap_mode; + gw->info = info; + gw->evas = e; + + /* setup drm outbuf */ + /* set drm card fd */ + gw->priv.fd = info->info.fd; + /* try to setup the drm card for this outbuf */ + if (!evas_drm_outbuf_setup(gw)) + { + ERR("Could not setup drm outbuf"); + free(gw); + return NULL; + } + + if (gw->w < gw->priv.mode.hdisplay) gw->w = gw->priv.mode.hdisplay; + if (gw->h < gw->priv.mode.vdisplay) gw->h = gw->priv.mode.vdisplay; + + info->info.output = gw->priv.fb; + // TODO: change vsync for drm egl + //gw->vsync = info->vsync; + + gw->priv.num = NUM_BUFFERS; + /* check for buffer override */ + // TODO: change for gbm_bo related drm buffer number. + if ((num = getenv("EVAS_GL_DRM_BUFFERS"))) + { + gw->priv.num = atoi(num); + + /* cap maximum # of buffers */ + if (gw->priv.num <= 0) gw->priv.num = 1; + else if (gw->priv.num > 3) gw->priv.num = 3; + } + /* end drm outbuf setup */ + + /* setup gbm egl surface */ + context_attrs[0] = EGL_CONTEXT_CLIENT_VERSION; + context_attrs[1] = 2; + context_attrs[2] = EGL_NONE; + + config_attrs[n++] = EGL_SURFACE_TYPE; + config_attrs[n++] = EGL_WINDOW_BIT; + config_attrs[n++] = EGL_RED_SIZE; + config_attrs[n++] = 1; + config_attrs[n++] = EGL_GREEN_SIZE; + config_attrs[n++] = 1; + config_attrs[n++] = EGL_BLUE_SIZE; + config_attrs[n++] = 1; + config_attrs[n++] = EGL_ALPHA_SIZE; + if (gw->alpha) config_attrs[n++] = 1; + else config_attrs[n++] = 0; + config_attrs[n++] = EGL_RENDERABLE_TYPE; + config_attrs[n++] = EGL_OPENGL_ES2_BIT; + config_attrs[n++] = EGL_NONE; + + DBG("GBM DEVICE: %x", (unsigned int)gbm); + gw->egl_disp = eglGetDisplay((EGLNativeDisplayType)(gw->gbm)); + if (gw->egl_disp == EGL_NO_DISPLAY) + { + ERR("eglGetDisplay() fail. code=%#x", eglGetError()); + eng_window_free(gw); + return NULL; + } + if (!eglInitialize(gw->egl_disp, &major_version, &minor_version)) + { + ERR("eglInitialize() fail. code=%#x", eglGetError()); + eng_window_free(gw); + return NULL; + } + eglBindAPI(EGL_OPENGL_ES_API); + if (eglGetError() != EGL_SUCCESS) + { + ERR("eglBindAPI() fail. code=%#x", eglGetError()); + eng_window_free(gw); + return NULL; + } + + num_config = 0; + if (!eglChooseConfig(gw->egl_disp, config_attrs, &gw->egl_config, + 1, &num_config) || (num_config != 1)) + { + ERR("eglChooseConfig() fail. code=%#x", eglGetError()); + eng_window_free(gw); + return NULL; + } + + gw->egl_surface[0] = + eglCreateWindowSurface(gw->egl_disp, gw->egl_config, + (EGLNativeWindowType)gw->surface, NULL); + if (gw->egl_surface[0] == EGL_NO_SURFACE) + { + ERR("eglCreateWindowSurface() fail for %p. code=%#x", + gw->surface, eglGetError()); + eng_window_free(gw); + return NULL; + } + + gw->egl_context[0] = + eglCreateContext(gw->egl_disp, gw->egl_config, context, context_attrs); + if (gw->egl_context[0] == EGL_NO_CONTEXT) + { + ERR("eglCreateContext() fail. code=%#x", eglGetError()); + eng_window_free(gw); + return NULL; + } + + if (context == EGL_NO_CONTEXT) context = gw->egl_context[0]; + + if (eglMakeCurrent(gw->egl_disp, gw->egl_surface[0], + gw->egl_surface[0], gw->egl_context[0]) == EGL_FALSE) + { + ERR("eglMakeCurrent() fail. code=%#x", eglGetError()); + eng_window_free(gw); + return NULL; + } + + vendor = glGetString(GL_VENDOR); + renderer = glGetString(GL_RENDERER); + version = glGetString(GL_VERSION); + if (!vendor) vendor = (unsigned char *)"-UNKNOWN-"; + if (!renderer) renderer = (unsigned char *)"-UNKNOWN-"; + if (!version) version = (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); + } + + 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); + eng_window_free(gw); + return NULL; + } + + gw->gl_context = glsym_evas_gl_common_context_new(); + if (!gw->gl_context) + { + eng_window_free(gw); + return NULL; + } + + gw->gl_context->egldisp = gw->egl_disp; + gw->gl_context->eglctxt = gw->egl_context[0]; + + eng_window_use(gw); + glsym_evas_gl_common_context_resize(gw->gl_context, w, h, rot); + + gw->surf = EINA_TRUE; + + return gw; +} + +void +eng_window_free(Outbuf *gw) +{ + int ref = 0; + + win_count--; + eng_window_use(gw); + + if (gw == _evas_gl_drm_window) _evas_gl_drm_window = NULL; + + if (gw->gl_context) + { + ref = gw->gl_context->references - 1; + glsym_evas_gl_common_context_free(gw->gl_context); + } + + eglMakeCurrent(gw->egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (gw->egl_context[0] != context) + eglDestroyContext(gw->egl_disp, gw->egl_context[0]); + + if (gw->egl_surface[0] != EGL_NO_SURFACE) + eglDestroySurface(gw->egl_disp, gw->egl_surface[0]); + +//TODO: consider gbm_surface destroy or not. +#if 0 + if (gw->surface) + { + gbm_surface_destroy(gw->surface); + gw->info->info.surface = NULL; + } +#endif + + if (ref == 0) + { + if (context) eglDestroyContext(gw->egl_disp, context); + eglTerminate(gw->egl_disp); + eglReleaseThread(); + context = EGL_NO_CONTEXT; + } + free(gw); +} + +Eina_Bool +eng_window_make_current(void *data, void *doit) +{ + Outbuf *gw; + + if (!(gw = data)) return EINA_FALSE; + + if (doit) + { + if (!eglMakeCurrent(gw->egl_disp, gw->egl_surface[0], + gw->egl_surface[0], gw->egl_context[0])) + return EINA_FALSE; + } + else + { + if (!eglMakeCurrent(gw->egl_disp, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT)) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +void +eng_window_use(Outbuf *gw) +{ + Eina_Bool force = EINA_FALSE; + + glsym_evas_gl_preload_render_lock(eng_window_make_current, gw); + + if (_evas_gl_drm_window) + { + if (eglGetCurrentContext() != _evas_gl_drm_window->egl_context[0]) + force = EINA_TRUE; + } + + if ((_evas_gl_drm_window != gw) || (force)) + { + if (_evas_gl_drm_window) + { + glsym_evas_gl_common_context_use(_evas_gl_drm_window->gl_context); + glsym_evas_gl_common_context_flush(_evas_gl_drm_window->gl_context); + } + + _evas_gl_drm_window = gw; + + if (gw) + { + if (gw->egl_surface[0] != EGL_NO_SURFACE) + { + if (eglMakeCurrent(gw->egl_disp, gw->egl_surface[0], + gw->egl_surface[0], + gw->egl_context[0]) == EGL_FALSE) + ERR("eglMakeCurrent() failed!"); + } + } + } + + if (gw) glsym_evas_gl_common_context_use(gw->gl_context); +} + +void +eng_window_unsurf(Outbuf *gw) +{ + if (!gw->surf) return; + if (!getenv("EVAS_GL_WIN_RESURF")) return; + if (getenv("EVAS_GL_INFO")) printf("unsurf %p\n", gw); + + if (_evas_gl_drm_window) + glsym_evas_gl_common_context_flush(_evas_gl_drm_window->gl_context); + if (_evas_gl_drm_window == gw) + { + eglMakeCurrent(gw->egl_disp, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (gw->egl_surface[0] != EGL_NO_SURFACE) + eglDestroySurface(gw->egl_disp, gw->egl_surface[0]); + gw->egl_surface[0] = EGL_NO_SURFACE; + + _evas_gl_drm_window = NULL; + } + + gw->surf = EINA_FALSE; +} + +void +eng_window_resurf(Outbuf *gw) +{ + if (gw->surf) return; + if (getenv("EVAS_GL_INFO")) printf("resurf %p\n", gw); + + gw->egl_surface[0] = + eglCreateWindowSurface(gw->egl_disp, gw->egl_config, + (EGLNativeWindowType)gw->surface, NULL); + + if (gw->egl_surface[0] == EGL_NO_SURFACE) + { + ERR("eglCreateWindowSurface() fail for %p. code=%#x", + gw->surface, eglGetError()); + return; + } + + if (eglMakeCurrent(gw->egl_disp, gw->egl_surface[0], gw->egl_surface[0], + gw->egl_context[0]) == EGL_FALSE) + ERR("eglMakeCurrent() failed!"); + + gw->surf = EINA_TRUE; +} + +Context_3D * +eng_gl_context_new(Outbuf *gw) +{ + Context_3D *ctx; + int context_attrs[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + if (!gw) return NULL; + + ctx = calloc(1, sizeof(Context_3D)); + if (!ctx) return NULL; + + ctx->context = eglCreateContext(gw->egl_disp, gw->egl_config, + gw->egl_context[0], context_attrs); + + if (!ctx->context) + { + ERR("EGL context creation failed."); + goto error; + } + + ctx->display = gw->egl_disp; + ctx->surface = gw->egl_surface[0]; + + return ctx; + +error: + free(ctx); + return NULL; +} + +void +eng_gl_context_free(Context_3D *ctx) +{ + eglDestroyContext(ctx->display, ctx->context); + free(ctx); +} + +void +eng_gl_context_use(Context_3D *ctx) +{ + if (eglMakeCurrent(ctx->display, ctx->surface, + ctx->surface, ctx->context) == EGL_FALSE) + { + ERR("eglMakeCurrent() failed."); + } +} + +void +eng_outbuf_reconfigure(Outbuf *ob, int w, int h, int rot, Outbuf_Depth depth EINA_UNUSED) +{ + ob->w = w; + ob->h = h; + ob->rot = rot; + eng_window_use(ob); + glsym_evas_gl_common_context_resize(ob->gl_context, w, h, rot); + + //TODO: need drm gbm surface destroy & re-create.? +} + +int +eng_outbuf_get_rot(Outbuf *ob) +{ + return ob->rot; +} + +Render_Engine_Swap_Mode +eng_outbuf_swap_mode(Outbuf *ob) +{ + if (ob->swap_mode == MODE_AUTO && extn_have_buffer_age) + { + Render_Engine_Swap_Mode swap_mode; + + EGLint age = 0; + + if (!eglQuerySurface(ob->egl_disp, + ob->egl_surface[0], + EGL_BUFFER_AGE_EXT, &age)) + age = 0; + + if (age == 1) swap_mode = MODE_COPY; + else if (age == 2) swap_mode = MODE_DOUBLE; + else if (age == 3) swap_mode = MODE_TRIPLE; + else if (age == 4) swap_mode = MODE_QUADRUPLE; + else swap_mode = MODE_FULL; + if ((int)age != ob->prev_age) swap_mode = MODE_FULL; + ob->prev_age = age; + + return swap_mode; + } + + return ob->swap_mode; +} + +Eina_Bool +eng_outbuf_region_first_rect(Outbuf *ob) +{ + ob->gl_context->preserve_bit = GL_COLOR_BUFFER_BIT0_QCOM; + + glsym_evas_gl_preload_render_lock(eng_preload_make_current, ob); + eng_window_use(ob); + if (!_re_wincheck(ob)) return EINA_TRUE; + + glsym_evas_gl_common_context_resize(ob->gl_context, + ob->w, ob->h, + ob->rot); + + glsym_evas_gl_common_context_flush(ob->gl_context); + glsym_evas_gl_common_context_newframe(ob->gl_context); + + return EINA_FALSE; +} + +void* +eng_outbuf_new_region_for_update(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 +eng_outbuf_push_updated_region(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->draw.drew = EINA_TRUE; + glsym_evas_gl_common_context_flush(ob->gl_context); +} + +void +eng_outbuf_push_free_region_for_update(Outbuf *ob EINA_UNUSED, RGBA_Image *update EINA_UNUSED) +{ + /* Nothing to do here as we don't really create an image per area */ +} + +void +eng_outbuf_flush(Outbuf *ob, Tilebuf_Rect *rects, Evas_Render_Mode render_mode) +{ + if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) goto end; + + if (!_re_wincheck(ob)) goto end; + if (!ob->draw.drew) goto end; + + ob->draw.drew = EINA_FALSE; + eng_window_use(ob); + glsym_evas_gl_common_context_done(ob->gl_context); + + if (!ob->vsync) + { + if (ob->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); + } +// TODO: Check eglSwapBuffersWithDamage for gl_drm and apply +#if 0 + if ((glsym_eglSwapBuffersWithDamage) && (ob->swap_mode != MODE_FULL)) + + { + EGLint num = 0, *result = NULL, i = 0; + Tilebuf_Rect *r; + + // if partial swaps can be done use re->rects + num = eina_inlist_count(EINA_INLIST_GET(rects)); + if (num > 0) + { + result = alloca(sizeof(EGLint) * 4 * num); + EINA_INLIST_FOREACH(EINA_INLIST_GET(rects), r) + { + int gw, gh; + + gw = ob->gl_context->w; + gh = ob->gl_context->h; + switch (ob->rot) + { + case 0: + result[i + 0] = r->x; + result[i + 1] = gh - (r->y + r->h); + result[i + 2] = r->w; + result[i + 3] = r->h; + break; + case 90: + result[i + 0] = r->y; + result[i + 1] = r->x; + result[i + 2] = r->h; + result[i + 3] = r->w; + break; + case 180: + result[i + 0] = gw - (r->x + r->w); + result[i + 1] = r->y; + result[i + 2] = r->w; + result[i + 3] = r->h; + break; + case 270: + result[i + 0] = gh - (r->y + r->h); + result[i + 1] = gw - (r->x + r->w); + result[i + 2] = r->h; + result[i + 3] = r->w; + break; + default: + result[i + 0] = r->x; + result[i + 1] = gh - (r->y + r->h); + result[i + 2] = r->w; + result[i + 3] = r->h; + break; + } + i += 4; + } + glsym_eglSwapBuffersWithDamage(ob->egl_disp, + ob->egl_surface[0], + result, num); + } + } + else +#endif + 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); + } + + //Flush GL Surface data to Framebuffer + _outbuf_flush_famebuffer(ob); + + ob->frame_cnt++; + + end: + //TODO: Need render unlock after drm page flip? + glsym_evas_gl_preload_render_unlock(eng_preload_make_current, ob); +} + +Evas_Engine_GL_Context * +eng_outbuf_gl_context_get(Outbuf *ob) +{ + return ob->gl_context; +} + +void * +eng_outbuf_egl_display_get(Outbuf *ob) +{ + return ob->egl_disp; +} diff --git a/src/modules/evas/engines/gl_drm/evas_engine.c b/src/modules/evas/engines/gl_drm/evas_engine.c new file mode 100644 index 0000000000..d6ef083c12 --- /dev/null +++ b/src/modules/evas/engines/gl_drm/evas_engine.c @@ -0,0 +1,1015 @@ +#include "evas_common_private.h" /* Also includes international specific stuff */ +#include "evas_engine.h" +#include + +#ifdef HAVE_DLSYM +# include /* dlopen,dlclose,etc */ +#else +# error gl_drm 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_gl_drm_log_dom = -1; +int extn_have_buffer_age = 1; + +/* 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; +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; + +/* local structures */ +typedef struct _Render_Engine Render_Engine; + +struct _Render_Engine +{ + Render_Engine_GL_Generic generic; +}; + +/* 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) (); + +/* 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; + +/* 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); +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); + +static void _re_winfree(Render_Engine *re); + +/* local variables */ +static Ecore_Drm_Device *drm_dev = NULL; +static Eina_Bool initted = EINA_FALSE; +static int gl_wins = 0; + +/* local inline functions */ +static inline Outbuf * +eng_get_ob(Render_Engine *re) +{ + return re->generic.software.ob; +} + +static inline void +_drm_device_shutdown(Evas_Engine_Info_GL_Drm *info) +{ + if (!info) return; + /* check if we already opened the card. if so, close it */ + if ((info->info.fd >= 0) && (info->info.own_fd)) + { + ecore_drm_device_close(drm_dev); + info->info.fd = -1; + ecore_drm_device_free(drm_dev); + } +} + +/* 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 +}; + +/* 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); + + 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) + { + if (getenv("EVAS_GL_INFO")) + printf("EGL EXTN:\n%s\n", str); + 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 = (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 = (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 = (Render_Engine *)data; + EGLContext ctx; + EGLSurface sfc; + EGLDisplay dpy; + int ret = 0; + + 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) eng_window_use(NULL); + + ret = eglMakeCurrent(dpy, sfc, sfc, ctx); + if (!ret) + { + ERR("eglMakeCurrent() failed! Error Code=%#x", eglGetError()); + return 0; + } + } + + return 1; +} + +static void * +evgl_eng_native_window_create(void *data) +{ + Render_Engine *re = (Render_Engine *)data; + struct gbm_surface *surface; + Evas_Engine_Info_GL_Drm *info; + + if (!re) + { + ERR("Invalid Render Engine Data!"); + return NULL; + } + + info = eng_get_ob(re)->info; + if (!info) + { + ERR("Invalid Evas Engine GL_DRM Info!"); + return NULL; + } + + surface = gbm_surface_create(info->info.gbm, 1, 1, + info->info.format, + info->info.flags); + if (!surface) + { + ERR("Could not create gl drm window: %m"); + } + + return (void*)surface; +} + +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("Inavlid native surface."); + return 0; + } + + gbm_surface_destroy((struct gbm_surface*)native_window); + return 1; +} + +static void * +evgl_eng_window_surface_create(void *data, void *native_window) +{ + Render_Engine *re = (Render_Engine *)data; + EGLSurface surface = EGL_NO_SURFACE; + + 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 = (Render_Engine *)data; + EGLBoolean ret = EGL_FALSE; + + 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) +{ + Render_Engine *re = (Render_Engine *)data; + EGLContext context = EGL_NO_CONTEXT; + int context_attrs[3]; + + if (!re) + { + ERR("Invalid Render Engine Data!"); + 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("Engine Context Creations Failed. Error: %#x.", eglGetError()); + return NULL; + } + + return (void*)context; +} + +static int +evgl_eng_context_destroy(void *data, void *context) +{ + Render_Engine *re = (Render_Engine *)data; + EGLBoolean ret = EGL_FALSE; + + 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 = (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 = (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 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)); + eng_window_unsurf(eng_get_ob(re)); +} + +/* engine specific override functions */ +static void * +eng_info(Evas *eo_e EINA_UNUSED) +{ + Evas_Engine_Info_GL_Drm *info; + + /* try to allocate space for our engine info */ + if (!(info = calloc(1, sizeof(Evas_Engine_Info_GL_Drm)))) + 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_GL_Drm *info; + + if (info = (Evas_Engine_Info_GL_Drm *)in) + free(info); +} + +static int +eng_setup(Evas *eo_e, void *in) +{ + Evas_Engine_Info_GL_Drm *info = NULL; + Evas_Public_Data *epd = NULL; + Render_Engine *re = NULL; + Render_Engine_Swap_Mode swap_mode = MODE_FULL; + const char *s; + + /* try to cast to our engine info structure */ + if (!(info = (Evas_Engine_Info_GL_Drm *)in)) return 0; + + /* try to get the evas public data */ + if (!(epd = eo_data_scope_get(eo_e, EVAS_CANVAS_CLASS))) return 0; + + if ((s = getenv("EVAS_GL_SWAP_MODE"))) + { + 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 + { + swap_mode = MODE_FULL; + } + + /* check for existing engine output */ + if (!epd->engine.data.output) + { + Outbuf *ob; + Render_Engine_Merge_Mode merge_mode = MERGE_FULL; + + if (!initted) + { + evas_common_cpu_init(); + evas_common_blend_init(); + evas_common_image_init(); + evas_common_convert_init(); + evas_common_scale_init(); + evas_common_rectangle_init(); + evas_common_polygon_init(); + evas_common_line_init(); + evas_common_font_init(); + evas_common_draw_init(); + evas_common_tilebuf_init(); + glsym_evas_gl_preload_init(); + } + + /* if we have no drm device, get one */ + if (info->info.fd < 0) + { + /* try to init drm (this includes openening the card & tty) */ + if (!ecore_drm_init()) + return 0; + + /* try getting the default drm device */ + if (!(drm_dev = ecore_drm_device_find(NULL, NULL))) + { + _drm_device_shutdown(info); + return 0; + } + + /* check if we already opened the drm device with ecore_evas */ + if (info->info.fd < 0) + { + /* try to open the drm ourselfs (most likely because we get called from expedite) */ + if (!ecore_drm_device_open(drm_dev)) + { + _drm_device_shutdown(info); + return 0; + } + info->info.own_fd = EINA_TRUE; + info->info.fd = ecore_drm_device_fd_get(drm_dev); + } + /* try to init drm (this includes openening tty) */ + /* FIXME replace with ecore_drm_tty */ + if (!evas_drm_init(info)) + { + _drm_device_shutdown(info); + return 0; + } + } + + if (!(info->info.gbm) || !(info->info.surface)) + { + if (!evas_drm_gbm_init(info, epd->output.w, epd->output.h)) + { + evas_drm_shutdown(info); + _drm_device_shutdown(info); + return 0; + } + } + + DBG("FD: %d, GBM_DEVICE: 0x%x, GBM_SURFACE: 0x%x", + info->info.fd, (unsigned int)info->info.gbm, + (unsigned int)info->info.surface); + + re = calloc(1, sizeof(Render_Engine)); + if (!re) return 0; + + /* try to create new outbuf */ + ob = eng_window_new(info, eo_e, + info->info.gbm, + info->info.surface, + info->info.screen, + info->info.depth, + epd->output.w, epd->output.h, + info->indirect, + info->info.destination_alpha, + info->info.rotation, + swap_mode); + if (!ob) + { + /* shutdown destroy gbm surface & shutdown gbm device */ + evas_drm_gbm_shutdown(info); + /* shutdown tty */ + /* FIXME use ecore_drm_tty */ + evas_drm_shutdown(info); + _drm_device_shutdown(info); + free(re); + return 0; + } + + + if (!evas_render_engine_gl_generic_init(&re->generic, ob, + eng_outbuf_swap_mode, + eng_outbuf_get_rot, + eng_outbuf_reconfigure, + eng_outbuf_region_first_rect, + eng_outbuf_new_region_for_update, + eng_outbuf_push_updated_region, + eng_outbuf_push_free_region_for_update, + NULL, + eng_outbuf_flush, + eng_window_free, + eng_window_use, + eng_outbuf_gl_context_get, + eng_outbuf_egl_display_get, + eng_gl_context_new, + eng_gl_context_use, + &evgl_funcs, + epd->output.w, epd->output.h)) + { + /* free outbuf */ + eng_window_free(ob); + /* shutdown destroy gbm surface & shutdown gbm device */ + evas_drm_gbm_shutdown(info); + /* shutdown tty */ + /* FIXME use ecore_drm_tty */ + evas_drm_shutdown(info); + _drm_device_shutdown(info); + free(re); + return 0; + } + + /* tell the engine to use this render_engine for output */ + epd->engine.data.output = re; + gl_wins++; + + if ((s = getenv("EVAS_GL_PARTIAL_MERGE"))) + { + 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 + { + re = epd->engine.data.output; + + if (eng_get_ob(re) && _re_wincheck(eng_get_ob(re))) + { + if ((eng_get_ob(re)->info->info.gbm != eng_get_ob(re)->gbm) || + (eng_get_ob(re)->info->info.surface != eng_get_ob(re)->surface) || + (eng_get_ob(re)->info->info.screen != eng_get_ob(re)->screen) || + (eng_get_ob(re)->info->info.depth != eng_get_ob(re)->depth) || + (eng_get_ob(re)->info->info.destination_alpha != eng_get_ob(re)->alpha)) + { + Outbuf *ob; + + eng_get_ob(re)->gl_context->references++; + gl_wins--; + + ob = eng_window_new(info, eo_e, + eng_get_ob(re)->info->info.gbm, + eng_get_ob(re)->info->info.surface, + eng_get_ob(re)->info->info.screen, + eng_get_ob(re)->info->info.depth, + epd->output.w, epd->output.h, + eng_get_ob(re)->info->indirect, + eng_get_ob(re)->info->info.destination_alpha, + eng_get_ob(re)->info->info.rotation, + swap_mode); + + eng_window_free(eng_get_ob(re)); + re->generic.software.ob = NULL; + + eng_window_use(ob); + if (ob) + { + evas_render_engine_software_generic_update(&re->generic.software, ob, + epd->output.w, epd->output.h); + + gl_wins++; + eng_get_ob(re)->gl_context->references--; + } + } + else if ((eng_get_ob(re)->w != epd->output.w) || + (eng_get_ob(re)->h != epd->output.h) || + (eng_get_ob(re)->info->info.rotation != eng_get_ob(re)->rot)) + { + Outbuf *ob; + + eng_get_ob(re)->gl_context->references++; + gl_wins--; + + eng_window_free(eng_get_ob(re)); + re->generic.software.ob = NULL; + evas_drm_gbm_shutdown(eng_get_ob(re)->info); + if (!evas_drm_gbm_init(info, epd->output.w, epd->output.h)) + return 0; + + DBG("FD: %d, GBM_DEVICE: 0x%x, GBM_SURFACE: 0x%x", + info->info.fd, (unsigned int)info->info.gbm, + (unsigned int)info->info.surface); + + ob = eng_window_new(info, eo_e, + info->info.gbm, + info->info.surface, + info->info.screen, + info->info.depth, + epd->output.w, epd->output.h, + info->indirect, + info->info.destination_alpha, + info->info.rotation, + swap_mode); + + eng_window_use(ob); + if (ob) + { + evas_render_engine_software_generic_update(&re->generic.software, ob, + epd->output.w, epd->output.h); + + gl_wins++; + eng_get_ob(re)->gl_context->references--; + } + } + } + } + if (!eng_get_ob(re)) + { + free(re); + return 0; + } + + if (!epd->engine.data.output) + { + if (eng_get_ob(re)) + { + eng_window_free(eng_get_ob(re)); + gl_wins--; + evas_drm_gbm_shutdown(info); + /* shutdown tty */ + /* FIXME use ecore_drm_tty */ + evas_drm_shutdown(info); + _drm_device_shutdown(info); + } + free(re); + return 0; + } + + 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); + } + eng_window_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); + + evas_drm_gbm_shutdown(eng_get_ob(re)->info); + /* shutdown tty */ + /* FIXME use ecore_drm_tty */ + evas_drm_shutdown(eng_get_ob(re)->info); + _drm_device_shutdown(eng_get_ob(re)->info); + + //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_image_shutdown(); + evas_common_font_shutdown(); + initted = EINA_FALSE; + } +} + +Eina_Bool +eng_preload_make_current(void *data, void *doit) +{ + Outbuf *ob = 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 Eina_Bool +eng_canvas_alpha_get(void *data, void *info EINA_UNUSED) +{ + Render_Engine *re = (Render_Engine *)data; + if (!re) return EINA_FALSE; + + return re->generic.software.ob->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); +} + +/* 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_gl_drm_log_dom < 0) + { + _evas_engine_gl_drm_log_dom = + eina_log_domain_register("evas-gl-drm", EVAS_DEFAULT_LOG_COLOR); + } + + /* if we could not create a logging domain, error out */ + if (_evas_engine_gl_drm_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 */ +#define ORD(f) EVAS_API_OVERRIDE(f, &func, eng_) + ORD(info); + ORD(info_free); + ORD(setup); + ORD(canvas_alpha_get); + ORD(output_free); + ORD(output_dump); + + /* Mesa's EGL driver loads wayland egl by default. (called by eglGetProcaddr() ) + * implicit env set (EGL_PLATFORM=drm) prevent that. + */ + setenv("EGL_PLATFORM", "drm", 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_gl_drm_log_dom); +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "gl_drm", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_ENGINE, engine, gl_drm); + +#ifndef EVAS_STATIC_BUILD_DRM +EVAS_EINA_MODULE_DEFINE(engine, gl_drm); +#endif diff --git a/src/modules/evas/engines/gl_drm/evas_engine.h b/src/modules/evas/engines/gl_drm/evas_engine.h new file mode 100644 index 0000000000..8f9aa5a01f --- /dev/null +++ b/src/modules/evas/engines/gl_drm/evas_engine.h @@ -0,0 +1,190 @@ +#ifndef EVAS_ENGINE_H +# define EVAS_ENGINE_H + +#include "config.h" +#include "evas_common_private.h" +#include "evas_private.h" +#include "Evas.h" +#include "Evas_Engine_GL_Drm.h" +#include "evas_macros.h" + +#define GL_GLEXT_PROTOTYPES +#include +#include +#include +#include "../gl_generic/Evas_Engine_GL_Generic.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +extern int extn_have_buffer_age; +extern int _evas_engine_gl_drm_log_dom; + +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; + +# ifdef ERR +# undef ERR +# endif +# define ERR(...) EINA_LOG_DOM_ERR(_evas_engine_gl_drm_log_dom, __VA_ARGS__) + +# ifdef DBG +# undef DBG +# endif +# define DBG(...) EINA_LOG_DOM_DBG(_evas_engine_gl_drm_log_dom, __VA_ARGS__) + +# ifdef INF +# undef INF +# endif +# define INF(...) EINA_LOG_DOM_INFO(_evas_engine_gl_drm_log_dom, __VA_ARGS__) + +# ifdef WRN +# undef WRN +# endif +# define WRN(...) EINA_LOG_DOM_WARN(_evas_engine_gl_drm_log_dom, __VA_ARGS__) + +# ifdef CRI +# undef CRI +# endif +# define CRI(...) EINA_LOG_DOM_CRIT(_evas_engine_gl_drm_log_dom, __VA_ARGS__) + +# define NUM_BUFFERS 2 + +typedef struct _Buffer Buffer; +typedef struct _Plane Plane; +typedef struct _Outbuf Outbuf; + +struct _Buffer +{ + int stride; + int size; + int handle; + unsigned int fb; + struct gbm_bo *bo; //used for hardware framebuffers + Eina_Bool valid : 1; +}; + +struct _Plane +{ + unsigned int id; + unsigned int crtcs; + + struct + { + unsigned int x, y; + unsigned int w, h; + } src, dst; + + unsigned int num_formats; + unsigned int formats[]; +}; + +struct _Outbuf +{ + EGLContext egl_context[1]; + EGLSurface egl_surface[1]; + EGLConfig egl_config; + EGLDisplay egl_disp; + struct gbm_device *gbm; + struct gbm_surface *surface; + Evas *evas; + uint32_t format; + uint32_t flags; + Evas_Engine_GL_Context *gl_context; + Evas_Engine_Info_GL_Drm *info; + Render_Engine_Swap_Mode swap_mode; + int w, h; + int depth, rot, screen, alpha; + int prev_age; + int frame_cnt; + int vsync; + Eina_Bool lost_back : 1; + Eina_Bool surf : 1; + + struct + { + Eina_Bool drew : 1; + } draw; + + struct + { + int fd; + unsigned int conn, crtc, fb; + Buffer buffer[NUM_BUFFERS]; + int curr, num; + drmModeModeInfo mode; + drmEventContext ctx; + Eina_List *pending_writes; + Eina_List *planes; + Eina_Bool pending_flip : 1; + } priv; +}; + +struct _Context_3D +{ + EGLDisplay display; + EGLContext context; + EGLSurface surface; +}; + +Outbuf *eng_window_new(Evas_Engine_Info_GL_Drm *info, Evas *e, struct gbm_device *gbm, struct gbm_surface *surface, int screen, int depth, int w, int h, int indirect, int alpha, int rot, Render_Engine_Swap_Mode swap_mode); +void eng_window_free(Outbuf *gw); +void eng_window_use(Outbuf *gw); +void eng_window_unsurf(Outbuf *gw); +void eng_window_resurf(Outbuf *gw); + +void eng_outbuf_reconfigure(Outbuf *ob, int w, int h, int rot, Outbuf_Depth depth); +int eng_outbuf_get_rot(Outbuf *ob); +Render_Engine_Swap_Mode eng_outbuf_swap_mode(Outbuf *ob); +Eina_Bool eng_outbuf_region_first_rect(Outbuf *ob); +void *eng_outbuf_new_region_for_update(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch); +void eng_outbuf_push_free_region_for_update(Outbuf *ob, RGBA_Image *update); +void eng_outbuf_push_updated_region(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h); +void eng_outbuf_flush(Outbuf *ob, Tilebuf_Rect *rects, Evas_Render_Mode render_mode); +Evas_Engine_GL_Context* eng_outbuf_gl_context_get(Outbuf *ob); +void *eng_outbuf_egl_display_get(Outbuf *ob); + +void eng_gl_context_free(Context_3D *context); +void eng_gl_context_use(Context_3D *context); + +Eina_Bool eng_preload_make_current(void *data, void *doit); + +Context_3D *eng_gl_context_new(Outbuf *win); + +static inline Eina_Bool +_re_wincheck(Outbuf *ob) +{ + if (ob->surf) return EINA_TRUE; + eng_window_resurf(ob); + ob->lost_back = 1; + if (!ob->surf) + { + ERR("GL engine can't re-create window surface!"); + } + return EINA_FALSE; +} + +//TODO: Need to split below evas_drm_... apis +Eina_Bool evas_drm_init(Evas_Engine_Info_GL_Drm *info); +Eina_Bool evas_drm_shutdown(Evas_Engine_Info_GL_Drm *info); +Eina_Bool evas_drm_gbm_init(Evas_Engine_Info_GL_Drm *info, int w, int h); +Eina_Bool evas_drm_gbm_shutdown(Evas_Engine_Info_GL_Drm *info); +Eina_Bool evas_drm_outbuf_setup(Outbuf *ob); +void evas_drm_outbuf_framebuffer_set(Outbuf *ob, Buffer *buffer); +Eina_Bool evas_drm_framebuffer_send(Outbuf *ob, Buffer *buffer); +#endif