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; - if (depth < 8) { + DBG(("%s: %dx%d @ %d\n", __FUNCTION__, width, height, depth)); + + 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 +#include #include #include #include @@ -58,6 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "../compat-api.h" #define _XF86DRI_SERVER_ +#include #include #include @@ -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<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 #include /* for xf86InterpretEDID */ +#include +#include + #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,6 +749,225 @@ static void update_flush_interval(struct sna *sna) max_vrefresh, sna->vblank_interval)); } +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 kgem_bo *bo; + + if (use_shadow(sna, crtc)) { + if (!sna_crtc_enable_shadow(sna, sna_crtc)) + return NULL; + + DBG(("%s: attaching to per-crtc pixmap %dx%d\n", + __FUNCTION__, crtc->mode.HDisplay, crtc->mode.VDisplay)); + + 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); + } +} + +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; + + 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; + + free(crtc->params); + crtc->params = params; + crtc->nparams = nparams; + + crtc->filter = filter; + if (filter) { + crtc->filter_width = filter->width; + crtc->filter_height = filter->height; + } else { + crtc->filter_width = 0; + crtc->filter_height = 0; + } + + 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; + + 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) @@ -682,63 +975,41 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, 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 *saved_bo, *bo; + struct drm_mode_modeinfo saved_kmode; - DBG(("%s(rotation=%d, x=%d, y=%d, mode=%dx%d@%d)\n", - __FUNCTION__, rotation, x, y, + 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)); - DBG(("%s: current fb pixmap = %d, front is %lu\n", - __FUNCTION__, - sna_mode->fb_pixmap, - sna->front->drawable.serialNumber)); + assert(mode->HDisplay <= sna->mode.kmode->max_width && + mode->VDisplay <= sna->mode.kmode->max_height); - if (sna_mode->fb_pixmap != sna->front->drawable.serialNumber) { - kgem_submit(&sna->kgem); - sna_mode_remove_fb(sna); - } - - if (sna_mode->fb_id == 0) { - struct kgem_bo *bo = sna_pixmap_pin(sna->front); - if (!bo) - return 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; - - DBG(("%s: handle %d attached to fb %d\n", - __FUNCTION__, bo->handle, sna_mode->fb_id)); - - sna_mode->fb_pixmap = sna->front->drawable.serialNumber; - } - - saved_mode = crtc->mode; - saved_x = crtc->x; - saved_y = crtc->y; - saved_rotation = crtc->rotation; - - crtc->mode = *mode; - crtc->x = x; - crtc->y = y; - crtc->rotation = rotation; + /* 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; + if (!new_front) + return FALSE; - bo = sna_pixmap_pin(sna->front); - if (!bo) - 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); - assert(bo->delta == 0); - - mode->fb_id = get_fb(sna, bo, width, height); - if (mode->fb_id == 0) - goto fail; - - 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); @@ -1987,18 +2193,6 @@ Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) return TRUE; } -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) { @@ -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)); + if (crtc == desired) { + DBG(("%s: box is on desired crtc [%p]\n", + __FUNCTION__, crtc)); + return crtc; + } + coverage = sna_box_area(&cover_box); DBG(("%s: box covers %d of crtc %d\n", __FUNCTION__, coverage, c)); - if (coverage && crtc == desired) { - DBG(("%s: box is on desired crtc [%p]\n", - __FUNCTION__, crtc)); - *crtc_box_ret = crtc_box; - return crtc; - } 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]; - 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; + 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); + 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 #include +#include #include #include @@ -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);