diff options
-rw-r--r-- | src/sna/Makefile.am | 6 | ||||
-rw-r--r-- | src/sna/sna.h | 23 | ||||
-rw-r--r-- | src/sna/sna_display.c | 12 | ||||
-rw-r--r-- | src/sna/sna_driver.c | 18 | ||||
-rw-r--r-- | src/sna/sna_present.c | 472 | ||||
-rw-r--r-- | test/dri3.c | 2 |
6 files changed, 530 insertions, 3 deletions
diff --git a/src/sna/Makefile.am b/src/sna/Makefile.am index aa3f6a58..a299ea2e 100644 --- a/src/sna/Makefile.am +++ b/src/sna/Makefile.am @@ -120,6 +120,12 @@ libsna_la_SOURCES += sna_dri3.c libsna_la_LIBADD += $(DRI3_LIBS) endif +if PRESENT +AM_CFLAGS += $(PRESENT_CFLAGS) +libsna_la_SOURCES += sna_present.c +libsna_la_LIBADD += $(PRESENT_LIBS) +endif + if XVMC libsna_la_SOURCES += \ sna_video_hwmc.h \ diff --git a/src/sna/sna.h b/src/sna/sna.h index 5114927d..b9a40db1 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -340,6 +340,13 @@ struct sna { #endif } dri3; + struct sna_present { + bool available; + bool open; +#if HAVE_PRESENT +#endif + } present; + struct sna_xv { XvAdaptorPtr adaptors; int num_adaptors; @@ -538,6 +545,22 @@ static inline bool sna_dri3_open(struct sna *sna, ScreenPtr pScreen) { return fa static inline void sna_dri3_close(struct sna *sna, ScreenPtr pScreen) { } #endif +#if HAVE_PRESENT +bool sna_present_open(struct sna *sna, ScreenPtr pScreen); +void sna_present_update(struct sna *sna); +void sna_present_close(struct sna *sna, ScreenPtr pScreen); +void sna_present_flip_handler(struct sna *sna, + struct drm_event_vblank *event); +void sna_present_vblank_handler(struct sna *sna, + struct drm_event_vblank *event); +#else +static inline bool sna_present_open(struct sna *sna, ScreenPtr pScreen) { return false; } +static inline void sna_present_update(struct sna *sna) { } +static inline void sna_present_close(struct sna *sna, ScreenPtr pScreen) { } +static inline void sna_present_flip_handler(struct sna *sna, struct drm_event_vblank *event) { } +static inline void sna_present_vblank_handler(struct sna *sna, struct drm_event_vblank *event) { } +#endif + extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation); extern int sna_crtc_to_pipe(xf86CrtcPtr crtc); extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc); diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c index 2b7e4731..62dce47f 100644 --- a/src/sna/sna_display.c +++ b/src/sna/sna_display.c @@ -4602,6 +4602,8 @@ sna_crtc_config_notify(ScreenPtr screen) sna_mode_update(sna); sna_cursors_reload(sna); + + sna_present_update(sna); } #if HAS_PIXMAP_SHARING @@ -5778,11 +5780,17 @@ void sna_mode_wakeup(struct sna *sna) struct drm_event *e = (struct drm_event *)&buffer[i]; switch (e->type) { case DRM_EVENT_VBLANK: - sna_dri2_vblank_handler(sna, (struct drm_event_vblank *)e); + if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2) + sna_present_vblank_handler(sna, (struct drm_event_vblank *)e); + else + sna_dri2_vblank_handler(sna, (struct drm_event_vblank *)e); break; case DRM_EVENT_FLIP_COMPLETE: if (((struct drm_event_vblank *)e)->user_data) { - sna_dri2_page_flip_handler(sna, (struct drm_event_vblank *)e); + if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2) + sna_present_flip_handler(sna, (struct drm_event_vblank *)e); + else + sna_dri2_page_flip_handler(sna, (struct drm_event_vblank *)e); } else { if (!--sna->mode.shadow_flip) sna_mode_redisplay(sna); diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c index 97f27e27..7a58a86d 100644 --- a/src/sna/sna_driver.c +++ b/src/sna/sna_driver.c @@ -646,6 +646,13 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags) setup_dri(sna); + sna->present.available = false; + if (xf86ReturnOptValBool(sna->Options, OPTION_PRESENT, TRUE)) { +#if HAVE_PRESENT + sna->present.available = !!xf86LoadSubModule(scrn, "present"); +#endif + } + sna_acpi_init(sna); return TRUE; @@ -867,6 +874,11 @@ static Bool sna_early_close_screen(CLOSE_SCREEN_ARGS_DECL) sna_uevent_fini(scrn); sna_mode_close(sna); + if (sna->present.open) { + sna_present_close(sna, screen); + sna->present.open = false; + } + if (sna->dri3.open) { sna_dri3_close(sna, screen); sna->dri3.open = false; @@ -1098,6 +1110,12 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL) sna_video_init(sna, screen); sna_dri_init(sna, screen); + if (sna->present.available) + sna->present.open = sna_present_open(sna, screen); + if (sna->present.open) + xf86DrvMsg(sna->scrn->scrnIndex, X_INFO, + "hardware support for Present enabled\n"); + if (serverGeneration == 1) xf86ShowUnusedOptions(scrn->scrnIndex, scrn->options); diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c new file mode 100644 index 00000000..cacef789 --- /dev/null +++ b/src/sna/sna_present.c @@ -0,0 +1,472 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <xf86drm.h> + +#include "sna.h" + +#include <xf86.h> +#include <present.h> + +static present_screen_info_rec present_info; + +struct sna_present_event { + uint64_t event_id; + unsigned int count; + xf86CrtcPtr crtc; + uint64_t msc; + uint64_t ust; +}; + +static inline struct sna_present_event * +to_present_event(uintptr_t data) +{ + return (struct sna_present_event *)(data & ~3); +} + +#define MARK_PRESENT(x) ((void *)((uintptr_t)(x) | 2)) + +static int pipe_from_crtc(RRCrtcPtr crtc) +{ + return crtc ? sna_crtc_to_pipe(crtc->devPrivate) : -1; +} + +static uint32_t pipe_select(int pipe) +{ + if (pipe > 1) + return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; + else if (pipe > 0) + return DRM_VBLANK_SECONDARY; + else + return 0; +} + +static inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, int pipe) +{ + DBG(("%s(pipe=%d, waiting until seq=%u%s)\n", + __FUNCTION__, pipe, vbl->request.sequence, + vbl->request.type & DRM_VBLANK_RELATIVE ? " [relative]" : "")); + vbl->request.type |= pipe_select(pipe); + return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl); +} + +static RRCrtcPtr +sna_present_get_crtc(WindowPtr window) +{ + struct sna *sna = to_sna_from_drawable(&window->drawable); + BoxRec box; + xf86CrtcPtr crtc; + + DBG(("%s\n", __FUNCTION__)); + + box.x1 = window->drawable.x; + box.y1 = window->drawable.y; + box.x2 = box.x1 + window->drawable.width; + box.y2 = box.y1 + window->drawable.height; + + crtc = sna_covering_crtc(sna, &box, NULL); + if (crtc) + return crtc->randr_crtc; + + return NULL; +} + +static int +sna_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc) +{ + struct sna *sna = to_sna_from_screen(crtc->pScreen); + int pipe = pipe_from_crtc(crtc); + union drm_wait_vblank vbl; + + DBG(("%s(pipe=%d)\n", __FUNCTION__, pipe)); + + VG_CLEAR(vbl); + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.sequence = 0; + if (sna_wait_vblank(sna, &vbl, pipe) == 0) { + *ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec); + *msc = sna_crtc_record_vblank(crtc->devPrivate, &vbl); + } else { + const struct ust_msc *swap = sna_crtc_last_swap(crtc->devPrivate); + *ust = ust64(swap->tv_sec, swap->tv_usec); + *msc = swap->msc; + } + + DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld\n", __FUNCTION__, pipe, + (int)(*ust / 1000000), (int)(*ust % 1000000), + (long long)*msc)); + + return Success; +} + +void +sna_present_vblank_handler(struct sna *sna, struct drm_event_vblank *event) +{ + struct sna_present_event *info = to_present_event(event->user_data); + + DBG(("%s: pipe=%d event=%lld, tv=%d.%06d msc=%d\n", __FUNCTION__, + sna_crtc_to_pipe(info->crtc), (long long)info->event_id, + event->tv_sec, event->tv_usec, event->sequence)); + present_event_notify(info->event_id, + ust64(event->tv_sec, event->tv_usec), + sna_crtc_record_event(info->crtc, event)); + free(info); +} + +static int +sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) +{ + struct sna *sna = to_sna_from_screen(crtc->pScreen); + struct sna_present_event *event; + union drm_wait_vblank vbl; + + DBG(("%s(pipe=%d, event=%lld, msc=%lld)\n", + __FUNCTION__, pipe_from_crtc(crtc), + (long long)event_id, (long long)msc)); + + event = malloc(sizeof(struct sna_present_event)); + if (event == NULL) + return BadAlloc; + + event->event_id = event_id; + event->crtc = crtc->devPrivate; + + VG_CLEAR(vbl); + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + vbl.request.sequence = msc; + vbl.request.signal = (uintptr_t)MARK_PRESENT(event); + if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(event->crtc))) { + DBG(("%s: vblank enqueue failed\n", __FUNCTION__)); + free(event); + return BadMatch; + } + + return Success; +} + +static void +sna_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) +{ + DBG(("%s(pipe=%d, event=%lld, msc=%lld)\n", + __FUNCTION__, pipe_from_crtc(crtc), + (long long)event_id, (long long)msc)); +} + +static void +sna_present_flush(WindowPtr window) +{ + PixmapPtr pixmap = get_window_pixmap(window); + struct sna_pixmap *priv; + + DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber)); + + priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_FORCE); + if (priv && priv->gpu_bo) + kgem_scanout_flush(&to_sna_from_pixmap(pixmap)->kgem, priv->gpu_bo); +} + +static bool +check_flip__crtc(struct sna *sna, + RRCrtcPtr crtc) +{ + if (!sna->scrn->vtSema) { + DBG(("%s: not master\n", __FUNCTION__)); + return false; + } + + if (!sna_crtc_is_on(crtc->devPrivate)) { + DBG(("%s: CRTC off\n", __FUNCTION__)); + return false; + } + + if (sna->mode.shadow_active) { + DBG(("%s: shadow buffer active\n", __FUNCTION__)); + return false; + } + + return true; +} + +static Bool +sna_present_check_flip(RRCrtcPtr crtc, + WindowPtr window, + PixmapPtr pixmap, + Bool sync_flip) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *flip; + + DBG(("%s(pipe=%d, pixmap=%ld, sync_flip=%d)\n", + __FUNCTION__, + pipe_from_crtc(crtc), + pixmap->drawable.serialNumber, + sync_flip)); + + if (sna->flags & SNA_NO_FLIP) { + DBG(("%s: flips not suported\n", __FUNCTION__)); + return FALSE; + } + + if (sync_flip) { + if ((sna->flags & SNA_HAS_FLIP) == 0) { + DBG(("%s: async flips not suported\n", __FUNCTION__)); + return FALSE; + } + } else { + if ((sna->flags & SNA_HAS_ASYNC_FLIP) == 0) { + DBG(("%s: async flips not suported\n", __FUNCTION__)); + return FALSE; + } + } + + if (!check_flip__crtc(sna, crtc)) { + DBG(("%s: flip invalid for CRTC\n", __FUNCTION__)); + return FALSE; + } + + flip = sna_pixmap(pixmap); + if (flip == NULL) { + DBG(("%s: unattached pixmap\n", __FUNCTION__)); + return FALSE; + } + + if (flip->cpu_bo && IS_STATIC_PTR(flip->ptr)) { + DBG(("%s: SHM pixmap\n", __FUNCTION__)); + return FALSE; + } + + return TRUE; +} + +static uint64_t gettime_ust64(void) +{ + struct timespec tv; + + if (clock_gettime(CLOCK_MONOTONIC, &tv)) + return 0; + + return ust64(tv.tv_sec, tv.tv_nsec / 1000); +} + +static Bool +page_flip__async(RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + struct kgem_bo *bo) +{ + DBG(("%s(pipe=%d, event=%lld, handle=%d)\n", + __FUNCTION__, + pipe_from_crtc(crtc), + (long long)event_id, + bo->handle)); + + if (!sna_page_flip(to_sna_from_screen(crtc->pScreen), bo, NULL, -1)) { + DBG(("%s: async pageflip failed\n", __FUNCTION__)); + present_info.capabilities &= ~PresentCapabilityAsync; + return FALSE; + } + + present_event_notify(event_id, gettime_ust64(), target_msc); + return TRUE; +} + +void +sna_present_flip_handler(struct sna *sna, struct drm_event_vblank *event) +{ + struct sna_present_event *info = to_present_event(event->user_data); + + DBG(("%s(count=%d, ref-pipe?=%d)\n", __FUNCTION__, + info->count, event->user_data & 1)); + + if (event->user_data & 1) { + info->msc = sna_crtc_record_event(info->crtc, event); + info->ust = ust64(event->tv_sec, event->tv_usec); + } + + if (--info->count) + return; + + DBG(("%s: pipe=%d, tv=%d.%06d msc %lld, complete\n", __FUNCTION__, + info->crtc ? sna_crtc_to_pipe(info->crtc) : -1, + info->crtc ? (int)(info->ust / 1000000) : event->tv_sec, + info->crtc ? (int)(info->ust % 1000000) : event->tv_usec, + info->crtc ? (long long)info->msc : (long long)event->sequence)); + present_event_notify(info->event_id, info->ust, info->msc); + free(info); +} + +static Bool +page_flip(ScreenPtr screen, + RRCrtcPtr crtc, + uint64_t event_id, + struct kgem_bo *bo) +{ + struct sna *sna = to_sna_from_screen(screen); + struct sna_present_event *event; + + DBG(("%s(pipe=%d, event=%lld, handle=%d)\n", + __FUNCTION__, + pipe_from_crtc(crtc), + (long long)event_id, + bo->handle)); + + event = malloc(sizeof(struct sna_present_event)); + if (event == NULL) + return FALSE; + + event->event_id = event_id; + event->crtc = crtc ? crtc->devPrivate : NULL; + event->count = sna_page_flip(sna, bo, + MARK_PRESENT(event), + crtc ? sna_crtc_to_pipe(event->crtc) : -1); + if (event->count == 0) { + DBG(("%s: pageflip failed\n", __FUNCTION__)); + free(event); + return FALSE; + } + + return TRUE; +} + +static struct kgem_bo * +get_flip_bo(PixmapPtr pixmap) +{ + struct sna_pixmap *priv; + + DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber)); + + priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | __MOVE_FORCE); + if (priv == NULL) { + DBG(("%s: cannot force pixmap to the GPU\n", __FUNCTION__)); + return NULL; + } + + if (priv->gpu_bo->tiling == I915_TILING_Y && + !sna_pixmap_change_tiling(pixmap, I915_TILING_X)) { + DBG(("%s: bad tiling, cannot convert\n", __FUNCTION__)); + return NULL; + } + + priv->pinned |= PIN_SCANOUT; + return priv->gpu_bo; +} + +static Bool +sna_present_flip(RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + PixmapPtr pixmap, + Bool sync_flip) +{ + struct kgem_bo *bo; + + DBG(("%s(pipe=%d, event=%lld, msc=%lld, pixmap=%ld, sync?=%d)\n", + __FUNCTION__, + pipe_from_crtc(crtc), + (long long)event_id, + (long long)target_msc, + pixmap->drawable.serialNumber, sync_flip)); + + if (!check_flip__crtc(to_sna_from_pixmap(pixmap), crtc)) { + DBG(("%s: flip invalid for CRTC\n", __FUNCTION__)); + return FALSE; + } + + bo = get_flip_bo(pixmap); + if (bo == NULL) { + DBG(("%s: flip invalid bo\n", __FUNCTION__)); + return FALSE; + } + + if (sync_flip) + return page_flip(crtc->pScreen, crtc, event_id, bo); + else + return page_flip__async(crtc, event_id, target_msc, bo); +} + +static void +sna_present_unflip(ScreenPtr screen, uint64_t event_id) +{ + struct kgem_bo *bo; + + DBG(("%s(event=%lld)\n", __FUNCTION__, (long long)event_id)); + bo = get_flip_bo(screen->GetScreenPixmap(screen)); + if (bo == NULL || !page_flip(screen, NULL, event_id, bo)) { + struct sna *sna = to_sna_from_screen(screen); + const struct ust_msc *swap; + DBG(("%s: failed, trying to restore original mode\n", __FUNCTION__)); + xf86SetDesiredModes(sna->scrn); + swap = sna_crtc_last_swap(sna_mode_first_crtc(sna)); + present_event_notify(event_id, + ust64(swap->tv_sec, swap->tv_usec), + swap->msc); + } +} + +static present_screen_info_rec present_info = { + .version = PRESENT_SCREEN_INFO_VERSION, + + .get_crtc = sna_present_get_crtc, + .get_ust_msc = sna_present_get_ust_msc, + .queue_vblank = sna_present_queue_vblank, + .abort_vblank = sna_present_abort_vblank, + .flush = sna_present_flush, + + .capabilities = PresentCapabilityNone, + .check_flip = sna_present_check_flip, + .flip = sna_present_flip, + .unflip = sna_present_unflip, +}; + +bool sna_present_open(struct sna *sna, ScreenPtr screen) +{ + if (sna->mode.num_real_crtc == 0) + return false; + + sna_present_update(sna); + + return present_screen_init(screen, &present_info); +} + +void sna_present_update(struct sna *sna) +{ + if (sna->flags & SNA_HAS_ASYNC_FLIP) + present_info.capabilities |= PresentCapabilityAsync; + else + present_info.capabilities &= ~PresentCapabilityAsync; + + DBG(("%s: has_async_flip? %d\n", __FUNCTION__, + !!(present_info.capabilities & PresentCapabilityAsync))); +} + +void sna_present_close(struct sna *sna, ScreenPtr screen) +{ + DBG(("%s()\n", __FUNCTION__)); +} diff --git a/test/dri3.c b/test/dri3.c index b5eb4baf..45f3285c 100644 --- a/test/dri3.c +++ b/test/dri3.c @@ -43,7 +43,7 @@ Pixmap dri3_create_pixmap(Display *dpy, xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd); return pixmap; } - return NullPixmap; + return 0; } int dri3_create_fd(Display *dpy, |