exa_accel.c revision 52397711
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					   pGC->clientClipType)) {
432	    dstregion = REGION_CREATE(pScreen, NullBox, 0);
433	    REGION_COPY(pScreen, dstregion, srcregion);
434	    REGION_TRANSLATE(pScreen, dstregion, dst_off_x - dx - src_off_x,
435			     dst_off_y - dy - src_off_y);
436	}
437    }
438
439    pixmaps[0].as_dst = TRUE;
440    pixmaps[0].as_src = FALSE;
441    pixmaps[0].pPix = pDstPixmap;
442    pixmaps[0].pReg = dstregion;
443    pixmaps[1].as_dst = FALSE;
444    pixmaps[1].as_src = TRUE;
445    pixmaps[1].pPix = pSrcPixmap;
446    pixmaps[1].pReg = srcregion;
447
448    pSrcExaPixmap = ExaGetPixmapPriv (pSrcPixmap);
449    pDstExaPixmap = ExaGetPixmapPriv (pDstPixmap);
450
451    /* Check whether the accelerator can use this pixmap.
452     * If the pitch of the pixmaps is out of range, there's nothing
453     * we can do but fall back to software rendering.
454     */
455    if (pSrcExaPixmap->accel_blocked & EXA_RANGE_PITCH ||
456        pDstExaPixmap->accel_blocked & EXA_RANGE_PITCH)
457	goto fallback;
458
459    /* If the width or the height of either of the pixmaps
460     * is out of range, check whether the boxes are actually out of the
461     * addressable range as well. If they aren't, we can still do
462     * the copying in hardware.
463     */
464    if (pSrcExaPixmap->accel_blocked || pDstExaPixmap->accel_blocked) {
465        int i;
466
467        for (i = 0; i < nbox; i++) {
468            /* src */
469            if ((pbox[i].x2 + dx + src_off_x) >= pExaScr->info->maxX ||
470                (pbox[i].y2 + dy + src_off_y) >= pExaScr->info->maxY)
471                goto fallback;
472
473            /* dst */
474            if ((pbox[i].x2 + dst_off_x) >= pExaScr->info->maxX ||
475                (pbox[i].y2 + dst_off_y) >= pExaScr->info->maxY)
476                goto fallback;
477        }
478    }
479
480    exaDoMigration (pixmaps, 2, TRUE);
481
482    /* Mixed directions must be handled specially if the card is lame */
483    if ((pExaScr->info->flags & EXA_TWO_BITBLT_DIRECTIONS) &&
484	reverse != upsidedown) {
485	if (exaCopyNtoNTwoDir(pSrcDrawable, pDstDrawable, pGC, pbox, nbox,
486			       dx, dy))
487	    goto out;
488	goto fallback;
489    }
490
491    if (!exaPixmapIsOffscreen(pSrcPixmap) ||
492	!exaPixmapIsOffscreen(pDstPixmap) ||
493	!(*pExaScr->info->PrepareCopy) (pSrcPixmap, pDstPixmap, reverse ? -1 : 1,
494					upsidedown ? -1 : 1,
495					pGC ? pGC->alu : GXcopy,
496					pGC ? pGC->planemask : FB_ALLONES)) {
497	goto fallback;
498    }
499
500    while (nbox--)
501    {
502	(*pExaScr->info->Copy) (pDstPixmap,
503				pbox->x1 + dx + src_off_x,
504				pbox->y1 + dy + src_off_y,
505				pbox->x1 + dst_off_x, pbox->y1 + dst_off_y,
506				pbox->x2 - pbox->x1, pbox->y2 - pbox->y1);
507	pbox++;
508    }
509
510    (*pExaScr->info->DoneCopy) (pDstPixmap);
511    exaMarkSync (pDstDrawable->pScreen);
512
513    goto out;
514
515fallback:
516    EXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrcDrawable, pDstDrawable,
517		  exaDrawableLocation(pSrcDrawable),
518		  exaDrawableLocation(pDstDrawable)));
519    exaPrepareAccessReg (pDstDrawable, EXA_PREPARE_DEST, dstregion);
520    exaPrepareAccessReg (pSrcDrawable, EXA_PREPARE_SRC, srcregion);
521    fbCopyNtoN (pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse,
522		upsidedown, bitplane, closure);
523    exaFinishAccess (pSrcDrawable, EXA_PREPARE_SRC);
524    exaFinishAccess (pDstDrawable, EXA_PREPARE_DEST);
525
526out:
527    if (dstregion) {
528	REGION_UNINIT(pScreen, dstregion);
529	REGION_DESTROY(pScreen, dstregion);
530    }
531    if (srcregion) {
532	REGION_UNINIT(pScreen, srcregion);
533	REGION_DESTROY(pScreen, srcregion);
534    }
535}
536
537RegionPtr
538exaCopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC,
539	    int srcx, int srcy, int width, int height, int dstx, int dsty)
540{
541    ExaScreenPriv (pDstDrawable->pScreen);
542
543    if (pExaScr->swappedOut) {
544        return  ExaCheckCopyArea(pSrcDrawable, pDstDrawable, pGC,
545                                 srcx, srcy, width, height, dstx, dsty);
546    }
547
548    return  fbDoCopy (pSrcDrawable, pDstDrawable, pGC,
549                      srcx, srcy, width, height,
550                      dstx, dsty, exaCopyNtoN, 0, NULL);
551}
552
553static void
554exaPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
555	     DDXPointPtr ppt)
556{
557    int i;
558    xRectangle *prect;
559
560    /* If we can't reuse the current GC as is, don't bother accelerating the
561     * points.
562     */
563    if (pGC->fillStyle != FillSolid) {
564	ExaCheckPolyPoint(pDrawable, pGC, mode, npt, ppt);
565	return;
566    }
567
568    prect = xalloc(sizeof(xRectangle) * npt);
569    for (i = 0; i < npt; i++) {
570	prect[i].x = ppt[i].x;
571	prect[i].y = ppt[i].y;
572	if (i > 0 && mode == CoordModePrevious) {
573	    prect[i].x += prect[i - 1].x;
574	    prect[i].y += prect[i - 1].y;
575	}
576	prect[i].width = 1;
577	prect[i].height = 1;
578    }
579    pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect);
580    xfree(prect);
581}
582
583/**
584 * exaPolylines() checks if it can accelerate the lines as a group of
585 * horizontal or vertical lines (rectangles), and uses existing rectangle fill
586 * acceleration if so.
587 */
588static void
589exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
590	     DDXPointPtr ppt)
591{
592    xRectangle *prect;
593    int x1, x2, y1, y2;
594    int i;
595
596    /* Don't try to do wide lines or non-solid fill style. */
597    if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid ||
598	pGC->fillStyle != FillSolid) {
599	ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
600	return;
601    }
602
603    prect = xalloc(sizeof(xRectangle) * (npt - 1));
604    x1 = ppt[0].x;
605    y1 = ppt[0].y;
606    /* If we have any non-horizontal/vertical, fall back. */
607    for (i = 0; i < npt - 1; i++) {
608	if (mode == CoordModePrevious) {
609	    x2 = x1 + ppt[i + 1].x;
610	    y2 = y1 + ppt[i + 1].y;
611	} else {
612	    x2 = ppt[i + 1].x;
613	    y2 = ppt[i + 1].y;
614	}
615
616	if (x1 != x2 && y1 != y2) {
617	    xfree(prect);
618	    ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
619	    return;
620	}
621
622	if (x1 < x2) {
623	    prect[i].x = x1;
624	    prect[i].width = x2 - x1 + 1;
625	} else {
626	    prect[i].x = x2;
627	    prect[i].width = x1 - x2 + 1;
628	}
629	if (y1 < y2) {
630	    prect[i].y = y1;
631	    prect[i].height = y2 - y1 + 1;
632	} else {
633	    prect[i].y = y2;
634	    prect[i].height = y1 - y2 + 1;
635	}
636
637	x1 = x2;
638	y1 = y2;
639    }
640    pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect);
641    xfree(prect);
642}
643
644/**
645 * exaPolySegment() checks if it can accelerate the lines as a group of
646 * horizontal or vertical lines (rectangles), and uses existing rectangle fill
647 * acceleration if so.
648 */
649static void
650exaPolySegment (DrawablePtr pDrawable, GCPtr pGC, int nseg,
651		xSegment *pSeg)
652{
653    xRectangle *prect;
654    int i;
655
656    /* Don't try to do wide lines or non-solid fill style. */
657    if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid ||
658	pGC->fillStyle != FillSolid)
659    {
660	ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg);
661	return;
662    }
663
664    /* If we have any non-horizontal/vertical, fall back. */
665    for (i = 0; i < nseg; i++) {
666	if (pSeg[i].x1 != pSeg[i].x2 && pSeg[i].y1 != pSeg[i].y2) {
667	    ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg);
668	    return;
669	}
670    }
671
672    prect = xalloc(sizeof(xRectangle) * nseg);
673    for (i = 0; i < nseg; i++) {
674	if (pSeg[i].x1 < pSeg[i].x2) {
675	    prect[i].x = pSeg[i].x1;
676	    prect[i].width = pSeg[i].x2 - pSeg[i].x1 + 1;
677	} else {
678	    prect[i].x = pSeg[i].x2;
679	    prect[i].width = pSeg[i].x1 - pSeg[i].x2 + 1;
680	}
681	if (pSeg[i].y1 < pSeg[i].y2) {
682	    prect[i].y = pSeg[i].y1;
683	    prect[i].height = pSeg[i].y2 - pSeg[i].y1 + 1;
684	} else {
685	    prect[i].y = pSeg[i].y2;
686	    prect[i].height = pSeg[i].y1 - pSeg[i].y2 + 1;
687	}
688
689	/* don't paint last pixel */
690	if (pGC->capStyle == CapNotLast) {
691	    if (prect[i].width == 1)
692		prect[i].height--;
693	    else
694		prect[i].width--;
695	}
696    }
697    pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect);
698    xfree(prect);
699}
700
701static Bool exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion,
702				Pixel pixel, CARD32 planemask, CARD32 alu,
703				unsigned int clientClipType);
704
705static void
706exaPolyFillRect(DrawablePtr pDrawable,
707		GCPtr	    pGC,
708		int	    nrect,
709		xRectangle  *prect)
710{
711    ExaScreenPriv (pDrawable->pScreen);
712    RegionPtr	    pClip = fbGetCompositeClip(pGC);
713    PixmapPtr	    pPixmap = exaGetDrawablePixmap(pDrawable);
714    ExaPixmapPriv (pPixmap);
715    register BoxPtr pbox;
716    BoxPtr	    pextent;
717    int		    extentX1, extentX2, extentY1, extentY2;
718    int		    fullX1, fullX2, fullY1, fullY2;
719    int		    partX1, partX2, partY1, partY2;
720    int		    xoff, yoff;
721    int		    xorg, yorg;
722    int		    n;
723    ExaMigrationRec pixmaps[2];
724    RegionPtr pReg = RECTS_TO_REGION(pScreen, nrect, prect, CT_UNSORTED);
725
726    /* Compute intersection of rects and clip region */
727    REGION_TRANSLATE(pScreen, pReg, pDrawable->x, pDrawable->y);
728    REGION_INTERSECT(pScreen, pReg, pClip, pReg);
729
730    if (!REGION_NUM_RECTS(pReg)) {
731	goto out;
732    }
733
734    pixmaps[0].as_dst = TRUE;
735    pixmaps[0].as_src = FALSE;
736    pixmaps[0].pPix = pPixmap;
737    pixmaps[0].pReg = NULL;
738
739    exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
740
741    if (pExaScr->swappedOut || pExaPixmap->accel_blocked)
742    {
743	goto fallback;
744    }
745
746    /* For ROPs where overlaps don't matter, convert rectangles to region and
747     * call exaFillRegion{Solid,Tiled}.
748     */
749    if ((pGC->fillStyle == FillSolid || pGC->fillStyle == FillTiled) &&
750	(nrect == 1 || pGC->alu == GXcopy || pGC->alu == GXclear ||
751	 pGC->alu == GXnoop || pGC->alu == GXcopyInverted ||
752	 pGC->alu == GXset)) {
753	if (((pGC->fillStyle == FillSolid || pGC->tileIsPixel) &&
754	     exaFillRegionSolid(pDrawable, pReg, pGC->fillStyle == FillSolid ?
755				pGC->fgPixel : pGC->tile.pixel,	pGC->planemask,
756				pGC->alu, pGC->clientClipType)) ||
757	    (pGC->fillStyle == FillTiled && !pGC->tileIsPixel &&
758	     exaFillRegionTiled(pDrawable, pReg, pGC->tile.pixmap, &pGC->patOrg,
759				pGC->planemask, pGC->alu,
760				pGC->clientClipType))) {
761	    goto out;
762	}
763    }
764
765    if (pGC->fillStyle != FillSolid &&
766	!(pGC->tileIsPixel && pGC->fillStyle == FillTiled))
767    {
768	goto fallback;
769    }
770
771    exaDoMigration (pixmaps, 1, TRUE);
772
773    if (!exaPixmapIsOffscreen (pPixmap) ||
774	!(*pExaScr->info->PrepareSolid) (pPixmap,
775					 pGC->alu,
776					 pGC->planemask,
777					 pGC->fgPixel))
778    {
779fallback:
780	ExaCheckPolyFillRect (pDrawable, pGC, nrect, prect);
781	goto out;
782    }
783
784    xorg = pDrawable->x;
785    yorg = pDrawable->y;
786
787    pextent = REGION_EXTENTS(pGC->pScreen, pClip);
788    extentX1 = pextent->x1;
789    extentY1 = pextent->y1;
790    extentX2 = pextent->x2;
791    extentY2 = pextent->y2;
792    while (nrect--)
793    {
794	fullX1 = prect->x + xorg;
795	fullY1 = prect->y + yorg;
796	fullX2 = fullX1 + (int) prect->width;
797	fullY2 = fullY1 + (int) prect->height;
798	prect++;
799
800	if (fullX1 < extentX1)
801	    fullX1 = extentX1;
802
803	if (fullY1 < extentY1)
804	    fullY1 = extentY1;
805
806	if (fullX2 > extentX2)
807	    fullX2 = extentX2;
808
809	if (fullY2 > extentY2)
810	    fullY2 = extentY2;
811
812	if ((fullX1 >= fullX2) || (fullY1 >= fullY2))
813	    continue;
814	n = REGION_NUM_RECTS (pClip);
815	if (n == 1)
816	{
817	    (*pExaScr->info->Solid) (pPixmap,
818				     fullX1 + xoff, fullY1 + yoff,
819				     fullX2 + xoff, fullY2 + yoff);
820	}
821	else
822	{
823	    pbox = REGION_RECTS(pClip);
824	    /*
825	     * clip the rectangle to each box in the clip region
826	     * this is logically equivalent to calling Intersect(),
827	     * but rectangles may overlap each other here.
828	     */
829	    while(n--)
830	    {
831		partX1 = pbox->x1;
832		if (partX1 < fullX1)
833		    partX1 = fullX1;
834		partY1 = pbox->y1;
835		if (partY1 < fullY1)
836		    partY1 = fullY1;
837		partX2 = pbox->x2;
838		if (partX2 > fullX2)
839		    partX2 = fullX2;
840		partY2 = pbox->y2;
841		if (partY2 > fullY2)
842		    partY2 = fullY2;
843
844		pbox++;
845
846		if (partX1 < partX2 && partY1 < partY2) {
847		    (*pExaScr->info->Solid) (pPixmap,
848					     partX1 + xoff, partY1 + yoff,
849					     partX2 + xoff, partY2 + yoff);
850		}
851	    }
852	}
853    }
854    (*pExaScr->info->DoneSolid) (pPixmap);
855    exaMarkSync(pDrawable->pScreen);
856
857out:
858    REGION_UNINIT(pScreen, pReg);
859    REGION_DESTROY(pScreen, pReg);
860}
861
862const GCOps exaOps = {
863    exaFillSpans,
864    ExaCheckSetSpans,
865    exaPutImage,
866    exaCopyArea,
867    ExaCheckCopyPlane,
868    exaPolyPoint,
869    exaPolylines,
870    exaPolySegment,
871    miPolyRectangle,
872    ExaCheckPolyArc,
873    miFillPolygon,
874    exaPolyFillRect,
875    miPolyFillArc,
876    miPolyText8,
877    miPolyText16,
878    miImageText8,
879    miImageText16,
880    ExaCheckImageGlyphBlt,
881    ExaCheckPolyGlyphBlt,
882    ExaCheckPushPixels,
883};
884
885void
886exaCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
887{
888    RegionRec	rgnDst;
889    int		dx, dy;
890    PixmapPtr	pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin);
891
892    dx = ptOldOrg.x - pWin->drawable.x;
893    dy = ptOldOrg.y - pWin->drawable.y;
894    REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy);
895
896    REGION_INIT (pWin->drawable.pScreen, &rgnDst, NullBox, 0);
897
898    REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc);
899#ifdef COMPOSITE
900    if (pPixmap->screen_x || pPixmap->screen_y)
901	REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst,
902			  -pPixmap->screen_x, -pPixmap->screen_y);
903#endif
904
905    fbCopyRegion (&pPixmap->drawable, &pPixmap->drawable,
906		  NULL,
907		  &rgnDst, dx, dy, exaCopyNtoN, 0, NULL);
908
909    REGION_UNINIT(pWin->drawable.pScreen, &rgnDst);
910}
911
912static Bool
913exaFillRegionSolid (DrawablePtr	pDrawable, RegionPtr pRegion, Pixel pixel,
914		    CARD32 planemask, CARD32 alu, unsigned int clientClipType)
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, clientClipType)
928	? NULL : pRegion;
929
930    exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
931    REGION_TRANSLATE(pScreen, pRegion, xoff, yoff);
932
933    if (pExaPixmap->accel_blocked)
934    {
935	goto out;
936    } else {
937	exaDoMigration (pixmaps, 1, TRUE);
938    }
939
940    if (exaPixmapIsOffscreen (pPixmap) &&
941	(*pExaScr->info->PrepareSolid) (pPixmap, alu, planemask, pixel))
942    {
943	int nbox;
944	BoxPtr pBox;
945
946	nbox = REGION_NUM_RECTS (pRegion);
947	pBox = REGION_RECTS (pRegion);
948
949	while (nbox--)
950	{
951	    (*pExaScr->info->Solid) (pPixmap, pBox->x1, pBox->y1, pBox->x2,
952				     pBox->y2);
953	    pBox++;
954	}
955	(*pExaScr->info->DoneSolid) (pPixmap);
956	exaMarkSync(pDrawable->pScreen);
957
958	if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS) &&
959	    pDrawable->width == 1 && pDrawable->height == 1 &&
960	    pDrawable->bitsPerPixel != 24) {
961	    ExaPixmapPriv(pPixmap);
962
963	    switch (pDrawable->bitsPerPixel) {
964	    case 32:
965		*(CARD32*)pExaPixmap->sys_ptr = pixel;
966		break;
967	    case 16:
968		*(CARD16*)pExaPixmap->sys_ptr = pixel;
969		break;
970	    case 8:
971		*(CARD8*)pExaPixmap->sys_ptr = pixel;
972	    }
973
974	    REGION_UNION(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys,
975			 pRegion);
976	}
977
978	ret = TRUE;
979    }
980
981out:
982    REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff);
983
984    return ret;
985}
986
987/* Try to do an accelerated tile of the pTile into pRegion of pDrawable.
988 * Based on fbFillRegionTiled(), fbTile().
989 */
990Bool
991exaFillRegionTiled (DrawablePtr pDrawable, RegionPtr pRegion, PixmapPtr pTile,
992		    DDXPointPtr pPatOrg, CARD32 planemask, CARD32 alu,
993		    unsigned int clientClipType)
994{
995    ExaScreenPriv(pDrawable->pScreen);
996    PixmapPtr pPixmap;
997    ExaPixmapPrivPtr pExaPixmap;
998    ExaPixmapPrivPtr pTileExaPixmap = ExaGetPixmapPriv(pTile);
999    int xoff, yoff;
1000    int tileWidth, tileHeight;
1001    ExaMigrationRec pixmaps[2];
1002    int nbox = REGION_NUM_RECTS (pRegion);
1003    BoxPtr pBox = REGION_RECTS (pRegion);
1004    Bool ret = FALSE;
1005    int i;
1006
1007    tileWidth = pTile->drawable.width;
1008    tileHeight = pTile->drawable.height;
1009
1010    /* If we're filling with a solid color, grab it out and go to
1011     * FillRegionSolid, saving numerous copies.
1012     */
1013    if (tileWidth == 1 && tileHeight == 1)
1014	return exaFillRegionSolid(pDrawable, pRegion,
1015				  exaGetPixmapFirstPixel (pTile), planemask,
1016				  alu, clientClipType);
1017
1018    pixmaps[0].as_dst = TRUE;
1019    pixmaps[0].as_src = FALSE;
1020    pixmaps[0].pPix = pPixmap = exaGetDrawablePixmap (pDrawable);
1021    pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillTiled,
1022					    alu, clientClipType)
1023	? NULL : pRegion;
1024    pixmaps[1].as_dst = FALSE;
1025    pixmaps[1].as_src = TRUE;
1026    pixmaps[1].pPix = pTile;
1027    pixmaps[1].pReg = NULL;
1028
1029    pExaPixmap = ExaGetPixmapPriv (pPixmap);
1030
1031    if (pExaPixmap->accel_blocked || pTileExaPixmap->accel_blocked)
1032    {
1033	return FALSE;
1034    } else {
1035	exaDoMigration (pixmaps, 2, TRUE);
1036    }
1037
1038    pPixmap = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
1039
1040    if (!pPixmap || !exaPixmapIsOffscreen(pTile))
1041	return FALSE;
1042
1043    if ((*pExaScr->info->PrepareCopy) (pTile, pPixmap, 1, 1, alu, planemask))
1044    {
1045	if (xoff || yoff)
1046	    REGION_TRANSLATE(pScreen, pRegion, xoff, yoff);
1047
1048	for (i = 0; i < nbox; i++)
1049	{
1050	    int height = pBox[i].y2 - pBox[i].y1;
1051	    int dstY = pBox[i].y1;
1052	    int tileY;
1053
1054	    if (alu == GXcopy)
1055		height = min(height, tileHeight);
1056
1057	    modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY);
1058
1059	    while (height > 0) {
1060		int width = pBox[i].x2 - pBox[i].x1;
1061		int dstX = pBox[i].x1;
1062		int tileX;
1063		int h = tileHeight - tileY;
1064
1065		if (alu == GXcopy)
1066		    width = min(width, tileWidth);
1067
1068		if (h > height)
1069		    h = height;
1070		height -= h;
1071
1072		modulus(dstX - xoff - pDrawable->x - pPatOrg->x, tileWidth,
1073			tileX);
1074
1075		while (width > 0) {
1076		    int w = tileWidth - tileX;
1077		    if (w > width)
1078			w = width;
1079		    width -= w;
1080
1081		    (*pExaScr->info->Copy) (pPixmap, tileX, tileY, dstX, dstY,
1082					    w, h);
1083		    dstX += w;
1084		    tileX = 0;
1085		}
1086		dstY += h;
1087		tileY = 0;
1088	    }
1089	}
1090	(*pExaScr->info->DoneCopy) (pPixmap);
1091
1092	/* With GXcopy, we only need to do the basic algorithm up to the tile
1093	 * size; then, we can just keep doubling the destination in each
1094	 * direction until it fills the box. This way, the number of copy
1095	 * operations is O(log(rx)) + O(log(ry)) instead of O(rx * ry), where
1096	 * rx/ry is the ratio between box and tile width/height. This can make
1097	 * a big difference if each driver copy incurs a significant constant
1098	 * overhead.
1099	 */
1100	if (alu != GXcopy)
1101	    ret = TRUE;
1102	else {
1103	    Bool more_copy = FALSE;
1104
1105	    for (i = 0; i < nbox; i++) {
1106		int dstX = pBox[i].x1 + tileWidth;
1107		int dstY = pBox[i].y1 + tileHeight;
1108
1109		if ((dstX < pBox[i].x2) || (dstY < pBox[i].y2)) {
1110		    more_copy = TRUE;
1111		    break;
1112		}
1113	    }
1114
1115	    if (more_copy == FALSE)
1116		ret = TRUE;
1117
1118	    if (more_copy && (*pExaScr->info->PrepareCopy) (pPixmap, pPixmap,
1119							    1, 1, alu, planemask)) {
1120		for (i = 0; i < nbox; i++)
1121		{
1122		    int dstX = pBox[i].x1 + tileWidth;
1123		    int dstY = pBox[i].y1 + tileHeight;
1124		    int width = min(pBox[i].x2 - dstX, tileWidth);
1125		    int height = min(pBox[i].y2 - pBox[i].y1, tileHeight);
1126
1127		    while (dstX < pBox[i].x2) {
1128			(*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1,
1129						dstX, pBox[i].y1, width, height);
1130			dstX += width;
1131			width = min(pBox[i].x2 - dstX, width * 2);
1132		    }
1133
1134		    width = pBox[i].x2 - pBox[i].x1;
1135		    height = min(pBox[i].y2 - dstY, tileHeight);
1136
1137		    while (dstY < pBox[i].y2) {
1138			(*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1,
1139						pBox[i].x1, dstY, width, height);
1140			dstY += height;
1141			height = min(pBox[i].y2 - dstY, height * 2);
1142		    }
1143		}
1144
1145		(*pExaScr->info->DoneCopy) (pPixmap);
1146
1147		ret = TRUE;
1148	    }
1149	}
1150
1151	exaMarkSync(pDrawable->pScreen);
1152
1153	if (xoff || yoff)
1154	    REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff);
1155    }
1156
1157    return ret;
1158}
1159
1160
1161/**
1162 * Accelerates GetImage for solid ZPixmap downloads from framebuffer memory.
1163 *
1164 * This is probably the only case we actually care about.  The rest fall through
1165 * to migration and fbGetImage, which hopefully will result in migration pushing
1166 * the pixmap out of framebuffer.
1167 */
1168void
1169exaGetImage (DrawablePtr pDrawable, int x, int y, int w, int h,
1170	     unsigned int format, unsigned long planeMask, char *d)
1171{
1172    ExaScreenPriv (pDrawable->pScreen);
1173    ExaMigrationRec pixmaps[1];
1174    BoxRec Box;
1175    RegionRec Reg;
1176    PixmapPtr pPix;
1177    int xoff, yoff;
1178    Bool ok;
1179
1180    pixmaps[0].as_dst = FALSE;
1181    pixmaps[0].as_src = TRUE;
1182    pixmaps[0].pPix = pPix = exaGetDrawablePixmap (pDrawable);
1183    pixmaps[0].pReg = &Reg;
1184
1185    exaGetDrawableDeltas (pDrawable, pPix, &xoff, &yoff);
1186
1187    Box.x1 = pDrawable->y + x + xoff;
1188    Box.y1 = pDrawable->y + y + yoff;
1189    Box.x2 = Box.x1 + w;
1190    Box.y2 = Box.y1 + h;
1191
1192    REGION_INIT(pScreen, &Reg, &Box, 1);
1193
1194    if (pExaScr->swappedOut)
1195	goto fallback;
1196
1197    exaDoMigration(pixmaps, 1, FALSE);
1198
1199    pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
1200
1201    if (pPix == NULL || pExaScr->info->DownloadFromScreen == NULL)
1202	goto fallback;
1203
1204    /* Only cover the ZPixmap, solid copy case. */
1205    if (format != ZPixmap || !EXA_PM_IS_SOLID(pDrawable, planeMask))
1206	goto fallback;
1207
1208    /* Only try to handle the 8bpp and up cases, since we don't want to think
1209     * about <8bpp.
1210     */
1211    if (pDrawable->bitsPerPixel < 8)
1212	goto fallback;
1213
1214    ok = pExaScr->info->DownloadFromScreen(pPix, pDrawable->x + x + xoff,
1215					   pDrawable->y + y + yoff, w, h, d,
1216					   PixmapBytePad(w, pDrawable->depth));
1217    if (ok) {
1218	exaWaitSync(pDrawable->pScreen);
1219	goto out;
1220    }
1221
1222fallback:
1223    EXA_FALLBACK(("from %p (%c)\n", pDrawable,
1224		  exaDrawableLocation(pDrawable)));
1225
1226    exaPrepareAccessReg (pDrawable, EXA_PREPARE_SRC, &Reg);
1227    fbGetImage (pDrawable, x, y, w, h, format, planeMask, d);
1228    exaFinishAccess (pDrawable, EXA_PREPARE_SRC);
1229
1230out:
1231    REGION_UNINIT(pScreen, &Reg);
1232}
1233