X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fegl%2Fdrivers%2Fdri2%2Fegl_dri2.c;h=e854903a6f1b3a7c928f68f28455c2f7ce1d70d6;hb=982ac69e3f838904f4e218a5ccab1eff9a25c387;hp=4cc5f2313333b0c416755af8392c99ff16420283;hpb=fc2a66cfcddea34af0e93dd2221ae1fd3fdd9e87;p=mesa.git diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index 4cc5f231333..81df0d909ee 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -25,8 +25,6 @@ * Kristian Høgsberg */ -#define WL_HIDE_DEPRECATED - #include #include #include @@ -38,9 +36,11 @@ #include #include #include +#include +#include #ifdef HAVE_LIBDRM #include -#include +#include "drm-uapi/drm_fourcc.h" #endif #include #include @@ -48,93 +48,288 @@ #include #ifdef HAVE_WAYLAND_PLATFORM +#include #include "wayland-drm.h" #include "wayland-drm-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" +#endif + +#ifdef HAVE_X11_PLATFORM +#include "X11/Xlibint.h" #endif +#include "egldefines.h" #include "egl_dri2.h" +#include "GL/mesa_glinterop.h" +#include "loader/loader.h" +#include "util/os_file.h" #include "util/u_atomic.h" +#include "util/u_vector.h" +#include "mapi/glapi/glapi.h" +#include "util/bitscan.h" +#include "util/u_math.h" -/* The kernel header drm_fourcc.h defines the DRM formats below. We duplicate - * some of the definitions here so that building Mesa won't bleeding-edge - * kernel headers. +/* Additional definitions not yet in the drm_fourcc.h. */ -#ifndef DRM_FORMAT_R8 -#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + +#ifndef DRM_FORMAT_P012 +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cb:Cr plane 12 bits per channel */ #endif -#ifndef DRM_FORMAT_RG88 -#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ +#ifndef DRM_FORMAT_P016 +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cb:Cr plane 16 bits per channel */ +#endif + +#define NUM_ATTRIBS 12 + +static const struct dri2_pbuffer_visual { + const char *format_name; + unsigned int dri_image_format; + int rgba_shifts[4]; + unsigned int rgba_sizes[4]; +} dri2_pbuffer_visuals[] = { + { + "ABGR16F", + __DRI_IMAGE_FORMAT_ABGR16161616F, + { 0, 16, 32, 48 }, + { 16, 16, 16, 16 } + }, + { + "XBGR16F", + __DRI_IMAGE_FORMAT_XBGR16161616F, + { 0, 16, 32, -1 }, + { 16, 16, 16, 0 } + }, + { + "A2RGB10", + __DRI_IMAGE_FORMAT_ARGB2101010, + { 20, 10, 0, 30 }, + { 10, 10, 10, 2 } + }, + { + "X2RGB10", + __DRI_IMAGE_FORMAT_XRGB2101010, + { 20, 10, 0, -1 }, + { 10, 10, 10, 0 } + }, + { + "ARGB8888", + __DRI_IMAGE_FORMAT_ARGB8888, + { 16, 8, 0, 24 }, + { 8, 8, 8, 8 } + }, + { + "RGB888", + __DRI_IMAGE_FORMAT_XRGB8888, + { 16, 8, 0, -1 }, + { 8, 8, 8, 0 } + }, + { + "RGB565", + __DRI_IMAGE_FORMAT_RGB565, + { 11, 5, 0, -1 }, + { 5, 6, 5, 0 } + }, +}; + +static void +dri_set_background_context(void *loaderPrivate) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + _EGLThreadInfo *t = _eglGetCurrentThread(); + + _eglBindContextToThread(ctx, t); +} + +static void +dri2_gl_flush() +{ + static void (*glFlush)(void); + static mtx_t glFlushMutex = _MTX_INITIALIZER_NP; + + mtx_lock(&glFlushMutex); + if (!glFlush) + glFlush = _glapi_get_proc_address("glFlush"); + mtx_unlock(&glFlushMutex); + + /* if glFlush is not available things are horribly broken */ + if (!glFlush) { + _eglLog(_EGL_WARNING, "DRI2: failed to find glFlush entry point"); + return; + } + + glFlush(); +} + +static GLboolean +dri_is_thread_safe(void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + UNUSED _EGLDisplay *display = dri2_surf->base.Resource.Display; + +#ifdef HAVE_X11_PLATFORM + Display *xdpy = (Display*)display->PlatformDisplay; + + /* Check Xlib is running in thread safe mode when running on EGL/X11-xlib + * platform + * + * 'lock_fns' is the XLockDisplay function pointer of the X11 display 'dpy'. + * It wll be NULL if XInitThreads wasn't called. + */ + if (display->Platform == _EGL_PLATFORM_X11 && xdpy && !xdpy->lock_fns) + return false; #endif -#ifndef DRM_FORMAT_GR88 -#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ +#ifdef HAVE_WAYLAND_PLATFORM + if (display->Platform == _EGL_PLATFORM_WAYLAND) + return true; #endif + return true; +} + +const __DRIbackgroundCallableExtension background_callable_extension = { + .base = { __DRI_BACKGROUND_CALLABLE, 2 }, + + .setBackgroundContext = dri_set_background_context, + .isThreadSafe = dri_is_thread_safe, +}; + const __DRIuseInvalidateExtension use_invalidate = { .base = { __DRI_USE_INVALIDATE, 1 } }; -EGLint dri2_to_egl_attribute_map[] = { - 0, - EGL_BUFFER_SIZE, /* __DRI_ATTRIB_BUFFER_SIZE */ - EGL_LEVEL, /* __DRI_ATTRIB_LEVEL */ - EGL_RED_SIZE, /* __DRI_ATTRIB_RED_SIZE */ - EGL_GREEN_SIZE, /* __DRI_ATTRIB_GREEN_SIZE */ - EGL_BLUE_SIZE, /* __DRI_ATTRIB_BLUE_SIZE */ - EGL_LUMINANCE_SIZE, /* __DRI_ATTRIB_LUMINANCE_SIZE */ - EGL_ALPHA_SIZE, /* __DRI_ATTRIB_ALPHA_SIZE */ - 0, /* __DRI_ATTRIB_ALPHA_MASK_SIZE */ - EGL_DEPTH_SIZE, /* __DRI_ATTRIB_DEPTH_SIZE */ - EGL_STENCIL_SIZE, /* __DRI_ATTRIB_STENCIL_SIZE */ - 0, /* __DRI_ATTRIB_ACCUM_RED_SIZE */ - 0, /* __DRI_ATTRIB_ACCUM_GREEN_SIZE */ - 0, /* __DRI_ATTRIB_ACCUM_BLUE_SIZE */ - 0, /* __DRI_ATTRIB_ACCUM_ALPHA_SIZE */ - EGL_SAMPLE_BUFFERS, /* __DRI_ATTRIB_SAMPLE_BUFFERS */ - EGL_SAMPLES, /* __DRI_ATTRIB_SAMPLES */ - 0, /* __DRI_ATTRIB_RENDER_TYPE, */ - 0, /* __DRI_ATTRIB_CONFIG_CAVEAT */ - 0, /* __DRI_ATTRIB_CONFORMANT */ - 0, /* __DRI_ATTRIB_DOUBLE_BUFFER */ - 0, /* __DRI_ATTRIB_STEREO */ - 0, /* __DRI_ATTRIB_AUX_BUFFERS */ - 0, /* __DRI_ATTRIB_TRANSPARENT_TYPE */ - 0, /* __DRI_ATTRIB_TRANSPARENT_INDEX_VALUE */ - 0, /* __DRI_ATTRIB_TRANSPARENT_RED_VALUE */ - 0, /* __DRI_ATTRIB_TRANSPARENT_GREEN_VALUE */ - 0, /* __DRI_ATTRIB_TRANSPARENT_BLUE_VALUE */ - 0, /* __DRI_ATTRIB_TRANSPARENT_ALPHA_VALUE */ - 0, /* __DRI_ATTRIB_FLOAT_MODE (deprecated) */ - 0, /* __DRI_ATTRIB_RED_MASK */ - 0, /* __DRI_ATTRIB_GREEN_MASK */ - 0, /* __DRI_ATTRIB_BLUE_MASK */ - 0, /* __DRI_ATTRIB_ALPHA_MASK */ - EGL_MAX_PBUFFER_WIDTH, /* __DRI_ATTRIB_MAX_PBUFFER_WIDTH */ - EGL_MAX_PBUFFER_HEIGHT, /* __DRI_ATTRIB_MAX_PBUFFER_HEIGHT */ - EGL_MAX_PBUFFER_PIXELS, /* __DRI_ATTRIB_MAX_PBUFFER_PIXELS */ - 0, /* __DRI_ATTRIB_OPTIMAL_PBUFFER_WIDTH */ - 0, /* __DRI_ATTRIB_OPTIMAL_PBUFFER_HEIGHT */ - 0, /* __DRI_ATTRIB_VISUAL_SELECT_GROUP */ - 0, /* __DRI_ATTRIB_SWAP_METHOD */ - EGL_MAX_SWAP_INTERVAL, /* __DRI_ATTRIB_MAX_SWAP_INTERVAL */ - EGL_MIN_SWAP_INTERVAL, /* __DRI_ATTRIB_MIN_SWAP_INTERVAL */ - 0, /* __DRI_ATTRIB_BIND_TO_TEXTURE_RGB */ - 0, /* __DRI_ATTRIB_BIND_TO_TEXTURE_RGBA */ - 0, /* __DRI_ATTRIB_BIND_TO_MIPMAP_TEXTURE */ - 0, /* __DRI_ATTRIB_BIND_TO_TEXTURE_TARGETS */ - EGL_Y_INVERTED_NOK, /* __DRI_ATTRIB_YINVERTED */ - 0, /* __DRI_ATTRIB_FRAMEBUFFER_SRGB_CAPABLE */ +static void +dri2_get_pbuffer_drawable_info(__DRIdrawable * draw, + int *x, int *y, int *w, int *h, + void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + + *x = *y = 0; + *w = dri2_surf->base.Width; + *h = dri2_surf->base.Height; +} + +static int +dri2_get_bytes_per_pixel(struct dri2_egl_surface *dri2_surf) +{ + const int depth = dri2_surf->base.Config->BufferSize; + return depth ? util_next_power_of_two(depth / 8) : 0; +} + +static void +dri2_put_image(__DRIdrawable * draw, int op, + int x, int y, int w, int h, + char *data, void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + const int bpp = dri2_get_bytes_per_pixel(dri2_surf); + const int width = dri2_surf->base.Width; + const int height = dri2_surf->base.Height; + const int dst_stride = width*bpp; + const int src_stride = w*bpp; + const int x_offset = x*bpp; + int copy_width = src_stride; + + if (!dri2_surf->swrast_device_buffer) + dri2_surf->swrast_device_buffer = malloc(height*dst_stride); + + if (dri2_surf->swrast_device_buffer) { + const char *src = data; + char *dst = dri2_surf->swrast_device_buffer; + + dst += x_offset; + dst += y*dst_stride; + + /* Drivers are allowed to submit OOB PutImage requests, so clip here. */ + if (copy_width > dst_stride - x_offset) + copy_width = dst_stride - x_offset; + if (h > height - y) + h = height - y; + + for (; 0 < h; --h) { + memcpy(dst, src, copy_width); + dst += dst_stride; + src += src_stride; + } + } +} + +static void +dri2_get_image(__DRIdrawable * read, + int x, int y, int w, int h, + char *data, void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + const int bpp = dri2_get_bytes_per_pixel(dri2_surf); + const int width = dri2_surf->base.Width; + const int height = dri2_surf->base.Height; + const int src_stride = width*bpp; + const int dst_stride = w*bpp; + const int x_offset = x*bpp; + int copy_width = dst_stride; + const char *src = dri2_surf->swrast_device_buffer; + char *dst = data; + + if (!src) { + memset(data, 0, copy_width * h); + return; + } + + src += x_offset; + src += y*src_stride; + + /* Drivers are allowed to submit OOB GetImage requests, so clip here. */ + if (copy_width > src_stride - x_offset) + copy_width = src_stride - x_offset; + if (h > height - y) + h = height - y; + + for (; 0 < h; --h) { + memcpy(dst, src, copy_width); + src += src_stride; + dst += dst_stride; + } + +} + +/* HACK: technically we should have swrast_null, instead of these. + */ +const __DRIswrastLoaderExtension swrast_pbuffer_loader_extension = { + .base = { __DRI_SWRAST_LOADER, 1 }, + .getDrawableInfo = dri2_get_pbuffer_drawable_info, + .putImage = dri2_put_image, + .getImage = dri2_get_image, +}; + +static const EGLint dri2_to_egl_attribute_map[__DRI_ATTRIB_MAX] = { + [__DRI_ATTRIB_BUFFER_SIZE ] = EGL_BUFFER_SIZE, + [__DRI_ATTRIB_LEVEL] = EGL_LEVEL, + [__DRI_ATTRIB_LUMINANCE_SIZE] = EGL_LUMINANCE_SIZE, + [__DRI_ATTRIB_DEPTH_SIZE] = EGL_DEPTH_SIZE, + [__DRI_ATTRIB_STENCIL_SIZE] = EGL_STENCIL_SIZE, + [__DRI_ATTRIB_SAMPLE_BUFFERS] = EGL_SAMPLE_BUFFERS, + [__DRI_ATTRIB_SAMPLES] = EGL_SAMPLES, + [__DRI_ATTRIB_MAX_PBUFFER_WIDTH] = EGL_MAX_PBUFFER_WIDTH, + [__DRI_ATTRIB_MAX_PBUFFER_HEIGHT] = EGL_MAX_PBUFFER_HEIGHT, + [__DRI_ATTRIB_MAX_PBUFFER_PIXELS] = EGL_MAX_PBUFFER_PIXELS, + [__DRI_ATTRIB_MAX_SWAP_INTERVAL] = EGL_MAX_SWAP_INTERVAL, + [__DRI_ATTRIB_MIN_SWAP_INTERVAL] = EGL_MIN_SWAP_INTERVAL, + [__DRI_ATTRIB_YINVERTED] = EGL_Y_INVERTED_NOK, }; const __DRIconfig * dri2_get_dri_config(struct dri2_egl_config *conf, EGLint surface_type, EGLenum colorspace) { + const bool double_buffer = surface_type == EGL_WINDOW_BIT; const bool srgb = colorspace == EGL_GL_COLORSPACE_SRGB_KHR; - return surface_type == EGL_WINDOW_BIT ? conf->dri_double_config[srgb] : - conf->dri_single_config[srgb]; + return conf->dri_config[double_buffer][srgb]; } static EGLBoolean @@ -149,79 +344,185 @@ dri2_match_config(const _EGLConfig *conf, const _EGLConfig *criteria) return EGL_TRUE; } +void +dri2_get_shifts_and_sizes(const __DRIcoreExtension *core, + const __DRIconfig *config, int *shifts, + unsigned int *sizes) +{ + unsigned int mask; + + if (core->getConfigAttrib(config, __DRI_ATTRIB_RED_SHIFT, (unsigned int *)&shifts[0])) { + core->getConfigAttrib(config, __DRI_ATTRIB_GREEN_SHIFT, (unsigned int *)&shifts[1]); + core->getConfigAttrib(config, __DRI_ATTRIB_BLUE_SHIFT, (unsigned int *)&shifts[2]); + core->getConfigAttrib(config, __DRI_ATTRIB_ALPHA_SHIFT, (unsigned int *)&shifts[3]); + } else { + /* Driver isn't exposing shifts, so convert masks to shifts */ + core->getConfigAttrib(config, __DRI_ATTRIB_RED_MASK, &mask); + shifts[0] = ffs(mask) - 1; + core->getConfigAttrib(config, __DRI_ATTRIB_GREEN_MASK, &mask); + shifts[1] = ffs(mask) - 1; + core->getConfigAttrib(config, __DRI_ATTRIB_BLUE_MASK, &mask); + shifts[2] = ffs(mask) - 1; + core->getConfigAttrib(config, __DRI_ATTRIB_ALPHA_MASK, &mask); + shifts[3] = ffs(mask) - 1; + } + + core->getConfigAttrib(config, __DRI_ATTRIB_RED_SIZE, &sizes[0]); + core->getConfigAttrib(config, __DRI_ATTRIB_GREEN_SIZE, &sizes[1]); + core->getConfigAttrib(config, __DRI_ATTRIB_BLUE_SIZE, &sizes[2]); + core->getConfigAttrib(config, __DRI_ATTRIB_ALPHA_SIZE, &sizes[3]); +} + +void +dri2_get_render_type_float(const __DRIcoreExtension *core, + const __DRIconfig *config, + bool *is_float) +{ + unsigned int render_type; + + core->getConfigAttrib(config, __DRI_ATTRIB_RENDER_TYPE, &render_type); + *is_float = (render_type & __DRI_ATTRIB_FLOAT_BIT) ? true : false; +} + +unsigned int +dri2_image_format_for_pbuffer_config(struct dri2_egl_display *dri2_dpy, + const __DRIconfig *config) +{ + int shifts[4]; + unsigned int sizes[4]; + + dri2_get_shifts_and_sizes(dri2_dpy->core, config, shifts, sizes); + + for (unsigned i = 0; i < ARRAY_SIZE(dri2_pbuffer_visuals); ++i) { + const struct dri2_pbuffer_visual *visual = &dri2_pbuffer_visuals[i]; + + if (shifts[0] == visual->rgba_shifts[0] && + shifts[1] == visual->rgba_shifts[1] && + shifts[2] == visual->rgba_shifts[2] && + shifts[3] == visual->rgba_shifts[3] && + sizes[0] == visual->rgba_sizes[0] && + sizes[1] == visual->rgba_sizes[1] && + sizes[2] == visual->rgba_sizes[2] && + sizes[3] == visual->rgba_sizes[3]) { + return visual->dri_image_format; + } + } + + return __DRI_IMAGE_FORMAT_NONE; +} + struct dri2_egl_config * dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, - EGLint surface_type, const EGLint *attr_list, - const unsigned int *rgba_masks) + EGLint surface_type, const EGLint *attr_list, + const int *rgba_shifts, const unsigned int *rgba_sizes) { struct dri2_egl_config *conf; - struct dri2_egl_display *dri2_dpy; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); _EGLConfig base; unsigned int attrib, value, double_buffer; bool srgb = false; EGLint key, bind_to_texture_rgb, bind_to_texture_rgba; - unsigned int dri_masks[4] = { 0, 0, 0, 0 }; + int dri_shifts[4] = { -1, -1, -1, -1 }; + unsigned int dri_sizes[4] = { 0, 0, 0, 0 }; _EGLConfig *matching_config; EGLint num_configs = 0; EGLint config_id; - int i; - dri2_dpy = disp->DriverData; _eglInitConfig(&base, disp, id); - i = 0; double_buffer = 0; bind_to_texture_rgb = 0; bind_to_texture_rgba = 0; - while (dri2_dpy->core->indexConfigAttrib(dri_config, i++, &attrib, &value)) { + for (int i = 0; i < __DRI_ATTRIB_MAX; ++i) { + if (!dri2_dpy->core->indexConfigAttrib(dri_config, i, &attrib, &value)) + break; + switch (attrib) { case __DRI_ATTRIB_RENDER_TYPE: - if (value & __DRI_ATTRIB_RGBA_BIT) - value = EGL_RGB_BUFFER; - else if (value & __DRI_ATTRIB_LUMINANCE_BIT) - value = EGL_LUMINANCE_BUFFER; - else - return NULL; - _eglSetConfigKey(&base, EGL_COLOR_BUFFER_TYPE, value); - break; + if (value & __DRI_ATTRIB_FLOAT_BIT) + base.ComponentType = EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT; + if (value & __DRI_ATTRIB_RGBA_BIT) + value = EGL_RGB_BUFFER; + else if (value & __DRI_ATTRIB_LUMINANCE_BIT) + value = EGL_LUMINANCE_BUFFER; + else + return NULL; + base.ColorBufferType = value; + break; case __DRI_ATTRIB_CONFIG_CAVEAT: if (value & __DRI_ATTRIB_NON_CONFORMANT_CONFIG) value = EGL_NON_CONFORMANT_CONFIG; else if (value & __DRI_ATTRIB_SLOW_BIT) value = EGL_SLOW_CONFIG; - else - value = EGL_NONE; - _eglSetConfigKey(&base, EGL_CONFIG_CAVEAT, value); + else + value = EGL_NONE; + base.ConfigCaveat = value; break; case __DRI_ATTRIB_BIND_TO_TEXTURE_RGB: - bind_to_texture_rgb = value; - break; + bind_to_texture_rgb = value; + break; case __DRI_ATTRIB_BIND_TO_TEXTURE_RGBA: - bind_to_texture_rgba = value; - break; + bind_to_texture_rgba = value; + break; case __DRI_ATTRIB_DOUBLE_BUFFER: - double_buffer = value; - break; + double_buffer = value; + break; + + case __DRI_ATTRIB_RED_SIZE: + dri_sizes[0] = value; + base.RedSize = value; + break; case __DRI_ATTRIB_RED_MASK: - dri_masks[0] = value; + dri_shifts[0] = ffs(value) - 1; + break; + + case __DRI_ATTRIB_RED_SHIFT: + dri_shifts[0] = value; + break; + + case __DRI_ATTRIB_GREEN_SIZE: + dri_sizes[1] = value; + base.GreenSize = value; break; case __DRI_ATTRIB_GREEN_MASK: - dri_masks[1] = value; + dri_shifts[1] = ffs(value) - 1; + break; + + case __DRI_ATTRIB_GREEN_SHIFT: + dri_shifts[1] = value; + break; + + case __DRI_ATTRIB_BLUE_SIZE: + dri_sizes[2] = value; + base.BlueSize = value; break; case __DRI_ATTRIB_BLUE_MASK: - dri_masks[2] = value; + dri_shifts[2] = ffs(value) - 1; + break; + + case __DRI_ATTRIB_BLUE_SHIFT: + dri_shifts[2] = value; + break; + + case __DRI_ATTRIB_ALPHA_SIZE: + dri_sizes[3] = value; + base.AlphaSize = value; break; case __DRI_ATTRIB_ALPHA_MASK: - dri_masks[3] = value; + dri_shifts[3] = ffs(value) - 1; + break; + + case __DRI_ATTRIB_ALPHA_SHIFT: + dri_shifts[3] = value; break; case __DRI_ATTRIB_ACCUM_RED_SIZE: @@ -235,21 +536,36 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, case __DRI_ATTRIB_FRAMEBUFFER_SRGB_CAPABLE: srgb = value != 0; + if (!disp->Extensions.KHR_gl_colorspace && srgb) + return NULL; break; + case __DRI_ATTRIB_MAX_PBUFFER_WIDTH: + base.MaxPbufferWidth = _EGL_MAX_PBUFFER_WIDTH; + break; + case __DRI_ATTRIB_MAX_PBUFFER_HEIGHT: + base.MaxPbufferHeight = _EGL_MAX_PBUFFER_HEIGHT; + break; + case __DRI_ATTRIB_MUTABLE_RENDER_BUFFER: + if (disp->Extensions.KHR_mutable_render_buffer) + surface_type |= EGL_MUTABLE_RENDER_BUFFER_BIT_KHR; + break; default: - key = dri2_to_egl_attribute_map[attrib]; - if (key != 0) - _eglSetConfigKey(&base, key, value); - break; + key = dri2_to_egl_attribute_map[attrib]; + if (key != 0) + _eglSetConfigKey(&base, key, value); + break; } } if (attr_list) - for (i = 0; attr_list[i] != EGL_NONE; i += 2) + for (int i = 0; attr_list[i] != EGL_NONE; i += 2) _eglSetConfigKey(&base, attr_list[i], attr_list[i+1]); - if (rgba_masks && memcmp(rgba_masks, dri_masks, sizeof(dri_masks))) + if (rgba_shifts && memcmp(rgba_shifts, dri_shifts, sizeof(dri_shifts))) + return NULL; + + if (rgba_sizes && memcmp(rgba_sizes, dri_sizes, sizeof(dri_sizes))) return NULL; base.NativeRenderable = EGL_TRUE; @@ -262,6 +578,22 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, base.BindToTextureRGBA = bind_to_texture_rgba; } + if (double_buffer) { + surface_type &= ~EGL_PIXMAP_BIT; + } + + /* No support for pbuffer + MSAA for now. + * + * XXX TODO: pbuffer + MSAA does not work and causes crashes. + * See QT bugreport: https://bugreports.qt.io/browse/QTBUG-47509 + */ + if (base.Samples) { + surface_type &= ~EGL_PBUFFER_BIT; + } + + if (!surface_type) + return NULL; + base.RenderableType = disp->ClientAPIs; base.Conformant = disp->ClientAPIs; @@ -282,10 +614,8 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, if (num_configs == 1) { conf = (struct dri2_egl_config *) matching_config; - if (double_buffer && !conf->dri_double_config[srgb]) - conf->dri_double_config[srgb] = dri_config; - else if (!double_buffer && !conf->dri_single_config[srgb]) - conf->dri_single_config[srgb] = dri_config; + if (!conf->dri_config[double_buffer][srgb]) + conf->dri_config[double_buffer][srgb] = dri_config; else /* a similar config type is already added (unlikely) => discard */ return NULL; @@ -295,10 +625,7 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, if (conf == NULL) return NULL; - if (double_buffer) - conf->dri_double_config[srgb] = dri_config; - else - conf->dri_single_config[srgb] = dri_config; + conf->dri_config[double_buffer][srgb] = dri_config; memcpy(&conf->base, &base, sizeof base); conf->base.SurfaceType = 0; @@ -307,19 +634,48 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, _eglLinkConfig(&conf->base); } else { - assert(0); + unreachable("duplicates should not be possible"); return NULL; } - if (double_buffer) { - surface_type &= ~EGL_PIXMAP_BIT; - } - conf->base.SurfaceType |= surface_type; return conf; } +EGLBoolean +dri2_add_pbuffer_configs_for_visuals(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + unsigned int format_count[ARRAY_SIZE(dri2_pbuffer_visuals)] = { 0 }; + unsigned int config_count = 0; + + for (unsigned i = 0; dri2_dpy->driver_configs[i] != NULL; i++) { + for (unsigned j = 0; j < ARRAY_SIZE(dri2_pbuffer_visuals); j++) { + struct dri2_egl_config *dri2_conf; + + dri2_conf = dri2_add_config(disp, dri2_dpy->driver_configs[i], + config_count + 1, EGL_PBUFFER_BIT, NULL, + dri2_pbuffer_visuals[j].rgba_shifts, dri2_pbuffer_visuals[j].rgba_sizes); + + if (dri2_conf) { + if (dri2_conf->base.ConfigID == config_count + 1) + config_count++; + format_count[j]++; + } + } + } + + for (unsigned i = 0; i < ARRAY_SIZE(format_count); i++) { + if (!format_count[i]) { + _eglLog(_EGL_DEBUG, "No DRI config supports native format %s", + dri2_pbuffer_visuals[i].format_name); + } + } + + return (config_count != 0); +} + __DRIimage * dri2_lookup_egl_image(__DRIscreen *screen, void *image, void *data) { @@ -352,57 +708,90 @@ struct dri2_extension_match { int offset; }; -static struct dri2_extension_match dri2_driver_extensions[] = { +static const struct dri2_extension_match dri3_driver_extensions[] = { + { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) }, + { __DRI_IMAGE_DRIVER, 1, offsetof(struct dri2_egl_display, image_driver) }, + { NULL, 0, 0 } +}; + +static const struct dri2_extension_match dri2_driver_extensions[] = { { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) }, { __DRI_DRI2, 2, offsetof(struct dri2_egl_display, dri2) }, { NULL, 0, 0 } }; -static struct dri2_extension_match dri2_core_extensions[] = { +static const struct dri2_extension_match dri2_core_extensions[] = { { __DRI2_FLUSH, 1, offsetof(struct dri2_egl_display, flush) }, { __DRI_TEX_BUFFER, 2, offsetof(struct dri2_egl_display, tex_buffer) }, { __DRI_IMAGE, 1, offsetof(struct dri2_egl_display, image) }, { NULL, 0, 0 } }; -static struct dri2_extension_match swrast_driver_extensions[] = { +static const struct dri2_extension_match swrast_driver_extensions[] = { { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) }, { __DRI_SWRAST, 2, offsetof(struct dri2_egl_display, swrast) }, { NULL, 0, 0 } }; -static struct dri2_extension_match swrast_core_extensions[] = { +static const struct dri2_extension_match swrast_core_extensions[] = { { __DRI_TEX_BUFFER, 2, offsetof(struct dri2_egl_display, tex_buffer) }, { NULL, 0, 0 } }; +static const struct dri2_extension_match optional_driver_extensions[] = { + { __DRI_CONFIG_OPTIONS, 1, offsetof(struct dri2_egl_display, configOptions) }, + { NULL, 0, 0 } +}; + +static const struct dri2_extension_match optional_core_extensions[] = { + { __DRI2_ROBUSTNESS, 1, offsetof(struct dri2_egl_display, robustness) }, + { __DRI2_NO_ERROR, 1, offsetof(struct dri2_egl_display, no_error) }, + { __DRI2_CONFIG_QUERY, 1, offsetof(struct dri2_egl_display, config) }, + { __DRI2_FENCE, 1, offsetof(struct dri2_egl_display, fence) }, + { __DRI2_BUFFER_DAMAGE, 1, offsetof(struct dri2_egl_display, buffer_damage) }, + { __DRI2_RENDERER_QUERY, 1, offsetof(struct dri2_egl_display, rendererQuery) }, + { __DRI2_INTEROP, 1, offsetof(struct dri2_egl_display, interop) }, + { __DRI_IMAGE, 1, offsetof(struct dri2_egl_display, image) }, + { __DRI2_FLUSH_CONTROL, 1, offsetof(struct dri2_egl_display, flush_control) }, + { __DRI2_BLOB, 1, offsetof(struct dri2_egl_display, blob) }, + { __DRI_MUTABLE_RENDER_BUFFER_DRIVER, 1, offsetof(struct dri2_egl_display, mutable_render_buffer) }, + { NULL, 0, 0 } +}; + static EGLBoolean dri2_bind_extensions(struct dri2_egl_display *dri2_dpy, - struct dri2_extension_match *matches, - const __DRIextension **extensions) + const struct dri2_extension_match *matches, + const __DRIextension **extensions, + bool optional) { - int i, j, ret = EGL_TRUE; + int ret = EGL_TRUE; void *field; - for (i = 0; extensions[i]; i++) { - _eglLog(_EGL_DEBUG, "DRI2: found extension `%s'", extensions[i]->name); - for (j = 0; matches[j].name; j++) { - if (strcmp(extensions[i]->name, matches[j].name) == 0 && - extensions[i]->version >= matches[j].version) { - field = ((char *) dri2_dpy + matches[j].offset); - *(const __DRIextension **) field = extensions[i]; - _eglLog(_EGL_INFO, "DRI2: found extension %s version %d", - extensions[i]->name, extensions[i]->version); - } + for (int i = 0; extensions[i]; i++) { + _eglLog(_EGL_DEBUG, "found extension `%s'", extensions[i]->name); + for (int j = 0; matches[j].name; j++) { + if (strcmp(extensions[i]->name, matches[j].name) == 0 && + extensions[i]->version >= matches[j].version) { + field = ((char *) dri2_dpy + matches[j].offset); + *(const __DRIextension **) field = extensions[i]; + _eglLog(_EGL_INFO, "found extension %s version %d", + extensions[i]->name, extensions[i]->version); + break; + } } } - for (j = 0; matches[j].name; j++) { + for (int j = 0; matches[j].name; j++) { field = ((char *) dri2_dpy + matches[j].offset); if (*(const __DRIextension **) field == NULL) { - _eglLog(_EGL_WARNING, "DRI2: did not find extension %s version %d", - matches[j].name, matches[j].version); - ret = EGL_FALSE; + if (optional) { + _eglLog(_EGL_DEBUG, "did not find optional extension %s version %d", + matches[j].name, matches[j].version); + } else { + _eglLog(_EGL_WARNING, "did not find extension %s version %d", + matches[j].name, matches[j].version); + ret = EGL_FALSE; + } } } @@ -412,123 +801,56 @@ dri2_bind_extensions(struct dri2_egl_display *dri2_dpy, static const __DRIextension ** dri2_open_driver(_EGLDisplay *disp) { - struct dri2_egl_display *dri2_dpy = disp->DriverData; - const __DRIextension **extensions = NULL; - char path[PATH_MAX], *search_paths, *p, *next, *end; - char *get_extensions_name; - const __DRIextension **(*get_extensions)(void); - - search_paths = NULL; - if (geteuid() == getuid()) { - /* don't allow setuid apps to use LIBGL_DRIVERS_PATH */ - search_paths = getenv("LIBGL_DRIVERS_PATH"); - } - if (search_paths == NULL) - search_paths = DEFAULT_DRIVER_DIR; - - dri2_dpy->driver = NULL; - end = search_paths + strlen(search_paths); - for (p = search_paths; p < end; p = next + 1) { - int len; - next = strchr(p, ':'); - if (next == NULL) - next = end; - - len = next - p; -#if GLX_USE_TLS - snprintf(path, sizeof path, - "%.*s/tls/%s_dri.so", len, p, dri2_dpy->driver_name); - dri2_dpy->driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); -#endif - if (dri2_dpy->driver == NULL) { - snprintf(path, sizeof path, - "%.*s/%s_dri.so", len, p, dri2_dpy->driver_name); - dri2_dpy->driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); - if (dri2_dpy->driver == NULL) - _eglLog(_EGL_DEBUG, "failed to open %s: %s\n", path, dlerror()); - } - /* not need continue to loop all paths once the driver is found */ - if (dri2_dpy->driver != NULL) - break; - -#ifdef ANDROID - snprintf(path, sizeof path, "%.*s/gallium_dri.so", len, p); - dri2_dpy->driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); - if (dri2_dpy->driver == NULL) - _eglLog(_EGL_DEBUG, "failed to open %s: %s\n", path, dlerror()); - else - break; -#endif - } - - if (dri2_dpy->driver == NULL) { - _eglLog(_EGL_WARNING, - "DRI2: failed to open %s (search paths %s)", - dri2_dpy->driver_name, search_paths); - return NULL; - } - - _eglLog(_EGL_DEBUG, "DRI2: dlopen(%s)", path); - - if (asprintf(&get_extensions_name, "%s_%s", - __DRI_DRIVER_GET_EXTENSIONS, dri2_dpy->driver_name) != -1) { - get_extensions = dlsym(dri2_dpy->driver, get_extensions_name); - if (get_extensions) { - extensions = get_extensions(); - } else { - _eglLog(_EGL_DEBUG, "driver does not expose %s(): %s\n", - get_extensions_name, dlerror()); - } - free(get_extensions_name); - } - - if (!extensions) - extensions = dlsym(dri2_dpy->driver, __DRI_DRIVER_EXTENSIONS); - if (extensions == NULL) { - _eglLog(_EGL_WARNING, - "DRI2: driver exports no extensions (%s)", dlerror()); - dlclose(dri2_dpy->driver); - } + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + static const char *search_path_vars[] = { + "LIBGL_DRIVERS_PATH", + NULL, + }; - return extensions; + return loader_open_driver(dri2_dpy->driver_name, + &dri2_dpy->driver, + search_path_vars); } -EGLBoolean -dri2_load_driver(_EGLDisplay *disp) +static EGLBoolean +dri2_load_driver_common(_EGLDisplay *disp, + const struct dri2_extension_match *driver_extensions) { - struct dri2_egl_display *dri2_dpy = disp->DriverData; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); const __DRIextension **extensions; extensions = dri2_open_driver(disp); if (!extensions) return EGL_FALSE; - if (!dri2_bind_extensions(dri2_dpy, dri2_driver_extensions, extensions)) { + if (!dri2_bind_extensions(dri2_dpy, driver_extensions, extensions, false)) { dlclose(dri2_dpy->driver); + dri2_dpy->driver = NULL; return EGL_FALSE; } dri2_dpy->driver_extensions = extensions; + dri2_bind_extensions(dri2_dpy, optional_driver_extensions, extensions, true); + return EGL_TRUE; } EGLBoolean -dri2_load_driver_swrast(_EGLDisplay *disp) +dri2_load_driver(_EGLDisplay *disp) { - struct dri2_egl_display *dri2_dpy = disp->DriverData; - const __DRIextension **extensions; - - extensions = dri2_open_driver(disp); - if (!extensions) - return EGL_FALSE; + return dri2_load_driver_common(disp, dri2_driver_extensions); +} - if (!dri2_bind_extensions(dri2_dpy, swrast_driver_extensions, extensions)) { - dlclose(dri2_dpy->driver); - return EGL_FALSE; - } - dri2_dpy->driver_extensions = extensions; +EGLBoolean +dri2_load_driver_dri3(_EGLDisplay *disp) +{ + return dri2_load_driver_common(disp, dri3_driver_extensions); +} - return EGL_TRUE; +EGLBoolean +dri2_load_driver_swrast(_EGLDisplay *disp) +{ + return dri2_load_driver_common(disp, swrast_driver_extensions); } static unsigned @@ -544,13 +866,47 @@ dri2_renderer_query_integer(struct dri2_egl_display *dri2_dpy, int param) return value; } +static const char * +dri2_query_driver_name(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + return dri2_dpy->driver_name; +} + +static char * +dri2_query_driver_config(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + const __DRIconfigOptionsExtension *ext = dri2_dpy->configOptions; + + if (ext->base.version >= 2) + return ext->getXml(dri2_dpy->driver_name); + + return strdup(ext->xml); +} + + void dri2_setup_screen(_EGLDisplay *disp) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); unsigned int api_mask; - if (dri2_dpy->dri2) { + /* + * EGL 1.5 specification defines the default value to 1. Moreover, + * eglSwapInterval() is required to clamp requested value to the supported + * range. Since the default value is implicitly assumed to be supported, + * use it as both minimum and maximum for the platforms that do not allow + * changing the interval. Platforms, which allow it (e.g. x11, wayland) + * override these values already. + */ + dri2_dpy->min_swap_interval = 1; + dri2_dpy->max_swap_interval = 1; + dri2_dpy->default_swap_interval = 1; + + if (dri2_dpy->image_driver) { + api_mask = dri2_dpy->image_driver->getAPIMask(dri2_dpy->dri_screen); + } else if (dri2_dpy->dri2) { api_mask = dri2_dpy->dri2->getAPIMask(dri2_dpy->dri_screen); } else { assert(dri2_dpy->swrast); @@ -561,24 +917,36 @@ dri2_setup_screen(_EGLDisplay *disp) } disp->ClientAPIs = 0; - if (api_mask & (1 <<__DRI_API_OPENGL)) + if ((api_mask & (1 <<__DRI_API_OPENGL)) && _eglIsApiValid(EGL_OPENGL_API)) disp->ClientAPIs |= EGL_OPENGL_BIT; - if (api_mask & (1 <<__DRI_API_GLES)) + if ((api_mask & (1 << __DRI_API_GLES)) && _eglIsApiValid(EGL_OPENGL_ES_API)) disp->ClientAPIs |= EGL_OPENGL_ES_BIT; - if (api_mask & (1 << __DRI_API_GLES2)) + if ((api_mask & (1 << __DRI_API_GLES2)) && _eglIsApiValid(EGL_OPENGL_ES_API)) disp->ClientAPIs |= EGL_OPENGL_ES2_BIT; - if (api_mask & (1 << __DRI_API_GLES3)) + if ((api_mask & (1 << __DRI_API_GLES3)) && _eglIsApiValid(EGL_OPENGL_ES_API)) disp->ClientAPIs |= EGL_OPENGL_ES3_BIT_KHR; - assert(dri2_dpy->dri2 || dri2_dpy->swrast); + assert(dri2_dpy->image_driver || dri2_dpy->dri2 || dri2_dpy->swrast); + disp->Extensions.KHR_no_config_context = EGL_TRUE; disp->Extensions.KHR_surfaceless_context = EGL_TRUE; - disp->Extensions.MESA_configless_context = EGL_TRUE; + + if (dri2_dpy->configOptions) { + disp->Extensions.MESA_query_driver = EGL_TRUE; + } + + /* Report back to EGL the bitmask of priorities supported */ + disp->Extensions.IMG_context_priority = + dri2_renderer_query_integer(dri2_dpy, + __DRI2_RENDERER_HAS_CONTEXT_PRIORITY); + + disp->Extensions.EXT_pixel_format_float = EGL_TRUE; if (dri2_renderer_query_integer(dri2_dpy, __DRI2_RENDERER_HAS_FRAMEBUFFER_SRGB)) disp->Extensions.KHR_gl_colorspace = EGL_TRUE; - if ((dri2_dpy->dri2 && dri2_dpy->dri2->base.version >= 3) || + if (dri2_dpy->image_driver || + (dri2_dpy->dri2 && dri2_dpy->dri2->base.version >= 3) || (dri2_dpy->swrast && dri2_dpy->swrast->base.version >= 3)) { disp->Extensions.KHR_create_context = EGL_TRUE; @@ -586,13 +954,28 @@ dri2_setup_screen(_EGLDisplay *disp) disp->Extensions.EXT_create_context_robustness = EGL_TRUE; } + if (dri2_dpy->no_error) + disp->Extensions.KHR_create_context_no_error = EGL_TRUE; + if (dri2_dpy->fence) { disp->Extensions.KHR_fence_sync = EGL_TRUE; disp->Extensions.KHR_wait_sync = EGL_TRUE; if (dri2_dpy->fence->get_fence_from_cl_event) disp->Extensions.KHR_cl_event2 = EGL_TRUE; + if (dri2_dpy->fence->base.version >= 2 && + dri2_dpy->fence->get_capabilities) { + unsigned capabilities = + dri2_dpy->fence->get_capabilities(dri2_dpy->dri_screen); + disp->Extensions.ANDROID_native_fence_sync = + (capabilities & __DRI_FENCE_CAP_NATIVE_FD) != 0; + } } + if (dri2_dpy->blob) + disp->Extensions.ANDROID_blob_cache = EGL_TRUE; + + disp->Extensions.KHR_reusable_sync = EGL_TRUE; + if (dri2_dpy->image) { if (dri2_dpy->image->base.version >= 10 && dri2_dpy->image->getCapabilities != NULL) { @@ -615,55 +998,100 @@ dri2_setup_screen(_EGLDisplay *disp) dri2_dpy->image->createImageFromTexture) { disp->Extensions.KHR_gl_texture_2D_image = EGL_TRUE; disp->Extensions.KHR_gl_texture_cubemap_image = EGL_TRUE; + + if (dri2_renderer_query_integer(dri2_dpy, + __DRI2_RENDERER_HAS_TEXTURE_3D)) + disp->Extensions.KHR_gl_texture_3D_image = EGL_TRUE; } - if (dri2_renderer_query_integer(dri2_dpy, - __DRI2_RENDERER_HAS_TEXTURE_3D)) - disp->Extensions.KHR_gl_texture_3D_image = EGL_TRUE; #ifdef HAVE_LIBDRM if (dri2_dpy->image->base.version >= 8 && dri2_dpy->image->createImageFromDmaBufs) { disp->Extensions.EXT_image_dma_buf_import = EGL_TRUE; + disp->Extensions.EXT_image_dma_buf_import_modifiers = EGL_TRUE; } #endif } + + if (dri2_dpy->flush_control) + disp->Extensions.KHR_context_flush_control = EGL_TRUE; + + if (dri2_dpy->buffer_damage && dri2_dpy->buffer_damage->set_damage_region) + disp->Extensions.KHR_partial_update = EGL_TRUE; +} + +void +dri2_setup_swap_interval(_EGLDisplay *disp, int max_swap_interval) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; + + /* Allow driconf to override applications.*/ + if (dri2_dpy->config) + dri2_dpy->config->configQueryi(dri2_dpy->dri_screen, + "vblank_mode", &vblank_mode); + switch (vblank_mode) { + case DRI_CONF_VBLANK_NEVER: + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = 0; + dri2_dpy->default_swap_interval = 0; + break; + case DRI_CONF_VBLANK_ALWAYS_SYNC: + dri2_dpy->min_swap_interval = 1; + dri2_dpy->max_swap_interval = max_swap_interval; + dri2_dpy->default_swap_interval = 1; + break; + case DRI_CONF_VBLANK_DEF_INTERVAL_0: + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = max_swap_interval; + dri2_dpy->default_swap_interval = 0; + break; + default: + case DRI_CONF_VBLANK_DEF_INTERVAL_1: + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = max_swap_interval; + dri2_dpy->default_swap_interval = 1; + break; + } } -/* All platforms but DRM call this function to create the screen, query the - * dri extensions, setup the vtables and populate the driver_configs. - * DRM inherits all that information from its display - GBM. +/* All platforms but DRM call this function to create the screen and populate + * the driver_configs. DRM inherits that information from its display - GBM. */ EGLBoolean dri2_create_screen(_EGLDisplay *disp) { - const __DRIextension **extensions; - struct dri2_egl_display *dri2_dpy; - unsigned i; - - dri2_dpy = disp->DriverData; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - if (dri2_dpy->dri2) { + if (dri2_dpy->image_driver) { + dri2_dpy->dri_screen = + dri2_dpy->image_driver->createNewScreen2(0, dri2_dpy->fd, + dri2_dpy->loader_extensions, + dri2_dpy->driver_extensions, + &dri2_dpy->driver_configs, + disp); + } else if (dri2_dpy->dri2) { if (dri2_dpy->dri2->base.version >= 4) { dri2_dpy->dri_screen = dri2_dpy->dri2->createNewScreen2(0, dri2_dpy->fd, - dri2_dpy->extensions, + dri2_dpy->loader_extensions, dri2_dpy->driver_extensions, &dri2_dpy->driver_configs, disp); } else { dri2_dpy->dri_screen = dri2_dpy->dri2->createNewScreen(0, dri2_dpy->fd, - dri2_dpy->extensions, + dri2_dpy->loader_extensions, &dri2_dpy->driver_configs, disp); } } else { assert(dri2_dpy->swrast); if (dri2_dpy->swrast->base.version >= 4) { dri2_dpy->dri_screen = - dri2_dpy->swrast->createNewScreen2(0, dri2_dpy->extensions, + dri2_dpy->swrast->createNewScreen2(0, dri2_dpy->loader_extensions, dri2_dpy->driver_extensions, &dri2_dpy->driver_configs, disp); } else { dri2_dpy->dri_screen = - dri2_dpy->swrast->createNewScreen(0, dri2_dpy->extensions, + dri2_dpy->swrast->createNewScreen(0, dri2_dpy->loader_extensions, &dri2_dpy->driver_configs, disp); } } @@ -673,144 +1101,159 @@ dri2_create_screen(_EGLDisplay *disp) return EGL_FALSE; } - dri2_dpy->own_dri_screen = 1; + dri2_dpy->own_dri_screen = true; + return EGL_TRUE; +} + +EGLBoolean +dri2_setup_extensions(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + const struct dri2_extension_match *mandatory_core_extensions; + const __DRIextension **extensions; extensions = dri2_dpy->core->getExtensions(dri2_dpy->dri_screen); - if (dri2_dpy->dri2) { - if (!dri2_bind_extensions(dri2_dpy, dri2_core_extensions, extensions)) - goto cleanup_dri_screen; - } else { - assert(dri2_dpy->swrast); - if (!dri2_bind_extensions(dri2_dpy, swrast_core_extensions, extensions)) - goto cleanup_dri_screen; - } + if (dri2_dpy->image_driver || dri2_dpy->dri2) + mandatory_core_extensions = dri2_core_extensions; + else + mandatory_core_extensions = swrast_core_extensions; - for (i = 0; extensions[i]; i++) { - if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0) { - dri2_dpy->robustness = (__DRIrobustnessExtension *) extensions[i]; - } - if (strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0) { - dri2_dpy->config = (__DRI2configQueryExtension *) extensions[i]; - } - if (strcmp(extensions[i]->name, __DRI2_FENCE) == 0) { - dri2_dpy->fence = (__DRI2fenceExtension *) extensions[i]; - } - if (strcmp(extensions[i]->name, __DRI2_RENDERER_QUERY) == 0) { - dri2_dpy->rendererQuery = (__DRI2rendererQueryExtension *) extensions[i]; - } - } + if (!dri2_bind_extensions(dri2_dpy, mandatory_core_extensions, extensions, false)) + return EGL_FALSE; - dri2_setup_screen(disp); +#ifdef HAVE_DRI3_MODIFIERS + dri2_dpy->multibuffers_available = + (dri2_dpy->dri3_major_version > 1 || (dri2_dpy->dri3_major_version == 1 && + dri2_dpy->dri3_minor_version >= 2)) && + (dri2_dpy->present_major_version > 1 || (dri2_dpy->present_major_version == 1 && + dri2_dpy->present_minor_version >= 2)) && + (dri2_dpy->image && dri2_dpy->image->base.version >= 15); +#endif + dri2_bind_extensions(dri2_dpy, optional_core_extensions, extensions, true); return EGL_TRUE; - - cleanup_dri_screen: - dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); - - return EGL_FALSE; } /** - * Called via eglInitialize(), GLX_drv->API.Initialize(). + * Called via eglInitialize(), drv->Initialize(). + * + * This must be guaranteed to be called exactly once, even if eglInitialize is + * called many times (without a eglTerminate in between). */ static EGLBoolean -dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp) +dri2_initialize(_EGLDisplay *disp) { - /* not until swrast_dri is supported */ - if (disp->Options.UseFallback) - return EGL_FALSE; + EGLBoolean ret = EGL_FALSE; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + /* In the case where the application calls eglMakeCurrent(context1), + * eglTerminate, then eglInitialize again (without a call to eglReleaseThread + * or eglMakeCurrent(NULL) before that), dri2_dpy structure is still + * initialized, as we need it to be able to free context1 correctly. + * + * It would probably be safest to forcibly release the display with + * dri2_display_release, to make sure the display is reinitialized correctly. + * However, the EGL spec states that we need to keep a reference to the + * current context (so we cannot call dri2_make_current(NULL)), and therefore + * we would leak context1 as we would be missing the old display connection + * to free it up correctly. + */ + if (dri2_dpy) { + dri2_dpy->ref_count++; + return EGL_TRUE; + } + + loader_set_logger(_eglLog); switch (disp->Platform) { -#ifdef HAVE_SURFACELESS_PLATFORM case _EGL_PLATFORM_SURFACELESS: - if (disp->Options.TestOnly) - return EGL_TRUE; - return dri2_initialize_surfaceless(drv, disp); -#endif - -#ifdef HAVE_X11_PLATFORM + ret = dri2_initialize_surfaceless(disp); + break; + case _EGL_PLATFORM_DEVICE: + ret = dri2_initialize_device(disp); + break; case _EGL_PLATFORM_X11: - if (disp->Options.TestOnly) - return EGL_TRUE; - return dri2_initialize_x11(drv, disp); -#endif - -#ifdef HAVE_DRM_PLATFORM + ret = dri2_initialize_x11(disp); + break; case _EGL_PLATFORM_DRM: - if (disp->Options.TestOnly) - return EGL_TRUE; - return dri2_initialize_drm(drv, disp); -#endif -#ifdef HAVE_WAYLAND_PLATFORM + ret = dri2_initialize_drm(disp); + break; case _EGL_PLATFORM_WAYLAND: - if (disp->Options.TestOnly) - return EGL_TRUE; - return dri2_initialize_wayland(drv, disp); -#endif -#ifdef HAVE_ANDROID_PLATFORM + ret = dri2_initialize_wayland(disp); + break; case _EGL_PLATFORM_ANDROID: - if (disp->Options.TestOnly) - return EGL_TRUE; - return dri2_initialize_android(drv, disp); -#endif - + ret = dri2_initialize_android(disp); + break; default: - _eglLog(_EGL_WARNING, "No EGL platform enabled."); + unreachable("Callers ensure we cannot get here."); return EGL_FALSE; } + + if (!ret) + return EGL_FALSE; + + dri2_dpy = dri2_egl_display(disp); + dri2_dpy->ref_count++; + + return EGL_TRUE; } /** - * Called via eglTerminate(), drv->API.Terminate(). + * Decrement display reference count, and free up display if necessary. */ -static EGLBoolean -dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp) +static void +dri2_display_release(_EGLDisplay *disp) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - unsigned i; + struct dri2_egl_display *dri2_dpy; + + if (!disp) + return; + + dri2_dpy = dri2_egl_display(disp); + + assert(dri2_dpy->ref_count > 0); + dri2_dpy->ref_count--; + + if (dri2_dpy->ref_count > 0) + return; - _eglReleaseDisplayResources(drv, disp); _eglCleanupDisplay(disp); + dri2_display_destroy(disp); +} - if (dri2_dpy->own_dri_screen) +void +dri2_display_destroy(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + if (dri2_dpy->own_dri_screen) { + if (dri2_dpy->vtbl && dri2_dpy->vtbl->close_screen_notify) + dri2_dpy->vtbl->close_screen_notify(disp); dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); + } if (dri2_dpy->fd >= 0) close(dri2_dpy->fd); if (dri2_dpy->driver) dlclose(dri2_dpy->driver); - free(dri2_dpy->device_name); free(dri2_dpy->driver_name); +#ifdef HAVE_WAYLAND_PLATFORM + free(dri2_dpy->device_name); +#endif + switch (disp->Platform) { -#ifdef HAVE_X11_PLATFORM case _EGL_PLATFORM_X11: - if (dri2_dpy->own_device) { - xcb_disconnect(dri2_dpy->conn); - } + dri2_teardown_x11(dri2_dpy); break; -#endif -#ifdef HAVE_DRM_PLATFORM case _EGL_PLATFORM_DRM: - if (dri2_dpy->own_device) { - gbm_device_destroy(&dri2_dpy->gbm_dri->base.base); - } + dri2_teardown_drm(dri2_dpy); break; -#endif -#ifdef HAVE_WAYLAND_PLATFORM case _EGL_PLATFORM_WAYLAND: - if (dri2_dpy->wl_drm) - wl_drm_destroy(dri2_dpy->wl_drm); - if (dri2_dpy->wl_shm) - wl_shm_destroy(dri2_dpy->wl_shm); - wl_registry_destroy(dri2_dpy->wl_registry); - wl_event_queue_destroy(dri2_dpy->wl_queue); - if (dri2_dpy->own_device) { - wl_display_disconnect(dri2_dpy->wl_dpy); - } + dri2_teardown_wayland(dri2_dpy); break; -#endif default: + /* TODO: add teardown for other platforms */ break; } @@ -818,13 +1261,62 @@ dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp) * the ones from the gbm device. As such the gbm itself is responsible * for the cleanup. */ - if (disp->Platform != _EGL_PLATFORM_DRM) { - for (i = 0; dri2_dpy->driver_configs[i]; i++) + if (disp->Platform != _EGL_PLATFORM_DRM && dri2_dpy->driver_configs) { + for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) free((__DRIconfig *) dri2_dpy->driver_configs[i]); free(dri2_dpy->driver_configs); } free(dri2_dpy); disp->DriverData = NULL; +} + +__DRIbuffer * +dri2_egl_surface_alloc_local_buffer(struct dri2_egl_surface *dri2_surf, + unsigned int att, unsigned int format) +{ + struct dri2_egl_display *dri2_dpy = + dri2_egl_display(dri2_surf->base.Resource.Display); + + if (att >= ARRAY_SIZE(dri2_surf->local_buffers)) + return NULL; + + if (!dri2_surf->local_buffers[att]) { + dri2_surf->local_buffers[att] = + dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen, att, format, + dri2_surf->base.Width, dri2_surf->base.Height); + } + + return dri2_surf->local_buffers[att]; +} + +void +dri2_egl_surface_free_local_buffers(struct dri2_egl_surface *dri2_surf) +{ + struct dri2_egl_display *dri2_dpy = + dri2_egl_display(dri2_surf->base.Resource.Display); + + for (int i = 0; i < ARRAY_SIZE(dri2_surf->local_buffers); i++) { + if (dri2_surf->local_buffers[i]) { + dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen, + dri2_surf->local_buffers[i]); + dri2_surf->local_buffers[i] = NULL; + } + } +} + +/** + * Called via eglTerminate(), drv->Terminate(). + * + * This must be guaranteed to be called exactly once, even if eglTerminate is + * called many times (without a eglInitialize in between). + */ +static EGLBoolean +dri2_terminate(_EGLDisplay *disp) +{ + /* Release all non-current Context/Surfaces. */ + _eglReleaseDisplayResources(disp); + + dri2_display_release(disp); return EGL_TRUE; } @@ -885,7 +1377,7 @@ dri2_create_context_attribs_error(int dri_error) break; default: - assert(0); + assert(!"unknown dri_error code"); egl_error = EGL_BAD_MATCH; break; } @@ -901,14 +1393,14 @@ dri2_fill_context_attribs(struct dri2_egl_context *dri2_ctx, { int pos = 0; - assert(*num_attribs >= 8); + assert(*num_attribs >= NUM_ATTRIBS); ctx_attribs[pos++] = __DRI_CTX_ATTRIB_MAJOR_VERSION; ctx_attribs[pos++] = dri2_ctx->base.ClientMajorVersion; ctx_attribs[pos++] = __DRI_CTX_ATTRIB_MINOR_VERSION; ctx_attribs[pos++] = dri2_ctx->base.ClientMinorVersion; - if (dri2_ctx->base.Flags != 0) { + if (dri2_ctx->base.Flags != 0 || dri2_ctx->base.NoError) { /* If the implementation doesn't support the __DRI2_ROBUSTNESS * extension, don't even try to send it the robust-access flag. * It may explode. Instead, generate the required EGL error here. @@ -920,7 +1412,8 @@ dri2_fill_context_attribs(struct dri2_egl_context *dri2_ctx, } ctx_attribs[pos++] = __DRI_CTX_ATTRIB_FLAGS; - ctx_attribs[pos++] = dri2_ctx->base.Flags; + ctx_attribs[pos++] = dri2_ctx->base.Flags | + (dri2_ctx->base.NoError ? __DRI_CTX_FLAG_NO_ERROR : 0); } if (dri2_ctx->base.ResetNotificationStrategy != EGL_NO_RESET_NOTIFICATION_KHR) { @@ -933,8 +1426,35 @@ dri2_fill_context_attribs(struct dri2_egl_context *dri2_ctx, return false; } - ctx_attribs[pos++] = __DRI_CTX_ATTRIB_RESET_STRATEGY; - ctx_attribs[pos++] = __DRI_CTX_RESET_LOSE_CONTEXT; + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_RESET_STRATEGY; + ctx_attribs[pos++] = __DRI_CTX_RESET_LOSE_CONTEXT; + } + + if (dri2_ctx->base.ContextPriority != EGL_CONTEXT_PRIORITY_MEDIUM_IMG) { + unsigned val; + + switch (dri2_ctx->base.ContextPriority) { + case EGL_CONTEXT_PRIORITY_HIGH_IMG: + val = __DRI_CTX_PRIORITY_HIGH; + break; + case EGL_CONTEXT_PRIORITY_MEDIUM_IMG: + val = __DRI_CTX_PRIORITY_MEDIUM; + break; + case EGL_CONTEXT_PRIORITY_LOW_IMG: + val = __DRI_CTX_PRIORITY_LOW; + break; + default: + _eglError(EGL_BAD_CONFIG, "eglCreateContext"); + return false; + } + + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_PRIORITY; + ctx_attribs[pos++] = val; + } + + if (dri2_ctx->base.ReleaseBehavior == EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR) { + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_RELEASE_BEHAVIOR; + ctx_attribs[pos++] = __DRI_CTX_RELEASE_BEHAVIOR_NONE; } *num_attribs = pos; @@ -943,11 +1463,11 @@ dri2_fill_context_attribs(struct dri2_egl_context *dri2_ctx, } /** - * Called via eglCreateContext(), drv->API.CreateContext(). + * Called via eglCreateContext(), drv->CreateContext(). */ static _EGLContext * -dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, - _EGLContext *share_list, const EGLint *attrib_list) +dri2_create_context(_EGLDisplay *disp, _EGLConfig *conf, + _EGLContext *share_list, const EGLint *attrib_list) { struct dri2_egl_context *dri2_ctx; struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); @@ -957,8 +1477,9 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, struct dri2_egl_config *dri2_config = dri2_egl_config(conf); const __DRIconfig *dri_config; int api; - - (void) drv; + unsigned error; + unsigned num_attribs = NUM_ATTRIBS; + uint32_t ctx_attribs[NUM_ATTRIBS]; dri2_ctx = malloc(sizeof *dri2_ctx); if (!dri2_ctx) { @@ -969,6 +1490,31 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, if (!_eglInitContext(&dri2_ctx->base, disp, conf, attrib_list)) goto cleanup; + /* The EGL_EXT_create_context_robustness spec says: + * + * "Add to the eglCreateContext context creation errors: [...] + * + * * If the reset notification behavior of and the + * newly created context are different then an EGL_BAD_MATCH error is + * generated." + */ + if (share_list && share_list->ResetNotificationStrategy != + dri2_ctx->base.ResetNotificationStrategy) { + _eglError(EGL_BAD_MATCH, "eglCreateContext"); + goto cleanup; + } + + /* The EGL_KHR_create_context_no_error spec says: + * + * "BAD_MATCH is generated if the value of EGL_CONTEXT_OPENGL_NO_ERROR_KHR + * used to create does not match the value of + * EGL_CONTEXT_OPENGL_NO_ERROR_KHR for the context being created." + */ + if (share_list && share_list->NoError != dri2_ctx->base.NoError) { + _eglError(EGL_BAD_MATCH, "eglCreateContext"); + goto cleanup; + } + switch (dri2_ctx->base.ClientAPI) { case EGL_OPENGL_ES_API: switch (dri2_ctx->base.ClientMajorVersion) { @@ -993,6 +1539,9 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, && dri2_ctx->base.ClientMinorVersion >= 2)) && dri2_ctx->base.Profile == EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR) api = __DRI_API_OPENGL_CORE; + else if (dri2_ctx->base.ClientMajorVersion == 3 && + dri2_ctx->base.ClientMinorVersion == 1) + api = __DRI_API_OPENGL_CORE; else api = __DRI_API_OPENGL; break; @@ -1010,32 +1559,33 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, * doubleBufferMode check in * src/mesa/main/context.c:check_compatible() */ - if (dri2_config->dri_double_config[0]) - dri_config = dri2_config->dri_double_config[0]; + if (dri2_config->dri_config[1][0]) + dri_config = dri2_config->dri_config[1][0]; else - dri_config = dri2_config->dri_single_config[0]; - - /* EGL_WINDOW_BIT is set only when there is a dri_double_config. This - * makes sure the back buffer will always be used. - */ - if (conf->SurfaceType & EGL_WINDOW_BIT) - dri2_ctx->base.WindowRenderBuffer = EGL_BACK_BUFFER; + dri_config = dri2_config->dri_config[0][0]; } else dri_config = NULL; - if (dri2_dpy->dri2) { - if (dri2_dpy->dri2->base.version >= 3) { - unsigned error; - unsigned num_attribs = 8; - uint32_t ctx_attribs[8]; - - if (!dri2_fill_context_attribs(dri2_ctx, dri2_dpy, ctx_attribs, - &num_attribs)) - goto cleanup; + if (!dri2_fill_context_attribs(dri2_ctx, dri2_dpy, ctx_attribs, + &num_attribs)) + goto cleanup; - dri2_ctx->dri_context = - dri2_dpy->dri2->createContextAttribs(dri2_dpy->dri_screen, + if (dri2_dpy->image_driver) { + dri2_ctx->dri_context = + dri2_dpy->image_driver->createContextAttribs(dri2_dpy->dri_screen, + api, + dri_config, + shared, + num_attribs / 2, + ctx_attribs, + & error, + dri2_ctx); + dri2_create_context_attribs_error(error); + } else if (dri2_dpy->dri2) { + if (dri2_dpy->dri2->base.version >= 3) { + dri2_ctx->dri_context = + dri2_dpy->dri2->createContextAttribs(dri2_dpy->dri_screen, api, dri_config, shared, @@ -1043,26 +1593,18 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, ctx_attribs, & error, dri2_ctx); - dri2_create_context_attribs_error(error); + dri2_create_context_attribs_error(error); } else { - dri2_ctx->dri_context = - dri2_dpy->dri2->createNewContextForAPI(dri2_dpy->dri_screen, - api, - dri_config, + dri2_ctx->dri_context = + dri2_dpy->dri2->createNewContextForAPI(dri2_dpy->dri_screen, + api, + dri_config, shared, - dri2_ctx); + dri2_ctx); } } else { assert(dri2_dpy->swrast); if (dri2_dpy->swrast->base.version >= 3) { - unsigned error; - unsigned num_attribs = 8; - uint32_t ctx_attribs[8]; - - if (!dri2_fill_context_attribs(dri2_ctx, dri2_dpy, ctx_attribs, - &num_attribs)) - goto cleanup; - dri2_ctx->dri_context = dri2_dpy->swrast->createContextAttribs(dri2_dpy->dri_screen, api, @@ -1094,10 +1636,10 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, } /** - * Called via eglDestroyContext(), drv->API.DestroyContext(). + * Called via eglDestroyContext(), drv->DestroyContext(). */ static EGLBoolean -dri2_destroy_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx) +dri2_destroy_context(_EGLDisplay *disp, _EGLContext *ctx) { struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); @@ -1110,121 +1652,295 @@ dri2_destroy_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx) return EGL_TRUE; } +EGLBoolean +dri2_init_surface(_EGLSurface *surf, _EGLDisplay *disp, EGLint type, + _EGLConfig *conf, const EGLint *attrib_list, + EGLBoolean enable_out_fence, void *native_surface) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + dri2_surf->out_fence_fd = -1; + dri2_surf->enable_out_fence = false; + if (dri2_dpy->fence && dri2_dpy->fence->base.version >= 2 && + dri2_dpy->fence->get_capabilities && + (dri2_dpy->fence->get_capabilities(dri2_dpy->dri_screen) & + __DRI_FENCE_CAP_NATIVE_FD)) { + dri2_surf->enable_out_fence = enable_out_fence; + } + + return _eglInitSurface(surf, disp, type, conf, attrib_list, native_surface); +} + +static void +dri2_surface_set_out_fence_fd( _EGLSurface *surf, int fence_fd) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + if (dri2_surf->out_fence_fd >= 0) + close(dri2_surf->out_fence_fd); + + dri2_surf->out_fence_fd = fence_fd; +} + +void +dri2_fini_surface(_EGLSurface *surf) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + dri2_surface_set_out_fence_fd(surf, -1); + dri2_surf->enable_out_fence = false; +} + +static EGLBoolean +dri2_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + if (!_eglPutSurface(surf)) + return EGL_TRUE; + + return dri2_dpy->vtbl->destroy_surface(disp, surf); +} + +static void +dri2_surf_update_fence_fd(_EGLContext *ctx, + _EGLDisplay *disp, _EGLSurface *surf) +{ + __DRIcontext *dri_ctx = dri2_egl_context(ctx)->dri_context; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + int fence_fd = -1; + void *fence; + + if (!dri2_surf->enable_out_fence) + return; + + fence = dri2_dpy->fence->create_fence_fd(dri_ctx, -1); + if (fence) { + fence_fd = dri2_dpy->fence->get_fence_fd(dri2_dpy->dri_screen, + fence); + dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, fence); + } + dri2_surface_set_out_fence_fd(surf, fence_fd); +} + +EGLBoolean +dri2_create_drawable(struct dri2_egl_display *dri2_dpy, + const __DRIconfig *config, + struct dri2_egl_surface *dri2_surf, + void *loaderPrivate) +{ + __DRIcreateNewDrawableFunc createNewDrawable; + + if (dri2_dpy->image_driver) + createNewDrawable = dri2_dpy->image_driver->createNewDrawable; + else if (dri2_dpy->dri2) + createNewDrawable = dri2_dpy->dri2->createNewDrawable; + else if (dri2_dpy->swrast) + createNewDrawable = dri2_dpy->swrast->createNewDrawable; + else + return _eglError(EGL_BAD_ALLOC, "no createNewDrawable"); + + dri2_surf->dri_drawable = createNewDrawable(dri2_dpy->dri_screen, + config, loaderPrivate); + if (dri2_surf->dri_drawable == NULL) + return _eglError(EGL_BAD_ALLOC, "createNewDrawable"); + + return EGL_TRUE; +} + /** - * Called via eglMakeCurrent(), drv->API.MakeCurrent(). + * Called via eglMakeCurrent(), drv->MakeCurrent(). */ static EGLBoolean -dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, - _EGLSurface *rsurf, _EGLContext *ctx) +dri2_make_current(_EGLDisplay *disp, _EGLSurface *dsurf, + _EGLSurface *rsurf, _EGLContext *ctx) { - struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv); struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - struct dri2_egl_surface *dri2_dsurf = dri2_egl_surface(dsurf); - struct dri2_egl_surface *dri2_rsurf = dri2_egl_surface(rsurf); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + _EGLDisplay *old_disp = NULL; + struct dri2_egl_display *old_dri2_dpy = NULL; _EGLContext *old_ctx; _EGLSurface *old_dsurf, *old_rsurf; + _EGLSurface *tmp_dsurf, *tmp_rsurf; __DRIdrawable *ddraw, *rdraw; __DRIcontext *cctx; + EGLint egl_error = EGL_SUCCESS; + + if (!dri2_dpy) + return _eglError(EGL_NOT_INITIALIZED, "eglMakeCurrent"); - /* make new bindings */ + /* make new bindings, set the EGL error otherwise */ if (!_eglBindContext(ctx, dsurf, rsurf, &old_ctx, &old_dsurf, &old_rsurf)) return EGL_FALSE; - /* flush before context switch */ - if (old_ctx && dri2_drv->glFlush) - dri2_drv->glFlush(); - - ddraw = (dri2_dsurf) ? dri2_dsurf->dri_drawable : NULL; - rdraw = (dri2_rsurf) ? dri2_rsurf->dri_drawable : NULL; - cctx = (dri2_ctx) ? dri2_ctx->dri_context : NULL; - if (old_ctx) { __DRIcontext *old_cctx = dri2_egl_context(old_ctx)->dri_context; + old_disp = old_ctx->Resource.Display; + old_dri2_dpy = dri2_egl_display(old_disp); + + /* flush before context switch */ + dri2_gl_flush(); + + if (old_dsurf) + dri2_surf_update_fence_fd(old_ctx, disp, old_dsurf); + + /* Disable shared buffer mode */ + if (old_dsurf && _eglSurfaceInSharedBufferMode(old_dsurf) && + old_dri2_dpy->vtbl->set_shared_buffer_mode) { + old_dri2_dpy->vtbl->set_shared_buffer_mode(old_disp, old_dsurf, false); + } + dri2_dpy->core->unbindContext(old_cctx); } - if ((cctx == NULL && ddraw == NULL && rdraw == NULL) || - dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) { - if (old_dsurf) - drv->API.DestroySurface(drv, disp, old_dsurf); - if (old_rsurf) - drv->API.DestroySurface(drv, disp, old_rsurf); - if (old_ctx) - drv->API.DestroyContext(drv, disp, old_ctx); + ddraw = (dsurf) ? dri2_dpy->vtbl->get_dri_drawable(dsurf) : NULL; + rdraw = (rsurf) ? dri2_dpy->vtbl->get_dri_drawable(rsurf) : NULL; + cctx = (dri2_ctx) ? dri2_ctx->dri_context : NULL; - return EGL_TRUE; - } else { - /* undo the previous _eglBindContext */ - _eglBindContext(old_ctx, old_dsurf, old_rsurf, &ctx, &dsurf, &rsurf); - assert(&dri2_ctx->base == ctx && - &dri2_dsurf->base == dsurf && - &dri2_rsurf->base == rsurf); + if (cctx || ddraw || rdraw) { + if (!dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) { + _EGLContext *tmp_ctx; - _eglPutSurface(dsurf); - _eglPutSurface(rsurf); - _eglPutContext(ctx); + /* dri2_dpy->core->bindContext failed. We cannot tell for sure why, but + * setting the error to EGL_BAD_MATCH is surely better than leaving it + * as EGL_SUCCESS. + */ + egl_error = EGL_BAD_MATCH; + + /* undo the previous _eglBindContext */ + _eglBindContext(old_ctx, old_dsurf, old_rsurf, &ctx, &tmp_dsurf, &tmp_rsurf); + assert(&dri2_ctx->base == ctx && + tmp_dsurf == dsurf && + tmp_rsurf == rsurf); + + _eglPutSurface(dsurf); + _eglPutSurface(rsurf); + _eglPutContext(ctx); + + _eglPutSurface(old_dsurf); + _eglPutSurface(old_rsurf); + _eglPutContext(old_ctx); + + ddraw = (old_dsurf) ? dri2_dpy->vtbl->get_dri_drawable(old_dsurf) : NULL; + rdraw = (old_rsurf) ? dri2_dpy->vtbl->get_dri_drawable(old_rsurf) : NULL; + cctx = (old_ctx) ? dri2_egl_context(old_ctx)->dri_context : NULL; + + /* undo the previous dri2_dpy->core->unbindContext */ + if (dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) { + if (old_dsurf && _eglSurfaceInSharedBufferMode(old_dsurf) && + old_dri2_dpy->vtbl->set_shared_buffer_mode) { + old_dri2_dpy->vtbl->set_shared_buffer_mode(old_disp, old_dsurf, true); + } + + return _eglError(egl_error, "eglMakeCurrent"); + } + + /* We cannot restore the same state as it was before calling + * eglMakeCurrent() and the spec isn't clear about what to do. We + * can prevent EGL from calling into the DRI driver with no DRI + * context bound. + */ + dsurf = rsurf = NULL; + ctx = NULL; - _eglPutSurface(old_dsurf); - _eglPutSurface(old_rsurf); - _eglPutContext(old_ctx); + _eglBindContext(ctx, dsurf, rsurf, &tmp_ctx, &tmp_dsurf, &tmp_rsurf); + assert(tmp_ctx == old_ctx && tmp_dsurf == old_dsurf && + tmp_rsurf == old_rsurf); - return EGL_FALSE; + _eglLog(_EGL_WARNING, "DRI2: failed to rebind the previous context"); + } else { + /* dri2_dpy->core->bindContext succeeded, so take a reference on the + * dri2_dpy. This prevents dri2_dpy from being reinitialized when a + * EGLDisplay is terminated and then initialized again while a + * context is still bound. See dri2_intitialize() for a more in depth + * explanation. */ + dri2_dpy->ref_count++; + } + } + + dri2_destroy_surface(disp, old_dsurf); + dri2_destroy_surface(disp, old_rsurf); + + if (old_ctx) { + dri2_destroy_context(disp, old_ctx); + dri2_display_release(old_disp); + } + + if (egl_error != EGL_SUCCESS) + return _eglError(egl_error, "eglMakeCurrent"); + + if (dsurf && _eglSurfaceHasMutableRenderBuffer(dsurf) && + dri2_dpy->vtbl->set_shared_buffer_mode) { + /* Always update the shared buffer mode. This is obviously needed when + * the active EGL_RENDER_BUFFER is EGL_SINGLE_BUFFER. When + * EGL_RENDER_BUFFER is EGL_BACK_BUFFER, the update protects us in the + * case where external non-EGL API may have changed window's shared + * buffer mode since we last saw it. + */ + bool mode = (dsurf->ActiveRenderBuffer == EGL_SINGLE_BUFFER); + dri2_dpy->vtbl->set_shared_buffer_mode(disp, dsurf, mode); } + + return EGL_TRUE; +} + +__DRIdrawable * +dri2_surface_get_dri_drawable(_EGLSurface *surf) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + return dri2_surf->dri_drawable; } /* - * Called from eglGetProcAddress() via drv->API.GetProcAddress(). + * Called from eglGetProcAddress() via drv->GetProcAddress(). */ static _EGLProc -dri2_get_proc_address(_EGLDriver *drv, const char *procname) +dri2_get_proc_address(const _EGLDriver *drv, const char *procname) { - struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv); - - return dri2_drv->get_proc_address(procname); + return _glapi_get_proc_address(procname); } static _EGLSurface* -dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *dpy, - _EGLConfig *conf, void *native_window, - const EGLint *attrib_list) +dri2_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf, + void *native_window, const EGLint *attrib_list) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->create_window_surface(drv, dpy, conf, native_window, + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + return dri2_dpy->vtbl->create_window_surface(disp, conf, native_window, attrib_list); } static _EGLSurface* -dri2_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *dpy, - _EGLConfig *conf, void *native_pixmap, - const EGLint *attrib_list) +dri2_create_pixmap_surface(_EGLDisplay *disp, _EGLConfig *conf, + void *native_pixmap, const EGLint *attrib_list) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->create_pixmap_surface(drv, dpy, conf, native_pixmap, + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->create_pixmap_surface) + return NULL; + return dri2_dpy->vtbl->create_pixmap_surface(disp, conf, native_pixmap, attrib_list); } static _EGLSurface* -dri2_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *dpy, - _EGLConfig *conf, const EGLint *attrib_list) -{ - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->create_pbuffer_surface(drv, dpy, conf, attrib_list); -} - -static EGLBoolean -dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf) +dri2_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf, + const EGLint *attrib_list) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->destroy_surface(drv, dpy, surf); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->create_pbuffer_surface) + return NULL; + return dri2_dpy->vtbl->create_pbuffer_surface(disp, conf, attrib_list); } static EGLBoolean -dri2_swap_interval(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf, +dri2_swap_interval(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint interval) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->swap_interval(drv, dpy, surf, interval); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->swap_interval) + return EGL_TRUE; + return dri2_dpy->vtbl->swap_interval(drv, disp, surf, interval); } /** @@ -1235,7 +1951,7 @@ void dri2_flush_drawable_for_swapbuffers(_EGLDisplay *disp, _EGLSurface *draw) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw); + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(draw); if (dri2_dpy->flush) { if (dri2_dpy->flush->base.version >= 4) { @@ -1253,69 +1969,137 @@ dri2_flush_drawable_for_swapbuffers(_EGLDisplay *disp, _EGLSurface *draw) * after calling eglSwapBuffers." */ dri2_dpy->flush->flush_with_flags(dri2_ctx->dri_context, - dri2_surf->dri_drawable, + dri_drawable, __DRI2_FLUSH_DRAWABLE | __DRI2_FLUSH_INVALIDATE_ANCILLARY, __DRI2_THROTTLE_SWAPBUFFER); } else { - dri2_dpy->flush->flush(dri2_surf->dri_drawable); + dri2_dpy->flush->flush(dri_drawable); } } } static EGLBoolean -dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf) +dri2_swap_buffers(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->swap_buffers(drv, dpy, surf); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + _EGLContext *ctx = _eglGetCurrentContext(); + EGLBoolean ret; + + if (ctx && surf) + dri2_surf_update_fence_fd(ctx, disp, surf); + ret = dri2_dpy->vtbl->swap_buffers(drv, disp, surf); + + /* SwapBuffers marks the end of the frame; reset the damage region for + * use again next time. + */ + if (ret && dri2_dpy->buffer_damage && + dri2_dpy->buffer_damage->set_damage_region) + dri2_dpy->buffer_damage->set_damage_region(dri_drawable, 0, NULL); + + return ret; } static EGLBoolean -dri2_swap_buffers_with_damage(_EGLDriver *drv, _EGLDisplay *dpy, +dri2_swap_buffers_with_damage(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, const EGLint *rects, EGLint n_rects) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->swap_buffers_with_damage(drv, dpy, surf, - rects, n_rects); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + _EGLContext *ctx = _eglGetCurrentContext(); + EGLBoolean ret; + + if (ctx && surf) + dri2_surf_update_fence_fd(ctx, disp, surf); + if (dri2_dpy->vtbl->swap_buffers_with_damage) + ret = dri2_dpy->vtbl->swap_buffers_with_damage(drv, disp, surf, + rects, n_rects); + else + ret = dri2_dpy->vtbl->swap_buffers(drv, disp, surf); + + /* SwapBuffers marks the end of the frame; reset the damage region for + * use again next time. + */ + if (ret && dri2_dpy->buffer_damage && + dri2_dpy->buffer_damage->set_damage_region) + dri2_dpy->buffer_damage->set_damage_region(dri_drawable, 0, NULL); + + return ret; } static EGLBoolean -dri2_swap_buffers_region(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf, +dri2_swap_buffers_region(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint numRects, const EGLint *rects) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->swap_buffers_region(drv, dpy, surf, numRects, rects); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + EGLBoolean ret; + + if (!dri2_dpy->vtbl->swap_buffers_region) + return EGL_FALSE; + ret = dri2_dpy->vtbl->swap_buffers_region(drv, disp, surf, numRects, rects); + + /* SwapBuffers marks the end of the frame; reset the damage region for + * use again next time. + */ + if (ret && dri2_dpy->buffer_damage && + dri2_dpy->buffer_damage->set_damage_region) + dri2_dpy->buffer_damage->set_damage_region(dri_drawable, 0, NULL); + + return ret; +} + +static EGLBoolean +dri2_set_damage_region(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, + EGLint *rects, EGLint n_rects) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + __DRIdrawable *drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + + if (!dri2_dpy->buffer_damage || !dri2_dpy->buffer_damage->set_damage_region) + return EGL_FALSE; + + dri2_dpy->buffer_damage->set_damage_region(drawable, n_rects, rects); + return EGL_TRUE; } static EGLBoolean -dri2_post_sub_buffer(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf, +dri2_post_sub_buffer(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint x, EGLint y, EGLint width, EGLint height) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->post_sub_buffer(drv, dpy, surf, x, y, width, height); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->post_sub_buffer) + return EGL_FALSE; + return dri2_dpy->vtbl->post_sub_buffer(drv, disp, surf, x, y, width, height); } static EGLBoolean -dri2_copy_buffers(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf, +dri2_copy_buffers(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, void *native_pixmap_target) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->copy_buffers(drv, dpy, surf, native_pixmap_target); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->copy_buffers) + return _eglError(EGL_BAD_NATIVE_PIXMAP, "no support for native pixmaps"); + return dri2_dpy->vtbl->copy_buffers(drv, disp, surf, native_pixmap_target); } static EGLint -dri2_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf) +dri2_query_buffer_age(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->query_buffer_age(drv, dpy, surf); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->query_buffer_age) + return 0; + return dri2_dpy->vtbl->query_buffer_age(drv, disp, surf); } static EGLBoolean -dri2_wait_client(_EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx) +dri2_wait_client(const _EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - struct dri2_egl_surface *dri2_surf = dri2_egl_surface(ctx->DrawSurface); + _EGLSurface *surf = ctx->DrawSurface; + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); (void) drv; @@ -1323,13 +2107,13 @@ dri2_wait_client(_EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx) * we need to copy fake to real here.*/ if (dri2_dpy->flush != NULL) - dri2_dpy->flush->flush(dri2_surf->dri_drawable); + dri2_dpy->flush->flush(dri_drawable); return EGL_TRUE; } static EGLBoolean -dri2_wait_native(_EGLDriver *drv, _EGLDisplay *disp, EGLint engine) +dri2_wait_native(const _EGLDriver *drv, _EGLDisplay *disp, EGLint engine) { (void) drv; (void) disp; @@ -1342,14 +2126,14 @@ dri2_wait_native(_EGLDriver *drv, _EGLDisplay *disp, EGLint engine) } static EGLBoolean -dri2_bind_tex_image(_EGLDriver *drv, - _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) +dri2_bind_tex_image(const _EGLDriver *drv, + _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); struct dri2_egl_context *dri2_ctx; _EGLContext *ctx; GLint format, target; + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); ctx = _eglGetCurrentContext(); dri2_ctx = dri2_egl_context(ctx); @@ -1357,7 +2141,7 @@ dri2_bind_tex_image(_EGLDriver *drv, if (!_eglBindTexImage(drv, disp, surf, buffer)) return EGL_FALSE; - switch (dri2_surf->base.TextureFormat) { + switch (surf->TextureFormat) { case EGL_TEXTURE_RGB: format = __DRI_TEXTURE_FORMAT_RGB; break; @@ -1369,7 +2153,7 @@ dri2_bind_tex_image(_EGLDriver *drv, format = __DRI_TEXTURE_FORMAT_RGBA; } - switch (dri2_surf->base.TextureTarget) { + switch (surf->TextureTarget) { case EGL_TEXTURE_2D: target = GL_TEXTURE_2D; break; @@ -1378,22 +2162,22 @@ dri2_bind_tex_image(_EGLDriver *drv, assert(!"Unexpected texture target in dri2_bind_tex_image()"); } - (*dri2_dpy->tex_buffer->setTexBuffer2)(dri2_ctx->dri_context, - target, format, - dri2_surf->dri_drawable); + dri2_dpy->tex_buffer->setTexBuffer2(dri2_ctx->dri_context, + target, format, + dri_drawable); return EGL_TRUE; } static EGLBoolean -dri2_release_tex_image(_EGLDriver *drv, - _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) +dri2_release_tex_image(const _EGLDriver *drv, + _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); struct dri2_egl_context *dri2_ctx; _EGLContext *ctx; GLint target; + __DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); ctx = _eglGetCurrentContext(); dri2_ctx = dri2_egl_context(ctx); @@ -1401,31 +2185,30 @@ dri2_release_tex_image(_EGLDriver *drv, if (!_eglReleaseTexImage(drv, disp, surf, buffer)) return EGL_FALSE; - switch (dri2_surf->base.TextureTarget) { + switch (surf->TextureTarget) { case EGL_TEXTURE_2D: target = GL_TEXTURE_2D; break; default: - assert(0); + assert(!"missing texture target"); } if (dri2_dpy->tex_buffer->base.version >= 3 && dri2_dpy->tex_buffer->releaseTexBuffer != NULL) { - (*dri2_dpy->tex_buffer->releaseTexBuffer)(dri2_ctx->dri_context, - target, - dri2_surf->dri_drawable); + dri2_dpy->tex_buffer->releaseTexBuffer(dri2_ctx->dri_context, + target, dri_drawable); } return EGL_TRUE; } static _EGLImage* -dri2_create_image(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx, +dri2_create_image(const _EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attr_list) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->create_image(drv, dpy, ctx, target, buffer, + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + return dri2_dpy->vtbl->create_image(drv, disp, ctx, target, buffer, attr_list); } @@ -1445,20 +2228,40 @@ dri2_create_image_from_dri(_EGLDisplay *disp, __DRIimage *dri_image) return NULL; } - if (!_eglInitImage(&dri2_img->base, disp)) { - free(dri2_img); - return NULL; - } + _eglInitImage(&dri2_img->base, disp); dri2_img->dri_image = dri_image; return &dri2_img->base; } +/** + * Translate a DRI Image extension error code into an EGL error code. + */ +static EGLint +egl_error_from_dri_image_error(int dri_error) +{ + switch (dri_error) { + case __DRI_IMAGE_ERROR_SUCCESS: + return EGL_SUCCESS; + case __DRI_IMAGE_ERROR_BAD_ALLOC: + return EGL_BAD_ALLOC; + case __DRI_IMAGE_ERROR_BAD_MATCH: + return EGL_BAD_MATCH; + case __DRI_IMAGE_ERROR_BAD_PARAMETER: + return EGL_BAD_PARAMETER; + case __DRI_IMAGE_ERROR_BAD_ACCESS: + return EGL_BAD_ACCESS; + default: + assert(!"unknown dri_error code"); + return EGL_BAD_ALLOC; + } +} + static _EGLImage * dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx, - EGLClientBuffer buffer, - const EGLint *attr_list) + EGLClientBuffer buffer, + const EGLint *attr_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); @@ -1470,9 +2273,32 @@ dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx, return EGL_NO_IMAGE_KHR; } - dri_image = - dri2_dpy->image->createImageFromRenderbuffer(dri2_ctx->dri_context, - renderbuffer, NULL); + if (!disp->Extensions.KHR_gl_renderbuffer_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + if (dri2_dpy->image->base.version >= 17 && + dri2_dpy->image->createImageFromRenderbuffer2) { + unsigned error = ~0; + + dri_image = dri2_dpy->image->createImageFromRenderbuffer2( + dri2_ctx->dri_context, renderbuffer, NULL, &error); + + assert(!!dri_image == (error == __DRI_IMAGE_ERROR_SUCCESS)); + + if (!dri_image) { + _eglError(egl_error_from_dri_image_error(error), "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + } else { + dri_image = dri2_dpy->image->createImageFromRenderbuffer( + dri2_ctx->dri_context, renderbuffer, NULL); + if (!dri_image) { + _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + } return dri2_create_image_from_dri(disp, dri_image); } @@ -1499,15 +2325,14 @@ static const struct wl_drm_components_descriptor { static _EGLImage * dri2_create_image_wayland_wl_buffer(_EGLDisplay *disp, _EGLContext *ctx, - EGLClientBuffer _buffer, - const EGLint *attr_list) + EGLClientBuffer _buffer, + const EGLint *attr_list) { struct wl_drm_buffer *buffer; struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); const struct wl_drm_components_descriptor *f; __DRIimage *dri_image; _EGLImageAttribs attrs; - EGLint err; int32_t plane; buffer = wayland_drm_buffer_get(dri2_dpy->wl_server_drm, @@ -1515,13 +2340,10 @@ dri2_create_image_wayland_wl_buffer(_EGLDisplay *disp, _EGLContext *ctx, if (!buffer) return NULL; - err = _eglParseImageAttribList(&attrs, disp, attr_list); - plane = attrs.PlaneWL; - if (err != EGL_SUCCESS) { - _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer"); + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) return NULL; - } + plane = attrs.PlaneWL; f = buffer->driver_format; if (plane < 0 || plane >= f->nplanes) { _eglError(EGL_BAD_PARAMETER, @@ -1530,7 +2352,8 @@ dri2_create_image_wayland_wl_buffer(_EGLDisplay *disp, _EGLContext *ctx, } dri_image = dri2_dpy->image->fromPlanar(buffer->driver_buffer, plane, NULL); - + if (dri_image == NULL && plane == 0) + dri_image = dri2_dpy->image->dupImage(buffer->driver_buffer, NULL); if (dri_image == NULL) { _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer"); return NULL; @@ -1541,12 +2364,14 @@ dri2_create_image_wayland_wl_buffer(_EGLDisplay *disp, _EGLContext *ctx, #endif static EGLBoolean -dri2_get_sync_values_chromium(_EGLDisplay *dpy, _EGLSurface *surf, +dri2_get_sync_values_chromium(_EGLDisplay *disp, _EGLSurface *surf, EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->get_sync_values(dpy, surf, ust, msc, sbc); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->get_sync_values) + return EGL_FALSE; + return dri2_dpy->vtbl->get_sync_values(disp, surf, ust, msc, sbc); } /** @@ -1556,42 +2381,17 @@ dri2_get_sync_values_chromium(_EGLDisplay *dpy, _EGLSurface *surf, static void dri2_create_image_khr_texture_error(int dri_error) { - EGLint egl_error; - - switch (dri_error) { - case __DRI_IMAGE_ERROR_SUCCESS: - return; - - case __DRI_IMAGE_ERROR_BAD_ALLOC: - egl_error = EGL_BAD_ALLOC; - break; - - case __DRI_IMAGE_ERROR_BAD_MATCH: - egl_error = EGL_BAD_MATCH; - break; + EGLint egl_error = egl_error_from_dri_image_error(dri_error); - case __DRI_IMAGE_ERROR_BAD_PARAMETER: - egl_error = EGL_BAD_PARAMETER; - break; - - case __DRI_IMAGE_ERROR_BAD_ACCESS: - egl_error = EGL_BAD_ACCESS; - break; - - default: - assert(0); - egl_error = EGL_BAD_MATCH; - break; - } - - _eglError(egl_error, "dri2_create_image_khr_texture"); + if (egl_error != EGL_SUCCESS) + _eglError(egl_error, "dri2_create_image_khr_texture"); } static _EGLImage * dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx, - EGLenum target, - EGLClientBuffer buffer, - const EGLint *attr_list) + EGLenum target, + EGLClientBuffer buffer, + const EGLint *attr_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); @@ -1607,35 +2407,43 @@ dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx, return EGL_NO_IMAGE_KHR; } - if (_eglParseImageAttribList(&attrs, disp, attr_list) != EGL_SUCCESS) + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) return EGL_NO_IMAGE_KHR; switch (target) { case EGL_GL_TEXTURE_2D_KHR: + if (!disp->Extensions.KHR_gl_texture_2D_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } depth = 0; gl_target = GL_TEXTURE_2D; break; case EGL_GL_TEXTURE_3D_KHR: - if (disp->Extensions.KHR_gl_texture_3D_image) { - depth = attrs.GLTextureZOffset; - gl_target = GL_TEXTURE_3D; - break; - } - else { + if (!disp->Extensions.KHR_gl_texture_3D_image) { _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); return EGL_NO_IMAGE_KHR; } + + depth = attrs.GLTextureZOffset; + gl_target = GL_TEXTURE_3D; + break; case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR: case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR: case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR: case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: + if (!disp->Extensions.KHR_gl_texture_cubemap_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + depth = target - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR; gl_target = GL_TEXTURE_CUBE_MAP; break; default: - _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + unreachable("Unexpected target in dri2_create_image_khr_texture()"); return EGL_NO_IMAGE_KHR; } @@ -1645,11 +2453,7 @@ dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx, return EGL_NO_IMAGE_KHR; } - if (!_eglInitImage(&dri2_img->base, disp)) { - _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); - free(dri2_img); - return EGL_NO_IMAGE_KHR; - } + _eglInitImage(&dri2_img->base, disp); dri2_img->dri_image = dri2_dpy->image->createImageFromTexture(dri2_ctx->dri_context, @@ -1668,34 +2472,45 @@ dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx, return &dri2_img->base; } +static EGLBoolean +dri2_query_surface(_EGLDisplay *disp, _EGLSurface *surf, + EGLint attribute, EGLint *value) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->query_surface) + return _eglQuerySurface(disp, surf, attribute, value); + return dri2_dpy->vtbl->query_surface(disp, surf, attribute, value); +} + static struct wl_buffer* -dri2_create_wayland_buffer_from_image(_EGLDriver *drv, _EGLDisplay *dpy, +dri2_create_wayland_buffer_from_image(const _EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - return dri2_dpy->vtbl->create_wayland_buffer_from_image(drv, dpy, img); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->create_wayland_buffer_from_image) + return NULL; + return dri2_dpy->vtbl->create_wayland_buffer_from_image(drv, disp, img); } #ifdef HAVE_LIBDRM static _EGLImage * dri2_create_image_mesa_drm_buffer(_EGLDisplay *disp, _EGLContext *ctx, - EGLClientBuffer buffer, const EGLint *attr_list) + EGLClientBuffer buffer, const EGLint *attr_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - EGLint format, name, pitch, err; + EGLint format, name, pitch; _EGLImageAttribs attrs; __DRIimage *dri_image; name = (EGLint) (uintptr_t) buffer; - err = _eglParseImageAttribList(&attrs, disp, attr_list); - if (err != EGL_SUCCESS) + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) return NULL; if (attrs.Width <= 0 || attrs.Height <= 0 || attrs.DRMBufferStrideMESA <= 0) { _eglError(EGL_BAD_PARAMETER, - "bad width, height or stride"); + "bad width, height or stride"); return NULL; } @@ -1706,18 +2521,18 @@ dri2_create_image_mesa_drm_buffer(_EGLDisplay *disp, _EGLContext *ctx, break; default: _eglError(EGL_BAD_PARAMETER, - "dri2_create_image_khr: unsupported pixmap depth"); + "dri2_create_image_khr: unsupported pixmap depth"); return NULL; } dri_image = dri2_dpy->image->createImageFromName(dri2_dpy->dri_screen, - attrs.Width, - attrs.Height, - format, - name, - pitch, - NULL); + attrs.Width, + attrs.Height, + format, + name, + pitch, + NULL); return dri2_create_image_from_dri(disp, dri_image); } @@ -1725,8 +2540,6 @@ dri2_create_image_mesa_drm_buffer(_EGLDisplay *disp, _EGLContext *ctx, static EGLBoolean dri2_check_dma_buf_attribs(const _EGLImageAttribs *attrs) { - unsigned i; - /** * The spec says: * @@ -1744,10 +2557,8 @@ dri2_check_dma_buf_attribs(const _EGLImageAttribs *attrs) * incomplete, EGL_BAD_PARAMETER is generated." */ if (attrs->Width <= 0 || attrs->Height <= 0 || - !attrs->DMABufFourCC.IsPresent) { - _eglError(EGL_BAD_PARAMETER, "attribute(s) missing"); - return EGL_FALSE; - } + !attrs->DMABufFourCC.IsPresent) + return _eglError(EGL_BAD_PARAMETER, "attribute(s) missing"); /** * Also: @@ -1756,27 +2567,54 @@ dri2_check_dma_buf_attribs(const _EGLImageAttribs *attrs) * specified for a plane's pitch or offset isn't supported by EGL, * EGL_BAD_ACCESS is generated." */ - for (i = 0; i < ARRAY_SIZE(attrs->DMABufPlanePitches); ++i) { + for (unsigned i = 0; i < ARRAY_SIZE(attrs->DMABufPlanePitches); ++i) { if (attrs->DMABufPlanePitches[i].IsPresent && - attrs->DMABufPlanePitches[i].Value <= 0) { - _eglError(EGL_BAD_ACCESS, "invalid pitch"); - return EGL_FALSE; + attrs->DMABufPlanePitches[i].Value <= 0) + return _eglError(EGL_BAD_ACCESS, "invalid pitch"); + } + + /** + * If is EGL_LINUX_DMA_BUF_EXT, both or neither of the following + * attribute values may be given. + * + * This is referring to EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT and + * EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, and the same for other planes. + */ + for (unsigned i = 0; i < DMA_BUF_MAX_PLANES; ++i) { + if (attrs->DMABufPlaneModifiersLo[i].IsPresent != + attrs->DMABufPlaneModifiersHi[i].IsPresent) + return _eglError(EGL_BAD_PARAMETER, "modifier attribute lo or hi missing"); + } + + /* Although the EGL_EXT_image_dma_buf_import_modifiers spec doesn't + * mandate it, we only accept the same modifier across all planes. */ + for (unsigned i = 1; i < DMA_BUF_MAX_PLANES; ++i) { + if (attrs->DMABufPlaneFds[i].IsPresent) { + if ((attrs->DMABufPlaneModifiersLo[0].IsPresent != + attrs->DMABufPlaneModifiersLo[i].IsPresent) || + (attrs->DMABufPlaneModifiersLo[0].Value != + attrs->DMABufPlaneModifiersLo[i].Value) || + (attrs->DMABufPlaneModifiersHi[0].Value != + attrs->DMABufPlaneModifiersHi[i].Value)) + return _eglError(EGL_BAD_PARAMETER, "modifier attributes not equal"); } } return EGL_TRUE; } -/* Returns the total number of file descriptors. Zero indicates an error. */ +/* Returns the total number of planes for the format or zero if it isn't a + * valid fourcc format. + */ static unsigned -dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) +dri2_num_fourcc_format_planes(EGLint format) { - unsigned i, plane_n; - - switch (attrs->DMABufFourCC.Value) { + switch (format) { case DRM_FORMAT_R8: case DRM_FORMAT_RG88: case DRM_FORMAT_GR88: + case DRM_FORMAT_R16: + case DRM_FORMAT_GR1616: case DRM_FORMAT_RGB332: case DRM_FORMAT_BGR233: case DRM_FORMAT_XRGB4444: @@ -1815,18 +2653,25 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_RGBA1010102: case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: case DRM_FORMAT_UYVY: case DRM_FORMAT_VYUY: - plane_n = 1; - break; + case DRM_FORMAT_AYUV: + case DRM_FORMAT_XYUV8888: + return 1; + case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: - plane_n = 2; - break; + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + return 2; + case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: case DRM_FORMAT_YUV411: @@ -1837,12 +2682,40 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) case DRM_FORMAT_YVU422: case DRM_FORMAT_YUV444: case DRM_FORMAT_YVU444: - plane_n = 3; - break; + return 3; + default: - _eglError(EGL_BAD_ATTRIBUTE, "invalid format"); return 0; } +} + +/* Returns the total number of file descriptors. Zero indicates an error. */ +static unsigned +dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) +{ + unsigned plane_n = dri2_num_fourcc_format_planes(attrs->DMABufFourCC.Value); + if (plane_n == 0) { + _eglError(EGL_BAD_MATCH, "unknown drm fourcc format"); + return 0; + } + + for (unsigned i = plane_n; i < DMA_BUF_MAX_PLANES; i++) { + /** + * The modifiers extension spec says: + * + * "Modifiers may modify any attribute of a buffer import, including + * but not limited to adding extra planes to a format which + * otherwise does not have those planes. As an example, a modifier + * may add a plane for an external compression buffer to a + * single-plane format. The exact meaning and effect of any + * modifier is canonically defined by drm_fourcc.h, not as part of + * this extension." + */ + if (attrs->DMABufPlaneModifiersLo[i].IsPresent && + attrs->DMABufPlaneModifiersHi[i].IsPresent) { + plane_n = i + 1; + } + } /** * The spec says: @@ -1850,7 +2723,7 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) * "* If is EGL_LINUX_DMA_BUF_EXT, and the list of attributes is * incomplete, EGL_BAD_PARAMETER is generated." */ - for (i = 0; i < plane_n; ++i) { + for (unsigned i = 0; i < plane_n; ++i) { if (!attrs->DMABufPlaneFds[i].IsPresent || !attrs->DMABufPlaneOffsets[i].IsPresent || !attrs->DMABufPlanePitches[i].IsPresent) { @@ -1865,9 +2738,9 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) * "If is EGL_LINUX_DMA_BUF_EXT, and the EGL_LINUX_DRM_FOURCC_EXT * attribute indicates a single-plane format, EGL_BAD_ATTRIBUTE is * generated if any of the EGL_DMA_BUF_PLANE1_* or EGL_DMA_BUF_PLANE2_* - * attributes are specified." + * or EGL_DMA_BUF_PLANE3_* attributes are specified." */ - for (i = plane_n; i < 3; ++i) { + for (unsigned i = plane_n; i < DMA_BUF_MAX_PLANES; ++i) { if (attrs->DMABufPlaneFds[i].IsPresent || attrs->DMABufPlaneOffsets[i].IsPresent || attrs->DMABufPlanePitches[i].IsPresent) { @@ -1879,6 +2752,66 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) return plane_n; } +static EGLBoolean +dri2_query_dma_buf_formats(const _EGLDriver *drv, _EGLDisplay *disp, + EGLint max, EGLint *formats, EGLint *count) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (max < 0 || (max > 0 && formats == NULL)) + return _eglError(EGL_BAD_PARAMETER, "invalid value for max count of formats"); + + if (dri2_dpy->image->base.version < 15 || + dri2_dpy->image->queryDmaBufFormats == NULL) + return EGL_FALSE; + + if (!dri2_dpy->image->queryDmaBufFormats(dri2_dpy->dri_screen, max, + formats, count)) + return EGL_FALSE; + + if (max > 0) { + /* Assert that all of the formats returned are actually fourcc formats. + * Some day, if we want the internal interface function to be able to + * return the fake fourcc formats defined in dri_interface.h, we'll have + * to do something more clever here to pair the list down to just real + * fourcc formats so that we don't leak the fake internal ones. + */ + for (int i = 0; i < *count; i++) { + assert(dri2_num_fourcc_format_planes(formats[i]) > 0); + } + } + + return EGL_TRUE; +} + +static EGLBoolean +dri2_query_dma_buf_modifiers(const _EGLDriver *drv, _EGLDisplay *disp, EGLint format, + EGLint max, EGLuint64KHR *modifiers, + EGLBoolean *external_only, EGLint *count) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + if (dri2_num_fourcc_format_planes(format) == 0) + return _eglError(EGL_BAD_PARAMETER, "invalid fourcc format"); + + if (max < 0) + return _eglError(EGL_BAD_PARAMETER, "invalid value for max count of formats"); + + if (max > 0 && modifiers == NULL) + return _eglError(EGL_BAD_PARAMETER, "invalid modifiers array"); + + if (dri2_dpy->image->base.version < 15 || + dri2_dpy->image->queryDmaBufModifiers == NULL) + return EGL_FALSE; + + if (dri2_dpy->image->queryDmaBufModifiers(dri2_dpy->dri_screen, format, + max, modifiers, + (unsigned int *) external_only, + count) == false) + return _eglError(EGL_BAD_PARAMETER, "invalid format"); + + return EGL_TRUE; +} + /** * The spec says: * @@ -1889,20 +2822,20 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) * * Therefore we must never close or otherwise modify the file descriptors. */ -static _EGLImage * +_EGLImage * dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx, - EGLClientBuffer buffer, const EGLint *attr_list) + EGLClientBuffer buffer, const EGLint *attr_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); _EGLImage *res; - EGLint err; _EGLImageAttribs attrs; __DRIimage *dri_image; unsigned num_fds; - unsigned i; - int fds[3]; - int pitches[3]; - int offsets[3]; + int fds[DMA_BUF_MAX_PLANES]; + int pitches[DMA_BUF_MAX_PLANES]; + int offsets[DMA_BUF_MAX_PLANES]; + uint64_t modifier; + bool has_modifier = false; unsigned error; /** @@ -1916,11 +2849,8 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx, return NULL; } - err = _eglParseImageAttribList(&attrs, disp, attr_list); - if (err != EGL_SUCCESS) { - _eglError(err, "bad attribute"); + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) return NULL; - } if (!dri2_check_dma_buf_attribs(&attrs)) return NULL; @@ -1929,22 +2859,50 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx, if (!num_fds) return NULL; - for (i = 0; i < num_fds; ++i) { + for (unsigned i = 0; i < num_fds; ++i) { fds[i] = attrs.DMABufPlaneFds[i].Value; pitches[i] = attrs.DMABufPlanePitches[i].Value; offsets[i] = attrs.DMABufPlaneOffsets[i].Value; } - dri_image = - dri2_dpy->image->createImageFromDmaBufs(dri2_dpy->dri_screen, - attrs.Width, attrs.Height, attrs.DMABufFourCC.Value, - fds, num_fds, pitches, offsets, - attrs.DMABufYuvColorSpaceHint.Value, - attrs.DMABufSampleRangeHint.Value, - attrs.DMABufChromaHorizontalSiting.Value, - attrs.DMABufChromaVerticalSiting.Value, - &error, - NULL); + /* dri2_check_dma_buf_attribs ensures that the modifier, if available, + * will be present in attrs.DMABufPlaneModifiersLo[0] and + * attrs.DMABufPlaneModifiersHi[0] */ + if (attrs.DMABufPlaneModifiersLo[0].IsPresent) { + modifier = combine_u32_into_u64(attrs.DMABufPlaneModifiersHi[0].Value, + attrs.DMABufPlaneModifiersLo[0].Value); + has_modifier = true; + } + + if (has_modifier) { + if (dri2_dpy->image->base.version < 15 || + dri2_dpy->image->createImageFromDmaBufs2 == NULL) { + _eglError(EGL_BAD_MATCH, "unsupported dma_buf format modifier"); + return EGL_NO_IMAGE_KHR; + } + dri_image = + dri2_dpy->image->createImageFromDmaBufs2(dri2_dpy->dri_screen, + attrs.Width, attrs.Height, attrs.DMABufFourCC.Value, + modifier, fds, num_fds, pitches, offsets, + attrs.DMABufYuvColorSpaceHint.Value, + attrs.DMABufSampleRangeHint.Value, + attrs.DMABufChromaHorizontalSiting.Value, + attrs.DMABufChromaVerticalSiting.Value, + &error, + NULL); + } + else { + dri_image = + dri2_dpy->image->createImageFromDmaBufs(dri2_dpy->dri_screen, + attrs.Width, attrs.Height, attrs.DMABufFourCC.Value, + fds, num_fds, pitches, offsets, + attrs.DMABufYuvColorSpaceHint.Value, + attrs.DMABufSampleRangeHint.Value, + attrs.DMABufChromaHorizontalSiting.Value, + attrs.DMABufChromaVerticalSiting.Value, + &error, + NULL); + } dri2_create_image_khr_texture_error(error); if (!dri_image) @@ -1955,42 +2913,28 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx, return res; } static _EGLImage * -dri2_create_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, - const EGLint *attr_list) +dri2_create_drm_image_mesa(const _EGLDriver *drv, _EGLDisplay *disp, + const EGLint *attr_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img; _EGLImageAttribs attrs; unsigned int dri_use, valid_mask; int format; - EGLint err = EGL_SUCCESS; (void) drv; - dri2_img = malloc(sizeof *dri2_img); - if (!dri2_img) { - _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); - return EGL_NO_IMAGE_KHR; - } - if (!attr_list) { - err = EGL_BAD_PARAMETER; - goto cleanup_img; - } - - if (!_eglInitImage(&dri2_img->base, disp)) { - err = EGL_BAD_PARAMETER; - goto cleanup_img; + _eglError(EGL_BAD_PARAMETER, __func__); + return EGL_NO_IMAGE_KHR; } - err = _eglParseImageAttribList(&attrs, disp, attr_list); - if (err != EGL_SUCCESS) - goto cleanup_img; + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) + return EGL_NO_IMAGE_KHR; if (attrs.Width <= 0 || attrs.Height <= 0) { - _eglLog(_EGL_WARNING, "bad width or height (%dx%d)", - attrs.Width, attrs.Height); - goto cleanup_img; + _eglError(EGL_BAD_PARAMETER, __func__); + return EGL_NO_IMAGE_KHR; } switch (attrs.DRMBufferFormatMESA) { @@ -1998,9 +2942,8 @@ dri2_create_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, format = __DRI_IMAGE_FORMAT_ARGB8888; break; default: - _eglLog(_EGL_WARNING, "bad image format value 0x%04x", - attrs.DRMBufferFormatMESA); - goto cleanup_img; + _eglError(EGL_BAD_PARAMETER, __func__); + return EGL_NO_IMAGE_KHR; } valid_mask = @@ -2008,9 +2951,8 @@ dri2_create_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, EGL_DRM_BUFFER_USE_SHARE_MESA | EGL_DRM_BUFFER_USE_CURSOR_MESA; if (attrs.DRMBufferUseMESA & ~valid_mask) { - _eglLog(_EGL_WARNING, "bad image use bit 0x%04x", - attrs.DRMBufferUseMESA & ~valid_mask); - goto cleanup_img; + _eglError(EGL_BAD_PARAMETER, __func__); + return EGL_NO_IMAGE_KHR; } dri_use = 0; @@ -2021,27 +2963,30 @@ dri2_create_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, if (attrs.DRMBufferUseMESA & EGL_DRM_BUFFER_USE_CURSOR_MESA) dri_use |= __DRI_IMAGE_USE_CURSOR; + dri2_img = malloc(sizeof *dri2_img); + if (!dri2_img) { + _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + _eglInitImage(&dri2_img->base, disp); + dri2_img->dri_image = dri2_dpy->image->createImage(dri2_dpy->dri_screen, - attrs.Width, attrs.Height, + attrs.Width, attrs.Height, format, dri_use, dri2_img); if (dri2_img->dri_image == NULL) { - err = EGL_BAD_ALLOC; - goto cleanup_img; + free(dri2_img); + _eglError(EGL_BAD_ALLOC, "dri2_create_drm_image_mesa"); + return EGL_NO_IMAGE_KHR; } return &dri2_img->base; - - cleanup_img: - free(dri2_img); - _eglError(err, "dri2_create_drm_image_mesa"); - - return EGL_NO_IMAGE_KHR; } static EGLBoolean -dri2_export_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img, - EGLint *name, EGLint *handle, EGLint *stride) +dri2_export_drm_image_mesa(const _EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img, + EGLint *name, EGLint *handle, EGLint *stride) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img = dri2_egl_image(img); @@ -2049,67 +2994,129 @@ dri2_export_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img, (void) drv; if (name && !dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_NAME, name)) { - _eglError(EGL_BAD_ALLOC, "dri2_export_drm_image_mesa"); - return EGL_FALSE; - } + __DRI_IMAGE_ATTRIB_NAME, name)) + return _eglError(EGL_BAD_ALLOC, "dri2_export_drm_image_mesa"); if (handle) dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_HANDLE, handle); + __DRI_IMAGE_ATTRIB_HANDLE, handle); if (stride) dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_STRIDE, stride); + __DRI_IMAGE_ATTRIB_STRIDE, stride); return EGL_TRUE; } +/** + * Checks if we can support EGL_MESA_image_dma_buf_export on this image. + + * The spec provides a boolean return for the driver to reject exporting for + * basically any reason, but doesn't specify any particular error cases. For + * now, we just fail if we don't have a DRM fourcc for the format. + */ +static bool +dri2_can_export_dma_buf_image(_EGLDisplay *disp, _EGLImage *img) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + EGLint fourcc; + + if (!dri2_dpy->image->queryImage(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_FOURCC, &fourcc)) { + return false; + } + + return true; +} + static EGLBoolean -dri2_export_dma_buf_image_query_mesa(_EGLDriver *drv, _EGLDisplay *disp, +dri2_export_dma_buf_image_query_mesa(const _EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img, EGLint *fourcc, EGLint *nplanes, EGLuint64KHR *modifiers) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img = dri2_egl_image(img); + int num_planes; (void) drv; + if (!dri2_can_export_dma_buf_image(disp, img)) + return EGL_FALSE; + dri2_dpy->image->queryImage(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_NUM_PLANES, &num_planes); if (nplanes) - dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_NUM_PLANES, nplanes); + *nplanes = num_planes; + if (fourcc) dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_FOURCC, fourcc); + __DRI_IMAGE_ATTRIB_FOURCC, fourcc); + + if (modifiers) { + int mod_hi, mod_lo; + uint64_t modifier = DRM_FORMAT_MOD_INVALID; + bool query; - if (modifiers) - *modifiers = 0; + query = dri2_dpy->image->queryImage(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_MODIFIER_UPPER, + &mod_hi); + query &= dri2_dpy->image->queryImage(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_MODIFIER_LOWER, + &mod_lo); + if (query) + modifier = combine_u32_into_u64 (mod_hi, mod_lo); + + for (int i = 0; i < num_planes; i++) + modifiers[i] = modifier; + } return EGL_TRUE; } static EGLBoolean -dri2_export_dma_buf_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img, +dri2_export_dma_buf_image_mesa(const _EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img, int *fds, EGLint *strides, EGLint *offsets) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img = dri2_egl_image(img); + EGLint nplanes; (void) drv; + if (!dri2_can_export_dma_buf_image(disp, img)) + return EGL_FALSE; + + /* EGL_MESA_image_dma_buf_export spec says: + * "If the number of fds is less than the number of planes, then + * subsequent fd slots should contain -1." + */ + if (fds) { + /* Query nplanes so that we know how big the given array is. */ + dri2_dpy->image->queryImage(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_NUM_PLANES, &nplanes); + memset(fds, -1, nplanes * sizeof(int)); + } + /* rework later to provide multiple fds/strides/offsets */ if (fds) dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_FD, fds); + __DRI_IMAGE_ATTRIB_FD, fds); if (strides) dri2_dpy->image->queryImage(dri2_img->dri_image, - __DRI_IMAGE_ATTRIB_STRIDE, strides); - - if (offsets) - offsets[0] = 0; + __DRI_IMAGE_ATTRIB_STRIDE, strides); + + if (offsets) { + int img_offset; + bool ret = dri2_dpy->image->queryImage(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_OFFSET, &img_offset); + if (ret) + offsets[0] = img_offset; + else + offsets[0] = 0; + } return EGL_TRUE; } @@ -2117,9 +3124,9 @@ dri2_export_dma_buf_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *im #endif _EGLImage * -dri2_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, - _EGLContext *ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint *attr_list) +dri2_create_image_khr(const _EGLDriver *drv, _EGLDisplay *disp, + _EGLContext *ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint *attr_list) { (void) drv; @@ -2131,15 +3138,8 @@ dri2_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: - return dri2_create_image_khr_texture(disp, ctx, target, buffer, attr_list); case EGL_GL_TEXTURE_3D_KHR: - if (disp->Extensions.KHR_gl_texture_3D_image) { - return dri2_create_image_khr_texture(disp, ctx, target, buffer, attr_list); - } - else { - _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); - return EGL_NO_IMAGE_KHR; - } + return dri2_create_image_khr_texture(disp, ctx, target, buffer, attr_list); case EGL_GL_RENDERBUFFER_KHR: return dri2_create_image_khr_renderbuffer(disp, ctx, buffer, attr_list); #ifdef HAVE_LIBDRM @@ -2159,7 +3159,7 @@ dri2_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, } static EGLBoolean -dri2_destroy_image_khr(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *image) +dri2_destroy_image_khr(const _EGLDriver *drv, _EGLDisplay *disp, _EGLImage *image) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img = dri2_egl_image(image); @@ -2181,7 +3181,7 @@ dri2_wl_reference_buffer(void *user_data, uint32_t name, int fd, _EGLDisplay *disp = user_data; struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); __DRIimage *img; - int i, dri_components = 0; + int dri_components = 0; if (fd == -1) img = dri2_dpy->image->createImageFromNames(dri2_dpy->dri_screen, @@ -2208,7 +3208,7 @@ dri2_wl_reference_buffer(void *user_data, uint32_t name, int fd, dri2_dpy->image->queryImage(img, __DRI_IMAGE_ATTRIB_COMPONENTS, &dri_components); buffer->driver_format = NULL; - for (i = 0; i < ARRAY_SIZE(wl_drm_components); i++) + for (int i = 0; i < ARRAY_SIZE(wl_drm_components); i++) if (wl_drm_components[i].dri_components == dri_components) buffer->driver_format = &wl_drm_components[i]; @@ -2227,27 +3227,24 @@ dri2_wl_release_buffer(void *user_data, struct wl_drm_buffer *buffer) dri2_dpy->image->destroyImage(buffer->driver_buffer); } -static struct wayland_drm_callbacks wl_drm_callbacks = { - .authenticate = NULL, - .reference_buffer = dri2_wl_reference_buffer, - .release_buffer = dri2_wl_release_buffer -}; - static EGLBoolean -dri2_bind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp, - struct wl_display *wl_dpy) +dri2_bind_wayland_display_wl(const _EGLDriver *drv, _EGLDisplay *disp, + struct wl_display *wl_dpy) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + const struct wayland_drm_callbacks wl_drm_callbacks = { + .authenticate = (int(*)(void *, uint32_t)) dri2_dpy->vtbl->authenticate, + .reference_buffer = dri2_wl_reference_buffer, + .release_buffer = dri2_wl_release_buffer, + .is_format_supported = dri2_wl_is_format_supported + }; int flags = 0; uint64_t cap; (void) drv; if (dri2_dpy->wl_server_drm) - return EGL_FALSE; - - wl_drm_callbacks.authenticate = - (int(*)(void *, uint32_t)) dri2_dpy->vtbl->authenticate; + return EGL_FALSE; if (drmGetCap(dri2_dpy->fd, DRM_CAP_PRIME, &cap) == 0 && cap == (DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT) && @@ -2256,11 +3253,11 @@ dri2_bind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp, flags |= WAYLAND_DRM_PRIME; dri2_dpy->wl_server_drm = - wayland_drm_init(wl_dpy, dri2_dpy->device_name, + wayland_drm_init(wl_dpy, dri2_dpy->device_name, &wl_drm_callbacks, disp, flags); if (!dri2_dpy->wl_server_drm) - return EGL_FALSE; + return EGL_FALSE; #ifdef HAVE_DRM_PLATFORM /* We have to share the wl_drm instance with gbm, so gbm can convert @@ -2273,15 +3270,15 @@ dri2_bind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp, } static EGLBoolean -dri2_unbind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp, - struct wl_display *wl_dpy) +dri2_unbind_wayland_display_wl(const _EGLDriver *drv, _EGLDisplay *disp, + struct wl_display *wl_dpy) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); (void) drv; if (!dri2_dpy->wl_server_drm) - return EGL_FALSE; + return EGL_FALSE; wayland_drm_uninit(dri2_dpy->wl_server_drm); dri2_dpy->wl_server_drm = NULL; @@ -2290,7 +3287,7 @@ dri2_unbind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp, } static EGLBoolean -dri2_query_wayland_buffer_wl(_EGLDriver *drv, _EGLDisplay *disp, +dri2_query_wayland_buffer_wl(const _EGLDriver *drv, _EGLDisplay *disp, struct wl_resource *buffer_resource, EGLint attribute, EGLint *value) { @@ -2330,20 +3327,35 @@ dri2_egl_unref_sync(struct dri2_egl_display *dri2_dpy, struct dri2_egl_sync *dri2_sync) { if (p_atomic_dec_zero(&dri2_sync->refcount)) { - dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence); + switch (dri2_sync->base.Type) { + case EGL_SYNC_REUSABLE_KHR: + cnd_destroy(&dri2_sync->cond); + break; + case EGL_SYNC_NATIVE_FENCE_ANDROID: + if (dri2_sync->base.SyncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID) + close(dri2_sync->base.SyncFd); + break; + default: + break; + } + + if (dri2_sync->fence) + dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence); + free(dri2_sync); } } static _EGLSync * -dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy, - EGLenum type, const EGLint *attrib_list, - const EGLAttrib *attrib_list64) +dri2_create_sync(const _EGLDriver *drv, _EGLDisplay *disp, + EGLenum type, const EGLAttrib *attrib_list) { _EGLContext *ctx = _eglGetCurrentContext(); - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); struct dri2_egl_sync *dri2_sync; + EGLint ret; + pthread_condattr_t attr; dri2_sync = calloc(1, sizeof(struct dri2_egl_sync)); if (!dri2_sync) { @@ -2351,8 +3363,7 @@ dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy, return NULL; } - if (!_eglInitSync(&dri2_sync->base, dpy, type, attrib_list, - attrib_list64)) { + if (!_eglInitSync(&dri2_sync->base, disp, type, attrib_list)) { free(dri2_sync); return NULL; } @@ -2386,6 +3397,50 @@ dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy, dri2_sync->fence, 0, 0)) dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; break; + + case EGL_SYNC_REUSABLE_KHR: + /* intialize attr */ + ret = pthread_condattr_init(&attr); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + free(dri2_sync); + return NULL; + } + + /* change clock attribute to CLOCK_MONOTONIC */ + ret = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + free(dri2_sync); + return NULL; + } + + ret = pthread_cond_init(&dri2_sync->cond, &attr); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + free(dri2_sync); + return NULL; + } + + /* initial status of reusable sync must be "unsignaled" */ + dri2_sync->base.SyncStatus = EGL_UNSIGNALED_KHR; + break; + + case EGL_SYNC_NATIVE_FENCE_ANDROID: + if (dri2_dpy->fence->create_fence_fd) { + dri2_sync->fence = dri2_dpy->fence->create_fence_fd( + dri2_ctx->dri_context, + dri2_sync->base.SyncFd); + } + if (!dri2_sync->fence) { + _eglError(EGL_BAD_ATTRIBUTE, "eglCreateSyncKHR"); + free(dri2_sync); + return NULL; + } + break; } p_atomic_set(&dri2_sync->refcount, 1); @@ -2393,24 +3448,80 @@ dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy, } static EGLBoolean -dri2_destroy_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync) +dri2_destroy_sync(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSync *sync) { - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + EGLint ret = EGL_TRUE; + EGLint err; + + /* if type of sync is EGL_SYNC_REUSABLE_KHR and it is not signaled yet, + * then unlock all threads possibly blocked by the reusable sync before + * destroying it. + */ + if (dri2_sync->base.Type == EGL_SYNC_REUSABLE_KHR && + dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR) { + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + /* unblock all threads currently blocked by sync */ + err = cnd_broadcast(&dri2_sync->cond); + + if (err) { + _eglError(EGL_BAD_ACCESS, "eglDestroySyncKHR"); + ret = EGL_FALSE; + } + } dri2_egl_unref_sync(dri2_dpy, dri2_sync); - return EGL_TRUE; + + return ret; +} + +static EGLint +dri2_dup_native_fence_fd(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSync *sync) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + + assert(sync->Type == EGL_SYNC_NATIVE_FENCE_ANDROID); + + if (sync->SyncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + /* try to retrieve the actual native fence fd.. if rendering is + * not flushed this will just return -1, aka NO_NATIVE_FENCE_FD: + */ + sync->SyncFd = dri2_dpy->fence->get_fence_fd(dri2_dpy->dri_screen, + dri2_sync->fence); + } + + if (sync->SyncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + /* if native fence fd still not created, return an error: */ + _eglError(EGL_BAD_PARAMETER, "eglDupNativeFenceFDANDROID"); + return EGL_NO_NATIVE_FENCE_FD_ANDROID; + } + + return os_dupfd_cloexec(sync->SyncFd); +} + +static void +dri2_set_blob_cache_funcs(const _EGLDriver *drv, _EGLDisplay *disp, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + dri2_dpy->blob->set_cache_funcs(dri2_dpy->dri_screen, + disp->BlobCacheSet, + disp->BlobCacheGet); } static EGLint -dri2_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync, +dri2_client_wait_sync(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSync *sync, EGLint flags, EGLTime timeout) { _EGLContext *ctx = _eglGetCurrentContext(); - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); unsigned wait_flags = 0; + EGLint ret = EGL_CONDITION_SATISFIED_KHR; /* The EGL_KHR_fence_sync spec states: @@ -2424,143 +3535,191 @@ dri2_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync, /* the sync object should take a reference while waiting */ dri2_egl_ref_sync(dri2_sync); - if (dri2_dpy->fence->client_wait_sync(dri2_ctx ? dri2_ctx->dri_context : NULL, + switch (sync->Type) { + case EGL_SYNC_FENCE_KHR: + case EGL_SYNC_NATIVE_FENCE_ANDROID: + case EGL_SYNC_CL_EVENT_KHR: + if (dri2_dpy->fence->client_wait_sync(dri2_ctx ? dri2_ctx->dri_context : NULL, dri2_sync->fence, wait_flags, timeout)) - dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; - else - ret = EGL_TIMEOUT_EXPIRED_KHR; + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + else + ret = EGL_TIMEOUT_EXPIRED_KHR; + break; - dri2_egl_unref_sync(dri2_dpy, dri2_sync); - return ret; + case EGL_SYNC_REUSABLE_KHR: + if (dri2_ctx && dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR && + (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR)) { + /* flush context if EGL_SYNC_FLUSH_COMMANDS_BIT_KHR is set */ + dri2_gl_flush(); + } + + /* if timeout is EGL_FOREVER_KHR, it should wait without any timeout.*/ + if (timeout == EGL_FOREVER_KHR) { + mtx_lock(&dri2_sync->mutex); + cnd_wait(&dri2_sync->cond, &dri2_sync->mutex); + mtx_unlock(&dri2_sync->mutex); + } else { + /* if reusable sync has not been yet signaled */ + if (dri2_sync->base.SyncStatus != EGL_SIGNALED_KHR) { + /* timespecs for cnd_timedwait */ + struct timespec current; + struct timespec expire; + + /* We override the clock to monotonic when creating the condition + * variable. */ + clock_gettime(CLOCK_MONOTONIC, ¤t); + + /* calculating when to expire */ + expire.tv_nsec = timeout % 1000000000L; + expire.tv_sec = timeout / 1000000000L; + + expire.tv_nsec += current.tv_nsec; + expire.tv_sec += current.tv_sec; + + /* expire.nsec now is a number between 0 and 1999999998 */ + if (expire.tv_nsec > 999999999L) { + expire.tv_sec++; + expire.tv_nsec -= 1000000000L; + } + + mtx_lock(&dri2_sync->mutex); + ret = cnd_timedwait(&dri2_sync->cond, &dri2_sync->mutex, &expire); + mtx_unlock(&dri2_sync->mutex); + + if (ret == thrd_busy) { + if (dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR) { + ret = EGL_TIMEOUT_EXPIRED_KHR; + } else { + _eglError(EGL_BAD_ACCESS, "eglClientWaitSyncKHR"); + ret = EGL_FALSE; + } + } + } + } + break; + } + dri2_egl_unref_sync(dri2_dpy, dri2_sync); + + return ret; } -static EGLint -dri2_server_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync) +static EGLBoolean +dri2_signal_sync(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSync *sync, + EGLenum mode) { - _EGLContext *ctx = _eglGetCurrentContext(); - struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); - struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + EGLint ret; - dri2_dpy->fence->server_wait_sync(dri2_ctx->dri_context, - dri2_sync->fence, 0); - return EGL_TRUE; -} + if (sync->Type != EGL_SYNC_REUSABLE_KHR) + return _eglError(EGL_BAD_MATCH, "eglSignalSyncKHR"); -static void -dri2_unload(_EGLDriver *drv) -{ - struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv); + if (mode != EGL_SIGNALED_KHR && mode != EGL_UNSIGNALED_KHR) + return _eglError(EGL_BAD_ATTRIBUTE, "eglSignalSyncKHR"); - if (dri2_drv->handle) - dlclose(dri2_drv->handle); - free(dri2_drv); -} + dri2_sync->base.SyncStatus = mode; -static EGLBoolean -dri2_load(_EGLDriver *drv) -{ - struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv); -#ifdef HAVE_ANDROID_PLATFORM - const char *libname = "libglapi.so"; -#elif defined(__APPLE__) - const char *libname = "libglapi.0.dylib"; -#else - const char *libname = "libglapi.so.0"; -#endif - void *handle; - - /* RTLD_GLOBAL to make sure glapi symbols are visible to DRI drivers */ - handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL); - if (handle) { - dri2_drv->get_proc_address = (_EGLProc (*)(const char *)) - dlsym(handle, "_glapi_get_proc_address"); - if (!dri2_drv->get_proc_address || !libname) { - /* no need to keep a reference */ - dlclose(handle); - handle = NULL; - } - } + if (mode == EGL_SIGNALED_KHR) { + ret = cnd_broadcast(&dri2_sync->cond); - /* if glapi is not available, loading DRI drivers will fail */ - if (!dri2_drv->get_proc_address) { - _eglLog(_EGL_WARNING, "DRI2: failed to find _glapi_get_proc_address"); - return EGL_FALSE; + /* fail to broadcast */ + if (ret) + return _eglError(EGL_BAD_ACCESS, "eglSignalSyncKHR"); } - dri2_drv->glFlush = (void (*)(void)) - dri2_drv->get_proc_address("glFlush"); + return EGL_TRUE; +} - dri2_drv->handle = handle; +static EGLint +dri2_server_wait_sync(const _EGLDriver *drv, _EGLDisplay *disp, _EGLSync *sync) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + dri2_dpy->fence->server_wait_sync(dri2_ctx->dri_context, + dri2_sync->fence, 0); return EGL_TRUE; } -/** - * This is the main entrypoint into the driver, called by libEGL. - * Create a new _EGLDriver object and init its dispatch table. - */ -_EGLDriver * -_eglBuiltInDriverDRI2(const char *args) +static int +dri2_interop_query_device_info(_EGLDisplay *disp, _EGLContext *ctx, + struct mesa_glinterop_device_info *out) { - struct dri2_egl_driver *dri2_drv; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); - (void) args; + if (!dri2_dpy->interop) + return MESA_GLINTEROP_UNSUPPORTED; - dri2_drv = calloc(1, sizeof *dri2_drv); - if (!dri2_drv) - return NULL; + return dri2_dpy->interop->query_device_info(dri2_ctx->dri_context, out); +} - if (!dri2_load(&dri2_drv->base)) { - free(dri2_drv); - return NULL; - } +static int +dri2_interop_export_object(_EGLDisplay *disp, _EGLContext *ctx, + struct mesa_glinterop_export_in *in, + struct mesa_glinterop_export_out *out) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); - _eglInitDriverFallbacks(&dri2_drv->base); - dri2_drv->base.API.Initialize = dri2_initialize; - dri2_drv->base.API.Terminate = dri2_terminate; - dri2_drv->base.API.CreateContext = dri2_create_context; - dri2_drv->base.API.DestroyContext = dri2_destroy_context; - dri2_drv->base.API.MakeCurrent = dri2_make_current; - dri2_drv->base.API.CreateWindowSurface = dri2_create_window_surface; - dri2_drv->base.API.CreatePixmapSurface = dri2_create_pixmap_surface; - dri2_drv->base.API.CreatePbufferSurface = dri2_create_pbuffer_surface; - dri2_drv->base.API.DestroySurface = dri2_destroy_surface; - dri2_drv->base.API.GetProcAddress = dri2_get_proc_address; - dri2_drv->base.API.WaitClient = dri2_wait_client; - dri2_drv->base.API.WaitNative = dri2_wait_native; - dri2_drv->base.API.BindTexImage = dri2_bind_tex_image; - dri2_drv->base.API.ReleaseTexImage = dri2_release_tex_image; - dri2_drv->base.API.SwapInterval = dri2_swap_interval; - dri2_drv->base.API.SwapBuffers = dri2_swap_buffers; - dri2_drv->base.API.SwapBuffersWithDamageEXT = dri2_swap_buffers_with_damage; - dri2_drv->base.API.SwapBuffersRegionNOK = dri2_swap_buffers_region; - dri2_drv->base.API.PostSubBufferNV = dri2_post_sub_buffer; - dri2_drv->base.API.CopyBuffers = dri2_copy_buffers, - dri2_drv->base.API.QueryBufferAge = dri2_query_buffer_age; - dri2_drv->base.API.CreateImageKHR = dri2_create_image; - dri2_drv->base.API.DestroyImageKHR = dri2_destroy_image_khr; - dri2_drv->base.API.CreateWaylandBufferFromImageWL = dri2_create_wayland_buffer_from_image; + if (!dri2_dpy->interop) + return MESA_GLINTEROP_UNSUPPORTED; + + return dri2_dpy->interop->export_object(dri2_ctx->dri_context, in, out); +} + +const _EGLDriver _eglDriver = { + .Initialize = dri2_initialize, + .Terminate = dri2_terminate, + .CreateContext = dri2_create_context, + .DestroyContext = dri2_destroy_context, + .MakeCurrent = dri2_make_current, + .CreateWindowSurface = dri2_create_window_surface, + .CreatePixmapSurface = dri2_create_pixmap_surface, + .CreatePbufferSurface = dri2_create_pbuffer_surface, + .DestroySurface = dri2_destroy_surface, + .GetProcAddress = dri2_get_proc_address, + .WaitClient = dri2_wait_client, + .WaitNative = dri2_wait_native, + .BindTexImage = dri2_bind_tex_image, + .ReleaseTexImage = dri2_release_tex_image, + .SwapInterval = dri2_swap_interval, + .SwapBuffers = dri2_swap_buffers, + .SwapBuffersWithDamageEXT = dri2_swap_buffers_with_damage, + .SwapBuffersRegionNOK = dri2_swap_buffers_region, + .SetDamageRegion = dri2_set_damage_region, + .PostSubBufferNV = dri2_post_sub_buffer, + .CopyBuffers = dri2_copy_buffers, + .QueryBufferAge = dri2_query_buffer_age, + .CreateImageKHR = dri2_create_image, + .DestroyImageKHR = dri2_destroy_image_khr, + .CreateWaylandBufferFromImageWL = dri2_create_wayland_buffer_from_image, + .QuerySurface = dri2_query_surface, + .QueryDriverName = dri2_query_driver_name, + .QueryDriverConfig = dri2_query_driver_config, #ifdef HAVE_LIBDRM - dri2_drv->base.API.CreateDRMImageMESA = dri2_create_drm_image_mesa; - dri2_drv->base.API.ExportDRMImageMESA = dri2_export_drm_image_mesa; - dri2_drv->base.API.ExportDMABUFImageQueryMESA = dri2_export_dma_buf_image_query_mesa; - dri2_drv->base.API.ExportDMABUFImageMESA = dri2_export_dma_buf_image_mesa; + .CreateDRMImageMESA = dri2_create_drm_image_mesa, + .ExportDRMImageMESA = dri2_export_drm_image_mesa, + .ExportDMABUFImageQueryMESA = dri2_export_dma_buf_image_query_mesa, + .ExportDMABUFImageMESA = dri2_export_dma_buf_image_mesa, + .QueryDmaBufFormatsEXT = dri2_query_dma_buf_formats, + .QueryDmaBufModifiersEXT = dri2_query_dma_buf_modifiers, #endif #ifdef HAVE_WAYLAND_PLATFORM - dri2_drv->base.API.BindWaylandDisplayWL = dri2_bind_wayland_display_wl; - dri2_drv->base.API.UnbindWaylandDisplayWL = dri2_unbind_wayland_display_wl; - dri2_drv->base.API.QueryWaylandBufferWL = dri2_query_wayland_buffer_wl; + .BindWaylandDisplayWL = dri2_bind_wayland_display_wl, + .UnbindWaylandDisplayWL = dri2_unbind_wayland_display_wl, + .QueryWaylandBufferWL = dri2_query_wayland_buffer_wl, #endif - dri2_drv->base.API.GetSyncValuesCHROMIUM = dri2_get_sync_values_chromium; - dri2_drv->base.API.CreateSyncKHR = dri2_create_sync; - dri2_drv->base.API.ClientWaitSyncKHR = dri2_client_wait_sync; - dri2_drv->base.API.WaitSyncKHR = dri2_server_wait_sync; - dri2_drv->base.API.DestroySyncKHR = dri2_destroy_sync; - - dri2_drv->base.Name = "DRI2"; - dri2_drv->base.Unload = dri2_unload; - - return &dri2_drv->base; -} + .GetSyncValuesCHROMIUM = dri2_get_sync_values_chromium, + .CreateSyncKHR = dri2_create_sync, + .ClientWaitSyncKHR = dri2_client_wait_sync, + .SignalSyncKHR = dri2_signal_sync, + .WaitSyncKHR = dri2_server_wait_sync, + .DestroySyncKHR = dri2_destroy_sync, + .GLInteropQueryDeviceInfo = dri2_interop_query_device_info, + .GLInteropExportObject = dri2_interop_export_object, + .DupNativeFenceFDANDROID = dri2_dup_native_fence_fd, + .SetBlobCacheFuncsANDROID = dri2_set_blob_cache_funcs, +};