X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fegl%2Fdrivers%2Fdri2%2Fplatform_x11.c;h=745e16a6cc1658c6405b87dee99f805f2a39263c;hb=faf0f811e3f9fb724a89c463c0cb6a0d61715f95;hp=f27bf176fb6de70775ac578af1fcdb68f932cb68;hpb=636d01bd61cac83e13c3c64874e7e34e828ca93a;p=mesa.git diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c index f27bf176fb6..745e16a6cc1 100644 --- a/src/egl/drivers/dri2/platform_x11.c +++ b/src/egl/drivers/dri2/platform_x11.c @@ -25,6 +25,8 @@ * Kristian Høgsberg */ +#include +#include #include #include #include @@ -33,21 +35,28 @@ #include #include #include +#ifdef HAVE_LIBDRM #include +#endif #include #include #include "egl_dri2.h" +#include "egl_dri2_fallbacks.h" +#include "loader.h" + +static EGLBoolean +dri2_x11_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, + EGLint interval); static void swrastCreateDrawable(struct dri2_egl_display * dri2_dpy, - struct dri2_egl_surface * dri2_surf, - int depth) + struct dri2_egl_surface * dri2_surf) { uint32_t mask; const uint32_t function = GXcopy; uint32_t valgc[2]; - + /* create GC's */ dri2_surf->gc = xcb_generate_id(dri2_dpy->conn); mask = XCB_GC_FUNCTION; @@ -58,8 +67,7 @@ swrastCreateDrawable(struct dri2_egl_display * dri2_dpy, valgc[0] = function; valgc[1] = False; xcb_create_gc(dri2_dpy->conn, dri2_surf->swapgc, dri2_surf->drawable, mask, valgc); - dri2_surf->depth = depth; - switch (depth) { + switch (dri2_surf->depth) { case 32: case 24: dri2_surf->bytes_per_pixel = 4; @@ -74,7 +82,7 @@ swrastCreateDrawable(struct dri2_egl_display * dri2_dpy, dri2_surf->bytes_per_pixel = 0; break; default: - _eglLog(_EGL_WARNING, "unsupported depth %d", depth); + _eglLog(_EGL_WARNING, "unsupported depth %d", dri2_surf->depth); } } @@ -170,13 +178,24 @@ swrastGetImage(__DRIdrawable * read, } +static xcb_screen_t * +get_xcb_screen(xcb_screen_iterator_t iter, int screen) +{ + for (; iter.rem; --screen, xcb_screen_next(&iter)) + if (screen == 0) + return iter.data; + + return NULL; +} + + /** * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface(). */ static _EGLSurface * -dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, - _EGLConfig *conf, EGLNativeWindowType window, - const EGLint *attrib_list) +dri2_x11_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, + _EGLConfig *conf, void *native_surface, + const EGLint *attrib_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); @@ -185,6 +204,11 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, xcb_get_geometry_reply_t *reply; xcb_screen_iterator_t s; xcb_generic_error_t *error; + xcb_drawable_t drawable; + xcb_screen_t *screen; + + STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface)); + drawable = (uintptr_t) native_surface; (void) drv; @@ -199,22 +223,32 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, dri2_surf->region = XCB_NONE; if (type == EGL_PBUFFER_BIT) { - dri2_surf->drawable = xcb_generate_id(dri2_dpy->conn); s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); + screen = get_xcb_screen(s, dri2_dpy->screen); + if (!screen) { + _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_create_surface"); + goto cleanup_surf; + } + + dri2_surf->drawable = xcb_generate_id(dri2_dpy->conn); xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize, - dri2_surf->drawable, s.data->root, + dri2_surf->drawable, screen->root, dri2_surf->base.Width, dri2_surf->base.Height); } else { - dri2_surf->drawable = window; + if (!drawable) { + _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_create_surface"); + goto cleanup_surf; + } + dri2_surf->drawable = drawable; } if (dri2_dpy->dri2) { - dri2_surf->dri_drawable = - (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen, - type == EGL_WINDOW_BIT ? - dri2_conf->dri_double_config : - dri2_conf->dri_single_config, - dri2_surf); + const __DRIconfig *config = + dri2_get_dri_config(dri2_conf, type, dri2_surf->base.GLColorspace); + + dri2_surf->dri_drawable = + (*dri2_dpy->dri2->createNewDrawable)(dri2_dpy->dri_screen, config, + dri2_surf); } else { assert(dri2_dpy->swrast); dri2_surf->dri_drawable = @@ -227,12 +261,6 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable"); goto cleanup_pixmap; } - - if (dri2_dpy->dri2) { - xcb_dri2_create_drawable (dri2_dpy->conn, dri2_surf->drawable); - } else { - swrastCreateDrawable(dri2_dpy, dri2_surf, _eglGetConfigKey(conf, EGL_BUFFER_SIZE)); - } if (type != EGL_PBUFFER_BIT) { cookie = xcb_get_geometry (dri2_dpy->conn, dri2_surf->drawable); @@ -245,9 +273,22 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, dri2_surf->base.Width = reply->width; dri2_surf->base.Height = reply->height; + dri2_surf->depth = reply->depth; free(reply); } + if (dri2_dpy->dri2) { + xcb_dri2_create_drawable (dri2_dpy->conn, dri2_surf->drawable); + } else { + if (type == EGL_PBUFFER_BIT) { + dri2_surf->depth = _eglGetConfigKey(conf, EGL_BUFFER_SIZE); + } + swrastCreateDrawable(dri2_dpy, dri2_surf); + } + + /* we always copy the back buffer to front */ + dri2_surf->base.PostSubBufferSupportedNV = EGL_TRUE; + return &dri2_surf->base; cleanup_dri_drawable: @@ -265,33 +306,47 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface(). */ static _EGLSurface * -dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp, - _EGLConfig *conf, EGLNativeWindowType window, - const EGLint *attrib_list) +dri2_x11_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp, + _EGLConfig *conf, void *native_window, + const EGLint *attrib_list) { - return dri2_create_surface(drv, disp, EGL_WINDOW_BIT, conf, - window, attrib_list); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLSurface *surf; + + surf = dri2_x11_create_surface(drv, disp, EGL_WINDOW_BIT, conf, + native_window, attrib_list); + if (surf != NULL) { + /* When we first create the DRI2 drawable, its swap interval on the + * server side is 1. + */ + surf->SwapInterval = 1; + + /* Override that with a driconf-set value. */ + dri2_x11_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval); + } + + return surf; } static _EGLSurface * -dri2_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp, - _EGLConfig *conf, EGLNativePixmapType pixmap, - const EGLint *attrib_list) +dri2_x11_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp, + _EGLConfig *conf, void *native_pixmap, + const EGLint *attrib_list) { - return dri2_create_surface(drv, disp, EGL_PIXMAP_BIT, conf, - pixmap, attrib_list); + return dri2_x11_create_surface(drv, disp, EGL_PIXMAP_BIT, conf, + native_pixmap, attrib_list); } static _EGLSurface * -dri2_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp, - _EGLConfig *conf, const EGLint *attrib_list) +dri2_x11_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp, + _EGLConfig *conf, const EGLint *attrib_list) { - return dri2_create_surface(drv, disp, EGL_PBUFFER_BIT, conf, - XCB_WINDOW_NONE, attrib_list); + return dri2_x11_create_surface(drv, disp, EGL_PBUFFER_BIT, conf, + XCB_WINDOW_NONE, attrib_list); } static EGLBoolean -dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) +dri2_x11_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); @@ -325,8 +380,8 @@ dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) * \c DRI2GetBuffers or \c DRI2GetBuffersWithFormat. */ static void -dri2_process_buffers(struct dri2_egl_surface *dri2_surf, - xcb_dri2_dri2_buffer_t *buffers, unsigned count) +dri2_x11_process_buffers(struct dri2_egl_surface *dri2_surf, + xcb_dri2_dri2_buffer_t *buffers, unsigned count) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(dri2_surf->base.Resource.Display); @@ -366,10 +421,10 @@ dri2_process_buffers(struct dri2_egl_surface *dri2_surf, } static __DRIbuffer * -dri2_get_buffers(__DRIdrawable * driDrawable, - int *width, int *height, - unsigned int *attachments, int count, - int *out_count, void *loaderPrivate) +dri2_x11_get_buffers(__DRIdrawable * driDrawable, + int *width, int *height, + unsigned int *attachments, int count, + int *out_count, void *loaderPrivate) { struct dri2_egl_surface *dri2_surf = loaderPrivate; struct dri2_egl_display *dri2_dpy = @@ -391,7 +446,7 @@ dri2_get_buffers(__DRIdrawable * driDrawable, *out_count = reply->count; dri2_surf->base.Width = *width = reply->width; dri2_surf->base.Height = *height = reply->height; - dri2_process_buffers(dri2_surf, buffers, *out_count); + dri2_x11_process_buffers(dri2_surf, buffers, *out_count); free(reply); @@ -399,10 +454,10 @@ dri2_get_buffers(__DRIdrawable * driDrawable, } static __DRIbuffer * -dri2_get_buffers_with_format(__DRIdrawable * driDrawable, - int *width, int *height, - unsigned int *attachments, int count, - int *out_count, void *loaderPrivate) +dri2_x11_get_buffers_with_format(__DRIdrawable * driDrawable, + int *width, int *height, + unsigned int *attachments, int count, + int *out_count, void *loaderPrivate) { struct dri2_egl_surface *dri2_surf = loaderPrivate; struct dri2_egl_display *dri2_dpy = @@ -429,7 +484,7 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable, dri2_surf->base.Width = *width = reply->width; dri2_surf->base.Height = *height = reply->height; *out_count = reply->count; - dri2_process_buffers(dri2_surf, buffers, *out_count); + dri2_x11_process_buffers(dri2_surf, buffers, *out_count); free(reply); @@ -437,7 +492,7 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable, } static void -dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate) +dri2_x11_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate) { (void) driDrawable; @@ -452,23 +507,8 @@ dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate) #endif } -static char * -dri2_strndup(const char *s, int length) -{ - char *d; - - d = malloc(length + 1); - if (d == NULL) - return NULL; - - memcpy(d, s, length); - d[length] = '\0'; - - return d; -} - static EGLBoolean -dri2_connect(struct dri2_egl_display *dri2_dpy) +dri2_x11_connect(struct dri2_egl_display *dri2_dpy) { xcb_xfixes_query_version_reply_t *xfixes_query; xcb_xfixes_query_version_cookie_t xfixes_query_cookie; @@ -478,6 +518,7 @@ dri2_connect(struct dri2_egl_display *dri2_dpy) xcb_dri2_connect_cookie_t connect_cookie; xcb_generic_error_t *error; xcb_screen_iterator_t s; + xcb_screen_t *screen; char *driver_name, *device_name; const xcb_query_extension_reply_t *extension; @@ -501,9 +542,13 @@ dri2_connect(struct dri2_egl_display *dri2_dpy) XCB_DRI2_MINOR_VERSION); s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); - connect_cookie = xcb_dri2_connect_unchecked (dri2_dpy->conn, - s.data->root, - XCB_DRI2_DRIVER_TYPE_DRI); + screen = get_xcb_screen(s, dri2_dpy->screen); + if (!screen) { + _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_x11_connect"); + return EGL_FALSE; + } + connect_cookie = xcb_dri2_connect_unchecked(dri2_dpy->conn, screen->root, + XCB_DRI2_DRIVER_TYPE_DRI); xfixes_query = xcb_xfixes_query_version_reply (dri2_dpy->conn, @@ -534,22 +579,29 @@ dri2_connect(struct dri2_egl_display *dri2_dpy) return EGL_FALSE; } - driver_name = xcb_dri2_connect_driver_name (connect); - dri2_dpy->driver_name = - dri2_strndup(driver_name, - xcb_dri2_connect_driver_name_length (connect)); - -#if XCB_DRI2_CONNECT_DEVICE_NAME_BROKEN - device_name = driver_name + ((connect->driver_name_length + 3) & ~3); -#else device_name = xcb_dri2_connect_device_name (connect); -#endif dri2_dpy->device_name = - dri2_strndup(device_name, - xcb_dri2_connect_device_name_length (connect)); + strndup(device_name, + xcb_dri2_connect_device_name_length(connect)); + + dri2_dpy->fd = loader_open_device(dri2_dpy->device_name); + if (dri2_dpy->fd == -1) { + _eglLog(_EGL_WARNING, + "DRI2: could not open %s (%s)", dri2_dpy->device_name, + strerror(errno)); + free(dri2_dpy->device_name); + free(connect); + return EGL_FALSE; + } + + driver_name = xcb_dri2_connect_driver_name (connect); + dri2_dpy->driver_name = + strndup(driver_name, + xcb_dri2_connect_driver_name_length(connect)); if (dri2_dpy->device_name == NULL || dri2_dpy->driver_name == NULL) { + close(dri2_dpy->fd); free(dri2_dpy->device_name); free(dri2_dpy->driver_name); free(connect); @@ -567,26 +619,34 @@ dri2_x11_authenticate(_EGLDisplay *disp, uint32_t id) xcb_dri2_authenticate_reply_t *authenticate; xcb_dri2_authenticate_cookie_t authenticate_cookie; xcb_screen_iterator_t s; + xcb_screen_t *screen; int ret = 0; s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); + + screen = get_xcb_screen(s, dri2_dpy->screen); + if (!screen) { + _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_x11_authenticate"); + return -1; + } + authenticate_cookie = - xcb_dri2_authenticate_unchecked(dri2_dpy->conn, s.data->root, id); + xcb_dri2_authenticate_unchecked(dri2_dpy->conn, screen->root, id); authenticate = xcb_dri2_authenticate_reply(dri2_dpy->conn, authenticate_cookie, NULL); if (authenticate == NULL || !authenticate->authenticated) ret = -1; - if (authenticate) - free(authenticate); + free(authenticate); return ret; } static EGLBoolean -dri2_authenticate(_EGLDisplay *disp) +dri2_x11_local_authenticate(_EGLDisplay *disp) { +#ifdef HAVE_LIBDRM struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); drm_magic_t magic; @@ -599,18 +659,19 @@ dri2_authenticate(_EGLDisplay *disp) _eglLog(_EGL_WARNING, "DRI2: failed to authenticate"); return EGL_FALSE; } - +#endif return EGL_TRUE; } static EGLBoolean -dri2_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy, - _EGLDisplay *disp) +dri2_x11_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy, + _EGLDisplay *disp) { xcb_screen_iterator_t s; xcb_depth_iterator_t d; xcb_visualtype_t *visuals; int i, j, id; + unsigned int rgba_masks[4]; EGLint surface_type; EGLint config_attrs[] = { EGL_NATIVE_VISUAL_ID, 0, @@ -619,7 +680,7 @@ dri2_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy, }; s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); - d = xcb_screen_allowed_depths_iterator(s.data); + d = xcb_screen_allowed_depths_iterator(get_xcb_screen(s, dri2_dpy->screen)); id = 1; surface_type = @@ -641,8 +702,26 @@ dri2_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy, config_attrs[1] = visuals[i].visual_id; config_attrs[3] = visuals[i]._class; + rgba_masks[0] = visuals[i].red_mask; + rgba_masks[1] = visuals[i].green_mask; + rgba_masks[2] = visuals[i].blue_mask; + rgba_masks[3] = 0; dri2_add_config(disp, dri2_dpy->driver_configs[j], id++, - d.data->depth, surface_type, config_attrs); + surface_type, config_attrs, rgba_masks); + + /* Allow a 24-bit RGB visual to match a 32-bit RGBA EGLConfig. + * Otherwise it will only match a 32-bit RGBA visual. On a + * composited window manager on X11, this will make all of the + * EGLConfigs with destination alpha get blended by the + * compositor. This is probably not what the application + * wants... especially on drivers that only have 32-bit RGBA + * EGLConfigs! */ + if (d.data->depth == 24) { + rgba_masks[3] = + ~(rgba_masks[0] | rgba_masks[1] | rgba_masks[2]); + dri2_add_config(disp, dri2_dpy->driver_configs[j], id++, + surface_type, config_attrs, rgba_masks); + } } } @@ -661,31 +740,17 @@ static EGLBoolean dri2_copy_region(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw, xcb_xfixes_region_t region) { - 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_surf = dri2_egl_surface(draw); - _EGLContext *ctx; enum xcb_dri2_attachment_t render_attachment; xcb_dri2_copy_region_cookie_t cookie; - if (dri2_drv->glFlush) { - ctx = _eglGetCurrentContext(); - if (ctx && ctx->DrawSurface == &dri2_surf->base) - dri2_drv->glFlush(); - } - - (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable); + /* No-op for a pixmap or pbuffer surface */ + if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT) + return EGL_TRUE; -#if 0 - /* FIXME: Add support for dri swapbuffers, that'll give us swap - * interval and page flipping (at least for fullscreen windows) as - * well as the page flip event. Unless surface->SwapBehavior is - * EGL_BUFFER_PRESERVED. */ -#if __DRI2_FLUSH_VERSION >= 2 - if (pdraw->psc->f) - (*pdraw->psc->f->flushInvalidate)(pdraw->driDrawable); -#endif -#endif + if (dri2_dpy->flush) + (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable); if (dri2_surf->have_fake_front) render_attachment = XCB_DRI2_ATTACHMENT_BUFFER_FAKE_FRONT_LEFT; @@ -702,25 +767,78 @@ dri2_copy_region(_EGLDriver *drv, _EGLDisplay *disp, return EGL_TRUE; } +static int64_t +dri2_x11_swap_buffers_msc(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw, + int64_t msc, int64_t divisor, int64_t remainder) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw); + uint32_t msc_hi = msc >> 32; + uint32_t msc_lo = msc & 0xffffffff; + uint32_t divisor_hi = divisor >> 32; + uint32_t divisor_lo = divisor & 0xffffffff; + uint32_t remainder_hi = remainder >> 32; + uint32_t remainder_lo = remainder & 0xffffffff; + xcb_dri2_swap_buffers_cookie_t cookie; + xcb_dri2_swap_buffers_reply_t *reply; + int64_t swap_count = -1; + + /* No-op for a pixmap or pbuffer surface */ + if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT) + return 0; + + if (draw->SwapBehavior == EGL_BUFFER_PRESERVED || !dri2_dpy->swap_available) + return dri2_copy_region(drv, disp, draw, dri2_surf->region) ? 0 : -1; + + dri2_flush_drawable_for_swapbuffers(disp, draw); + + cookie = xcb_dri2_swap_buffers_unchecked(dri2_dpy->conn, dri2_surf->drawable, + msc_hi, msc_lo, divisor_hi, divisor_lo, remainder_hi, remainder_lo); + + reply = xcb_dri2_swap_buffers_reply(dri2_dpy->conn, cookie, NULL); + + if (reply) { + swap_count = (((int64_t)reply->swap_hi) << 32) | reply->swap_lo; + free(reply); + } + + /* Since we aren't watching for the server's invalidate events like we're + * supposed to (due to XCB providing no mechanism for filtering the events + * the way xlib does), and SwapBuffers is a common cause of invalidate + * events, just shove one down to the driver, even though we haven't told + * the driver that we're the kind of loader that provides reliable + * invalidate events. This causes the driver to request buffers again at + * its next draw, so that we get the correct buffers if a pageflip + * happened. The driver should still be using the viewport hack to catch + * window resizes. + */ + if (dri2_dpy->flush && + dri2_dpy->flush->base.version >= 3 && dri2_dpy->flush->invalidate) + (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable); + + return swap_count; +} + static EGLBoolean -dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) +dri2_x11_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw); - if (dri2_dpy->dri2) { - return dri2_copy_region(drv, disp, draw, dri2_surf->region); + if (dri2_dpy->dri2) { + return dri2_x11_swap_buffers_msc(drv, disp, draw, 0, 0, 0) != -1; } else { assert(dri2_dpy->swrast); - + dri2_dpy->core->swapBuffers(dri2_surf->dri_drawable); return EGL_TRUE; } } static EGLBoolean -dri2_swap_buffers_region(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw, - EGLint numRects, const EGLint *rects) +dri2_x11_swap_buffers_region(_EGLDriver *drv, _EGLDisplay *disp, + _EGLSurface *draw, + EGLint numRects, const EGLint *rects) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw); @@ -732,10 +850,9 @@ dri2_swap_buffers_region(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw, if (numRects > (int)ARRAY_SIZE(rectangles)) return dri2_copy_region(drv, disp, draw, dri2_surf->region); - /* FIXME: Invert y here? */ for (i = 0; i < numRects; i++) { rectangles[i].x = rects[i * 4]; - rectangles[i].y = rects[i * 4 + 1]; + rectangles[i].y = dri2_surf->base.Height - rects[i * 4 + 1] - rects[i * 4 + 3]; rectangles[i].width = rects[i * 4 + 2]; rectangles[i].height = rects[i * 4 + 3]; } @@ -749,12 +866,48 @@ dri2_swap_buffers_region(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw, } static EGLBoolean -dri2_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, - EGLNativePixmapType target) +dri2_x11_post_sub_buffer(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw, + EGLint x, EGLint y, EGLint width, EGLint height) +{ + const EGLint rect[4] = { x, y, width, height }; + + if (x < 0 || y < 0 || width < 0 || height < 0) + _eglError(EGL_BAD_PARAMETER, "eglPostSubBufferNV"); + + return dri2_x11_swap_buffers_region(drv, disp, draw, 1, rect); +} + +static EGLBoolean +dri2_x11_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, + EGLint interval) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + if (interval > surf->Config->MaxSwapInterval) + interval = surf->Config->MaxSwapInterval; + else if (interval < surf->Config->MinSwapInterval) + interval = surf->Config->MinSwapInterval; + + if (interval != surf->SwapInterval && dri2_dpy->swap_available) + xcb_dri2_swap_interval(dri2_dpy->conn, dri2_surf->drawable, interval); + + surf->SwapInterval = interval; + + return EGL_TRUE; +} + +static EGLBoolean +dri2_x11_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, + void *native_pixmap_target) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); xcb_gcontext_t gc; + xcb_pixmap_t target; + + STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target)); + target = (uintptr_t) native_pixmap_target; (void) drv; @@ -881,34 +1034,84 @@ dri2_x11_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, } static EGLBoolean -dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) +dri2_x11_get_sync_values(_EGLDisplay *display, _EGLSurface *surface, + EGLuint64KHR *ust, EGLuint64KHR *msc, + EGLuint64KHR *sbc) { - struct dri2_egl_display *dri2_dpy; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(display); + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface); + xcb_dri2_get_msc_cookie_t cookie; + xcb_dri2_get_msc_reply_t *reply; + + cookie = xcb_dri2_get_msc(dri2_dpy->conn, dri2_surf->drawable); + reply = xcb_dri2_get_msc_reply(dri2_dpy->conn, cookie, NULL); + + if (!reply) { + _eglError(EGL_BAD_ACCESS, __func__); + return EGL_FALSE; + } - drv->API.CreateWindowSurface = dri2_create_window_surface; - drv->API.CreatePixmapSurface = dri2_create_pixmap_surface; - drv->API.CreatePbufferSurface = dri2_create_pbuffer_surface; - drv->API.DestroySurface = dri2_destroy_surface; - drv->API.SwapBuffers = dri2_swap_buffers; - drv->API.CopyBuffers = dri2_copy_buffers; + *ust = ((EGLuint64KHR) reply->ust_hi << 32) | reply->ust_lo; + *msc = ((EGLuint64KHR) reply->msc_hi << 32) | reply->msc_lo; + *sbc = ((EGLuint64KHR) reply->sbc_hi << 32) | reply->sbc_lo; + free(reply); + + return EGL_TRUE; +} + +static struct dri2_egl_display_vtbl dri2_x11_swrast_display_vtbl = { + .authenticate = NULL, + .create_window_surface = dri2_x11_create_window_surface, + .create_pixmap_surface = dri2_x11_create_pixmap_surface, + .create_pbuffer_surface = dri2_x11_create_pbuffer_surface, + .destroy_surface = dri2_x11_destroy_surface, + .create_image = dri2_fallback_create_image_khr, + .swap_interval = dri2_fallback_swap_interval, + .swap_buffers = dri2_x11_swap_buffers, + .swap_buffers_region = dri2_fallback_swap_buffers_region, + .post_sub_buffer = dri2_fallback_post_sub_buffer, + .copy_buffers = dri2_x11_copy_buffers, + .query_buffer_age = dri2_fallback_query_buffer_age, + .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image, + .get_sync_values = dri2_fallback_get_sync_values, +}; + +static struct dri2_egl_display_vtbl dri2_x11_display_vtbl = { + .authenticate = dri2_x11_authenticate, + .create_window_surface = dri2_x11_create_window_surface, + .create_pixmap_surface = dri2_x11_create_pixmap_surface, + .create_pbuffer_surface = dri2_x11_create_pbuffer_surface, + .destroy_surface = dri2_x11_destroy_surface, + .create_image = dri2_x11_create_image_khr, + .swap_interval = dri2_x11_swap_interval, + .swap_buffers = dri2_x11_swap_buffers, + .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage, + .swap_buffers_region = dri2_x11_swap_buffers_region, + .post_sub_buffer = dri2_x11_post_sub_buffer, + .copy_buffers = dri2_x11_copy_buffers, + .query_buffer_age = dri2_fallback_query_buffer_age, + .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image, + .get_sync_values = dri2_x11_get_sync_values, +}; - drv->API.SwapBuffersRegionNOK = NULL; - drv->API.CreateImageKHR = NULL; - drv->API.DestroyImageKHR = NULL; - drv->API.CreateDRMImageMESA = NULL; - drv->API.ExportDRMImageMESA = NULL; +static EGLBoolean +dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy; - dri2_dpy = malloc(sizeof *dri2_dpy); + dri2_dpy = calloc(1, sizeof *dri2_dpy); if (!dri2_dpy) return _eglError(EGL_BAD_ALLOC, "eglInitialize"); - memset(dri2_dpy, 0, sizeof *dri2_dpy); - disp->DriverData = (void *) dri2_dpy; if (disp->PlatformDisplay == NULL) { - dri2_dpy->conn = xcb_connect(0, 0); + dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen); + dri2_dpy->own_device = true; } else { - dri2_dpy->conn = XGetXCBConnection((Display *) disp->PlatformDisplay); + Display *dpy = disp->PlatformDisplay; + + dri2_dpy->conn = XGetXCBConnection(dpy); + dri2_dpy->screen = DefaultScreen(dpy); } if (xcb_connection_has_error(dri2_dpy->conn)) { @@ -916,11 +1119,16 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) goto cleanup_dpy; } + /* + * Every hardware driver_name is set using strdup. Doing the same in + * here will allow is to simply free the memory at dri2_terminate(). + */ + dri2_dpy->driver_name = strdup("swrast"); if (!dri2_load_driver_swrast(disp)) goto cleanup_conn; dri2_dpy->swrast_loader_extension.base.name = __DRI_SWRAST_LOADER; - dri2_dpy->swrast_loader_extension.base.version = __DRI_SWRAST_LOADER_VERSION; + dri2_dpy->swrast_loader_extension.base.version = 2; dri2_dpy->swrast_loader_extension.getDrawableInfo = swrastGetDrawableInfo; dri2_dpy->swrast_loader_extension.putImage = swrastPutImage; dri2_dpy->swrast_loader_extension.getImage = swrastGetImage; @@ -933,13 +1141,14 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) goto cleanup_driver; if (dri2_dpy->conn) { - if (!dri2_add_configs_for_visuals(dri2_dpy, disp)) + if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp)) goto cleanup_configs; } - /* we're supporting EGL 1.4 */ - disp->VersionMajor = 1; - disp->VersionMinor = 4; + /* Fill vtbl last to prevent accidentally calling virtual function during + * initialization. + */ + dri2_dpy->vtbl = &dri2_x11_swrast_display_vtbl; return EGL_TRUE; @@ -949,6 +1158,7 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) cleanup_driver: dlclose(dri2_dpy->driver); cleanup_conn: + free(dri2_dpy->driver_name); if (disp->PlatformDisplay == NULL) xcb_disconnect(dri2_dpy->conn); cleanup_dpy: @@ -957,72 +1167,98 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) return EGL_FALSE; } +static void +dri2_x11_setup_swap_interval(struct dri2_egl_display *dri2_dpy) +{ + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; + int arbitrary_max_interval = 1000; + + /* default behavior for no SwapBuffers support: no vblank syncing + * either. + */ + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = 0; + + if (!dri2_dpy->swap_available) + return; + + /* If we do have swapbuffers, then we can support pretty much any swap + * interval, but we 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 = arbitrary_max_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 = arbitrary_max_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 = arbitrary_max_interval; + dri2_dpy->default_swap_interval = 1; + break; + } +} static EGLBoolean dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp) { struct dri2_egl_display *dri2_dpy; - drv->API.CreateWindowSurface = dri2_create_window_surface; - drv->API.CreatePixmapSurface = dri2_create_pixmap_surface; - drv->API.CreatePbufferSurface = dri2_create_pbuffer_surface; - drv->API.DestroySurface = dri2_destroy_surface; - drv->API.SwapBuffers = dri2_swap_buffers; - drv->API.CopyBuffers = dri2_copy_buffers; - drv->API.CreateImageKHR = dri2_x11_create_image_khr; - drv->API.SwapBuffersRegionNOK = dri2_swap_buffers_region; - - dri2_dpy = malloc(sizeof *dri2_dpy); + dri2_dpy = calloc(1, sizeof *dri2_dpy); if (!dri2_dpy) return _eglError(EGL_BAD_ALLOC, "eglInitialize"); - memset(dri2_dpy, 0, sizeof *dri2_dpy); - disp->DriverData = (void *) dri2_dpy; if (disp->PlatformDisplay == NULL) { - dri2_dpy->conn = xcb_connect(0, 0); + dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen); + dri2_dpy->own_device = true; } else { - dri2_dpy->conn = XGetXCBConnection((Display *) disp->PlatformDisplay); + Display *dpy = disp->PlatformDisplay; + + dri2_dpy->conn = XGetXCBConnection(dpy); + dri2_dpy->screen = DefaultScreen(dpy); } - if (xcb_connection_has_error(dri2_dpy->conn)) { + if (!dri2_dpy->conn || xcb_connection_has_error(dri2_dpy->conn)) { _eglLog(_EGL_WARNING, "DRI2: xcb_connect failed"); goto cleanup_dpy; } - if (dri2_dpy->conn) { - if (!dri2_connect(dri2_dpy)) - goto cleanup_conn; - } + if (!dri2_x11_connect(dri2_dpy)) + goto cleanup_conn; if (!dri2_load_driver(disp)) - goto cleanup_conn; + goto cleanup_fd; - dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC); - if (dri2_dpy->fd == -1) { - _eglLog(_EGL_WARNING, - "DRI2: could not open %s (%s)", dri2_dpy->device_name, - strerror(errno)); + if (!dri2_x11_local_authenticate(disp)) goto cleanup_driver; - } - - if (dri2_dpy->conn) { - if (!dri2_authenticate(disp)) - goto cleanup_fd; - } if (dri2_dpy->dri2_minor >= 1) { dri2_dpy->dri2_loader_extension.base.name = __DRI_DRI2_LOADER; dri2_dpy->dri2_loader_extension.base.version = 3; - dri2_dpy->dri2_loader_extension.getBuffers = dri2_get_buffers; - dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_flush_front_buffer; + dri2_dpy->dri2_loader_extension.getBuffers = dri2_x11_get_buffers; + dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_x11_flush_front_buffer; dri2_dpy->dri2_loader_extension.getBuffersWithFormat = - dri2_get_buffers_with_format; + dri2_x11_get_buffers_with_format; } else { dri2_dpy->dri2_loader_extension.base.name = __DRI_DRI2_LOADER; dri2_dpy->dri2_loader_extension.base.version = 2; - dri2_dpy->dri2_loader_extension.getBuffers = dri2_get_buffers; - dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_flush_front_buffer; + dri2_dpy->dri2_loader_extension.getBuffers = dri2_x11_get_buffers; + dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_x11_flush_front_buffer; dri2_dpy->dri2_loader_extension.getBuffersWithFormat = NULL; } @@ -1030,36 +1266,41 @@ dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp) dri2_dpy->extensions[1] = &image_lookup_extension.base; dri2_dpy->extensions[2] = NULL; + dri2_dpy->swap_available = (dri2_dpy->dri2_minor >= 2); + dri2_dpy->invalidate_available = (dri2_dpy->dri2_minor >= 3); + if (!dri2_create_screen(disp)) - goto cleanup_fd; + goto cleanup_driver; - if (dri2_dpy->conn) { - if (!dri2_add_configs_for_visuals(dri2_dpy, disp)) - goto cleanup_configs; - } + dri2_x11_setup_swap_interval(dri2_dpy); disp->Extensions.KHR_image_pixmap = EGL_TRUE; disp->Extensions.NOK_swap_region = EGL_TRUE; disp->Extensions.NOK_texture_from_pixmap = EGL_TRUE; + disp->Extensions.NV_post_sub_buffer = EGL_TRUE; + disp->Extensions.CHROMIUM_sync_control = EGL_TRUE; #ifdef HAVE_WAYLAND_PLATFORM disp->Extensions.WL_bind_wayland_display = EGL_TRUE; #endif - dri2_dpy->authenticate = dri2_x11_authenticate; - /* we're supporting EGL 1.4 */ - disp->VersionMajor = 1; - disp->VersionMinor = 4; + if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp)) + goto cleanup_configs; + + /* Fill vtbl last to prevent accidentally calling virtual function during + * initialization. + */ + dri2_dpy->vtbl = &dri2_x11_display_vtbl; return EGL_TRUE; cleanup_configs: _eglCleanupDisplay(disp); dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); - cleanup_fd: - close(dri2_dpy->fd); cleanup_driver: dlclose(dri2_dpy->driver); + cleanup_fd: + close(dri2_dpy->fd); cleanup_conn: if (disp->PlatformDisplay == NULL) xcb_disconnect(dri2_dpy->conn);