| From 78d8055191eafa06dc09f1bea18272bf15dccc9f Mon Sep 17 00:00:00 2001 |
| From: Chris Wolfe <cwolfe@chromium.org> |
| Date: Fri, 22 Feb 2013 12:41:01 -0800 |
| Subject: [PATCH] xf86-video-intel: Split framebuffer and flip crtcs. |
| |
| Try to allocate a separate framebuffer for each CRTC and flip those |
| directly with the back-buffer of updated drawables. This avoids |
| blitting content when the drawable size matches the crtc size. |
| |
| When CopyRegion is used top copy content into or out of the screen |
| buffer, it may be redirected to exactly one scanout buffer. This is |
| enough to support our one-drawable-per-crtc case, but not sufficient |
| in general. |
| |
| This change also moves page flip state that was previously centralized |
| in the intel_mode structure into the intel_crtc structure associated |
| with the reference crtc for a flip. This allows flips to pending on |
| multiple pipes simultaneously. |
| |
| Tested with the WebGL aquarium and space rocks demos, a Youtube |
| video and WebKit poster circle demo. Added and removed displays while |
| active and while suspended. |
| |
| Signed-off-by: Dominik Behr <dbehr@chromium.org> |
| Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> |
| |
| Conflicts: |
| src/intel_options.c |
| src/intel_options.h |
| src/uxa/intel.h |
| --- |
| src/intel_options.c | 1 + |
| src/intel_options.h | 1 + |
| src/uxa/intel.h | 55 +++- |
| src/uxa/intel_display.c | 706 +++++++++++++++++++++++++++++++++++++++--------- |
| src/uxa/intel_dri.c | 191 +++++++++++-- |
| src/uxa/intel_driver.c | 10 + |
| src/uxa/intel_uxa.c | 50 ++++ |
| src/uxa/intel_video.c | 11 +- |
| 8 files changed, 864 insertions(+), 161 deletions(-) |
| |
| diff --git a/src/intel_options.c b/src/intel_options.c |
| index 02a4ae1..92a3bac 100644 |
| --- a/src/intel_options.c |
| +++ b/src/intel_options.c |
| @@ -20,6 +20,7 @@ const OptionInfoRec intel_options[] = { |
| {OPTION_PREFER_OVERLAY, "XvPreferOverlay", OPTV_BOOLEAN, {0}, 0}, |
| {OPTION_HOTPLUG, "HotPlug", OPTV_BOOLEAN, {0}, 1}, |
| {OPTION_REPROBE, "ReprobeOutputs", OPTV_BOOLEAN, {0}, 0}, |
| + {OPTION_SPLIT_FRAMEBUFFER,"SplitFramebuffer", OPTV_BOOLEAN, {0}, TRUE}, |
| #ifdef INTEL_XVMC |
| {OPTION_XVMC, "XvMC", OPTV_BOOLEAN, {0}, 1}, |
| #endif |
| diff --git a/src/intel_options.h b/src/intel_options.h |
| index 77f0c45..d8e6fc0 100644 |
| --- a/src/intel_options.h |
| +++ b/src/intel_options.h |
| @@ -27,6 +27,7 @@ enum intel_options { |
| OPTION_PREFER_OVERLAY, |
| OPTION_HOTPLUG, |
| OPTION_REPROBE, |
| + OPTION_SPLIT_FRAMEBUFFER, |
| #if defined(XvMCExtension) && defined(ENABLE_XVMC) |
| OPTION_XVMC, |
| #define INTEL_XVMC 1 |
| diff --git a/src/uxa/intel.h b/src/uxa/intel.h |
| index 0cbe68a..f5594a7 100644 |
| --- a/src/uxa/intel.h |
| +++ b/src/uxa/intel.h |
| @@ -103,6 +103,7 @@ struct intel_pixmap { |
| #define PIN_DRI3 0x4 |
| #define PIN_PRIME 0x8 |
| #define PIN_GLAMOR 0x10 |
| + uint32_t fb; |
| }; |
| |
| #if HAS_DEVPRIVATEKEYREC |
| @@ -334,6 +335,7 @@ typedef struct intel_screen_private { |
| Bool force_fallback; |
| Bool has_kernel_flush; |
| Bool needs_flush; |
| + Bool use_split_framebuffer; |
| |
| struct _DRI2FrameEvent *pending_flip[2]; |
| |
| @@ -399,15 +401,44 @@ extern void intel_mode_remove_fb(intel_screen_private *intel); |
| extern void intel_mode_close(intel_screen_private *intel); |
| extern void intel_mode_fini(intel_screen_private *intel); |
| |
| +extern void intel_pixmap_remove_fb(intel_screen_private *intel, PixmapPtr pixmap); |
| +extern void intel_set_front_fb_id(intel_screen_private *intel, PixmapPtr new_front_pixmap); |
| + |
| extern int intel_get_pipe_from_crtc_id(drm_intel_bufmgr *bufmgr, xf86CrtcPtr crtc); |
| extern int intel_crtc_id(xf86CrtcPtr crtc); |
| extern int intel_output_dpms_status(xf86OutputPtr output); |
| extern void intel_copy_fb(ScrnInfoPtr scrn); |
| |
| +struct intel_scanout { |
| + PixmapPtr pixmap; |
| + BoxRec area; /* area of the virtual screen provided by this scanout */ |
| +}; |
| + |
| +/* Splits the screen buffer into one scanout per distinct crtc region. */ |
| +extern Bool intel_split_fb(intel_screen_private *intel); |
| + |
| +/* Merges any scanouts into the screen buffer. */ |
| +extern void intel_merge_fb(intel_screen_private *intel); |
| + |
| +/* Finds a scanout that exactly matches the area, and stores it in |out_scanout|. |
| + * Will return FALSE if any scanout partially intersects the area. If no scanout |
| + * includes the area, will return TRUE with |out_scanout| set to NULL. |
| + */ |
| +extern Bool intel_find_scanout(intel_screen_private *intel, BoxPtr area, |
| + struct intel_scanout **out_scanout); |
| + |
| +/* Finds a scanout that contains the area, and stores it in |out_scanout|. |
| + * Will return FALSE if any scanout partially intersects the area. If no scanout |
| + * includes the area, will return TRUE with |out_scanout| set to NULL. |
| + */ |
| +extern Bool intel_covering_scanout(intel_screen_private *intel, BoxPtr area, |
| + struct intel_scanout **out_scanout); |
| + |
| enum DRI2FrameEventType { |
| DRI2_SWAP, |
| DRI2_SWAP_CHAIN, |
| - DRI2_FLIP, |
| + DRI2_FLIP_FRONT, |
| + DRI2_FLIP_SPLIT, |
| DRI2_WAITMSC, |
| }; |
| |
| @@ -432,13 +463,14 @@ typedef struct _DRI2FrameEvent { |
| void *event_data; |
| DRI2BufferPtr front; |
| DRI2BufferPtr back; |
| + BoxRec area; /* screen rectangle being flipped */ |
| |
| struct _DRI2FrameEvent *chain; |
| } DRI2FrameEventRec, *DRI2FrameEventPtr; |
| |
| extern Bool intel_do_pageflip(intel_screen_private *intel, |
| - dri_bo *new_front, |
| - DRI2FrameEventPtr flip_info, int ref_crtc_hw_id); |
| + PixmapPtr new_front, |
| + DRI2FrameEventPtr flip_info); |
| |
| static inline intel_screen_private * |
| intel_get_screen_private(ScrnInfoPtr scrn) |
| @@ -474,6 +506,11 @@ extern void I915EmitInvarientState(ScrnInfoPtr scrn); |
| extern void I830EmitFlush(ScrnInfoPtr scrn); |
| |
| extern void I830InitVideo(ScreenPtr pScreen); |
| + |
| +extern void intel_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b); |
| +extern void intel_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box); |
| +extern void intel_drawable_box(DrawablePtr draw, BoxPtr draw_box); |
| + |
| extern xf86CrtcPtr intel_covering_crtc(ScrnInfoPtr scrn, BoxPtr box, |
| xf86CrtcPtr desired, BoxPtr crtc_box_ret); |
| |
| @@ -684,6 +721,18 @@ void intel_uxa_block_handler(intel_screen_private *intel); |
| Bool intel_get_aperture_space(ScrnInfoPtr scrn, drm_intel_bo ** bo_table, |
| int num_bos); |
| |
| +/* Copies a box between pixmaps using intel->uxa_driver. */ |
| +extern Bool intel_uxa_driver_copy_pixmap(intel_screen_private *intel, |
| + PixmapPtr src, PixmapPtr dst, |
| + int src_x, int src_y, |
| + int dst_x, int dst_y, |
| + int w, int h); |
| + |
| +/* Fills a box of a pixmap using intel->uxa_driver. */ |
| +extern Bool intel_uxa_driver_fill_pixmap(intel_screen_private *intel, |
| + uint32_t src, PixmapPtr dst, |
| + int x, int y, int w, int h); |
| + |
| static inline Bool intel_pixmap_is_offscreen(PixmapPtr pixmap) |
| { |
| struct intel_pixmap *priv = intel_get_pixmap_private(pixmap); |
| diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c |
| index d7fb9a2..5767d25 100644 |
| --- a/src/uxa/intel_display.c |
| +++ b/src/uxa/intel_display.c |
| @@ -60,27 +60,33 @@ |
| |
| #define KNOWN_MODE_FLAGS ((1<<14)-1) |
| |
| +#define MAX_SCANOUTS (4) |
| + |
| +enum intel_scanout_state { |
| + INTEL_SCANOUT_INVALID = 0, |
| + INTEL_SCANOUT_FRONT, |
| + INTEL_SCANOUT_SPLIT, |
| +}; |
| + |
| struct intel_mode { |
| int fd; |
| - uint32_t fb_id; |
| drmModeResPtr mode_res; |
| int cpp; |
| |
| drmEventContext event_context; |
| - DRI2FrameEventPtr flip_info; |
| - int old_fb_id; |
| - int flip_count; |
| - unsigned int fe_frame; |
| - unsigned int fe_tv_sec; |
| - unsigned int fe_tv_usec; |
| + |
| + uint32_t front_fb_id; |
| + |
| + int scanout_state; |
| + struct intel_scanout scanouts[MAX_SCANOUTS]; |
| |
| struct list outputs; |
| struct list crtcs; |
| }; |
| |
| struct intel_pageflip { |
| - struct intel_mode *mode; |
| - Bool dispatch_me; |
| + struct intel_crtc *reference_crtc; |
| + struct intel_crtc *flipped_crtc; |
| }; |
| |
| struct intel_crtc { |
| @@ -88,10 +94,20 @@ struct intel_crtc { |
| drmModeModeInfo kmode; |
| drmModeCrtcPtr mode_crtc; |
| int pipe; |
| + |
| + struct { |
| + int pending; /* number of outstanding flip requests */ |
| + unsigned int frame; |
| + unsigned int tv_sec; |
| + unsigned int tv_usec; |
| + DRI2FrameEventPtr info; |
| + } flip; |
| + |
| dri_bo *cursor; |
| dri_bo *rotate_bo; |
| uint32_t rotate_pitch; |
| uint32_t rotate_fb_id; |
| + |
| xf86CrtcPtr crtc; |
| struct list link; |
| PixmapPtr scanout_pixmap; |
| @@ -128,6 +144,9 @@ struct intel_output { |
| }; |
| |
| static void |
| +intel_crtc_merge_scanouts(intel_screen_private *intel); |
| + |
| +static void |
| intel_output_dpms(xf86OutputPtr output, int mode); |
| |
| static void |
| @@ -449,16 +468,97 @@ intel_mode_disable_unused_functions(ScrnInfoPtr scrn) |
| } |
| } |
| |
| +/* Get a scratch pixmap attached to the current front buffer. This needs to be |
| + * used rather than GetScreenPixmap before the screen is fully initialized, |
| + * and during intel_xf86crtc_resize when the state is inconsistent. |
| + */ |
| +static PixmapPtr intel_get_scratch_front_pixmap(intel_screen_private *intel) |
| +{ |
| + ScrnInfoPtr scrn = intel->scrn; |
| + PixmapPtr pixmap; |
| + |
| + pixmap = GetScratchPixmapHeader( |
| + scrn->pScreen, |
| + scrn->virtualX, |
| + scrn->virtualY, |
| + scrn->depth, |
| + scrn->bitsPerPixel, |
| + intel->front_pitch, |
| + NULL); |
| + |
| + intel_set_pixmap_bo(pixmap, intel->front_buffer); |
| + return pixmap; |
| +} |
| + |
| +static void intel_free_scratch_front_pixmap(PixmapPtr pixmap) |
| +{ |
| + if (pixmap == NULL) |
| + return; |
| + intel_set_pixmap_bo(pixmap, NULL); |
| + FreeScratchPixmapHeader(pixmap); |
| +} |
| + |
| +static uint32_t |
| +intel_pixmap_ensure_fb(intel_screen_private *intel, PixmapPtr pixmap) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + struct intel_pixmap *intel_pixmap = intel_get_pixmap_private(pixmap); |
| + int ret; |
| + |
| + if (intel_pixmap->fb != 0) |
| + return intel_pixmap->fb; |
| + |
| + ret = drmModeAddFB(mode->fd, |
| + pixmap->drawable.width, |
| + pixmap->drawable.height, |
| + intel->scrn->depth, |
| + intel->scrn->bitsPerPixel, |
| + intel_pixmap_pitch(pixmap), |
| + intel_get_pixmap_bo(pixmap)->handle, |
| + &intel_pixmap->fb); |
| + if (ret < 0) { |
| + xf86DrvMsg(intel->scrn->scrnIndex, X_ERROR, |
| + "failed to add fb for pixmap: %s\n", |
| + strerror(-ret)); |
| + return FALSE; |
| + } |
| + |
| + return intel_pixmap->fb; |
| +} |
| + |
| +void intel_pixmap_remove_fb(intel_screen_private *intel, PixmapPtr pixmap) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + struct intel_pixmap *intel_pixmap = intel_get_pixmap_private(pixmap); |
| + int ret; |
| + |
| + if (intel_pixmap->fb == 0) |
| + return; |
| + |
| + ret = drmModeRmFB(mode->fd, intel_pixmap->fb); |
| + if (ret < 0) { |
| + xf86DrvMsg(intel->scrn->scrnIndex, X_ERROR, |
| + "failed to remove fb for pixmap: %s\n", |
| + strerror(-ret)); |
| + } |
| + |
| + intel_pixmap->fb = 0; |
| +} |
| + |
| static Bool |
| intel_crtc_apply(xf86CrtcPtr crtc) |
| { |
| ScrnInfoPtr scrn = crtc->scrn; |
| + intel_screen_private *intel = intel_get_screen_private(scrn); |
| + struct intel_mode *mode = intel->modes; |
| struct intel_crtc *intel_crtc = crtc->driver_private; |
| - struct intel_mode *mode = intel_crtc->mode; |
| + BoxRec crtc_box; |
| + struct intel_scanout *scanout; |
| xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); |
| uint32_t *output_ids; |
| int output_count = 0; |
| - int fb_id, x, y; |
| + uint32_t fb_id; |
| + int x, y; |
| int i, ret = FALSE; |
| |
| output_ids = calloc(sizeof(uint32_t), xf86_config->num_output); |
| @@ -493,9 +593,21 @@ intel_crtc_apply(xf86CrtcPtr crtc) |
| crtc->gamma_blue, crtc->gamma_size); |
| #endif |
| |
| + fb_id = mode->front_fb_id; |
| x = crtc->x; |
| y = crtc->y; |
| - fb_id = mode->fb_id; |
| + |
| + intel_crtc_box(crtc, &crtc_box); |
| + if (!intel_find_scanout(intel, &crtc_box, &scanout)) { |
| + /* partially intersects a scanout; merge everything */ |
| + intel_crtc_merge_scanouts(intel); |
| + intel_batch_submit(scrn); |
| + } |
| + if (scanout != NULL) { |
| + fb_id = intel_pixmap_ensure_fb(intel, scanout->pixmap); |
| + x -= scanout->area.x1; |
| + y -= scanout->area.y1; |
| + } |
| if (intel_crtc->rotate_fb_id) { |
| fb_id = intel_crtc->rotate_fb_id; |
| x = 0; |
| @@ -521,7 +633,6 @@ intel_crtc_apply(xf86CrtcPtr crtc) |
| for (i = 0; i < xf86_config->num_output; i++) { |
| xf86OutputPtr output = xf86_config->output[i]; |
| struct intel_output *intel_output; |
| - |
| if (output->crtc != crtc) |
| continue; |
| |
| @@ -544,20 +655,20 @@ intel_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, |
| { |
| ScrnInfoPtr scrn = crtc->scrn; |
| intel_screen_private *intel = intel_get_screen_private(scrn); |
| + struct intel_mode *intel_mode = intel->modes; |
| struct intel_crtc *intel_crtc = crtc->driver_private; |
| - struct intel_mode *intel_mode = intel_crtc->mode; |
| int saved_x, saved_y; |
| Rotation saved_rotation; |
| DisplayModeRec saved_mode; |
| - int ret = TRUE; |
| - unsigned int pitch = scrn->displayWidth * intel->cpp; |
| + int ret; |
| |
| - if (intel_mode->fb_id == 0) { |
| + if (intel_mode->front_fb_id == 0) { |
| ret = drmModeAddFB(intel_mode->fd, |
| scrn->virtualX, scrn->virtualY, |
| scrn->depth, scrn->bitsPerPixel, |
| - pitch, intel->front_buffer->handle, |
| - &intel_mode->fb_id); |
| + scrn->displayWidth * intel->cpp, |
| + intel->front_buffer->handle, |
| + &intel_mode->front_fb_id); |
| if (ret < 0) { |
| ErrorF("failed to add fb\n"); |
| return FALSE; |
| @@ -580,14 +691,17 @@ intel_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, |
| intel_batch_submit(crtc->scrn); |
| |
| mode_to_kmode(crtc->scrn, &intel_crtc->kmode, mode); |
| - ret = intel_crtc_apply(crtc); |
| - if (!ret) { |
| - crtc->x = saved_x; |
| - crtc->y = saved_y; |
| - crtc->rotation = saved_rotation; |
| - crtc->mode = saved_mode; |
| - } |
| - return ret; |
| + if (!intel_crtc_apply(crtc)) |
| + goto error_undo; |
| + |
| + return TRUE; |
| + |
| +error_undo: |
| + crtc->x = saved_x; |
| + crtc->y = saved_y; |
| + crtc->rotation = saved_rotation; |
| + crtc->mode = saved_mode; |
| + return FALSE; |
| } |
| |
| static void |
| @@ -1507,33 +1621,53 @@ intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num) |
| list_add(&intel_output->link, &mode->outputs); |
| } |
| |
| +static void |
| +intel_destroy_scanout(struct intel_scanout *scanout) |
| +{ |
| + if (!scanout) |
| + return; |
| + |
| + if (scanout->pixmap) { |
| + intel_set_pixmap_bo(scanout->pixmap, NULL); |
| + FreeScratchPixmapHeader(scanout->pixmap); |
| + } |
| + memset(scanout, 0, sizeof(*scanout)); |
| +} |
| + |
| static Bool |
| intel_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height) |
| { |
| xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); |
| - struct intel_crtc *intel_crtc = xf86_config->crtc[0]->driver_private; |
| - struct intel_mode *mode = intel_crtc->mode; |
| intel_screen_private *intel = intel_get_screen_private(scrn); |
| + struct intel_mode *mode = intel->modes; |
| drm_intel_bo *old_front = NULL; |
| - Bool ret; |
| + int ret; |
| uint32_t old_fb_id; |
| int i, old_width, old_height, old_pitch; |
| unsigned long pitch; |
| uint32_t tiling; |
| ScreenPtr screen; |
| |
| + intel_batch_submit(scrn); |
| + |
| if (scrn->virtualX == width && scrn->virtualY == height) |
| return TRUE; |
| |
| intel_glamor_flush(intel); |
| - intel_batch_submit(scrn); |
| + for (i = 0; i < MAX_SCANOUTS; i++) { |
| + intel_destroy_scanout(&mode->scanouts[i]); |
| + } |
| + mode->scanout_state = INTEL_SCANOUT_INVALID; |
| |
| old_width = scrn->virtualX; |
| old_height = scrn->virtualY; |
| old_pitch = scrn->displayWidth; |
| - old_fb_id = mode->fb_id; |
| + old_fb_id = mode->front_fb_id; |
| old_front = intel->front_buffer; |
| |
| + intel->front_buffer = NULL; |
| + mode->front_fb_id = 0; |
| + |
| if (intel->back_pixmap) { |
| screen = intel->back_pixmap->drawable.pScreen; |
| screen->DestroyPixmap(intel->back_pixmap); |
| @@ -1556,7 +1690,7 @@ intel_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height) |
| ret = drmModeAddFB(mode->fd, width, height, scrn->depth, |
| scrn->bitsPerPixel, pitch, |
| intel->front_buffer->handle, |
| - &mode->fb_id); |
| + &mode->front_fb_id); |
| if (ret) |
| goto fail; |
| |
| @@ -1593,102 +1727,415 @@ fail: |
| scrn->virtualX = old_width; |
| scrn->virtualY = old_height; |
| scrn->displayWidth = old_pitch; |
| - if (old_fb_id != mode->fb_id) |
| - drmModeRmFB(mode->fd, mode->fb_id); |
| - mode->fb_id = old_fb_id; |
| + if (old_fb_id != mode->front_fb_id) |
| + drmModeRmFB(mode->fd, mode->front_fb_id); |
| + mode->front_fb_id = old_fb_id; |
| |
| return FALSE; |
| } |
| |
| +static PixmapPtr intel_create_split_fb(intel_screen_private *intel, int width, int height) |
| +{ |
| + ScrnInfoPtr scrn = intel->scrn; |
| + drm_intel_bo *pixmap_bo = NULL; |
| + PixmapPtr pixmap = NULL; |
| + unsigned long pitch; |
| + uint32_t tiling; |
| + |
| + pixmap_bo = intel_allocate_framebuffer(scrn, width, height, intel->cpp, |
| + &pitch, &tiling); |
| + if (!pixmap_bo) |
| + goto fail; |
| + |
| + pixmap = GetScratchPixmapHeader(scrn->pScreen, |
| + width, height, |
| + scrn->depth, |
| + scrn->bitsPerPixel, |
| + pitch, |
| + NULL); |
| + if (!pixmap) |
| + goto fail; |
| + |
| + intel_set_pixmap_bo(pixmap, pixmap_bo); |
| + drm_intel_bo_unreference(pixmap_bo); |
| + pixmap_bo = NULL; |
| + |
| + return pixmap; |
| +fail: |
| + drm_intel_bo_unreference(pixmap_bo); |
| + return NULL; |
| +} |
| + |
| Bool |
| -intel_do_pageflip(intel_screen_private *intel, |
| - dri_bo *new_front, |
| - DRI2FrameEventPtr flip_info, int ref_crtc_hw_id) |
| +intel_split_fb(intel_screen_private *intel) |
| +{ |
| + ScrnInfoPtr scrn = intel->scrn; |
| + struct intel_mode *mode = intel->modes; |
| + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); |
| + struct intel_scanout *scanout; |
| + PixmapPtr screen_pixmap; |
| + BoxRec crtc_box; |
| + int i, j, old_state; |
| + |
| + if (mode->scanout_state == INTEL_SCANOUT_SPLIT) |
| + return TRUE; /* nothing to do */ |
| + |
| + old_state = mode->scanout_state; |
| + mode->scanout_state = INTEL_SCANOUT_SPLIT; |
| + |
| + screen_pixmap = intel_get_scratch_front_pixmap(intel); |
| + |
| + for (i = 0; i < config->num_crtc; i++) { |
| + xf86CrtcPtr crtc = config->crtc[i]; |
| + if (!crtc->enabled) |
| + continue; |
| + |
| + intel_crtc_box(crtc, &crtc_box); |
| + |
| + for (j = 0; j < MAX_SCANOUTS; j++) { |
| + scanout = &mode->scanouts[j]; |
| + if (scanout->pixmap == NULL) |
| + break; /* beyond the last valid entry */ |
| + |
| + if (crtc_box.x1 == scanout->area.x1 && |
| + crtc_box.y1 == scanout->area.y1 && |
| + crtc_box.x2 == scanout->area.x2 && |
| + crtc_box.y2 == scanout->area.y2) |
| + break; /* already have a scanout for this crtc */ |
| + } |
| + if (j == MAX_SCANOUTS) { |
| + xf86DrvMsg(intel->scrn->scrnIndex, X_WARNING, |
| + "failed to split framebuffer: all scanouts in use\n"); |
| + goto fail; |
| + } |
| + |
| + if (scanout->pixmap) |
| + continue; /* already have a complete scanout for this crtc */ |
| + |
| + /* need to allocate a new scanout bo for this crtc */ |
| + scanout->pixmap = intel_create_split_fb(intel, |
| + crtc->mode.HDisplay, crtc->mode.VDisplay); |
| + if (!scanout->pixmap) { |
| + xf86DrvMsg(intel->scrn->scrnIndex, X_WARNING, |
| + "failed to split framebuffer: allocation failure\n"); |
| + goto fail; |
| + } |
| + |
| + scanout->area.x1 = crtc_box.x1; |
| + scanout->area.y1 = crtc_box.y1; |
| + scanout->area.x2 = crtc_box.x2; |
| + scanout->area.y2 = crtc_box.y2; |
| + |
| + if (old_state == INTEL_SCANOUT_FRONT) { |
| + /* copy current content from the front buffer */ |
| + intel_uxa_driver_copy_pixmap(intel, |
| + screen_pixmap, scanout->pixmap, |
| + crtc_box.x1, crtc_box.y1, 0, 0, |
| + crtc_box.x2 - crtc_box.x1, |
| + crtc_box.y2 - crtc_box.y1); |
| + } |
| + } |
| + |
| + intel_batch_submit(scrn); |
| + for (i = 0; i < config->num_crtc; i++) { |
| + xf86CrtcPtr crtc = config->crtc[i]; |
| + if (!crtc->enabled) |
| + continue; |
| + |
| + if (!intel_crtc_apply(crtc)) |
| + goto fail; |
| + } |
| + |
| + intel_free_scratch_front_pixmap(screen_pixmap); |
| + |
| + return TRUE; |
| + |
| +fail: |
| + mode->scanout_state = old_state; |
| + intel_free_scratch_front_pixmap(screen_pixmap); |
| + return FALSE; |
| +} |
| + |
| +static void |
| +intel_crtc_merge_scanouts(intel_screen_private *intel) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + PixmapPtr screen_pixmap; |
| + int i, old_state; |
| + |
| + old_state = mode->scanout_state; |
| + mode->scanout_state = INTEL_SCANOUT_FRONT; |
| + |
| + screen_pixmap = intel_get_scratch_front_pixmap(intel); |
| + |
| + for (i = 0; i < MAX_SCANOUTS; i++) { |
| + struct intel_scanout *scanout = &mode->scanouts[i]; |
| + if (scanout->pixmap == NULL) |
| + continue; |
| + |
| + if (old_state == INTEL_SCANOUT_SPLIT) { |
| + /* copy current content from the split buffer */ |
| + intel_uxa_driver_copy_pixmap(intel, |
| + scanout->pixmap, screen_pixmap, |
| + 0, 0, |
| + scanout->area.x1, scanout->area.y1, |
| + scanout->area.x2 - scanout->area.x1, |
| + scanout->area.y2 - scanout->area.y1); |
| + } |
| + |
| + intel_destroy_scanout(scanout); |
| + } |
| + |
| + intel_free_scratch_front_pixmap(screen_pixmap); |
| +} |
| + |
| +void |
| +intel_merge_fb(intel_screen_private *intel) |
| { |
| ScrnInfoPtr scrn = intel->scrn; |
| + struct intel_mode *mode = intel->modes; |
| xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); |
| - struct intel_crtc *crtc = config->crtc[0]->driver_private; |
| - struct intel_mode *mode = crtc->mode; |
| - unsigned int pitch = scrn->displayWidth * intel->cpp; |
| - struct intel_pageflip *flip; |
| - uint32_t new_fb_id; |
| int i; |
| |
| - /* |
| - * Create a new handle for the back buffer |
| - */ |
| - if (drmModeAddFB(mode->fd, scrn->virtualX, scrn->virtualY, |
| - scrn->depth, scrn->bitsPerPixel, pitch, |
| - new_front->handle, &new_fb_id)) |
| - goto error_out; |
| + if (mode->scanout_state == INTEL_SCANOUT_FRONT) |
| + return; /* nothing to do */ |
| |
| - drm_intel_bo_disable_reuse(new_front); |
| - intel_glamor_flush(intel); |
| + intel_crtc_merge_scanouts(intel); |
| intel_batch_submit(scrn); |
| |
| - /* |
| - * Queue flips on all enabled CRTCs |
| - * Note that if/when we get per-CRTC buffers, we'll have to update this. |
| - * Right now it assumes a single shared fb across all CRTCs, with the |
| - * kernel fixing up the offset of each CRTC as necessary. |
| - * |
| - * Also, flips queued on disabled or incorrectly configured displays |
| - * may never complete; this is a configuration error. |
| - */ |
| - mode->fe_frame = 0; |
| - mode->fe_tv_sec = 0; |
| - mode->fe_tv_usec = 0; |
| + for (i = 0; i < config->num_crtc; i++) { |
| + xf86CrtcPtr crtc = config->crtc[i]; |
| + if (!crtc->enabled) |
| + continue; |
| + if (!intel_crtc_apply(crtc)) |
| + continue; /* update as many crtcs as possible */ |
| + } |
| +} |
| + |
| +Bool |
| +intel_find_scanout(intel_screen_private *intel, BoxPtr area, |
| + struct intel_scanout **out_scanout) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + BoxRec intersect_box; |
| + int i; |
| + |
| + *out_scanout = NULL; |
| + |
| + if (mode->scanout_state != INTEL_SCANOUT_SPLIT) |
| + return TRUE; /* always use screen buffer */ |
| + |
| + for (i = 0; i < MAX_SCANOUTS; i++) { |
| + struct intel_scanout *scanout = &mode->scanouts[i]; |
| + if (scanout->pixmap == NULL) |
| + continue; |
| + |
| + if (scanout->area.x1 == area->x1 && |
| + scanout->area.y1 == area->y1 && |
| + scanout->area.x2 == area->x2 && |
| + scanout->area.y2 == area->y2) { |
| + *out_scanout = scanout; |
| + return TRUE; |
| + } |
| + |
| + intel_box_intersect(&intersect_box, &scanout->area, area); |
| + if (intersect_box.x1 != intersect_box.x2 || |
| + intersect_box.y1 != intersect_box.y2) { |
| + /* partial intersection; must merge to use area. */ |
| + return FALSE; |
| + } |
| + } |
| + |
| + /* did not intersect any scanouts; use the screen. */ |
| + return TRUE; |
| +} |
| + |
| +Bool |
| +intel_covering_scanout(intel_screen_private *intel, BoxPtr area, |
| + struct intel_scanout **out_scanout) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + BoxRec intersect_box; |
| + int i; |
| + |
| + *out_scanout = NULL; |
| + |
| + if (mode->scanout_state != INTEL_SCANOUT_SPLIT) |
| + return TRUE; /* always use screen buffer */ |
| + |
| + for (i = 0; i < MAX_SCANOUTS; i++) { |
| + struct intel_scanout *scanout = &mode->scanouts[i]; |
| + if (scanout->pixmap == NULL) |
| + continue; |
| + |
| + if (scanout->area.x1 <= area->x1 && |
| + scanout->area.y1 <= area->y1 && |
| + scanout->area.x2 >= area->x2 && |
| + scanout->area.y2 >= area->y2) { |
| + *out_scanout = scanout; |
| + return TRUE; |
| + } |
| |
| + intel_box_intersect(&intersect_box, &scanout->area, area); |
| + if (intersect_box.x1 != intersect_box.x2 || |
| + intersect_box.y1 != intersect_box.y2) { |
| + /* partial intersection; must merge to use area. */ |
| + return FALSE; |
| + } |
| + } |
| + |
| + /* did not intersect any scanouts; use the screen. */ |
| + return TRUE; |
| +} |
| + |
| +Bool |
| +intel_do_pageflip(intel_screen_private *intel, |
| + PixmapPtr new_front, |
| + DRI2FrameEventPtr flip_info) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + ScrnInfoPtr scrn = intel->scrn; |
| + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); |
| + struct intel_pageflip **flip_data = NULL; |
| + uint32_t new_front_fb_id; |
| + struct intel_crtc *reference_crtc; |
| + BoxRec crtc_box, intersect_box; |
| + int i, ret; |
| + |
| + if (mode->scanout_state == INTEL_SCANOUT_INVALID) { |
| + xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| + "flip queue failed: scanout state not configured.\n"); |
| + return FALSE; |
| + } |
| + |
| + new_front_fb_id = intel_pixmap_ensure_fb(intel, new_front); |
| + if (new_front_fb_id == 0) |
| + goto error_undo; |
| + |
| + flip_data = calloc(config->num_crtc, sizeof(*flip_data)); |
| + if (!flip_data) { |
| + xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| + "flip queue failed: memory allocation error.\n"); |
| + goto error_undo; |
| + } |
| + |
| + /* Find the reference CRTC, check safety and allocate flip data. */ |
| + reference_crtc = NULL; |
| for (i = 0; i < config->num_crtc; i++) { |
| - if (!intel_crtc_on(config->crtc[i])) |
| + xf86CrtcPtr crtc = config->crtc[i]; |
| + struct intel_crtc *intel_crtc = crtc->driver_private; |
| + if (!crtc->enabled) |
| continue; |
| |
| - mode->flip_info = flip_info; |
| - mode->flip_count++; |
| + if (flip_info->pipe == intel_crtc->pipe) |
| + reference_crtc = intel_crtc; |
| |
| - crtc = config->crtc[i]->driver_private; |
| + intel_crtc_box(crtc, &crtc_box); |
| + intel_box_intersect(&intersect_box, &crtc_box, &flip_info->area); |
| + |
| + if (intersect_box.x1 == intersect_box.x2 || |
| + intersect_box.y1 == intersect_box.y2) { |
| + /* Skip crtcs unaffected by this update. */ |
| + if (reference_crtc == intel_crtc) { |
| + xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| + "flip queue failed: updated area does " |
| + "not include the reference crtc.\n"); |
| + goto error_undo; |
| + } |
| + continue; |
| + } |
| |
| - flip = calloc(1, sizeof(struct intel_pageflip)); |
| - if (flip == NULL) { |
| + if (intersect_box.x1 != crtc_box.x1 || |
| + intersect_box.y1 != crtc_box.y1 || |
| + intersect_box.x2 != crtc_box.x2 || |
| + intersect_box.y2 != crtc_box.y2) { |
| xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| - "flip queue: carrier alloc failed.\n"); |
| + "flip queue failed: updated area partially " |
| + "intersects crtc %d.\n", |
| + crtc_id(intel_crtc)); |
| goto error_undo; |
| } |
| + flip_data[i] = calloc(1, sizeof(**flip_data)); |
| + if (!flip_data[i]) { |
| + xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| + "flip queue failed: memory allocation error.\n"); |
| + goto error_undo; |
| + } |
| + } |
| + if (reference_crtc == NULL) { |
| + xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| + "flip queue failed: no reference crtc for pipe %d.\n", |
| + flip_info->pipe); |
| + goto error_undo; |
| + } |
| + if (reference_crtc->flip.info) { |
| + xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| + "flip queue failed: reference crtc %d (for pipe %d) " |
| + "already has a flip pending.\n", |
| + crtc_id(reference_crtc), |
| + flip_info->pipe); |
| + goto error_undo; |
| + } |
| |
| - /* Only the reference crtc will finally deliver its page flip |
| - * completion event. All other crtc's events will be discarded. |
| - */ |
| - flip->dispatch_me = (intel_crtc_to_pipe(crtc->crtc) == ref_crtc_hw_id); |
| - flip->mode = mode; |
| + intel_batch_submit(scrn); |
| |
| - if (drmModePageFlip(mode->fd, |
| - crtc_id(crtc), |
| - new_fb_id, |
| - DRM_MODE_PAGE_FLIP_EVENT, flip)) { |
| + reference_crtc->flip.pending = 0; |
| + reference_crtc->flip.frame = 0; |
| + reference_crtc->flip.tv_sec = 0; |
| + reference_crtc->flip.tv_usec = 0; |
| + |
| + /* Defer storing the flip info until we have successfully queued all of |
| + * the flips. If this function returns FALSE, the caller will free the |
| + * flip_info structure, so any successful flips must not access it. |
| + */ |
| + reference_crtc->flip.info = NULL; |
| + |
| + /* |
| + * Queue flips on all updated CRTCs. |
| + * Flips queued on disabled or incorrectly-configured crtcs may never |
| + * complete. This type of configuration error will result in the |
| + * flip never completing, and leak some objects. |
| + */ |
| + for (i = 0; i < config->num_crtc; i++) { |
| + struct intel_crtc *intel_crtc = config->crtc[i]->driver_private; |
| + if (!flip_data[i]) |
| + continue; |
| + |
| + flip_data[i]->reference_crtc = reference_crtc; |
| + flip_data[i]->flipped_crtc = intel_crtc; |
| + ret = drmModePageFlip(mode->fd, |
| + crtc_id(intel_crtc), |
| + new_front_fb_id, |
| + DRM_MODE_PAGE_FLIP_EVENT, |
| + flip_data[i]); |
| + if (ret < 0) { |
| xf86DrvMsg(scrn->scrnIndex, X_WARNING, |
| - "flip queue failed: %s\n", strerror(errno)); |
| - free(flip); |
| + "flip queue failed: error flipping crtc %d: %s\n", |
| + crtc_id(intel_crtc), strerror(-ret)); |
| goto error_undo; |
| } |
| + /* The flip_data object will be freed when the flip completes. */ |
| + flip_data[i] = NULL; |
| + |
| + reference_crtc->flip.pending++; |
| } |
| |
| - mode->old_fb_id = mode->fb_id; |
| - mode->fb_id = new_fb_id; |
| + reference_crtc->flip.info = flip_info; |
| + free(flip_data); |
| return TRUE; |
| |
| error_undo: |
| - drmModeRmFB(mode->fd, new_fb_id); |
| - for (i = 0; i < config->num_crtc; i++) { |
| - if (config->crtc[i]->enabled) |
| - intel_crtc_apply(config->crtc[i]); |
| + if (flip_data) { |
| + /* Free any unused flip data objects. */ |
| + for (i = 0; i < config->num_crtc; i++) { |
| + free(flip_data[i]); |
| + } |
| + free(flip_data); |
| } |
| - |
| -error_out: |
| - xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", |
| - strerror(errno)); |
| return FALSE; |
| } |
| |
| + |
| static const xf86CrtcConfigFuncsRec intel_xf86crtc_config_funcs = { |
| intel_xf86crtc_resize |
| }; |
| @@ -1705,31 +2152,33 @@ intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, |
| unsigned int tv_usec, void *event_data) |
| { |
| struct intel_pageflip *flip = event_data; |
| - struct intel_mode *mode = flip->mode; |
| + struct intel_crtc *reference_crtc = flip->reference_crtc; |
| + struct intel_crtc *flipped_crtc = flip->flipped_crtc; |
| |
| - /* Is this the event whose info shall be delivered to higher level? */ |
| - if (flip->dispatch_me) { |
| - /* Yes: Cache msc, ust for later delivery. */ |
| - mode->fe_frame = frame; |
| - mode->fe_tv_sec = tv_sec; |
| - mode->fe_tv_usec = tv_usec; |
| + if (reference_crtc == flipped_crtc) { |
| + /* Cache information from the reference crtc's flip for the event handler. */ |
| + reference_crtc->flip.frame = frame; |
| + reference_crtc->flip.tv_sec = tv_sec; |
| + reference_crtc->flip.tv_usec = tv_usec; |
| } |
| + |
| free(flip); |
| |
| - /* Last crtc completed flip? */ |
| - mode->flip_count--; |
| - if (mode->flip_count > 0) |
| + /* Was this the last pending flip? */ |
| + reference_crtc->flip.pending--; |
| + if (reference_crtc->flip.pending > 0) |
| return; |
| |
| - /* Release framebuffer */ |
| - drmModeRmFB(mode->fd, mode->old_fb_id); |
| - |
| - if (mode->flip_info == NULL) |
| + if (reference_crtc->flip.info == NULL) |
| return; |
| |
| - /* Deliver cached msc, ust from reference crtc to flip event handler */ |
| - I830DRI2FlipEventHandler(mode->fe_frame, mode->fe_tv_sec, |
| - mode->fe_tv_usec, mode->flip_info); |
| + /* Deliver cached info from reference crtc to flip event handler */ |
| + I830DRI2FlipEventHandler( |
| + reference_crtc->flip.frame, |
| + reference_crtc->flip.tv_sec, |
| + reference_crtc->flip.tv_usec, |
| + reference_crtc->flip.info); |
| + reference_crtc->flip.info = NULL; |
| } |
| |
| static void |
| @@ -1873,7 +2322,6 @@ intel_mode_init(struct intel_screen_private *intel) |
| * feedback on every server generation, so perform the |
| * registration within ScreenInit and not PreInit. |
| */ |
| - mode->flip_count = 0; |
| AddGeneralSocket(mode->fd); |
| RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, |
| drm_wakeup_handler, mode); |
| @@ -1884,9 +2332,9 @@ intel_mode_remove_fb(intel_screen_private *intel) |
| { |
| struct intel_mode *mode = intel->modes; |
| |
| - if (mode->fb_id) { |
| - drmModeRmFB(mode->fd, mode->fb_id); |
| - mode->fb_id = 0; |
| + if (mode->front_fb_id != 0) { |
| + drmModeRmFB(mode->fd, mode->front_fb_id); |
| + mode->front_fb_id = 0; |
| } |
| } |
| |
| @@ -1918,6 +2366,7 @@ void |
| intel_mode_fini(intel_screen_private *intel) |
| { |
| struct intel_mode *mode = intel->modes; |
| + int i; |
| |
| if (mode == NULL) |
| return; |
| @@ -1934,8 +2383,9 @@ intel_mode_fini(intel_screen_private *intel) |
| link)->output); |
| } |
| |
| - if (mode->fb_id) |
| - drmModeRmFB(mode->fd, mode->fb_id); |
| + for (i = 0; i < MAX_SCANOUTS; i++) { |
| + intel_destroy_scanout(&mode->scanouts[i]); |
| + } |
| |
| /* mode->rotate_fb_id should have been destroyed already */ |
| |
| @@ -1973,23 +2423,10 @@ Bool intel_crtc_on(xf86CrtcPtr crtc) |
| xf86OutputPtr output = xf86_config->output[i]; |
| if (output->crtc == crtc && |
| intel_output_dpms_status(output) == DPMSModeOn) { |
| - ret = TRUE; |
| - break; |
| + return TRUE; |
| } |
| } |
| - if (!ret) |
| - return FALSE; |
| - |
| - /* And finally check with the kernel that the fb is bound */ |
| - drm_crtc = drmModeGetCrtc(intel_crtc->mode->fd, crtc_id(intel_crtc)); |
| - if (drm_crtc == NULL) |
| - return FALSE; |
| - |
| - ret = (drm_crtc->mode_valid && |
| - intel_crtc->mode->fb_id == drm_crtc->buffer_id); |
| - free(drm_crtc); |
| - |
| - return ret; |
| + return FALSE; |
| } |
| |
| static PixmapPtr |
| @@ -2050,6 +2487,13 @@ intel_create_pixmap_for_fbcon(ScrnInfoPtr pScrn) |
| return pixmap; |
| } |
| |
| +void intel_set_front_fb_id(intel_screen_private *intel, PixmapPtr new_front_pixmap) |
| +{ |
| + struct intel_mode *mode = intel->modes; |
| + mode->front_fb_id = intel_pixmap_ensure_fb(intel, new_front_pixmap); |
| +} |
| + |
| + |
| void intel_copy_fb(ScrnInfoPtr pScrn) |
| { |
| ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; |
| diff --git a/src/uxa/intel_dri.c b/src/uxa/intel_dri.c |
| index 08a0bd4..b4c1a13 100644 |
| --- a/src/uxa/intel_dri.c |
| +++ b/src/uxa/intel_dri.c |
| @@ -439,6 +439,12 @@ I830DRI2CopyRegion(DrawablePtr drawable, RegionPtr pRegion, |
| ? drawable : &dstPrivate->pixmap->drawable; |
| RegionPtr pCopyClip; |
| GCPtr gc; |
| + BoxRec draw_box; |
| + struct intel_scanout *scanout; |
| + |
| + if (pRegion->extents.x1 == pRegion->extents.x2 || |
| + pRegion->extents.y1 == pRegion->extents.y2) |
| + return; /* nothing actually being copied */ |
| |
| gc = GetScratchGC(dst->depth, screen); |
| if (!gc) |
| @@ -525,6 +531,22 @@ I830DRI2CopyRegion(DrawablePtr drawable, RegionPtr pRegion, |
| } |
| } |
| |
| + if (pixmap_is_scanout(get_drawable_pixmap(dst))) { |
| + intel_drawable_box(drawable, &draw_box); |
| + if (!intel_find_scanout(intel, &draw_box, &scanout) || scanout == NULL) |
| + intel_merge_fb(intel); |
| + else |
| + dst = &scanout->pixmap->drawable; |
| + } |
| + |
| + if (pixmap_is_scanout(get_drawable_pixmap(src))) { |
| + intel_drawable_box(drawable, &draw_box); |
| + if (!intel_find_scanout(intel, &draw_box, &scanout) || scanout == NULL) |
| + intel_merge_fb(intel); |
| + else |
| + src = &scanout->pixmap->drawable; |
| + } |
| + |
| /* It's important that this copy gets submitted before the |
| * direct rendering client submits rendering for the next |
| * frame, but we don't actually need to submit right now. The |
| @@ -790,9 +812,36 @@ I830DRI2ExchangeBuffers(struct intel_screen_private *intel, DRI2BufferPtr front, |
| back_priv->pixmap); |
| dri_bo_unreference (intel->front_buffer); |
| intel->front_buffer = new_front->bo; |
| + intel_set_front_fb_id(intel, front_priv->pixmap); |
| dri_bo_reference (intel->front_buffer); |
| } |
| |
| +static void |
| +I830DRI2ExchangeBuffersSplit(struct intel_screen_private *intel, |
| + DrawablePtr draw, |
| + DRI2BufferPtr front, DRI2BufferPtr back) |
| +{ |
| + I830DRI2BufferPrivatePtr back_priv; |
| + struct intel_scanout *scanout; |
| + BoxRec draw_box; |
| + |
| + back_priv = back->driverPrivate; |
| + |
| + intel_drawable_box(draw, &draw_box); |
| + if (!intel_find_scanout(intel, &draw_box, &scanout) || scanout == NULL) { |
| + xf86DrvMsg(intel->scrn->scrnIndex, X_WARNING, |
| + "failed to exchange with scanout"); |
| + return; |
| + } |
| + |
| + /* Swap pixmap bos */ |
| + intel_exchange_pixmap_buffers(intel, |
| + scanout->pixmap, |
| + back_priv->pixmap); |
| + |
| + back->name = pixmap_flink(back_priv->pixmap); |
| +} |
| + |
| static PixmapPtr |
| intel_glamor_create_back_pixmap(ScreenPtr screen, |
| PixmapPtr front_pixmap, |
| @@ -825,13 +874,6 @@ intel_glamor_create_back_pixmap(ScreenPtr screen, |
| return back_pixmap; |
| } |
| |
| -static drm_intel_bo *get_pixmap_bo(I830DRI2BufferPrivatePtr priv) |
| -{ |
| - drm_intel_bo *bo = intel_get_pixmap_bo(priv->pixmap); |
| - assert(bo != NULL); /* guaranteed by construction of the DRI2 buffer */ |
| - return bo; |
| -} |
| - |
| /* |
| * Our internal swap routine takes care of actually exchanging, blitting, or |
| * flipping buffers as necessary. |
| @@ -842,17 +884,20 @@ I830DRI2ScheduleFlip(struct intel_screen_private *intel, |
| DRI2FrameEventPtr info) |
| { |
| I830DRI2BufferPrivatePtr priv = info->back->driverPrivate; |
| - drm_intel_bo *new_back, *old_back; |
| + drm_intel_bo *new_back; |
| int tmp_name; |
| |
| if (!intel->use_triple_buffer) { |
| - info->type = DRI2_SWAP; |
| - if (!intel_do_pageflip(intel, |
| - get_pixmap_bo(priv), |
| - info, info->pipe)) |
| + if (!intel_do_pageflip(intel, priv->pixmap, info)) |
| return FALSE; |
| |
| - I830DRI2ExchangeBuffers(intel, info->front, info->back); |
| + if (info->type == DRI2_FLIP_SPLIT) { |
| + info->type = DRI2_SWAP; |
| + I830DRI2ExchangeBuffersSplit(intel, draw, info->front, info->back); |
| + } else { |
| + info->type = DRI2_SWAP; |
| + I830DRI2ExchangeBuffers(intel, info->front, info->back); |
| + } |
| return TRUE; |
| } |
| |
| @@ -903,8 +948,7 @@ I830DRI2ScheduleFlip(struct intel_screen_private *intel, |
| intel->back_buffer = NULL; |
| } |
| |
| - old_back = get_pixmap_bo(priv); |
| - if (!intel_do_pageflip(intel, old_back, info, info->pipe)) { |
| + if (!intel_do_pageflip(intel, priv->pixmap, info)) { |
| intel->back_buffer = new_back; |
| return FALSE; |
| } |
| @@ -990,6 +1034,79 @@ can_exchange(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back) |
| return TRUE; |
| } |
| |
| +static Bool |
| +can_exchange_split(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back) |
| +{ |
| + struct intel_screen_private *intel = intel_get_screen_private(xf86Screens[drawable->pScreen->myNum]); |
| + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(intel->scrn); |
| + I830DRI2BufferPrivatePtr front_priv = front->driverPrivate; |
| + I830DRI2BufferPrivatePtr back_priv = back->driverPrivate; |
| + PixmapPtr front_pixmap = front_priv->pixmap; |
| + PixmapPtr back_pixmap = back_priv->pixmap; |
| + struct intel_pixmap *front_intel = intel_get_pixmap_private(front_pixmap); |
| + struct intel_pixmap *back_intel = intel_get_pixmap_private(back_pixmap); |
| + WindowPtr draw_win; |
| + Bool found_match, found_conflict; |
| + BoxRec draw_box, crtc_box, intersect_box; |
| + int i; |
| + |
| + if (drawable == NULL || |
| + drawable->type != DRAWABLE_WINDOW || |
| + intel->shadow_present || |
| + !intel->use_pageflipping || |
| + !intel->use_split_framebuffer) |
| + return FALSE; |
| + |
| + if (!pixmap_is_scanout(front_pixmap)) |
| + return FALSE; |
| + |
| + /* Reject cases where the drawable and backing store have different |
| + * sizes. This occurs occasionally when a resize and an update happen |
| + * simultaneously. */ |
| + if (drawable->width != back_pixmap->drawable.width || |
| + drawable->height != back_pixmap->drawable.height) |
| + return FALSE; |
| + |
| + /* Reported depth differs because the scanout pixmap has no alpha, |
| + * however the formats are still interchangeable. */ |
| + if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel || |
| + front_intel->tiling != back_intel->tiling) |
| + return FALSE; |
| + |
| + /* Can only do a split exchange if the window is completely exposed. */ |
| + draw_win = (WindowPtr)drawable; |
| + if (!RegionEqual(&draw_win->clipList, &draw_win->winSize)) |
| + return FALSE; |
| + |
| + /* Only split if the area matches at least one crtc, and does not |
| + * partially intersect any crtcs. */ |
| + found_match = FALSE; |
| + found_conflict = FALSE; |
| + intel_drawable_box(drawable, &draw_box); |
| + for (i = 0; i < config->num_crtc; i++) { |
| + xf86CrtcPtr crtc = config->crtc[i]; |
| + if (!crtc->enabled) |
| + continue; |
| + |
| + intel_crtc_box(crtc, &crtc_box); |
| + if (crtc_box.x1 == draw_box.x1 && |
| + crtc_box.y1 == draw_box.y1 && |
| + crtc_box.x2 == draw_box.x2 && |
| + crtc_box.y2 == draw_box.y2) { |
| + /* regions are equal, so can flip with this crtc */ |
| + found_match = TRUE; |
| + continue; |
| + } |
| + |
| + /* not equal, so any intersection indicates a conflict */ |
| + intel_box_intersect(&intersect_box, &draw_box, &crtc_box); |
| + if (intersect_box.x1 != intersect_box.x2 && |
| + intersect_box.y1 != intersect_box.y2) |
| + found_conflict = TRUE; |
| + } |
| + return found_match && !found_conflict; |
| +} |
| + |
| void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, |
| unsigned int tv_usec, DRI2FrameEventPtr swap_info) |
| { |
| @@ -1007,13 +1124,19 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, |
| return; |
| } |
| |
| - |
| switch (swap_info->type) { |
| - case DRI2_FLIP: |
| + case DRI2_FLIP_SPLIT: |
| + if (can_exchange_split(drawable, swap_info->front, swap_info->back)) { |
| + if (I830DRI2ScheduleFlip(intel, drawable, swap_info)) |
| + return; |
| + } |
| + |
| + case DRI2_FLIP_FRONT: |
| /* If we can still flip... */ |
| - if (can_exchange(drawable, swap_info->front, swap_info->back) && |
| - I830DRI2ScheduleFlip(intel, drawable, swap_info)) |
| - return; |
| + if (can_exchange(drawable, swap_info->front, swap_info->back)) { |
| + if (I830DRI2ScheduleFlip(intel, drawable, swap_info)) |
| + return; |
| + } |
| |
| /* else fall through to exchange/blit */ |
| case DRI2_SWAP: { |
| @@ -1032,7 +1155,7 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, |
| break; |
| default: |
| xf86DrvMsg(intel->scrn->scrnIndex, X_WARNING, |
| - "%s: unknown vblank event received\n", __func__); |
| + "%s: unknown vblank event %d received\n", __func__, swap_info->type); |
| /* Unknown type */ |
| break; |
| } |
| @@ -1116,7 +1239,7 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, |
| |
| default: |
| xf86DrvMsg(intel->scrn->scrnIndex, X_WARNING, |
| - "%s: unknown vblank event received\n", __func__); |
| + "%s: unknown vblank event %d received\n", __func__, flip_info->type); |
| /* Unknown type */ |
| break; |
| } |
| @@ -1190,6 +1313,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, |
| swap_info->front = front; |
| swap_info->back = back; |
| swap_info->pipe = pipe; |
| + intel_drawable_box(draw, &swap_info->area); |
| |
| if (!i830_dri2_add_frame_event(swap_info)) { |
| free(swap_info); |
| @@ -1215,13 +1339,19 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, |
| |
| /* Flips need to be submitted one frame before */ |
| if (can_exchange(draw, front, back)) { |
| - swap_type = DRI2_FLIP; |
| - flip = 1; |
| + intel_merge_fb(intel); |
| + swap_type = DRI2_FLIP_FRONT; |
| + flip = 1; |
| + } else if (can_exchange_split(draw, front, back)) { |
| + if (!intel_split_fb(intel)) |
| + goto blit_fallback; |
| + swap_type = DRI2_FLIP_SPLIT; |
| + flip = 1; |
| } |
| |
| swap_info->type = swap_type; |
| |
| - /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP. |
| + /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP_*. |
| * Do it early, so handling of different timing constraints |
| * for divisor, remainder and msc vs. target_msc works. |
| */ |
| @@ -1322,6 +1452,17 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, |
| return TRUE; |
| |
| blit_fallback: |
| + /* |
| + * If per-CRTC flips are enabled, I830DRI2CopyRegion will substitute a |
| + destination Drawable with its origin at (0,0). This means the blit |
| + will get clipped away on the second display in an extended desktop |
| + configuration, since the clip region begins where the drawable lies |
| + in the screen rather than at y=0. Since the common situation where |
| + this happens is where the second display is powered off, just turn |
| + off per-CRTC flips. They will be re-enabled again when the screen |
| + is powered back on. |
| + */ |
| + intel_merge_fb(intel); |
| I830DRI2FallbackBlitSwap(draw, front, back); |
| DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); |
| if (swap_info) |
| diff --git a/src/uxa/intel_driver.c b/src/uxa/intel_driver.c |
| index f3758f9..6a18839 100644 |
| --- a/src/uxa/intel_driver.c |
| +++ b/src/uxa/intel_driver.c |
| @@ -611,6 +611,14 @@ static Bool I830PreInit(ScrnInfoPtr scrn, int flags) |
| xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Triple buffering? %s\n", |
| intel->use_triple_buffer ? "enabled" : "disabled"); |
| |
| + intel->use_split_framebuffer = |
| + xf86ReturnOptValBool(intel->Options, |
| + OPTION_SPLIT_FRAMEBUFFER, |
| + TRUE); |
| + xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Split framebuffer? %s\n", |
| + intel->use_split_framebuffer ? "enabled" : "disabled"); |
| + |
| + |
| xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Framebuffer %s\n", |
| intel->tiling & INTEL_TILING_FB ? "tiled" : "linear"); |
| xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Pixmaps %s\n", |
| @@ -915,6 +923,7 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL) |
| #endif |
| struct pci_device *const device = intel->PciInfo; |
| int fb_bar = IS_GEN2(intel) ? 0 : 2; |
| + int ret; |
| |
| scrn->videoRam = device->regions[fb_bar].size / 1024; |
| |
| @@ -1079,6 +1088,7 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL) |
| * later memory should be bound when allocating, e.g rotate_mem */ |
| scrn->vtSema = TRUE; |
| |
| + |
| return I830EnterVT(VT_FUNC_ARGS(0)); |
| } |
| |
| diff --git a/src/uxa/intel_uxa.c b/src/uxa/intel_uxa.c |
| index d4ba7fc..84ed551 100644 |
| --- a/src/uxa/intel_uxa.c |
| +++ b/src/uxa/intel_uxa.c |
| @@ -633,6 +633,8 @@ dri_bo *intel_get_pixmap_bo(PixmapPtr pixmap) |
| |
| void intel_set_pixmap_bo(PixmapPtr pixmap, dri_bo * bo) |
| { |
| + ScrnInfoPtr scrn = xf86Screens[pixmap->drawable.pScreen->myNum]; |
| + intel_screen_private *intel = intel_get_screen_private(scrn); |
| struct intel_pixmap *priv; |
| |
| priv = intel_get_pixmap_private(pixmap); |
| @@ -643,6 +645,7 @@ void intel_set_pixmap_bo(PixmapPtr pixmap, dri_bo * bo) |
| if (priv->bo == bo) |
| return; |
| |
| + intel_pixmap_remove_fb(intel, pixmap); |
| dri_bo_unreference(priv->bo); |
| list_del(&priv->batch); |
| |
| @@ -693,6 +696,9 @@ static Bool intel_uxa_prepare_access(PixmapPtr pixmap, uxa_access_t access) |
| dri_bo *bo = priv->bo; |
| int ret; |
| |
| + if (pixmap_is_scanout(pixmap)) |
| + intel_merge_fb(intel); |
| + |
| /* Transitioning to glamor acceleration, we need to flush all pending |
| * usage by UXA. */ |
| if (access == UXA_GLAMOR_ACCESS_RW || access == UXA_GLAMOR_ACCESS_RO) { |
| @@ -778,8 +784,13 @@ static Bool intel_uxa_put_image(PixmapPtr pixmap, |
| int w, int h, |
| char *src, int src_pitch) |
| { |
| + ScrnInfoPtr scrn = xf86Screens[pixmap->drawable.pScreen->myNum]; |
| + intel_screen_private *intel = intel_get_screen_private(scrn); |
| struct intel_pixmap *priv; |
| |
| + if (pixmap_is_scanout(pixmap)) |
| + intel_merge_fb(intel); |
| + |
| priv = intel_get_pixmap_private(pixmap); |
| if (!intel_pixmap_is_busy(priv)) { |
| /* bo is not busy so can be replaced without a stall, upload in-place. */ |
| @@ -895,6 +906,8 @@ static Bool intel_uxa_get_image(PixmapPtr pixmap, |
| int w, int h, |
| char *dst, int dst_pitch) |
| { |
| + ScrnInfoPtr scrn = xf86Screens[pixmap->drawable.pScreen->myNum]; |
| + intel_screen_private *intel = intel_get_screen_private(scrn); |
| struct intel_pixmap *priv; |
| PixmapPtr scratch = NULL; |
| Bool ret; |
| @@ -906,6 +919,24 @@ static Bool intel_uxa_get_image(PixmapPtr pixmap, |
| * Also the gpu is much faster at detiling. |
| */ |
| |
| + if (pixmap_is_scanout(pixmap)) { |
| + struct intel_scanout *scanout; |
| + BoxRec get_box; |
| + |
| + get_box.x1 = x; |
| + get_box.x2 = x + w; |
| + get_box.y1 = y; |
| + get_box.y2 = y + h; |
| + if (!intel_covering_scanout(intel, &get_box, &scanout) || scanout == NULL) { |
| + intel_merge_fb(intel); |
| + } else { |
| + /* Adjust the copy to come from the scanout. */ |
| + pixmap = scanout->pixmap; |
| + x -= scanout->area.x1; |
| + y -= scanout->area.y1; |
| + } |
| + } |
| + |
| priv = intel_get_pixmap_private(pixmap); |
| if (intel_pixmap_is_busy(priv) || priv->tiling != I915_TILING_NONE) { |
| ScreenPtr screen = pixmap->drawable.pScreen; |
| @@ -1307,6 +1338,25 @@ static Bool intel_option_accel_blt(intel_screen_private *intel) |
| return strcasecmp(s, "blt") == 0; |
| } |
| |
| +Bool |
| +intel_uxa_driver_copy_pixmap(intel_screen_private *intel, |
| + PixmapPtr src, PixmapPtr dst, |
| + int src_x, int src_y, |
| + int dst_x, int dst_y, |
| + int w, int h) |
| +{ |
| + if (!intel->uxa_driver->check_copy(src, dst, GXcopy, FB_ALLONES)) |
| + return FALSE; |
| + |
| + if (!intel->uxa_driver->prepare_copy(src, dst, -1, -1, |
| + GXcopy, FB_ALLONES)) |
| + return FALSE; |
| + |
| + intel->uxa_driver->copy(dst, src_x, src_y, dst_x, dst_y, w, h); |
| + intel->uxa_driver->done_copy(dst); |
| + return TRUE; |
| +} |
| + |
| Bool intel_uxa_init(ScreenPtr screen) |
| { |
| ScrnInfoPtr scrn = xf86ScreenToScrn(screen); |
| diff --git a/src/uxa/intel_video.c b/src/uxa/intel_video.c |
| index 238cd47..3249da9 100644 |
| --- a/src/uxa/intel_video.c |
| +++ b/src/uxa/intel_video.c |
| @@ -1014,7 +1014,7 @@ I830CopyPlanarData(intel_adaptor_private *adaptor_priv, |
| return TRUE; |
| } |
| |
| -static void intel_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b) |
| +void intel_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b) |
| { |
| dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1; |
| dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2; |
| @@ -1029,7 +1029,7 @@ static void intel_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b) |
| dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0; |
| } |
| |
| -static void intel_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box) |
| +void intel_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box) |
| { |
| if (crtc->enabled) { |
| crtc_box->x1 = crtc->x; |
| @@ -1042,6 +1042,13 @@ static void intel_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box) |
| crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0; |
| } |
| |
| +void intel_drawable_box(DrawablePtr draw, BoxPtr draw_box) { |
| + draw_box->x1 = draw->x; |
| + draw_box->x2 = draw->x + draw->width; |
| + draw_box->y1 = draw->y; |
| + draw_box->y2 = draw->y + draw->height; |
| +} |
| + |
| static int intel_box_area(BoxPtr box) |
| { |
| return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1); |
| -- |
| 2.0.0.526.g5318336 |
| |