8234f5bc7e
QueryDeviceString can successfully return NULL. The convention however is that when NULL is returned, the error will be set. This commit makes the returned string an output parameter which allows us to return the success status and have the error set accordingly. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3614>
1213 lines
34 KiB
C
1213 lines
34 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2016, 2017 Red Hat Inc.
|
|
* Copyright (C) 2018, 2019 DisplayLink (UK) Ltd.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Written by:
|
|
* Jonas Ådahl <jadahl@gmail.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <EGL/egl.h>
|
|
#include <EGL/eglext.h>
|
|
#include <EGL/eglmesaext.h>
|
|
#include <gio/gio.h>
|
|
#include <glib.h>
|
|
#include <glib-object.h>
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
#include "backends/meta-egl.h"
|
|
#include "backends/meta-egl-ext.h"
|
|
#include "meta/util.h"
|
|
|
|
struct _MetaEgl
|
|
{
|
|
GObject parent;
|
|
|
|
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
|
|
|
|
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
|
|
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
|
|
|
|
PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplayWL;
|
|
PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBufferWL;
|
|
|
|
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
|
|
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
|
|
|
|
PFNEGLGETOUTPUTLAYERSEXTPROC eglGetOutputLayersEXT;
|
|
PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC eglQueryOutputLayerAttribEXT;
|
|
|
|
PFNEGLCREATESTREAMKHRPROC eglCreateStreamKHR;
|
|
PFNEGLDESTROYSTREAMKHRPROC eglDestroyStreamKHR;
|
|
PFNEGLQUERYSTREAMKHRPROC eglQueryStreamKHR;
|
|
|
|
PFNEGLCREATESTREAMATTRIBNVPROC eglCreateStreamAttribNV;
|
|
|
|
PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC eglCreateStreamProducerSurfaceKHR;
|
|
|
|
PFNEGLSTREAMCONSUMEROUTPUTEXTPROC eglStreamConsumerOutputEXT;
|
|
|
|
PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC eglStreamConsumerGLTextureExternalKHR;
|
|
|
|
PFNEGLSTREAMCONSUMERACQUIREKHRPROC eglStreamConsumerAcquireKHR;
|
|
PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC eglStreamConsumerAcquireAttribNV;
|
|
|
|
PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT;
|
|
PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT;
|
|
|
|
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaEgl, meta_egl, G_TYPE_OBJECT)
|
|
|
|
G_DEFINE_QUARK (-meta-egl-error-quark, meta_egl_error)
|
|
|
|
static const char *
|
|
get_egl_error_str (EGLint error_number)
|
|
{
|
|
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;
|
|
case EGL_BAD_STREAM_KHR:
|
|
return "An EGLStreamKHR argument does not name a valid EGL stream.";
|
|
break;
|
|
case EGL_BAD_STATE_KHR:
|
|
return "An EGLStreamKHR argument is not in a valid state";
|
|
break;
|
|
case EGL_BAD_DEVICE_EXT:
|
|
return "An EGLDeviceEXT argument does not name a valid EGL device.";
|
|
break;
|
|
case EGL_BAD_OUTPUT_LAYER_EXT:
|
|
return "An EGLOutputLayerEXT argument does not name a valid EGL output layer.";
|
|
case EGL_RESOURCE_BUSY_EXT:
|
|
return "The operation could not be completed on the requested resource because it is temporary unavailable.";
|
|
default:
|
|
return "Unknown error";
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_egl_error (GError **error)
|
|
{
|
|
EGLint error_number;
|
|
const char *error_str;
|
|
|
|
if (!error)
|
|
return;
|
|
|
|
error_number = eglGetError ();
|
|
if (error_number == EGL_SUCCESS)
|
|
{
|
|
g_warning ("Expected an EGL error but eglGetError returned EGL_SUCCESS");
|
|
error_number = -1;
|
|
}
|
|
|
|
error_str = get_egl_error_str (error_number);
|
|
g_set_error_literal (error, META_EGL_ERROR,
|
|
error_number,
|
|
error_str);
|
|
}
|
|
|
|
static void
|
|
check_egl_error (GError **error)
|
|
{
|
|
EGLint error_number;
|
|
const char *error_str;
|
|
|
|
if (!error)
|
|
return;
|
|
|
|
error_number = eglGetError ();
|
|
if (error_number == EGL_SUCCESS)
|
|
return;
|
|
|
|
error_str = get_egl_error_str (error_number);
|
|
g_set_error_literal (error, META_EGL_ERROR,
|
|
error_number,
|
|
error_str);
|
|
}
|
|
|
|
gboolean
|
|
meta_extensions_string_has_extensions_valist (const char *extensions_str,
|
|
const char ***missing_extensions,
|
|
const char *first_extension,
|
|
va_list var_args)
|
|
{
|
|
char **extensions;
|
|
const 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,
|
|
const char ***missing_extensions,
|
|
const 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 (eglGetError ()));
|
|
return FALSE;
|
|
}
|
|
|
|
va_start (var_args, first_extension);
|
|
has_extensions =
|
|
meta_extensions_string_has_extensions_valist (extensions_str,
|
|
missing_extensions,
|
|
first_extension,
|
|
var_args);
|
|
va_end (var_args);
|
|
|
|
return has_extensions;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_initialize (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
GError **error)
|
|
{
|
|
if (!eglInitialize (display, NULL, NULL))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_bind_api (MetaEgl *egl,
|
|
EGLenum api,
|
|
GError **error)
|
|
{
|
|
if (!eglBindAPI (api))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gpointer
|
|
meta_egl_get_proc_address (MetaEgl *egl,
|
|
const char *procname,
|
|
GError **error)
|
|
{
|
|
gpointer func;
|
|
|
|
func = (gpointer) eglGetProcAddress (procname);
|
|
if (!func)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Could not load symbol '%s': Not found",
|
|
procname);
|
|
return NULL;
|
|
}
|
|
|
|
return func;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_get_config_attrib (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLConfig config,
|
|
EGLint attribute,
|
|
EGLint *value,
|
|
GError **error)
|
|
{
|
|
if (!eglGetConfigAttrib (display,
|
|
config,
|
|
attribute,
|
|
value))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
EGLConfig *
|
|
meta_egl_choose_all_configs (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
const EGLint *attrib_list,
|
|
EGLint *out_num_configs,
|
|
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;
|
|
}
|
|
|
|
if (num_matches == 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"No matching EGL configs");
|
|
g_free (configs);
|
|
return NULL;
|
|
}
|
|
|
|
*out_num_configs = num_matches;
|
|
return configs;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_choose_first_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;
|
|
}
|
|
|
|
if (num_matches == 0)
|
|
{
|
|
g_free (configs);
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"No matching EGLConfig found");
|
|
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_window_surface (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLConfig config,
|
|
EGLNativeWindowType native_window_type,
|
|
const EGLint *attrib_list,
|
|
GError **error)
|
|
{
|
|
EGLSurface surface;
|
|
|
|
surface = eglCreateWindowSurface (display, config,
|
|
native_window_type, attrib_list);
|
|
if (surface == EGL_NO_SURFACE)
|
|
{
|
|
set_egl_error (error);
|
|
return EGL_NO_SURFACE;
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_destroy_surface (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLSurface surface,
|
|
GError **error)
|
|
{
|
|
if (!eglDestroySurface (display, surface))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_terminate (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
GError **error)
|
|
{
|
|
if (!eglTerminate (display))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
EGLContext
|
|
meta_egl_create_context (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLConfig config,
|
|
EGLContext share_context,
|
|
const EGLint *attrib_list,
|
|
GError **error)
|
|
{
|
|
EGLContext context;
|
|
|
|
context = eglCreateContext (display, config, share_context, attrib_list);
|
|
if (context == EGL_NO_CONTEXT)
|
|
{
|
|
set_egl_error (error);
|
|
return EGL_NO_CONTEXT;
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_destroy_context (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLContext context,
|
|
GError **error)
|
|
{
|
|
if (!eglDestroyContext (display, context))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
EGLImageKHR
|
|
meta_egl_create_image (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLContext context,
|
|
EGLenum target,
|
|
EGLClientBuffer buffer,
|
|
const EGLint *attrib_list,
|
|
GError **error)
|
|
{
|
|
EGLImageKHR image;
|
|
|
|
if (!is_egl_proc_valid (egl->eglCreateImageKHR, error))
|
|
return EGL_NO_IMAGE_KHR;
|
|
|
|
image = egl->eglCreateImageKHR (display, context,
|
|
target, buffer, attrib_list);
|
|
if (image == EGL_NO_IMAGE_KHR)
|
|
{
|
|
set_egl_error (error);
|
|
return EGL_NO_IMAGE_KHR;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_destroy_image (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLImageKHR image,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglDestroyImageKHR, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglDestroyImageKHR (display, image))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
EGLImageKHR
|
|
meta_egl_create_dmabuf_image (MetaEgl *egl,
|
|
EGLDisplay egl_display,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
uint32_t drm_format,
|
|
uint32_t n_planes,
|
|
const int *fds,
|
|
const uint32_t *strides,
|
|
const uint32_t *offsets,
|
|
const uint64_t *modifiers,
|
|
GError **error)
|
|
{
|
|
EGLint attribs[39];
|
|
int atti = 0;
|
|
|
|
/* This requires the Mesa commit in
|
|
* Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or
|
|
* Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652).
|
|
* Otherwise Mesa closes the fd behind our back and re-importing
|
|
* will fail.
|
|
* https://bugs.freedesktop.org/show_bug.cgi?id=76188
|
|
*/
|
|
|
|
attribs[atti++] = EGL_WIDTH;
|
|
attribs[atti++] = width;
|
|
attribs[atti++] = EGL_HEIGHT;
|
|
attribs[atti++] = height;
|
|
attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
|
|
attribs[atti++] = drm_format;
|
|
attribs[atti++] = EGL_IMAGE_PRESERVED_KHR;
|
|
attribs[atti++] = EGL_TRUE;
|
|
|
|
if (n_planes > 0)
|
|
{
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
|
|
attribs[atti++] = fds[0];
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
|
|
attribs[atti++] = offsets[0];
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
|
|
attribs[atti++] = strides[0];
|
|
if (modifiers)
|
|
{
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
|
|
attribs[atti++] = modifiers[0] & 0xFFFFFFFF;
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
|
|
attribs[atti++] = modifiers[0] >> 32;
|
|
}
|
|
}
|
|
|
|
if (n_planes > 1)
|
|
{
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
|
|
attribs[atti++] = fds[1];
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
|
|
attribs[atti++] = offsets[1];
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
|
|
attribs[atti++] = strides[1];
|
|
if (modifiers)
|
|
{
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
|
|
attribs[atti++] = modifiers[1] & 0xFFFFFFFF;
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
|
|
attribs[atti++] = modifiers[1] >> 32;
|
|
}
|
|
}
|
|
|
|
if (n_planes > 2)
|
|
{
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
|
|
attribs[atti++] = fds[2];
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
|
|
attribs[atti++] = offsets[2];
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
|
|
attribs[atti++] = strides[2];
|
|
if (modifiers)
|
|
{
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
|
|
attribs[atti++] = modifiers[2] & 0xFFFFFFFF;
|
|
attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
|
|
attribs[atti++] = modifiers[2] >> 32;
|
|
}
|
|
}
|
|
|
|
attribs[atti++] = EGL_NONE;
|
|
g_assert (atti <= G_N_ELEMENTS (attribs));
|
|
|
|
return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT, NULL,
|
|
attribs,
|
|
error);
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_make_current (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLSurface draw,
|
|
EGLSurface read,
|
|
EGLContext context,
|
|
GError **error)
|
|
{
|
|
if (!eglMakeCurrent (display, draw, read, context))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_swap_buffers (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLSurface surface,
|
|
GError **error)
|
|
{
|
|
if (!eglSwapBuffers (display, surface))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_bind_wayland_display (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
struct wl_display *wayland_display,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglBindWaylandDisplayWL, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglBindWaylandDisplayWL (display, wayland_display))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_query_wayland_buffer (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
struct wl_resource *buffer,
|
|
EGLint attribute,
|
|
EGLint *value,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglQueryWaylandBufferWL, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglQueryWaylandBufferWL (display, buffer, attribute, value))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_query_device_string (MetaEgl *egl,
|
|
EGLDeviceEXT device,
|
|
EGLint name,
|
|
const char **out_string,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GError) local_error = NULL;
|
|
const char *device_string;
|
|
|
|
if (!is_egl_proc_valid (egl->eglQueryDeviceStringEXT, error))
|
|
return FALSE;
|
|
|
|
device_string = egl->eglQueryDeviceStringEXT (device, name);
|
|
if (!device_string)
|
|
{
|
|
check_egl_error (&local_error);
|
|
if (local_error)
|
|
{
|
|
g_propagate_error (error, local_error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*out_string = device_string;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_egl_device_has_extensions (MetaEgl *egl,
|
|
EGLDeviceEXT device,
|
|
const char ***missing_extensions,
|
|
const char *first_extension,
|
|
...)
|
|
{
|
|
va_list var_args;
|
|
const char *extensions_str;
|
|
gboolean has_extensions;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!meta_egl_query_device_string (egl, device, EGL_EXTENSIONS,
|
|
&extensions_str, &error))
|
|
{
|
|
g_warning ("Failed to query device string: %s", error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!extensions_str)
|
|
{
|
|
g_warning ("EGL_EXTENSIONS device string returned NULL");
|
|
return FALSE;
|
|
}
|
|
|
|
va_start (var_args, first_extension);
|
|
has_extensions =
|
|
meta_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;
|
|
}
|
|
|
|
EGLStreamKHR
|
|
meta_egl_create_stream_attrib (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
const EGLAttrib *attrib_list,
|
|
GError **error)
|
|
{
|
|
EGLStreamKHR stream;
|
|
|
|
if (!is_egl_proc_valid (egl->eglCreateStreamAttribNV, error))
|
|
return FALSE;
|
|
|
|
stream = egl->eglCreateStreamAttribNV (display, attrib_list);
|
|
if (stream == EGL_NO_STREAM_KHR)
|
|
{
|
|
set_egl_error (error);
|
|
return EGL_NO_STREAM_KHR;
|
|
}
|
|
|
|
return stream;
|
|
}
|
|
|
|
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->eglStreamConsumerAcquireAttribNV, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglStreamConsumerAcquireAttribNV (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;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_stream_consumer_acquire (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLStreamKHR stream,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglStreamConsumerAcquireKHR, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglStreamConsumerAcquireKHR (display, stream))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_query_dma_buf_formats (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLint max_formats,
|
|
EGLint *formats,
|
|
EGLint *num_formats,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglQueryDmaBufFormatsEXT, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglQueryDmaBufFormatsEXT (display, max_formats, formats,
|
|
num_formats))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_query_dma_buf_modifiers (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLint format,
|
|
EGLint max_modifiers,
|
|
EGLuint64KHR *modifiers,
|
|
EGLBoolean *external_only,
|
|
EGLint *num_modifiers,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglQueryDmaBufModifiersEXT, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglQueryDmaBufModifiersEXT (display, format, max_modifiers,
|
|
modifiers, external_only,
|
|
num_modifiers))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_egl_query_display_attrib (MetaEgl *egl,
|
|
EGLDisplay display,
|
|
EGLint attribute,
|
|
EGLAttrib *value,
|
|
GError **error)
|
|
{
|
|
if (!is_egl_proc_valid (egl->eglQueryDisplayAttribEXT, error))
|
|
return FALSE;
|
|
|
|
if (!egl->eglQueryDisplayAttribEXT (display, attribute, value))
|
|
{
|
|
set_egl_error (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define GET_EGL_PROC_ADDR(proc) \
|
|
egl->proc = (void *) eglGetProcAddress (#proc);
|
|
|
|
static void
|
|
meta_egl_constructed (GObject *object)
|
|
{
|
|
MetaEgl *egl = META_EGL (object);
|
|
|
|
GET_EGL_PROC_ADDR (eglGetPlatformDisplayEXT);
|
|
|
|
GET_EGL_PROC_ADDR (eglCreateImageKHR);
|
|
GET_EGL_PROC_ADDR (eglDestroyImageKHR);
|
|
|
|
GET_EGL_PROC_ADDR (eglBindWaylandDisplayWL);
|
|
GET_EGL_PROC_ADDR (eglQueryWaylandBufferWL);
|
|
|
|
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 (eglCreateStreamAttribNV);
|
|
|
|
GET_EGL_PROC_ADDR (eglCreateStreamProducerSurfaceKHR);
|
|
|
|
GET_EGL_PROC_ADDR (eglStreamConsumerOutputEXT);
|
|
|
|
GET_EGL_PROC_ADDR (eglStreamConsumerGLTextureExternalKHR);
|
|
|
|
GET_EGL_PROC_ADDR (eglStreamConsumerAcquireKHR);
|
|
GET_EGL_PROC_ADDR (eglStreamConsumerAcquireAttribNV);
|
|
|
|
GET_EGL_PROC_ADDR (eglQueryDmaBufFormatsEXT);
|
|
GET_EGL_PROC_ADDR (eglQueryDmaBufModifiersEXT);
|
|
|
|
GET_EGL_PROC_ADDR (eglQueryDisplayAttribEXT);
|
|
}
|
|
|
|
#undef GET_EGL_PROC_ADDR
|
|
|
|
static void
|
|
meta_egl_init (MetaEgl *egl)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_egl_class_init (MetaEglClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->constructed = meta_egl_constructed;
|
|
}
|