2016-08-17 22:18:28 -04:00
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright ( C ) 2016 Red Hat Inc .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA
* 02111 - 1307 , USA .
*
* Written by :
* Jonas Å dahl < jadahl @ gmail . com >
*/
# include "config.h"
2016-08-31 23:04:22 -04:00
# include "backends/meta-backend-private.h"
2016-08-17 22:18:28 -04:00
# include "backends/meta-egl.h"
2016-08-17 23:27:45 -04:00
# include "backends/meta-egl-ext.h"
2016-10-28 07:15:02 -04:00
# include "meta/util.h"
2016-08-17 22:18:28 -04:00
# include <EGL/egl.h>
2016-10-28 07:15:02 -04:00
# include <EGL/eglext.h>
2016-08-17 22:18:28 -04:00
# include <gio/gio.h>
# include <glib.h>
# include <glib-object.h>
struct _MetaEgl
{
GObject parent ;
2016-10-28 07:15:02 -04:00
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT ;
2016-08-17 23:27:45 -04:00
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT ;
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT ;
PFNEGLGETOUTPUTLAYERSEXTPROC eglGetOutputLayersEXT ;
PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC eglQueryOutputLayerAttribEXT ;
PFNEGLCREATESTREAMKHRPROC eglCreateStreamKHR ;
PFNEGLDESTROYSTREAMKHRPROC eglDestroyStreamKHR ;
PFNEGLQUERYSTREAMKHRPROC eglQueryStreamKHR ;
PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC eglCreateStreamProducerSurfaceKHR ;
PFNEGLSTREAMCONSUMEROUTPUTEXTPROC eglStreamConsumerOutputEXT ;
PFNEGLSTREAMCONSUMERACQUIREATTRIBEXTPROC eglStreamConsumerAcquireAttribEXT ;
PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC eglStreamConsumerGLTextureExternalKHR ;
2016-08-17 22:18:28 -04:00
} ;
G_DEFINE_TYPE ( MetaEgl , meta_egl , G_TYPE_OBJECT )
static const char *
get_egl_error_str ( void )
{
EGLint error_number ;
error_number = eglGetError ( ) ;
switch ( error_number )
{
case EGL_SUCCESS :
return " The last function succeeded without error. " ;
break ;
case EGL_NOT_INITIALIZED :
return " EGL is not initialized, or could not be initialized, for the specified EGL display connection. " ;
break ;
case EGL_BAD_ACCESS :
return " EGL cannot access a requested resource (for example a context is bound in another thread). " ;
break ;
case EGL_BAD_ALLOC :
return " EGL failed to allocate resources for the requested operation. " ;
break ;
case EGL_BAD_ATTRIBUTE :
return " An unrecognized attribute or attribute value was passed in the attribute list. " ;
break ;
case EGL_BAD_CONTEXT :
return " An EGLContext argument does not name a valid EGL rendering context. " ;
break ;
case EGL_BAD_CONFIG :
return " An EGLConfig argument does not name a valid EGL frame buffer configuration. " ;
break ;
case EGL_BAD_CURRENT_SURFACE :
return " The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid. " ;
break ;
case EGL_BAD_DISPLAY :
return " An EGLDisplay argument does not name a valid EGL display connection. " ;
break ;
case EGL_BAD_SURFACE :
return " An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL rendering. " ;
break ;
case EGL_BAD_MATCH :
return " Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface). " ;
break ;
case EGL_BAD_PARAMETER :
return " One or more argument values are invalid. " ;
break ;
case EGL_BAD_NATIVE_PIXMAP :
return " A NativePixmapType argument does not refer to a valid native pixmap. " ;
break ;
case EGL_BAD_NATIVE_WINDOW :
return " A NativeWindowType argument does not refer to a valid native window. " ;
break ;
case EGL_CONTEXT_LOST :
return " A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering. " ;
break ;
default :
return " Unknown error " ;
break ;
}
}
static void
set_egl_error ( GError * * error )
{
const char * error_str ;
2016-10-20 01:38:33 -04:00
if ( ! error )
return ;
2016-08-17 22:18:28 -04:00
error_str = get_egl_error_str ( ) ;
g_set_error ( error , G_IO_ERROR ,
G_IO_ERROR_FAILED ,
error_str ) ;
}
2016-08-17 23:05:20 -04:00
static gboolean
extensions_string_has_extensions_valist ( const char * extensions_str ,
char * * * missing_extensions ,
char * first_extension ,
va_list var_args )
{
char * * extensions ;
char * extension ;
size_t num_missing_extensions = 0 ;
if ( missing_extensions )
* missing_extensions = NULL ;
extensions = g_strsplit ( extensions_str , " " , - 1 ) ;
extension = first_extension ;
while ( extension )
{
if ( ! g_strv_contains ( ( const char * const * ) extensions , extension ) )
{
num_missing_extensions + + ;
if ( missing_extensions )
{
* missing_extensions = g_realloc_n ( * missing_extensions ,
num_missing_extensions + 1 ,
sizeof ( const char * ) ) ;
( * missing_extensions ) [ num_missing_extensions - 1 ] = extension ;
( * missing_extensions ) [ num_missing_extensions ] = NULL ;
}
else
{
break ;
}
}
extension = va_arg ( var_args , char * ) ;
}
g_strfreev ( extensions ) ;
return num_missing_extensions = = 0 ;
}
gboolean
meta_egl_has_extensions ( MetaEgl * egl ,
EGLDisplay display ,
char * * * missing_extensions ,
char * first_extension ,
. . . )
{
va_list var_args ;
const char * extensions_str ;
gboolean has_extensions ;
extensions_str = ( const char * ) eglQueryString ( display , EGL_EXTENSIONS ) ;
if ( ! extensions_str )
{
g_warning ( " Failed to query string: %s " , get_egl_error_str ( ) ) ;
return FALSE ;
}
va_start ( var_args , first_extension ) ;
has_extensions = extensions_string_has_extensions_valist ( extensions_str ,
missing_extensions ,
first_extension ,
var_args ) ;
va_end ( var_args ) ;
return has_extensions ;
}
2016-09-29 10:26:24 -04:00
gboolean
meta_egl_initialize ( MetaEgl * egl ,
EGLDisplay display ,
GError * * error )
{
if ( ! eglInitialize ( display , NULL , NULL ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
2016-08-30 23:25:45 -04:00
gboolean
meta_egl_choose_config ( MetaEgl * egl ,
EGLDisplay display ,
const EGLint * attrib_list ,
EGLConfig * chosen_config ,
GError * * error )
{
EGLint num_configs ;
EGLConfig * configs ;
EGLint num_matches ;
if ( ! eglGetConfigs ( display , NULL , 0 , & num_configs ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
if ( num_configs < 1 )
{
g_set_error ( error , G_IO_ERROR ,
G_IO_ERROR_FAILED ,
" No EGL configurations available " ) ;
return FALSE ;
}
configs = g_new0 ( EGLConfig , num_configs ) ;
if ( ! eglChooseConfig ( display , attrib_list , configs , num_configs , & num_matches ) )
{
g_free ( configs ) ;
set_egl_error ( error ) ;
return FALSE ;
}
/*
* We don ' t have any preference specified yet , so lets choose the first one .
*/
* chosen_config = configs [ 0 ] ;
g_free ( configs ) ;
return TRUE ;
}
EGLSurface
meta_egl_create_pbuffer_surface ( MetaEgl * egl ,
EGLDisplay display ,
EGLConfig config ,
const EGLint * attrib_list ,
GError * * error )
{
EGLSurface surface ;
surface = eglCreatePbufferSurface ( display , config , attrib_list ) ;
if ( surface = = EGL_NO_SURFACE )
{
set_egl_error ( error ) ;
return EGL_NO_SURFACE ;
}
return surface ;
}
2016-10-28 07:15:02 -04:00
static gboolean
is_egl_proc_valid_real ( void * proc ,
const char * proc_name ,
GError * * error )
{
if ( ! proc )
{
g_set_error ( error , G_IO_ERROR ,
G_IO_ERROR_FAILED ,
" EGL proc '%s' not resolved " ,
proc_name ) ;
return FALSE ;
}
return TRUE ;
}
# define is_egl_proc_valid(proc, error) \
is_egl_proc_valid_real ( proc , # proc , error )
EGLDisplay
meta_egl_get_platform_display ( MetaEgl * egl ,
EGLenum platform ,
void * native_display ,
const EGLint * attrib_list ,
GError * * error )
{
EGLDisplay display ;
if ( ! is_egl_proc_valid ( egl - > eglGetPlatformDisplayEXT , error ) )
return EGL_NO_DISPLAY ;
display = egl - > eglGetPlatformDisplayEXT ( platform ,
native_display ,
attrib_list ) ;
if ( display = = EGL_NO_DISPLAY )
{
set_egl_error ( error ) ;
return EGL_NO_DISPLAY ;
}
return display ;
}
2016-08-17 23:27:45 -04:00
gboolean
meta_egl_query_devices ( MetaEgl * egl ,
EGLint max_devices ,
EGLDeviceEXT * devices ,
EGLint * num_devices ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglQueryDevicesEXT , error ) )
return FALSE ;
if ( ! egl - > eglQueryDevicesEXT ( max_devices ,
devices ,
num_devices ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
const char *
meta_egl_query_device_string ( MetaEgl * egl ,
EGLDeviceEXT device ,
EGLint name ,
GError * * error )
{
const char * device_string ;
if ( ! is_egl_proc_valid ( egl - > eglQueryDeviceStringEXT , error ) )
return NULL ;
device_string = egl - > eglQueryDeviceStringEXT ( device , name ) ;
if ( ! device_string )
{
set_egl_error ( error ) ;
return NULL ;
}
return device_string ;
}
gboolean
meta_egl_egl_device_has_extensions ( MetaEgl * egl ,
EGLDeviceEXT device ,
char * * * missing_extensions ,
char * first_extension ,
. . . )
{
va_list var_args ;
const char * extensions_str ;
gboolean has_extensions ;
GError * error = NULL ;
extensions_str = meta_egl_query_device_string ( egl , device , EGL_EXTENSIONS ,
& error ) ;
if ( ! extensions_str )
{
g_warning ( " Failed to query device string: %s " , error - > message ) ;
g_error_free ( error ) ;
return FALSE ;
}
va_start ( var_args , first_extension ) ;
has_extensions = extensions_string_has_extensions_valist ( extensions_str ,
missing_extensions ,
first_extension ,
var_args ) ;
va_end ( var_args ) ;
return has_extensions ;
}
gboolean
meta_egl_get_output_layers ( MetaEgl * egl ,
EGLDisplay display ,
const EGLAttrib * attrib_list ,
EGLOutputLayerEXT * layers ,
EGLint max_layers ,
EGLint * num_layers ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglGetOutputLayersEXT , error ) )
return FALSE ;
if ( ! egl - > eglGetOutputLayersEXT ( display ,
attrib_list ,
layers ,
max_layers ,
num_layers ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
gboolean
meta_egl_query_output_layer_attrib ( MetaEgl * egl ,
EGLDisplay display ,
EGLOutputLayerEXT layer ,
EGLint attribute ,
EGLAttrib * value ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglQueryOutputLayerAttribEXT , error ) )
return FALSE ;
if ( ! egl - > eglQueryOutputLayerAttribEXT ( display , layer ,
attribute , value ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
EGLStreamKHR
meta_egl_create_stream ( MetaEgl * egl ,
EGLDisplay display ,
const EGLint * attrib_list ,
GError * * error )
{
EGLStreamKHR stream ;
if ( ! is_egl_proc_valid ( egl - > eglCreateStreamKHR , error ) )
return EGL_NO_STREAM_KHR ;
stream = egl - > eglCreateStreamKHR ( display , attrib_list ) ;
if ( stream = = EGL_NO_STREAM_KHR )
{
set_egl_error ( error ) ;
return EGL_NO_STREAM_KHR ;
}
return stream ;
}
gboolean
meta_egl_destroy_stream ( MetaEgl * egl ,
EGLDisplay display ,
EGLStreamKHR stream ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglDestroyStreamKHR , error ) )
return FALSE ;
if ( ! egl - > eglDestroyStreamKHR ( display , stream ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
gboolean
meta_egl_query_stream ( MetaEgl * egl ,
EGLDisplay display ,
EGLStreamKHR stream ,
EGLenum attribute ,
EGLint * value ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglQueryStreamKHR , error ) )
return FALSE ;
if ( ! egl - > eglQueryStreamKHR ( display , stream , attribute , value ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
EGLSurface
meta_egl_create_stream_producer_surface ( MetaEgl * egl ,
EGLDisplay display ,
EGLConfig config ,
EGLStreamKHR stream ,
const EGLint * attrib_list ,
GError * * error )
{
EGLSurface surface ;
if ( ! is_egl_proc_valid ( egl - > eglCreateStreamProducerSurfaceKHR , error ) )
return EGL_NO_SURFACE ;
surface = egl - > eglCreateStreamProducerSurfaceKHR ( display ,
config ,
stream ,
attrib_list ) ;
if ( surface = = EGL_NO_SURFACE )
{
set_egl_error ( error ) ;
return EGL_NO_SURFACE ;
}
return surface ;
}
gboolean
meta_egl_stream_consumer_output ( MetaEgl * egl ,
EGLDisplay display ,
EGLStreamKHR stream ,
EGLOutputLayerEXT layer ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglStreamConsumerOutputEXT , error ) )
return FALSE ;
if ( ! egl - > eglStreamConsumerOutputEXT ( display , stream , layer ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
gboolean
meta_egl_stream_consumer_acquire_attrib ( MetaEgl * egl ,
EGLDisplay display ,
EGLStreamKHR stream ,
EGLAttrib * attrib_list ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglStreamConsumerAcquireAttribEXT , error ) )
return FALSE ;
if ( ! egl - > eglStreamConsumerAcquireAttribEXT ( display , stream , attrib_list ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
gboolean
meta_egl_stream_consumer_gl_texture_external ( MetaEgl * egl ,
EGLDisplay display ,
EGLStreamKHR stream ,
GError * * error )
{
if ( ! is_egl_proc_valid ( egl - > eglStreamConsumerGLTextureExternalKHR , error ) )
return FALSE ;
if ( ! egl - > eglStreamConsumerGLTextureExternalKHR ( display , stream ) )
{
set_egl_error ( error ) ;
return FALSE ;
}
return TRUE ;
}
2016-08-17 23:05:20 -04:00
# define GET_EGL_PROC_ADDR(proc) \
egl - > proc = ( void * ) eglGetProcAddress ( # proc ) ;
# define GET_EGL_PROC_ADDR_REQUIRED(proc) \
GET_EGL_PROC_ADDR ( proc ) \
if ( ! egl - > proc ) \
{ \
meta_fatal ( " Failed to get proc address for '%s' \n " , # proc ) ; \
}
static void
meta_egl_constructed ( GObject * object )
{
2016-10-28 07:15:02 -04:00
MetaEgl * egl = META_EGL ( object ) ;
GET_EGL_PROC_ADDR_REQUIRED ( eglGetPlatformDisplayEXT ) ;
2016-08-17 23:27:45 -04:00
GET_EGL_PROC_ADDR ( eglQueryDevicesEXT ) ;
GET_EGL_PROC_ADDR ( eglQueryDeviceStringEXT ) ;
GET_EGL_PROC_ADDR ( eglGetOutputLayersEXT ) ;
GET_EGL_PROC_ADDR ( eglQueryOutputLayerAttribEXT ) ;
GET_EGL_PROC_ADDR ( eglCreateStreamKHR ) ;
GET_EGL_PROC_ADDR ( eglDestroyStreamKHR ) ;
GET_EGL_PROC_ADDR ( eglQueryStreamKHR ) ;
GET_EGL_PROC_ADDR ( eglCreateStreamProducerSurfaceKHR ) ;
GET_EGL_PROC_ADDR ( eglStreamConsumerOutputEXT ) ;
GET_EGL_PROC_ADDR ( eglStreamConsumerAcquireAttribEXT ) ;
GET_EGL_PROC_ADDR ( eglStreamConsumerGLTextureExternalKHR ) ;
2016-08-17 23:05:20 -04:00
}
# undef GET_EGL_PROC_ADDR
2016-08-17 22:18:28 -04:00
static void
meta_egl_init ( MetaEgl * egl )
{
}
static void
meta_egl_class_init ( MetaEglClass * klass )
{
2016-08-17 23:05:20 -04:00
GObjectClass * object_class = G_OBJECT_CLASS ( klass ) ;
object_class - > constructed = meta_egl_constructed ;
2016-08-17 22:18:28 -04:00
}