summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/xfree86/drivers/modesetting/driver.c6
-rw-r--r--hw/xfree86/drivers/modesetting/driver.h6
-rw-r--r--hw/xfree86/drivers/modesetting/drmmode_display.c43
-rw-r--r--hw/xfree86/drivers/modesetting/drmmode_display.h8
-rw-r--r--hw/xfree86/drivers/modesetting/present.c429
5 files changed, 485 insertions, 7 deletions
diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index ae562a1e1..860333863 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -123,6 +123,7 @@ typedef enum {
OPTION_DEVICE_PATH,
OPTION_SHADOW_FB,
OPTION_ACCEL_METHOD,
+ OPTION_PAGEFLIP,
} modesettingOpts;
static const OptionInfoRec Options[] = {
@@ -130,6 +131,7 @@ static const OptionInfoRec Options[] = {
{OPTION_DEVICE_PATH, "kmsdev", OPTV_STRING, {0}, FALSE},
{OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE},
+ {OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
@@ -820,6 +822,8 @@ PreInit(ScrnInfoPtr pScrn, int flags)
try_enable_glamor(pScrn);
if (ms->drmmode.glamor) {
+ ms->drmmode.pageflip =
+ xf86ReturnOptValBool(ms->Options, OPTION_PAGEFLIP, TRUE);
} else {
Bool prefer_shadow = TRUE;
@@ -836,6 +840,8 @@ PreInit(ScrnInfoPtr pScrn, int flags)
"ShadowFB: preferred %s, enabled %s\n",
prefer_shadow ? "YES" : "NO",
ms->drmmode.shadow_enable ? "YES" : "NO");
+
+ ms->drmmode.pageflip = FALSE;
}
if (drmmode_pre_init(pScrn, &ms->drmmode, pScrn->bitsPerPixel / 8) == FALSE) {
diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h
index 843a105ed..9ae4966fd 100644
--- a/hw/xfree86/drivers/modesetting/driver.h
+++ b/hw/xfree86/drivers/modesetting/driver.h
@@ -101,6 +101,12 @@ typedef struct _modesettingRec {
drmEventContext event_context;
+ /**
+ * Page flipping stuff.
+ * @{
+ */
+ /** @} */
+
DamagePtr damage;
Bool dirty_enabled;
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 506ea2426..8dbac07f2 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -51,7 +51,8 @@
#include "driver.h"
static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
-static int
+
+int
drmmode_bo_destroy(drmmode_ptr drmmode, drmmode_bo *bo)
{
int ret;
@@ -72,7 +73,7 @@ drmmode_bo_destroy(drmmode_ptr drmmode, drmmode_bo *bo)
return 0;
}
-static uint32_t
+uint32_t
drmmode_bo_get_pitch(drmmode_bo *bo)
{
#ifdef GLAMOR_HAS_GBM
@@ -143,6 +144,35 @@ drmmode_create_bo(drmmode_ptr drmmode, drmmode_bo *bo,
}
Bool
+drmmode_bo_for_pixmap(drmmode_ptr drmmode, drmmode_bo *bo, PixmapPtr pixmap)
+{
+ ScreenPtr screen = xf86ScrnToScreen(drmmode->scrn);
+ uint16_t pitch;
+ uint32_t size;
+ int fd;
+
+#ifdef GLAMOR_HAS_GBM
+ if (drmmode->glamor) {
+ bo->gbm = glamor_gbm_bo_from_pixmap(screen, pixmap);
+ bo->dumb = NULL;
+ return bo->gbm != NULL;
+ }
+#endif
+
+ fd = glamor_fd_from_pixmap(screen, pixmap, &pitch, &size);
+ if (fd < 0) {
+ xf86DrvMsg(drmmode->scrn->scrnIndex, X_ERROR,
+ "Failed to get fd for flip to new front.\n");
+ return FALSE;
+ }
+
+ bo->dumb = dumb_get_bo_from_fd(drmmode->fd, fd, pitch, size);
+ close(fd);
+
+ return bo->dumb != NULL;
+}
+
+Bool
drmmode_SetSlaveBO(PixmapPtr ppix,
drmmode_ptr drmmode, int fd_handle, int pitch, int size)
{
@@ -368,6 +398,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
if (crtc->scrn->pScreen)
xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
+ drmmode_crtc->need_modeset = FALSE;
crtc->funcs->dpms(crtc, DPMSModeOn);
/* go through all the outputs and force DPMS them back on? */
@@ -1003,6 +1034,7 @@ static void
drmmode_output_dpms(xf86OutputPtr output, int mode)
{
drmmode_output_private_ptr drmmode_output = output->driver_private;
+ xf86CrtcPtr crtc = output->crtc;
drmModeConnectorPtr koutput = drmmode_output->mode_output;
drmmode_ptr drmmode = drmmode_output->drmmode;
@@ -1011,6 +1043,13 @@ drmmode_output_dpms(xf86OutputPtr output, int mode)
drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
drmmode_output->dpms_enum_id, mode);
+
+ if (mode == DPMSModeOn && crtc) {
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ if (drmmode_crtc->need_modeset)
+ drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
+ crtc->x, crtc->y);
+ }
return;
}
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index 85a0ec435..fe363c577 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -46,7 +46,6 @@ typedef struct {
typedef struct {
int fd;
unsigned fb_id;
- unsigned old_fb_id;
drmModeFBPtr mode_fb;
int cpp;
ScrnInfoPtr scrn;
@@ -63,6 +62,8 @@ typedef struct {
Bool glamor;
Bool shadow_enable;
+ /** Is Option "PageFlip" enabled? */
+ Bool pageflip;
void *shadow_fb;
/**
@@ -107,6 +108,8 @@ typedef struct {
uint32_t msc_prev;
uint64_t msc_high;
/** @} */
+
+ Bool need_modeset;
} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
typedef struct {
@@ -141,6 +144,9 @@ extern DevPrivateKeyRec msPixmapPrivateKeyRec;
#define msGetPixmapPriv(drmmode, p) ((msPixmapPrivPtr)dixGetPrivateAddr(&(p)->devPrivates, &(drmmode)->pixmapPrivateKeyRec))
+Bool drmmode_bo_for_pixmap(drmmode_ptr drmmode, drmmode_bo *bo, PixmapPtr pixmap);
+int drmmode_bo_destroy(drmmode_ptr drmmode, drmmode_bo *bo);
+uint32_t drmmode_bo_get_pitch(drmmode_bo *bo);
uint32_t drmmode_bo_get_handle(drmmode_bo *bo);
Bool drmmode_glamor_handle_new_screen_pixmap(drmmode_ptr drmmode);
void *drmmode_map_slave_bo(drmmode_ptr drmmode, msPixmapPrivPtr ppriv);
diff --git a/hw/xfree86/drivers/modesetting/present.c b/hw/xfree86/drivers/modesetting/present.c
index 43df148d1..090539861 100644
--- a/hw/xfree86/drivers/modesetting/present.c
+++ b/hw/xfree86/drivers/modesetting/present.c
@@ -44,6 +44,7 @@
#include <present.h>
#include "driver.h"
+#include "drmmode_display.h"
#if 0
#define DebugPresent(x) ErrorF x
@@ -174,8 +175,10 @@ ms_present_queue_vblank(RRCrtcPtr crtc,
/* If we hit EBUSY, then try to flush events. If we can't, then
* this is an error.
*/
- if (errno != EBUSY || ms_flush_drm_events(screen) < 0)
+ if (errno != EBUSY || ms_flush_drm_events(screen) < 0) {
+ ms_drm_abort_seq(scrn, seq);
return BadAlloc;
+ }
}
DebugPresent(("\t\tmq %lld seq %u msc %llu (hw msc %u)\n",
(long long) event_id, seq, (long long) msc,
@@ -221,6 +224,413 @@ ms_present_flush(WindowPtr window)
#endif
}
+#ifdef GLAMOR
+
+/*
+ * Event data for an in progress flip.
+ * This contains a pointer to the vblank event,
+ * and information about the flip in progress.
+ * a reference to this is stored in the per-crtc
+ * flips.
+ */
+struct ms_flipdata {
+ ScreenPtr screen;
+ struct ms_present_vblank_event *event;
+ /* number of CRTC events referencing this */
+ int flip_count;
+ uint64_t fe_msc;
+ uint64_t fe_usec;
+ uint32_t old_fb_id;
+};
+
+/*
+ * Per crtc pageflipping infomation,
+ * These are submitted to the queuing code
+ * one of them per crtc per flip.
+ */
+struct ms_crtc_pageflip {
+ Bool on_reference_crtc;
+ /* reference to the ms_flipdata */
+ struct ms_flipdata *flipdata;
+};
+
+/**
+ * Free an ms_crtc_pageflip.
+ *
+ * Drops the reference count on the flipdata.
+ */
+static void
+ms_present_flip_free(struct ms_crtc_pageflip *flip)
+{
+ struct ms_flipdata *flipdata = flip->flipdata;
+
+ free(flip);
+ if (--flipdata->flip_count > 0)
+ return;
+ free(flipdata);
+}
+
+/**
+ * Callback for the DRM event queue when a single flip has completed
+ *
+ * Once the flip has been completed on all pipes, notify the
+ * extension code telling it when that happened
+ */
+static void
+ms_flip_handler(uint64_t msc, uint64_t ust, void *data)
+{
+ struct ms_crtc_pageflip *flip = data;
+ ScreenPtr screen = flip->flipdata->screen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ struct ms_flipdata *flipdata = flip->flipdata;
+
+ DebugPresent(("\t\tms:fh %lld c %d msc %llu ust %llu\n",
+ (long long) flipdata->event->event_id,
+ flipdata->flip_count,
+ (long long) msc, (long long) ust));
+
+ if (flip->on_reference_crtc) {
+ flipdata->fe_msc = msc;
+ flipdata->fe_usec = ust;
+ }
+
+ if (flipdata->flip_count == 1) {
+ DebugPresent(("\t\tms:fc %lld c %d msc %llu ust %llu\n",
+ (long long) flipdata->event->event_id,
+ flipdata->flip_count,
+ (long long) flipdata->fe_msc, (long long) flipdata->fe_usec));
+
+
+ ms_present_vblank_handler(flipdata->fe_msc,
+ flipdata->fe_usec,
+ flipdata->event);
+
+ drmModeRmFB(ms->fd, flipdata->old_fb_id);
+ }
+ ms_present_flip_free(flip);
+}
+
+/*
+ * Callback for the DRM queue abort code. A flip has been aborted.
+ */
+static void
+ms_present_flip_abort(void *data)
+{
+ struct ms_crtc_pageflip *flip = data;
+ struct ms_flipdata *flipdata = flip->flipdata;
+
+ DebugPresent(("\t\tms:fa %lld c %d\n", (long long) flipdata->event->event_id, flipdata->flip_count));
+
+ if (flipdata->flip_count == 1)
+ free(flipdata->event);
+
+ ms_present_flip_free(flip);
+}
+
+static Bool
+queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
+ struct ms_flipdata *flipdata,
+ int ref_crtc_vblank_pipe, uint32_t flags)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ struct ms_crtc_pageflip *flip;
+ uint32_t seq;
+ int err;
+
+ flip = calloc(1, sizeof(struct ms_crtc_pageflip));
+ if (flip == NULL) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "flip queue: carrier alloc failed.\n");
+ return FALSE;
+ }
+
+ /* Only the reference crtc will finally deliver its page flip
+ * completion event. All other crtc's events will be discarded.
+ */
+ flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
+ flip->flipdata = flipdata;
+
+ seq = ms_drm_queue_alloc(crtc, flip, ms_flip_handler, ms_present_flip_abort);
+ if (!seq) {
+ free(flip);
+ return FALSE;
+ }
+
+ DebugPresent(("\t\tms:fq %lld c %d -> %d seq %llu\n",
+ (long long) flipdata->event->event_id,
+ flipdata->flip_count, flipdata->flip_count + 1,
+ (long long) seq));
+
+ /* take a reference on flipdata for use in flip */
+ flipdata->flip_count++;
+
+ while (drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
+ ms->drmmode.fb_id, flags, (void *) (uintptr_t) seq)) {
+ err = errno;
+ /* We may have failed because the event queue was full. Flush it
+ * and retry. If there was nothing to flush, then we failed for
+ * some other reason and should just return an error.
+ */
+ if (ms_flush_drm_events(screen) <= 0) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "flip queue failed: %s\n", strerror(err));
+ /* Aborting will also decrement flip_count and free(flip). */
+ ms_drm_abort_seq(scrn, seq);
+ return FALSE;
+ }
+
+ /* We flushed some events, so try again. */
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
+ }
+
+ /* The page flip succeded. */
+ return TRUE;
+}
+
+
+static Bool
+ms_do_pageflip(ScreenPtr screen,
+ PixmapPtr new_front,
+ struct ms_present_vblank_event *event,
+ int ref_crtc_vblank_pipe,
+ Bool async)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+ drmmode_bo new_front_bo;
+ uint32_t flags;
+ int i;
+ struct ms_flipdata *flipdata;
+ glamor_block_handler(screen);
+
+ new_front_bo.gbm = glamor_gbm_bo_from_pixmap(screen, new_front);
+ new_front_bo.dumb = NULL;
+ if (!new_front_bo.gbm) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to get GBM bo for flip to new front.\n");
+ return FALSE;
+ }
+
+ flipdata = calloc(1, sizeof(struct ms_flipdata));
+ if (!flipdata) {
+ drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to allocate flipdata.\n");
+ return FALSE;
+ }
+
+ flipdata->event = event;
+ flipdata->screen = screen;
+
+ /*
+ * Take a local reference on flipdata.
+ * if the first flip fails, the sequence abort
+ * code will free the crtc flip data, and drop
+ * it's reference which would cause this to be
+ * freed when we still required it.
+ */
+ flipdata->flip_count++;
+
+ /* Create a new handle for the back buffer */
+ flipdata->old_fb_id = ms->drmmode.fb_id;
+ if (drmModeAddFB(ms->fd, scrn->virtualX, scrn->virtualY,
+ scrn->depth, scrn->bitsPerPixel,
+ drmmode_bo_get_pitch(&new_front_bo),
+ drmmode_bo_get_handle(&new_front_bo), &ms->drmmode.fb_id)) {
+ goto error_out;
+ }
+
+ drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
+
+ flags = DRM_MODE_PAGE_FLIP_EVENT;
+ if (async)
+ flags |= DRM_MODE_PAGE_FLIP_ASYNC;
+
+ /* Queue flips on all enabled CRTCs.
+ *
+ * Note that if/when we get per-CRTC buffers, we'll have to update this.
+ * Right now it assumes a single shared fb across all CRTCs, with the
+ * kernel fixing up the offset of each CRTC as necessary.
+ *
+ * Also, flips queued on disabled or incorrectly configured displays
+ * may never complete; this is a configuration error.
+ */
+ for (i = 0; i < config->num_crtc; i++) {
+ xf86CrtcPtr crtc = config->crtc[i];
+
+ if (!ms_crtc_on(crtc))
+ continue;
+
+ if (!queue_flip_on_crtc(screen, crtc, flipdata,
+ ref_crtc_vblank_pipe,
+ flags)) {
+ goto error_undo;
+ }
+ }
+
+ /*
+ * Do we have more than our local reference,
+ * if so and no errors, then drop our local
+ * reference and return now.
+ */
+ if (flipdata->flip_count > 1) {
+ flipdata->flip_count--;
+ return TRUE;
+ }
+
+error_undo:
+
+ /*
+ * Have we just got the local reference?
+ * free the framebuffer if so since nobody successfully
+ * submitted anything
+ */
+ if (flipdata->flip_count == 1) {
+ drmModeRmFB(ms->fd, ms->drmmode.fb_id);
+ ms->drmmode.fb_id = flipdata->old_fb_id;
+ }
+
+error_out:
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
+ strerror(errno));
+ /* if only the local reference - free the structure,
+ * else drop the local reference and return */
+ if (flipdata->flip_count == 1)
+ free(flipdata);
+ else
+ flipdata->flip_count--;
+
+ return FALSE;
+}
+
+/*
+ * Test to see if page flipping is possible on the target crtc
+ */
+static Bool
+ms_present_check_flip(RRCrtcPtr crtc,
+ WindowPtr window,
+ PixmapPtr pixmap,
+ Bool sync_flip)
+{
+ ScreenPtr screen = window->drawable.pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+ int num_crtcs_on = 0;
+ int i;
+
+ if (!ms->drmmode.pageflip)
+ return FALSE;
+
+ if (!scrn->vtSema)
+ return FALSE;
+
+ for (i = 0; i < config->num_crtc; i++) {
+ drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private;
+
+ /* Don't do pageflipping if CRTCs are rotated. */
+ if (drmmode_crtc->rotate_bo.gbm)
+ return FALSE;
+
+ if (ms_crtc_on(config->crtc[i]))
+ num_crtcs_on++;
+ }
+
+ /* We can't do pageflipping if all the CRTCs are off. */
+ if (num_crtcs_on == 0)
+ return FALSE;
+
+ /* Check stride, can't change that on flip */
+ if (pixmap->devKind != drmmode_bo_get_pitch(&ms->drmmode.front_bo))
+ return FALSE;
+
+ /* Make sure there's a bo we can get to */
+ /* XXX: actually do this. also...is it sufficient?
+ * if (!glamor_get_pixmap_private(pixmap))
+ * return FALSE;
+ */
+
+ return TRUE;
+}
+
+/*
+ * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true,
+ * then wait for vblank. Otherwise, flip immediately
+ */
+static Bool
+ms_present_flip(RRCrtcPtr crtc,
+ uint64_t event_id,
+ uint64_t target_msc,
+ PixmapPtr pixmap,
+ Bool sync_flip)
+{
+ ScreenPtr screen = crtc->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+ drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
+ Bool ret;
+ struct ms_present_vblank_event *event;
+
+ if (!ms_present_check_flip(crtc, screen->root, pixmap, sync_flip))
+ return FALSE;
+
+ event = calloc(1, sizeof(struct ms_present_vblank_event));
+ if (!event)
+ return FALSE;
+
+ event->event_id = event_id;
+ ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip);
+ if (!ret)
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR, "present flip failed\n");
+
+ return ret;
+}
+
+/*
+ * Queue a flip back to the normal frame buffer
+ */
+static void
+ms_present_unflip(ScreenPtr screen, uint64_t event_id)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ PixmapPtr pixmap = screen->GetScreenPixmap(screen);
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+ int i;
+ struct ms_present_vblank_event *event;
+
+ event = calloc(1, sizeof(struct ms_present_vblank_event));
+ if (!event)
+ return;
+
+ event->event_id = event_id;
+
+ if (ms_present_check_flip(NULL, screen->root, pixmap, TRUE) &&
+ ms_do_pageflip(screen, pixmap, event, -1, FALSE)) {
+ return;
+ }
+
+ for (i = 0; i < config->num_crtc; i++) {
+ xf86CrtcPtr crtc = config->crtc[i];
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+ if (!crtc->enabled)
+ continue;
+
+ if (drmmode_crtc->dpms_mode == DPMSModeOn)
+ crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
+ crtc->x, crtc->y);
+ else
+ drmmode_crtc->need_modeset = TRUE;
+ }
+
+ present_event_notify(event_id, 0, 0);
+}
+#endif
+
static present_screen_info_rec ms_present_screen_info = {
.version = PRESENT_SCREEN_INFO_VERSION,
@@ -231,13 +641,24 @@ static present_screen_info_rec ms_present_screen_info = {
.flush = ms_present_flush,
.capabilities = PresentCapabilityNone,
- .check_flip = 0,
- .flip = 0,
- .unflip = 0,
+ .check_flip = ms_present_check_flip,
+#ifdef GLAMOR
+ .flip = ms_present_flip,
+ .unflip = ms_present_unflip,
+#endif
};
Bool
ms_present_screen_init(ScreenPtr screen)
{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ uint64_t value;
+ int ret;
+
+ ret = drmGetCap(ms->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+ if (ret == 0 && value == 1)
+ ms_present_screen_info.capabilities |= PresentCapabilityAsync;
+
return present_screen_init(screen, &ms_present_screen_info);
}