diff --git a/Makefile.am b/Makefile.am index 7a8fc5ba7..28266c5ae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,10 @@ if DRI3 DRI3_DIR=dri3 endif +if PRESENT +PRESENT_DIR=present +endif + SUBDIRS = \ doc \ man \ @@ -42,6 +46,7 @@ SUBDIRS = \ damageext \ $(COMPOSITE_DIR) \ $(GLX_DIR) \ + $(PRESENT_DIR) \ $(DRI3_DIR) \ exa \ config \ diff --git a/configure.ac b/configure.ac index 546790d4c..7432912f9 100644 --- a/configure.ac +++ b/configure.ac @@ -614,6 +614,7 @@ AC_ARG_ENABLE(glx, AS_HELP_STRING([--disable-glx], [Build GLX extensi AC_ARG_ENABLE(dri, AS_HELP_STRING([--enable-dri], [Build DRI extension (default: auto)]), [DRI=$enableval]) AC_ARG_ENABLE(dri2, AS_HELP_STRING([--enable-dri2], [Build DRI2 extension (default: auto)]), [DRI2=$enableval], [DRI2=auto]) AC_ARG_ENABLE(dri3, AS_HELP_STRING([--enable-dri3], [Build DRI3 extension (default: auto)]), [DRI3=$enableval], [DRI3=auto]) +AC_ARG_ENABLE(present, AS_HELP_STRING([--disable-present], [Build Present extension (default: enabled)]), [PRESENT=$enableval], [PRESENT=yes]) AC_ARG_ENABLE(xinerama, AS_HELP_STRING([--disable-xinerama], [Build Xinerama extension (default: enabled)]), [XINERAMA=$enableval], [XINERAMA=yes]) AC_ARG_ENABLE(xf86vidmode, AS_HELP_STRING([--disable-xf86vidmode], [Build XF86VidMode extension (default: auto)]), [XF86VIDMODE=$enableval], [XF86VIDMODE=auto]) AC_ARG_ENABLE(xace, AS_HELP_STRING([--disable-xace], [Build X-ACE extension (default: enabled)]), [XACE=$enableval], [XACE=yes]) @@ -807,6 +808,7 @@ DAMAGEPROTO="damageproto >= 1.1" XCMISCPROTO="xcmiscproto >= 1.2.0" BIGREQSPROTO="bigreqsproto >= 1.1.0" XTRANS="xtrans >= 1.2.2" +PRESENTPROTO="presentproto >= 1.0" dnl List of libraries that require a specific version LIBAPPLEWM="applewm >= 1.4" @@ -1160,6 +1162,14 @@ if test "x$DRI2" = xyes; then fi AM_CONDITIONAL(DRI2_AIGLX, test "x$DRI2_AIGLX" = xyes) +AM_CONDITIONAL(PRESENT, [test "x$PRESENT" = xyes]) +if test "x$PRESENT" = xyes; then + AC_DEFINE(PRESENT, 1, [Support Present extension]) + REQUIRED_MODULES="$REQUIRED_MODULES $PRESENTPROTO" + SDK_REQUIRED_MODULES="$SDK_REQUIRED_MODULES $PRESENTPROTO" + PRESENT_INC='-I$(top_srcdir)/present' + PRESENT_LIB='$(top_builddir)/present/libpresent.la' +fi AM_CONDITIONAL(XINERAMA, [test "x$XINERAMA" = xyes]) if test "x$XINERAMA" = xyes; then @@ -1584,7 +1594,7 @@ AC_EGREP_CPP([I_AM_SVR4],[ AC_DEFINE([SVR4],1,[Define to 1 on systems derived from System V Release 4]) AC_MSG_RESULT([yes])], AC_MSG_RESULT([no])) -XSERVER_CFLAGS="$XSERVER_CFLAGS $CORE_INCS $XEXT_INC $COMPOSITE_INC $DAMAGE_INC $FIXES_INC $XI_INC $MI_INC $MIEXT_SYNC_INC $MIEXT_SHADOW_INC $MIEXT_LAYER_INC $MIEXT_DAMAGE_INC $RENDER_INC $RANDR_INC $FB_INC $DBE_INC" +XSERVER_CFLAGS="$XSERVER_CFLAGS $CORE_INCS $XEXT_INC $COMPOSITE_INC $DAMAGE_INC $FIXES_INC $XI_INC $MI_INC $MIEXT_SYNC_INC $MIEXT_SHADOW_INC $MIEXT_LAYER_INC $MIEXT_DAMAGE_INC $RENDER_INC $RANDR_INC $FB_INC $DBE_INC $PRESENT_INC" dnl --------------------------------------------------------------------------- dnl DDX section. @@ -1597,7 +1607,7 @@ AC_MSG_RESULT([$XVFB]) AM_CONDITIONAL(XVFB, [test "x$XVFB" = xyes]) if test "x$XVFB" = xyes; then - XVFB_LIBS="$FB_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB" + XVFB_LIBS="$FB_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB" XVFB_SYS_LIBS="$XVFBMODULES_LIBS $GLX_SYS_LIBS" AC_SUBST([XVFB_LIBS]) AC_SUBST([XVFB_SYS_LIBS]) @@ -1618,7 +1628,7 @@ if test "x$XNEST" = xyes; then if test "x$have_xnest" = xno; then AC_MSG_ERROR([Xnest build explicitly requested, but required modules not found.]) fi - XNEST_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB $DIX_LIB $OS_LIB" + XNEST_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB $DIX_LIB $OS_LIB" XNEST_SYS_LIBS="$XNESTMODULES_LIBS $GLX_SYS_LIBS" AC_SUBST([XNEST_LIBS]) AC_SUBST([XNEST_SYS_LIBS]) @@ -1643,7 +1653,7 @@ if test "x$XORG" = xyes; then XORG_OSINCS='-I$(top_srcdir)/hw/xfree86/os-support -I$(top_srcdir)/hw/xfree86/os-support/bus -I$(top_srcdir)/os' XORG_INCS="$XORG_DDXINCS $XORG_OSINCS" XORG_CFLAGS="$XORGSERVER_CFLAGS -DHAVE_XORG_CONFIG_H" - XORG_LIBS="$COMPOSITE_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $XI_LIB $XKB_LIB" + XORG_LIBS="$COMPOSITE_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $XI_LIB $XKB_LIB" dnl ================================================================== dnl symbol visibility @@ -2056,7 +2066,7 @@ if test "x$DMX" = xyes; then fi DMX_INCLUDES="$XEXT_INC $RENDER_INC $RECORD_INC" XDMX_CFLAGS="$DMXMODULES_CFLAGS" - XDMX_LIBS="$FB_LIB $MI_LIB $XEXT_LIB $RENDER_LIB $RECORD_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_SHADOW_LIB $MIEXT_DAMAGE_LIB $COMPOSITE_LIB $DAMAGE_LIB $MAIN_LIB $DIX_LIB $CONFIG_LIB $OS_LIB $FIXES_LIB" + XDMX_LIBS="$FB_LIB $MI_LIB $XEXT_LIB $RENDER_LIB $RECORD_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_SHADOW_LIB $MIEXT_DAMAGE_LIB $COMPOSITE_LIB $DAMAGE_LIB $MAIN_LIB $DIX_LIB $CONFIG_LIB $OS_LIB $FIXES_LIB" XDMX_SYS_LIBS="$DMXMODULES_LIBS" AC_SUBST([XDMX_CFLAGS]) AC_SUBST([XDMX_LIBS]) @@ -2165,7 +2175,7 @@ if test "$KDRIVE" = yes; then KDRIVE_CFLAGS="$XSERVER_CFLAGS -DHAVE_KDRIVE_CONFIG_H $TSLIB_CFLAGS" - KDRIVE_PURE_LIBS="$FB_LIB $MI_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $OS_LIB" + KDRIVE_PURE_LIBS="$FB_LIB $MI_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $OS_LIB" KDRIVE_LIB='$(top_builddir)/hw/kdrive/src/libkdrive.la' case $host_os in *linux*) @@ -2288,6 +2298,7 @@ Xi/Makefile xfixes/Makefile exa/Makefile dri3/Makefile +present/Makefile hw/Makefile hw/xfree86/Makefile hw/xfree86/common/Makefile diff --git a/hw/dmx/dmxextension.c b/hw/dmx/dmxextension.c index d7296ae2d..c6c6a8e6d 100644 --- a/hw/dmx/dmxextension.c +++ b/hw/dmx/dmxextension.c @@ -68,6 +68,9 @@ * _any_ header files. */ extern FontPtr defaultFont; +/* Hack to get Present to build (present requires RandR) */ +RESTYPE RRCrtcType; + /** This routine provides information to the DMX protocol extension * about a particular screen. */ Bool diff --git a/include/dix-config.h.in b/include/dix-config.h.in index d96da6a27..397ee967a 100644 --- a/include/dix-config.h.in +++ b/include/dix-config.h.in @@ -255,6 +255,9 @@ /* Internal define for Xinerama */ #undef PANORAMIX +/* Support Present extension */ +#undef PRESENT + /* Overall prefix */ #undef PROJECTROOT diff --git a/include/extinit.h b/include/extinit.h index bdb149ca6..fa5f29378 100644 --- a/include/extinit.h +++ b/include/extinit.h @@ -181,4 +181,9 @@ extern void XvMCExtensionInit(void); extern void dri3_extension_init(void); #endif +#if defined(PRESENT) +#include +#include "presentext.h" +#endif + #endif diff --git a/include/xorg-server.h.in b/include/xorg-server.h.in index 5b3b664a4..1281b3e5e 100644 --- a/include/xorg-server.h.in +++ b/include/xorg-server.h.in @@ -70,6 +70,9 @@ /* Internal define for Xinerama */ #undef PANORAMIX +/* Support Present extension */ +#undef PRESENT + /* Support RANDR extension */ #undef RANDR diff --git a/mi/miinitext.c b/mi/miinitext.c index e49948bb5..636618282 100644 --- a/mi/miinitext.c +++ b/mi/miinitext.c @@ -287,7 +287,10 @@ static ExtensionModule staticExtensions[] = { #ifdef DPMSExtension {DPMSExtensionInit, DPMSExtensionName, &noDPMSExtension}, #endif + {present_extension_init, PRESENT_NAME, NULL}, +#ifdef DRI3 {dri3_extension_init, DRI3_NAME, NULL}, +#endif #ifdef RES {ResExtensionInit, XRES_NAME, &noResExtension}, #endif diff --git a/present/Makefile.am b/present/Makefile.am new file mode 100644 index 000000000..7fea6699f --- /dev/null +++ b/present/Makefile.am @@ -0,0 +1,17 @@ +noinst_LTLIBRARIES = libpresent.la +AM_CFLAGS = \ + -DHAVE_XORG_CONFIG_H \ + @DIX_CFLAGS@ @XORG_CFLAGS@ + +libpresent_la_SOURCES = \ + present.h \ + present.c \ + present_event.c \ + present_fake.c \ + present_fence.c \ + present_notify.c \ + present_priv.h \ + present_request.c \ + present_screen.c + +sdk_HEADERS = present.h presentext.h diff --git a/present/present.c b/present/present.c new file mode 100644 index 000000000..4c97ce40c --- /dev/null +++ b/present/present.c @@ -0,0 +1,775 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" +#include +#include +#include +#ifdef MONOTONIC_CLOCK +#include +#endif + +static uint64_t present_event_id; +static struct xorg_list present_exec_queue; +static struct xorg_list present_flip_queue; + +#if 0 +#define DebugPresent(x) ErrorF x +#else +#define DebugPresent(x) +#endif + +/* + * Copies the update region from a pixmap to the target drawable + */ +static void +present_copy_region(DrawablePtr drawable, + PixmapPtr pixmap, + RegionPtr update, + int16_t x_off, + int16_t y_off) +{ + ScreenPtr screen = drawable->pScreen; + GCPtr gc; + + gc = GetScratchGC(drawable->depth, screen); + if (update) { + ChangeGCVal changes[2]; + + changes[0].val = x_off; + changes[1].val = y_off; + ChangeGC(serverClient, gc, + GCClipXOrigin|GCClipYOrigin, + changes); + (*gc->funcs->ChangeClip)(gc, CT_REGION, update, 0); + } + ValidateGC(drawable, gc); + (*gc->ops->CopyArea)(&pixmap->drawable, + drawable, + gc, + 0, 0, + pixmap->drawable.width, pixmap->drawable.height, + x_off, y_off); + if (update) + (*gc->funcs->ChangeClip)(gc, CT_NONE, NULL, 0); + FreeScratchGC(gc); +} + +static Bool +present_check_flip(RRCrtcPtr crtc, + WindowPtr window, + PixmapPtr pixmap, + Bool sync_flip, + RegionPtr valid, + int16_t x_off, + int16_t y_off) +{ + ScreenPtr screen = window->drawable.pScreen; + WindowPtr root = screen->root; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (!screen_priv) + return FALSE; + + if (!screen_priv->info) + return FALSE; + + if (!crtc) + return FALSE; + + /* Check to see if the driver supports flips at all */ + if (!screen_priv->info->flip) + return FALSE; + + /* Can't pend a flip while unflipping */ + if (screen_priv->unflip_event_id) { + return FALSE; + } + + /* Can't have two pending flips at the same time */ + if (screen_priv->flip_pending) { + return FALSE; + } + + /* Make sure the window hasn't been redirected with Composite */ + if (screen->GetWindowPixmap(window) != screen->GetScreenPixmap(screen)) + return FALSE; + + /* Check for full-screen window */ + if (!RegionEqual(&window->clipList, &root->winSize)) { + return FALSE; + } + + /* Source pixmap must align with window exactly */ + if (x_off || y_off) { + return FALSE; + } + + /* Make sure the area marked as valid fills the screen */ + if (valid && !RegionEqual(valid, &root->winSize)) { + return FALSE; + } + + /* Does the window match the pixmap exactly? */ + if (window->drawable.x != 0 || window->drawable.y != 0 || +#ifdef COMPOSITE + window->drawable.x != pixmap->screen_x || window->drawable.y != pixmap->screen_y || +#endif + window->drawable.width != pixmap->drawable.width || + window->drawable.height != pixmap->drawable.height) { + return FALSE; + } + + /* Ask the driver for permission */ + if (screen_priv->info->check_flip) { + if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) { + return FALSE; + } + } + + return TRUE; +} + +static Bool +present_flip(RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + PixmapPtr pixmap, + Bool sync_flip) +{ + ScreenPtr screen = crtc->pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip); +} + +static void +present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc) +{ + int n; + + present_send_complete_notify(vblank->window, kind, mode, vblank->serial, ust, crtc_msc - vblank->msc_offset); + for (n = 0; n < vblank->num_notifies; n++) { + WindowPtr window = vblank->notifies[n].window; + CARD32 serial = vblank->notifies[n].serial; + + if (window) + present_send_complete_notify(window, kind, mode, serial, ust, crtc_msc - vblank->msc_offset); + } +} + +static void +present_pixmap_idle(PixmapPtr pixmap, WindowPtr window, CARD32 serial, struct present_fence *present_fence) +{ + present_fence_set_triggered(present_fence); + present_send_idle_notify(window, serial, pixmap, present_fence); +} + +RRCrtcPtr +present_get_crtc(WindowPtr window) +{ + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (!screen_priv) + return NULL; + + if (!screen_priv->info) + return NULL; + + return (*screen_priv->info->get_crtc)(window); +} + +uint32_t +present_query_capabilities(RRCrtcPtr crtc) +{ + present_screen_priv_ptr screen_priv; + + if (!crtc) + return 0; + + screen_priv = present_screen_priv(crtc->pScreen); + + if (!screen_priv) + return 0; + + if (!screen_priv->info) + return 0; + + return screen_priv->info->capabilities; +} + +static int +present_get_ust_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc) +{ + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (crtc == NULL) + return present_fake_get_ust_msc(screen, ust, msc); + else + return (*screen_priv->info->get_ust_msc)(crtc, ust, msc); +} + +static void +present_flush(WindowPtr window) +{ + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (!screen_priv) + return; + + if (!screen_priv->info) + return; + + (*screen_priv->info->flush) (window); +} + +static int +present_queue_vblank(ScreenPtr screen, + RRCrtcPtr crtc, + uint64_t event_id, + uint64_t msc) +{ + Bool ret; + + if (crtc == NULL) + ret = present_fake_queue_vblank(screen, event_id, msc); + else + { + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + ret = (*screen_priv->info->queue_vblank) (crtc, event_id, msc); + } + return ret; +} + +static uint64_t +present_window_to_crtc_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t window_msc, uint64_t new_msc) +{ + present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE); + + if (crtc != window_priv->crtc) { + uint64_t old_ust, old_msc; + + /* The old CRTC may have been turned off, in which case + * we'll just use whatever previous MSC we'd seen from this CRTC + */ + + if (present_get_ust_msc(window, window_priv->crtc, &old_ust, &old_msc) != Success) + old_msc = window_priv->msc; + + window_priv->msc_offset += new_msc - old_msc; + window_priv->crtc = crtc; + } + + return window_msc + window_priv->msc_offset; +} + +static void +present_flip_idle(ScreenPtr screen) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (screen_priv->flip_pixmap) { + present_pixmap_idle(screen_priv->flip_pixmap, screen_priv->flip_window, + screen_priv->flip_serial, screen_priv->flip_idle_fence); + present_fence_destroy(screen_priv->flip_idle_fence); + dixDestroyPixmap(screen_priv->flip_pixmap, screen_priv->flip_pixmap->drawable.id); + screen_priv->flip_crtc = NULL; + screen_priv->flip_window = NULL; + screen_priv->flip_serial = 0; + screen_priv->flip_pixmap = NULL; + screen_priv->flip_idle_fence = NULL; + } +} + +static void +present_unflip(ScreenPtr screen) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + assert (!screen_priv->unflip_event_id); + assert (!screen_priv->flip_pending); + + /* Update the screen pixmap with the current flip pixmap contents + */ + if (screen_priv->flip_pixmap) { + present_copy_region(&screen->GetScreenPixmap(screen)->drawable, + screen_priv->flip_pixmap, + NULL, 0, 0); + } + screen_priv->unflip_event_id = ++present_event_id; + DebugPresent(("u %lld\n", screen_priv->unflip_event_id)); + (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id); +} + +static void +present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc); + +static void +present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) +{ + WindowPtr window = vblank->window; + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + DebugPresent(("\tn %p %8lld: %08lx -> %08lx\n", vblank, vblank->target_msc, + vblank->pixmap ? vblank->pixmap->drawable.id : 0, + vblank->window->drawable.id)); + + assert (vblank == screen_priv->flip_pending); + + present_flip_idle(screen); + + /* Transfer reference for pixmap and fence from vblank to screen_priv */ + screen_priv->flip_crtc = vblank->crtc; + screen_priv->flip_window = vblank->window; + screen_priv->flip_serial = vblank->serial; + screen_priv->flip_pixmap = vblank->pixmap; + screen_priv->flip_idle_fence = vblank->idle_fence; + + vblank->pixmap = NULL; + vblank->idle_fence = NULL; + + screen_priv->flip_pending = NULL; + + if (vblank->abort_flip) + present_unflip(screen); + + if (!vblank->window_destroyed) + present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc); + present_vblank_destroy(vblank); +} + +void +present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc) +{ + present_vblank_ptr vblank, tmp; + int s; + + xorg_list_for_each_entry_safe(vblank, tmp, &present_exec_queue, event_queue) { + if (vblank->event_id == event_id) { + xorg_list_del(&vblank->event_queue); + present_execute(vblank, ust, msc); + return; + } + } + xorg_list_for_each_entry_safe(vblank, tmp, &present_flip_queue, event_queue) { + if (vblank->event_id == event_id) { + xorg_list_del(&vblank->event_queue); + present_flip_notify(vblank, ust, msc); + return; + } + } + + for (s = 0; s < screenInfo.numScreens; s++) { + ScreenPtr screen = screenInfo.screens[s]; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (event_id == screen_priv->unflip_event_id) { + DebugPresent(("\tun %lld\n", event_id)); + screen_priv->unflip_event_id = 0; + present_flip_idle(screen); + } + } +} + +/* + * 'window' is being reconfigured. Check to see if it is involved + * in flipping and clean up as necessary + */ +void +present_check_flip_window (WindowPtr window) +{ + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + present_window_priv_ptr window_priv = present_window_priv(window); + present_vblank_ptr flip_pending = screen_priv->flip_pending; + present_vblank_ptr vblank; + + /* If this window hasn't ever been used with Present, it can't be + * flipping + */ + if (!window_priv) + return; + + if (screen_priv->unflip_event_id) + return; + + if (flip_pending) { + /* + * Check pending flip + */ + if (flip_pending->window == window) { + if (!present_check_flip(flip_pending->crtc, window, flip_pending->pixmap, + flip_pending->sync_flip, NULL, 0, 0)) + flip_pending->abort_flip = TRUE; + } + } else { + /* + * Check current flip + */ + if (window == screen_priv->flip_window) { + if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, FALSE, NULL, 0, 0)) + present_unflip(screen); + } + } + + /* Now check any queued vblanks */ + xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) { + if (vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, FALSE, NULL, 0, 0)) + vblank->flip = FALSE; + } +} + +/* + * Once the required MSC has been reached, execute the pending request. + * + * For requests to actually present something, either blt contents to + * the screen or queue a frame buffer swap. + * + * For requests to just get the current MSC/UST combo, skip that part and + * go straight to event delivery + */ + +static void +present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) +{ + WindowPtr window = vblank->window; + present_screen_priv_ptr screen_priv = present_screen_priv(window->drawable.pScreen); + + if (vblank->wait_fence) { + /* XXX check fence, queue if not ready */ + } + + xorg_list_del(&vblank->event_queue); + if (vblank->pixmap) { + + if (vblank->flip && screen_priv->flip_pending == NULL && !screen_priv->unflip_event_id) { + + DebugPresent(("\tf %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id)); + /* Prepare to flip by removing from the window/screen lists + * and sticking it into the flip_pending field + */ + screen_priv->flip_pending = vblank; + xorg_list_del(&vblank->window_list); + + xorg_list_add(&vblank->event_queue, &present_flip_queue); + /* Try to flip + */ + if (present_flip(vblank->crtc, vblank->event_id, vblank->target_msc, vblank->pixmap, vblank->sync_flip)) + return; + + xorg_list_del(&vblank->event_queue); + /* Oops, flip failed. Clear the flip_pending field + */ + screen_priv->flip_pending = NULL; + vblank->flip = FALSE; + } + DebugPresent(("\tc %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id)); + if (screen_priv->flip_pending) { + + /* Check pending flip + */ + if (window == screen_priv->flip_pending->window) + screen_priv->flip_pending->abort_flip = TRUE; + } else if (!screen_priv->unflip_event_id) { + + /* Check current flip + */ + if (window == screen_priv->flip_window) + present_unflip(window->drawable.pScreen); + } + present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off); + + /* present_copy_region sticks the region into a scratch GC, + * which is then freed, freeing the region + */ + vblank->update = NULL; + present_flush(window); + + present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence); + } + present_vblank_notify(vblank, vblank->kind, PresentCompleteModeCopy, ust, crtc_msc); + present_vblank_destroy(vblank); +} + +int +present_pixmap(WindowPtr window, + PixmapPtr pixmap, + CARD32 serial, + RegionPtr valid, + RegionPtr update, + int16_t x_off, + int16_t y_off, + RRCrtcPtr target_crtc, + SyncFence *wait_fence, + SyncFence *idle_fence, + uint32_t options, + uint64_t window_msc, + uint64_t divisor, + uint64_t remainder, + present_notify_ptr notifies, + int num_notifies) +{ + uint64_t ust; + uint64_t target_msc; + uint64_t crtc_msc; + int ret; + present_vblank_ptr vblank; + ScreenPtr screen = window->drawable.pScreen; + present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE); + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + if (!window_priv) + return BadAlloc; + + if (!target_crtc) { + /* Update the CRTC if we have a pixmap or we don't have a CRTC + */ + if (!pixmap) + target_crtc = window_priv->crtc; + + if (!target_crtc) + target_crtc = present_get_crtc(window); + } + + present_get_ust_msc(window, target_crtc, &ust, &crtc_msc); + + target_msc = present_window_to_crtc_msc(window, target_crtc, window_msc, crtc_msc); + + /* Stash the current MSC away in case we need it later + */ + window_priv->msc = crtc_msc; + + /* Adjust target_msc to match modulus + */ + if (crtc_msc >= target_msc) { + if (divisor != 0) { + target_msc = crtc_msc - (crtc_msc % divisor) + remainder; + if (target_msc <= crtc_msc) + target_msc += divisor; + } else + target_msc = crtc_msc; + } + + /* + * Look for a matching presentation already on the list and + * don't bother doing the previous one if this one will overwrite it + * in the same frame + */ + + if (!update) { + xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) { + + if (!vblank->pixmap) + continue; + + if (vblank->crtc != target_crtc || vblank->target_msc != target_msc) + continue; + + present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence); + present_fence_destroy(vblank->idle_fence); + dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id); + + vblank->pixmap = NULL; + vblank->idle_fence = NULL; + } + } + + vblank = calloc (1, sizeof (present_vblank_rec)); + if (!vblank) + return BadAlloc; + + xorg_list_append(&vblank->window_list, &window_priv->vblank); + xorg_list_init(&vblank->event_queue); + + vblank->screen = screen; + vblank->window = window; + vblank->pixmap = pixmap; + vblank->event_id = ++present_event_id; + if (pixmap) { + vblank->kind = PresentCompleteKindPixmap; + pixmap->refcnt++; + } else + vblank->kind = PresentCompleteKindNotifyMSC; + + vblank->serial = serial; + + if (valid) { + vblank->valid = RegionDuplicate(valid); + if (!vblank->valid) + goto no_mem; + } + if (update) { + vblank->update = RegionDuplicate(update); + if (!vblank->update) + goto no_mem; + } + + vblank->x_off = x_off; + vblank->y_off = y_off; + vblank->target_msc = target_msc; + vblank->crtc = target_crtc; + vblank->msc_offset = window_priv->msc_offset; + vblank->notifies = notifies; + vblank->num_notifies = num_notifies; + + if (!screen_priv->info || !(screen_priv->info->capabilities & PresentCapabilityAsync)) + vblank->sync_flip = TRUE; + + if (pixmap && present_check_flip (target_crtc, window, pixmap, vblank->sync_flip, valid, x_off, y_off)) { + vblank->flip = TRUE; + if (vblank->sync_flip) + target_msc--; + } + + if (idle_fence) { + vblank->idle_fence = present_fence_create(idle_fence); + if (!vblank->idle_fence) + goto no_mem; + } + + if (pixmap) + DebugPresent(("q %p %8lld: %08lx -> %08lx (crtc %d)\n", + vblank, target_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id, target_crtc ? 1 : 0)); + + xorg_list_add(&vblank->event_queue, &present_exec_queue); + if (target_msc >= crtc_msc) { + ret = present_queue_vblank(screen, target_crtc, vblank->event_id, target_msc); + if (ret != Success) { + xorg_list_del(&vblank->event_queue); + goto failure; + } + } else + present_execute(vblank, ust, crtc_msc); + + return Success; + +no_mem: + ret = BadAlloc; +failure: + vblank->notifies = NULL; + present_vblank_destroy(vblank); + return ret; +} + +void +present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) +{ + present_vblank_ptr vblank, tmp; + + if (crtc == NULL) + present_fake_abort_vblank(screen, event_id, msc); + else + { + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + (*screen_priv->info->abort_vblank) (crtc, event_id, msc); + } + + xorg_list_for_each_entry_safe(vblank, tmp, &present_exec_queue, event_queue) { + if (vblank->event_id == event_id) { + xorg_list_del(&vblank->event_queue); + return; + } + } + xorg_list_for_each_entry_safe(vblank, tmp, &present_flip_queue, event_queue) { + if (vblank->event_id == event_id) { + xorg_list_del(&vblank->event_queue); + return; + } + } +} + +int +present_notify_msc(WindowPtr window, + CARD32 serial, + uint64_t target_msc, + uint64_t divisor, + uint64_t remainder) +{ + return present_pixmap(window, + NULL, + serial, + NULL, NULL, + 0, 0, + NULL, + NULL, NULL, + 0, + target_msc, divisor, remainder, NULL, 0); +} + +void +present_flip_destroy(ScreenPtr screen) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + /* XXX this needs to be synchronous for server reset */ + + /* Do the actual cleanup once the flip has been performed by the hardware */ + if (screen_priv->flip_pending) + screen_priv->flip_pending->abort_flip = TRUE; +} + +void +present_vblank_destroy(present_vblank_ptr vblank) +{ + /* Remove vblank from window and screen lists */ + xorg_list_del(&vblank->window_list); + + DebugPresent(("\td %p %8lld: %08lx -> %08lx\n", vblank, vblank->target_msc, + vblank->pixmap ? vblank->pixmap->drawable.id : 0, + vblank->window->drawable.id)); + + /* Drop pixmap reference */ + if (vblank->pixmap) + dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id); + + /* Free regions */ + if (vblank->valid) + RegionDestroy(vblank->valid); + if (vblank->update) + RegionDestroy(vblank->update); + + if (vblank->idle_fence) + present_fence_destroy(vblank->idle_fence); + + if (vblank->notifies) + present_destroy_notifies(vblank->notifies, vblank->num_notifies); + + free(vblank); +} + +Bool +present_init(void) +{ + xorg_list_init(&present_exec_queue); + xorg_list_init(&present_flip_queue); + present_fake_queue_init(); + return TRUE; +} diff --git a/present/present.h b/present/present.h new file mode 100644 index 000000000..6a451fb23 --- /dev/null +++ b/present/present.h @@ -0,0 +1,118 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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. + */ + +#ifndef _PRESENT_H_ +#define _PRESENT_H_ + +#include +#include "randrstr.h" +#include "presentext.h" + +typedef struct present_vblank present_vblank_rec, *present_vblank_ptr; + +/* Return the current CRTC for 'window'. + */ +typedef RRCrtcPtr (*present_get_crtc_ptr) (WindowPtr window); + +/* Return the current ust/msc for 'crtc' + */ +typedef int (*present_get_ust_msc_ptr) (RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc); + +/* Queue callback on 'crtc' for time 'msc'. Call present_event_notify with 'event_id' + * at or after 'msc'. Return false if it didn't happen (which might occur if 'crtc' + * is not currently generating vblanks). + */ +typedef Bool (*present_queue_vblank_ptr) (RRCrtcPtr crtc, + uint64_t event_id, + uint64_t msc); + +/* Abort pending vblank. The extension is no longer interested in + * 'event_id' which was to be notified at 'msc'. If possible, the + * driver is free to de-queue the notification. + */ +typedef void (*present_abort_vblank_ptr) (RRCrtcPtr crtc, uint64_t event_id, uint64_t msc); + +/* Flush pending drawing on 'window' to the hardware. + */ +typedef void (*present_flush_ptr) (WindowPtr window); + +/* Check if 'pixmap' is suitable for flipping to 'window'. + */ +typedef Bool (*present_check_flip_ptr) (RRCrtcPtr crtc, WindowPtr window, PixmapPtr pixmap, Bool sync_flip); + +/* Flip pixmap, return false if it didn't happen. + * + * 'crtc' is to be used for any necessary synchronization. + * + * 'sync_flip' requests that the flip be performed at the next + * vertical blank interval to avoid tearing artifacts. If false, the + * flip should be performed as soon as possible. + * + * present_event_notify should be called with 'event_id' when the flip + * occurs + */ +typedef Bool (*present_flip_ptr) (RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + PixmapPtr pixmap, + Bool sync_flip); + +/* "unflip" back to the regular screen scanout buffer + * + * present_event_notify should be called with 'event_id' when the unflip occurs. + */ +typedef void (*present_unflip_ptr) (ScreenPtr screen, + uint64_t event_id); + +#define PRESENT_SCREEN_INFO_VERSION 0 + +typedef struct present_screen_info { + uint32_t version; + + present_get_crtc_ptr get_crtc; + present_get_ust_msc_ptr get_ust_msc; + present_queue_vblank_ptr queue_vblank; + present_abort_vblank_ptr abort_vblank; + present_flush_ptr flush; + uint32_t capabilities; + present_check_flip_ptr check_flip; + present_flip_ptr flip; + present_unflip_ptr unflip; + +} present_screen_info_rec, *present_screen_info_ptr; + +/* + * Called when 'event_id' occurs. 'ust' and 'msc' indicate when the + * event actually happened + */ +extern _X_EXPORT void +present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc); + +/* 'crtc' has been turned off, so any pending events will never occur. + */ +extern _X_EXPORT void +present_event_abandon(RRCrtcPtr crtc); + +extern _X_EXPORT Bool +present_screen_init(ScreenPtr screen, present_screen_info_ptr info); + +#endif /* _PRESENT_H_ */ diff --git a/present/present_event.c b/present/present_event.c new file mode 100644 index 000000000..a30bc8286 --- /dev/null +++ b/present/present_event.c @@ -0,0 +1,239 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" + +#include "present_priv.h" + +RESTYPE present_event_type; + +static int +present_free_event(pointer data, XID id) +{ + present_event_ptr present_event = (present_event_ptr) data; + present_window_priv_ptr window_priv = present_window_priv(present_event->window); + present_event_ptr *previous, current; + + for (previous = &window_priv->events; (current = *previous); previous = ¤t->next) { + if (current == present_event) { + *previous = present_event->next; + break; + } + } + free((pointer) present_event); + return 1; + +} + +void +present_free_events(WindowPtr window) +{ + present_window_priv_ptr window_priv = present_window_priv(window); + present_event_ptr event; + + if (!window_priv) + return; + + while ((event = window_priv->events)) + FreeResource(event->id, RT_NONE); +} + +static void +present_event_swap(xGenericEvent *from, xGenericEvent *to) +{ + *to = *from; + swaps(&to->sequenceNumber); + swapl(&to->length); + swaps(&to->evtype); + switch (from->evtype) { + case PresentConfigureNotify: { + xPresentConfigureNotify *c = (xPresentConfigureNotify *) to; + + swapl(&c->eid); + swapl(&c->window); + swaps(&c->x); + swaps(&c->y); + swaps(&c->width); + swaps(&c->height); + swaps(&c->off_x); + swaps(&c->off_y); + swaps(&c->pixmap_width); + swaps(&c->pixmap_height); + swapl(&c->pixmap_flags); + break; + } + case PresentCompleteNotify: + { + xPresentCompleteNotify *c = (xPresentCompleteNotify *) to; + swapl(&c->eid); + swapl(&c->window); + swapl(&c->serial); + swapll(&c->ust); + swapll(&c->msc); + } + case PresentIdleNotify: + { + xPresentIdleNotify *c = (xPresentIdleNotify *) to; + swapl(&c->eid); + swapl(&c->window); + swapl(&c->serial); + swapl(&c->idle_fence); + } + } +} + +void +present_send_config_notify(WindowPtr window, int x, int y, int w, int h, int bw, WindowPtr sibling) +{ + present_window_priv_ptr window_priv = present_window_priv(window); + + if (window_priv) { + xPresentConfigureNotify cn = { + .type = GenericEvent, + .extension = present_request, + .length = (sizeof(xPresentConfigureNotify) - 32) >> 2, + .evtype = PresentConfigureNotify, + .eid = 0, + .window = window->drawable.id, + .x = x, + .y = y, + .width = w, + .height = h, + .off_x = 0, + .off_y = 0, + .pixmap_width = w, + .pixmap_height = h, + .pixmap_flags = 0 + }; + present_event_ptr event; + + for (event = window_priv->events; event; event = event->next) { + if (event->mask & (1 << PresentConfigureNotify)) { + cn.eid = event->id; + WriteEventsToClient(event->client, 1, (xEvent *) &cn); + } + } + } +} + +void +present_send_complete_notify(WindowPtr window, CARD8 kind, CARD8 mode, CARD32 serial, uint64_t ust, uint64_t msc) +{ + present_window_priv_ptr window_priv = present_window_priv(window); + + if (window_priv) { + xPresentCompleteNotify cn = { + .type = GenericEvent, + .extension = present_request, + .length = (sizeof(xPresentCompleteNotify) - 32) >> 2, + .evtype = PresentCompleteNotify, + .kind = kind, + .mode = mode, + .eid = 0, + .window = window->drawable.id, + .serial = serial, + .ust = ust, + .msc = msc, + }; + present_event_ptr event; + + for (event = window_priv->events; event; event = event->next) { + if (event->mask & PresentCompleteNotifyMask) { + cn.eid = event->id; + WriteEventsToClient(event->client, 1, (xEvent *) &cn); + } + } + } +} + +void +present_send_idle_notify(WindowPtr window, CARD32 serial, PixmapPtr pixmap, struct present_fence *idle_fence) +{ + present_window_priv_ptr window_priv = present_window_priv(window); + + if (window_priv) { + xPresentIdleNotify in = { + .type = GenericEvent, + .extension = present_request, + .length = (sizeof(xPresentIdleNotify) - 32) >> 2, + .evtype = PresentIdleNotify, + .eid = 0, + .window = window->drawable.id, + .serial = serial, + .pixmap = pixmap->drawable.id, + .idle_fence = present_fence_id(idle_fence) + }; + present_event_ptr event; + + for (event = window_priv->events; event; event = event->next) { + if (event->mask & PresentIdleNotifyMask) { + in.eid = event->id; + WriteEventsToClient(event->client, 1, (xEvent *) &in); + } + } + } +} + +int +present_select_input(ClientPtr client, XID eid, WindowPtr window, CARD32 mask) +{ + present_window_priv_ptr window_priv = present_get_window_priv(window, mask != 0); + present_event_ptr event; + + if (!window_priv) { + if (mask) + return BadAlloc; + return Success; + } + + event = calloc (1, sizeof (present_event_rec)); + if (!event) + return BadAlloc; + + event->client = client; + event->window = window; + event->id = eid; + event->mask = mask; + + event->next = window_priv->events; + window_priv->events = event; + + if (!AddResource(event->id, present_event_type, (pointer) event)) + return BadAlloc; + + return Success; +} + +Bool +present_event_init(void) +{ + present_event_type = CreateNewResourceType(present_free_event, "PresentEvent"); + if (!present_event_type) + return FALSE; + + GERegisterExtension(present_request, present_event_swap); + return TRUE; +} diff --git a/present/present_fake.c b/present/present_fake.c new file mode 100644 index 000000000..a67759254 --- /dev/null +++ b/present/present_fake.c @@ -0,0 +1,140 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" +#include "list.h" + +static struct xorg_list fake_vblank_queue; + +typedef struct present_fake_vblank { + struct xorg_list list; + uint64_t event_id; + OsTimerPtr timer; + ScreenPtr screen; +} present_fake_vblank_rec, *present_fake_vblank_ptr; + +int +present_fake_get_ust_msc(ScreenPtr screen, uint64_t *ust, uint64_t *msc) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + *ust = GetTimeInMicros(); + *msc = *ust / screen_priv->fake_interval; + return Success; +} + +static void +present_fake_notify(ScreenPtr screen, uint64_t event_id) +{ + uint64_t ust, msc; + + present_fake_get_ust_msc(screen, &ust, &msc); + present_event_notify(event_id, ust, msc); +} + +static CARD32 +present_fake_do_timer(OsTimerPtr timer, + CARD32 time, + pointer arg) +{ + present_fake_vblank_ptr fake_vblank = arg; + + present_fake_notify(fake_vblank->screen, fake_vblank->event_id); + xorg_list_del(&fake_vblank->list); + free(fake_vblank); + return 0; +} + +void +present_fake_abort_vblank(ScreenPtr screen, uint64_t event_id, uint64_t msc) +{ + present_fake_vblank_ptr fake_vblank, tmp; + + xorg_list_for_each_entry_safe(fake_vblank, tmp, &fake_vblank_queue, list) { + if (fake_vblank->event_id == event_id) { + TimerCancel(fake_vblank->timer); + xorg_list_del(&fake_vblank->list); + free (fake_vblank); + break; + } + } +} + +int +present_fake_queue_vblank(ScreenPtr screen, + uint64_t event_id, + uint64_t msc) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + uint64_t ust = msc * screen_priv->fake_interval; + uint64_t now = GetTimeInMicros(); + INT32 delay = ((int64_t) (ust - now)) / 1000; + present_fake_vblank_ptr fake_vblank; + + if (delay <= 0) { + present_fake_notify(screen, event_id); + return Success; + } + + fake_vblank = calloc (1, sizeof (present_fake_vblank_rec)); + if (!fake_vblank) + return BadAlloc; + + fake_vblank->screen = screen; + fake_vblank->event_id = event_id; + fake_vblank->timer = TimerSet(NULL, 0, delay, present_fake_do_timer, fake_vblank); + if (!fake_vblank->timer) { + free(fake_vblank); + return BadAlloc; + } + + xorg_list_add(&fake_vblank->list, &fake_vblank_queue); + + return Success; +} + +void +present_fake_screen_init(ScreenPtr screen) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + /* For screens with hardware vblank support, the fake code + * will be used for off-screen windows and while screens are blanked, + * in which case we want a slow interval here + * + * Otherwise, pretend that the screen runs at 60Hz + */ + if (screen_priv->info && screen_priv->info->get_crtc) + screen_priv->fake_interval = 1000000; + else + screen_priv->fake_interval = 16667; +} + +void +present_fake_queue_init(void) +{ + xorg_list_init(&fake_vblank_queue); +} diff --git a/present/present_fence.c b/present/present_fence.c new file mode 100644 index 000000000..db5efcaad --- /dev/null +++ b/present/present_fence.c @@ -0,0 +1,112 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" +#include +#include +#include + +/* + * Wraps SyncFence objects so we can add a SyncTrigger to find out + * when the SyncFence gets destroyed and clean up appropriately + */ + +struct present_fence { + SyncTrigger trigger; + SyncFence *fence; +}; + +/* + * SyncTrigger callbacks + */ +static Bool +present_fence_sync_check_trigger(SyncTrigger *trigger, XSyncValue oldval) +{ + return FALSE; +} + +static void +present_fence_sync_trigger_fired(SyncTrigger *trigger) +{ +} + +static void +present_fence_sync_counter_destroyed(SyncTrigger *trigger) +{ + struct present_fence *present_fence = container_of(trigger, struct present_fence, trigger); + + present_fence->fence = NULL; +} + +struct present_fence * +present_fence_create(SyncFence *fence) +{ + struct present_fence *present_fence; + + present_fence = calloc (1, sizeof (struct present_fence)); + if (!present_fence) + return NULL; + + present_fence->fence = fence; + present_fence->trigger.pSync = (SyncObject *) fence; + present_fence->trigger.CheckTrigger = present_fence_sync_check_trigger; + present_fence->trigger.TriggerFired = present_fence_sync_trigger_fired; + present_fence->trigger.CounterDestroyed = present_fence_sync_counter_destroyed; + + if (SyncAddTriggerToSyncObject(&present_fence->trigger) != Success) { + free (present_fence); + return NULL; + } + return present_fence; +} + +void +present_fence_destroy(struct present_fence *present_fence) +{ + if (present_fence) { + if (present_fence->fence) + SyncDeleteTriggerFromSyncObject(&present_fence->trigger); + free(present_fence); + } +} + +void +present_fence_set_triggered(struct present_fence *present_fence) +{ + if (present_fence) + if (present_fence->fence) + (*present_fence->fence->funcs.SetTriggered) (present_fence->fence); +} + +XID +present_fence_id(struct present_fence *present_fence) +{ + if (!present_fence) + return None; + if (!present_fence->fence) + return None; + return present_fence->fence->sync.id; +} diff --git a/present/present_notify.c b/present/present_notify.c new file mode 100644 index 000000000..e272e08dc --- /dev/null +++ b/present/present_notify.c @@ -0,0 +1,114 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" + +/* + * Mark all pending notifies for 'window' as invalid when + * the window is destroyed + */ + +void +present_clear_window_notifies(WindowPtr window) +{ + present_notify_ptr notify; + present_window_priv_ptr window_priv = present_window_priv(window); + + if (!window_priv) + return; + + xorg_list_for_each_entry(notify, &window_priv->notifies, window_list) { + notify->window = NULL; + } +} + +/* + * 'notify' is being freed; remove it from the window's notify list + */ + +void +present_free_window_notify(present_notify_ptr notify) +{ + xorg_list_del(¬ify->window_list); +} + +/* + * 'notify' is new; add it to the specified window + */ + +int +present_add_window_notify(present_notify_ptr notify) +{ + WindowPtr window = notify->window; + present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE); + + if (!window_priv) + return BadAlloc; + + xorg_list_add(¬ify->window_list, &window_priv->notifies); + return Success; +} + +int +present_create_notifies(ClientPtr client, int num_notifies, xPresentNotify *x_notifies, present_notify_ptr *p_notifies) +{ + present_notify_ptr notifies; + int i; + int added = 0; + int status; + + notifies = calloc (num_notifies, sizeof (present_notify_rec)); + if (!notifies) + return BadAlloc; + + for (i = 0; i < num_notifies; i++) { + status = dixLookupWindow(¬ifies[i].window, x_notifies[i].window, client, DixGetAttrAccess); + if (status != Success) + goto bail; + + notifies[i].serial = x_notifies[i].serial; + status = present_add_window_notify(¬ifies[i]); + if (status != Success) + goto bail; + + added = i; + } + return Success; + +bail: + present_destroy_notifies(notifies, added); + return status; +} + +void +present_destroy_notifies(present_notify_ptr notifies, int num_notifies) +{ + int i; + for (i = 0; i < num_notifies; i++) + present_free_window_notify(¬ifies[i]); + + free(notifies); +} diff --git a/present/present_priv.h b/present/present_priv.h new file mode 100644 index 000000000..a92b62a2d --- /dev/null +++ b/present/present_priv.h @@ -0,0 +1,288 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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. + */ + +#ifndef _PRESENT_PRIV_H_ +#define _PRESENT_PRIV_H_ + +#include +#include "scrnintstr.h" +#include "misc.h" +#include "list.h" +#include "windowstr.h" +#include "dixstruct.h" +#include "present.h" +#include +#include +#include +#include + +extern int present_request; + +extern DevPrivateKeyRec present_screen_private_key; + +typedef struct present_fence *present_fence_ptr; + +typedef struct present_notify present_notify_rec, *present_notify_ptr; + +struct present_notify { + struct xorg_list window_list; + WindowPtr window; + CARD32 serial; +}; + +struct present_vblank { + struct xorg_list window_list; + struct xorg_list event_queue; + ScreenPtr screen; + WindowPtr window; + PixmapPtr pixmap; + RegionPtr valid; + RegionPtr update; + RRCrtcPtr crtc; + uint32_t serial; + int16_t x_off; + int16_t y_off; + CARD16 kind; + uint64_t event_id; + uint64_t target_msc; + uint64_t msc_offset; + present_fence_ptr idle_fence; + present_fence_ptr wait_fence; + present_notify_ptr notifies; + int num_notifies; + Bool flip; + Bool sync_flip; + Bool abort_flip; + + Bool window_destroyed; +}; + +typedef struct present_screen_priv { + CloseScreenProcPtr CloseScreen; + ConfigNotifyProcPtr ConfigNotify; + DestroyWindowProcPtr DestroyWindow; + ClipNotifyProcPtr ClipNotify; + + present_vblank_ptr flip_pending; + uint64_t unflip_event_id; + + uint32_t fake_interval; + + /* Currently active flipped pixmap and fence */ + RRCrtcPtr flip_crtc; + WindowPtr flip_window; + uint32_t flip_serial; + PixmapPtr flip_pixmap; + present_fence_ptr flip_idle_fence; + + present_screen_info_ptr info; +} present_screen_priv_rec, *present_screen_priv_ptr; + +#define wrap(priv,real,mem,func) {\ + priv->mem = real->mem; \ + real->mem = func; \ +} + +#define unwrap(priv,real,mem) {\ + real->mem = priv->mem; \ +} + +static inline present_screen_priv_ptr +present_screen_priv(ScreenPtr screen) +{ + return (present_screen_priv_ptr)dixLookupPrivate(&(screen)->devPrivates, &present_screen_private_key); +} + +/* + * Each window has a list of clients and event masks + */ +typedef struct present_event *present_event_ptr; + +typedef struct present_event { + present_event_ptr next; + ClientPtr client; + WindowPtr window; + XID id; + int mask; +} present_event_rec; + +typedef struct present_window_priv { + present_event_ptr events; + RRCrtcPtr crtc; /* Last reported CRTC from get_ust_msc */ + uint64_t msc_offset; + uint64_t msc; /* Last reported MSC from the current crtc */ + struct xorg_list vblank; + struct xorg_list notifies; +} present_window_priv_rec, *present_window_priv_ptr; + +extern DevPrivateKeyRec present_window_private_key; + +static inline present_window_priv_ptr +present_window_priv(WindowPtr window) +{ + return (present_window_priv_ptr)dixGetPrivate(&(window)->devPrivates, &present_window_private_key); +} + +present_window_priv_ptr +present_get_window_priv(WindowPtr window, Bool create); + +extern RESTYPE present_event_type; + +/* + * present.c + */ +int +present_pixmap(WindowPtr window, + PixmapPtr pixmap, + CARD32 serial, + RegionPtr valid, + RegionPtr update, + int16_t x_off, + int16_t y_off, + RRCrtcPtr target_crtc, + SyncFence *wait_fence, + SyncFence *idle_fence, + uint32_t options, + uint64_t target_msc, + uint64_t divisor, + uint64_t remainder, + present_notify_ptr notifies, + int num_notifies); + +int +present_notify_msc(WindowPtr window, + CARD32 serial, + uint64_t target_msc, + uint64_t divisor, + uint64_t remainder); + +void +present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc); + +void +present_vblank_destroy(present_vblank_ptr vblank); + +void +present_flip_destroy(ScreenPtr screen); + +void +present_check_flip_window(WindowPtr window); + +RRCrtcPtr +present_get_crtc(WindowPtr window); + +uint32_t +present_query_capabilities(RRCrtcPtr crtc); + +Bool +present_init(void); + +/* + * present_event.c + */ + +void +present_free_events(WindowPtr window); + +void +present_send_config_notify(WindowPtr window, int x, int y, int w, int h, int bw, WindowPtr sibling); + +void +present_send_complete_notify(WindowPtr window, CARD8 kind, CARD8 mode, CARD32 serial, uint64_t ust, uint64_t msc); + +void +present_send_idle_notify(WindowPtr window, CARD32 serial, PixmapPtr pixmap, present_fence_ptr idle_fence); + +int +present_select_input(ClientPtr client, + CARD32 eid, + WindowPtr window, + CARD32 event_mask); + +Bool +present_event_init(void); + +/* + * present_fake.c + */ +int +present_fake_get_ust_msc(ScreenPtr screen, uint64_t *ust, uint64_t *msc); + +int +present_fake_queue_vblank(ScreenPtr screen, uint64_t event_id, uint64_t msc); + +void +present_fake_abort_vblank(ScreenPtr screen, uint64_t event_id, uint64_t msc); + +void +present_fake_screen_init(ScreenPtr screen); + +void +present_fake_queue_init(void); + +/* + * present_fence.c + */ +struct present_fence * +present_fence_create(SyncFence *sync_fence); + +void +present_fence_destroy(struct present_fence *present_fence); + +void +present_fence_set_triggered(struct present_fence *present_fence); + +XID +present_fence_id(struct present_fence *present_fence); + +/* + * present_notify.c + */ +void +present_clear_window_notifies(WindowPtr window); + +void +present_free_window_notify(present_notify_ptr notify); + +int +present_add_window_notify(present_notify_ptr notify); + +int +present_create_notifies(ClientPtr client, int num_notifies, xPresentNotify *x_notifies, present_notify_ptr *p_notifies); + +void +present_destroy_notifies(present_notify_ptr notifies, int num_notifies); + +/* + * present_request.c + */ +int +proc_present_dispatch(ClientPtr client); + +int +sproc_present_dispatch(ClientPtr client); + +/* + * present_screen.c + */ + +#endif /* _PRESENT_PRIV_H_ */ diff --git a/present/present_request.c b/present/present_request.c new file mode 100644 index 000000000..095fa2daf --- /dev/null +++ b/present/present_request.c @@ -0,0 +1,330 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" +#include "randrstr.h" + +static int +proc_present_query_version(ClientPtr client) +{ + REQUEST(xPresentQueryVersionReq); + xPresentQueryVersionReply rep = { + .type = X_Reply, + .sequenceNumber = client->sequence, + .length = 0, + .majorVersion = PRESENT_MAJOR, + .minorVersion = PRESENT_MINOR + }; + + REQUEST_SIZE_MATCH(xPresentQueryVersionReq); + (void) stuff; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.majorVersion); + swapl(&rep.minorVersion); + } + WriteToClient(client, sizeof(rep), &rep); + return Success; +} + +#define VERIFY_FENCE_OR_NONE(fence_ptr, fence_id, client, access) do { \ + if ((fence_id) == None) \ + (fence_ptr) = NULL; \ + else { \ + int __rc__ = SyncVerifyFence(&fence_ptr, fence_id, client, access); \ + if (__rc__ != Success) \ + return __rc__; \ + } \ + } while (0) + +#define VERIFY_CRTC_OR_NONE(crtc_ptr, crtc_id, client, access) do { \ + if ((crtc_id) == None) \ + (crtc_ptr) = NULL; \ + else { \ + VERIFY_RR_CRTC(crtc_id, crtc_ptr, access); \ + } \ + } while (0) + +static int +proc_present_pixmap(ClientPtr client) +{ + REQUEST(xPresentPixmapReq); + WindowPtr window; + PixmapPtr pixmap; + RegionPtr valid = NULL; + RegionPtr update = NULL; + SyncFence *wait_fence; + SyncFence *idle_fence; + RRCrtcPtr target_crtc; + int ret; + int nnotifies; + present_notify_ptr notifies = NULL; + + REQUEST_AT_LEAST_SIZE(xPresentPixmapReq); + ret = dixLookupWindow(&window, stuff->window, client, DixWriteAccess); + if (ret != Success) + return ret; + ret = dixLookupResourceByType((pointer *) &pixmap, stuff->pixmap, RT_PIXMAP, client, DixReadAccess); + if (ret != Success) + return ret; + + if (window->drawable.depth != pixmap->drawable.depth) + return BadMatch; + + VERIFY_REGION_OR_NONE(valid, stuff->valid, client, DixReadAccess); + VERIFY_REGION_OR_NONE(update, stuff->update, client, DixReadAccess); + + VERIFY_CRTC_OR_NONE(target_crtc, stuff->target_crtc, client, DixReadAccess); + + VERIFY_FENCE_OR_NONE(wait_fence, stuff->wait_fence, client, DixReadAccess); + VERIFY_FENCE_OR_NONE(idle_fence, stuff->idle_fence, client, DixWriteAccess); + + if (stuff->options & ~(PresentAllOptions)) { + client->errorValue = stuff->options; + return BadValue; + } + + /* + * Check to see if remainder is sane + */ + if (stuff->divisor == 0) { + if (stuff->remainder != 0) { + client->errorValue = (CARD32) stuff->remainder; + return BadValue; + } + } else { + if (stuff->remainder >= stuff->divisor) { + client->errorValue = (CARD32) stuff->remainder; + return BadValue; + } + } + + nnotifies = (client->req_len << 2) - sizeof (xPresentPixmapReq); + if (nnotifies % sizeof (xPresentNotify)) + return BadLength; + + nnotifies /= sizeof (xPresentNotify); + if (nnotifies) { + ret = present_create_notifies(client, nnotifies, (xPresentNotify *) (stuff + 1), ¬ifies); + if (ret != Success) + return ret; + } + + ret = present_pixmap(window, pixmap, stuff->serial, valid, update, + stuff->x_off, stuff->y_off, target_crtc, + wait_fence, idle_fence, stuff->options, + stuff->target_msc, stuff->divisor, stuff->remainder, notifies, nnotifies); + if (ret != Success) + present_destroy_notifies(notifies, nnotifies); + return ret; +} + +static int +proc_present_notify_msc(ClientPtr client) +{ + REQUEST(xPresentNotifyMSCReq); + WindowPtr window; + int rc; + + REQUEST_SIZE_MATCH(xPresentNotifyMSCReq); + rc = dixLookupWindow(&window, stuff->window, client, DixReadAccess); + if (rc != Success) + return rc; + + /* + * Check to see if remainder is sane + */ + if (stuff->divisor == 0) { + if (stuff->remainder != 0) { + client->errorValue = (CARD32) stuff->remainder; + return BadValue; + } + } else { + if (stuff->remainder >= stuff->divisor) { + client->errorValue = (CARD32) stuff->remainder; + return BadValue; + } + } + + return present_notify_msc(window, stuff->serial, + stuff->target_msc, stuff->divisor, stuff->remainder); +} + +static int +proc_present_select_input (ClientPtr client) +{ + REQUEST(xPresentSelectInputReq); + WindowPtr window; + int rc; + + REQUEST_SIZE_MATCH(xPresentSelectInputReq); + + LEGAL_NEW_RESOURCE(stuff->eid, client); + + rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); + if (rc != Success) + return rc; + + if (stuff->eventMask & ~PresentAllEvents) { + client->errorValue = stuff->eventMask; + return BadValue; + } + return present_select_input(client, stuff->eid, window, stuff->eventMask); +} + +static int +proc_present_query_capabilities (ClientPtr client) +{ + REQUEST(xPresentQueryCapabilitiesReq); + xPresentQueryCapabilitiesReply rep = { + .type = X_Reply, + .sequenceNumber = client->sequence, + .length = 0, + }; + WindowPtr window; + RRCrtcPtr crtc = NULL; + int r; + + r = dixLookupWindow(&window, stuff->target, client, DixGetAttrAccess); + switch (r) { + case Success: + crtc = present_get_crtc(window); + break; + case BadWindow: + VERIFY_RR_CRTC(stuff->target, crtc, DixGetAttrAccess); + break; + default: + return r; + } + + rep.capabilities = present_query_capabilities(crtc); + + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.capabilities); + } + WriteToClient(client, sizeof(rep), &rep); + return Success; +} + +int (*proc_present_vector[PresentNumberRequests]) (ClientPtr) = { + proc_present_query_version, /* 0 */ + proc_present_pixmap, /* 1 */ + proc_present_notify_msc, /* 2 */ + proc_present_select_input, /* 3 */ + proc_present_query_capabilities, /* 4 */ +}; + +int +proc_present_dispatch(ClientPtr client) +{ + REQUEST(xReq); + if (stuff->data >= PresentNumberRequests || !proc_present_vector[stuff->data]) + return BadRequest; + return (*proc_present_vector[stuff->data]) (client); +} + +static int +sproc_present_query_version(ClientPtr client) +{ + REQUEST(xPresentQueryVersionReq); + + swaps(&stuff->length); + swapl(&stuff->majorVersion); + swapl(&stuff->minorVersion); + return (*proc_present_vector[stuff->presentReqType]) (client); +} + +static int +sproc_present_pixmap(ClientPtr client) +{ + REQUEST(xPresentPixmapReq); + + swaps(&stuff->length); + swapl(&stuff->window); + swapl(&stuff->pixmap); + swapl(&stuff->valid); + swapl(&stuff->update); + swaps(&stuff->x_off); + swaps(&stuff->y_off); + swapll(&stuff->target_msc); + swapll(&stuff->divisor); + swapll(&stuff->remainder); + swapl(&stuff->idle_fence); + return (*proc_present_vector[stuff->presentReqType]) (client); +} + +static int +sproc_present_notify_msc(ClientPtr client) +{ + REQUEST(xPresentNotifyMSCReq); + + swaps(&stuff->length); + swapl(&stuff->window); + swapll(&stuff->target_msc); + swapll(&stuff->divisor); + swapll(&stuff->remainder); + return (*proc_present_vector[stuff->presentReqType]) (client); +} + +static int +sproc_present_select_input (ClientPtr client) +{ + REQUEST(xPresentSelectInputReq); + + swaps(&stuff->length); + swapl(&stuff->window); + swapl(&stuff->eventMask); + return (*proc_present_vector[stuff->presentReqType]) (client); +} + +static int +sproc_present_query_capabilities (ClientPtr client) +{ + REQUEST(xPresentQueryCapabilitiesReq); + swaps(&stuff->length); + swapl(&stuff->target); + return (*proc_present_vector[stuff->presentReqType]) (client); +} + +int (*sproc_present_vector[PresentNumberRequests]) (ClientPtr) = { + sproc_present_query_version, /* 0 */ + sproc_present_pixmap, /* 1 */ + sproc_present_notify_msc, /* 2 */ + sproc_present_select_input, /* 3 */ + sproc_present_query_capabilities, /* 4 */ +}; + +int +sproc_present_dispatch(ClientPtr client) +{ + REQUEST(xReq); + if (stuff->data >= PresentNumberRequests || !sproc_present_vector[stuff->data]) + return BadRequest; + return (*sproc_present_vector[stuff->data]) (client); +} diff --git a/present/present_screen.c b/present/present_screen.c new file mode 100644 index 000000000..50b2b2d23 --- /dev/null +++ b/present/present_screen.c @@ -0,0 +1,231 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_XORG_CONFIG_H +#include +#endif + +#include "present_priv.h" + +int present_request; +DevPrivateKeyRec present_screen_private_key; +DevPrivateKeyRec present_window_private_key; + +/* + * Get a pointer to a present window private, creating if necessary + */ +present_window_priv_ptr +present_get_window_priv(WindowPtr window, Bool create) +{ + present_window_priv_ptr window_priv = present_window_priv(window); + + if (!create || window_priv != NULL) + return window_priv; + window_priv = calloc (1, sizeof (present_window_priv_rec)); + if (!window_priv) + return NULL; + xorg_list_init(&window_priv->vblank); + xorg_list_init(&window_priv->notifies); + dixSetPrivate(&window->devPrivates, &present_window_private_key, window_priv); + return window_priv; +} + +/* + * Hook the close screen function to clean up our screen private + */ +static Bool +present_close_screen(ScreenPtr screen) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + present_flip_destroy(screen); + + unwrap(screen_priv, screen, CloseScreen); + (*screen->CloseScreen) (screen); + free(screen_priv); + return TRUE; +} + +/* + * Free any queued presentations for this window + */ +static void +present_free_window_vblank(WindowPtr window) +{ + present_window_priv_ptr window_priv = present_window_priv(window); + present_vblank_ptr vblank, tmp; + + xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) { + present_abort_vblank(window->drawable.pScreen, vblank->crtc, vblank->event_id, vblank->target_msc); + present_vblank_destroy(vblank); + } +} + +/* + * Clean up any pending or current flips for this window + */ +static void +present_clear_window_flip(WindowPtr window) +{ + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + present_vblank_ptr flip_pending = screen_priv->flip_pending; + + if (flip_pending && flip_pending->window == window) { + assert (flip_pending->abort_flip); + flip_pending->window_destroyed = TRUE; + } + if (screen_priv->flip_window == window) + screen_priv->flip_window = NULL; +} + +/* + * Hook the close window function to clean up our window private + */ +static Bool +present_destroy_window(WindowPtr window) +{ + Bool ret; + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + present_window_priv_ptr window_priv = present_window_priv(window); + + if (window_priv) { + present_clear_window_notifies(window); + present_free_events(window); + present_free_window_vblank(window); + present_clear_window_flip(window); + free(window_priv); + } + unwrap(screen_priv, screen, DestroyWindow); + if (screen->DestroyWindow) + ret = screen->DestroyWindow (window); + else + ret = TRUE; + wrap(screen_priv, screen, DestroyWindow, present_destroy_window); + return ret; +} + +/* + * Hook the config notify screen function to deliver present config notify events + */ +static int +present_config_notify(WindowPtr window, + int x, int y, int w, int h, int bw, + WindowPtr sibling) +{ + int ret; + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + present_send_config_notify(window, x, y, w, h, bw, sibling); + + unwrap(screen_priv, screen, ConfigNotify); + if (screen->ConfigNotify) + ret = screen->ConfigNotify (window, x, y, w, h, bw, sibling); + else + ret = 0; + wrap(screen_priv, screen, ConfigNotify, present_config_notify); + return ret; +} + +/* + * Hook the clip notify screen function to un-flip as necessary + */ + +static void +present_clip_notify(WindowPtr window, int dx, int dy) +{ + ScreenPtr screen = window->drawable.pScreen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + + present_check_flip_window(window); + unwrap(screen_priv, screen, ClipNotify) + if (screen->ClipNotify) + screen->ClipNotify (window, dx, dy); + wrap(screen_priv, screen, ClipNotify, present_clip_notify); +} + +/* + * Initialize a screen for use with present + */ +int +present_screen_init(ScreenPtr screen, present_screen_info_ptr info) +{ + if (!dixRegisterPrivateKey(&present_screen_private_key, PRIVATE_SCREEN, 0)) + return FALSE; + + if (!dixRegisterPrivateKey(&present_window_private_key, PRIVATE_WINDOW, 0)) + return FALSE; + + if (!present_screen_priv(screen)) { + present_screen_priv_ptr screen_priv = calloc(1, sizeof (present_screen_priv_rec)); + if (!screen_priv) + return FALSE; + + wrap(screen_priv, screen, CloseScreen, present_close_screen); + wrap(screen_priv, screen, DestroyWindow, present_destroy_window); + wrap(screen_priv, screen, ConfigNotify, present_config_notify); + wrap(screen_priv, screen, ClipNotify, present_clip_notify); + + screen_priv->info = info; + + dixSetPrivate(&screen->devPrivates, &present_screen_private_key, screen_priv); + + present_fake_screen_init(screen); + } + + return TRUE; +} + +/* + * Initialize the present extension + */ +void +present_extension_init(void) +{ + ExtensionEntry *extension; + int i; + + extension = AddExtension(PRESENT_NAME, PresentNumberEvents, PresentNumberErrors, + proc_present_dispatch, sproc_present_dispatch, + NULL, StandardMinorOpcode); + if (!extension) + goto bail; + + present_request = extension->base; + + if (!present_init()) + goto bail; + + if (!present_event_init()) + goto bail; + + for (i = 0; i < screenInfo.numScreens; i++) { + if (!present_screen_init(screenInfo.screens[i], NULL)) + goto bail; + } + return; + +bail: + FatalError("Cannot initialize Present extension"); +} diff --git a/present/presentext.h b/present/presentext.h new file mode 100644 index 000000000..f177f55dc --- /dev/null +++ b/present/presentext.h @@ -0,0 +1,29 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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. + */ + +#ifndef _PRESENTEXT_H_ +#define _PRESENTEXT_H_ + +extern _X_EXPORT void +present_extension_init(void); + +#endif /* _PRESENTEXT_H_ */