[PATCH 1/2] EXA: Extend mixed pixmaps scheme to allow driver PrepareAccess hook to fail.
Michel Dänzer
michel at daenzer.net
Tue Sep 22 10:54:05 PDT 2009
From: Michel Dänzer <daenzer at vmware.com>
If the PrepareAccess hook fails, use the DownloadFromScreen hook to retrieve
driver pixmap contents to a system RAM copy, perform software rendering on that
and copy the results back using the UploadToScreen hook. Use the classic
migration logic to minimize transfers (which as a bonus allows slightly
cleaning up some of the existing mixed pixmap code).
This enables things that weren't possible before with driver-allocated pixmap
storage: If some (or all) GPU pixmap storage can't be mapped directly by the
CPU, this can be handled between the PrepareAccess and
DownloadFrom/UploadToScreen hooks, e.g.:
* Radeon KMS on big endian machines can fail PrepareAccess if the pixmap
requires byte-swapping and swap bytes in DownloadFrom/UploadToScreen.
* Environments where GPU and CPU don't have a shared address space at all.
Here the driver PrepareAccess hook will always fail and leave all transfers
between GPU / CPU storage to the Download/From/UploadToScreen hooks.
Drivers which can handle all pixmaps in the PrepareAccess hook should notice
little if any difference.
---
exa/exa.c | 106 ++++++++++++++++++++++++++++++++++---------
exa/exa_migration_classic.c | 6 +-
exa/exa_migration_mixed.c | 91 ++++---------------------------------
exa/exa_mixed.c | 87 ++++++++++++++---------------------
exa/exa_priv.h | 6 +++
5 files changed, 138 insertions(+), 158 deletions(-)
diff --git a/exa/exa.c b/exa/exa.c
index 483e3b4..6a1029a 100644
--- a/exa/exa.c
+++ b/exa/exa.c
@@ -324,7 +324,7 @@ ExaDoPrepareAccess(DrawablePtr pDrawable, int index)
offscreen = exaPixmapIsOffscreen(pPixmap);
- if (offscreen)
+ if (offscreen && pExaPixmap->fb_ptr)
pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr;
else
pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
@@ -333,18 +333,8 @@ ExaDoPrepareAccess(DrawablePtr pDrawable, int index)
pExaScr->access[index].pixmap = pPixmap;
pExaScr->access[index].count = 1;
- if (!offscreen) {
- /* Do we need to allocate our system buffer? */
- if ((pExaScr->info->flags & EXA_HANDLES_PIXMAPS) && (pExaScr->info->flags & EXA_MIXED_PIXMAPS)) {
- if (!pExaPixmap->sys_ptr && !exaPixmapIsPinned(pPixmap)) {
- pExaPixmap->sys_ptr = malloc(pExaPixmap->sys_pitch * pDrawable->height);
- if (!pExaPixmap->sys_ptr)
- FatalError("EXA: malloc failed for size %d bytes\n", pExaPixmap->sys_pitch * pDrawable->height);
- pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
- }
- }
+ if (!offscreen)
return FALSE;
- }
exaWaitSync (pDrawable->pScreen);
@@ -360,7 +350,8 @@ ExaDoPrepareAccess(DrawablePtr pDrawable, int index)
}
if (!(*pExaScr->info->PrepareAccess) (pPixmap, index)) {
- if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED)
+ if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED &&
+ !(pExaScr->info->flags & EXA_MIXED_PIXMAPS))
FatalError("Driver failed PrepareAccess on a pinned pixmap.\n");
exaMoveOutPixmap (pPixmap);
@@ -374,11 +365,12 @@ void
exaPrepareAccessReg(DrawablePtr pDrawable, int index, RegionPtr pReg)
{
PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable);
- ExaScreenPriv(pPixmap->drawable.pScreen);
+ ScreenPtr pScreen = pDrawable->pScreen;
+ ExaScreenPriv(pScreen);
+ ExaMigrationRec pixmaps[1];
+ Bool access_prepared;
if (pExaScr->do_migration) {
- ExaMigrationRec pixmaps[1];
-
if (index == EXA_PREPARE_DEST || index == EXA_PREPARE_AUX_DEST) {
pixmaps[0].as_dst = TRUE;
pixmaps[0].as_src = FALSE;
@@ -388,11 +380,73 @@ exaPrepareAccessReg(DrawablePtr pDrawable, int index, RegionPtr pReg)
}
pixmaps[0].pPix = pPixmap;
pixmaps[0].pReg = pReg;
+ }
+ if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS))
exaDoMigration(pixmaps, 1, FALSE);
- }
- ExaDoPrepareAccess(pDrawable, index);
+ access_prepared = ExaDoPrepareAccess(pDrawable, index);
+
+ /* With mixed pixmaps, if we fail to get direct access to the driver pixmap,
+ * we use the DownloadFromScreen hook to retrieve contents to a copy in
+ * system memory, perform software rendering on that and move back the
+ * results with the UploadToScreen hook (see exaFinishAccess).
+ */
+ if (!access_prepared && (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) &&
+ (pExaScr->info->flags & EXA_MIXED_PIXMAPS)) {
+ Bool is_offscreen = exaPixmapIsOffscreen(pPixmap);
+ ExaPixmapPriv(pPixmap);
+
+ /* Do we need to allocate our system buffer? */
+ if (!pExaPixmap->sys_ptr) {
+ pExaPixmap->sys_ptr = malloc(pExaPixmap->sys_pitch *
+ pPixmap->drawable.height);
+ if (!pExaPixmap->sys_ptr)
+ FatalError("EXA: malloc failed for size %d bytes\n",
+ pExaPixmap->sys_pitch * pPixmap->drawable.height);
+ }
+
+ if (!pExaPixmap->pDamage && (is_offscreen || !exaPixmapIsPinned(pPixmap))) {
+ Bool as_dst = pixmaps[0].as_dst;
+
+ /* Set up damage tracking */
+ pExaPixmap->pDamage = DamageCreate (NULL, NULL,
+ DamageReportNone, TRUE,
+ pScreen, pPixmap);
+
+ DamageRegister (&pPixmap->drawable, pExaPixmap->pDamage);
+ /* This ensures that pending damage reflects the current operation. */
+ /* This is used by exa to optimize migration. */
+ DamageSetReportAfterOp (pExaPixmap->pDamage, TRUE);
+
+ if (is_offscreen) {
+ exaPixmapDirty(pPixmap, 0, 0, pPixmap->drawable.width,
+ pPixmap->drawable.height);
+
+ /* We don't know which region of the destination will be damaged,
+ * have to assume all of it
+ */
+ if (as_dst) {
+ pixmaps[0].as_dst = FALSE;
+ pixmaps[0].as_src = TRUE;
+ pixmaps[0].pReg = NULL;
+ }
+ pPixmap->devKind = pExaPixmap->fb_pitch;
+ exaCopyDirtyToSys(pixmaps);
+ }
+
+ if (as_dst)
+ exaPixmapDirty(pPixmap, 0, 0, pPixmap->drawable.width,
+ pPixmap->drawable.height);
+ } else if (is_offscreen) {
+ pPixmap->devKind = pExaPixmap->fb_pitch;
+ exaCopyDirtyToSys(pixmaps);
+ }
+
+ pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
+ pPixmap->devKind = pExaPixmap->sys_pitch;
+ pExaPixmap->offscreen = FALSE;
+ }
}
/**
@@ -419,6 +473,7 @@ exaFinishAccess(DrawablePtr pDrawable, int index)
ExaScreenPriv (pScreen);
PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable);
ExaPixmapPriv (pPixmap);
+ Bool is_offscreen;
int i;
if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS))
@@ -447,10 +502,19 @@ exaFinishAccess(DrawablePtr pDrawable, int index)
/* We always hide the devPrivate.ptr. */
pPixmap->devPrivate.ptr = NULL;
- if (pExaScr->info->FinishAccess == NULL)
- return;
+ is_offscreen = exaPixmapIsOffscreen(pPixmap);
+
+ /* Move back results of software rendering on system memory copy of mixed
+ * driver pixmap (see exaPrepareAccessReg).
+ */
+ if (pExaPixmap->pDamage && is_offscreen &&
+ (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) &&
+ (pExaScr->info->flags & EXA_MIXED_PIXMAPS)) {
+ DamageRegionProcessPending(&pPixmap->drawable);
+ exaMoveInPixmap_mixed(pPixmap);
+ }
- if (!exaPixmapIsOffscreen (pPixmap))
+ if (!is_offscreen || !pExaScr->info->FinishAccess)
return;
if (index >= EXA_PREPARE_AUX_DEST &&
diff --git a/exa/exa_migration_classic.c b/exa/exa_migration_classic.c
index d8e1e86..c58e0af 100644
--- a/exa/exa_migration_classic.c
+++ b/exa/exa_migration_classic.c
@@ -120,7 +120,7 @@ exaCopyDirty(ExaMigrationPtr migrate, RegionPtr pValidDst, RegionPtr pValidSrc,
Bool need_sync = FALSE;
/* Damaged bits are valid in current copy but invalid in other one */
- if (exaPixmapIsOffscreen(pPixmap)) {
+ if (pExaPixmap->offscreen) {
REGION_UNION(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB,
damage);
REGION_SUBTRACT(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys,
@@ -263,7 +263,7 @@ exaCopyDirty(ExaMigrationPtr migrate, RegionPtr pValidDst, RegionPtr pValidSrc,
* the framebuffer memory copy to the system memory copy. Both areas must be
* allocated.
*/
-static void
+void
exaCopyDirtyToSys (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
@@ -281,7 +281,7 @@ exaCopyDirtyToSys (ExaMigrationPtr migrate)
* the system memory copy to the framebuffer memory copy. Both areas must be
* allocated.
*/
-static void
+void
exaCopyDirtyToFb (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
diff --git a/exa/exa_migration_mixed.c b/exa/exa_migration_mixed.c
index d1ee987..a71a5ad 100644
--- a/exa/exa_migration_mixed.c
+++ b/exa/exa_migration_mixed.c
@@ -31,55 +31,16 @@
#include "exa_priv.h"
#include "exa.h"
-static void
-exaUploadFallback(PixmapPtr pPixmap, CARD8 *src, int src_pitch)
-{
- ExaPixmapPriv(pPixmap);
- RegionPtr damage = DamageRegion (pExaPixmap->pDamage);
- GCPtr pGC = GetScratchGC (pPixmap->drawable.depth,
- pPixmap->drawable.pScreen);
- int nbox, cpp = pPixmap->drawable.bitsPerPixel / 8;
- DamagePtr backup = pExaPixmap->pDamage;
- BoxPtr pbox;
- CARD8 *src2;
-
- /* We don't want damage optimisations. */
- pExaPixmap->pDamage = NULL;
- ValidateGC (&pPixmap->drawable, pGC);
-
- pbox = REGION_RECTS(damage);
- nbox = REGION_NUM_RECTS(damage);
-
- while (nbox--) {
- src2 = src + pbox->y1 * src_pitch + pbox->x1 * cpp;
-
- ExaCheckPutImage(&pPixmap->drawable, pGC,
- pPixmap->drawable.depth, pbox->x1, pbox->y1,
- pbox->x2 - pbox->x1, pbox->y2 - pbox->y1, 0,
- ZPixmap, (char*) src2);
-
- pbox++;
- }
-
- FreeScratchGC (pGC);
- pExaPixmap->pDamage = backup;
-}
-
void
exaCreateDriverPixmap_mixed(PixmapPtr pPixmap)
{
ScreenPtr pScreen = pPixmap->drawable.pScreen;
ExaScreenPriv(pScreen);
ExaPixmapPriv(pPixmap);
- RegionPtr damage = DamageRegion (pExaPixmap->pDamage);
- void *sys_buffer = pExaPixmap->sys_ptr;
int w = pPixmap->drawable.width, h = pPixmap->drawable.height;
int depth = pPixmap->drawable.depth, bpp = pPixmap->drawable.bitsPerPixel;
int usage_hint = pPixmap->usage_hint;
- int sys_pitch = pExaPixmap->sys_pitch;
- int paddedWidth = sys_pitch;
- int nbox;
- BoxPtr pbox;
+ int paddedWidth = pExaPixmap->sys_pitch;
/* Already done. */
if (pExaPixmap->driverPriv)
@@ -105,50 +66,8 @@ exaCreateDriverPixmap_mixed(PixmapPtr pPixmap)
if (!pExaPixmap->driverPriv)
return;
- pExaPixmap->offscreen = TRUE;
- pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr = NULL;
- pExaPixmap->sys_pitch = pPixmap->devKind = 0;
-
- pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED;
(*pScreen->ModifyPixmapHeader)(pPixmap, w, h, 0, 0,
paddedWidth, NULL);
-
- /* scratch pixmaps */
- if (!w || !h)
- goto finish;
-
- /* we do not malloc memory by default. */
- if (!sys_buffer)
- goto finish;
-
- if (!pExaScr->info->UploadToScreen)
- goto fallback;
-
- pbox = REGION_RECTS(damage);
- nbox = REGION_NUM_RECTS(damage);
-
- while (nbox--) {
- if (!pExaScr->info->UploadToScreen(pPixmap, pbox->x1, pbox->y1, pbox->x2 - pbox->x1,
- pbox->y2 - pbox->y1, (char *) (sys_buffer) + pbox->y1 * sys_pitch + pbox->x1 * (bpp / 8), sys_pitch))
- goto fallback;
-
- pbox++;
- }
-
- goto finish;
-
-fallback:
- exaUploadFallback(pPixmap, sys_buffer, sys_pitch);
-
-finish:
- free(sys_buffer);
-
- /* We no longer need this. */
- if (pExaPixmap->pDamage) {
- DamageUnregister(&pPixmap->drawable, pExaPixmap->pDamage);
- DamageDestroy(pExaPixmap->pDamage);
- pExaPixmap->pDamage = NULL;
- }
}
void
@@ -175,8 +94,16 @@ exaDoMigration_mixed(ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel)
for (i = 0; i < npixmaps; i++) {
PixmapPtr pPixmap = pixmaps[i].pPix;
ExaPixmapPriv(pPixmap);
+
if (!pExaPixmap->driverPriv)
exaCreateDriverPixmap_mixed(pPixmap);
+
+ if (pExaPixmap->pDamage && exaPixmapIsOffscreen(pPixmap)) {
+ pPixmap->devKind = pExaPixmap->fb_pitch;
+ exaCopyDirtyToFb(pixmaps + i);
+ }
+
+ pExaPixmap->offscreen = exaPixmapIsOffscreen(pPixmap);
}
}
diff --git a/exa/exa_mixed.c b/exa/exa_mixed.c
index 6aa73f2..167ffa9 100644
--- a/exa/exa_mixed.c
+++ b/exa/exa_mixed.c
@@ -32,8 +32,6 @@
#include "exa.h"
/* This file holds the driver allocated pixmaps + better initial placement code.
- * A pinned pixmap implies one that is either driver based already or otherwise altered.
- * Proper care is taken to free the initially allocated buffer.
*/
static _X_INLINE void*
@@ -46,9 +44,6 @@ ExaGetPixmapAddress(PixmapPtr p)
/**
* exaCreatePixmap() creates a new pixmap.
- *
- * Pixmaps are always marked as pinned, unless the pixmap can still be transfered to a
- * driver pixmaps.
*/
PixmapPtr
exaCreatePixmap_mixed(ScreenPtr pScreen, int w, int h, int depth,
@@ -85,7 +80,6 @@ exaCreatePixmap_mixed(ScreenPtr pScreen, int w, int h, int depth,
pExaPixmap->sys_pitch = paddedWidth;
pExaPixmap->area = NULL;
- pExaPixmap->offscreen = FALSE;
pExaPixmap->fb_ptr = NULL;
pExaPixmap->pDamage = NULL;
@@ -93,36 +87,15 @@ exaCreatePixmap_mixed(ScreenPtr pScreen, int w, int h, int depth,
exaSetAccelBlock(pExaScr, pExaPixmap,
w, h, bpp);
- /* Avoid freeing sys_ptr. */
- pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED;
-
(*pScreen->ModifyPixmapHeader)(pPixmap, w, h, 0, 0,
paddedWidth, NULL);
- /* We want to be able to transfer the pixmap to driver memory later on. */
- pExaPixmap->score = EXA_PIXMAP_SCORE_INIT;
-
/* A scratch pixmap will become a driver pixmap right away. */
if (!w || !h) {
exaCreateDriverPixmap_mixed(pPixmap);
- } else {
- /* Set up damage tracking */
- pExaPixmap->pDamage = DamageCreate (NULL, NULL,
- DamageReportNone, TRUE,
- pScreen, pPixmap);
-
- if (pExaPixmap->pDamage == NULL) {
- swap(pExaScr, pScreen, DestroyPixmap);
- pScreen->DestroyPixmap (pPixmap);
- swap(pExaScr, pScreen, DestroyPixmap);
- return NULL;
- }
-
- DamageRegister (&pPixmap->drawable, pExaPixmap->pDamage);
- /* This ensures that pending damage reflects the current operation. */
- /* This is used by exa to optimize migration. */
- DamageSetReportAfterOp (pExaPixmap->pDamage, TRUE);
- }
+ pExaPixmap->offscreen = exaPixmapIsOffscreen(pPixmap);
+ } else
+ pExaPixmap->offscreen = FALSE;
return pPixmap;
}
@@ -134,7 +107,7 @@ exaModifyPixmapHeader_mixed(PixmapPtr pPixmap, int width, int height, int depth,
ScreenPtr pScreen = pPixmap->drawable.pScreen;
ExaScreenPrivPtr pExaScr;
ExaPixmapPrivPtr pExaPixmap;
- Bool ret;
+ Bool ret, is_offscreen;
if (!pPixmap)
return FALSE;
@@ -142,26 +115,23 @@ exaModifyPixmapHeader_mixed(PixmapPtr pPixmap, int width, int height, int depth,
pExaScr = ExaGetScreenPriv(pScreen);
pExaPixmap = ExaGetPixmapPriv(pPixmap);
- if (pExaPixmap) {
- if (!exaPixmapIsPinned(pPixmap)) {
- free(pExaPixmap->sys_ptr);
- pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr = NULL;
- pExaPixmap->sys_pitch = pPixmap->devKind = 0;
-
- /* We no longer need this. */
+ if (pPixData) {
+ if (pExaPixmap->driverPriv) {
if (pExaPixmap->pDamage) {
DamageUnregister(&pPixmap->drawable, pExaPixmap->pDamage);
DamageDestroy(pExaPixmap->pDamage);
pExaPixmap->pDamage = NULL;
}
- }
- if (pPixData)
- pExaPixmap->sys_ptr = pPixData;
+ pExaScr->info->DestroyPixmap(pScreen, pExaPixmap->driverPriv);
+ pExaPixmap->driverPriv = NULL;
+ }
- if (devKind > 0)
- pExaPixmap->sys_pitch = devKind;
+ pExaPixmap->offscreen = FALSE;
+ pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED;
+ }
+ if (pExaPixmap->driverPriv) {
if (width > 0 && height > 0 && bitsPerPixel > 0) {
exaSetFbPitch(pExaScr, pExaPixmap,
width, height, bitsPerPixel);
@@ -169,9 +139,15 @@ exaModifyPixmapHeader_mixed(PixmapPtr pPixmap, int width, int height, int depth,
exaSetAccelBlock(pExaScr, pExaPixmap,
width, height, bitsPerPixel);
}
+ }
- /* Anything can happen, don't try to predict it all. */
- pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED;
+ is_offscreen = exaPixmapIsOffscreen(pPixmap);
+ if (is_offscreen) {
+ pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr;
+ pPixmap->devKind = pExaPixmap->fb_pitch;
+ } else {
+ pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
+ pPixmap->devKind = pExaPixmap->sys_pitch;
}
/* Only pass driver pixmaps to the driver. */
@@ -182,10 +158,6 @@ exaModifyPixmapHeader_mixed(PixmapPtr pPixmap, int width, int height, int depth,
* If pPixmap->devPrivate.ptr is non-NULL, then we've got a non-offscreen pixmap.
* We need to store the pointer, because PrepareAccess won't be called.
*/
- if (!pPixData && pPixmap->devPrivate.ptr && pPixmap->devKind) {
- pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr;
- pExaPixmap->sys_pitch = pPixmap->devKind;
- }
if (ret == TRUE)
goto out;
}
@@ -196,6 +168,13 @@ exaModifyPixmapHeader_mixed(PixmapPtr pPixmap, int width, int height, int depth,
swap(pExaScr, pScreen, ModifyPixmapHeader);
out:
+ if (is_offscreen) {
+ pExaPixmap->fb_ptr = pPixmap->devPrivate.ptr;
+ pExaPixmap->fb_pitch = pPixmap->devKind;
+ } else {
+ pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr;
+ pExaPixmap->sys_pitch = pPixmap->devKind;
+ }
/* Always NULL this, we don't want lingering pointers. */
pPixmap->devPrivate.ptr = NULL;
@@ -215,10 +194,14 @@ exaDestroyPixmap_mixed(PixmapPtr pPixmap)
if (pExaPixmap->driverPriv)
pExaScr->info->DestroyPixmap(pScreen, pExaPixmap->driverPriv);
- else if (pExaPixmap->sys_ptr && !exaPixmapIsPinned(pPixmap))
- free(pExaPixmap->sys_ptr);
pExaPixmap->driverPriv = NULL;
- pExaPixmap->sys_ptr = NULL;
+
+ if (pExaPixmap->pDamage) {
+ if (pExaPixmap->sys_ptr)
+ free(pExaPixmap->sys_ptr);
+ pExaPixmap->sys_ptr = NULL;
+ pExaPixmap->pDamage = NULL;
+ }
}
swap(pExaScr, pScreen, DestroyPixmap);
diff --git a/exa/exa_priv.h b/exa/exa_priv.h
index 869cf17..11a2e55 100644
--- a/exa/exa_priv.h
+++ b/exa/exa_priv.h
@@ -665,6 +665,12 @@ exaGlyphs (CARD8 op,
/* exa_migration_classic.c */
void
+exaCopyDirtyToSys (ExaMigrationPtr migrate);
+
+void
+exaCopyDirtyToFb (ExaMigrationPtr migrate);
+
+void
exaDoMigration_classic (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel);
void
--
1.6.4.3
More information about the xorg-devel
mailing list