exa_accel.c revision 4642e01f
1/*
2 * Copyright � 2001 Keith Packard
3 *
4 * Partly based on code that is Copyright � The XFree86 Project Inc.
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  Keith Packard makes no
13 * representations about the suitability of this software for any purpose.  It
14 * is provided "as is" without express or implied warranty.
15 *
16 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Authors:
25 *    Eric Anholt <eric@anholt.net>
26 *    Michel D�nzer <michel@tungstengraphics.com>
27 *
28 */
29
30#ifdef HAVE_DIX_CONFIG_H
31#include <dix-config.h>
32#endif
33#include "exa_priv.h"
34#include <X11/fonts/fontstruct.h>
35#include "dixfontstr.h"
36#include "exa.h"
37
38static void
39exaFillSpans(DrawablePtr pDrawable, GCPtr pGC, int n,
40	     DDXPointPtr ppt, int *pwidth, int fSorted)
41{
42    ScreenPtr	    pScreen = pDrawable->pScreen;
43    ExaScreenPriv (pScreen);
44    RegionPtr	    pClip = fbGetCompositeClip(pGC);
45    PixmapPtr	    pPixmap = exaGetDrawablePixmap (pDrawable);
46    ExaPixmapPriv (pPixmap);
47    BoxPtr	    pextent, pbox;
48    int		    nbox;
49    int		    extentX1, extentX2, extentY1, extentY2;
50    int		    fullX1, fullX2, fullY1;
51    int		    partX1, partX2;
52    int		    off_x, off_y;
53    ExaMigrationRec pixmaps[1];
54
55    pixmaps[0].as_dst = TRUE;
56    pixmaps[0].as_src = FALSE;
57    pixmaps[0].pPix = pPixmap;
58    pixmaps[0].pReg = NULL;
59
60    if (pExaScr->swappedOut ||
61	pGC->fillStyle != FillSolid ||
62	pExaPixmap->accel_blocked)
63    {
64	ExaCheckFillSpans (pDrawable, pGC, n, ppt, pwidth, fSorted);
65	return;
66    } else {
67	exaDoMigration (pixmaps, 1, TRUE);
68    }
69
70    if (!(pPixmap = exaGetOffscreenPixmap (pDrawable, &off_x, &off_y)) ||
71	!(*pExaScr->info->PrepareSolid) (pPixmap,
72					 pGC->alu,
73					 pGC->planemask,
74					 pGC->fgPixel))
75    {
76	ExaCheckFillSpans (pDrawable, pGC, n, ppt, pwidth, fSorted);
77	return;
78    }
79
80    pextent = REGION_EXTENTS(pGC->pScreen, pClip);
81    extentX1 = pextent->x1;
82    extentY1 = pextent->y1;
83    extentX2 = pextent->x2;
84    extentY2 = pextent->y2;
85    while (n--)
86    {
87	fullX1 = ppt->x;
88	fullY1 = ppt->y;
89	fullX2 = fullX1 + (int) *pwidth;
90	ppt++;
91	pwidth++;
92
93	if (fullY1 < extentY1 || extentY2 <= fullY1)
94	    continue;
95
96	if (fullX1 < extentX1)
97	    fullX1 = extentX1;
98
99	if (fullX2 > extentX2)
100	    fullX2 = extentX2;
101
102	if (fullX1 >= fullX2)
103	    continue;
104
105	nbox = REGION_NUM_RECTS (pClip);
106	if (nbox == 1)
107	{
108	    (*pExaScr->info->Solid) (pPixmap,
109				     fullX1 + off_x, fullY1 + off_y,
110				     fullX2 + off_x, fullY1 + 1 + off_y);
111	}
112	else
113	{
114	    pbox = REGION_RECTS(pClip);
115	    while(nbox--)
116	    {
117		if (pbox->y1 <= fullY1 && fullY1 < pbox->y2)
118		{
119		    partX1 = pbox->x1;
120		    if (partX1 < fullX1)
121			partX1 = fullX1;
122		    partX2 = pbox->x2;
123		    if (partX2 > fullX2)
124			partX2 = fullX2;
125		    if (partX2 > partX1) {
126			(*pExaScr->info->Solid) (pPixmap,
127						 partX1 + off_x, fullY1 + off_y,
128						 partX2 + off_x, fullY1 + 1 + off_y);
129		    }
130		}
131		pbox++;
132	    }
133	}
134    }
135    (*pExaScr->info->DoneSolid) (pPixmap);
136    exaMarkSync(pScreen);
137}
138
139static Bool
140exaDoPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y,
141	       int w, int h, int format, char *bits, int src_stride)
142{
143    ExaScreenPriv (pDrawable->pScreen);
144    PixmapPtr pPix = exaGetDrawablePixmap (pDrawable);
145    ExaPixmapPriv(pPix);
146    RegionPtr pClip;
147    BoxPtr pbox;
148    int nbox;
149    int xoff, yoff;
150    int bpp = pDrawable->bitsPerPixel;
151    Bool access_prepared = FALSE;
152
153    if (pExaPixmap->accel_blocked)
154	return FALSE;
155
156    /* Don't bother with under 8bpp, XYPixmaps. */
157    if (format != ZPixmap || bpp < 8)
158	return FALSE;
159
160    /* Only accelerate copies: no rop or planemask. */
161    if (!EXA_PM_IS_SOLID(pDrawable, pGC->planemask) || pGC->alu != GXcopy)
162	return FALSE;
163
164    if (pExaScr->swappedOut)
165	return FALSE;
166
167    if (pExaPixmap->pDamage) {
168	ExaMigrationRec pixmaps[1];
169
170 	pixmaps[0].as_dst = TRUE;
171	pixmaps[0].as_src = FALSE;
172	pixmaps[0].pPix = pPix;
173	pixmaps[0].pReg = DamagePendingRegion(pExaPixmap->pDamage);
174
175	exaDoMigration (pixmaps, 1, TRUE);
176    }
177
178    pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
179
180    if (!pPix || !pExaScr->info->UploadToScreen)
181	return FALSE;
182
183    x += pDrawable->x;
184    y += pDrawable->y;
185
186    pClip = fbGetCompositeClip(pGC);
187    for (nbox = REGION_NUM_RECTS(pClip),
188	 pbox = REGION_RECTS(pClip);
189	 nbox--;
190	 pbox++)
191    {
192	int x1 = x;
193	int y1 = y;
194	int x2 = x + w;
195	int y2 = y + h;
196	char *src;
197	Bool ok;
198
199	if (x1 < pbox->x1)
200	    x1 = pbox->x1;
201	if (y1 < pbox->y1)
202	    y1 = pbox->y1;
203	if (x2 > pbox->x2)
204	    x2 = pbox->x2;
205	if (y2 > pbox->y2)
206	    y2 = pbox->y2;
207	if (x1 >= x2 || y1 >= y2)
208	    continue;
209
210	src = bits + (y1 - y) * src_stride + (x1 - x) * (bpp / 8);
211	ok = pExaScr->info->UploadToScreen(pPix, x1 + xoff, y1 + yoff,
212					   x2 - x1, y2 - y1, src, src_stride);
213	/* If we fail to accelerate the upload, fall back to using unaccelerated
214	 * fb calls.
215	 */
216	if (!ok) {
217	    FbStip *dst;
218	    FbStride dst_stride;
219	    int	dstBpp;
220	    int	dstXoff, dstYoff;
221
222	    if (!access_prepared) {
223		ExaDoPrepareAccess(pDrawable, EXA_PREPARE_DEST);
224
225		access_prepared = TRUE;
226	    }
227
228	    fbGetStipDrawable(pDrawable, dst, dst_stride, dstBpp,
229			      dstXoff, dstYoff);
230
231	    fbBltStip((FbStip *)bits + (y1 - y) * (src_stride / sizeof(FbStip)),
232		      src_stride / sizeof(FbStip),
233		      (x1 - x) * dstBpp,
234		      dst + (y1 + dstYoff) * dst_stride,
235		      dst_stride,
236		      (x1 + dstXoff) * dstBpp,
237		      (x2 - x1) * dstBpp,
238		      y2 - y1,
239		      GXcopy, FB_ALLONES, dstBpp);
240	}
241    }
242
243    if (access_prepared)
244	exaFinishAccess(pDrawable, EXA_PREPARE_DEST);
245    else
246	exaMarkSync(pDrawable->pScreen);
247
248    return TRUE;
249}
250
251static void
252exaPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y,
253	     int w, int h, int leftPad, int format, char *bits)
254{
255    if (!exaDoPutImage(pDrawable, pGC, depth, x, y, w, h, format, bits,
256		       PixmapBytePad(w, pDrawable->depth)))
257	ExaCheckPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format,
258			 bits);
259}
260
261static Bool inline
262exaCopyNtoNTwoDir (DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable,
263		   GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy)
264{
265    ExaScreenPriv (pDstDrawable->pScreen);
266    PixmapPtr pSrcPixmap, pDstPixmap;
267    int src_off_x, src_off_y, dst_off_x, dst_off_y;
268    int dirsetup;
269
270    /* Need to get both pixmaps to call the driver routines */
271    pSrcPixmap = exaGetOffscreenPixmap (pSrcDrawable, &src_off_x, &src_off_y);
272    pDstPixmap = exaGetOffscreenPixmap (pDstDrawable, &dst_off_x, &dst_off_y);
273    if (!pSrcPixmap || !pDstPixmap)
274	return FALSE;
275
276    /*
277     * Now the case of a chip that only supports xdir = ydir = 1 or
278     * xdir = ydir = -1, but we have xdir != ydir.
279     */
280    dirsetup = 0;	/* No direction set up yet. */
281    for (; nbox; pbox++, nbox--) {
282	if (dx >= 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) {
283	    /* Do a xdir = ydir = -1 blit instead. */
284	    if (dirsetup != -1) {
285		if (dirsetup != 0)
286		    pExaScr->info->DoneCopy(pDstPixmap);
287		dirsetup = -1;
288		if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
289						   pDstPixmap,
290						   -1, -1,
291						   pGC ? pGC->alu : GXcopy,
292						   pGC ? pGC->planemask :
293							 FB_ALLONES))
294		    return FALSE;
295	    }
296	    (*pExaScr->info->Copy)(pDstPixmap,
297				   src_off_x + pbox->x1 + dx,
298				   src_off_y + pbox->y1 + dy,
299				   dst_off_x + pbox->x1,
300				   dst_off_y + pbox->y1,
301				   pbox->x2 - pbox->x1,
302				   pbox->y2 - pbox->y1);
303	} else if (dx < 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) {
304	    /* Do a xdir = ydir = 1 blit instead. */
305	    if (dirsetup != 1) {
306		if (dirsetup != 0)
307		    pExaScr->info->DoneCopy(pDstPixmap);
308		dirsetup = 1;
309		if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
310						   pDstPixmap,
311						   1, 1,
312						   pGC ? pGC->alu : GXcopy,
313						   pGC ? pGC->planemask :
314							 FB_ALLONES))
315		    return FALSE;
316	    }
317	    (*pExaScr->info->Copy)(pDstPixmap,
318				   src_off_x + pbox->x1 + dx,
319				   src_off_y + pbox->y1 + dy,
320				   dst_off_x + pbox->x1,
321				   dst_off_y + pbox->y1,
322				   pbox->x2 - pbox->x1,
323				   pbox->y2 - pbox->y1);
324	} else if (dx >= 0) {
325	    /*
326	     * xdir = 1, ydir = -1.
327	     * Perform line-by-line xdir = ydir = 1 blits, going up.
328	     */
329	    int i;
330	    if (dirsetup != 1) {
331		if (dirsetup != 0)
332		    pExaScr->info->DoneCopy(pDstPixmap);
333		dirsetup = 1;
334		if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
335						   pDstPixmap,
336						   1, 1,
337						   pGC ? pGC->alu : GXcopy,
338						   pGC ? pGC->planemask :
339							 FB_ALLONES))
340		    return FALSE;
341	    }
342	    for (i = pbox->y2 - pbox->y1 - 1; i >= 0; i--)
343		(*pExaScr->info->Copy)(pDstPixmap,
344				       src_off_x + pbox->x1 + dx,
345				       src_off_y + pbox->y1 + dy + i,
346				       dst_off_x + pbox->x1,
347				       dst_off_y + pbox->y1 + i,
348				       pbox->x2 - pbox->x1, 1);
349	} else {
350	    /*
351	     * xdir = -1, ydir = 1.
352	     * Perform line-by-line xdir = ydir = -1 blits, going down.
353	     */
354	    int i;
355	    if (dirsetup != -1) {
356		if (dirsetup != 0)
357		    pExaScr->info->DoneCopy(pDstPixmap);
358		dirsetup = -1;
359		if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
360						   pDstPixmap,
361						   -1, -1,
362						   pGC ? pGC->alu : GXcopy,
363						   pGC ? pGC->planemask :
364							 FB_ALLONES))
365		    return FALSE;
366	    }
367	    for (i = 0; i < pbox->y2 - pbox->y1; i++)
368		(*pExaScr->info->Copy)(pDstPixmap,
369				       src_off_x + pbox->x1 + dx,
370				       src_off_y + pbox->y1 + dy + i,
371				       dst_off_x + pbox->x1,
372				       dst_off_y + pbox->y1 + i,
373				       pbox->x2 - pbox->x1, 1);
374	}
375    }
376    if (dirsetup != 0)
377	pExaScr->info->DoneCopy(pDstPixmap);
378    exaMarkSync(pDstDrawable->pScreen);
379    return TRUE;
380}
381
382void
383exaCopyNtoN (DrawablePtr    pSrcDrawable,
384	     DrawablePtr    pDstDrawable,
385	     GCPtr	    pGC,
386	     BoxPtr	    pbox,
387	     int	    nbox,
388	     int	    dx,
389	     int	    dy,
390	     Bool	    reverse,
391	     Bool	    upsidedown,
392	     Pixel	    bitplane,
393	     void	    *closure)
394{
395    ExaScreenPriv (pDstDrawable->pScreen);
396    PixmapPtr pSrcPixmap, pDstPixmap;
397    ExaPixmapPrivPtr pSrcExaPixmap, pDstExaPixmap;
398    int	    src_off_x, src_off_y;
399    int	    dst_off_x, dst_off_y;
400    ExaMigrationRec pixmaps[2];
401    RegionPtr srcregion = NULL, dstregion = NULL;
402    xRectangle *rects;
403
404    /* avoid doing copy operations if no boxes */
405    if (nbox == 0)
406	return;
407
408    pSrcPixmap = exaGetDrawablePixmap (pSrcDrawable);
409    pDstPixmap = exaGetDrawablePixmap (pDstDrawable);
410
411    exaGetDrawableDeltas (pSrcDrawable, pSrcPixmap, &src_off_x, &src_off_y);
412    exaGetDrawableDeltas (pDstDrawable, pDstPixmap, &dst_off_x, &dst_off_y);
413
414    rects = xalloc(nbox * sizeof(xRectangle));
415
416    if (rects) {
417	int i;
418
419	for (i = 0; i < nbox; i++) {
420	    rects[i].x = pbox[i].x1 + dx + src_off_x;
421	    rects[i].y = pbox[i].y1 + dy + src_off_y;
422	    rects[i].width = pbox[i].x2 - pbox[i].x1;
423	    rects[i].height = pbox[i].y2 - pbox[i].y1;
424	}
425
426	srcregion  = RECTS_TO_REGION(pScreen, nbox, rects, CT_YXBANDED);
427	xfree(rects);
428
429	if (!pGC || !exaGCReadsDestination(pDstDrawable, pGC->planemask,
430					   pGC->fillStyle, pGC->alu)) {
431	    dstregion = REGION_CREATE(pScreen, NullBox, 0);
432	    REGION_COPY(pScreen, dstregion, srcregion);
433	    REGION_TRANSLATE(pScreen, dstregion, dst_off_x - dx - src_off_x,
434			     dst_off_y - dy - src_off_y);
435	}
436    }
437
438    pixmaps[0].as_dst = TRUE;
439    pixmaps[0].as_src = FALSE;
440    pixmaps[0].pPix = pDstPixmap;
441    pixmaps[0].pReg = dstregion;
442    pixmaps[1].as_dst = FALSE;
443    pixmaps[1].as_src = TRUE;
444    pixmaps[1].pPix = pSrcPixmap;
445    pixmaps[1].pReg = srcregion;
446
447    pSrcExaPixmap = ExaGetPixmapPriv (pSrcPixmap);
448    pDstExaPixmap = ExaGetPixmapPriv (pDstPixmap);
449
450    /* Check whether the accelerator can use this pixmap.
451     * If the pitch of the pixmaps is out of range, there's nothing
452     * we can do but fall back to software rendering.
453     */
454    if (pSrcExaPixmap->accel_blocked & EXA_RANGE_PITCH ||
455        pDstExaPixmap->accel_blocked & EXA_RANGE_PITCH)
456	goto fallback;
457
458    /* If the width or the height of either of the pixmaps
459     * is out of range, check whether the boxes are actually out of the
460     * addressable range as well. If they aren't, we can still do
461     * the copying in hardware.
462     */
463    if (pSrcExaPixmap->accel_blocked || pDstExaPixmap->accel_blocked) {
464        int i;
465
466        for (i = 0; i < nbox; i++) {
467            /* src */
468            if ((pbox[i].x2 + dx + src_off_x) >= pExaScr->info->maxX ||
469                (pbox[i].y2 + dy + src_off_y) >= pExaScr->info->maxY)
470                goto fallback;
471
472            /* dst */
473            if ((pbox[i].x2 + dst_off_x) >= pExaScr->info->maxX ||
474                (pbox[i].y2 + dst_off_y) >= pExaScr->info->maxY)
475                goto fallback;
476        }
477    }
478
479    exaDoMigration (pixmaps, 2, TRUE);
480
481    /* Mixed directions must be handled specially if the card is lame */
482    if ((pExaScr->info->flags & EXA_TWO_BITBLT_DIRECTIONS) &&
483	reverse != upsidedown) {
484	if (exaCopyNtoNTwoDir(pSrcDrawable, pDstDrawable, pGC, pbox, nbox,
485			       dx, dy))
486	    goto out;
487	goto fallback;
488    }
489
490    if (!exaPixmapIsOffscreen(pSrcPixmap) ||
491	!exaPixmapIsOffscreen(pDstPixmap) ||
492	!(*pExaScr->info->PrepareCopy) (pSrcPixmap, pDstPixmap, reverse ? -1 : 1,
493					upsidedown ? -1 : 1,
494					pGC ? pGC->alu : GXcopy,
495					pGC ? pGC->planemask : FB_ALLONES)) {
496	goto fallback;
497    }
498
499    while (nbox--)
500    {
501	(*pExaScr->info->Copy) (pDstPixmap,
502				pbox->x1 + dx + src_off_x,
503				pbox->y1 + dy + src_off_y,
504				pbox->x1 + dst_off_x, pbox->y1 + dst_off_y,
505				pbox->x2 - pbox->x1, pbox->y2 - pbox->y1);
506	pbox++;
507    }
508
509    (*pExaScr->info->DoneCopy) (pDstPixmap);
510    exaMarkSync (pDstDrawable->pScreen);
511
512    goto out;
513
514fallback:
515    EXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrcDrawable, pDstDrawable,
516		  exaDrawableLocation(pSrcDrawable),
517		  exaDrawableLocation(pDstDrawable)));
518    exaPrepareAccessReg (pDstDrawable, EXA_PREPARE_DEST, dstregion);
519    exaPrepareAccessReg (pSrcDrawable, EXA_PREPARE_SRC, srcregion);
520    fbCopyNtoN (pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse,
521		upsidedown, bitplane, closure);
522    exaFinishAccess (pSrcDrawable, EXA_PREPARE_SRC);
523    exaFinishAccess (pDstDrawable, EXA_PREPARE_DEST);
524
525out:
526    if (dstregion) {
527	REGION_UNINIT(pScreen, dstregion);
528	REGION_DESTROY(pScreen, dstregion);
529    }
530    if (srcregion) {
531	REGION_UNINIT(pScreen, srcregion);
532	REGION_DESTROY(pScreen, srcregion);
533    }
534}
535
536RegionPtr
537exaCopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC,
538	    int srcx, int srcy, int width, int height, int dstx, int dsty)
539{
540    ExaScreenPriv (pDstDrawable->pScreen);
541
542    if (pExaScr->swappedOut) {
543        return  ExaCheckCopyArea(pSrcDrawable, pDstDrawable, pGC,
544                                 srcx, srcy, width, height, dstx, dsty);
545    }
546
547    return  fbDoCopy (pSrcDrawable, pDstDrawable, pGC,
548                      srcx, srcy, width, height,
549                      dstx, dsty, exaCopyNtoN, 0, NULL);
550}
551
552static void
553exaPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
554	     DDXPointPtr ppt)
555{
556    int i;
557    xRectangle *prect;
558
559    /* If we can't reuse the current GC as is, don't bother accelerating the
560     * points.
561     */
562    if (pGC->fillStyle != FillSolid) {
563	ExaCheckPolyPoint(pDrawable, pGC, mode, npt, ppt);
564	return;
565    }
566
567    prect = xalloc(sizeof(xRectangle) * npt);
568    for (i = 0; i < npt; i++) {
569	prect[i].x = ppt[i].x;
570	prect[i].y = ppt[i].y;
571	if (i > 0 && mode == CoordModePrevious) {
572	    prect[i].x += prect[i - 1].x;
573	    prect[i].y += prect[i - 1].y;
574	}
575	prect[i].width = 1;
576	prect[i].height = 1;
577    }
578    pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect);
579    xfree(prect);
580}
581
582/**
583 * exaPolylines() checks if it can accelerate the lines as a group of
584 * horizontal or vertical lines (rectangles), and uses existing rectangle fill
585 * acceleration if so.
586 */
587static void
588exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
589	     DDXPointPtr ppt)
590{
591    xRectangle *prect;
592    int x1, x2, y1, y2;
593    int i;
594
595    /* Don't try to do wide lines or non-solid fill style. */
596    if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid ||
597	pGC->fillStyle != FillSolid) {
598	ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
599	return;
600    }
601
602    prect = xalloc(sizeof(xRectangle) * (npt - 1));
603    x1 = ppt[0].x;
604    y1 = ppt[0].y;
605    /* If we have any non-horizontal/vertical, fall back. */
606    for (i = 0; i < npt - 1; i++) {
607	if (mode == CoordModePrevious) {
608	    x2 = x1 + ppt[i + 1].x;
609	    y2 = y1 + ppt[i + 1].y;
610	} else {
611	    x2 = ppt[i + 1].x;
612	    y2 = ppt[i + 1].y;
613	}
614
615	if (x1 != x2 && y1 != y2) {
616	    xfree(prect);
617	    ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
618	    return;
619	}
620
621	if (x1 < x2) {
622	    prect[i].x = x1;
623	    prect[i].width = x2 - x1 + 1;
624	} else {
625	    prect[i].x = x2;
626	    prect[i].width = x1 - x2 + 1;
627	}
628	if (y1 < y2) {
629	    prect[i].y = y1;
630	    prect[i].height = y2 - y1 + 1;
631	} else {
632	    prect[i].y = y2;
633	    prect[i].height = y1 - y2 + 1;
634	}
635
636	x1 = x2;
637	y1 = y2;
638    }
639    pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect);
640    xfree(prect);
641}
642
643/**
644 * exaPolySegment() checks if it can accelerate the lines as a group of
645 * horizontal or vertical lines (rectangles), and uses existing rectangle fill
646 * acceleration if so.
647 */
648static void
649exaPolySegment (DrawablePtr pDrawable, GCPtr pGC, int nseg,
650		xSegment *pSeg)
651{
652    xRectangle *prect;
653    int i;
654
655    /* Don't try to do wide lines or non-solid fill style. */
656    if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid ||
657	pGC->fillStyle != FillSolid)
658    {
659	ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg);
660	return;
661    }
662
663    /* If we have any non-horizontal/vertical, fall back. */
664    for (i = 0; i < nseg; i++) {
665	if (pSeg[i].x1 != pSeg[i].x2 && pSeg[i].y1 != pSeg[i].y2) {
666	    ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg);
667	    return;
668	}
669    }
670
671    prect = xalloc(sizeof(xRectangle) * nseg);
672    for (i = 0; i < nseg; i++) {
673	if (pSeg[i].x1 < pSeg[i].x2) {
674	    prect[i].x = pSeg[i].x1;
675	    prect[i].width = pSeg[i].x2 - pSeg[i].x1 + 1;
676	} else {
677	    prect[i].x = pSeg[i].x2;
678	    prect[i].width = pSeg[i].x1 - pSeg[i].x2 + 1;
679	}
680	if (pSeg[i].y1 < pSeg[i].y2) {
681	    prect[i].y = pSeg[i].y1;
682	    prect[i].height = pSeg[i].y2 - pSeg[i].y1 + 1;
683	} else {
684	    prect[i].y = pSeg[i].y2;
685	    prect[i].height = pSeg[i].y1 - pSeg[i].y2 + 1;
686	}
687
688	/* don't paint last pixel */
689	if (pGC->capStyle == CapNotLast) {
690	    if (prect[i].width == 1)
691		prect[i].height--;
692	    else
693		prect[i].width--;
694	}
695    }
696    pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect);
697    xfree(prect);
698}
699
700static Bool exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion,
701				Pixel pixel, CARD32 planemask, CARD32 alu);
702
703static void
704exaPolyFillRect(DrawablePtr pDrawable,
705		GCPtr	    pGC,
706		int	    nrect,
707		xRectangle  *prect)
708{
709    ExaScreenPriv (pDrawable->pScreen);
710    RegionPtr	    pClip = fbGetCompositeClip(pGC);
711    PixmapPtr	    pPixmap = exaGetDrawablePixmap(pDrawable);
712    ExaPixmapPriv (pPixmap);
713    register BoxPtr pbox;
714    BoxPtr	    pextent;
715    int		    extentX1, extentX2, extentY1, extentY2;
716    int		    fullX1, fullX2, fullY1, fullY2;
717    int		    partX1, partX2, partY1, partY2;
718    int		    xoff, yoff;
719    int		    xorg, yorg;
720    int		    n;
721    ExaMigrationRec pixmaps[2];
722    RegionPtr pReg = RECTS_TO_REGION(pScreen, nrect, prect, CT_UNSORTED);
723
724    /* Compute intersection of rects and clip region */
725    REGION_TRANSLATE(pScreen, pReg, pDrawable->x, pDrawable->y);
726    REGION_INTERSECT(pScreen, pReg, pClip, pReg);
727
728    if (!REGION_NUM_RECTS(pReg)) {
729	goto out;
730    }
731
732    pixmaps[0].as_dst = TRUE;
733    pixmaps[0].as_src = FALSE;
734    pixmaps[0].pPix = pPixmap;
735    pixmaps[0].pReg = NULL;
736
737    exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
738
739    if (pExaScr->swappedOut || pExaPixmap->accel_blocked)
740    {
741	goto fallback;
742    }
743
744    /* For ROPs where overlaps don't matter, convert rectangles to region and
745     * call exaFillRegion{Solid,Tiled}.
746     */
747    if ((pGC->fillStyle == FillSolid || pGC->fillStyle == FillTiled) &&
748	(nrect == 1 || pGC->alu == GXcopy || pGC->alu == GXclear ||
749	 pGC->alu == GXnoop || pGC->alu == GXcopyInverted ||
750	 pGC->alu == GXset)) {
751	if (((pGC->fillStyle == FillSolid || pGC->tileIsPixel) &&
752	     exaFillRegionSolid(pDrawable, pReg, pGC->fillStyle == FillSolid ?
753				pGC->fgPixel : pGC->tile.pixel,	pGC->planemask,
754				pGC->alu)) ||
755	    (pGC->fillStyle == FillTiled && !pGC->tileIsPixel &&
756	     exaFillRegionTiled(pDrawable, pReg, pGC->tile.pixmap, &pGC->patOrg,
757				pGC->planemask, pGC->alu))) {
758	    goto out;
759	}
760    }
761
762    if (pGC->fillStyle != FillSolid &&
763	!(pGC->tileIsPixel && pGC->fillStyle == FillTiled))
764    {
765	goto fallback;
766    }
767
768    exaDoMigration (pixmaps, 1, TRUE);
769
770    if (!exaPixmapIsOffscreen (pPixmap) ||
771	!(*pExaScr->info->PrepareSolid) (pPixmap,
772					 pGC->alu,
773					 pGC->planemask,
774					 pGC->fgPixel))
775    {
776fallback:
777	ExaCheckPolyFillRect (pDrawable, pGC, nrect, prect);
778	goto out;
779    }
780
781    xorg = pDrawable->x;
782    yorg = pDrawable->y;
783
784    pextent = REGION_EXTENTS(pGC->pScreen, pClip);
785    extentX1 = pextent->x1;
786    extentY1 = pextent->y1;
787    extentX2 = pextent->x2;
788    extentY2 = pextent->y2;
789    while (nrect--)
790    {
791	fullX1 = prect->x + xorg;
792	fullY1 = prect->y + yorg;
793	fullX2 = fullX1 + (int) prect->width;
794	fullY2 = fullY1 + (int) prect->height;
795	prect++;
796
797	if (fullX1 < extentX1)
798	    fullX1 = extentX1;
799
800	if (fullY1 < extentY1)
801	    fullY1 = extentY1;
802
803	if (fullX2 > extentX2)
804	    fullX2 = extentX2;
805
806	if (fullY2 > extentY2)
807	    fullY2 = extentY2;
808
809	if ((fullX1 >= fullX2) || (fullY1 >= fullY2))
810	    continue;
811	n = REGION_NUM_RECTS (pClip);
812	if (n == 1)
813	{
814	    (*pExaScr->info->Solid) (pPixmap,
815				     fullX1 + xoff, fullY1 + yoff,
816				     fullX2 + xoff, fullY2 + yoff);
817	}
818	else
819	{
820	    pbox = REGION_RECTS(pClip);
821	    /*
822	     * clip the rectangle to each box in the clip region
823	     * this is logically equivalent to calling Intersect(),
824	     * but rectangles may overlap each other here.
825	     */
826	    while(n--)
827	    {
828		partX1 = pbox->x1;
829		if (partX1 < fullX1)
830		    partX1 = fullX1;
831		partY1 = pbox->y1;
832		if (partY1 < fullY1)
833		    partY1 = fullY1;
834		partX2 = pbox->x2;
835		if (partX2 > fullX2)
836		    partX2 = fullX2;
837		partY2 = pbox->y2;
838		if (partY2 > fullY2)
839		    partY2 = fullY2;
840
841		pbox++;
842
843		if (partX1 < partX2 && partY1 < partY2) {
844		    (*pExaScr->info->Solid) (pPixmap,
845					     partX1 + xoff, partY1 + yoff,
846					     partX2 + xoff, partY2 + yoff);
847		}
848	    }
849	}
850    }
851    (*pExaScr->info->DoneSolid) (pPixmap);
852    exaMarkSync(pDrawable->pScreen);
853
854out:
855    REGION_UNINIT(pScreen, pReg);
856    REGION_DESTROY(pScreen, pReg);
857}
858
859const GCOps exaOps = {
860    exaFillSpans,
861    ExaCheckSetSpans,
862    exaPutImage,
863    exaCopyArea,
864    ExaCheckCopyPlane,
865    exaPolyPoint,
866    exaPolylines,
867    exaPolySegment,
868    miPolyRectangle,
869    ExaCheckPolyArc,
870    miFillPolygon,
871    exaPolyFillRect,
872    miPolyFillArc,
873    miPolyText8,
874    miPolyText16,
875    miImageText8,
876    miImageText16,
877    ExaCheckImageGlyphBlt,
878    ExaCheckPolyGlyphBlt,
879    ExaCheckPushPixels,
880};
881
882void
883exaCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
884{
885    RegionRec	rgnDst;
886    int		dx, dy;
887    PixmapPtr	pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin);
888
889    dx = ptOldOrg.x - pWin->drawable.x;
890    dy = ptOldOrg.y - pWin->drawable.y;
891    REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy);
892
893    REGION_INIT (pWin->drawable.pScreen, &rgnDst, NullBox, 0);
894
895    REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc);
896#ifdef COMPOSITE
897    if (pPixmap->screen_x || pPixmap->screen_y)
898	REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst,
899			  -pPixmap->screen_x, -pPixmap->screen_y);
900#endif
901
902    fbCopyRegion (&pPixmap->drawable, &pPixmap->drawable,
903		  NULL,
904		  &rgnDst, dx, dy, exaCopyNtoN, 0, NULL);
905
906    REGION_UNINIT(pWin->drawable.pScreen, &rgnDst);
907}
908
909static Bool
910exaFillRegionSolid (DrawablePtr	pDrawable,
911		    RegionPtr	pRegion,
912		    Pixel	pixel,
913		    CARD32	planemask,
914		    CARD32	alu)
915{
916    ExaScreenPriv(pDrawable->pScreen);
917    PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable);
918    ExaPixmapPriv (pPixmap);
919    int xoff, yoff;
920    ExaMigrationRec pixmaps[1];
921    Bool ret = FALSE;
922
923    pixmaps[0].as_dst = TRUE;
924    pixmaps[0].as_src = FALSE;
925    pixmaps[0].pPix = pPixmap;
926    pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillSolid,
927					    alu) ? NULL : pRegion;
928
929    exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
930    REGION_TRANSLATE(pScreen, pRegion, xoff, yoff);
931
932    if (pExaPixmap->accel_blocked)
933    {
934	goto out;
935    } else {
936	exaDoMigration (pixmaps, 1, TRUE);
937    }
938
939    if (exaPixmapIsOffscreen (pPixmap) &&
940	(*pExaScr->info->PrepareSolid) (pPixmap, alu, planemask, pixel))
941    {
942	int nbox;
943	BoxPtr pBox;
944
945	nbox = REGION_NUM_RECTS (pRegion);
946	pBox = REGION_RECTS (pRegion);
947
948	while (nbox--)
949	{
950	    (*pExaScr->info->Solid) (pPixmap, pBox->x1, pBox->y1, pBox->x2,
951				     pBox->y2);
952	    pBox++;
953	}
954	(*pExaScr->info->DoneSolid) (pPixmap);
955	exaMarkSync(pDrawable->pScreen);
956
957	if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS) &&
958	    pDrawable->width == 1 && pDrawable->height == 1 &&
959	    pDrawable->bitsPerPixel != 24) {
960	    ExaPixmapPriv(pPixmap);
961
962	    switch (pDrawable->bitsPerPixel) {
963	    case 32:
964		*(CARD32*)pExaPixmap->sys_ptr = pixel;
965		break;
966	    case 16:
967		*(CARD16*)pExaPixmap->sys_ptr = pixel;
968		break;
969	    case 8:
970		*(CARD8*)pExaPixmap->sys_ptr = pixel;
971	    }
972
973	    REGION_UNION(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys,
974			 pRegion);
975	}
976
977	ret = TRUE;
978    }
979
980out:
981    REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff);
982
983    return ret;
984}
985
986/* Try to do an accelerated tile of the pTile into pRegion of pDrawable.
987 * Based on fbFillRegionTiled(), fbTile().
988 */
989Bool
990exaFillRegionTiled (DrawablePtr	pDrawable,
991		    RegionPtr	pRegion,
992		    PixmapPtr	pTile,
993		    DDXPointPtr pPatOrg,
994		    CARD32	planemask,
995		    CARD32	alu)
996{
997    ExaScreenPriv(pDrawable->pScreen);
998    PixmapPtr pPixmap;
999    ExaPixmapPrivPtr pExaPixmap;
1000    ExaPixmapPrivPtr pTileExaPixmap = ExaGetPixmapPriv(pTile);
1001    int xoff, yoff;
1002    int tileWidth, tileHeight;
1003    ExaMigrationRec pixmaps[2];
1004    int nbox = REGION_NUM_RECTS (pRegion);
1005    BoxPtr pBox = REGION_RECTS (pRegion);
1006    Bool ret = FALSE;
1007    int i;
1008
1009    tileWidth = pTile->drawable.width;
1010    tileHeight = pTile->drawable.height;
1011
1012    /* If we're filling with a solid color, grab it out and go to
1013     * FillRegionSolid, saving numerous copies.
1014     */
1015    if (tileWidth == 1 && tileHeight == 1)
1016	return exaFillRegionSolid(pDrawable, pRegion,
1017				  exaGetPixmapFirstPixel (pTile), planemask,
1018				  alu);
1019
1020    pixmaps[0].as_dst = TRUE;
1021    pixmaps[0].as_src = FALSE;
1022    pixmaps[0].pPix = pPixmap = exaGetDrawablePixmap (pDrawable);
1023    pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillTiled,
1024					    alu) ? NULL : pRegion;
1025    pixmaps[1].as_dst = FALSE;
1026    pixmaps[1].as_src = TRUE;
1027    pixmaps[1].pPix = pTile;
1028    pixmaps[1].pReg = NULL;
1029
1030    pExaPixmap = ExaGetPixmapPriv (pPixmap);
1031
1032    if (pExaPixmap->accel_blocked || pTileExaPixmap->accel_blocked)
1033    {
1034	return FALSE;
1035    } else {
1036	exaDoMigration (pixmaps, 2, TRUE);
1037    }
1038
1039    pPixmap = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
1040
1041    if (!pPixmap || !exaPixmapIsOffscreen(pTile))
1042	return FALSE;
1043
1044    if ((*pExaScr->info->PrepareCopy) (pTile, pPixmap, 1, 1, alu, planemask))
1045    {
1046	if (xoff || yoff)
1047	    REGION_TRANSLATE(pScreen, pRegion, xoff, yoff);
1048
1049	for (i = 0; i < nbox; i++)
1050	{
1051	    int height = pBox[i].y2 - pBox[i].y1;
1052	    int dstY = pBox[i].y1;
1053	    int tileY;
1054
1055	    if (alu == GXcopy)
1056		height = min(height, tileHeight);
1057
1058	    modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY);
1059
1060	    while (height > 0) {
1061		int width = pBox[i].x2 - pBox[i].x1;
1062		int dstX = pBox[i].x1;
1063		int tileX;
1064		int h = tileHeight - tileY;
1065
1066		if (alu == GXcopy)
1067		    width = min(width, tileWidth);
1068
1069		if (h > height)
1070		    h = height;
1071		height -= h;
1072
1073		modulus(dstX - xoff - pDrawable->x - pPatOrg->x, tileWidth,
1074			tileX);
1075
1076		while (width > 0) {
1077		    int w = tileWidth - tileX;
1078		    if (w > width)
1079			w = width;
1080		    width -= w;
1081
1082		    (*pExaScr->info->Copy) (pPixmap, tileX, tileY, dstX, dstY,
1083					    w, h);
1084		    dstX += w;
1085		    tileX = 0;
1086		}
1087		dstY += h;
1088		tileY = 0;
1089	    }
1090	}
1091	(*pExaScr->info->DoneCopy) (pPixmap);
1092
1093	/* With GXcopy, we only need to do the basic algorithm up to the tile
1094	 * size; then, we can just keep doubling the destination in each
1095	 * direction until it fills the box. This way, the number of copy
1096	 * operations is O(log(rx)) + O(log(ry)) instead of O(rx * ry), where
1097	 * rx/ry is the ratio between box and tile width/height. This can make
1098	 * a big difference if each driver copy incurs a significant constant
1099	 * overhead.
1100	 */
1101	if (alu != GXcopy)
1102	    ret = TRUE;
1103	else {
1104	    Bool more_copy = FALSE;
1105
1106	    for (i = 0; i < nbox; i++) {
1107		int dstX = pBox[i].x1 + tileWidth;
1108		int dstY = pBox[i].y1 + tileHeight;
1109
1110		if ((dstX < pBox[i].x2) || (dstY < pBox[i].y2)) {
1111		    more_copy = TRUE;
1112		    break;
1113		}
1114	    }
1115
1116	    if (more_copy == FALSE)
1117		ret = TRUE;
1118
1119	    if (more_copy && (*pExaScr->info->PrepareCopy) (pPixmap, pPixmap,
1120							    1, 1, alu, planemask)) {
1121		for (i = 0; i < nbox; i++)
1122		{
1123		    int dstX = pBox[i].x1 + tileWidth;
1124		    int dstY = pBox[i].y1 + tileHeight;
1125		    int width = min(pBox[i].x2 - dstX, tileWidth);
1126		    int height = min(pBox[i].y2 - pBox[i].y1, tileHeight);
1127
1128		    while (dstX < pBox[i].x2) {
1129			(*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1,
1130						dstX, pBox[i].y1, width, height);
1131			dstX += width;
1132			width = min(pBox[i].x2 - dstX, width * 2);
1133		    }
1134
1135		    width = pBox[i].x2 - pBox[i].x1;
1136		    height = min(pBox[i].y2 - dstY, tileHeight);
1137
1138		    while (dstY < pBox[i].y2) {
1139			(*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1,
1140						pBox[i].x1, dstY, width, height);
1141			dstY += height;
1142			height = min(pBox[i].y2 - dstY, height * 2);
1143		    }
1144		}
1145
1146		(*pExaScr->info->DoneCopy) (pPixmap);
1147
1148		ret = TRUE;
1149	    }
1150	}
1151
1152	exaMarkSync(pDrawable->pScreen);
1153
1154	if (xoff || yoff)
1155	    REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff);
1156    }
1157
1158    return ret;
1159}
1160
1161
1162/**
1163 * Accelerates GetImage for solid ZPixmap downloads from framebuffer memory.
1164 *
1165 * This is probably the only case we actually care about.  The rest fall through
1166 * to migration and fbGetImage, which hopefully will result in migration pushing
1167 * the pixmap out of framebuffer.
1168 */
1169void
1170exaGetImage (DrawablePtr pDrawable, int x, int y, int w, int h,
1171	     unsigned int format, unsigned long planeMask, char *d)
1172{
1173    ExaScreenPriv (pDrawable->pScreen);
1174    ExaMigrationRec pixmaps[1];
1175    BoxRec Box;
1176    RegionRec Reg;
1177    PixmapPtr pPix;
1178    int xoff, yoff;
1179    Bool ok;
1180
1181    pixmaps[0].as_dst = FALSE;
1182    pixmaps[0].as_src = TRUE;
1183    pixmaps[0].pPix = pPix = exaGetDrawablePixmap (pDrawable);
1184    pixmaps[0].pReg = &Reg;
1185
1186    exaGetDrawableDeltas (pDrawable, pPix, &xoff, &yoff);
1187
1188    Box.x1 = pDrawable->y + x + xoff;
1189    Box.y1 = pDrawable->y + y + yoff;
1190    Box.x2 = Box.x1 + w;
1191    Box.y2 = Box.y1 + h;
1192
1193    REGION_INIT(pScreen, &Reg, &Box, 1);
1194
1195    if (pExaScr->swappedOut)
1196	goto fallback;
1197
1198    exaDoMigration(pixmaps, 1, FALSE);
1199
1200    pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
1201
1202    if (pPix == NULL || pExaScr->info->DownloadFromScreen == NULL)
1203	goto fallback;
1204
1205    /* Only cover the ZPixmap, solid copy case. */
1206    if (format != ZPixmap || !EXA_PM_IS_SOLID(pDrawable, planeMask))
1207	goto fallback;
1208
1209    /* Only try to handle the 8bpp and up cases, since we don't want to think
1210     * about <8bpp.
1211     */
1212    if (pDrawable->bitsPerPixel < 8)
1213	goto fallback;
1214
1215    ok = pExaScr->info->DownloadFromScreen(pPix, pDrawable->x + x + xoff,
1216					   pDrawable->y + y + yoff, w, h, d,
1217					   PixmapBytePad(w, pDrawable->depth));
1218    if (ok) {
1219	exaWaitSync(pDrawable->pScreen);
1220	goto out;
1221    }
1222
1223fallback:
1224    EXA_FALLBACK(("from %p (%c)\n", pDrawable,
1225		  exaDrawableLocation(pDrawable)));
1226
1227    exaPrepareAccessReg (pDrawable, EXA_PREPARE_SRC, &Reg);
1228    fbGetImage (pDrawable, x, y, w, h, format, planeMask, d);
1229    exaFinishAccess (pDrawable, EXA_PREPARE_SRC);
1230
1231out:
1232    REGION_UNINIT(pScreen, &Reg);
1233}
1234