diff options
Diffstat (limited to 'exa/exa_migration.c')
-rw-r--r-- | exa/exa_migration.c | 374 |
1 files changed, 209 insertions, 165 deletions
diff --git a/exa/exa_migration.c b/exa/exa_migration.c index 70d8e1235..d69526b7f 100644 --- a/exa/exa_migration.c +++ b/exa/exa_migration.c @@ -90,7 +90,8 @@ exaPixmapIsDirty (PixmapPtr pPix) ExaPixmapPriv (pPix); return pExaPixmap == NULL || - REGION_NOTEMPTY (pScreen, DamageRegion(pExaPixmap->pDamage)); + REGION_NOTEMPTY (pScreen, DamageRegion(pExaPixmap->pDamage)) || + !REGION_EQUAL(pScreen, &pExaPixmap->validSys, &pExaPixmap->validFB); } /** @@ -113,92 +114,71 @@ exaPixmapShouldBeInFB (PixmapPtr pPix) /** * If the pixmap is currently dirty, this copies at least the dirty area from - * the framebuffer memory copy to the system memory copy. Both areas must be - * allocated. + * FB to system or vice versa. Both areas must be allocated. */ -static void -exaCopyDirtyToSys (PixmapPtr pPixmap) +static _X_INLINE void +exaCopyDirty(ExaMigrationPtr migrate, RegionPtr pValidDst, RegionPtr pValidSrc, + Bool (*transfer) (PixmapPtr pPix, int x, int y, int w, int h, + char *sys, int sys_pitch), CARD8 *fallback_src, + CARD8 *fallback_dst, int fallback_srcpitch, int fallback_dstpitch, + int fallback_index, void (*sync) (ScreenPtr pScreen)) { - ExaScreenPriv (pPixmap->drawable.pScreen); + PixmapPtr pPixmap = migrate->pPix; ExaPixmapPriv (pPixmap); - RegionPtr pRegion = DamageRegion (pExaPixmap->pDamage); - CARD8 *save_ptr; + RegionPtr damage = DamageRegion (pExaPixmap->pDamage); + RegionRec CopyReg; + Bool save_offscreen; int save_pitch; - BoxPtr pBox = REGION_RECTS(pRegion); - int nbox = REGION_NUM_RECTS(pRegion); - Bool do_sync = FALSE; + BoxPtr pBox; + int nbox; + Bool access_prepared = FALSE; - save_ptr = pPixmap->devPrivate.ptr; - save_pitch = pPixmap->devKind; - pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; - pPixmap->devKind = pExaPixmap->fb_pitch; + /* Damaged bits are valid in current copy but invalid in other one */ + if (exaPixmapIsOffscreen(pPixmap)) { + REGION_UNION(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB, + damage); + REGION_SUBTRACT(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys, + damage); + } else { + REGION_UNION(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys, + damage); + REGION_SUBTRACT(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB, + damage); + } - while (nbox--) { - pBox->x1 = max(pBox->x1, 0); - pBox->y1 = max(pBox->y1, 0); - pBox->x2 = min(pBox->x2, pPixmap->drawable.width); - pBox->y2 = min(pBox->y2, pPixmap->drawable.height); + REGION_EMPTY(pScreen, damage); - if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2) - continue; + /* Copy bits valid in source but not in destination */ + REGION_NULL(pScreen, &CopyReg); + REGION_SUBTRACT(pScreen, &CopyReg, pValidSrc, pValidDst); - if (pExaScr->info->DownloadFromScreen == NULL || - !pExaScr->info->DownloadFromScreen (pPixmap, - pBox->x1, pBox->y1, - pBox->x2 - pBox->x1, - pBox->y2 - pBox->y1, - pExaPixmap->sys_ptr - + pBox->y1 * pExaPixmap->sys_pitch - + pBox->x1 * pPixmap->drawable.bitsPerPixel / 8, - pExaPixmap->sys_pitch)) - { - exaPrepareAccess(&pPixmap->drawable, EXA_PREPARE_SRC); - exaMemcpyBox (pPixmap, pBox, - pExaPixmap->fb_ptr, pExaPixmap->fb_pitch, - pExaPixmap->sys_ptr, pExaPixmap->sys_pitch); - exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_SRC); - } - else - do_sync = TRUE; + if (migrate->as_dst) { + RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - pBox++; - } + if (REGION_NIL(pending_damage)) { + static Bool firsttime = TRUE; - /* Make sure the bits have actually landed, since we don't necessarily sync - * when accessing pixmaps in system memory. - */ - if (do_sync) - exaWaitSync (pPixmap->drawable.pScreen); + if (firsttime) { + ErrorF("%s: Pending damage region empty!\n", __func__); + firsttime = FALSE; + } + } - pPixmap->devPrivate.ptr = save_ptr; - pPixmap->devKind = save_pitch; + REGION_INTERSECT(pScreen, &CopyReg, &CopyReg, pending_damage); - /* The previously damaged bits are now no longer damaged but valid */ - REGION_UNION(pPixmap->drawable.pScreen, - &pExaPixmap->validReg, &pExaPixmap->validReg, pRegion); - DamageEmpty (pExaPixmap->pDamage); -} + if (migrate->pReg) + REGION_SUBTRACT(pScreen, &CopyReg, &CopyReg, migrate->pReg); + } else { + if (migrate->pReg) + REGION_INTERSECT(pScreen, &CopyReg, &CopyReg, migrate->pReg); + } -/** - * If the pixmap is currently dirty, this copies at least the dirty area from - * the system memory copy to the framebuffer memory copy. Both areas must be - * allocated. - */ -static void -exaCopyDirtyToFb (PixmapPtr pPixmap) -{ - ExaScreenPriv (pPixmap->drawable.pScreen); - ExaPixmapPriv (pPixmap); - RegionPtr pRegion = DamageRegion (pExaPixmap->pDamage); - CARD8 *save_ptr; - int save_pitch; - BoxPtr pBox = REGION_RECTS(pRegion); - int nbox = REGION_NUM_RECTS(pRegion); - Bool do_sync = FALSE; + pBox = REGION_RECTS(&CopyReg); + nbox = REGION_NUM_RECTS(&CopyReg); - save_ptr = pPixmap->devPrivate.ptr; + save_offscreen = pExaPixmap->offscreen; save_pitch = pPixmap->devKind; - pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; + pExaPixmap->offscreen = TRUE; pPixmap->devKind = pExaPixmap->fb_pitch; while (nbox--) { @@ -210,72 +190,75 @@ exaCopyDirtyToFb (PixmapPtr pPixmap) if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2) continue; - if (pExaScr->info->UploadToScreen == NULL || - !pExaScr->info->UploadToScreen (pPixmap, - pBox->x1, pBox->y1, - pBox->x2 - pBox->x1, - pBox->y2 - pBox->y1, - pExaPixmap->sys_ptr - + pBox->y1 * pExaPixmap->sys_pitch - + pBox->x1 * pPixmap->drawable.bitsPerPixel / 8, - pExaPixmap->sys_pitch)) + if (!transfer || !transfer (pPixmap, + pBox->x1, pBox->y1, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1, + pExaPixmap->sys_ptr + + pBox->y1 * pExaPixmap->sys_pitch + + pBox->x1 * pPixmap->drawable.bitsPerPixel / 8, + pExaPixmap->sys_pitch)) { - exaPrepareAccess(&pPixmap->drawable, EXA_PREPARE_DEST); + if (!access_prepared) { + ExaDoPrepareAccess(&pPixmap->drawable, fallback_index); + access_prepared = TRUE; + } exaMemcpyBox (pPixmap, pBox, - pExaPixmap->sys_ptr, pExaPixmap->sys_pitch, - pExaPixmap->fb_ptr, pExaPixmap->fb_pitch); - exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_DEST); + fallback_src, fallback_srcpitch, + fallback_dst, fallback_dstpitch); } - else - do_sync = TRUE; pBox++; } - if (do_sync) - exaMarkSync (pPixmap->drawable.pScreen); + if (access_prepared) + exaFinishAccess(&pPixmap->drawable, fallback_index); + else + sync (pPixmap->drawable.pScreen); - pPixmap->devPrivate.ptr = save_ptr; + pExaPixmap->offscreen = save_offscreen; pPixmap->devKind = save_pitch; - /* The previously damaged bits are now no longer damaged but valid */ - REGION_UNION(pPixmap->drawable.pScreen, - &pExaPixmap->validReg, &pExaPixmap->validReg, pRegion); - DamageEmpty (pExaPixmap->pDamage); + /* The copied bits are now valid in destination */ + REGION_UNION(pScreen, pValidDst, pValidDst, &CopyReg); + + REGION_UNINIT(pScreen, &CopyReg); } /** - * Copies out important pixmap data and removes references to framebuffer area. - * Called when the memory manager decides it's time to kick the pixmap out of - * framebuffer entirely. + * If the pixmap is currently dirty, this copies at least the dirty area from + * the framebuffer memory copy to the system memory copy. Both areas must be + * allocated. */ -void -exaPixmapSave (ScreenPtr pScreen, ExaOffscreenArea *area) +static void +exaCopyDirtyToSys (ExaMigrationPtr migrate) { - PixmapPtr pPixmap = area->privData; - ExaPixmapPriv(pPixmap); - RegionPtr pDamageReg = DamageRegion(pExaPixmap->pDamage); - - DBG_MIGRATE (("Save %p (%p) (%dx%d) (%c)\n", pPixmap, - (void*)(ExaGetPixmapPriv(pPixmap)->area ? - ExaGetPixmapPriv(pPixmap)->area->offset : 0), - pPixmap->drawable.width, - pPixmap->drawable.height, - exaPixmapIsDirty(pPixmap) ? 'd' : 'c')); + PixmapPtr pPixmap = migrate->pPix; + ExaScreenPriv (pPixmap->drawable.pScreen); + ExaPixmapPriv (pPixmap); - if (exaPixmapIsOffscreen(pPixmap)) { - exaCopyDirtyToSys (pPixmap); - pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; - pPixmap->devKind = pExaPixmap->sys_pitch; - pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; - } + exaCopyDirty(migrate, &pExaPixmap->validSys, &pExaPixmap->validFB, + pExaScr->info->DownloadFromScreen, pExaPixmap->fb_ptr, + pExaPixmap->sys_ptr, pExaPixmap->fb_pitch, + pExaPixmap->sys_pitch, EXA_PREPARE_SRC, exaWaitSync); +} - pExaPixmap->fb_ptr = NULL; - pExaPixmap->area = NULL; +/** + * If the pixmap is currently dirty, this copies at least the dirty area from + * the system memory copy to the framebuffer memory copy. Both areas must be + * allocated. + */ +static void +exaCopyDirtyToFb (ExaMigrationPtr migrate) +{ + PixmapPtr pPixmap = migrate->pPix; + ExaScreenPriv (pPixmap->drawable.pScreen); + ExaPixmapPriv (pPixmap); - /* Mark all valid bits as damaged, so they'll get copied to FB next time */ - REGION_UNION(pPixmap->drawable.pScreen, pDamageReg, pDamageReg, - &pExaPixmap->validReg); + exaCopyDirty(migrate, &pExaPixmap->validFB, &pExaPixmap->validSys, + pExaScr->info->UploadToScreen, pExaPixmap->sys_ptr, + pExaPixmap->fb_ptr, pExaPixmap->sys_pitch, + pExaPixmap->fb_pitch, EXA_PREPARE_DEST, exaMarkSync); } /** @@ -293,10 +276,11 @@ exaPixmapSave (ScreenPtr pScreen, ExaOffscreenArea *area) * we mark the pixmap dirty, so that the next exaMoveInPixmap will actually move * all the data, since it's almost surely all valid now. */ -void -exaMoveInPixmap (PixmapPtr pPixmap) +static void +exaDoMoveInPixmap (ExaMigrationPtr migrate) { - ScreenPtr pScreen = pPixmap->drawable.pScreen; + PixmapPtr pPixmap = migrate->pPix; + ScreenPtr pScreen = pPixmap->drawable.pScreen; ExaScreenPriv (pScreen); ExaPixmapPriv (pPixmap); @@ -304,10 +288,6 @@ exaMoveInPixmap (PixmapPtr pPixmap) if (pExaScr->swappedOut) return; - /* If we're already in FB, our work is done. */ - if (exaPixmapIsOffscreen(pPixmap)) - return; - /* If we're not allowed to move, then fail. */ if (exaPixmapIsPinned(pPixmap)) return; @@ -319,6 +299,9 @@ exaMoveInPixmap (PixmapPtr pPixmap) if (pPixmap->drawable.bitsPerPixel < 8) return; + if (pExaPixmap->accel_blocked) + return; + if (pExaPixmap->area == NULL) { pExaPixmap->area = exaOffscreenAlloc (pScreen, pExaPixmap->fb_size, @@ -331,6 +314,11 @@ exaMoveInPixmap (PixmapPtr pPixmap) pExaPixmap->area->offset; } + exaCopyDirtyToFb (migrate); + + if (exaPixmapIsOffscreen(pPixmap)) + return; + DBG_MIGRATE (("-> %p (0x%x) (%dx%d) (%c)\n", pPixmap, (ExaGetPixmapPriv(pPixmap)->area ? ExaGetPixmapPriv(pPixmap)->area->offset : 0), @@ -338,28 +326,37 @@ exaMoveInPixmap (PixmapPtr pPixmap) pPixmap->drawable.height, exaPixmapIsDirty(pPixmap) ? 'd' : 'c')); - exaCopyDirtyToFb (pPixmap); + pExaPixmap->offscreen = TRUE; - if (pExaScr->hideOffscreenPixmapData) - pPixmap->devPrivate.ptr = NULL; - else - pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; pPixmap->devKind = pExaPixmap->fb_pitch; pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; } +void +exaMoveInPixmap (PixmapPtr pPixmap) +{ + static ExaMigrationRec migrate = { .as_dst = FALSE, .as_src = TRUE, + .pReg = NULL }; + + migrate.pPix = pPixmap; + exaDoMoveInPixmap (&migrate); +} + /** * Switches the current active location of the pixmap to system memory, copying * updated data out if necessary. */ -void -exaMoveOutPixmap (PixmapPtr pPixmap) +static void +exaDoMoveOutPixmap (ExaMigrationPtr migrate) { + PixmapPtr pPixmap = migrate->pPix; ExaPixmapPriv (pPixmap); - if (exaPixmapIsPinned(pPixmap)) + if (!pExaPixmap->area || exaPixmapIsPinned(pPixmap)) return; + exaCopyDirtyToSys (migrate); + if (exaPixmapIsOffscreen(pPixmap)) { DBG_MIGRATE (("<- %p (%p) (%dx%d) (%c)\n", pPixmap, @@ -369,21 +366,53 @@ exaMoveOutPixmap (PixmapPtr pPixmap) pPixmap->drawable.height, exaPixmapIsDirty(pPixmap) ? 'd' : 'c')); - exaCopyDirtyToSys (pPixmap); + pExaPixmap->offscreen = FALSE; - pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; pPixmap->devKind = pExaPixmap->sys_pitch; pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; } } +void +exaMoveOutPixmap (PixmapPtr pPixmap) +{ + static ExaMigrationRec migrate = { .as_dst = FALSE, .as_src = TRUE, + .pReg = NULL }; + + migrate.pPix = pPixmap; + exaDoMoveOutPixmap (&migrate); +} + + +/** + * Copies out important pixmap data and removes references to framebuffer area. + * Called when the memory manager decides it's time to kick the pixmap out of + * framebuffer entirely. + */ +void +exaPixmapSave (ScreenPtr pScreen, ExaOffscreenArea *area) +{ + PixmapPtr pPixmap = area->privData; + ExaPixmapPriv(pPixmap); + + exaMoveOutPixmap(pPixmap); + + pExaPixmap->fb_ptr = NULL; + pExaPixmap->area = NULL; + + /* Mark all FB bits as invalid, so all valid system bits get copied to FB + * next time */ + REGION_EMPTY(pPixmap->drawable.pScreen, &pExaPixmap->validFB); +} + /** * For the "greedy" migration scheme, pushes the pixmap toward being located in * framebuffer memory. */ static void -exaMigrateTowardFb (PixmapPtr pPixmap) +exaMigrateTowardFb (ExaMigrationPtr migrate) { + PixmapPtr pPixmap = migrate->pPix; ExaPixmapPriv (pPixmap); if (pExaPixmap == NULL) { @@ -403,7 +432,7 @@ exaMigrateTowardFb (PixmapPtr pPixmap) (pointer)pPixmap, pExaPixmap->score)); if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT) { - exaMoveInPixmap(pPixmap); + exaDoMoveInPixmap(migrate); pExaPixmap->score = 0; } @@ -413,7 +442,7 @@ exaMigrateTowardFb (PixmapPtr pPixmap) if (pExaPixmap->score >= EXA_PIXMAP_SCORE_MOVE_IN && !exaPixmapIsOffscreen(pPixmap)) { - exaMoveInPixmap (pPixmap); + exaDoMoveInPixmap(migrate); } ExaOffscreenMarkUsed (pPixmap); @@ -424,8 +453,9 @@ exaMigrateTowardFb (PixmapPtr pPixmap) * system memory. */ static void -exaMigrateTowardSys (PixmapPtr pPixmap) +exaMigrateTowardSys (ExaMigrationPtr migrate) { + PixmapPtr pPixmap = migrate->pPix; ExaPixmapPriv (pPixmap); if (pExaPixmap == NULL) { @@ -447,7 +477,7 @@ exaMigrateTowardSys (PixmapPtr pPixmap) pExaPixmap->score--; if (pExaPixmap->score <= EXA_PIXMAP_SCORE_MOVE_OUT && pExaPixmap->area) - exaMoveOutPixmap (pPixmap); + exaDoMoveOutPixmap(migrate); } /** @@ -459,19 +489,29 @@ exaAssertNotDirty (PixmapPtr pPixmap) { ExaPixmapPriv (pPixmap); CARD8 *dst, *src; - RegionPtr pValidReg = &pExaPixmap->validReg; - int dst_pitch, src_pitch, cpp, y, nbox = REGION_NUM_RECTS(pValidReg); - BoxPtr pBox = REGION_RECTS(pValidReg); + RegionRec ValidReg; + int dst_pitch, src_pitch, cpp, y, nbox; + BoxPtr pBox; Bool ret = TRUE; - if (!nbox || exaPixmapIsPinned(pPixmap) || pExaPixmap->fb_ptr == NULL) + if (exaPixmapIsPinned(pPixmap) || pExaPixmap->area == NULL) return ret; + REGION_NULL(pScreen, &ValidReg); + REGION_INTERSECT(pScreen, &ValidReg, &pExaPixmap->validFB, + &pExaPixmap->validSys); + nbox = REGION_NUM_RECTS(&ValidReg); + + if (!nbox) + goto out; + + pBox = REGION_RECTS(&ValidReg); + dst_pitch = pExaPixmap->sys_pitch; src_pitch = pExaPixmap->fb_pitch; cpp = pPixmap->drawable.bitsPerPixel / 8; - exaPrepareAccess(&pPixmap->drawable, EXA_PREPARE_SRC); + ExaDoPrepareAccess(&pPixmap->drawable, EXA_PREPARE_SRC); while (nbox--) { int rowbytes; @@ -499,6 +539,8 @@ exaAssertNotDirty (PixmapPtr pPixmap) } exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_SRC); +out: + REGION_UNINIT(pScreen, &ValidReg); return ret; } @@ -514,6 +556,9 @@ exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) ExaScreenPriv(pScreen); int i, j; + if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) + return; + /* If this debugging flag is set, check each pixmap for whether it is marked * as clean, and if so, actually check if that's the case. This should help * catch issues with failing to mark a drawable as dirty. While it will @@ -553,7 +598,7 @@ exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) { for (i = 0; i < npixmaps; i++) { if (!exaPixmapIsDirty (pixmaps[i].pPix)) - exaMoveOutPixmap (pixmaps[i].pPix); + exaDoMoveOutPixmap (pixmaps + i); } return; } @@ -564,17 +609,17 @@ exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) */ if (!can_accel) { for (i = 0; i < npixmaps; i++) { - exaMigrateTowardSys (pixmaps[i].pPix); + exaMigrateTowardSys (pixmaps + i); if (!exaPixmapIsDirty (pixmaps[i].pPix)) - exaMoveOutPixmap (pixmaps[i].pPix); + exaDoMoveOutPixmap (pixmaps + i); } return; } /* Finally, the acceleration path. Move them all in. */ for (i = 0; i < npixmaps; i++) { - exaMigrateTowardFb(pixmaps[i].pPix); - exaMoveInPixmap(pixmaps[i].pPix); + exaMigrateTowardFb(pixmaps + i); + exaDoMoveInPixmap(pixmaps + i); } } else if (pExaScr->migration == ExaMigrationGreedy) { /* If we can't accelerate, either because the driver can't or because one of @@ -590,7 +635,7 @@ exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) */ if (!can_accel) { for (i = 0; i < npixmaps; i++) - exaMigrateTowardSys (pixmaps[i].pPix); + exaMigrateTowardSys (pixmaps + i); return; } @@ -598,14 +643,14 @@ exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) if (exaPixmapIsOffscreen(pixmaps[i].pPix)) { /* Found one in FB, so move all to FB. */ for (j = 0; j < npixmaps; j++) - exaMigrateTowardFb(pixmaps[j].pPix); + exaMigrateTowardFb(pixmaps + i); return; } } /* Nobody's in FB, so move all away from FB. */ for (i = 0; i < npixmaps; i++) - exaMigrateTowardSys(pixmaps[i].pPix); + exaMigrateTowardSys(pixmaps + i); } else if (pExaScr->migration == ExaMigrationAlways) { /* Always move the pixmaps out if we can't accelerate. If we can * accelerate, try to move them all in. If that fails, then move them @@ -613,26 +658,25 @@ exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) */ if (!can_accel) { for (i = 0; i < npixmaps; i++) - exaMoveOutPixmap(pixmaps[i].pPix); + exaDoMoveOutPixmap(pixmaps + i); return; } /* Now, try to move them all into FB */ for (i = 0; i < npixmaps; i++) { - exaMoveInPixmap(pixmaps[i].pPix); - ExaOffscreenMarkUsed (pixmaps[i].pPix); + exaDoMoveInPixmap(pixmaps + i); } - /* If we couldn't fit everything in, then kick back out */ + /* If we couldn't fit everything in, abort */ for (i = 0; i < npixmaps; i++) { if (!exaPixmapIsOffscreen(pixmaps[i].pPix)) { - EXA_FALLBACK(("Pixmap %p (%dx%d) not in fb\n", pixmaps[i].pPix, - pixmaps[i].pPix->drawable.width, - pixmaps[i].pPix->drawable.height)); - for (j = 0; j < npixmaps; j++) - exaMoveOutPixmap(pixmaps[j].pPix); - break; + return; } } + + /* Yay, everything's offscreen, mark memory as used */ + for (i = 0; i < npixmaps; i++) { + ExaOffscreenMarkUsed (pixmaps[i].pPix); + } } } |