diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2012-06-22 11:22:16 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2012-06-23 14:16:50 +0100 |
commit | 1e9319d5f56583be99f573f208cebb0ee3b5cc26 (patch) | |
tree | 5e29b4b01e356ffb1e23c0c407bc67d4094a95f0 | |
parent | e8b090902e788257610374deae659f01a91888f3 (diff) |
sna: extend RandR to support super sized monitor configurations
With the introduction of the third pipe on IvyBridge it is possible to
encounter situations where the combination of the three monitors exceed
the limits of the scanout engine and so prevent them being used at their
native resolutions. (It is conceivable to hit similar issues on earlier
generation, especially gen2/3.) One workaround, this patch, is to extend
the RandR shadow support to break the extended framebuffer into per-crtc
pixmaps.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r-- | src/intel_options.c | 1 | ||||
-rw-r--r-- | src/intel_options.h | 1 | ||||
-rw-r--r-- | src/sna/kgem.c | 25 | ||||
-rw-r--r-- | src/sna/kgem.h | 2 | ||||
-rw-r--r-- | src/sna/sna.h | 36 | ||||
-rw-r--r-- | src/sna/sna_accel.c | 108 | ||||
-rw-r--r-- | src/sna/sna_display.c | 1179 | ||||
-rw-r--r-- | src/sna/sna_dri.c | 129 | ||||
-rw-r--r-- | src/sna/sna_driver.c | 42 | ||||
-rw-r--r-- | src/sna/sna_render.c | 14 | ||||
-rw-r--r-- | src/sna/sna_video.c | 6 | ||||
-rw-r--r-- | src/sna/sna_video_textured.c | 2 |
12 files changed, 1074 insertions, 471 deletions
diff --git a/src/intel_options.c b/src/intel_options.c index 78575a63..d8455f93 100644 --- a/src/intel_options.c +++ b/src/intel_options.c @@ -22,6 +22,7 @@ const OptionInfoRec intel_options[] = { {OPTION_THROTTLE, "Throttle", OPTV_BOOLEAN, {0}, 1}, {OPTION_ZAPHOD, "ZaphodHeads", OPTV_STRING, {0}, 0}, {OPTION_DELAYED_FLUSH, "DelayedFlush", OPTV_BOOLEAN, {0}, 1}, + {OPTION_TEAR_FREE, "TearFree", OPTV_BOOLEAN, {0}, 1}, #endif #ifdef USE_UXA {OPTION_FALLBACKDEBUG, "FallbackDebug",OPTV_BOOLEAN, {0}, 0}, diff --git a/src/intel_options.h b/src/intel_options.h index 05a2ad14..c3e49995 100644 --- a/src/intel_options.h +++ b/src/intel_options.h @@ -28,6 +28,7 @@ enum intel_options { OPTION_THROTTLE, OPTION_ZAPHOD, OPTION_DELAYED_FLUSH, + OPTION_TEAR_FREE, #endif #ifdef USE_UXA OPTION_FALLBACKDEBUG, diff --git a/src/sna/kgem.c b/src/sna/kgem.c index 9fe36616..5b58e0ec 100644 --- a/src/sna/kgem.c +++ b/src/sna/kgem.c @@ -1990,7 +1990,6 @@ void _kgem_submit(struct kgem *kgem) if (kgem->wedged) kgem_cleanup(kgem); - kgem->flush_now = kgem->scanout; kgem_reset(kgem); assert(kgem->next_request != NULL); @@ -2486,14 +2485,30 @@ done: return tiling; } +static int bits_per_pixel(int depth) +{ + switch (depth) { + case 8: return 8; + case 15: + case 16: return 16; + case 24: + case 30: + case 32: return 32; + default: return 0; + } +} + unsigned kgem_can_create_2d(struct kgem *kgem, int width, int height, int depth) { - int bpp = BitsPerPixel(depth); uint32_t pitch, size; unsigned flags = 0; + int bpp; + + DBG(("%s: %dx%d @ %d\n", __FUNCTION__, width, height, depth)); - if (depth < 8) { + bpp = bits_per_pixel(depth); + if (bpp == 0) { DBG(("%s: unhandled depth %d\n", __FUNCTION__, depth)); return 0; } @@ -2509,6 +2524,8 @@ unsigned kgem_can_create_2d(struct kgem *kgem, I915_TILING_NONE, &pitch); if (size > 0 && size <= kgem->max_cpu_size) flags |= KGEM_CAN_CREATE_CPU | KGEM_CAN_CREATE_GPU; + if (size > 0 && size <= kgem->aperture_mappable/4) + flags |= KGEM_CAN_CREATE_GTT; if (size > kgem->large_object_size) flags |= KGEM_CAN_CREATE_LARGE; if (size > kgem->max_object_size) { @@ -2524,6 +2541,8 @@ unsigned kgem_can_create_2d(struct kgem *kgem, &pitch); if (size > 0 && size <= kgem->max_gpu_size) flags |= KGEM_CAN_CREATE_GPU; + if (size > 0 && size <= kgem->aperture_mappable/4) + flags |= KGEM_CAN_CREATE_GTT; if (size > kgem->large_object_size) flags |= KGEM_CAN_CREATE_LARGE; if (size > kgem->max_object_size) { diff --git a/src/sna/kgem.h b/src/sna/kgem.h index c154be5f..2d8def81 100644 --- a/src/sna/kgem.h +++ b/src/sna/kgem.h @@ -150,7 +150,6 @@ struct kgem { uint32_t need_retire:1; uint32_t need_throttle:1; uint32_t scanout:1; - uint32_t flush_now:1; uint32_t busy:1; uint32_t has_vmap :1; @@ -218,6 +217,7 @@ unsigned kgem_can_create_2d(struct kgem *kgem, int width, int height, int depth) #define KGEM_CAN_CREATE_GPU 0x1 #define KGEM_CAN_CREATE_CPU 0x2 #define KGEM_CAN_CREATE_LARGE 0x4 +#define KGEM_CAN_CREATE_GTT 0x8 struct kgem_bo * kgem_replace_bo(struct kgem *kgem, diff --git a/src/sna/sna.h b/src/sna/sna.h index ee8273c8..2e8925b7 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -46,6 +46,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "compiler.h" #include <xf86Crtc.h> +#include <xf86str.h> #include <windowstr.h> #include <glyphstr.h> #include <picturestr.h> @@ -58,6 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "../compat-api.h" #define _XF86DRI_SERVER_ +#include <drm.h> #include <dri2.h> #include <i915_drm.h> @@ -209,6 +211,7 @@ struct sna { #define SNA_NO_DELAYED_FLUSH 0x2 #define SNA_NO_WAIT 0x4 #define SNA_NO_FLIP 0x8 +#define SNA_TEAR_FREE 0x10 unsigned watch_flush; unsigned flush; @@ -226,14 +229,16 @@ struct sna { struct list active_pixmaps; struct list inactive_clock[2]; - PixmapPtr front, shadow; + PixmapPtr front; PixmapPtr freed_pixmap; struct sna_mode { - uint32_t fb_id; - uint32_t fb_pixmap; - drmModeResPtr mode_res; - int cpp; + drmModeResPtr kmode; + + int shadow_active; + DamagePtr shadow_damage; + struct kgem_bo *shadow; + int shadow_flip; struct list outputs; struct list crtcs; @@ -256,6 +261,7 @@ struct sna { ScreenBlockHandlerProcPtr BlockHandler; ScreenWakeupHandlerProcPtr WakeupHandler; CloseScreenProcPtr CloseScreen; + xf86ModeSetProc *ModeSet; PicturePtr clear; struct { @@ -302,9 +308,10 @@ struct sna { Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna); void sna_mode_adjust_frame(struct sna *sna, int x, int y); -extern void sna_mode_remove_fb(struct sna *sna); extern void sna_mode_update(struct sna *sna); extern void sna_mode_disable_unused(struct sna *sna); +extern void sna_mode_wakeup(struct sna *sna); +extern void sna_mode_redisplay(struct sna *sna); extern void sna_mode_fini(struct sna *sna); extern int sna_crtc_id(xf86CrtcPtr crtc); @@ -356,17 +363,17 @@ to_sna_from_kgem(struct kgem *kgem) extern xf86CrtcPtr sna_covering_crtc(ScrnInfoPtr scrn, const BoxRec *box, - xf86CrtcPtr desired, - BoxPtr crtc_box_ret); + xf86CrtcPtr desired); extern bool sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap, xf86CrtcPtr crtc, const BoxRec *clip); Bool sna_dri_open(struct sna *sna, ScreenPtr pScreen); -void sna_dri_wakeup(struct sna *sna); +void sna_dri_page_flip_handler(struct sna *sna, struct drm_event_vblank *event); +void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event); void sna_dri_close(struct sna *sna, ScreenPtr pScreen); -extern Bool sna_crtc_on(xf86CrtcPtr crtc); +extern bool sna_crtc_on(xf86CrtcPtr crtc); int sna_crtc_to_pipe(xf86CrtcPtr crtc); int sna_crtc_to_plane(xf86CrtcPtr crtc); @@ -408,10 +415,12 @@ get_drawable_dy(DrawablePtr drawable) return 0; } -static inline Bool pixmap_is_scanout(PixmapPtr pixmap) +bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo); +static inline bool sna_pixmap_is_scanout(struct sna *sna, PixmapPtr pixmap) { - ScreenPtr screen = pixmap->drawable.pScreen; - return pixmap == screen->GetScreenPixmap(screen); + return (pixmap == sna->front && + !sna->mode.shadow_active && + (sna->flags & SNA_NO_WAIT) == 0); } PixmapPtr sna_pixmap_create_upload(ScreenPtr screen, @@ -429,6 +438,7 @@ struct kgem_bo *sna_pixmap_change_tiling(PixmapPtr pixmap, uint32_t tiling); #define MOVE_INPLACE_HINT 0x4 #define MOVE_ASYNC_HINT 0x8 #define MOVE_SOURCE_HINT 0x10 +#define __MOVE_FORCE 0x20 bool must_check _sna_pixmap_move_to_cpu(PixmapPtr pixmap, unsigned flags); static inline bool must_check sna_pixmap_move_to_cpu(PixmapPtr pixmap, unsigned flags) { diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c index 5b0b33de..0f52a273 100644 --- a/src/sna/sna_accel.c +++ b/src/sna/sna_accel.c @@ -645,7 +645,7 @@ _sna_pixmap_reset(PixmapPtr pixmap) return _sna_pixmap_init(priv, pixmap); } -static struct sna_pixmap *sna_pixmap_attach(struct sna *sna, PixmapPtr pixmap) +static struct sna_pixmap *sna_pixmap_attach(PixmapPtr pixmap) { struct sna_pixmap *priv; @@ -657,6 +657,22 @@ static struct sna_pixmap *sna_pixmap_attach(struct sna *sna, PixmapPtr pixmap) return _sna_pixmap_init(priv, pixmap); } +bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo) +{ + struct sna_pixmap *priv; + + priv = sna_pixmap_attach(pixmap); + if (!priv) + return false; + + priv->gpu_bo = kgem_bo_reference(bo); + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + + return true; +} + static inline PixmapPtr create_pixmap(struct sna *sna, ScreenPtr screen, int width, int height, int depth, @@ -724,7 +740,7 @@ sna_pixmap_create_shm(ScreenPtr screen, pixmap->drawable.depth = depth; pixmap->drawable.bitsPerPixel = bpp; - priv = sna_pixmap_attach(sna, pixmap); + priv = sna_pixmap_attach(pixmap); if (!priv) { fbDestroyPixmap(pixmap); return NullPixmap; @@ -816,7 +832,7 @@ sna_pixmap_create_scratch(ScreenPtr screen, pixmap->drawable.depth = depth; pixmap->drawable.bitsPerPixel = bpp; - priv = sna_pixmap_attach(sna, pixmap); + priv = sna_pixmap_attach(pixmap); if (!priv) { fbDestroyPixmap(pixmap); return NullPixmap; @@ -907,7 +923,7 @@ force_create: if (pixmap == NullPixmap) return NullPixmap; - sna_pixmap_attach(sna, pixmap); + sna_pixmap_attach(pixmap); } else { struct sna_pixmap *priv; @@ -923,7 +939,7 @@ force_create: pixmap->devKind = pad; pixmap->devPrivate.ptr = NULL; - priv = sna_pixmap_attach(sna, pixmap); + priv = sna_pixmap_attach(pixmap); if (priv == NULL) { free(pixmap); goto fallback; @@ -2508,7 +2524,7 @@ sna_pixmap_force_to_gpu(PixmapPtr pixmap, unsigned flags) } } - if (!sna_pixmap_move_to_gpu(pixmap, flags)) + if (!sna_pixmap_move_to_gpu(pixmap, flags | __MOVE_FORCE)) return NULL; /* For large bo, try to keep only a single copy around */ @@ -2537,6 +2553,9 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags) DBG(("%s(pixmap=%ld, usage=%d)\n", __FUNCTION__, pixmap->drawable.serialNumber, pixmap->usage_hint)); + if ((flags & __MOVE_FORCE) == 0 && wedged(sna)) + return NULL; + priv = sna_pixmap(pixmap); if (priv == NULL) { DBG(("%s: not attached\n", __FUNCTION__)); @@ -12277,7 +12296,6 @@ sna_accel_flush_callback(CallbackListPtr *list, } kgem_submit(&sna->kgem); - sna->kgem.flush_now = 0; kgem_sync(&sna->kgem); @@ -12286,8 +12304,7 @@ sna_accel_flush_callback(CallbackListPtr *list, static struct sna_pixmap *sna_accel_scanout(struct sna *sna) { - PixmapPtr front = sna->shadow ? sna->shadow : sna->front; - struct sna_pixmap *priv = sna_pixmap(front); + struct sna_pixmap *priv = sna_pixmap(sna->front); return priv && priv->gpu_bo ? priv : NULL; } @@ -12298,12 +12315,48 @@ static void sna_accel_disarm_timer(struct sna *sna, int id) sna->timer_ready &= ~(1<<id); } +static bool has_shadow(struct sna *sna) +{ + DamagePtr damage = sna->mode.shadow_damage; + + if (!(damage && RegionNotEmpty(DamageRegion(damage)))) + return false; + + DBG(("%s: has pending damage\n", __FUNCTION__)); + if ((sna->flags & SNA_TEAR_FREE) == 0) + return true; + + DBG(("%s: outstanding flips: %d\n", + __FUNCTION__, sna->mode.shadow_flip)); + return !sna->mode.shadow_flip; +} + +static bool need_flush(struct sna *sna, struct sna_pixmap *scanout) +{ + DBG(("%s: scanout=%d shadow?=%d || (cpu?=%d || gpu?=%d) && !busy=%d)\n", + __FUNCTION__, + scanout && scanout->gpu_bo ? scanout->gpu_bo->handle : 0, + has_shadow(sna), + scanout && scanout->cpu_damage != NULL, + scanout && scanout->gpu_bo && scanout->gpu_bo->exec != NULL, + scanout && scanout->gpu_bo && __kgem_flush(&sna->kgem, scanout->gpu_bo))); + + if (has_shadow(sna)) + return true; + + if (!scanout) + return false; + + return (scanout->cpu_damage || scanout->gpu_bo->exec) && + !__kgem_flush(&sna->kgem, scanout->gpu_bo); +} + static bool sna_accel_do_flush(struct sna *sna) { struct sna_pixmap *priv; priv = sna_accel_scanout(sna); - if (priv == NULL || priv->gpu_bo == NULL) { + if (priv == NULL && !sna->mode.shadow_active) { DBG(("%s -- no scanout attached\n", __FUNCTION__)); sna_accel_disarm_timer(sna, FLUSH_TIMER); return false; @@ -12313,27 +12366,19 @@ static bool sna_accel_do_flush(struct sna *sna) return true; if (sna->timer_active & (1<<(FLUSH_TIMER))) { - if (sna->kgem.flush_now) { - sna->kgem.flush_now = 0; - if (priv->gpu_bo->exec) { - DBG(("%s -- forcing flush\n", __FUNCTION__)); - sna->timer_ready |= 1 << FLUSH_TIMER; - } - } - + DBG(("%s: flush timer active\n", __FUNCTION__)); if (sna->timer_ready & (1<<(FLUSH_TIMER))) { DBG(("%s (time=%ld), triggered\n", __FUNCTION__, (long)sna->time)); sna->timer_expire[FLUSH_TIMER] = sna->time + sna->vblank_interval; - return priv->cpu_damage || !__kgem_flush(&sna->kgem, priv->gpu_bo); + return true; } } else { - if (priv->cpu_damage == NULL && - !__kgem_flush(&sna->kgem, priv->gpu_bo)) { + if (!need_flush(sna, priv)) { DBG(("%s -- no pending write to scanout\n", __FUNCTION__)); } else { sna->timer_active |= 1 << FLUSH_TIMER; - sna->timer_ready |= 1 << FLUSH_TIMER; + sna->timer_ready |= 1 << FLUSH_TIMER; sna->timer_expire[FLUSH_TIMER] = sna->time + sna->vblank_interval / 2; DBG(("%s (time=%ld), starting\n", __FUNCTION__, (long)sna->time)); @@ -12447,24 +12492,24 @@ static void sna_accel_flush(struct sna *sna) struct sna_pixmap *priv = sna_accel_scanout(sna); bool busy; - assert(priv != NULL); DBG(("%s (time=%ld), cpu damage? %p, exec? %d nbatch=%d, busy? %d\n", __FUNCTION__, (long)sna->time, - priv->cpu_damage, - priv->gpu_bo->exec != NULL, + priv && priv->cpu_damage, + priv && priv->gpu_bo->exec != NULL, sna->kgem.nbatch, sna->kgem.busy)); - busy = priv->cpu_damage || priv->gpu_bo->rq; + busy = need_flush(sna, priv); if (!sna->kgem.busy && !busy) sna_accel_disarm_timer(sna, FLUSH_TIMER); sna->kgem.busy = busy; - if (priv->cpu_damage) - sna_pixmap_move_to_gpu(priv->pixmap, MOVE_READ); + if (priv) { + sna_pixmap_force_to_gpu(priv->pixmap, MOVE_READ); + kgem_bo_flush(&sna->kgem, priv->gpu_bo); + } - kgem_bo_flush(&sna->kgem, priv->gpu_bo); - sna->kgem.flush_now = 0; + sna_mode_redisplay(sna); } static void sna_accel_throttle(struct sna *sna) @@ -12811,10 +12856,9 @@ void sna_accel_wakeup_handler(struct sna *sna, fd_set *ready) DBG(("%s\n", __FUNCTION__)); if (sna->kgem.need_retire) kgem_retire(&sna->kgem); - if (!sna->kgem.need_retire) { + if (!sna->mode.shadow_active && !sna->kgem.need_retire) { DBG(("%s: GPU idle, flushing\n", __FUNCTION__)); kgem_submit(&sna->kgem); - sna->kgem.flush_now = 0; } if (sna->kgem.need_purge) kgem_purge_cache(&sna->kgem); diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c index de834ae9..0896af41 100644 --- a/src/sna/sna_display.c +++ b/src/sna/sna_display.c @@ -43,6 +43,9 @@ #include <xf86drm.h> #include <xf86DDC.h> /* for xf86InterpretEDID */ +#include <fb.h> +#include <fbpict.h> + #include "sna.h" #include "sna_reg.h" @@ -61,13 +64,12 @@ struct sna_crtc { struct drm_mode_modeinfo kmode; - PixmapPtr shadow; - uint32_t shadow_fb_id; + struct kgem_bo *bo; uint32_t cursor; + bool shadow; uint8_t id; uint8_t pipe; uint8_t plane; - uint8_t active; struct list link; }; @@ -126,10 +128,9 @@ static const char *backlight_interfaces[] = { /* Enough for 10 digits of backlight + '\n' + '\0' */ #define BACKLIGHT_VALUE_LEN 12 -static inline int -crtc_id(struct sna_crtc *crtc) +static inline uint32_t fb_id(struct kgem_bo *bo) { - return crtc->id; + return bo->delta; } int sna_crtc_id(xf86CrtcPtr crtc) @@ -137,9 +138,9 @@ int sna_crtc_id(xf86CrtcPtr crtc) return to_sna_crtc(crtc)->id; } -int sna_crtc_on(xf86CrtcPtr crtc) +bool sna_crtc_on(xf86CrtcPtr crtc) { - return to_sna_crtc(crtc)->active; + return to_sna_crtc(crtc)->bo != NULL; } int sna_crtc_to_pipe(xf86CrtcPtr crtc) @@ -159,7 +160,6 @@ static unsigned get_fb(struct sna *sna, struct kgem_bo *bo, { ScrnInfoPtr scrn = sna->scrn; struct drm_mode_fb_cmd arg; - int ret; assert(bo->proxy == NULL); if (bo->delta) { @@ -181,11 +181,11 @@ static unsigned get_fb(struct sna *sna, struct kgem_bo *bo, arg.depth = scrn->depth; arg.handle = bo->handle; - if ((ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg))) { + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg)) { xf86DrvMsg(scrn->scrnIndex, X_ERROR, "%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d: %d\n", __FUNCTION__, width, height, - scrn->depth, scrn->bitsPerPixel, bo->pitch, ret); + scrn->depth, scrn->bitsPerPixel, bo->pitch, errno); return 0; } @@ -397,16 +397,20 @@ mode_to_kmode(struct drm_mode_modeinfo *kmode, DisplayModePtr mode) bool sna_crtc_is_bound(struct sna *sna, xf86CrtcPtr crtc) { + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); struct drm_mode_crtc mode; + if (!sna_crtc->bo) + return false; + VG_CLEAR(mode); - mode.crtc_id = to_sna_crtc(crtc)->id; + mode.crtc_id = sna_crtc->id; if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode)) return false; DBG(("%s: crtc=%d, mode valid?=%d, fb attached?=%d\n", __FUNCTION__, - mode.crtc_id, mode.mode_valid, sna->mode.fb_id == mode.fb_id)); - return mode.mode_valid && sna->mode.fb_id == mode.fb_id; + mode.crtc_id, mode.mode_valid, fb_id(sna_crtc->bo) == mode.fb_id)); + return mode.mode_valid && fb_id(sna_crtc->bo) == mode.fb_id; } static Bool @@ -415,11 +419,9 @@ sna_crtc_apply(xf86CrtcPtr crtc) struct sna *sna = to_sna(crtc->scrn); struct sna_crtc *sna_crtc = to_sna_crtc(crtc); xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); - struct sna_mode *mode = &sna->mode; struct drm_mode_crtc arg; uint32_t output_ids[16]; int output_count = 0; - int fb_id, x, y; int i, ret = FALSE; DBG(("%s\n", __FUNCTION__)); @@ -439,22 +441,23 @@ sna_crtc_apply(xf86CrtcPtr crtc) output_count++; } - if (!xf86CrtcRotate(crtc)) { - DBG(("%s: failed to rotate crtc\n", __FUNCTION__)); - return FALSE; - } - crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, crtc->gamma_blue, crtc->gamma_size); - x = crtc->x; - y = crtc->y; - fb_id = mode->fb_id; - if (sna_crtc->shadow_fb_id) { - fb_id = sna_crtc->shadow_fb_id; - x = 0; - y = 0; + VG_CLEAR(arg); + arg.crtc_id = sna_crtc->id; + arg.fb_id = fb_id(sna_crtc->bo); + if (sna_crtc->shadow) { + arg.x = 0; + arg.y = 0; + } else { + arg.x = crtc->x; + arg.y = crtc->y; } + arg.set_connectors_ptr = (uintptr_t)output_ids; + arg.count_connectors = output_count; + arg.mode = sna_crtc->kmode; + arg.mode_valid = 1; xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO, "switch to mode %dx%d on crtc %d (pipe %d)\n", @@ -467,22 +470,14 @@ sna_crtc_apply(xf86CrtcPtr crtc) sna_crtc->kmode.hdisplay, sna_crtc->kmode.vdisplay, sna_crtc->kmode.clock, - fb_id, sna_crtc->shadow_fb_id ? " [shadow]" : "", + arg.fb_id, + sna_crtc->shadow ? " [shadow]" : "", output_count)); - VG_CLEAR(arg); - arg.x = x; - arg.y = y; - arg.crtc_id = sna_crtc->id; - arg.fb_id = fb_id; - arg.set_connectors_ptr = (uintptr_t)output_ids; - arg.count_connectors = output_count; - arg.mode = sna_crtc->kmode; - arg.mode_valid = 1; ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg); if (ret) { xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, - "failed to set mode: %s\n", strerror(-ret)); + "failed to set mode: %s\n", strerror(errno)); ret = FALSE; } else ret = TRUE; @@ -493,6 +488,79 @@ sna_crtc_apply(xf86CrtcPtr crtc) return ret; } +static bool sna_mode_enable_shadow(struct sna *sna) +{ + ScreenPtr screen = sna->scrn->pScreen; + + DBG(("%s\n", __FUNCTION__)); + assert(sna->mode.shadow == NULL); + assert(sna->mode.shadow_damage == NULL); + assert(sna->mode.shadow_active == 0); + + sna->mode.shadow_damage = DamageCreate(NULL, NULL, + DamageReportNone, TRUE, + screen, screen); + if (!sna->mode.shadow_damage) + return false; + + DamageRegister(&sna->front->drawable, sna->mode.shadow_damage); + return true; +} + +static void sna_mode_disable_shadow(struct sna *sna) +{ + if (!sna->mode.shadow_damage) + return; + + DBG(("%s\n", __FUNCTION__)); + + DamageUnregister(&sna->front->drawable, sna->mode.shadow_damage); + DamageDestroy(sna->mode.shadow_damage); + sna->mode.shadow_damage = NULL; + + if (sna->mode.shadow) { + kgem_bo_destroy(&sna->kgem, sna->mode.shadow); + sna->mode.shadow = NULL; + } + + sna->mode.shadow_active = 0; +} + +static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc) +{ + if (crtc->shadow) { + assert(sna->mode.shadow_damage && sna->mode.shadow_active); + return true; + } + + DBG(("%s: enabling for crtc %d\n", __FUNCTION__, crtc->id)); + + if (!sna->mode.shadow_active) { + if (!sna_mode_enable_shadow(sna)) + return false; + assert(sna->mode.shadow_damage); + assert(sna->mode.shadow == NULL); + } + + crtc->shadow = true; + sna->mode.shadow_active++; + return true; +} + +static void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc) +{ + if (!crtc->shadow) + return; + + DBG(("%s: disabling for crtc %d\n", __FUNCTION__, crtc->id)); + assert(sna->mode.shadow_active > 0); + + if (!--sna->mode.shadow_active) + sna_mode_disable_shadow(sna); + + crtc->shadow = false; +} + static void sna_crtc_disable(xf86CrtcPtr crtc) { @@ -507,7 +575,13 @@ sna_crtc_disable(xf86CrtcPtr crtc) arg.fb_id = 0; arg.mode_valid = 0; (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg); - sna_crtc->active = false; + + sna_crtc_disable_shadow(sna, sna_crtc); + + if (sna_crtc->bo) { + kgem_bo_destroy(&sna->kgem, sna_crtc->bo); + sna_crtc->bo = NULL; + } } static void @@ -675,70 +749,267 @@ static void update_flush_interval(struct sna *sna) max_vrefresh, sna->vblank_interval)); } -static Bool -sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, - Rotation rotation, int x, int y) +static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc) { + RRTransformPtr transform; + PictTransform crtc_to_fb; + struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc; + BoxRec b; + + assert(sna->scrn->virtualX && sna->scrn->virtualY); + + if (sna->scrn->virtualX > sna->mode.kmode->max_width || + sna->scrn->virtualY > sna->mode.kmode->max_height) { + DBG(("%s: framebuffer too large (%dx%d) > (%dx%d)\n", + __FUNCTION__, + sna->scrn->virtualX, sna->scrn->virtualY, + sna->mode.kmode->max_width, + sna->mode.kmode->max_height)); + return true; + } + + transform = NULL; + if (crtc->transformPresent) + transform = &crtc->transform; + if (RRTransformCompute(crtc->x, crtc->y, + crtc->mode.HDisplay, crtc->mode.VDisplay, + crtc->rotation, transform, + &crtc_to_fb, + &f_crtc_to_fb, + &f_fb_to_crtc)) { + DBG(("%s: RandR transform present\n", __FUNCTION__)); + return true; + } + + /* And finally check that it is entirely visible */ + b.x1 = b.y1 = 0; + b.x2 = crtc->mode.HDisplay; + b.y2 = crtc->mode.VDisplay; + pixman_f_transform_bounds(&f_crtc_to_fb, &b); + DBG(("%s? bounds (%d, %d), (%d, %d), framebufer %dx%d\n", + __FUNCTION__, b.x1, b.y1, b.x2, b.y2, + sna->scrn->virtualX, sna->scrn->virtualY)); + + if (b.x1 < 0 || b.y1 < 0 || + b.x2 > sna->scrn->virtualX || + b.y2 > sna->scrn->virtualY) { + DBG(("%s: scanout is partly outside the framebuffer\n", + __FUNCTION__)); + return true; + } + + return false; +} + +static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc) +{ + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); ScrnInfoPtr scrn = crtc->scrn; struct sna *sna = to_sna(scrn); - struct sna_crtc *sna_crtc = to_sna_crtc(crtc); - struct sna_mode *sna_mode = &sna->mode; - int saved_x, saved_y; - Rotation saved_rotation; - DisplayModeRec saved_mode; + struct kgem_bo *bo; - DBG(("%s(rotation=%d, x=%d, y=%d, mode=%dx%d@%d)\n", - __FUNCTION__, rotation, x, y, - mode->HDisplay, mode->VDisplay, mode->Clock)); + if (use_shadow(sna, crtc)) { + if (!sna_crtc_enable_shadow(sna, sna_crtc)) + return NULL; - DBG(("%s: current fb pixmap = %d, front is %lu\n", - __FUNCTION__, - sna_mode->fb_pixmap, - sna->front->drawable.serialNumber)); + DBG(("%s: attaching to per-crtc pixmap %dx%d\n", + __FUNCTION__, crtc->mode.HDisplay, crtc->mode.VDisplay)); - if (sna_mode->fb_pixmap != sna->front->drawable.serialNumber) { - kgem_submit(&sna->kgem); - sna_mode_remove_fb(sna); + bo = kgem_create_2d(&sna->kgem, + crtc->mode.HDisplay, crtc->mode.VDisplay, + scrn->bitsPerPixel, + I915_TILING_X, CREATE_SCANOUT); + if (bo == NULL) + return NULL; + + if (!get_fb(sna, bo, crtc->mode.HDisplay, crtc->mode.VDisplay)) { + kgem_bo_destroy(&sna->kgem, bo); + return NULL; + } + + return bo; + } else if (sna->flags & SNA_TEAR_FREE) { + DBG(("%s: tear-free updates requested\n", __FUNCTION__)); + + if (!sna_crtc_enable_shadow(sna, sna_crtc)) + return NULL; + + DBG(("%s: attaching to single shadow pixmap\n", __FUNCTION__)); + if (sna->mode.shadow == NULL) { + bo = kgem_create_2d(&sna->kgem, + sna->scrn->virtualX, + sna->scrn->virtualY, + scrn->bitsPerPixel, + I915_TILING_X, + CREATE_SCANOUT); + if (bo == NULL) + return NULL; + + if (!get_fb(sna, bo, + sna->scrn->virtualX, + sna->scrn->virtualY)) { + kgem_bo_destroy(&sna->kgem, bo); + return NULL; + } + + sna->mode.shadow = bo; + } + + return kgem_bo_reference(sna->mode.shadow); + } else { + DBG(("%s: attaching to framebuffer\n", __FUNCTION__)); + sna_crtc_disable_shadow(sna, sna_crtc); + bo = sna_pixmap_pin(sna->front); + if (!get_fb(sna, bo, scrn->virtualX, scrn->virtualY)) + return NULL; + + return kgem_bo_reference(bo); } +} - if (sna_mode->fb_id == 0) { - struct kgem_bo *bo = sna_pixmap_pin(sna->front); - if (!bo) - return FALSE; +static void sna_crtc_randr(xf86CrtcPtr crtc) +{ + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc; + PictTransform crtc_to_fb; + PictFilterPtr filter; + xFixed *params; + int nparams; + RRTransformPtr transform; + + transform = NULL; + if (crtc->transformPresent) + transform = &crtc->transform; + + RRTransformCompute(crtc->x, crtc->y, + crtc->mode.HDisplay, crtc->mode.VDisplay, + crtc->rotation, transform, + &crtc_to_fb, + &f_crtc_to_fb, + &f_fb_to_crtc); + + filter = NULL; + params = NULL; + nparams = 0; + if (sna_crtc->shadow) { +#ifdef RANDR_12_INTERFACE + if (transform) { + if (transform->nparams) { + params = malloc(transform->nparams * sizeof(xFixed)); + if (params) { + memcpy(params, transform->params, + transform->nparams * sizeof(xFixed)); + nparams = transform->nparams; + filter = transform->filter; + } + } else + filter = transform->filter; + } +#endif + crtc->transform_in_use = TRUE; + } else + crtc->transform_in_use = FALSE; - /* XXX recreate the fb in case the size has changed? */ - sna_mode->fb_id = get_fb(sna, bo, - scrn->virtualX, scrn->virtualY); - if (sna_mode->fb_id == 0) - return FALSE; + crtc->crtc_to_framebuffer = crtc_to_fb; + crtc->f_crtc_to_framebuffer = f_crtc_to_fb; + crtc->f_framebuffer_to_crtc = f_fb_to_crtc; - DBG(("%s: handle %d attached to fb %d\n", - __FUNCTION__, bo->handle, sna_mode->fb_id)); + free(crtc->params); + crtc->params = params; + crtc->nparams = nparams; - sna_mode->fb_pixmap = sna->front->drawable.serialNumber; + crtc->filter = filter; + if (filter) { + crtc->filter_width = filter->width; + crtc->filter_height = filter->height; + } else { + crtc->filter_width = 0; + crtc->filter_height = 0; } - saved_mode = crtc->mode; - saved_x = crtc->x; - saved_y = crtc->y; - saved_rotation = crtc->rotation; + crtc->bounds.x1 = 0; + crtc->bounds.x2 = crtc->mode.HDisplay; + crtc->bounds.y1 = 0; + crtc->bounds.y2 = crtc->mode.VDisplay; + pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds); + + DBG(("%s: transform? %d, bounds (%d, %d), (%d, %d)\n", + __FUNCTION__, crtc->transform_in_use, + crtc->bounds.x1, crtc->bounds.y1, + crtc->bounds.x2, crtc->bounds.y2)); +} + +static void +sna_crtc_damage(xf86CrtcPtr crtc) +{ + ScreenPtr screen = crtc->scrn->pScreen; + struct sna *sna = to_sna(crtc->scrn); + RegionRec region, *damage; + + region.extents = crtc->bounds; + region.data = NULL; - crtc->mode = *mode; - crtc->x = x; - crtc->y = y; - crtc->rotation = rotation; + if (region.extents.x1 < 0) + region.extents.x1 = 0; + if (region.extents.y1 < 0) + region.extents.y1 = 0; + if (region.extents.x2 > screen->width) + region.extents.x2 = screen->width; + if (region.extents.y2 > screen->width) + region.extents.y2 = screen->height; + DBG(("%s: marking crtc %d as completely damaged (%d, %d), (%d, %d)\n", + __FUNCTION__, to_sna_crtc(crtc)->id, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + assert(sna->mode.shadow_damage && sna->mode.shadow_active); + damage = DamageRegion(sna->mode.shadow_damage); + RegionUnion(damage, damage, ®ion); +} + +static Bool +sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, + Rotation rotation, int x, int y) +{ + ScrnInfoPtr scrn = crtc->scrn; + struct sna *sna = to_sna(scrn); + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + struct kgem_bo *saved_bo, *bo; + struct drm_mode_modeinfo saved_kmode; + + DBG(("%s(crtc=%d [pipe=%d] rotation=%d, x=%d, y=%d, mode=%dx%d@%d)\n", + __FUNCTION__, sna_crtc->id, sna_crtc->pipe, rotation, x, y, + mode->HDisplay, mode->VDisplay, mode->Clock)); + + assert(mode->HDisplay <= sna->mode.kmode->max_width && + mode->VDisplay <= sna->mode.kmode->max_height); + + /* Attach per-crtc pixmap or direct */ + bo = sna_crtc_attach(crtc); + if (bo == NULL) + return FALSE; + + saved_kmode = sna_crtc->kmode; + saved_bo = sna_crtc->bo; + sna_crtc->bo = bo; mode_to_kmode(&sna_crtc->kmode, mode); + if (!sna_crtc_apply(crtc)) { - crtc->x = saved_x; - crtc->y = saved_y; - crtc->rotation = saved_rotation; - crtc->mode = saved_mode; + sna_crtc->bo = saved_bo; + sna_crtc->kmode = saved_kmode; + kgem_bo_destroy(&sna->kgem, bo); return FALSE; } - sna_mode_update(sna); + if (saved_bo) + kgem_bo_destroy(&sna->kgem, saved_bo); update_flush_interval(sna); + + sna_crtc_randr(crtc); + if (sna_crtc->shadow) + sna_crtc_damage(crtc); + return TRUE; } @@ -748,8 +1019,18 @@ void sna_mode_adjust_frame(struct sna *sna, int x, int y) xf86OutputPtr output = config->output[config->compat_output]; xf86CrtcPtr crtc = output->crtc; - if (crtc && crtc->enabled) - sna_crtc_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y); + if (crtc && crtc->enabled) { + int saved_x = crtc->x; + int saved_y = crtc->y; + + crtc->x = x; + crtc->y = y; + if (!sna_crtc_set_mode_major(crtc, &crtc->mode, + crtc->rotation, x, y)) { + crtc->x = saved_x; + crtc->y = saved_y; + } + } } static void @@ -831,65 +1112,6 @@ sna_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image) (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite); } -static void * -sna_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) -{ - ScrnInfoPtr scrn = crtc->scrn; - struct sna *sna = to_sna(scrn); - struct sna_crtc *sna_crtc = to_sna_crtc(crtc); - PixmapPtr shadow; - struct kgem_bo *bo; - - DBG(("%s(%d, %d)\n", __FUNCTION__, width, height)); - - shadow = scrn->pScreen->CreatePixmap(scrn->pScreen, - width, height, scrn->depth, - SNA_CREATE_FB); - if (!shadow) - return NULL; - - bo = sna_pixmap_pin(shadow); - if (!bo) { - scrn->pScreen->DestroyPixmap(shadow); - return NULL; - } - - sna_crtc->shadow_fb_id = get_fb(sna, bo, width, height); - if (sna_crtc->shadow_fb_id == 0) { - scrn->pScreen->DestroyPixmap(shadow); - return NULL; - } - - DBG(("%s: attached handle %d to fb %d\n", - __FUNCTION__, bo->handle, sna_crtc->shadow_fb_id)); - return sna_crtc->shadow = shadow; -} - -static PixmapPtr -sna_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) -{ - return data; -} - -static void -sna_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data) -{ - struct sna_crtc *sna_crtc = to_sna_crtc(crtc); - - /* We may have not called shadow_create() on the data yet and - * be cleaning up a NULL shadow_pixmap. - */ - pixmap = data; - - DBG(("%s(fb=%d, handle=%d)\n", __FUNCTION__, - sna_crtc->shadow_fb_id, sna_pixmap_get_bo(pixmap)->handle)); - - sna_crtc->shadow_fb_id = 0; - - pixmap->drawable.pScreen->DestroyPixmap(pixmap); - sna_crtc->shadow = NULL; -} - static void sna_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, int size) @@ -924,9 +1146,6 @@ static const xf86CrtcFuncsRec sna_crtc_funcs = { .show_cursor = sna_crtc_show_cursor, .hide_cursor = sna_crtc_hide_cursor, .load_cursor_argb = sna_crtc_load_cursor_argb, - .shadow_create = sna_crtc_shadow_create, - .shadow_allocate = sna_crtc_shadow_allocate, - .shadow_destroy = sna_crtc_shadow_destroy, .gamma_set = sna_crtc_gamma_set, .destroy = sna_crtc_destroy, }; @@ -986,7 +1205,7 @@ sna_crtc_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num) if (sna_crtc == NULL) return; - sna_crtc->id = mode->mode_res->crtcs[num]; + sna_crtc->id = mode->kmode->crtcs[num]; VG_CLEAR(get_pipe); get_pipe.pipe = 0; @@ -1061,17 +1280,29 @@ sna_output_detect(xf86OutputPtr output) } static Bool -sna_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) +sna_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode) { struct sna_output *sna_output = output->driver_private; + struct sna *sna = to_sna(output->scrn); + + if (mode->HDisplay > sna->mode.kmode->max_width) + return MODE_VIRTUAL_X; + if (mode->VDisplay > sna->mode.kmode->max_height) + return MODE_VIRTUAL_Y; + + /* Check that we can successfully pin this into the global GTT */ + if ((kgem_can_create_2d(&sna->kgem, + mode->HDisplay, mode->VDisplay, + sna->scrn->bitsPerPixel) & KGEM_CAN_CREATE_GTT) == 0) + return MODE_MEM_VIRT; /* * If the connector type is a panel, we will use the panel limit to * verfiy whether the mode is valid. */ if (sna_output->has_panel_limits) { - if (pModes->HDisplay > sna_output->panel_hdisplay || - pModes->VDisplay > sna_output->panel_vdisplay) + if (mode->HDisplay > sna_output->panel_hdisplay || + mode->VDisplay > sna_output->panel_vdisplay) return MODE_PANEL; } @@ -1684,7 +1915,7 @@ sna_output_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num) char name[32]; koutput = drmModeGetConnector(sna->kgem.fd, - mode->mode_res->connectors[num]); + mode->kmode->connectors[num]); if (!koutput) return; @@ -1713,7 +1944,7 @@ sna_output_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num) if (!sna_output) goto cleanup_output; - sna_output->id = mode->mode_res->connectors[num]; + sna_output->id = mode->kmode->connectors[num]; sna_output->mode_output = koutput; output->mm_width = koutput->mmWidth; @@ -1773,12 +2004,9 @@ sna_redirect_screen_pixmap(ScrnInfoPtr scrn, PixmapPtr old, PixmapPtr new) static Bool sna_crtc_resize(ScrnInfoPtr scrn, int width, int height) { - struct sna *sna = to_sna(scrn); xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); - struct sna_mode *mode = &sna->mode; - PixmapPtr old_front; - uint32_t old_fb_id; - struct kgem_bo *bo; + struct sna *sna = to_sna(scrn); + PixmapPtr old_front, new_front; int i; DBG(("%s (%d, %d) -> (%d, %d)\n", @@ -1791,32 +2019,27 @@ sna_crtc_resize(ScrnInfoPtr scrn, int width, int height) assert(scrn->pScreen->GetScreenPixmap(scrn->pScreen) == sna->front); assert(scrn->pScreen->GetWindowPixmap(scrn->pScreen->root) == sna->front); + DBG(("%s: creating new framebuffer %dx%d\n", + __FUNCTION__, width, height)); - kgem_submit(&sna->kgem); - - old_fb_id = mode->fb_id; old_front = sna->front; - - sna->front = scrn->pScreen->CreatePixmap(scrn->pScreen, + new_front = scrn->pScreen->CreatePixmap(scrn->pScreen, width, height, scrn->depth, SNA_CREATE_FB); - if (!sna->front) - goto fail; - - bo = sna_pixmap_pin(sna->front); - if (!bo) - goto fail; - - assert(bo->delta == 0); + if (!new_front) + return FALSE; - mode->fb_id = get_fb(sna, bo, width, height); - if (mode->fb_id == 0) - goto fail; + for (i = 0; i < xf86_config->num_crtc; i++) + sna_crtc_disable_shadow(sna, to_sna_crtc(xf86_config->crtc[i])); + assert(sna->mode.shadow_active == 0); + assert(sna->mode.shadow_damage == NULL); + assert(sna->mode.shadow == NULL); - DBG(("%s: handle %d, pixmap serial %lu attached to fb %d\n", - __FUNCTION__, bo->handle, - sna->front->drawable.serialNumber, mode->fb_id)); + sna->front = new_front; + scrn->virtualX = width; + scrn->virtualY = height; + scrn->displayWidth = width; for (i = 0; i < xf86_config->num_crtc; i++) { xf86CrtcPtr crtc = xf86_config->crtc[i]; @@ -1824,18 +2047,12 @@ sna_crtc_resize(ScrnInfoPtr scrn, int width, int height) if (!crtc->enabled) continue; - if (!sna_crtc_apply(crtc)) - goto fail; + if (!sna_crtc_set_mode_major(crtc, + &crtc->mode, crtc->rotation, + crtc->x, crtc->y)) + sna_crtc_disable(crtc); } - sna_mode_update(sna); - kgem_bo_retire(&sna->kgem, bo); - - scrn->virtualX = width; - scrn->virtualY = height; - scrn->displayWidth = bo->pitch / sna->mode.cpp; - - sna->mode.fb_pixmap = sna->front->drawable.serialNumber; sna_redirect_screen_pixmap(scrn, old_front, sna->front); assert(scrn->pScreen->GetScreenPixmap(scrn->pScreen) == sna->front); assert(scrn->pScreen->GetWindowPixmap(scrn->pScreen->root) == sna->front); @@ -1843,20 +2060,14 @@ sna_crtc_resize(ScrnInfoPtr scrn, int width, int height) scrn->pScreen->DestroyPixmap(old_front); return TRUE; - -fail: - DBG(("%s: restoring original front pixmap and fb\n", __FUNCTION__)); - mode->fb_id = old_fb_id; - - if (sna->front) - scrn->pScreen->DestroyPixmap(sna->front); - sna->front = old_front; - return FALSE; } -static int do_page_flip(struct sna *sna, void *data, int ref_crtc_hw_id) +static int do_page_flip(struct sna *sna, struct kgem_bo *bo, + void *data, int ref_crtc_hw_id) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int width = sna->scrn->virtualX; + int height = sna->scrn->virtualY; int count = 0; int i; @@ -1871,36 +2082,40 @@ static int do_page_flip(struct sna *sna, void *data, int ref_crtc_hw_id) */ for (i = 0; i < config->num_crtc; i++) { struct sna_crtc *crtc = config->crtc[i]->driver_private; - uintptr_t evdata; + struct drm_mode_crtc_page_flip arg; - DBG(("%s: crtc %d active? %d\n",__FUNCTION__, i,crtc->active)); - if (!crtc->active) + DBG(("%s: crtc %d active? %d\n", + __FUNCTION__, i, crtc->bo != NULL)); + if (crtc->bo == NULL) continue; + arg.crtc_id = crtc->id; + arg.fb_id = get_fb(sna, bo, width, height); + if (arg.fb_id == 0) + goto disable; + /* Only the reference crtc will finally deliver its page flip * completion event. All other crtc's events will be discarded. */ - evdata = (uintptr_t)data; - evdata |= crtc->pipe == ref_crtc_hw_id; + arg.user_data = (uintptr_t)data; + arg.user_data |= crtc->pipe == ref_crtc_hw_id; + arg.flags = DRM_MODE_PAGE_FLIP_EVENT; + arg.reserved = 0; DBG(("%s: crtc %d [ref? %d] --> fb %d\n", __FUNCTION__, crtc->id, - crtc->pipe == ref_crtc_hw_id, - sna->mode.fb_id)); - if (drmModePageFlip(sna->kgem.fd, - crtc->id, - sna->mode.fb_id, - DRM_MODE_PAGE_FLIP_EVENT, - (void*)evdata)) { - int err = errno; + crtc->pipe == ref_crtc_hw_id, arg.fb_id)); + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { DBG(("%s: flip [fb=%d] on crtc %d [%d] failed - %d\n", - __FUNCTION__, sna->mode.fb_id, - i, crtc->id, err)); - xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, - "flip queue failed: %s\n", strerror(err)); + __FUNCTION__, arg.fb_id, i, crtc->id, errno)); +disable: + sna_crtc_disable(config->crtc[i]); continue; } + kgem_bo_destroy(&sna->kgem, crtc->bo); + crtc->bo = kgem_bo_reference(bo); + count++; } @@ -1914,23 +2129,9 @@ sna_page_flip(struct sna *sna, int ref_crtc_hw_id, uint32_t *old_fb) { - ScrnInfoPtr scrn = sna->scrn; - struct sna_mode *mode = &sna->mode; int count; - *old_fb = mode->fb_id; - - /* - * Create a new handle for the back buffer - */ - mode->fb_id = get_fb(sna, bo, scrn->virtualX, scrn->virtualY); - if (mode->fb_id == 0) { - mode->fb_id = *old_fb; - return 0; - } - - DBG(("%s: handle %d attached to fb %d\n", - __FUNCTION__, bo->handle, mode->fb_id)); + DBG(("%s: handle %d attached\n", __FUNCTION__, bo->handle)); kgem_submit(&sna->kgem); @@ -1943,10 +2144,8 @@ sna_page_flip(struct sna *sna, * Also, flips queued on disabled or incorrectly configured displays * may never complete; this is a configuration error. */ - count = do_page_flip(sna, data, ref_crtc_hw_id); + count = do_page_flip(sna, bo, data, ref_crtc_hw_id); DBG(("%s: page flipped %d crtcs\n", __FUNCTION__, count)); - if (count == 0) - mode->fb_id = *old_fb; return count; } @@ -1955,6 +2154,15 @@ static const xf86CrtcConfigFuncsRec sna_crtc_config_funcs = { sna_crtc_resize }; +static void set_size_range(struct sna *sna) +{ + /* We lie slightly as we expect no single monitor to exceed the + * crtc limits, so if the mode exceeds the scanout restrictions, + * we will quietly convert that to per-crtc pixmaps. + */ + xf86CrtcSetSizeRange(sna->scrn, 320, 200, INT16_MAX, INT16_MAX); +} + Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) { struct sna_mode *mode = &sna->mode; @@ -1965,21 +2173,19 @@ Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) xf86CrtcConfigInit(scrn, &sna_crtc_config_funcs); - mode->mode_res = drmModeGetResources(sna->kgem.fd); - if (!mode->mode_res) { + mode->kmode = drmModeGetResources(sna->kgem.fd); + if (!mode->kmode) { xf86DrvMsg(scrn->scrnIndex, X_ERROR, "failed to get resources: %s\n", strerror(errno)); return FALSE; } - xf86CrtcSetSizeRange(scrn, - 320, 200, - mode->mode_res->max_width, - mode->mode_res->max_height); - for (i = 0; i < mode->mode_res->count_crtcs; i++) + set_size_range(sna); + + for (i = 0; i < mode->kmode->count_crtcs; i++) sna_crtc_init(scrn, mode, i); - for (i = 0; i < mode->mode_res->count_connectors; i++) + for (i = 0; i < mode->kmode->count_connectors; i++) sna_output_init(scrn, mode, i); xf86InitialConfiguration(scrn, TRUE); @@ -1988,18 +2194,6 @@ Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) } void -sna_mode_remove_fb(struct sna *sna) -{ - struct sna_mode *mode = &sna->mode; - - DBG(("%s: deleting fb id %d for pixmap serial %d\n", - __FUNCTION__, mode->fb_id,mode->fb_pixmap)); - - mode->fb_id = 0; - mode->fb_pixmap = 0; -} - -void sna_mode_fini(struct sna *sna) { #if 0 @@ -2015,38 +2209,9 @@ sna_mode_fini(struct sna *sna) link)->output); } #endif - - sna_mode_remove_fb(sna); - - /* mode->shadow_fb_id should have been destroyed already */ -} - -static void sna_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box) -{ - if (crtc->enabled) { - crtc_box->x1 = crtc->x; - crtc_box->y1 = crtc->y; - - switch (crtc->rotation & 0xf) { - default: - assert(0); - case RR_Rotate_0: - case RR_Rotate_180: - crtc_box->x2 = crtc->x + crtc->mode.HDisplay; - crtc_box->y2 = crtc->y + crtc->mode.VDisplay; - break; - - case RR_Rotate_90: - case RR_Rotate_270: - crtc_box->x2 = crtc->x + crtc->mode.VDisplay; - crtc_box->y2 = crtc->y + crtc->mode.HDisplay; - break; - } - } else - crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0; } -static void sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b) +static bool sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b) { r->x1 = a->x1 > b->x1 ? a->x1 : b->x1; r->x2 = a->x2 < b->x2 ? a->x2 : b->x2; @@ -2057,8 +2222,7 @@ static void sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b) a->x1, a->y1, a->x2, a->y2, b->x1, b->y1, b->x2, b->y2, r->x1, r->y1, r->x2, r->y2)); - if (r->x1 >= r->x2 || r->y1 >= r->y2) - r->x1 = r->x2 = r->y1 = r->y2 = 0; + return r->x2 > r->x1 && r->y2 > r->y1; } static int sna_box_area(const BoxRec *box) @@ -2074,13 +2238,11 @@ static int sna_box_area(const BoxRec *box) xf86CrtcPtr sna_covering_crtc(ScrnInfoPtr scrn, const BoxRec *box, - xf86CrtcPtr desired, - BoxPtr crtc_box_ret) + xf86CrtcPtr desired) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); xf86CrtcPtr best_crtc; int best_coverage, c; - BoxRec best_crtc_box; /* If we do not own the VT, we do not own the CRTC either */ if (!scrn->vtSema) @@ -2091,51 +2253,46 @@ sna_covering_crtc(ScrnInfoPtr scrn, best_crtc = NULL; best_coverage = 0; - best_crtc_box.x1 = 0; - best_crtc_box.x2 = 0; - best_crtc_box.y1 = 0; - best_crtc_box.y2 = 0; for (c = 0; c < xf86_config->num_crtc; c++) { xf86CrtcPtr crtc = xf86_config->crtc[c]; - BoxRec crtc_box, cover_box; + BoxRec cover_box; int coverage; /* If the CRTC is off, treat it as not covering */ - if (!sna_crtc_on(crtc)) { + if (to_sna_crtc(crtc)->bo == NULL) { DBG(("%s: crtc %d off, skipping\n", __FUNCTION__, c)); continue; } - sna_crtc_box(crtc, &crtc_box); DBG(("%s: crtc %d: (%d, %d), (%d, %d)\n", __FUNCTION__, c, - crtc_box.x1, crtc_box.y1, - crtc_box.x2, crtc_box.y2)); + crtc->bounds.x1, crtc->bounds.y1, + crtc->bounds.x2, crtc->bounds.y2)); + + if (!sna_box_intersect(&cover_box, &crtc->bounds, box)) + continue; - sna_box_intersect(&cover_box, &crtc_box, box); DBG(("%s: box instersects (%d, %d), (%d, %d) of crtc %d\n", __FUNCTION__, cover_box.x1, cover_box.y1, cover_box.x2, cover_box.y2, c)); - coverage = sna_box_area(&cover_box); - DBG(("%s: box covers %d of crtc %d\n", - __FUNCTION__, coverage, c)); - if (coverage && crtc == desired) { + if (crtc == desired) { DBG(("%s: box is on desired crtc [%p]\n", __FUNCTION__, crtc)); - *crtc_box_ret = crtc_box; return crtc; } + + coverage = sna_box_area(&cover_box); + DBG(("%s: box covers %d of crtc %d\n", + __FUNCTION__, coverage, c)); if (coverage > best_coverage) { - best_crtc_box = crtc_box; best_crtc = crtc; best_coverage = coverage; } } DBG(("%s: best crtc = %p, coverage = %d\n", __FUNCTION__, best_crtc, best_coverage)); - *crtc_box_ret = best_crtc_box; return best_crtc; } @@ -2235,40 +2392,32 @@ sna_wait_for_scanline(struct sna *sna, xf86CrtcPtr crtc, const BoxRec *clip) { - pixman_box16_t box, crtc_box; Bool full_height; int y1, y2, pipe; assert(crtc); assert(sna_crtc_on(crtc)); - assert(pixmap_is_scanout(pixmap)); + assert(pixmap == sna->front); /* XXX WAIT_EVENT is still causing hangs on SNB */ if (sna->kgem.gen >= 60) return false; - sna_crtc_box(crtc, &crtc_box); - if (crtc->transform_in_use) { - box = *clip; - pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &box); - clip = &box; - } - /* * Make sure we don't wait for a scanline that will * never occur */ - y1 = clip->y1 - crtc_box.y1; + y1 = clip->y1 - crtc->bounds.y1; if (y1 < 0) y1 = 0; - y2 = clip->y2 - crtc_box.y1; - if (y2 > crtc_box.y2 - crtc_box.y1) - y2 = crtc_box.y2 - crtc_box.y1; + y2 = clip->y2 - crtc->bounds.y1; + if (y2 > crtc->bounds.y2 - crtc->bounds.y1) + y2 = crtc->bounds.y2 - crtc->bounds.y1; DBG(("%s: clipped range = %d, %d\n", __FUNCTION__, y1, y2)); if (y2 <= y1) return false; - full_height = y1 == 0 && y2 == crtc_box.y2 - crtc_box.y1; + full_height = y1 == 0 && y2 == crtc->bounds.y2 - crtc->bounds.y1; if (crtc->mode.Flags & V_INTERLACE) { /* DSL count field lines */ @@ -2298,10 +2447,396 @@ void sna_mode_update(struct sna *sna) /* Validate CRTC attachments */ for (i = 0; i < xf86_config->num_crtc; i++) { xf86CrtcPtr crtc = xf86_config->crtc[i]; + if (!crtc->active || !sna_crtc_is_bound(sna, crtc)) + sna_crtc_disable(crtc); + } +} + +static void +sna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + ScreenPtr screen = sna->scrn->pScreen; + PictFormatPtr format; + PicturePtr src, dst; + PixmapPtr pixmap; + BoxPtr b; + int n, error; + void *ptr; + + DBG(("%s: compositing transformed damage boxes\n", __FUNCTION__)); + + ptr = kgem_bo_map__gtt(&sna->kgem, sna_crtc->bo); + if (ptr == NULL) + return; + + pixmap = fbCreatePixmap(screen, 0, 0, sna->front->drawable.depth, 0); + if (pixmap == NullPixmap) + return; + + if (!screen->ModifyPixmapHeader(pixmap, + crtc->mode.HDisplay, + crtc->mode.VDisplay, + sna->front->drawable.depth, + sna->front->drawable.bitsPerPixel, + sna_crtc->bo->pitch, ptr)) + goto free_pixmap; + + error = sna_render_format_for_depth(sna->front->drawable.depth); + format = PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(error), error); + if (format == NULL) { + DBG(("%s: can't find format for depth=%d [%08x]\n", + __FUNCTION__, sna->front->drawable.depth, + (int)sna_render_format_for_depth(sna->front->drawable.depth))); + goto free_pixmap; + } + + src = CreatePicture(None, &sna->front->drawable, format, + 0, NULL, serverClient, &error); + if (!src) + goto free_pixmap; + + error = SetPictureTransform(src, &crtc->crtc_to_framebuffer); + if (error) + goto free_src; + + if (crtc->filter) + SetPicturePictFilter(src, crtc->filter, + crtc->params, crtc->nparams); + + dst = CreatePicture(None, &pixmap->drawable, format, + 0, NULL, serverClient, &error); + if (!dst) + goto free_src; + + kgem_bo_sync__gtt(&sna->kgem, sna_crtc->bo); + n = REGION_NUM_RECTS(region); + b = REGION_RECTS(region); + do { + BoxRec box; + + box = *b++; + box.x1 -= crtc->filter_width >> 1; + box.x2 += crtc->filter_width >> 1; + box.y1 -= crtc->filter_height >> 1; + box.y2 += crtc->filter_height >> 1; + pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, & box); + + DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n", + __FUNCTION__, + b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1, + box.x1, box.y1, box.x2, box.y2)); + + fbComposite(PictOpSrc, src, NULL, dst, + box.x1, box.y1, + 0, 0, + box.x1, box.y1, + box.x2 - box.x1, box.y2 - box.y1); + } while (--n); + + FreePicture(dst, None); +free_src: + FreePicture(src, None); +free_pixmap: + screen->DestroyPixmap(pixmap); +} + +static void +sna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + ScreenPtr screen = sna->scrn->pScreen; + struct sna_composite_op tmp; + PictFormatPtr format; + PicturePtr src, dst; + PixmapPtr pixmap; + BoxPtr b; + int n, error; + + DBG(("%s: compositing transformed damage boxes\n", __FUNCTION__)); + + pixmap = sna_pixmap_create_unattached(screen, + crtc->mode.HDisplay, + crtc->mode.VDisplay, + sna->front->drawable.depth); + if (pixmap == NullPixmap) + return; + + if (!sna_pixmap_attach_to_bo(pixmap, sna_crtc->bo)) + goto free_pixmap; + + error = sna_render_format_for_depth(sna->front->drawable.depth); + format = PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(error), error); + if (format == NULL) { + DBG(("%s: can't find format for depth=%d [%08x]\n", + __FUNCTION__, sna->front->drawable.depth, + (int)sna_render_format_for_depth(sna->front->drawable.depth))); + goto free_pixmap; + } + + src = CreatePicture(None, &sna->front->drawable, format, + 0, NULL, serverClient, &error); + if (!src) + goto free_pixmap; + + error = SetPictureTransform(src, &crtc->crtc_to_framebuffer); + if (error) + goto free_src; + + if (crtc->filter) + SetPicturePictFilter(src, crtc->filter, + crtc->params, crtc->nparams); + + dst = CreatePicture(None, &pixmap->drawable, format, + 0, NULL, serverClient, &error); + if (!dst) + goto free_src; + + if (!sna->render.composite(sna, + PictOpSrc, src, NULL, dst, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + memset(&tmp, 0, sizeof(tmp)))) { + DBG(("%s: unsupported operation!\n", __FUNCTION__)); + sna_crtc_redisplay__fallback(crtc, region); + goto free_dst; + } + + n = REGION_NUM_RECTS(region); + b = REGION_RECTS(region); + do { + BoxRec box; + + box = *b++; + box.x1 -= crtc->filter_width >> 1; + box.x2 += crtc->filter_width >> 1; + box.y1 -= crtc->filter_height >> 1; + box.y2 += crtc->filter_height >> 1; + pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, & box); + + DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n", + __FUNCTION__, + b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1, + box.x1, box.y1, box.x2, box.y2)); + + tmp.box(sna, &tmp, &box); + } while (--n); + tmp.done(sna, &tmp); + +free_dst: + FreePicture(dst, None); +free_src: + FreePicture(src, None); +free_pixmap: + screen->DestroyPixmap(pixmap); +} + +static void +sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + int16_t tx, ty; + + DBG(("%s: crtc %d [pipe=%d], damage (%d, %d), (%d, %d) x %d\n", + __FUNCTION__, sna_crtc->id, sna_crtc->pipe, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2, + REGION_NUM_RECTS(region))); + + assert(!wedged(sna)); + + if (crtc->filter == NULL && + sna_transform_is_integer_translation(&crtc->crtc_to_framebuffer, + &tx, &ty)) { + PixmapRec tmp; + + DBG(("%s: copy damage boxes\n", __FUNCTION__)); + + tmp.drawable.width = crtc->mode.HDisplay; + tmp.drawable.height = crtc->mode.VDisplay; + tmp.drawable.depth = sna->front->drawable.depth; + tmp.drawable.bitsPerPixel = sna->front->drawable.bitsPerPixel; + + /* XXX for tear-free we may want to try copying to a back + * and flipping. + */ + + if (sna->render.copy_boxes(sna, GXcopy, + sna->front, sna_pixmap_get_bo(sna->front), tx, ty, + &tmp, sna_crtc->bo, 0, 0, + REGION_RECTS(region), REGION_NUM_RECTS(region))) + return; + } + + sna_crtc_redisplay__composite(crtc, region); +} + +void sna_mode_redisplay(struct sna *sna) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + RegionPtr region; + int i; + + if (!sna->mode.shadow_damage) + return; + + DBG(("%s: posting shadow damage\n", __FUNCTION__)); + assert(sna->mode.shadow_active); + + region = DamageRegion(sna->mode.shadow_damage); + if (!RegionNotEmpty(region)) + return; + + if (!sna_pixmap_move_to_gpu(sna->front, MOVE_READ)) { + if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ)) + return; + + for (i = 0; i < config->num_crtc; i++) { + xf86CrtcPtr crtc = config->crtc[i]; + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + RegionRec damage; + + if (!sna_crtc->shadow) + continue; + + assert(crtc->enabled); + assert(crtc->transform_in_use); + + damage.extents = crtc->bounds; + damage.data = NULL; + RegionIntersect(&damage, &damage, region); + if (RegionNotEmpty(&damage)) + sna_crtc_redisplay__fallback(crtc, &damage); + RegionUninit(&damage); + } + + RegionEmpty(region); + return; + } + + for (i = 0; i < config->num_crtc; i++) { + xf86CrtcPtr crtc = config->crtc[i]; struct sna_crtc *sna_crtc = to_sna_crtc(crtc); - if (crtc->enabled) - sna_crtc->active = sna_crtc_is_bound(sna, crtc); - else - sna_crtc->active = false; + RegionRec damage; + + if (!sna_crtc->shadow || sna_crtc->bo == sna->mode.shadow) + continue; + + assert(crtc->enabled); + assert(crtc->transform_in_use); + + damage.extents = crtc->bounds; + damage.data = NULL; + RegionIntersect(&damage, &damage, region); + if (RegionNotEmpty(&damage)) + sna_crtc_redisplay(crtc, &damage); + RegionUninit(&damage); + } + + if (!sna->mode.shadow) { + kgem_submit(&sna->kgem); + RegionEmpty(region); + return; + } + + if (sna->mode.shadow_flip == 0) { + struct kgem_bo *new = sna_pixmap_get_bo(sna->front); + struct kgem_bo *old = sna->mode.shadow; + + DBG(("%s: flipping tear-free outputs\n", __FUNCTION__)); + kgem_bo_submit(&sna->kgem, new); + + for (i = 0; i < config->num_crtc; i++) { + struct sna_crtc *crtc = config->crtc[i]->driver_private; + struct drm_mode_crtc_page_flip arg; + + DBG(("%s: crtc %d active? %d\n", + __FUNCTION__, i, crtc->bo != NULL)); + if (crtc->bo != old) + continue; + + arg.crtc_id = crtc->id; + arg.fb_id = get_fb(sna, new, + sna->scrn->virtualX, + sna->scrn->virtualY); + if (arg.fb_id == 0) + goto disable; + + /* Only the reference crtc will finally deliver its page flip + * completion event. All other crtc's events will be discarded. + */ + arg.user_data = 0; + arg.flags = DRM_MODE_PAGE_FLIP_EVENT; + arg.reserved = 0; + + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { + DBG(("%s: flip [fb=%d] on crtc %d [%d] failed - %d\n", + __FUNCTION__, arg.fb_id, i, crtc->id, errno)); +disable: + sna_crtc_disable(config->crtc[i]); + continue; + } + + kgem_bo_destroy(&sna->kgem, old); + crtc->bo = kgem_bo_reference(new); + sna->mode.shadow_flip++; + } + + /* XXX only works if the kernel stalls fwrites to the current + * scanout whilst the flip is pending + */ + (void)sna->render.copy_boxes(sna, GXcopy, + sna->front, new, 0, 0, + sna->front, old, 0, 0, + REGION_RECTS(region), + REGION_NUM_RECTS(region)); + + sna_pixmap(sna->front)->gpu_bo = old; + sna->mode.shadow = new; + + new->flush = old->flush; + + RegionEmpty(region); + } +} + +void sna_mode_wakeup(struct sna *sna) +{ + char buffer[1024]; + int len, i; + + /* The DRM read semantics guarantees that we always get only + * complete events. + */ + len = read(sna->kgem.fd, buffer, sizeof (buffer)); + if (len < (int)sizeof(struct drm_event)) + return; + + DBG(("%s: len=%d\n", __FUNCTION__, len)); + + i = 0; + while (i < len) { + struct drm_event *e = (struct drm_event *)&buffer[i]; + switch (e->type) { + case DRM_EVENT_VBLANK: + sna_dri_vblank_handler(sna, (struct drm_event_vblank *)e); + break; + case DRM_EVENT_FLIP_COMPLETE: + if (((struct drm_event_vblank *)e)->user_data) + sna_dri_page_flip_handler(sna, (struct drm_event_vblank *)e); + else + sna->mode.shadow_flip--; + break; + default: + break; + } + i += e->length; } } diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c index 46a43e9d..7cf1d1c6 100644 --- a/src/sna/sna_dri.c +++ b/src/sna/sna_dri.c @@ -73,7 +73,6 @@ struct sna_dri_private { }; struct sna_dri_frame_event { - struct sna *sna; XID drawable_id; ClientPtr client; enum frame_event_type type; @@ -107,9 +106,9 @@ struct sna_dri_frame_event { static DevPrivateKeyRec sna_client_key; static inline struct sna_dri_frame_event * -to_frame_event(void *data) +to_frame_event(uintptr_t data) { - return (struct sna_dri_frame_event *)((uintptr_t)data & ~1); + return (struct sna_dri_frame_event *)(data & ~1); } static inline struct sna_dri_private * @@ -316,7 +315,7 @@ static void _sna_dri_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) if (buffer == NULL) return; - DBG(("%s: %p [handle=%d] -- refcnt=%d, pixmap=%d\n", + DBG(("%s: %p [handle=%d] -- refcnt=%d, pixmap=%ld\n", __FUNCTION__, buffer, private->bo->handle, private->refcnt, private->pixmap ? private->pixmap->drawable.serialNumber : 0)); @@ -458,12 +457,8 @@ sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region, return NULL; } - if (pixmap == sna->front && sync && - (sna->flags & SNA_NO_WAIT) == 0) { - BoxRec crtc_box; - - crtc = sna_covering_crtc(sna->scrn, ®ion->extents, - NULL, &crtc_box); + if (sync && sna_pixmap_is_scanout(sna, pixmap)) { + crtc = sna_covering_crtc(sna->scrn, ®ion->extents, NULL); if (crtc) flush = sna_wait_for_scanline(sna, pixmap, crtc, ®ion->extents); @@ -707,8 +702,8 @@ static int sna_dri_get_pipe(DrawablePtr pDraw) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pDraw->pScreen); - BoxRec box, crtcbox; xf86CrtcPtr crtc; + BoxRec box; int pipe; if (pDraw->type == DRAWABLE_PIXMAP) @@ -719,18 +714,15 @@ sna_dri_get_pipe(DrawablePtr pDraw) box.x2 = box.x1 + pDraw->width; box.y2 = box.y1 + pDraw->height; - crtc = sna_covering_crtc(pScrn, &box, NULL, &crtcbox); + crtc = sna_covering_crtc(pScrn, &box, NULL); /* Make sure the CRTC is valid and this is the real front buffer */ pipe = -1; - if (crtc != NULL && !crtc->rotatedData) + if (crtc != NULL) pipe = sna_crtc_to_pipe(crtc); - DBG(("%s(box=((%d, %d), (%d, %d)), crtcbox=((%d, %d), (%d, %d)), pipe=%d)\n", - __FUNCTION__, - box.x1, box.y1, box.x2, box.y2, - crtcbox.x1, crtcbox.y1, crtcbox.x2, crtcbox.y2, - pipe)); + DBG(("%s(box=((%d, %d), (%d, %d)), pipe=%d)\n", + __FUNCTION__, box.x1, box.y1, box.x2, box.y2, pipe)); return pipe; } @@ -882,7 +874,8 @@ sna_dri_frame_event_release_bo(struct kgem *kgem, struct kgem_bo *bo) } static void -sna_dri_frame_event_info_free(struct sna_dri_frame_event *info) +sna_dri_frame_event_info_free(struct sna *sna, + struct sna_dri_frame_event *info) { DBG(("%s: del[%p] (%p, %ld)\n", __FUNCTION__, info, info->client, (long)info->drawable_id)); @@ -890,23 +883,20 @@ sna_dri_frame_event_info_free(struct sna_dri_frame_event *info) list_del(&info->client_resource); list_del(&info->drawable_resource); - _sna_dri_destroy_buffer(info->sna, info->front); - _sna_dri_destroy_buffer(info->sna, info->back); + _sna_dri_destroy_buffer(sna, info->front); + _sna_dri_destroy_buffer(sna, info->back); if (info->old_front.bo) - sna_dri_frame_event_release_bo(&info->sna->kgem, - info->old_front.bo); + sna_dri_frame_event_release_bo(&sna->kgem, info->old_front.bo); if (info->next_front.bo) - sna_dri_frame_event_release_bo(&info->sna->kgem, - info->next_front.bo); + sna_dri_frame_event_release_bo(&sna->kgem, info->next_front.bo); if (info->cache.bo) - sna_dri_frame_event_release_bo(&info->sna->kgem, - info->cache.bo); + sna_dri_frame_event_release_bo(&sna->kgem, info->cache.bo); if (info->bo) - kgem_bo_destroy(&info->sna->kgem, info->bo); + kgem_bo_destroy(&sna->kgem, info->bo); free(info); } @@ -974,7 +964,7 @@ can_flip(struct sna * sna, return FALSE; } - if (sna->shadow) { + if (sna->mode.shadow_active) { DBG(("%s: no, shadow enabled\n", __FUNCTION__)); return FALSE; } @@ -1047,14 +1037,10 @@ inline static uint32_t pipe_select(int pipe) return 0; } -static void sna_dri_vblank_handle(int fd, - unsigned int frame, unsigned int tv_sec, - unsigned int tv_usec, - void *data) +void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event) { - struct sna_dri_frame_event *info = data; + struct sna_dri_frame_event *info = (void *)(uintptr_t)event->user_data; DrawablePtr draw; - struct sna *sna; int status; DBG(("%s(id=%d, type=%d)\n", __FUNCTION__, @@ -1069,8 +1055,6 @@ static void sna_dri_vblank_handle(int fd, if (status != Success) goto done; - sna = to_sna_from_drawable(draw); - switch (info->type) { case DRI2_FLIP: /* If we can still flip... */ @@ -1091,7 +1075,8 @@ static void sna_dri_vblank_handle(int fd, /* fall through to SwapComplete */ case DRI2_SWAP_THROTTLE: DBG(("%s: %d complete, frame=%d tv=%d.%06d\n", - __FUNCTION__, info->type, frame, tv_sec, tv_usec)); + __FUNCTION__, info->type, + event->sequence, event->tv_sec, event->tv_usec)); if (info->bo && kgem_bo_is_busy(info->bo)) { kgem_retire(&sna->kgem); @@ -1111,8 +1096,8 @@ static void sna_dri_vblank_handle(int fd, } DRI2SwapComplete(info->client, - draw, frame, - tv_sec, tv_usec, + draw, event->sequence, + event->tv_sec, event->tv_usec, DRI2_BLIT_COMPLETE, info->client ? info->event_complete : NULL, info->event_data); @@ -1121,7 +1106,9 @@ static void sna_dri_vblank_handle(int fd, case DRI2_WAITMSC: if (info->client) DRI2WaitMSCComplete(info->client, draw, - frame, tv_sec, tv_usec); + event->sequence, + event->tv_sec, + event->tv_usec); break; default: xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, @@ -1131,7 +1118,7 @@ static void sna_dri_vblank_handle(int fd, } done: - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); } static int @@ -1222,7 +1209,7 @@ static void sna_dri_flip_event(struct sna *sna, flip->event_data); } - sna_dri_frame_event_info_free(flip); + sna_dri_frame_event_info_free(sna, flip); break; case DRI2_FLIP_THROTTLE: @@ -1251,10 +1238,10 @@ static void sna_dri_flip_event(struct sna *sna, DRI2_EXCHANGE_COMPLETE, flip->client ? flip->event_complete : NULL, flip->event_data); - sna_dri_frame_event_info_free(flip); + sna_dri_frame_event_info_free(sna, flip); } } else - sna_dri_frame_event_info_free(flip); + sna_dri_frame_event_info_free(sna, flip); break; #if USE_ASYNC_SWAP && DRI2INFOREC_VERSION >= 7 @@ -1298,7 +1285,7 @@ finish_async_flip: DBG(("%s: async flip completed\n", __FUNCTION__)); sna->dri.flip_pending = NULL; - sna_dri_frame_event_info_free(flip); + sna_dri_frame_event_info_free(fsna, lip); } break; #endif @@ -1311,26 +1298,26 @@ finish_async_flip: } } -static void -sna_dri_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, - unsigned int tv_usec, void *data) +void +sna_dri_page_flip_handler(struct sna *sna, + struct drm_event_vblank *event) { - struct sna_dri_frame_event *info = to_frame_event(data); + struct sna_dri_frame_event *info = to_frame_event(event->user_data); DBG(("%s: pending flip_count=%d\n", __FUNCTION__, info->count)); /* Is this the event whose info shall be delivered to higher level? */ - if ((uintptr_t)data & 1) { + if (event->user_data & 1) { /* Yes: Cache msc, ust for later delivery. */ - info->fe_frame = frame; - info->fe_tv_sec = tv_sec; - info->fe_tv_usec = tv_usec; + info->fe_frame = event->sequence; + info->fe_tv_sec = event->tv_sec; + info->fe_tv_usec = event->tv_usec; } if (--info->count) return; - sna_dri_flip_event(info->sna, info); + sna_dri_flip_event(sna, info); } static int @@ -1387,7 +1374,6 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, info->type = type; - info->sna = sna; info->drawable_id = draw->id; info->client = client; info->event_complete = func; @@ -1407,7 +1393,7 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, if (!sna_dri_page_flip(sna, info)) { DBG(("%s: failed to queue page flip\n", __FUNCTION__)); - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); return FALSE; } @@ -1431,7 +1417,6 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, if (info == NULL) return FALSE; - info->sna = sna; info->drawable_id = draw->id; info->client = client; info->event_complete = func; @@ -1454,7 +1439,7 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe); vbl.request.sequence = 0; if (sna_wait_vblank(sna, &vbl)) { - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); return FALSE; } @@ -1509,7 +1494,7 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, vbl.request.sequence -= 1; vbl.request.signal = (unsigned long)info; if (sna_wait_vblank(sna, &vbl)) { - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); return FALSE; } @@ -1588,7 +1573,6 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, if (!info) goto blit_fallback; - info->sna = sna; info->drawable_id = draw->id; info->client = client; info->event_complete = func; @@ -1629,7 +1613,7 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, return TRUE; } - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); return TRUE; } @@ -1713,7 +1697,7 @@ blit_fallback: get_private(back)->bo, false); if (info) - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); *target_msc = 0; /* offscreen, so zero out target vblank count */ return TRUE; @@ -1812,7 +1796,7 @@ blit: sna_dri_reference_buffer(back); if (!sna_dri_page_flip(sna, info)) { - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); goto blit; } @@ -1952,7 +1936,6 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc, if (!info) goto out_complete; - info->sna = sna; info->drawable_id = draw->id; info->client = client; info->type = DRI2_WAITMSC; @@ -2009,7 +1992,7 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc, return TRUE; out_free_info: - sna_dri_frame_event_info_free(info); + sna_dri_frame_event_info_free(sna, info); out_complete: DRI2WaitMSCComplete(client, draw, target_msc, 0, 0); return TRUE; @@ -2093,20 +2076,6 @@ Bool sna_dri_open(struct sna *sna, ScreenPtr screen) return DRI2ScreenInit(screen, &info); } -void -sna_dri_wakeup(struct sna *sna) -{ - drmEventContext ctx; - - DBG(("%s\n", __FUNCTION__)); - - ctx.version = DRM_EVENT_CONTEXT_VERSION; - ctx.vblank_handler = sna_dri_vblank_handle; - ctx.page_flip_handler = sna_dri_page_flip_handler; - - drmHandleEvent(sna->kgem.fd, &ctx); -} - void sna_dri_close(struct sna *sna, ScreenPtr screen) { DBG(("%s()\n", __FUNCTION__)); diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c index a02ff766..3b3b93ff 100644 --- a/src/sna/sna_driver.c +++ b/src/sna/sna_driver.c @@ -47,6 +47,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #include <xf86cmap.h> #include <xf86drm.h> +#include <xf86RandR12.h> #include <micmap.h> #include <fb.h> @@ -492,8 +493,6 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags) if (!xf86SetDefaultVisual(scrn, -1)) return FALSE; - sna->mode.cpp = scrn->bitsPerPixel / 8; - if (!sna_get_early_options(scrn)) return FALSE; @@ -527,7 +526,10 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags) sna->flags |= SNA_NO_DELAYED_FLUSH; if (!xf86ReturnOptValBool(sna->Options, OPTION_SWAPBUFFERS_WAIT, TRUE)) sna->flags |= SNA_NO_WAIT; - if (!has_pageflipping(sna)) + if (has_pageflipping(sna)) { + if (xf86ReturnOptValBool(sna->Options, OPTION_TEAR_FREE, FALSE)) + sna->flags |= SNA_TEAR_FREE; + } else sna->flags |= SNA_NO_FLIP; xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Framebuffer %s\n", @@ -540,6 +542,8 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags) sna->flags & SNA_NO_THROTTLE ? "dis" : "en"); xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Delayed flush %sabled\n", sna->flags & SNA_NO_DELAYED_FLUSH ? "dis" : "en"); + xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "\"Tear free\" %sabled\n", + sna->flags & SNA_TEAR_FREE ? "en" : "dis"); if (!sna_mode_pre_init(scrn, sna)) { PreInitCleanup(scrn); @@ -608,7 +612,7 @@ sna_wakeup_handler(WAKEUPHANDLER_ARGS_DECL) sna_accel_wakeup_handler(sna, read_mask); if (FD_ISSET(sna->kgem.fd, (fd_set*)read_mask)) - sna_dri_wakeup(sna); + sna_mode_wakeup(sna); } #if HAVE_UDEV @@ -735,7 +739,6 @@ static void sna_leave_vt(VT_FUNC_ARGS_DECL) xf86RotateFreeShadow(scrn); xf86_hide_cursors(scrn); - sna_mode_remove_fb(sna); ret = drmDropMaster(sna->kgem.fd); if (ret) @@ -747,7 +750,7 @@ static void sna_leave_vt(VT_FUNC_ARGS_DECL) * check that the fd is readable before attempting to read the next * event from drm. */ -static Bool sna_dri_has_pending_events(struct sna *sna) +static Bool sna_mode_has_pending_events(struct sna *sna) { struct pollfd pfd; pfd.fd = sna->kgem.fd; @@ -767,8 +770,8 @@ static Bool sna_close_screen(CLOSE_SCREEN_ARGS_DECL) #endif /* drain the event queues */ - if (sna_dri_has_pending_events(sna)) - sna_dri_wakeup(sna); + if (sna_mode_has_pending_events(sna)) + sna_mode_wakeup(sna); if (scrn->vtSema == TRUE) sna_leave_vt(VT_FUNC_ARGS(0)); @@ -788,7 +791,6 @@ static Bool sna_close_screen(CLOSE_SCREEN_ARGS_DECL) sna->directRenderingOpen = FALSE; } - sna_mode_remove_fb(sna); if (sna->front) { screen->DestroyPixmap(sna->front); sna->front = NULL; @@ -799,6 +801,20 @@ static Bool sna_close_screen(CLOSE_SCREEN_ARGS_DECL) return TRUE; } +static void sna_mode_set(ScrnInfoPtr scrn) +{ + struct sna *sna = to_sna(scrn); + + DBG(("%s\n", __FUNCTION__)); + + if (sna->ModeSet) { + scrn->ModeSet = sna->ModeSet; + scrn->ModeSet(scrn); + scrn->ModeSet = sna_mode_set; + } + sna_mode_update(sna); +} + static Bool sna_register_all_privates(void) { @@ -917,9 +933,17 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL) screen->CloseScreen = sna_close_screen; screen->CreateScreenResources = sna_create_screen_resources; + sna->ModeSet = scrn->ModeSet; + scrn->ModeSet = sna_mode_set; + if (!xf86CrtcScreenInit(screen)) return FALSE; + xf86RandR12SetRotations(screen, + RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270 | + RR_Reflect_X | RR_Reflect_Y); + xf86RandR12SetTransformSupport(screen, TRUE); + if (!miCreateDefColormap(screen)) return FALSE; diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c index 6ddf6f36..a072994a 100644 --- a/src/sna/sna_render.c +++ b/src/sna/sna_render.c @@ -64,15 +64,15 @@ CARD32 sna_render_format_for_depth(int depth) { switch (depth) { - case 1: return PICT_a1; - case 4: return PICT_a4; - case 8: return PICT_a8; - case 15: return PICT_a1r5g5b5; - case 16: return PICT_r5g6b5; - case 30: return PICT_a2r10g10b10; + case 1: return PIXMAN_a1; + case 4: return PIXMAN_a4; + case 8: return PIXMAN_a8; + case 15: return PIXMAN_a1r5g5b5; + case 16: return PIXMAN_r5g6b5; + case 30: return PIXMAN_a2r10g10b10; default: assert(0); case 24: - case 32: return PICT_a8r8g8b8; + case 32: return PIXMAN_a8r8g8b8; } } diff --git a/src/sna/sna_video.c b/src/sna/sna_video.c index 08b848b3..6ad81c36 100644 --- a/src/sna/sna_video.c +++ b/src/sna/sna_video.c @@ -143,7 +143,6 @@ sna_video_clip_helper(ScrnInfoPtr scrn, Bool ret; RegionRec crtc_region_local; RegionPtr crtc_region = reg; - BoxRec crtc_box; INT32 x1, x2, y1, y2; xf86CrtcPtr crtc; @@ -161,11 +160,12 @@ sna_video_clip_helper(ScrnInfoPtr scrn, * For overlay video, compute the relevant CRTC and * clip video to that */ - crtc = sna_covering_crtc(scrn, dst, video->desired_crtc, &crtc_box); + crtc = sna_covering_crtc(scrn, dst, video->desired_crtc); /* For textured video, we don't actually want to clip at all. */ if (crtc && !video->textured) { - RegionInit(&crtc_region_local, &crtc_box, 0); + crtc_region_local.extents = crtc->bounds; + crtc_region_local.data = NULL; crtc_region = &crtc_region_local; RegionIntersect(crtc_region, crtc_region, reg); } diff --git a/src/sna/sna_video_textured.c b/src/sna/sna_video_textured.c index 33f3f712..1b3b3af2 100644 --- a/src/sna/sna_video_textured.c +++ b/src/sna/sna_video_textured.c @@ -281,7 +281,7 @@ sna_video_textured_put_image(ScrnInfoPtr scrn, } if (crtc && video->SyncToVblank != 0 && - pixmap == sna->front && !sna->shadow) + sna_pixmap_is_scanout(sna, pixmap)) flush = sna_wait_for_scanline(sna, pixmap, crtc, &clip->extents); |