X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fglx%2Fdri3_glx.c;h=96f13e6a07ba1c9b6046ca5eccaca3810156f429;hb=10130ccd8ca9e28e417268f4e5681b5c25398516;hp=1834c6d4a8e933695e7a6b608e2c367aac8c91b2;hpb=568a27588d13146d0fd7cb4e775e1ac0187e5e29;p=mesa.git diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c index 1834c6d4a8e..96f13e6a07b 100644 --- a/src/glx/dri3_glx.c +++ b/src/glx/dri3_glx.c @@ -65,9 +65,7 @@ #include #include #include -#include "glapi.h" #include "glxclient.h" -#include "xf86dri.h" #include #include #include @@ -75,9 +73,10 @@ #include #include -#include "xf86drm.h" #include "dri_common.h" #include "dri3_priv.h" +#include "loader.h" +#include "dri2.h" static const struct glx_context_vtable dri3_context_vtable; @@ -265,14 +264,39 @@ dri3_create_context(struct glx_screen *base, 0, NULL, &error); } +static void +dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer); + +static void +dri3_update_num_back(struct dri3_drawable *priv) +{ + priv->num_back = 1; + if (priv->flipping) { + if (!priv->is_pixmap && !(priv->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC)) + priv->num_back++; + priv->num_back++; + } + if (priv->swap_interval == 0) + priv->num_back++; +} + static void dri3_destroy_drawable(__GLXDRIdrawable *base) { struct dri3_screen *psc = (struct dri3_screen *) base->psc; struct dri3_drawable *pdraw = (struct dri3_drawable *) base; + xcb_connection_t *c = XGetXCBConnection(pdraw->base.psc->dpy); + int i; (*psc->core->destroyDrawable) (pdraw->driDrawable); + for (i = 0; i < DRI3_NUM_BUFFERS; i++) { + if (pdraw->buffers[i]) + dri3_free_render_buffer(pdraw, pdraw->buffers[i]); + } + + if (pdraw->special_event) + xcb_unregister_for_special_event(c, pdraw->special_event); free(pdraw); } @@ -313,6 +337,8 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable, break; } + dri3_update_num_back(pdraw); + (void) __glXInitialize(psc->base.dpy); /* Create a new drawable */ @@ -335,12 +361,34 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable, return &pdraw->base; } +static void +show_fps(struct dri3_drawable *draw, uint64_t current_ust) +{ + const uint64_t interval = + ((struct dri3_screen *) draw->base.psc)->show_fps_interval; + + draw->frames++; + + /* DRI3+Present together uses microseconds for UST. */ + if (draw->previous_ust + interval * 1000000 <= current_ust) { + if (draw->previous_ust) { + fprintf(stderr, "libGL: FPS = %.1f\n", + ((uint64_t) draw->frames * 1000000) / + (double)(current_ust - draw->previous_ust)); + } + draw->frames = 0; + draw->previous_ust = current_ust; + } +} + /* * Process one Present event */ static void dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_t *ge) { + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; + switch (ge->evtype) { case XCB_PRESENT_CONFIGURE_NOTIFY: { xcb_present_configure_notify_event_t *ce = (void *) ge; @@ -352,12 +400,34 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_ case XCB_PRESENT_COMPLETE_NOTIFY: { xcb_present_complete_notify_event_t *ce = (void *) ge; - if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) - priv->present_event_serial = ce->serial; - else - priv->present_msc_event_serial = ce->serial; - priv->ust = ce->ust; - priv->msc = ce->msc; + /* Compute the processed SBC number from the received 32-bit serial number merged + * with the upper 32-bits of the sent 64-bit serial number while checking for + * wrap + */ + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) { + priv->recv_sbc = (priv->send_sbc & 0xffffffff00000000LL) | ce->serial; + if (priv->recv_sbc > priv->send_sbc) + priv->recv_sbc -= 0x100000000; + switch (ce->mode) { + case XCB_PRESENT_COMPLETE_MODE_FLIP: + priv->flipping = true; + break; + case XCB_PRESENT_COMPLETE_MODE_COPY: + priv->flipping = false; + break; + } + dri3_update_num_back(priv); + + if (psc->show_fps_interval) + show_fps(priv, ce->ust); + + priv->ust = ce->ust; + priv->msc = ce->msc; + } else { + priv->recv_msc_serial = ce->serial; + priv->notify_ust = ce->ust; + priv->notify_msc = ce->msc; + } break; } case XCB_PRESENT_EVENT_IDLE_NOTIFY: { @@ -369,6 +439,10 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_ if (buf && buf->pixmap == ie->pixmap) { buf->busy = 0; + if (priv->num_back <= b && b < DRI3_MAX_BACK) { + dri3_free_render_buffer(priv, buf); + priv->buffers[b] = NULL; + } break; } } @@ -378,20 +452,41 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_ free(ge); } +static bool +dri3_wait_for_event(__GLXDRIdrawable *pdraw) +{ + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; + xcb_generic_event_t *ev; + xcb_present_generic_event_t *ge; + + xcb_flush(c); + ev = xcb_wait_for_special_event(c, priv->special_event); + if (!ev) + return false; + ge = (void *) ev; + dri3_handle_present_event(priv, ge); + return true; +} + +/** dri3_wait_for_msc + * + * Get the X server to send an event when the target msc/divisor/remainder is + * reached. + */ static int dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc) { xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; - xcb_generic_event_t *ev; - xcb_present_generic_event_t *ge; + uint32_t msc_serial; /* Ask for the an event for the target MSC */ - ++priv->present_msc_request_serial; + msc_serial = ++priv->send_msc_serial; xcb_present_notify_msc(c, priv->base.xDrawable, - priv->present_msc_request_serial, + msc_serial, target_msc, divisor, remainder); @@ -400,22 +495,24 @@ dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, /* Wait for the event */ if (priv->special_event) { - while (priv->present_msc_request_serial != priv->present_msc_event_serial) { - ev = xcb_wait_for_special_event(c, priv->special_event); - if (!ev) - break; - ge = (void *) ev; - dri3_handle_present_event(priv, ge); + while ((int32_t) (msc_serial - priv->recv_msc_serial) > 0) { + if (!dri3_wait_for_event(pdraw)) + return 0; } } - *ust = priv->ust; - *msc = priv->msc; - *sbc = priv->sbc; + *ust = priv->notify_ust; + *msc = priv->notify_msc; + *sbc = priv->recv_sbc; return 1; } +/** dri3_drawable_get_msc + * + * Return the current UST/MSC/SBC triplet by asking the server + * for an event + */ static int dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw, int64_t *ust, int64_t *msc, int64_t *sbc) @@ -425,12 +522,9 @@ dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw, /** dri3_wait_for_sbc * - * Wait for the swap buffer count to increase. The only way this - * can happen is if some other thread is doing swap buffers as - * we no longer share swap buffer counts with other processes. - * - * I'm not sure this is actually useful as such, and so this - * implementation is a kludge that just polls once a second + * Wait for the completed swap buffer count to reach the specified + * target. Presumably the application knows that this will be reached with + * outstanding complete events, or we're going to be here awhile. */ static int dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust, @@ -438,10 +532,24 @@ dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust, { struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; - while (priv->sbc < target_sbc) { - sleep(1); + /* From the GLX_OML_sync_control spec: + * + * "If = 0, the function will block until all previous + * swaps requested with glXSwapBuffersMscOML for that window have + * completed." + */ + if (!target_sbc) + target_sbc = priv->send_sbc; + + while (priv->recv_sbc < target_sbc) { + if (!dri3_wait_for_event(pdraw)) + return 0; } - return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc, sbc); + + *ust = priv->ust; + *msc = priv->msc; + *sbc = priv->recv_sbc; + return 1; } /** @@ -528,22 +636,44 @@ dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y, { struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; struct dri3_screen *psc = (struct dri3_screen *) pdraw->psc; + struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext(); xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); - struct dri3_buffer *back = dri3_back_buffer(priv); + struct dri3_buffer *back; - unsigned flags; + unsigned flags = __DRI2_FLUSH_DRAWABLE; /* Check we have the right attachments */ if (!priv->have_back || priv->is_pixmap) return; - flags = __DRI2_FLUSH_DRAWABLE; if (flush) flags |= __DRI2_FLUSH_CONTEXT; dri3_flush(psc, priv, flags, __DRI2_THROTTLE_SWAPBUFFER); + back = dri3_back_buffer(priv); y = priv->height - y - height; + if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) { + /* Update the linear buffer part of the back buffer + * for the dri3_copy_area operation + */ + psc->image->blitImage(pcp->driContext, + back->linear_buffer, + back->image, + 0, 0, back->width, + back->height, + 0, 0, back->width, + back->height, __BLIT_FLAG_FLUSH); + /* We use blitImage to update our fake front, + */ + if (priv->have_fake_front) + psc->image->blitImage(pcp->driContext, + dri3_fake_front_buffer(priv)->image, + back->image, + x, y, width, height, + x, y, width, height, __BLIT_FLAG_FLUSH); + } + dri3_fence_reset(c, back); dri3_copy_area(c, dri3_back_buffer(priv)->pixmap, @@ -554,7 +684,7 @@ dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y, /* Refresh the fake front (if present) after we just damaged the real * front. */ - if (priv->have_fake_front) { + if (priv->have_fake_front && !psc->is_different_gpu) { dri3_fence_reset(c, dri3_fake_front_buffer(priv)); dri3_copy_area(c, dri3_back_buffer(priv)->pixmap, @@ -587,25 +717,62 @@ dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src) static void dri3_wait_x(struct glx_context *gc) { + struct dri3_context *pcp = (struct dri3_context *) gc; struct dri3_drawable *priv = (struct dri3_drawable *) GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable); + struct dri3_screen *psc; + struct dri3_buffer *front; if (priv == NULL || !priv->have_fake_front) return; - dri3_copy_drawable(priv, dri3_fake_front_buffer(priv)->pixmap, priv->base.xDrawable); + psc = (struct dri3_screen *) priv->base.psc; + front = dri3_fake_front_buffer(priv); + + dri3_copy_drawable(priv, front->pixmap, priv->base.xDrawable); + + /* In the psc->is_different_gpu case, the linear buffer has been updated, + * but not yet the tiled buffer. + * Copy back to the tiled buffer we use for rendering. + * Note that we don't need flushing. + */ + if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) + psc->image->blitImage(pcp->driContext, + front->image, + front->linear_buffer, + 0, 0, front->width, + front->height, + 0, 0, front->width, + front->height, 0); } static void dri3_wait_gl(struct glx_context *gc) { + struct dri3_context *pcp = (struct dri3_context *) gc; struct dri3_drawable *priv = (struct dri3_drawable *) GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable); + struct dri3_screen *psc; + struct dri3_buffer *front; if (priv == NULL || !priv->have_fake_front) return; - dri3_copy_drawable(priv, priv->base.xDrawable, dri3_fake_front_buffer(priv)->pixmap); + psc = (struct dri3_screen *) priv->base.psc; + front = dri3_fake_front_buffer(priv); + + /* In the psc->is_different_gpu case, we update the linear_buffer + * before updating the real front. + */ + if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) + psc->image->blitImage(pcp->driContext, + front->linear_buffer, + front->image, + 0, 0, front->width, + front->height, + 0, 0, front->width, + front->height, __BLIT_FLAG_FLUSH); + dri3_copy_drawable(priv, priv->base.xDrawable, front->pixmap); } /** @@ -673,6 +840,7 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, struct dri3_screen *psc = (struct dri3_screen *) glx_screen; Display *dpy = glx_screen->dpy; struct dri3_buffer *buffer; + __DRIimage *pixmap_buffer; xcb_connection_t *c = XGetXCBConnection(dpy); xcb_pixmap_t pixmap; xcb_sync_fence_t sync_fence; @@ -685,11 +853,15 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, */ fence_fd = xshmfence_alloc_shm(); - if (fence_fd < 0) + if (fence_fd < 0) { + ErrorMessageF("DRI3 Fence object allocation failure %s\n", strerror(errno)); return NULL; + } shm_fence = xshmfence_map_shm(fence_fd); - if (shm_fence == NULL) + if (shm_fence == NULL) { + ErrorMessageF("DRI3 Fence object map failure %s\n", strerror(errno)); goto no_shm_fence; + } /* Allocate the image from the driver */ @@ -698,28 +870,63 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, goto no_buffer; buffer->cpp = dri3_cpp_for_format(format); - if (!buffer->cpp) + if (!buffer->cpp) { + ErrorMessageF("DRI3 buffer format %d invalid\n", format); goto no_image; + } - buffer->image = (*psc->image->createImage) (psc->driScreen, - width, height, - format, - __DRI_IMAGE_USE_SHARE|__DRI_IMAGE_USE_SCANOUT, - buffer); - + if (!psc->is_different_gpu) { + buffer->image = (*psc->image->createImage) (psc->driScreen, + width, height, + format, + __DRI_IMAGE_USE_SHARE | + __DRI_IMAGE_USE_SCANOUT, + buffer); + pixmap_buffer = buffer->image; + + if (!buffer->image) { + ErrorMessageF("DRI3 gpu image creation failure\n"); + goto no_image; + } + } else { + buffer->image = (*psc->image->createImage) (psc->driScreen, + width, height, + format, + 0, + buffer); + + if (!buffer->image) { + ErrorMessageF("DRI3 other gpu image creation failure\n"); + goto no_image; + } - if (!buffer->image) - goto no_image; + buffer->linear_buffer = (*psc->image->createImage) (psc->driScreen, + width, height, + format, + __DRI_IMAGE_USE_SHARE | + __DRI_IMAGE_USE_LINEAR, + buffer); + pixmap_buffer = buffer->linear_buffer; + + if (!buffer->linear_buffer) { + ErrorMessageF("DRI3 gpu linear image creation failure\n"); + goto no_linear_buffer; + } + } /* X wants the stride, so ask the image for it */ - if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_STRIDE, &stride)) + if (!(*psc->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, &stride)) { + ErrorMessageF("DRI3 get image stride failed\n"); goto no_buffer_attrib; + } buffer->pitch = stride; - if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_FD, &buffer_fd)) + if (!(*psc->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, &buffer_fd)) { + ErrorMessageF("DRI3 get image FD failed\n"); goto no_buffer_attrib; + } xcb_dri3_pixmap_from_buffer(c, (pixmap = xcb_generate_id(c)), @@ -736,6 +943,7 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, fence_fd); buffer->pixmap = pixmap; + buffer->own_pixmap = true; buffer->sync_fence = sync_fence; buffer->shm_fence = shm_fence; buffer->width = width; @@ -748,13 +956,17 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, return buffer; no_buffer_attrib: - (*psc->image->destroyImage)(buffer->image); + (*psc->image->destroyImage)(pixmap_buffer); +no_linear_buffer: + if (psc->is_different_gpu) + (*psc->image->destroyImage)(buffer->image); no_image: free(buffer); no_buffer: xshmfence_unmap_shm(shm_fence); no_shm_fence: close(fence_fd); + ErrorMessageF("DRI3 alloc_render_buffer failed\n"); return NULL; } @@ -769,10 +981,13 @@ dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer) struct dri3_screen *psc = (struct dri3_screen *) pdraw->base.psc; xcb_connection_t *c = XGetXCBConnection(pdraw->base.psc->dpy); - xcb_free_pixmap(c, buffer->pixmap); + if (buffer->own_pixmap) + xcb_free_pixmap(c, buffer->pixmap); xcb_sync_destroy_fence(c, buffer->sync_fence); xshmfence_unmap_shm(buffer->shm_fence); (*psc->image->destroyImage)(buffer->image); + if (buffer->linear_buffer) + (*psc->image->destroyImage)(buffer->linear_buffer); free(buffer); } @@ -818,6 +1033,9 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate) xcb_get_geometry_reply_t *geom_reply; xcb_void_cookie_t cookie; xcb_generic_error_t *error; + xcb_present_query_capabilities_cookie_t present_capabilities_cookie; + xcb_present_query_capabilities_reply_t *present_capabilities_reply; + /* Try to select for input on the window. * @@ -836,6 +1054,8 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate) XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY| XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY); + present_capabilities_cookie = xcb_present_query_capabilities(c, priv->base.xDrawable); + /* Create an XCB event queue to hold present events outside of the usual * application event queue */ @@ -865,6 +1085,16 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate) error = xcb_request_check(c, cookie); + present_capabilities_reply = xcb_present_query_capabilities_reply(c, + present_capabilities_cookie, + NULL); + + if (present_capabilities_reply) { + priv->present_capabilities = present_capabilities_reply->capabilities; + free(present_capabilities_reply); + } else + priv->present_capabilities = 0; + if (error) { if (error->error_code != BadWindow) { free(error); @@ -890,6 +1120,7 @@ image_format_to_fourcc(int format) /* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */ switch (format) { + case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888; case __DRI_IMAGE_FORMAT_RGB565: return __DRI_IMAGE_FOURCC_RGB565; case __DRI_IMAGE_FORMAT_XRGB8888: return __DRI_IMAGE_FOURCC_XRGB8888; case __DRI_IMAGE_FORMAT_ARGB8888: return __DRI_IMAGE_FOURCC_ARGB8888; @@ -987,6 +1218,7 @@ dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, goto no_image; buffer->pixmap = pixmap; + buffer->own_pixmap = false; buffer->width = bp_reply->width; buffer->height = bp_reply->height; buffer->buffer_type = buffer_type; @@ -1018,16 +1250,16 @@ dri3_find_back(xcb_connection_t *c, struct dri3_drawable *priv) xcb_present_generic_event_t *ge; for (;;) { + for (b = 0; b < priv->num_back; b++) { + int id = DRI3_BACK_ID((b + priv->cur_back) % priv->num_back); + struct dri3_buffer *buffer = priv->buffers[id]; - for (b = 0; b < DRI3_MAX_BACK; b++) { - int id = DRI3_BACK_ID(b); - struct dri3_buffer *buffer = priv->buffers[id]; - - if (!buffer) - return b; - if (!buffer->busy) - return b; + if (!buffer || !buffer->busy) { + priv->cur_back = id; + return id; + } } + xcb_flush(c); ev = xcb_wait_for_special_event(c, priv->special_event); if (!ev) return -1; @@ -1046,19 +1278,18 @@ dri3_get_buffer(__DRIdrawable *driDrawable, enum dri3_buffer_type buffer_type, void *loaderPrivate) { + struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext(); struct dri3_drawable *priv = loaderPrivate; + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); struct dri3_buffer *buffer; int buf_id; if (buffer_type == dri3_buffer_back) { - int back = dri3_find_back(c, priv); + buf_id = dri3_find_back(c, priv); - if (back < 0) + if (buf_id < 0) return NULL; - - priv->cur_back = back; - buf_id = DRI3_BACK_ID(priv->cur_back); } else { buf_id = DRI3_FRONT_ID; } @@ -1085,14 +1316,24 @@ dri3_get_buffer(__DRIdrawable *driDrawable, switch (buffer_type) { case dri3_buffer_back: if (buffer) { - dri3_fence_reset(c, new_buffer); - dri3_fence_await(c, buffer); - dri3_copy_area(c, - buffer->pixmap, - new_buffer->pixmap, - dri3_drawable_gc(priv), - 0, 0, 0, 0, priv->width, priv->height); + if (!buffer->linear_buffer) { + dri3_fence_reset(c, new_buffer); + dri3_fence_await(c, buffer); + dri3_copy_area(c, + buffer->pixmap, + new_buffer->pixmap, + dri3_drawable_gc(priv), + 0, 0, 0, 0, priv->width, priv->height); dri3_fence_trigger(c, new_buffer); + } else if ((&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) { + psc->image->blitImage(pcp->driContext, + new_buffer->image, + buffer->image, + 0, 0, priv->width, + priv->height, + 0, 0, priv->width, + priv->height, 0); + } dri3_free_render_buffer(priv, buffer); } break; @@ -1104,6 +1345,17 @@ dri3_get_buffer(__DRIdrawable *driDrawable, dri3_drawable_gc(priv), 0, 0, 0, 0, priv->width, priv->height); dri3_fence_trigger(c, new_buffer); + + if (new_buffer->linear_buffer && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) { + dri3_fence_await(c, new_buffer); + psc->image->blitImage(pcp->driContext, + new_buffer->image, + new_buffer->linear_buffer, + 0, 0, priv->width, + priv->height, + 0, 0, priv->width, + priv->height, 0); + } break; } buffer = new_buffer; @@ -1166,6 +1418,7 @@ dri3_get_buffers(__DRIdrawable *driDrawable, struct __DRIimageList *buffers) { struct dri3_drawable *priv = loaderPrivate; + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; struct dri3_buffer *front, *back; buffers->image_mask = 0; @@ -1183,7 +1436,15 @@ dri3_get_buffers(__DRIdrawable *driDrawable, buffer_mask |= __DRI_IMAGE_BUFFER_FRONT; if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) { - if (priv->is_pixmap) + /* All pixmaps are owned by the server gpu. + * When we use a different gpu, we can't use the pixmap + * as buffer since it is potentially tiled a way + * our device can't understand. In this case, use + * a fake front buffer. Hopefully the pixmap + * content will get synced with the fake front + * buffer. + */ + if (priv->is_pixmap && !psc->is_different_gpu) front = dri3_get_pixmap_buffer(driDrawable, format, dri3_buffer_front, @@ -1217,7 +1478,7 @@ dri3_get_buffers(__DRIdrawable *driDrawable, if (front) { buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT; buffers->front = front->image; - priv->have_fake_front = !priv->is_pixmap; + priv->have_fake_front = psc->is_different_gpu || !priv->is_pixmap; } if (back) { @@ -1233,9 +1494,21 @@ dri3_get_buffers(__DRIdrawable *driDrawable, /* The image loader extension record for DRI3 */ static const __DRIimageLoaderExtension imageLoaderExtension = { - {__DRI_IMAGE_LOADER, __DRI_IMAGE_LOADER_VERSION}, - .getBuffers = dri3_get_buffers, - .flushFrontBuffer = dri3_flush_front_buffer, + .base = { __DRI_IMAGE_LOADER, 1 }, + + .getBuffers = dri3_get_buffers, + .flushFrontBuffer = dri3_flush_front_buffer, +}; + +const __DRIuseInvalidateExtension dri3UseInvalidate = { + .base = { __DRI_USE_INVALIDATE, 1 } +}; + +static const __DRIextension *loader_extensions[] = { + &imageLoaderExtension.base, + &systemTimeExtension.base, + &dri3UseInvalidate.base, + NULL }; /** dri3_swap_buffers @@ -1246,57 +1519,105 @@ static int64_t dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, int64_t remainder, Bool flush) { + struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext(); struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; Display *dpy = priv->base.psc->dpy; xcb_connection_t *c = XGetXCBConnection(dpy); - int buf_id = DRI3_BACK_ID(priv->cur_back); + struct dri3_buffer *back; int64_t ret = 0; + uint32_t options = XCB_PRESENT_OPTION_NONE; unsigned flags = __DRI2_FLUSH_DRAWABLE; if (flush) flags |= __DRI2_FLUSH_CONTEXT; dri3_flush(psc, priv, flags, __DRI2_THROTTLE_SWAPBUFFER); + back = priv->buffers[DRI3_BACK_ID(priv->cur_back)]; + if (psc->is_different_gpu && back) { + /* Update the linear buffer before presenting the pixmap */ + psc->image->blitImage(pcp->driContext, + back->linear_buffer, + back->image, + 0, 0, back->width, + back->height, + 0, 0, back->width, + back->height, __BLIT_FLAG_FLUSH); + /* Update the fake front */ + if (priv->have_fake_front) + psc->image->blitImage(pcp->driContext, + priv->buffers[DRI3_FRONT_ID]->image, + back->image, + 0, 0, priv->width, + priv->height, + 0, 0, priv->width, + priv->height, __BLIT_FLAG_FLUSH); + } + dri3_flush_present_events(priv); - if (priv->buffers[buf_id] && !priv->is_pixmap) { - dri3_fence_reset(c, priv->buffers[buf_id]); + if (back && !priv->is_pixmap) { + dri3_fence_reset(c, back); /* Compute when we want the frame shown by taking the last known successful - * MSC and adding in a swap interval for each outstanding swap request + * MSC and adding in a swap interval for each outstanding swap request. + * target_msc=divisor=remainder=0 means "Use glXSwapBuffers() semantic" */ - ++priv->present_request_serial; - if (target_msc == 0) - target_msc = priv->msc + priv->swap_interval * (priv->present_request_serial - priv->present_event_serial); + ++priv->send_sbc; + if (target_msc == 0 && divisor == 0 && remainder == 0) + target_msc = priv->msc + priv->swap_interval * (priv->send_sbc - priv->recv_sbc); + else if (divisor == 0 && remainder > 0) { + /* From the GLX_OML_sync_control spec: + * + * "If = 0, the swap will occur when MSC becomes + * greater than or equal to ." + * + * Note that there's no mention of the remainder. The Present extension + * throws BadValue for remainder != 0 with divisor == 0, so just drop + * the passed in value. + */ + remainder = 0; + } - priv->buffers[buf_id]->busy = 1; + /* From the GLX_EXT_swap_control spec: + * + * "If is set to a value of 0, buffer swaps are not + * synchronized to a video frame." + * + * Implementation note: It is possible to enable triple buffering behaviour + * by not using XCB_PRESENT_OPTION_ASYNC, but this should not be the default. + */ + if (priv->swap_interval == 0) + options |= XCB_PRESENT_OPTION_ASYNC; + + back->busy = 1; + back->last_swap = priv->send_sbc; xcb_present_pixmap(c, priv->base.xDrawable, - priv->buffers[buf_id]->pixmap, - priv->present_request_serial, + back->pixmap, + (uint32_t) priv->send_sbc, 0, /* valid */ 0, /* update */ 0, /* x_off */ 0, /* y_off */ None, /* target_crtc */ None, - priv->buffers[buf_id]->sync_fence, - XCB_PRESENT_OPTION_NONE, + back->sync_fence, + options, target_msc, divisor, remainder, 0, NULL); - ret = ++priv->sbc; + ret = (int64_t) priv->send_sbc; /* If there's a fake front, then copy the source back buffer * to the fake front to keep it up to date. This needs * to reset the fence and make future users block until * the X server is done copying the bits */ - if (priv->have_fake_front) { + if (priv->have_fake_front && !psc->is_different_gpu) { dri3_fence_reset(c, priv->buffers[DRI3_FRONT_ID]); dri3_copy_area(c, - priv->buffers[buf_id]->pixmap, + back->pixmap, priv->buffers[DRI3_FRONT_ID]->pixmap, dri3_drawable_gc(priv), 0, 0, 0, 0, priv->width, priv->height); @@ -1307,9 +1628,27 @@ dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, ++(*priv->stamp); } + (*psc->f->invalidate)(priv->driDrawable); + return ret; } +static int +dri3_get_buffer_age(__GLXDRIdrawable *pdraw) +{ + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; + int back_id = DRI3_BACK_ID(dri3_find_back(c, priv)); + + if (back_id < 0 || !priv->buffers[back_id]) + return 0; + + if (priv->buffers[back_id]->last_swap != 0) + return priv->send_sbc - priv->buffers[back_id]->last_swap + 1; + else + return 0; +} + /** dri3_open * * Wrapper around xcb_dri3_open @@ -1322,14 +1661,13 @@ dri3_open(Display *dpy, xcb_dri3_open_cookie_t cookie; xcb_dri3_open_reply_t *reply; xcb_connection_t *c = XGetXCBConnection(dpy); - xcb_generic_error_t *error; int fd; cookie = xcb_dri3_open(c, root, provider); - reply = xcb_dri3_open_reply(c, cookie, &error); + reply = xcb_dri3_open_reply(c, cookie, NULL); if (!reply) return -1; @@ -1341,6 +1679,8 @@ dri3_open(Display *dpy, fd = xcb_dri3_open_reply_fds(c, reply)[0]; fcntl(fd, F_SETFD, FD_CLOEXEC); + free(reply); + return fd; } @@ -1388,6 +1728,7 @@ dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval) } priv->swap_interval = interval; + dri3_update_num_back(priv); return 0; } @@ -1441,7 +1782,8 @@ dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer) if (pdraw != NULL) { psc = (struct dri3_screen *) base->psc; - if (psc->texBuffer->releaseTexBuffer) + if (psc->texBuffer->base.version >= 3 && + psc->texBuffer->releaseTexBuffer != NULL) (*psc->texBuffer->releaseTexBuffer) (pcp->driContext, pdraw->base.textureTarget, pdraw->driDrawable); @@ -1449,15 +1791,15 @@ dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer) } static const struct glx_context_vtable dri3_context_vtable = { - dri3_destroy_context, - dri3_bind_context, - dri3_unbind_context, - dri3_wait_gl, - dri3_wait_x, - DRI_glXUseXFont, - dri3_bind_tex_image, - dri3_release_tex_image, - NULL, /* get_proc_address */ + .destroy = dri3_destroy_context, + .bind = dri3_bind_context, + .unbind = dri3_unbind_context, + .wait_gl = dri3_wait_gl, + .wait_x = dri3_wait_x, + .use_x_font = DRI_glXUseXFont, + .bind_tex_image = dri3_bind_tex_image, + .release_tex_image = dri3_release_tex_image, + .get_proc_address = NULL, }; /** dri3_bind_extensions @@ -1478,23 +1820,7 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv, __glXEnableDirectExtension(&psc->base, "GLX_SGI_swap_control"); __glXEnableDirectExtension(&psc->base, "GLX_MESA_swap_control"); __glXEnableDirectExtension(&psc->base, "GLX_SGI_make_current_read"); - - /* - * GLX_INTEL_swap_event is broken on the server side, where it's - * currently unconditionally enabled. This completely breaks - * systems running on drivers which don't support that extension. - * There's no way to test for its presence on this side, so instead - * of disabling it unconditionally, just disable it for drivers - * which are known to not support it, or for DDX drivers supporting - * only an older (pre-ScheduleSwap) version of DRI2. - * - * This is a hack which is required until: - * http://lists.x.org/archives/xorg-devel/2013-February/035449.html - * is merged and updated xserver makes it's way into distros: - */ -// if (pdp->swapAvailable && strcmp(driverName, "vmwgfx") != 0) { -// __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event"); -// } + __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event"); mask = psc->image_driver->getAPIMask(psc->driScreen); @@ -1506,7 +1832,12 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv, "GLX_EXT_create_context_es2_profile"); for (i = 0; extensions[i]; i++) { - if ((strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) { + /* when on a different gpu than the server, the server pixmaps + * can have a tiling mode we can't read. Thus we can't create + * a texture from them. + */ + if (!psc->is_different_gpu && + (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) { psc->texBuffer = (__DRItexBufferExtension *) extensions[i]; __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap"); } @@ -1516,18 +1847,28 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv, /* internal driver extension, no GL extension exposed */ } + if (strcmp(extensions[i]->name, __DRI_IMAGE) == 0) + psc->image = (__DRIimageExtension *) extensions[i]; + if ((strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0)) psc->config = (__DRI2configQueryExtension *) extensions[i]; if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0) __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context_robustness"); + + if (strcmp(extensions[i]->name, __DRI2_RENDERER_QUERY) == 0) { + psc->rendererQuery = (__DRI2rendererQueryExtension *) extensions[i]; + __glXEnableDirectExtension(&psc->base, "GLX_MESA_query_renderer"); + } } } static const struct glx_screen_vtable dri3_screen_vtable = { - dri3_create_context, - dri3_create_context_attribs + .create_context = dri3_create_context, + .create_context_attribs = dri3_create_context_attribs, + .query_renderer_integer = dri3_query_renderer_integer, + .query_renderer_string = dri3_query_renderer_string, }; /** dri3_create_screen @@ -1553,7 +1894,7 @@ dri3_create_screen(int screen, struct glx_display * priv) struct dri3_screen *psc; __GLXDRIscreen *psp; struct glx_config *configs = NULL, *visuals = NULL; - char *driverName, *deviceName; + char *driverName, *deviceName, *tmp; int i; psc = calloc(1, sizeof *psc); @@ -1580,9 +1921,11 @@ dri3_create_screen(int screen, struct glx_display * priv) return NULL; } + + psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu); deviceName = NULL; - driverName = dri3_get_driver_for_fd(psc->fd); + driverName = loader_get_driver_for_fd(psc->fd, 0); if (!driverName) { ErrorMessageF("No driver found\n"); goto handle_error; @@ -1618,8 +1961,7 @@ dri3_create_screen(int screen, struct glx_display * priv) psc->driScreen = psc->image_driver->createNewScreen2(screen, psc->fd, - (const __DRIextension **) - &pdp->loader_extensions[0], + pdp->loader_extensions, extensions, &driver_configs, psc); @@ -1628,28 +1970,32 @@ dri3_create_screen(int screen, struct glx_display * priv) goto handle_error; } - extensions = (*psc->core->getExtensions)(psc->driScreen); + dri3_bind_extensions(psc, priv, driverName); - for (i = 0; extensions[i]; i++) { - if (strcmp(extensions[i]->name, __DRI_IMAGE) == 0) - psc->image = (__DRIimageExtension *) extensions[i]; + if (!psc->image || psc->image->base.version < 7 || !psc->image->createImageFromFds) { + ErrorMessageF("Version 7 or imageFromFds image extension not found\n"); + goto handle_error; } - if (psc->image == NULL) { - ErrorMessageF("image extension not found\n"); + if (!psc->f || psc->f->base.version < 4) { + ErrorMessageF("Version 4 or later of flush extension not found\n"); goto handle_error; } - dri3_bind_extensions(psc, priv, driverName); + if (psc->is_different_gpu && psc->image->base.version < 9) { + ErrorMessageF("Different GPU, but image extension version 9 or later not found\n"); + goto handle_error; + } - if (!psc->f || psc->f->base.version < 4) { - ErrorMessageF("Version 4 or later of flush extension not found\n"); + if (psc->is_different_gpu && !psc->image->blitImage) { + ErrorMessageF("Different GPU, but blitImage not implemented for this driver\n"); goto handle_error; } - if (!psc->texBuffer || psc->texBuffer->base.version < 2 || - !psc->texBuffer->setTexBuffer2) - { + if (!psc->is_different_gpu && ( + !psc->texBuffer || psc->texBuffer->base.version < 2 || + !psc->texBuffer->setTexBuffer2 + )) { ErrorMessageF("Version 2 or later of texBuffer extension not found\n"); goto handle_error; } @@ -1657,8 +2003,10 @@ dri3_create_screen(int screen, struct glx_display * priv) configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs); visuals = driConvertConfigs(psc->core, psc->base.visuals, driver_configs); - if (!configs || !visuals) + if (!configs || !visuals) { + ErrorMessageF("No matching fbConfigs or visuals found\n"); goto handle_error; + } glx_config_destroy_list(psc->base.configs); psc->base.configs = configs; @@ -1684,9 +2032,19 @@ dri3_create_screen(int screen, struct glx_display * priv) psp->copySubBuffer = dri3_copy_sub_buffer; __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer"); + psp->getBufferAge = dri3_get_buffer_age; + __glXEnableDirectExtension(&psc->base, "GLX_EXT_buffer_age"); + free(driverName); free(deviceName); + tmp = getenv("LIBGL_SHOW_FPS"); + psc->show_fps_interval = tmp ? atoi(tmp) : 0; + if (psc->show_fps_interval < 0) + psc->show_fps_interval = 0; + + InfoMessageF("Using DRI3 for screen %d\n", screen); + return &psc->base; handle_error: @@ -1733,7 +2091,6 @@ _X_HIDDEN __GLXDRIdisplay * dri3_create_display(Display * dpy) { struct dri3_display *pdp; - int i; xcb_connection_t *c = XGetXCBConnection(dpy); xcb_dri3_query_version_cookie_t dri3_cookie; xcb_dri3_query_version_reply_t *dri3_reply; @@ -1783,17 +2140,14 @@ dri3_create_display(Display * dpy) } pdp->presentMajor = present_reply->major_version; pdp->presentMinor = present_reply->minor_version; + free(present_reply); pdp->base.destroyDisplay = dri3_destroy_display; pdp->base.createScreen = dri3_create_screen; - i = 0; - - pdp->loader_extensions[i++] = &imageLoaderExtension.base; - - pdp->loader_extensions[i++] = &systemTimeExtension.base; + loader_set_logger(dri_message); - pdp->loader_extensions[i++] = NULL; + pdp->loader_extensions = loader_extensions; return &pdp->base; no_extension: