X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fegl%2Fdrivers%2Fdri2%2Fegl_dri2.c;h=d941d82efb8206a008bbf8f61764b9354abbc8ff;hb=d18d0fdcd8daa0d9979b72978795f0305d2db7c8;hp=3bdac2944cb044e2c8fc5ed87175f379e4c91c75;hpb=fc5ca85a63c6d252a9e8c450f12d2f11057ea2c6;p=mesa.git diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index 3bdac2944cb..d941d82efb8 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -42,7 +42,6 @@ #include #include -#include "eglconfigutil.h" #include "eglconfig.h" #include "eglcontext.h" #include "egldisplay.h" @@ -50,26 +49,36 @@ #include "eglcurrent.h" #include "egllog.h" #include "eglsurface.h" +#include "eglimage.h" struct dri2_egl_driver { _EGLDriver base; + + void (*glFlush)(void); }; struct dri2_egl_display { - xcb_connection_t *conn; - int dri2_major; - int dri2_minor; - __DRIscreen *dri_screen; - void *driver; - __DRIcoreExtension *core; - __DRIdri2Extension *dri2; - __DRI2flushExtension *flush; - int fd; + xcb_connection_t *conn; + int dri2_major; + int dri2_minor; + __DRIscreen *dri_screen; + const __DRIconfig **driver_configs; + void *driver; + __DRIcoreExtension *core; + __DRIdri2Extension *dri2; + __DRI2flushExtension *flush; + __DRItexBufferExtension *tex_buffer; + __DRIimageExtension *image; + int fd; + + char *device_name; + char *driver_name; __DRIdri2LoaderExtension loader_extension; - const __DRIextension *extensions[2]; + __DRIimageLookupExtension image_lookup_extension; + const __DRIextension *extensions[3]; }; struct dri2_egl_context @@ -86,46 +95,25 @@ struct dri2_egl_surface __DRIbuffer buffers[5]; int buffer_count; xcb_xfixes_region_t region; - int have_back; int have_fake_front; int swap_interval; }; struct dri2_egl_config { - _EGLConfig base; + _EGLConfig base; const __DRIconfig *dri_config; }; -static struct dri2_egl_driver * -dri2_egl_driver(_EGLDriver *drv) -{ - return (struct dri2_egl_driver *) drv; -} - -static struct dri2_egl_display * -dri2_egl_display(_EGLDisplay *dpy) -{ - return (struct dri2_egl_display *) dpy->DriverData; -} - -static struct dri2_egl_context * -dri2_egl_context(_EGLContext *ctx) -{ - return (struct dri2_egl_context *) ctx; -} - -static struct dri2_egl_surface * -dri2_egl_surface(_EGLSurface *surf) +struct dri2_egl_image { - return (struct dri2_egl_surface *) surf; -} + _EGLImage base; + __DRIimage *dri_image; +}; -static struct dri2_egl_config * -dri2_egl_config(_EGLConfig *conf) -{ - return (struct dri2_egl_config *) conf; -} +/* standard typecasts */ +_EGL_DRIVER_STANDARD_TYPECASTS(dri2_egl) +_EGL_DRIVER_TYPECAST(dri2_egl_image, _EGLImage, obj) EGLint dri2_to_egl_attribute_map[] = { 0, @@ -134,7 +122,7 @@ EGLint dri2_to_egl_attribute_map[] = { EGL_RED_SIZE, /* __DRI_ATTRIB_RED_SIZE */ EGL_GREEN_SIZE, /* __DRI_ATTRIB_GREEN_SIZE */ EGL_BLUE_SIZE, /* __DRI_ATTRIB_BLUE_SIZE */ - 0, /* __DRI_ATTRIB_LUMINANCE_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 */ @@ -179,28 +167,22 @@ EGLint dri2_to_egl_attribute_map[] = { }; static void -dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id) +dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, + int depth, xcb_visualtype_t *visual) { struct dri2_egl_config *conf; struct dri2_egl_display *dri2_dpy; + _EGLConfig base; unsigned int attrib, value, double_buffer; EGLint key, bind_to_texture_rgb, bind_to_texture_rgba; int i; dri2_dpy = disp->DriverData; - conf = malloc(sizeof *conf); - if (conf == NULL) - return; - - conf->dri_config = dri_config; - _eglInitConfig(&conf->base, disp, id); + _eglInitConfig(&base, disp, id); i = 0; while (dri2_dpy->core->indexConfigAttrib(dri_config, i++, &attrib, &value)) { switch (attrib) { - case 0: - break; - case __DRI_ATTRIB_RENDER_TYPE: if (value & __DRI_ATTRIB_RGBA_BIT) value = EGL_RGB_BUFFER; @@ -208,7 +190,7 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id) value = EGL_LUMINANCE_BUFFER; else /* not valid */; - _eglSetConfigKey(&conf->base, EGL_COLOR_BUFFER_TYPE, value); + _eglSetConfigKey(&base, EGL_COLOR_BUFFER_TYPE, value); break; case __DRI_ATTRIB_CONFIG_CAVEAT: @@ -218,7 +200,7 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id) value = EGL_SLOW_CONFIG; else value = EGL_NONE; - _eglSetConfigKey(&conf->base, EGL_CONFIG_CAVEAT, value); + _eglSetConfigKey(&base, EGL_CONFIG_CAVEAT, value); break; case __DRI_ATTRIB_BIND_TO_TEXTURE_RGB: @@ -236,39 +218,57 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id) default: key = dri2_to_egl_attribute_map[attrib]; if (key != 0) - _eglSetConfigKey(&conf->base, key, value); + _eglSetConfigKey(&base, key, value); break; } } - /* EGL_SWAP_BEHAVIOR_PRESERVED_BIT */ + /* In EGL, double buffer or not isn't a config attribute. Pixmaps + * surfaces are always single buffered, pbuffer surfaces are always + * back buffers and windows can be either, selected by passing an + * attribute at window surface construction time. To support this + * we ignore all double buffer configs and manipulate the buffer we + * return in the getBuffer callback to get the behaviour we want. */ + + if (double_buffer) + return; + + if (visual != NULL) { + if (depth != _eglGetConfigKey(&base, EGL_BUFFER_SIZE)) + return; - if (double_buffer) { - /* FIXME: Figure out how to get the visual ID and types */ - _eglSetConfigKey(&conf->base, EGL_SURFACE_TYPE, EGL_WINDOW_BIT); - _eglSetConfigKey(&conf->base, EGL_NATIVE_VISUAL_ID, 0x21); - _eglSetConfigKey(&conf->base, EGL_NATIVE_VISUAL_TYPE, - XCB_VISUAL_CLASS_TRUE_COLOR); + _eglSetConfigKey(&base, EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT | + EGL_SWAP_BEHAVIOR_PRESERVED_BIT); + + _eglSetConfigKey(&base, EGL_NATIVE_VISUAL_ID, visual->visual_id); + _eglSetConfigKey(&base, EGL_NATIVE_VISUAL_TYPE, visual->_class); } else { - _eglSetConfigKey(&conf->base, - EGL_SURFACE_TYPE, EGL_PIXMAP_BIT | EGL_PBUFFER_BIT); - _eglSetConfigKey(&conf->base, - EGL_BIND_TO_TEXTURE_RGB, bind_to_texture_rgb); - _eglSetConfigKey(&conf->base, - EGL_BIND_TO_TEXTURE_RGBA, bind_to_texture_rgba); + _eglSetConfigKey(&base, EGL_SURFACE_TYPE, + EGL_PIXMAP_BIT | EGL_PBUFFER_BIT); } + _eglSetConfigKey(&base, EGL_NATIVE_RENDERABLE, EGL_TRUE); + _eglSetConfigKey(&base, EGL_BIND_TO_TEXTURE_RGB, bind_to_texture_rgb); + if (_eglGetConfigKey(&base, EGL_ALPHA_SIZE) > 0) + _eglSetConfigKey(&base, + EGL_BIND_TO_TEXTURE_RGBA, bind_to_texture_rgba); + /* EGL_OPENGL_ES_BIT, EGL_OPENVG_BIT, EGL_OPENGL_ES2_BIT */ - _eglSetConfigKey(&conf->base, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT); - _eglSetConfigKey(&conf->base, EGL_CONFORMANT, EGL_OPENGL_BIT); + _eglSetConfigKey(&base, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT); + _eglSetConfigKey(&base, EGL_CONFORMANT, EGL_OPENGL_BIT); - if (!_eglValidateConfig(&conf->base, EGL_FALSE)) { + if (!_eglValidateConfig(&base, EGL_FALSE)) { _eglLog(_EGL_DEBUG, "DRI2: failed to validate config %d", id); - free(conf); return; } - _eglAddConfig(disp, &conf->base); + conf = malloc(sizeof *conf); + if (conf != NULL) { + memcpy(&conf->base, &base, sizeof base); + conf->dri_config = dri_config; + _eglAddConfig(disp, &conf->base); + } } /** @@ -288,7 +288,6 @@ dri2_process_buffers(struct dri2_egl_surface *dri2_surf, dri2_surf->buffer_count = count; dri2_surf->have_fake_front = 0; - dri2_surf->have_back = 0; /* This assumes the DRI2 buffer attachment tokens matches the * __DRIbuffer tokens. */ @@ -298,10 +297,14 @@ dri2_process_buffers(struct dri2_egl_surface *dri2_surf, dri2_surf->buffers[i].pitch = buffers[i].pitch; dri2_surf->buffers[i].cpp = buffers[i].cpp; dri2_surf->buffers[i].flags = buffers[i].flags; + + /* We only use the DRI drivers single buffer configs. This + * means that if we try to render to a window, DRI2 will give us + * the fake front buffer, which we'll use as a back buffer. + * Note that EGL doesn't require that several clients rendering + * to the same window must see the same aux buffers. */ if (dri2_surf->buffers[i].attachment == __DRI_BUFFER_FAKE_FRONT_LEFT) dri2_surf->have_fake_front = 1; - if (dri2_surf->buffers[i].attachment == __DRI_BUFFER_BACK_LEFT) - dri2_surf->have_back = 1; } if (dri2_surf->region != XCB_NONE) @@ -358,6 +361,25 @@ dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate) #endif } +static __DRIimage * +dri2_lookup_egl_image(__DRIcontext *context, void *image, void *data) +{ + struct dri2_egl_context *dri2_ctx = data; + _EGLDisplay *disp = dri2_ctx->base.Resource.Display; + struct dri2_egl_image *dri2_img; + _EGLImage *img; + + img = _eglLookupImage(image, disp); + if (img == NULL) { + _eglError(EGL_BAD_PARAMETER, "dri2_lookup_egl_image"); + return NULL; + } + + dri2_img = dri2_egl_image(image); + + return dri2_img->dri_image; +} + static __DRIbuffer * dri2_get_buffers_with_format(__DRIdrawable * driDrawable, int *width, int *height, @@ -395,51 +417,91 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable, } #ifdef GLX_USE_TLS -static const char dri_driver_format[] = "%.*s/tls/%.*s_dri.so"; +static const char dri_driver_format[] = "%.*s/tls/%s_dri.so"; #else -static const char dri_driver_format[] = "%.*s/%.*s_dri.so"; +static const char dri_driver_format[] = "%.*s/%s_dri.so"; #endif static const char dri_driver_path[] = DEFAULT_DRIVER_DIR; -/** - * Called via eglInitialize(), GLX_drv->API.Initialize(). - */ +struct dri2_extension_match { + const char *name; + int version; + int offset; +}; + +static struct dri2_extension_match dri2_driver_extensions[] = { + { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) }, + { __DRI_DRI2, 1, offsetof(struct dri2_egl_display, dri2) }, + { NULL } +}; + +static 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 } +}; + static EGLBoolean -dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, - EGLint *major, EGLint *minor) +dri2_bind_extensions(struct dri2_egl_display *dri2_dpy, + struct dri2_extension_match *matches, + const __DRIextension **extensions) +{ + int i, j, 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 (j = 0; matches[j].name; j++) { + field = ((char *) dri2_dpy + matches[j].offset); + if (*(const __DRIextension **) field == NULL) { + _eglLog(_EGL_FATAL, "DRI2: did not find extension %s version %d", + matches[j].name, matches[j].version); + ret = EGL_FALSE; + } + } + + return ret; +} + +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) { - const __DRIextension **extensions; - const __DRIconfig **driver_configs; - struct dri2_egl_display *dri2_dpy; - char path[PATH_MAX], *search_paths, *p, *next, *end; xcb_xfixes_query_version_reply_t *xfixes_query; xcb_xfixes_query_version_cookie_t xfixes_query_cookie; xcb_dri2_query_version_reply_t *dri2_query; xcb_dri2_query_version_cookie_t dri2_query_cookie; - xcb_dri2_connect_reply_t *connect = NULL; + xcb_dri2_connect_reply_t *connect; xcb_dri2_connect_cookie_t connect_cookie; - xcb_dri2_authenticate_reply_t *authenticate; - xcb_dri2_authenticate_cookie_t authenticate_cookie; xcb_generic_error_t *error; - drm_magic_t magic; xcb_screen_iterator_t s; - int i; - - dri2_dpy = malloc(sizeof *dri2_dpy); - if (!dri2_dpy) - return _eglError(EGL_BAD_ALLOC, "eglInitialize"); - - disp->DriverData = (void *) dri2_dpy; - dri2_dpy->conn = XGetXCBConnection(disp->NativeDisplay); - if (!dri2_dpy->conn) { - dri2_dpy->conn = xcb_connect(0, 0); - if (!dri2_dpy->conn) { - _eglLog(_EGL_WARNING, "DRI2: xcb_connect failed"); - free(dri2_dpy); - return EGL_FALSE; - } - } xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_xfixes_id); xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri2_id); @@ -464,7 +526,7 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, error != NULL || xfixes_query->major_version < 2) { _eglLog(_EGL_FATAL, "DRI2: failed to query xfixes version"); free(error); - goto handle_error; + return EGL_FALSE; } free(xfixes_query); @@ -473,16 +535,136 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, if (dri2_query == NULL || error != NULL) { _eglLog(_EGL_FATAL, "DRI2: failed to query version"); free(error); - goto handle_error; + return EGL_FALSE; } dri2_dpy->dri2_major = dri2_query->major_version; dri2_dpy->dri2_minor = dri2_query->minor_version; free(dri2_query); connect = xcb_dri2_connect_reply (dri2_dpy->conn, connect_cookie, NULL); - if (connect->driver_name_length == 0 && connect->device_name_length == 0) { + if (connect == NULL || + connect->driver_name_length + connect->device_name_length == 0) { _eglLog(_EGL_FATAL, "DRI2: failed to authenticate"); - goto handle_error; + return EGL_FALSE; + } + + dri2_dpy->device_name = + dri2_strndup(xcb_dri2_connect_device_name (connect), + xcb_dri2_connect_device_name_length (connect)); + + dri2_dpy->driver_name = + dri2_strndup(xcb_dri2_connect_driver_name (connect), + xcb_dri2_connect_driver_name_length (connect)); + + if (dri2_dpy->device_name == NULL || dri2_dpy->driver_name == NULL) { + free(dri2_dpy->device_name); + free(dri2_dpy->driver_name); + free(connect); + return EGL_FALSE; + } + free(connect); + + return EGL_TRUE; +} + +static EGLBoolean +dri2_authenticate(struct dri2_egl_display *dri2_dpy) +{ + xcb_dri2_authenticate_reply_t *authenticate; + xcb_dri2_authenticate_cookie_t authenticate_cookie; + xcb_screen_iterator_t s; + drm_magic_t magic; + + if (drmGetMagic(dri2_dpy->fd, &magic)) { + _eglLog(_EGL_FATAL, "DRI2: failed to get drm magic"); + return EGL_FALSE; + } + + s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); + authenticate_cookie = + xcb_dri2_authenticate_unchecked(dri2_dpy->conn, s.data->root, magic); + authenticate = + xcb_dri2_authenticate_reply(dri2_dpy->conn, authenticate_cookie, NULL); + if (authenticate == NULL || !authenticate->authenticated) { + _eglLog(_EGL_FATAL, "DRI2: failed to authenticate"); + free(authenticate); + return EGL_FALSE; + } + + free(authenticate); + + return EGL_TRUE; +} + +static EGLBoolean +dri2_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; + + s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); + d = xcb_screen_allowed_depths_iterator(s.data); + id = 1; + while (d.rem > 0) { + EGLBoolean class_added[6] = { 0, }; + + visuals = xcb_depth_visuals(d.data); + for (i = 0; i < xcb_depth_visuals_length(d.data); i++) { + if (class_added[visuals[i]._class]) + continue; + + class_added[visuals[i]._class] = EGL_TRUE; + for (j = 0; dri2_dpy->driver_configs[j]; j++) + dri2_add_config(disp, dri2_dpy->driver_configs[j], + id++, d.data->depth, &visuals[i]); + } + + xcb_depth_next(&d); + } + + if (!disp->NumConfigs) { + _eglLog(_EGL_WARNING, "DRI2: failed to create any config"); + return EGL_FALSE; + } + + return EGL_TRUE; +} + +/** + * Called via eglInitialize(), GLX_drv->API.Initialize(). + */ +static EGLBoolean +dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, + EGLint *major, EGLint *minor) +{ + const __DRIextension **extensions; + struct dri2_egl_display *dri2_dpy; + char path[PATH_MAX], *search_paths, *p, *next, *end; + + dri2_dpy = malloc(sizeof *dri2_dpy); + if (!dri2_dpy) + return _eglError(EGL_BAD_ALLOC, "eglInitialize"); + + disp->DriverData = (void *) dri2_dpy; + if (disp->NativeDisplay == NULL) { + dri2_dpy->conn = xcb_connect(0, 0); + if (!dri2_dpy->conn) { + _eglLog(_EGL_WARNING, "DRI2: xcb_connect failed"); + goto cleanup_dpy; + } + } else { + dri2_dpy->conn = XGetXCBConnection(disp->NativeDisplay); + } + + if (dri2_dpy->conn == NULL) + goto cleanup_conn; + + if (dri2_dpy->conn) { + if (!dri2_connect(dri2_dpy)) + goto cleanup_conn; } search_paths = NULL; @@ -496,19 +678,12 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, dri2_dpy->driver = NULL; end = search_paths + strlen(search_paths); for (p = search_paths; p < end && dri2_dpy->driver == NULL; p = next + 1) { - int path_len; - next = strchr(p, ':'); if (next == NULL) next = end; - path_len = next - p; snprintf(path, sizeof path, - dri_driver_format, - path_len, p, - xcb_dri2_connect_driver_name_length (connect), - xcb_dri2_connect_driver_name (connect)); - + dri_driver_format, (int) (next - p), p, dri2_dpy->driver_name); dri2_dpy->driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); } @@ -516,7 +691,7 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, _eglLog(_EGL_FATAL, "DRI2: failed to open any driver (search paths %s)", search_paths); - goto handle_error; + goto cleanup_conn; } _eglLog(_EGL_DEBUG, "DRI2: dlopen(%s)", path); @@ -524,48 +699,22 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, if (extensions == NULL) { _eglLog(_EGL_FATAL, "DRI2: driver exports no extensions (%s)", dlerror()); - goto handle_error; + goto cleanup_driver; } - for (i = 0; extensions[i]; i++) { - if (strcmp(extensions[i]->name, __DRI_CORE) == 0) - dri2_dpy->core = (__DRIcoreExtension *) extensions[i]; - if (strcmp(extensions[i]->name, __DRI_DRI2) == 0) - dri2_dpy->dri2 = (__DRIdri2Extension *) extensions[i]; - } - - if (dri2_dpy->core == NULL) { - _eglLog(_EGL_FATAL, "DRI2: driver has no core extension"); - goto handle_error; - } + if (!dri2_bind_extensions(dri2_dpy, dri2_driver_extensions, extensions)) + goto cleanup_driver; - if (dri2_dpy->dri2 == NULL) { - _eglLog(_EGL_FATAL, "DRI2: driver has no dri2 extension"); - goto handle_error; - } - - snprintf(path, sizeof path, "%.*s", - xcb_dri2_connect_device_name_length (connect), - xcb_dri2_connect_device_name (connect)); - dri2_dpy->fd = open (path, O_RDWR); + dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR); if (dri2_dpy->fd == -1) { _eglLog(_EGL_FATAL, "DRI2: could not open %s (%s)", path, strerror(errno)); - goto handle_error; - } - - if (drmGetMagic(dri2_dpy->fd, &magic)) { - _eglLog(_EGL_FATAL, "DRI2: failed to get drm magic"); - goto handle_error; + goto cleanup_driver; } - authenticate_cookie = xcb_dri2_authenticate_unchecked (dri2_dpy->conn, - s.data->root, magic); - authenticate = xcb_dri2_authenticate_reply (dri2_dpy->conn, - authenticate_cookie, NULL); - if (authenticate == NULL || !authenticate->authenticated) { - _eglLog(_EGL_FATAL, "DRI2: failed to authenticate"); - goto handle_error; + if (dri2_dpy->conn) { + if (!dri2_authenticate(dri2_dpy)) + goto cleanup_fd; } if (dri2_dpy->dri2_minor >= 1) { @@ -583,51 +732,57 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp, dri2_dpy->loader_extension.getBuffersWithFormat = NULL; } + dri2_dpy->image_lookup_extension.base.name = __DRI_IMAGE_LOOKUP; + dri2_dpy->image_lookup_extension.base.version = 1; + dri2_dpy->image_lookup_extension.lookupEGLImage = dri2_lookup_egl_image; + dri2_dpy->extensions[0] = &dri2_dpy->loader_extension.base; - dri2_dpy->extensions[1] = NULL; + dri2_dpy->extensions[1] = &dri2_dpy->image_lookup_extension.base; + dri2_dpy->extensions[2] = NULL; dri2_dpy->dri_screen = dri2_dpy->dri2->createNewScreen(0, dri2_dpy->fd, dri2_dpy->extensions, - &driver_configs, dri2_dpy); + &dri2_dpy->driver_configs, dri2_dpy); if (dri2_dpy->dri_screen == NULL) { _eglLog(_EGL_FATAL, "DRI2: failed to create dri screen"); - free(dri2_dpy); - goto handle_error; + goto cleanup_fd; } extensions = dri2_dpy->core->getExtensions(dri2_dpy->dri_screen); - for (i = 0; extensions[i]; i++) { - _eglLog(_EGL_DEBUG, "DRI2: found extension `%s'", extensions[i]->name); - if ((strcmp(extensions[i]->name, __DRI2_FLUSH) == 0)) - dri2_dpy->flush = (__DRI2flushExtension *) extensions[i]; - } + if (!dri2_bind_extensions(dri2_dpy, dri2_core_extensions, extensions)) + goto cleanup_dri_screen; - if (dri2_dpy->flush == NULL) { - _eglLog(_EGL_FATAL, "DRI2: driver doesn't support the flush extension"); - free(dri2_dpy); - goto handle_error; - } - - for (i = 0; driver_configs[i]; i++) - dri2_add_config(disp, driver_configs[i], i + 1); - if (!disp->NumConfigs) { - _eglLog(_EGL_WARNING, "DRI2: failed to create any config"); - goto handle_error; + if (dri2_dpy->conn) { + if (!dri2_add_configs_for_visuals(dri2_dpy, disp)) + goto cleanup_configs; } disp->ClientAPIsMask = EGL_OPENGL_BIT; + disp->Extensions.KHR_image_base = EGL_TRUE; + disp->Extensions.KHR_image_pixmap = EGL_TRUE; + disp->Extensions.KHR_gl_renderbuffer_image = EGL_TRUE; /* we're supporting EGL 1.4 */ *major = 1; *minor = 4; - free (connect); return EGL_TRUE; - handle_error: - free(connect); + cleanup_configs: + _eglCleanupDisplay(disp); + cleanup_dri_screen: + dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); + cleanup_fd: + close(dri2_dpy->fd); + cleanup_driver: + dlclose(dri2_dpy->driver); + cleanup_conn: + if (disp->NativeDisplay == NULL) + xcb_disconnect(dri2_dpy->conn); + cleanup_dpy: free(dri2_dpy); + return EGL_FALSE; } @@ -642,10 +797,12 @@ dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp) _eglReleaseDisplayResources(drv, disp); _eglCleanupDisplay(disp); + dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); close(dri2_dpy->fd); dlclose(dri2_dpy->driver); + if (disp->NativeDisplay == NULL) + xcb_disconnect(dri2_dpy->conn); free(dri2_dpy); - disp->DriverData = NULL; return EGL_TRUE; @@ -670,10 +827,8 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, return NULL; } - if (!_eglInitContext(&dri2_ctx->base, disp, conf, attrib_list)) { - free(dri2_ctx); - return NULL; - } + if (!_eglInitContext(&dri2_ctx->base, disp, conf, attrib_list)) + goto cleanup; dri2_ctx->dri_context = dri2_dpy->dri2->createNewContext(dri2_dpy->dri_screen, @@ -682,12 +837,14 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, dri2_ctx_shared->dri_context : NULL, dri2_ctx); - if (!dri2_ctx->dri_context) { - free(dri2_ctx); - return NULL; - } + if (!dri2_ctx->dri_context) + goto cleanup; return &dri2_ctx->base; + + cleanup: + free(dri2_ctx); + return NULL; } static EGLBoolean @@ -718,6 +875,7 @@ static EGLBoolean dri2_make_current(_EGLDriver *drv, _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); @@ -729,6 +887,10 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, if (!_eglBindContext(&ctx, &dsurf, &rsurf)) return EGL_FALSE; + /* flush before context switch */ + if (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; @@ -768,14 +930,12 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, dri2_surf = malloc(sizeof *dri2_surf); if (!dri2_surf) { - _eglError(EGL_BAD_ALLOC, "eglCreateWindowSurface"); + _eglError(EGL_BAD_ALLOC, "dri2_create_surface"); return NULL; } - if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list)) { - free(dri2_surf); - return NULL; - } + if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list)) + goto cleanup_surf; dri2_surf->region = XCB_NONE; if (type == EGL_PBUFFER_BIT) { @@ -792,27 +952,38 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, dri2_surf->dri_drawable = (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen, dri2_conf->dri_config, dri2_surf); - if (dri2_surf == NULL) { - _eglError(EGL_BAD_ALLOC, "eglCreateWindowSurface"); - free(dri2_surf); - return NULL; + if (dri2_surf->dri_drawable == NULL) { + _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable"); + goto cleanup_pixmap; } xcb_dri2_create_drawable (dri2_dpy->conn, dri2_surf->drawable); - cookie = xcb_get_geometry (dri2_dpy->conn, dri2_surf->drawable); - reply = xcb_get_geometry_reply (dri2_dpy->conn, cookie, &error); - if (reply == NULL || error != NULL) { - _eglError(EGL_BAD_ALLOC, "xcb_get_geometry"); - free(dri2_surf); - free(error); - return NULL; + if (type != EGL_PBUFFER_BIT) { + cookie = xcb_get_geometry (dri2_dpy->conn, dri2_surf->drawable); + reply = xcb_get_geometry_reply (dri2_dpy->conn, cookie, &error); + if (reply == NULL || error != NULL) { + _eglError(EGL_BAD_ALLOC, "xcb_get_geometry"); + free(error); + goto cleanup_dri_drawable; + } + + dri2_surf->base.Width = reply->width; + dri2_surf->base.Height = reply->height; + free(reply); } - dri2_surf->base.Width = reply->width; - dri2_surf->base.Height = reply->height; - free(reply); return &dri2_surf->base; + + cleanup_dri_drawable: + dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable); + cleanup_pixmap: + if (type == EGL_PBUFFER_BIT) + xcb_free_pixmap(dri2_dpy->conn, dri2_surf->drawable); + cleanup_surf: + free(dri2_surf); + + return NULL; } /** @@ -847,30 +1018,39 @@ dri2_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp, static EGLBoolean dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) { + 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; 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); #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. */ + * 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_surf->have_back) + if (!dri2_surf->have_fake_front) return EGL_TRUE; cookie = xcb_dri2_copy_region_unchecked(dri2_dpy->conn, dri2_surf->drawable, dri2_surf->region, XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT, - XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT); + XCB_DRI2_ATTACHMENT_BUFFER_FAKE_FRONT_LEFT); free(xcb_dri2_copy_region_reply(dri2_dpy->conn, cookie, NULL)); return EGL_TRUE; @@ -943,6 +1123,215 @@ dri2_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, return EGL_TRUE; } +static EGLBoolean +dri2_bind_tex_image(_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; + + ctx = _eglGetCurrentContext(); + dri2_ctx = dri2_egl_context(ctx); + + if (buffer != EGL_BACK_BUFFER) { + _eglError(EGL_BAD_PARAMETER, "eglBindTexImage"); + return EGL_FALSE; + } + + /* We allow binding pixmaps too... Not conformat, but we can do it + * for free and it's useful for X compositors. Supposedly there's + * a EGL_NOKIA_texture_from_pixmap extension that allows that, but + * I couldn't find it at this time. */ + if ((dri2_surf->base.Type & (EGL_PBUFFER_BIT | EGL_PIXMAP_BIT)) == 0) { + _eglError(EGL_BAD_SURFACE, "eglBindTexImage"); + return EGL_FALSE; + } + + switch (dri2_surf->base.TextureFormat) { + case EGL_TEXTURE_RGB: + format = __DRI_TEXTURE_FORMAT_RGB; + break; + case EGL_TEXTURE_RGBA: + format = __DRI_TEXTURE_FORMAT_RGBA; + break; + default: + _eglError(EGL_BAD_MATCH, "eglBindTexImage"); + return EGL_FALSE; + } + + switch (dri2_surf->base.TextureTarget) { + case EGL_TEXTURE_2D: + target = GL_TEXTURE_2D; + break; + default: + _eglError(EGL_BAD_PARAMETER, "eglBindTexImage"); + return EGL_FALSE; + } + + (*dri2_dpy->tex_buffer->setTexBuffer2)(dri2_ctx->dri_context, + target, format, + dri2_surf->dri_drawable); + + return dri2_surf->base.BoundToTexture = EGL_TRUE; +} + +static EGLBoolean +dri2_release_tex_image(_EGLDriver *drv, + _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) +{ + return EGL_TRUE; +} + +static _EGLImage * +dri2_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx, + 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); + struct dri2_egl_image *dri2_img; + unsigned int attachments[1]; + xcb_drawable_t drawable; + xcb_dri2_get_buffers_cookie_t buffers_cookie; + xcb_dri2_get_buffers_reply_t *buffers_reply; + xcb_dri2_dri2_buffer_t *buffers; + xcb_get_geometry_cookie_t geometry_cookie; + xcb_get_geometry_reply_t *geometry_reply; + xcb_generic_error_t *error; + int stride, format; + + drawable = (xcb_drawable_t) buffer; + xcb_dri2_create_drawable (dri2_dpy->conn, drawable); + attachments[0] = XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT; + buffers_cookie = + xcb_dri2_get_buffers_unchecked (dri2_dpy->conn, + drawable, 1, 1, attachments); + geometry_cookie = xcb_get_geometry (dri2_dpy->conn, drawable); + buffers_reply = xcb_dri2_get_buffers_reply (dri2_dpy->conn, + buffers_cookie, NULL); + buffers = xcb_dri2_get_buffers_buffers (buffers_reply); + if (buffers == NULL) { + return NULL; + } + + geometry_reply = xcb_get_geometry_reply (dri2_dpy->conn, + geometry_cookie, &error); + if (geometry_reply == NULL || error != NULL) { + _eglError(EGL_BAD_ALLOC, "xcb_get_geometry"); + free(error); + free(buffers_reply); + } + + switch (geometry_reply->depth) { + case 16: + format = __DRI_IMAGE_FORMAT_RGB565; + break; + case 24: + format = __DRI_IMAGE_FORMAT_XRGB8888; + break; + case 32: + format = __DRI_IMAGE_FORMAT_ARGB8888; + break; + default: + _eglError(EGL_BAD_PARAMETER, + "dri2_create_image_khr: unsupported pixmap depth"); + free(buffers_reply); + free(geometry_reply); + return NULL; + } + + dri2_img = malloc(sizeof *dri2_img); + if (!dri2_img) { + free(buffers_reply); + free(geometry_reply); + _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + if (!_eglInitImage(&dri2_img->base, disp, attr_list)) { + free(buffers_reply); + free(geometry_reply); + return EGL_NO_IMAGE_KHR; + } + + stride = buffers[0].pitch / buffers[0].cpp; + dri2_img->dri_image = + dri2_dpy->image->createImageFromName(dri2_ctx->dri_context, + buffers_reply->width, + buffers_reply->height, + format, + buffers[0].name, + stride, + dri2_img); + + free(buffers_reply); + free(geometry_reply); + + return &dri2_img->base; +} + +static _EGLImage * +dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx, + 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); + struct dri2_egl_image *dri2_img; + GLuint renderbuffer = (GLuint) buffer; + + if (renderbuffer == 0) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + dri2_img = malloc(sizeof *dri2_img); + if (!dri2_img) { + _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + if (!_eglInitImage(&dri2_img->base, disp, attr_list)) + return EGL_NO_IMAGE_KHR; + + dri2_img->dri_image = + dri2_dpy->image->createImageFromRenderbuffer(dri2_ctx->dri_context, + renderbuffer, + dri2_img); + + return &dri2_img->base; +} + +static _EGLImage * +dri2_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, + _EGLContext *ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint *attr_list) +{ + switch (target) { + case EGL_NATIVE_PIXMAP_KHR: + return dri2_create_image_khr_pixmap(disp, ctx, buffer, attr_list); + case EGL_GL_RENDERBUFFER_KHR: + return dri2_create_image_khr_renderbuffer(disp, ctx, buffer, attr_list); + default: + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } +} + +static EGLBoolean +dri2_destroy_image_khr(_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); + + dri2_dpy->image->destroyImage(dri2_img->dri_image); + free(dri2_img); + + return EGL_TRUE; +} + /** * This is the main entrypoint into the driver, called by libEGL. * Create a new _EGLDriver object and init its dispatch table. @@ -970,9 +1359,16 @@ _eglMain(const char *args) dri2_drv->base.API.WaitClient = dri2_wait_client; dri2_drv->base.API.WaitNative = dri2_wait_native; dri2_drv->base.API.CopyBuffers = dri2_copy_buffers; + dri2_drv->base.API.BindTexImage = dri2_bind_tex_image; + dri2_drv->base.API.ReleaseTexImage = dri2_release_tex_image; + dri2_drv->base.API.CreateImageKHR = dri2_create_image_khr; + dri2_drv->base.API.DestroyImageKHR = dri2_destroy_image_khr; dri2_drv->base.Name = "DRI2"; dri2_drv->base.Unload = dri2_unload; + dri2_drv->glFlush = + (void (*)(void)) dri2_get_proc_address(&dri2_drv->base, "glFlush"); + return &dri2_drv->base; }