Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / mesa / drivers / dri / mga / mgapixel.c
1 /*
2  * Copyright 2000 Compaq Computer Inc. and VA Linux Systems, Inc.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * on the rights to use, copy, modify, merge, publish, distribute, sub
9  * license, and/or sell copies of the Software, and to permit persons to whom
10  * the Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 /**
26  * \file mgapixel.c
27  * Implement framebuffer pixel operations for MGA.
28  *
29  * \todo
30  * Someday the accelerated \c glReadPixels and \c glDrawPixels paths need to
31  * be resurrected.  They are currently ifdef'ed out because they don't seem
32  * to work and they only get activated some very rare circumstances.
33  *
34  * \author Keith Whitwell <keith@tungstengraphics.com>
35  * \author Gareth Hughes <gareth@valinux.com>
36  */
37
38 #include "main/mtypes.h"
39 #include "main/macros.h"
40 #include "mgadd.h"
41 #include "mgacontext.h"
42 #include "mgaioctl.h"
43 #include "mgapixel.h"
44 #include "mgastate.h"
45
46 #include "swrast/swrast.h"
47 #include "main/imports.h"
48
49 #if 0
50 #define IS_AGP_MEM( mmesa, p )                                            \
51    ((unsigned long)mmesa->mgaScreen->buffers.map <= ((unsigned long)p) && \
52     (unsigned long)mmesa->mgaScreen->buffers.map +                        \
53     (unsigned long)mmesa->mgaScreen->buffers.size > ((unsigned long)p))
54 #define AGP_OFFSET( mmesa, p )                                            \
55      (((unsigned long)p) - (unsigned long)mmesa->mgaScreen->buffers.map)
56
57
58 static GLboolean
59 check_depth_stencil_24_8( const struct gl_context *ctx, GLenum type,
60                           const struct gl_pixelstore_attrib *packing,
61                           const void *pixels, GLint sz,
62                           GLint pitch )
63 {
64    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
65
66    return ( type == GL_UNSIGNED_INT_24_8 &&
67             ctx->Visual->DepthBits == 24 &&
68             ctx->Visual->StencilBits == 8 &&
69             mmesa->mgaScreen->cpp == 4 &&
70             mmesa->hw_stencil &&
71             !ctx->Pixel.IndexShift &&
72             !ctx->Pixel.IndexOffset &&
73             !ctx->Pixel.MapStencilFlag &&
74             ctx->Pixel.DepthBias == 0.0 &&
75             ctx->Pixel.DepthScale == 1.0 &&
76             !packing->SwapBytes &&
77             pitch % 32 == 0 &&
78             pitch < 4096 );
79 }
80
81
82 static GLboolean
83 check_depth( const struct gl_context *ctx, GLenum type,
84              const struct gl_pixelstore_attrib *packing,
85              const void *pixels, GLint sz, GLint pitch )
86 {
87    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
88
89    if ( IS_AGP_MEM( mmesa, pixels ) &&
90         !( ( type == GL_UNSIGNED_INT && mmesa->mgaScreen->cpp == 4 ) ||
91            ( type == GL_UNSIGNED_SHORT && mmesa->mgaScreen->cpp == 2 ) ) )
92       return GL_FALSE;
93
94    return ( ctx->Pixel.DepthBias == 0.0 &&
95             ctx->Pixel.DepthScale == 1.0 &&
96             !packing->SwapBytes &&
97             pitch % 32 == 0 &&
98             pitch < 4096 );
99 }
100
101
102 static GLboolean
103 check_color( const struct gl_context *ctx, GLenum type, GLenum format,
104              const struct gl_pixelstore_attrib *packing,
105              const void *pixels, GLint sz, GLint pitch )
106 {
107    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
108    GLuint cpp = mmesa->mgaScreen->cpp;
109
110    /* Can't do conversions on agp reads/draws.
111     */
112    if ( IS_AGP_MEM( mmesa, pixels ) &&
113         !( pitch % 32 == 0 && pitch < 4096 &&
114            ( ( type == GL_UNSIGNED_BYTE &&
115                cpp == 4 && format == GL_BGRA ) ||
116              ( type == GL_UNSIGNED_INT_8_8_8_8 &&
117                cpp == 4 && format == GL_BGRA ) ||
118              ( type == GL_UNSIGNED_SHORT_5_6_5_REV &&
119                cpp == 2 && format == GL_RGB ) ) ) )
120       return GL_FALSE;
121
122    return (!ctx->_ImageTransferState &&
123            !packing->SwapBytes &&
124            !packing->LsbFirst);
125 }
126
127 static GLboolean
128 check_color_per_fragment_ops( const struct gl_context *ctx )
129 {
130    return (!(       ctx->Color.AlphaEnabled ||
131                     ctx->Depth.Test ||
132                     ctx->Fog.Enabled ||
133                     ctx->Scissor.Enabled ||
134                     ctx->Stencil._Enabled ||
135                     !ctx->Color.ColorMask[0][0] ||
136                     !ctx->Color.ColorMask[0][1] ||
137                     !ctx->Color.ColorMask[0][2] ||
138                     !ctx->Color.ColorMask[0][3] ||
139                     ctx->Color.ColorLogicOpEnabled ||
140                     ctx->Texture._EnabledUnits
141            ) &&
142            ctx->Current.RasterPosValid &&
143            ctx->Pixel.ZoomX == 1.0F &&
144            (ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F));
145 }
146
147 static GLboolean
148 check_depth_per_fragment_ops( const struct gl_context *ctx )
149 {
150    return ( ctx->Current.RasterPosValid &&
151             ctx->Color.ColorMask[0][RCOMP] == 0 &&
152             ctx->Color.ColorMask[0][BCOMP] == 0 &&
153             ctx->Color.ColorMask[0][GCOMP] == 0 &&
154             ctx->Color.ColorMask[0][ACOMP] == 0 &&
155             ctx->Pixel.ZoomX == 1.0F &&
156             ( ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F ) );
157 }
158
159 /* In addition to the requirements for depth:
160  */
161 #if defined(MESA_packed_depth_stencil)
162 static GLboolean
163 check_stencil_per_fragment_ops( const struct gl_context *ctx )
164 {
165    return ( !ctx->Pixel.IndexShift &&
166             !ctx->Pixel.IndexOffset );
167 }
168 #endif
169
170
171 static GLboolean
172 clip_pixelrect( const struct gl_context *ctx,
173                 const struct gl_framebuffer *buffer,
174                 GLint *x, GLint *y,
175                 GLsizei *width, GLsizei *height,
176                 GLint *skipPixels, GLint *skipRows,
177                 GLint *size )
178 {
179    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
180
181    *width = MIN2(*width, MAX_WIDTH); /* redundant? */
182
183    /* left clipping */
184    if (*x < buffer->_Xmin) {
185       *skipPixels += (buffer->_Xmin - *x);
186       *width -= (buffer->_Xmin - *x);
187       *x = buffer->_Xmin;
188    }
189
190    /* right clipping */
191    if (*x + *width > buffer->_Xmax)
192       *width -= (*x + *width - buffer->_Xmax - 1);
193
194    if (*width <= 0)
195       return GL_FALSE;
196
197    /* bottom clipping */
198    if (*y < buffer->_Ymin) {
199       *skipRows += (buffer->_Ymin - *y);
200       *height -= (buffer->_Ymin - *y);
201       *y = buffer->_Ymin;
202    }
203
204    /* top clipping */
205    if (*y + *height > buffer->_Ymax)
206       *height -= (*y + *height - buffer->_Ymax - 1);
207
208    if (*height <= 0)
209       return GL_FALSE;
210
211    *size = ((*y + *height - 1) * mmesa->mgaScreen->frontPitch +
212             (*x + *width - 1) * mmesa->mgaScreen->cpp);
213
214    return GL_TRUE;
215 }
216
217 static GLboolean
218 mgaTryReadPixels( struct gl_context *ctx,
219                   GLint x, GLint y, GLsizei width, GLsizei height,
220                   GLenum format, GLenum type,
221                   const struct gl_pixelstore_attrib *pack,
222                   GLvoid *pixels )
223 {
224    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
225    GLint size, skipPixels, skipRows;
226    GLint pitch = pack->RowLength ? pack->RowLength : width;
227    GLboolean ok;
228
229    GLuint planemask;
230    GLuint source;
231 #if 0
232    drmMGABlit blit;
233    GLuint dest;
234    GLint source_pitch, dest_pitch;
235    GLint delta_sx, delta_sy;
236    GLint delta_dx, delta_dy;
237    GLint blit_height, ydir;
238 #endif
239
240    if (!clip_pixelrect(ctx, ctx->ReadBuffer,
241                        &x, &y, &width, &height,
242                        &skipPixels, &skipRows, &size)) {
243       return GL_TRUE;
244    }
245
246    /* Only accelerate reading to agp buffers.
247     */
248    if ( !IS_AGP_MEM(mmesa, (char *)pixels) ||
249         !IS_AGP_MEM(mmesa, (char *)pixels + size) )
250       return GL_FALSE;
251
252    switch (format) {
253    case GL_DEPTH_STENCIL:
254       ok = check_depth_stencil_24_8(ctx, type, pack, pixels, size, pitch);
255       planemask = ~0;
256       source = mmesa->mgaScreen->depthOffset;
257       break;
258
259    case GL_DEPTH_COMPONENT:
260       ok = check_depth(ctx, type, pack, pixels, size, pitch);
261
262       /* Can't accelerate at this depth -- planemask does the wrong
263        * thing; it doesn't clear the low order bits in the
264        * destination, instead it leaves them untouched.
265        *
266        * Could get the acclerator to solid fill the destination with
267        * zeros first...  Or get the cpu to do it...
268        */
269       if (ctx->Visual.depthBits == 24)
270          return GL_FALSE;
271
272       planemask = ~0;
273       source = mmesa->mgaScreen->depthOffset;
274       break;
275
276    case GL_RGB:
277    case GL_BGRA:
278       ok = check_color(ctx, type, format, pack, pixels, size, pitch);
279       planemask = ~0;
280       source = (mmesa->draw_buffer == MGA_FRONT ?
281                 mmesa->mgaScreen->frontOffset :
282                 mmesa->mgaScreen->backOffset);
283       break;
284
285    default:
286       return GL_FALSE;
287    }
288
289    if (!ok) {
290       return GL_FALSE;
291    }
292
293
294    LOCK_HARDWARE( mmesa );
295
296 #if 0
297    {
298       __DRIdrawable *dPriv = mmesa->driDrawable;
299       int nbox, retcode, i;
300
301       UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
302
303       if (mmesa->dirty_cliprects & MGA_FRONT)
304          mgaUpdateRects( mmesa, MGA_FRONT );
305
306       nbox = dPriv->numClipRects;
307
308       y = dPriv->h - y - height;
309       x += mmesa->drawX;
310       y += mmesa->drawY;
311
312       dest = ((mmesa->mgaScreen->agp.handle + AGP_OFFSET(mmesa, pixels)) |
313               DO_dstmap_sys | DO_dstacc_agp);
314       source_pitch = mmesa->mgaScreen->frontPitch / mmesa->mgaScreen->cpp;
315       dest_pitch = pitch;
316       delta_sx = 0;
317       delta_sy = 0;
318       delta_dx = -x;
319       delta_dy = -y;
320       blit_height = 2*y + height;
321       ydir = -1;
322
323       if (0) fprintf(stderr, "XX doing readpixel blit src_pitch %d dst_pitch %d\n",
324                      source_pitch, dest_pitch);
325
326
327
328       for (i = 0 ; i < nbox ; )
329       {
330          int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects);
331          drm_clip_rect_t *box = dPriv->pClipRects;
332          drm_clip_rect_t *b = mmesa->sarea->boxes;
333          int n = 0;
334
335          for ( ; i < nr ; i++) {
336             GLint bx = box[i].x1;
337             GLint by = box[i].y1;
338             GLint bw = box[i].x2 - bx;
339             GLint bh = box[i].y2 - by;
340
341             if (bx < x) bw -= x - bx, bx = x;
342             if (by < y) bh -= y - by, by = y;
343             if (bx + bw > x + width) bw = x + width - bx;
344             if (by + bh > y + height) bh = y + height - by;
345             if (bw <= 0) continue;
346             if (bh <= 0) continue;
347
348             b->x1 = bx;
349             b->y1 = by;
350             b->x2 = bx + bw;
351             b->y2 = by + bh;
352             b++;
353             n++;
354          }
355
356          mmesa->sarea->nbox = n;
357
358          if (n && (retcode = drmCommandWrite( mmesa->driFd, DRM_MGA_BLIT,
359                                               &blit, sizeof(drmMGABlit)))) {
360             fprintf(stderr, "blit ioctl failed, retcode = %d\n", retcode);
361             UNLOCK_HARDWARE( mmesa );
362             exit(1);
363          }
364       }
365
366       UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
367    }
368 #endif
369
370    UNLOCK_HARDWARE( mmesa );
371
372    return GL_TRUE;
373 }
374
375 static void
376 mgaDDReadPixels( struct gl_context *ctx,
377                  GLint x, GLint y, GLsizei width, GLsizei height,
378                  GLenum format, GLenum type,
379                  const struct gl_pixelstore_attrib *pack,
380                  GLvoid *pixels )
381 {
382    if (!mgaTryReadPixels( ctx, x, y, width, height, format, type, pack, pixels))
383       _swrast_ReadPixels( ctx, x, y, width, height, format, type, pack, pixels);
384 }
385
386
387
388
389 static void do_draw_pix( struct gl_context *ctx,
390                          GLint x, GLint y, GLsizei width, GLsizei height,
391                          GLint pitch,
392                          const void *pixels,
393                          GLuint dest, GLuint planemask)
394 {
395 #if 0
396    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
397    drmMGABlit blit;
398    __DRIdrawable *dPriv = mmesa->driDrawable;
399    drm_clip_rect_t pbox = dPriv->pClipRects;
400    int nbox = dPriv->numClipRects;
401    int retcode, i;
402
403    y = dPriv->h - y - height;
404    x += mmesa->drawX;
405    y += mmesa->drawY;
406
407    blit.dest = dest;
408    blit.planemask = planemask;
409    blit.source = ((mmesa->mgaScreen->agp.handle + AGP_OFFSET(mmesa, pixels))
410                   | SO_srcmap_sys | SO_srcacc_agp);
411    blit.dest_pitch = mmesa->mgaScreen->frontPitch / mmesa->mgaScreen->cpp;
412    blit.source_pitch = pitch;
413    blit.delta_sx = -x;
414    blit.delta_sy = -y;
415    blit.delta_dx = 0;
416    blit.delta_dy = 0;
417    if (ctx->Pixel.ZoomY == -1) {
418       blit.height = height;
419       blit.ydir = 1;
420    } else {
421       blit.height = height;
422       blit.ydir = -1;
423    }
424
425    if (0) fprintf(stderr,
426                   "doing drawpixel blit src_pitch %d dst_pitch %d\n",
427                   blit.source_pitch, blit.dest_pitch);
428
429    for (i = 0 ; i < nbox ; )
430    {
431       int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects);
432       drm_clip_rect_t *box = mmesa->pClipRects;
433       drm_clip_rect_t *b = mmesa->sarea->boxes;
434       int n = 0;
435
436       for ( ; i < nr ; i++) {
437          GLint bx = box[i].x1;
438          GLint by = box[i].y1;
439          GLint bw = box[i].x2 - bx;
440          GLint bh = box[i].y2 - by;
441
442          if (bx < x) bw -= x - bx, bx = x;
443          if (by < y) bh -= y - by, by = y;
444          if (bx + bw > x + width) bw = x + width - bx;
445          if (by + bh > y + height) bh = y + height - by;
446          if (bw <= 0) continue;
447          if (bh <= 0) continue;
448
449          b->x1 = bx;
450          b->y1 = by;
451          b->x2 = bx + bw;
452          b->y2 = by + bh;
453          b++;
454          n++;
455       }
456
457       mmesa->sarea->nbox = n;
458
459       if (n && (retcode = drmCommandWrite( mmesa->driFd, DRM_MGA_BLIT,
460                                               &blit, sizeof(drmMGABlit)))) {
461          fprintf(stderr, "blit ioctl failed, retcode = %d\n", retcode);
462          UNLOCK_HARDWARE( mmesa );
463          exit(1);
464       }
465    }
466 #endif
467 }
468
469
470
471
472 static GLboolean
473 mgaTryDrawPixels( struct gl_context *ctx,
474                   GLint x, GLint y, GLsizei width, GLsizei height,
475                   GLenum format, GLenum type,
476                   const struct gl_pixelstore_attrib *unpack,
477                   const GLvoid *pixels )
478 {
479    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
480    GLint size, skipPixels, skipRows;
481    GLint pitch = unpack->RowLength ? unpack->RowLength : width;
482    GLuint dest, planemask;
483    GLuint cpp = mmesa->mgaScreen->cpp;
484
485    if (!clip_pixelrect(ctx, ctx->DrawBuffer,
486                        &x, &y, &width, &height,
487                        &skipPixels, &skipRows, &size)) {
488       return GL_TRUE;
489    }
490
491
492    switch (format) {
493    case GL_DEPTH_STENCIL:
494       dest = mmesa->mgaScreen->depthOffset;
495       planemask = ~0;
496       if (!check_depth_stencil_24_8(ctx, type, unpack, pixels, size, pitch) ||
497           !check_depth_per_fragment_ops(ctx) ||
498           !check_stencil_per_fragment_ops(ctx))
499          return GL_FALSE;
500       break;
501
502    case GL_DEPTH_COMPONENT:
503       dest = mmesa->mgaScreen->depthOffset;
504
505       if (ctx->Visual.depthBits == 24)
506          planemask = ~0xff;
507       else
508          planemask = ~0;
509
510       if (!check_depth(ctx, type, unpack, pixels, size, pitch) ||
511           !check_depth_per_fragment_ops(ctx))
512          return GL_FALSE;
513       break;
514
515    case GL_RGB:
516    case GL_BGRA:
517       dest = (mmesa->draw_buffer == MGA_FRONT ?
518               mmesa->mgaScreen->frontOffset :
519               mmesa->mgaScreen->backOffset);
520
521       planemask = mgaPackColor(cpp,
522                                ctx->Color.ColorMask[0][RCOMP],
523                                ctx->Color.ColorMask[0][GCOMP],
524                                ctx->Color.ColorMask[0][BCOMP],
525                                ctx->Color.ColorMask[0][ACOMP]);
526
527       if (cpp == 2)
528          planemask |= planemask << 16;
529
530       if (!check_color(ctx, type, format, unpack, pixels, size, pitch)) {
531          return GL_FALSE;
532       }
533       if (!check_color_per_fragment_ops(ctx)) {
534          return GL_FALSE;
535       }
536       break;
537
538    default:
539       return GL_FALSE;
540    }
541
542    LOCK_HARDWARE_QUIESCENT( mmesa );
543
544    if (mmesa->dirty_cliprects & MGA_FRONT)
545       mgaUpdateRects( mmesa, MGA_FRONT );
546
547    if ( IS_AGP_MEM(mmesa, (char *)pixels) &&
548         IS_AGP_MEM(mmesa, (char *)pixels + size) )
549    {
550       do_draw_pix( ctx, x, y, width, height, pitch, pixels,
551                    dest, planemask );
552       UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
553    }
554    else
555    {
556       /* Pixels is in regular memory -- get dma buffers and perform
557        * upload through them.
558        */
559 /*        drmBufPtr buf = mgaGetBufferLocked(mmesa); */
560       GLuint bufferpitch = (width*cpp+31)&~31;
561
562       char *address = 0; /*  mmesa->mgaScreen->agp.map; */
563
564       do {
565 /*       GLuint rows = MIN2( height, MGA_DMA_BUF_SZ / bufferpitch ); */
566          GLuint rows = height;
567
568
569          if (0) fprintf(stderr, "trying to upload %d rows (pitch %d)\n",
570                         rows, bufferpitch);
571
572          /* The texture conversion code is so slow that there is only
573           * negligble speedup when the buffers/images don't exactly
574           * match:
575           */
576 #if 0
577          if (cpp == 2) {
578             if (!_mesa_convert_texsubimage2d( MESA_FORMAT_RGB565,
579                                               0, 0, width, rows,
580                                               bufferpitch, format, type,
581                                               unpack, pixels, address )) {
582 /*             mgaReleaseBufLocked( mmesa, buf ); */
583                UNLOCK_HARDWARE(mmesa);
584                return GL_FALSE;
585             }
586          } else {
587             if (!_mesa_convert_texsubimage2d( MESA_FORMAT_ARGB8888,
588                                               0, 0, width, rows,
589                                               bufferpitch, format, type,
590                                               unpack, pixels, address )) {
591 /*             mgaReleaseBufLocked( mmesa, buf ); */
592                UNLOCK_HARDWARE(mmesa);
593                return GL_FALSE;
594             }
595          }
596 #else
597          memcpy( address, pixels, rows*bufferpitch );
598 #endif
599
600          do_draw_pix( ctx, x, y, width, rows,
601                       bufferpitch/cpp, address, dest, planemask );
602
603          /* Fix me -- use multiple buffers to avoid flush.
604           */
605          UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
606
607          pixels = (void *)((char *) pixels + rows * pitch);
608          height -= rows;
609          y += rows;
610       } while (height);
611
612 /*        mgaReleaseBufLocked( mmesa, buf ); */
613    }
614
615    UNLOCK_HARDWARE( mmesa );
616    mmesa->dirty |= MGA_UPLOAD_CLIPRECTS;
617
618    return GL_TRUE;
619 }
620
621 static void
622 mgaDDDrawPixels( struct gl_context *ctx,
623                  GLint x, GLint y, GLsizei width, GLsizei height,
624                  GLenum format, GLenum type,
625                  const struct gl_pixelstore_attrib *unpack,
626                  const GLvoid *pixels )
627 {
628    if (!mgaTryDrawPixels( ctx, x, y, width, height, format, type,
629                           unpack, pixels ))
630       _swrast_DrawPixels( ctx, x, y, width, height, format, type,
631                           unpack, pixels );
632 }
633 #endif
634
635
636 /* Stub functions - not a real allocator, always returns pointer to
637  * the same block of agp space which isn't used for anything else at
638  * present.
639  */
640 void mgaDDInitPixelFuncs( struct gl_context *ctx )
641 {
642 #if 0
643    /* evidently, these functions don't always work */
644    if (getenv("MGA_BLIT_PIXELS")) {
645       ctx->Driver.ReadPixels = mgaDDReadPixels; /* requires agp dest */
646       ctx->Driver.DrawPixels = mgaDDDrawPixels; /* works with agp/normal mem */
647    }
648 #endif
649 }