static void vl_dri3_flush_frontbuffer(struct pipe_screen *screen, struct pipe_resource *resource, unsigned level, unsigned layer, void *context_private, struct pipe_box *sub_box) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private; uint32_t options = XCB_PRESENT_OPTION_NONE; struct vl_dri3_buffer *back; struct pipe_box src_box; xcb_xfixes_region_t region; xcb_rectangle_t rectangle; back = scrn->back_buffers[scrn->cur_back]; if (!back) return; while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc) if (!dri3_wait_present_events(scrn)) return; rectangle.x = 0; rectangle.y = 0; rectangle.width = (scrn->output_texture) ? scrn->clip_width : scrn->width; rectangle.height = (scrn->output_texture) ? scrn->clip_height : scrn->height; region = xcb_generate_id(scrn->conn); xcb_xfixes_create_region(scrn->conn, region, 2, &rectangle); if (scrn->is_different_gpu) { u_box_origin_2d(back->width, back->height, &src_box); scrn->pipe->resource_copy_region(scrn->pipe, back->linear_texture, 0, 0, 0, 0, back->texture, 0, &src_box); scrn->pipe->flush(scrn->pipe, NULL, 0); } xshmfence_reset(back->shm_fence); back->busy = true; xcb_present_pixmap(scrn->conn, scrn->drawable, back->pixmap, (uint32_t)(++scrn->send_sbc), 0, region, 0, 0, None, None, back->sync_fence, options, scrn->next_msc, 0, 0, 0, NULL); xcb_flush(scrn->conn); return; }
static VkResult x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, uint32_t target_msc) { struct x11_image *image = &chain->images[image_index]; assert(image_index < chain->image_count); uint32_t options = XCB_PRESENT_OPTION_NONE; int64_t divisor = 0; int64_t remainder = 0; if (chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) options |= XCB_PRESENT_OPTION_ASYNC; xshmfence_reset(image->shm_fence); ++chain->send_sbc; xcb_void_cookie_t cookie = xcb_present_pixmap(chain->conn, chain->window, image->pixmap, (uint32_t) chain->send_sbc, 0, /* valid */ 0, /* update */ 0, /* x_off */ 0, /* y_off */ XCB_NONE, /* target_crtc */ XCB_NONE, image->sync_fence, options, target_msc, divisor, remainder, 0, NULL); xcb_discard_reply(chain->conn, cookie.sequence); image->busy = true; xcb_flush(chain->conn); return VK_SUCCESS; }
static void vl_dri3_flush_frontbuffer(struct pipe_screen *screen, struct pipe_resource *resource, unsigned level, unsigned layer, void *context_private, struct pipe_box *sub_box) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private; uint32_t options = XCB_PRESENT_OPTION_NONE; struct vl_dri3_buffer *back; back = scrn->back_buffers[scrn->cur_back]; if (!back) return; if (scrn->flushed) { while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc) if (!dri3_wait_present_events(scrn)) return; } xshmfence_reset(back->shm_fence); back->busy = true; xcb_present_pixmap(scrn->conn, scrn->drawable, back->pixmap, (uint32_t)(++scrn->send_sbc), 0, 0, 0, 0, None, None, back->sync_fence, options, scrn->next_msc, 0, 0, 0, NULL); xcb_flush(scrn->conn); scrn->flushed = true; return; }
/** loader_dri3_swap_buffers_msc * * Make the current back buffer visible using the present extension */ int64_t loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw, int64_t target_msc, int64_t divisor, int64_t remainder, unsigned flush_flags, 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)]; 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); /* 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); } dri3_flush_present_events(draw); if (back && !draw->is_pixmap) { dri3_fence_reset(draw->conn, 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. target_msc=divisor=remainder=0 means "Use glXSwapBuffers() * semantic" */ ++draw->send_sbc; if (target_msc == 0 && divisor == 0 && remainder == 0) target_msc = draw->msc + swap_interval * (draw->send_sbc - draw->recv_sbc); else if (divisor == 0 && remainder > 0) { /* From the GLX_OML_sync_control spec: * "If <divisor> = 0, the swap will occur when MSC becomes * greater than or equal to <target_msc>." * * 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; } /* From the GLX_EXT_swap_control spec * and the EGL 1.4 spec (page 53): * * "If <interval> 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 (swap_interval == 0) options |= XCB_PRESENT_OPTION_ASYNC; if (force_copy) options |= XCB_PRESENT_OPTION_COPY; back->busy = 1; back->last_swap = draw->send_sbc; xcb_present_pixmap(draw->conn, draw->drawable, back->pixmap, (uint32_t) draw->send_sbc, 0, /* valid */ 0, /* update */ 0, /* x_off */ 0, /* y_off */ None, /* target_crtc */ None, back->sync_fence, options, target_msc, divisor, remainder, 0, NULL); ret = (int64_t) draw->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 (draw->have_fake_front && !draw->is_different_gpu) { dri3_fence_reset(draw->conn, draw->buffers[LOADER_DRI3_FRONT_ID]); dri3_copy_area(draw->conn, back->pixmap, draw->buffers[LOADER_DRI3_FRONT_ID]->pixmap, dri3_drawable_gc(draw), 0, 0, 0, 0, draw->width, draw->height); dri3_fence_trigger(draw->conn, draw->buffers[LOADER_DRI3_FRONT_ID]); } xcb_flush(draw->conn); if (draw->stamp) ++(*draw->stamp); } (draw->ext->flush->invalidate)(draw->dri_drawable); return ret; }
static void PRESENTForceReleases(PRESENTpriv *present_priv) { PRESENTPixmapPriv *current = NULL; if (!present_priv->window) return; /* There should be no other thread listening for events here. * This can happen when hDestWindowOverride changes without reset. * This case should never happen, but can happen in theory.*/ if (present_priv->xcb_wait) { xcb_present_notify_msc(present_priv->xcb_connection, present_priv->window, 0, 0, 0, 0); xcb_flush(present_priv->xcb_connection); pthread_mutex_lock(&present_priv->mutex_xcb_wait); pthread_mutex_unlock(&present_priv->mutex_xcb_wait); /* the problem here is that we don't have access to the event the other thread got. * It is either presented event, idle event or notify event. */ while (present_priv->pixmap_present_pending >= 2) PRESENTwait_events(present_priv, FALSE); PRESENTflush_events(present_priv, TRUE); /* Remaining events to come can be a pair of present/idle, * or an idle, or nothing. To be sure we are after all pixmaps * have been presented, add an event to the queue that can only * be after the present event, then if we receive an event more, * we are sure all pixmaps were presented */ present_priv->notify_with_serial_pending = TRUE; xcb_present_notify_msc(present_priv->xcb_connection, present_priv->window, 1, present_priv->last_target + 5, 0, 0); xcb_flush(present_priv->xcb_connection); while (present_priv->notify_with_serial_pending) PRESENTwait_events(present_priv, FALSE); /* Now we are sure we are not expecting any new event */ } else { while (present_priv->pixmap_present_pending) /* wait all sent pixmaps are presented */ PRESENTwait_events(present_priv, FALSE); PRESENTflush_events(present_priv, TRUE); /* may be remaining idle event */ /* Since idle events are send with the complete events when it is not flips, * we are not expecting any new event here */ } current = present_priv->first_present_priv; while (current) { if (!current->released) { if (!current->last_present_was_flip && !present_priv->xcb_wait) { ERR("ERROR: a pixmap seems not released by PRESENT for no reason. Code bug.\n"); } else { /* Present the same pixmap with a non-valid part to force the copy mode and the releases */ xcb_xfixes_region_t valid, update; xcb_rectangle_t rect_update; rect_update.x = 0; rect_update.y = 0; rect_update.width = 8; rect_update.height = 1; valid = xcb_generate_id(present_priv->xcb_connection); update = xcb_generate_id(present_priv->xcb_connection); xcb_xfixes_create_region(present_priv->xcb_connection, valid, 1, &rect_update); xcb_xfixes_create_region(present_priv->xcb_connection, update, 1, &rect_update); /* here we know the pixmap has been presented. Thus if it is on screen, * the following request can only make it released by the server if it is not */ xcb_present_pixmap(present_priv->xcb_connection, present_priv->window, current->pixmap, 0, valid, update, 0, 0, None, None, None, XCB_PRESENT_OPTION_COPY | XCB_PRESENT_OPTION_ASYNC, 0, 0, 0, 0, NULL); xcb_flush(present_priv->xcb_connection); PRESENTwait_events(present_priv, FALSE); /* by assumption this can only be idle event */ PRESENTflush_events(present_priv, TRUE); /* Shoudln't be needed */ } } current = current->next; } /* Now all pixmaps are released (possibility if xcb_wait is true that one is not aware yet), * and we don't expect any new Present event to come from Xserver */ }