X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Floader%2Floader_dri3_helper.c;h=2207b7543b96c799b13bf04fe49e34b02dcd04be;hb=16d1a0bcdbfa376232e552a3382ee2a8751743a5;hp=86ae5aecd5fe1d4de47479d3b70d222bbdd557c4;hpb=1e3218bc5ba2b739261f0c0bacf4eb662d377236;p=mesa.git diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c index 86ae5aecd5f..2207b7543b9 100644 --- a/src/loader/loader_dri3_helper.c +++ b/src/loader/loader_dri3_helper.c @@ -32,6 +32,7 @@ #include +#include #include "loader_dri3_helper.h" /* From xmlpool/options.h, user exposed so should be stable */ @@ -40,6 +41,124 @@ #define DRI_CONF_VBLANK_DEF_INTERVAL_1 2 #define DRI_CONF_VBLANK_ALWAYS_SYNC 3 +/** + * A cached blit context. + */ +struct loader_dri3_blit_context { + mtx_t mtx; + __DRIcontext *ctx; + __DRIscreen *cur_screen; + const __DRIcoreExtension *core; +}; + +/* For simplicity we maintain the cache only for a single screen at a time */ +static struct loader_dri3_blit_context blit_context = { + _MTX_INITIALIZER_NP, NULL +}; + +static void +dri3_flush_present_events(struct loader_dri3_drawable *draw); + +/** + * Do we have blit functionality in the image blit extension? + * + * \param draw[in] The drawable intended to blit from / to. + * \return true if we have blit functionality. false otherwise. + */ +static bool loader_dri3_have_image_blit(const struct loader_dri3_drawable *draw) +{ + return draw->ext->image->base.version >= 9 && + draw->ext->image->blitImage != NULL; +} + +/** + * Get and lock (for use with the current thread) a dri context associated + * with the drawable's dri screen. The context is intended to be used with + * the dri image extension's blitImage method. + * + * \param draw[in] Pointer to the drawable whose dri screen we want a + * dri context for. + * \return A dri context or NULL if context creation failed. + * + * When the caller is done with the context (even if the context returned was + * NULL), the caller must call loader_dri3_blit_context_put. + */ +static __DRIcontext * +loader_dri3_blit_context_get(struct loader_dri3_drawable *draw) +{ + mtx_lock(&blit_context.mtx); + + if (blit_context.ctx && blit_context.cur_screen != draw->dri_screen) { + blit_context.core->destroyContext(blit_context.ctx); + blit_context.ctx = NULL; + } + + if (!blit_context.ctx) { + blit_context.ctx = draw->ext->core->createNewContext(draw->dri_screen, + NULL, NULL, NULL); + blit_context.cur_screen = draw->dri_screen; + blit_context.core = draw->ext->core; + } + + return blit_context.ctx; +} + +/** + * Release (for use with other threads) a dri context previously obtained using + * loader_dri3_blit_context_get. + */ +static void +loader_dri3_blit_context_put(void) +{ + mtx_unlock(&blit_context.mtx); +} + +/** + * Blit (parts of) the contents of a DRI image to another dri image + * + * \param draw[in] The drawable which owns the images. + * \param dst[in] The destination image. + * \param src[in] The source image. + * \param dstx0[in] Start destination coordinate. + * \param dsty0[in] Start destination coordinate. + * \param width[in] Blit width. + * \param height[in] Blit height. + * \param srcx0[in] Start source coordinate. + * \param srcy0[in] Start source coordinate. + * \param flush_flag[in] Image blit flush flag. + * \return true iff successful. + */ +static bool +loader_dri3_blit_image(struct loader_dri3_drawable *draw, + __DRIimage *dst, __DRIimage *src, + int dstx0, int dsty0, int width, int height, + int srcx0, int srcy0, int flush_flag) +{ + __DRIcontext *dri_context; + bool use_blit_context = false; + + if (!loader_dri3_have_image_blit(draw)) + return false; + + dri_context = draw->vtable->get_dri_context(draw); + + if (!dri_context || !draw->vtable->in_current_context(draw)) { + dri_context = loader_dri3_blit_context_get(draw); + use_blit_context = true; + flush_flag |= __BLIT_FLAG_FLUSH; + } + + if (dri_context) + draw->ext->image->blitImage(dri_context, dst, src, dstx0, dsty0, + width, height, srcx0, srcy0, + width, height, flush_flag); + + if (use_blit_context) + loader_dri3_blit_context_put(); + + return dri_context != NULL; +} + static inline void dri3_fence_reset(xcb_connection_t *c, struct loader_dri3_buffer *buffer) { @@ -70,17 +189,14 @@ dri3_update_num_back(struct loader_dri3_drawable *draw) { if (draw->flipping) draw->num_back = 3; - else if (draw->vtable->get_swap_interval(draw) != 0) - draw->num_back = 2; else - draw->num_back = 1; + draw->num_back = 2; } void loader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval) { - interval = draw->vtable->clamp_swap_interval(draw, interval); - draw->vtable->set_swap_interval(draw, interval); + draw->swap_interval = interval; dri3_update_num_back(draw); } @@ -97,9 +213,9 @@ dri3_free_render_buffer(struct loader_dri3_drawable *draw, xcb_free_pixmap(draw->conn, buffer->pixmap); xcb_sync_destroy_fence(draw->conn, buffer->sync_fence); xshmfence_unmap_shm(buffer->shm_fence); - (draw->ext->image->destroyImage)(buffer->image); + draw->ext->image->destroyImage(buffer->image); if (buffer->linear_buffer) - (draw->ext->image->destroyImage)(buffer->linear_buffer); + draw->ext->image->destroyImage(buffer->linear_buffer); free(buffer); } @@ -108,7 +224,7 @@ loader_dri3_drawable_fini(struct loader_dri3_drawable *draw) { int i; - (draw->ext->core->destroyDrawable)(draw->dri_drawable); + draw->ext->core->destroyDrawable(draw->dri_drawable); for (i = 0; i < LOADER_DRI3_NUM_BUFFERS; i++) { if (draw->buffers[i]) @@ -132,7 +248,7 @@ loader_dri3_drawable_init(xcb_connection_t *conn, bool is_different_gpu, const __DRIconfig *dri_config, struct loader_dri3_extensions *ext, - struct loader_dri3_vtable *vtable, + const struct loader_dri3_vtable *vtable, struct loader_dri3_drawable *draw) { xcb_get_geometry_cookie_t cookie; @@ -152,6 +268,8 @@ loader_dri3_drawable_init(xcb_connection_t *conn, draw->have_fake_front = 0; draw->first_init = true; + draw->cur_blit_source = -1; + if (draw->ext->config) draw->ext->config->configQueryi(draw->dri_screen, "vblank_mode", &vblank_mode); @@ -167,15 +285,15 @@ loader_dri3_drawable_init(xcb_connection_t *conn, swap_interval = 1; break; } - draw->vtable->set_swap_interval(draw, swap_interval); + draw->swap_interval = swap_interval; dri3_update_num_back(draw); /* Create a new drawable */ draw->dri_drawable = - (draw->ext->image_driver->createNewDrawable)(dri_screen, - dri_config, - draw); + draw->ext->image_driver->createNewDrawable(dri_screen, + dri_config, + draw); if (!draw->dri_drawable) return 1; @@ -193,6 +311,13 @@ loader_dri3_drawable_init(xcb_connection_t *conn, draw->vtable->set_drawable_size(draw, draw->width, draw->height); free(reply); + draw->swap_method = __DRI_ATTRIB_SWAP_UNDEFINED; + if (draw->ext->core->base.version >= 2) { + (void )draw->ext->core->getConfigAttrib(dri_config, + __DRI_ATTRIB_SWAP_METHOD, + &draw->swap_method); + } + /* * Make sure server has the same swap interval we do for the new * drawable. @@ -368,9 +493,21 @@ dri3_find_back(struct loader_dri3_drawable *draw) int b; xcb_generic_event_t *ev; xcb_present_generic_event_t *ge; + int num_to_consider = draw->num_back; + + /* Increase the likelyhood of reusing current buffer */ + dri3_flush_present_events(draw); + + /* Check whether we need to reuse the current back buffer as new back. + * In that case, wait until it's not busy anymore. + */ + if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1) { + num_to_consider = 1; + draw->cur_blit_source = -1; + } for (;;) { - for (b = 0; b < draw->num_back; b++) { + for (b = 0; b < num_to_consider; b++) { int id = LOADER_DRI3_BACK_ID((b + draw->cur_back) % draw->num_back); struct loader_dri3_buffer *buffer = draw->buffers[id]; @@ -470,9 +607,6 @@ loader_dri3_copy_sub_buffer(struct loader_dri3_drawable *draw, { struct loader_dri3_buffer *back; unsigned flags = __DRI2_FLUSH_DRAWABLE; - __DRIcontext *dri_context; - - dri_context = draw->vtable->get_dri_context(draw); /* Check we have the right attachments */ if (!draw->have_back || draw->is_pixmap) @@ -485,27 +619,26 @@ loader_dri3_copy_sub_buffer(struct loader_dri3_drawable *draw, back = dri3_back_buffer(draw); y = draw->height - y - height; - if (draw->is_different_gpu && draw->vtable->in_current_context(draw)) { + if (draw->is_different_gpu) { /* Update the linear buffer part of the back buffer * for the dri3_copy_area operation */ - draw->ext->image->blitImage(dri_context, - 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, + (void) loader_dri3_blit_image(draw, + back->linear_buffer, + back->image, + 0, 0, back->width, back->height, + 0, 0, __BLIT_FLAG_FLUSH); + /* We use blit_image to update our fake front, */ if (draw->have_fake_front) - draw->ext->image->blitImage(dri_context, - dri3_fake_front_buffer(draw)->image, - back->image, - x, y, width, height, - x, y, width, height, __BLIT_FLAG_FLUSH); + (void) loader_dri3_blit_image(draw, + dri3_fake_front_buffer(draw)->image, + back->image, + x, y, width, height, + x, y, __BLIT_FLAG_FLUSH); } + loader_dri3_swapbuffer_barrier(draw); dri3_fence_reset(draw->conn, back); dri3_copy_area(draw->conn, dri3_back_buffer(draw)->pixmap, @@ -549,13 +682,11 @@ void loader_dri3_wait_x(struct loader_dri3_drawable *draw) { struct loader_dri3_buffer *front; - __DRIcontext *dri_context; if (draw == NULL || !draw->have_fake_front) return; front = dri3_fake_front_buffer(draw); - dri_context = draw->vtable->get_dri_context(draw); loader_dri3_copy_drawable(draw, front->pixmap, draw->drawable); @@ -564,39 +695,34 @@ loader_dri3_wait_x(struct loader_dri3_drawable *draw) * Copy back to the tiled buffer we use for rendering. * Note that we don't need flushing. */ - if (draw->is_different_gpu && draw->vtable->in_current_context(draw)) - draw->ext->image->blitImage(dri_context, - front->image, - front->linear_buffer, - 0, 0, front->width, - front->height, - 0, 0, front->width, - front->height, 0); + if (draw->is_different_gpu) + (void) loader_dri3_blit_image(draw, + front->image, + front->linear_buffer, + 0, 0, front->width, front->height, + 0, 0, 0); } void loader_dri3_wait_gl(struct loader_dri3_drawable *draw) { struct loader_dri3_buffer *front; - __DRIcontext *dri_context; if (draw == NULL || !draw->have_fake_front) return; front = dri3_fake_front_buffer(draw); - dri_context = draw->vtable->get_dri_context(draw); /* In the psc->is_different_gpu case, we update the linear_buffer * before updating the real front. */ - if (draw->is_different_gpu && draw->vtable->in_current_context(draw)) - draw->ext->image->blitImage(dri_context, - front->linear_buffer, - front->image, - 0, 0, front->width, - front->height, - 0, 0, front->width, - front->height, __BLIT_FLAG_FLUSH); + if (draw->is_different_gpu) + (void) loader_dri3_blit_image(draw, + front->linear_buffer, + front->image, + 0, 0, front->width, front->height, + 0, 0, __BLIT_FLAG_FLUSH); + loader_dri3_swapbuffer_barrier(draw); loader_dri3_copy_drawable(draw, draw->drawable, front->pixmap); } @@ -632,36 +758,35 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw, bool force_copy) { struct loader_dri3_buffer *back; - __DRIcontext *dri_context; int64_t ret = 0; uint32_t options = XCB_PRESENT_OPTION_NONE; - int swap_interval; - - dri_context = draw->vtable->get_dri_context(draw); - swap_interval = draw->vtable->get_swap_interval(draw); draw->vtable->flush_drawable(draw, flush_flags); - back = draw->buffers[LOADER_DRI3_BACK_ID(draw->cur_back)]; + back = draw->buffers[dri3_find_back(draw)]; if (draw->is_different_gpu && back) { /* Update the linear buffer before presenting the pixmap */ - draw->ext->image->blitImage(dri_context, - back->linear_buffer, - back->image, - 0, 0, back->width, - back->height, - 0, 0, back->width, - back->height, __BLIT_FLAG_FLUSH); + (void) loader_dri3_blit_image(draw, + back->linear_buffer, + back->image, + 0, 0, back->width, back->height, + 0, 0, __BLIT_FLAG_FLUSH); /* Update the fake front */ if (draw->have_fake_front) - draw->ext->image->blitImage(dri_context, - draw->buffers[LOADER_DRI3_FRONT_ID]->image, - back->image, - 0, 0, draw->width, draw->height, - 0, 0, draw->width, draw->height, - __BLIT_FLAG_FLUSH); + (void) loader_dri3_blit_image(draw, + draw->buffers[LOADER_DRI3_FRONT_ID]->image, + back->image, + 0, 0, draw->width, draw->height, + 0, 0, __BLIT_FLAG_FLUSH); } + /* If we need to preload the new back buffer, remember the source. + * The force_copy parameter is used by EGL to attempt to preserve + * the back buffer across a call to this function. + */ + if (draw->swap_method == __DRI_ATTRIB_SWAP_COPY || force_copy) + draw->cur_blit_source = LOADER_DRI3_BACK_ID(draw->cur_back); + dri3_flush_present_events(draw); if (back && !draw->is_pixmap) { @@ -674,7 +799,7 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw, */ ++draw->send_sbc; if (target_msc == 0 && divisor == 0 && remainder == 0) - target_msc = draw->msc + swap_interval * + target_msc = draw->msc + draw->swap_interval * (draw->send_sbc - draw->recv_sbc); else if (divisor == 0 && remainder > 0) { /* From the GLX_OML_sync_control spec: @@ -698,10 +823,15 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw, * behaviour by not using XCB_PRESENT_OPTION_ASYNC, but this should not be * the default. */ - if (swap_interval == 0) + if (draw->swap_interval == 0) options |= XCB_PRESENT_OPTION_ASYNC; - if (force_copy) - options |= XCB_PRESENT_OPTION_COPY; + + /* If we need to populate the new back, but need to reuse the back + * buffer slot due to lack of local blit capabilities, make sure + * the server doesn't flip and we deadlock. + */ + if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1) + options |= XCB_PRESENT_OPTION_COPY; back->busy = 1; back->last_swap = draw->send_sbc; @@ -742,7 +872,7 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw, ++(*draw->stamp); } - (draw->ext->flush->invalidate)(draw->dri_drawable); + draw->ext->flush->invalidate(draw->dri_drawable); return ret; } @@ -858,34 +988,34 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format, goto no_image; if (!draw->is_different_gpu) { - buffer->image = (draw->ext->image->createImage)(draw->dri_screen, - width, height, - format, - __DRI_IMAGE_USE_SHARE | - __DRI_IMAGE_USE_SCANOUT | - __DRI_IMAGE_USE_BACKBUFFER, - buffer); + buffer->image = draw->ext->image->createImage(draw->dri_screen, + width, height, + format, + __DRI_IMAGE_USE_SHARE | + __DRI_IMAGE_USE_SCANOUT | + __DRI_IMAGE_USE_BACKBUFFER, + buffer); pixmap_buffer = buffer->image; if (!buffer->image) goto no_image; } else { - buffer->image = (draw->ext->image->createImage)(draw->dri_screen, - width, height, - format, - 0, - buffer); + buffer->image = draw->ext->image->createImage(draw->dri_screen, + width, height, + format, + 0, + buffer); if (!buffer->image) goto no_image; buffer->linear_buffer = - (draw->ext->image->createImage)(draw->dri_screen, - width, height, format, - __DRI_IMAGE_USE_SHARE | - __DRI_IMAGE_USE_LINEAR | - __DRI_IMAGE_USE_BACKBUFFER, - buffer); + draw->ext->image->createImage(draw->dri_screen, + width, height, format, + __DRI_IMAGE_USE_SHARE | + __DRI_IMAGE_USE_LINEAR | + __DRI_IMAGE_USE_BACKBUFFER, + buffer); pixmap_buffer = buffer->linear_buffer; if (!buffer->linear_buffer) @@ -894,14 +1024,14 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format, /* X wants the stride, so ask the image for it */ - if (!(draw->ext->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, - &stride)) + if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, + &stride)) goto no_buffer_attrib; buffer->pitch = stride; - if (!(draw->ext->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, - &buffer_fd)) + if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, + &buffer_fd)) goto no_buffer_attrib; xcb_dri3_pixmap_from_buffer(draw->conn, @@ -932,10 +1062,10 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format, return buffer; no_buffer_attrib: - (draw->ext->image->destroyImage)(pixmap_buffer); + draw->ext->image->destroyImage(pixmap_buffer); no_linear_buffer: if (draw->is_different_gpu) - (draw->ext->image->destroyImage)(buffer->image); + draw->ext->image->destroyImage(buffer->image); no_image: free(buffer); no_buffer: @@ -1084,19 +1214,19 @@ loader_dri3_create_image(xcb_connection_t *c, * we've gotten the planar wrapper, pull the single plane out of it and * discard the wrapper. */ - image_planar = (image->createImageFromFds)(dri_screen, - bp_reply->width, - bp_reply->height, - image_format_to_fourcc(format), - fds, 1, - &stride, &offset, loaderPrivate); + image_planar = image->createImageFromFds(dri_screen, + bp_reply->width, + bp_reply->height, + image_format_to_fourcc(format), + fds, 1, + &stride, &offset, loaderPrivate); close(fds[0]); if (!image_planar) return NULL; - ret = (image->fromPlanar)(image_planar, 0, loaderPrivate); + ret = image->fromPlanar(image_planar, 0, loaderPrivate); - (image->destroyImage)(image_planar); + image->destroyImage(image_planar); return ret; } @@ -1191,9 +1321,6 @@ dri3_get_buffer(__DRIdrawable *driDrawable, { struct loader_dri3_buffer *buffer; int buf_id; - __DRIcontext *dri_context; - - dri_context = draw->vtable->get_dri_context(draw); if (buffer_type == loader_dri3_buffer_back) { buf_id = dri3_find_back(draw); @@ -1240,16 +1367,17 @@ dri3_get_buffer(__DRIdrawable *driDrawable, draw->width, draw->height); dri3_fence_trigger(draw->conn, new_buffer); } else if (draw->vtable->in_current_context(draw)) { - draw->ext->image->blitImage(dri_context, - new_buffer->image, - buffer->image, - 0, 0, draw->width, draw->height, - 0, 0, draw->width, draw->height, 0); + (void) loader_dri3_blit_image(draw, + new_buffer->image, + buffer->image, + 0, 0, draw->width, draw->height, + 0, 0, 0); } dri3_free_render_buffer(draw, buffer); } break; case loader_dri3_buffer_front: + loader_dri3_swapbuffer_barrier(draw); dri3_fence_reset(draw->conn, new_buffer); dri3_copy_area(draw->conn, draw->drawable, @@ -1262,11 +1390,11 @@ dri3_get_buffer(__DRIdrawable *driDrawable, if (new_buffer->linear_buffer && draw->vtable->in_current_context(draw)) { dri3_fence_await(draw->conn, new_buffer); - draw->ext->image->blitImage(dri_context, - new_buffer->image, - new_buffer->linear_buffer, - 0, 0, draw->width, draw->height, - 0, 0, draw->width, draw->height, 0); + (void) loader_dri3_blit_image(draw, + new_buffer->image, + new_buffer->linear_buffer, + 0, 0, draw->width, draw->height, + 0, 0, 0); } break; } @@ -1276,6 +1404,31 @@ dri3_get_buffer(__DRIdrawable *driDrawable, } dri3_fence_await(draw->conn, buffer); + /* + * Do we need to preserve the content of a previous buffer? + * + * Note that this blit is needed only to avoid a wait for a buffer that + * is currently in the flip chain or being scanned out from. That's really + * a tradeoff. If we're ok with the wait we can reduce the number of back + * buffers to 1 for SWAP_EXCHANGE, and 1 for SWAP_COPY, + * but in the latter case we must disallow page-flipping. + */ + if (buffer_type == loader_dri3_buffer_back && + draw->cur_blit_source != -1 && + draw->buffers[draw->cur_blit_source] && + buffer != draw->buffers[draw->cur_blit_source]) { + + struct loader_dri3_buffer *source = draw->buffers[draw->cur_blit_source]; + + /* Avoid flushing here. Will propably do good for tiling hardware. */ + (void) loader_dri3_blit_image(draw, + buffer->image, + source->image, + 0, 0, draw->width, draw->height, + 0, 0, 0); + buffer->last_swap = source->last_swap; + draw->cur_blit_source = -1; + } /* Return the requested buffer */ return buffer; } @@ -1400,3 +1553,58 @@ loader_dri3_get_buffers(__DRIdrawable *driDrawable, return true; } + +/** loader_dri3_update_drawable_geometry + * + * Get the current drawable geometry. + */ +void +loader_dri3_update_drawable_geometry(struct loader_dri3_drawable *draw) +{ + xcb_get_geometry_cookie_t geom_cookie; + xcb_get_geometry_reply_t *geom_reply; + + geom_cookie = xcb_get_geometry(draw->conn, draw->drawable); + + geom_reply = xcb_get_geometry_reply(draw->conn, geom_cookie, NULL); + + if (geom_reply) { + draw->width = geom_reply->width; + draw->height = geom_reply->height; + draw->vtable->set_drawable_size(draw, draw->width, draw->height); + + free(geom_reply); + } +} + + +/** + * Make sure the server has flushed all pending swap buffers to hardware + * for this drawable. Ideally we'd want to send an X protocol request to + * have the server block our connection until the swaps are complete. That + * would avoid the potential round-trip here. + */ +void +loader_dri3_swapbuffer_barrier(struct loader_dri3_drawable *draw) +{ + int64_t ust, msc, sbc; + + (void) loader_dri3_wait_for_sbc(draw, 0, &ust, &msc, &sbc); +} + +/** + * Perform any cleanup associated with a close screen operation. + * \param dri_screen[in,out] Pointer to __DRIscreen about to be closed. + * + * This function destroys the screen's cached swap context if any. + */ +void +loader_dri3_close_screen(__DRIscreen *dri_screen) +{ + mtx_lock(&blit_context.mtx); + if (blit_context.ctx && blit_context.cur_screen == dri_screen) { + blit_context.core->destroyContext(blit_context.ctx); + blit_context.ctx = NULL; + } + mtx_unlock(&blit_context.mtx); +}