Merge remote branch 'main/master' into radeon-rewrite
[mesa.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 #if defined(MESA_packed_depth_stencil)
59 static GLboolean
60 check_depth_stencil_24_8( const GLcontext *ctx, GLenum type,
61 const struct gl_pixelstore_attrib *packing,
62 const void *pixels, GLint sz,
63 GLint pitch )
64 {
65 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
66
67 return ( type == GL_UNSIGNED_INT_24_8_MESA &&
68 ctx->Visual->DepthBits == 24 &&
69 ctx->Visual->StencilBits == 8 &&
70 mmesa->mgaScreen->cpp == 4 &&
71 mmesa->hw_stencil &&
72 !ctx->Pixel.IndexShift &&
73 !ctx->Pixel.IndexOffset &&
74 !ctx->Pixel.MapStencilFlag &&
75 ctx->Pixel.DepthBias == 0.0 &&
76 ctx->Pixel.DepthScale == 1.0 &&
77 !packing->SwapBytes &&
78 pitch % 32 == 0 &&
79 pitch < 4096 );
80 }
81 #endif
82
83
84 static GLboolean
85 check_depth( const GLcontext *ctx, GLenum type,
86 const struct gl_pixelstore_attrib *packing,
87 const void *pixels, GLint sz, GLint pitch )
88 {
89 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
90
91 if ( IS_AGP_MEM( mmesa, pixels ) &&
92 !( ( type == GL_UNSIGNED_INT && mmesa->mgaScreen->cpp == 4 ) ||
93 ( type == GL_UNSIGNED_SHORT && mmesa->mgaScreen->cpp == 2 ) ) )
94 return GL_FALSE;
95
96 return ( ctx->Pixel.DepthBias == 0.0 &&
97 ctx->Pixel.DepthScale == 1.0 &&
98 !packing->SwapBytes &&
99 pitch % 32 == 0 &&
100 pitch < 4096 );
101 }
102
103
104 static GLboolean
105 check_color( const GLcontext *ctx, GLenum type, GLenum format,
106 const struct gl_pixelstore_attrib *packing,
107 const void *pixels, GLint sz, GLint pitch )
108 {
109 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
110 GLuint cpp = mmesa->mgaScreen->cpp;
111
112 /* Can't do conversions on agp reads/draws.
113 */
114 if ( IS_AGP_MEM( mmesa, pixels ) &&
115 !( pitch % 32 == 0 && pitch < 4096 &&
116 ( ( type == GL_UNSIGNED_BYTE &&
117 cpp == 4 && format == GL_BGRA ) ||
118 ( type == GL_UNSIGNED_INT_8_8_8_8 &&
119 cpp == 4 && format == GL_BGRA ) ||
120 ( type == GL_UNSIGNED_SHORT_5_6_5_REV &&
121 cpp == 2 && format == GL_RGB ) ) ) )
122 return GL_FALSE;
123
124 return (!ctx->_ImageTransferState &&
125 !packing->SwapBytes &&
126 !packing->LsbFirst);
127 }
128
129 static GLboolean
130 check_color_per_fragment_ops( const GLcontext *ctx )
131 {
132 return (!( ctx->Color.AlphaEnabled ||
133 ctx->Depth.Test ||
134 ctx->Fog.Enabled ||
135 ctx->Scissor.Enabled ||
136 ctx->Stencil._Enabled ||
137 !ctx->Color.ColorMask[0] ||
138 !ctx->Color.ColorMask[1] ||
139 !ctx->Color.ColorMask[2] ||
140 !ctx->Color.ColorMask[3] ||
141 ctx->Color.ColorLogicOpEnabled ||
142 ctx->Texture._EnabledUnits
143 ) &&
144 ctx->Current.RasterPosValid &&
145 ctx->Pixel.ZoomX == 1.0F &&
146 (ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F));
147 }
148
149 static GLboolean
150 check_depth_per_fragment_ops( const GLcontext *ctx )
151 {
152 return ( ctx->Current.RasterPosValid &&
153 ctx->Color.ColorMask[RCOMP] == 0 &&
154 ctx->Color.ColorMask[BCOMP] == 0 &&
155 ctx->Color.ColorMask[GCOMP] == 0 &&
156 ctx->Color.ColorMask[ACOMP] == 0 &&
157 ctx->Pixel.ZoomX == 1.0F &&
158 ( ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F ) );
159 }
160
161 /* In addition to the requirements for depth:
162 */
163 #if defined(MESA_packed_depth_stencil)
164 static GLboolean
165 check_stencil_per_fragment_ops( const GLcontext *ctx )
166 {
167 return ( !ctx->Pixel.IndexShift &&
168 !ctx->Pixel.IndexOffset );
169 }
170 #endif
171
172
173 static GLboolean
174 clip_pixelrect( const GLcontext *ctx,
175 const GLframebuffer *buffer,
176 GLint *x, GLint *y,
177 GLsizei *width, GLsizei *height,
178 GLint *skipPixels, GLint *skipRows,
179 GLint *size )
180 {
181 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
182
183 *width = MIN2(*width, MAX_WIDTH); /* redundant? */
184
185 /* left clipping */
186 if (*x < buffer->_Xmin) {
187 *skipPixels += (buffer->_Xmin - *x);
188 *width -= (buffer->_Xmin - *x);
189 *x = buffer->_Xmin;
190 }
191
192 /* right clipping */
193 if (*x + *width > buffer->_Xmax)
194 *width -= (*x + *width - buffer->_Xmax - 1);
195
196 if (*width <= 0)
197 return GL_FALSE;
198
199 /* bottom clipping */
200 if (*y < buffer->_Ymin) {
201 *skipRows += (buffer->_Ymin - *y);
202 *height -= (buffer->_Ymin - *y);
203 *y = buffer->_Ymin;
204 }
205
206 /* top clipping */
207 if (*y + *height > buffer->_Ymax)
208 *height -= (*y + *height - buffer->_Ymax - 1);
209
210 if (*height <= 0)
211 return GL_FALSE;
212
213 *size = ((*y + *height - 1) * mmesa->mgaScreen->frontPitch +
214 (*x + *width - 1) * mmesa->mgaScreen->cpp);
215
216 return GL_TRUE;
217 }
218
219 static GLboolean
220 mgaTryReadPixels( GLcontext *ctx,
221 GLint x, GLint y, GLsizei width, GLsizei height,
222 GLenum format, GLenum type,
223 const struct gl_pixelstore_attrib *pack,
224 GLvoid *pixels )
225 {
226 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
227 GLint size, skipPixels, skipRows;
228 GLint pitch = pack->RowLength ? pack->RowLength : width;
229 GLboolean ok;
230
231 GLuint planemask;
232 GLuint source;
233 #if 0
234 drmMGABlit blit;
235 GLuint dest;
236 GLint source_pitch, dest_pitch;
237 GLint delta_sx, delta_sy;
238 GLint delta_dx, delta_dy;
239 GLint blit_height, ydir;
240 #endif
241
242 if (!clip_pixelrect(ctx, ctx->ReadBuffer,
243 &x, &y, &width, &height,
244 &skipPixels, &skipRows, &size)) {
245 return GL_TRUE;
246 }
247
248 /* Only accelerate reading to agp buffers.
249 */
250 if ( !IS_AGP_MEM(mmesa, (char *)pixels) ||
251 !IS_AGP_MEM(mmesa, (char *)pixels + size) )
252 return GL_FALSE;
253
254 switch (format) {
255 #if defined(MESA_packed_depth_stencil)
256 case GL_DEPTH_STENCIL_MESA:
257 ok = check_depth_stencil_24_8(ctx, type, pack, pixels, size, pitch);
258 planemask = ~0;
259 source = mmesa->mgaScreen->depthOffset;
260 break;
261 #endif
262
263 case GL_DEPTH_COMPONENT:
264 ok = check_depth(ctx, type, pack, pixels, size, pitch);
265
266 /* Can't accelerate at this depth -- planemask does the wrong
267 * thing; it doesn't clear the low order bits in the
268 * destination, instead it leaves them untouched.
269 *
270 * Could get the acclerator to solid fill the destination with
271 * zeros first... Or get the cpu to do it...
272 */
273 if (ctx->Visual.depthBits == 24)
274 return GL_FALSE;
275
276 planemask = ~0;
277 source = mmesa->mgaScreen->depthOffset;
278 break;
279
280 case GL_RGB:
281 case GL_BGRA:
282 ok = check_color(ctx, type, format, pack, pixels, size, pitch);
283 planemask = ~0;
284 source = (mmesa->draw_buffer == MGA_FRONT ?
285 mmesa->mgaScreen->frontOffset :
286 mmesa->mgaScreen->backOffset);
287 break;
288
289 default:
290 return GL_FALSE;
291 }
292
293 if (!ok) {
294 return GL_FALSE;
295 }
296
297
298 LOCK_HARDWARE( mmesa );
299
300 #if 0
301 {
302 __DRIdrawablePrivate *dPriv = mmesa->driDrawable;
303 int nbox, retcode, i;
304
305 UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
306
307 if (mmesa->dirty_cliprects & MGA_FRONT)
308 mgaUpdateRects( mmesa, MGA_FRONT );
309
310 nbox = dPriv->numClipRects;
311
312 y = dPriv->h - y - height;
313 x += mmesa->drawX;
314 y += mmesa->drawY;
315
316 dest = ((mmesa->mgaScreen->agp.handle + AGP_OFFSET(mmesa, pixels)) |
317 DO_dstmap_sys | DO_dstacc_agp);
318 source_pitch = mmesa->mgaScreen->frontPitch / mmesa->mgaScreen->cpp;
319 dest_pitch = pitch;
320 delta_sx = 0;
321 delta_sy = 0;
322 delta_dx = -x;
323 delta_dy = -y;
324 blit_height = 2*y + height;
325 ydir = -1;
326
327 if (0) fprintf(stderr, "XX doing readpixel blit src_pitch %d dst_pitch %d\n",
328 source_pitch, dest_pitch);
329
330
331
332 for (i = 0 ; i < nbox ; )
333 {
334 int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects);
335 drm_clip_rect_t *box = dPriv->pClipRects;
336 drm_clip_rect_t *b = mmesa->sarea->boxes;
337 int n = 0;
338
339 for ( ; i < nr ; i++) {
340 GLint bx = box[i].x1;
341 GLint by = box[i].y1;
342 GLint bw = box[i].x2 - bx;
343 GLint bh = box[i].y2 - by;
344
345 if (bx < x) bw -= x - bx, bx = x;
346 if (by < y) bh -= y - by, by = y;
347 if (bx + bw > x + width) bw = x + width - bx;
348 if (by + bh > y + height) bh = y + height - by;
349 if (bw <= 0) continue;
350 if (bh <= 0) continue;
351
352 b->x1 = bx;
353 b->y1 = by;
354 b->x2 = bx + bw;
355 b->y2 = by + bh;
356 b++;
357 n++;
358 }
359
360 mmesa->sarea->nbox = n;
361
362 if (n && (retcode = drmCommandWrite( mmesa->driFd, DRM_MGA_BLIT,
363 &blit, sizeof(drmMGABlit)))) {
364 fprintf(stderr, "blit ioctl failed, retcode = %d\n", retcode);
365 UNLOCK_HARDWARE( mmesa );
366 exit(1);
367 }
368 }
369
370 UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
371 }
372 #endif
373
374 UNLOCK_HARDWARE( mmesa );
375
376 return GL_TRUE;
377 }
378
379 static void
380 mgaDDReadPixels( GLcontext *ctx,
381 GLint x, GLint y, GLsizei width, GLsizei height,
382 GLenum format, GLenum type,
383 const struct gl_pixelstore_attrib *pack,
384 GLvoid *pixels )
385 {
386 if (!mgaTryReadPixels( ctx, x, y, width, height, format, type, pack, pixels))
387 _swrast_ReadPixels( ctx, x, y, width, height, format, type, pack, pixels);
388 }
389
390
391
392
393 static void do_draw_pix( GLcontext *ctx,
394 GLint x, GLint y, GLsizei width, GLsizei height,
395 GLint pitch,
396 const void *pixels,
397 GLuint dest, GLuint planemask)
398 {
399 #if 0
400 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
401 drmMGABlit blit;
402 __DRIdrawablePrivate *dPriv = mmesa->driDrawable;
403 drm_clip_rect_t pbox = dPriv->pClipRects;
404 int nbox = dPriv->numClipRects;
405 int retcode, i;
406
407 y = dPriv->h - y - height;
408 x += mmesa->drawX;
409 y += mmesa->drawY;
410
411 blit.dest = dest;
412 blit.planemask = planemask;
413 blit.source = ((mmesa->mgaScreen->agp.handle + AGP_OFFSET(mmesa, pixels))
414 | SO_srcmap_sys | SO_srcacc_agp);
415 blit.dest_pitch = mmesa->mgaScreen->frontPitch / mmesa->mgaScreen->cpp;
416 blit.source_pitch = pitch;
417 blit.delta_sx = -x;
418 blit.delta_sy = -y;
419 blit.delta_dx = 0;
420 blit.delta_dy = 0;
421 if (ctx->Pixel.ZoomY == -1) {
422 blit.height = height;
423 blit.ydir = 1;
424 } else {
425 blit.height = height;
426 blit.ydir = -1;
427 }
428
429 if (0) fprintf(stderr,
430 "doing drawpixel blit src_pitch %d dst_pitch %d\n",
431 blit.source_pitch, blit.dest_pitch);
432
433 for (i = 0 ; i < nbox ; )
434 {
435 int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects);
436 drm_clip_rect_t *box = mmesa->pClipRects;
437 drm_clip_rect_t *b = mmesa->sarea->boxes;
438 int n = 0;
439
440 for ( ; i < nr ; i++) {
441 GLint bx = box[i].x1;
442 GLint by = box[i].y1;
443 GLint bw = box[i].x2 - bx;
444 GLint bh = box[i].y2 - by;
445
446 if (bx < x) bw -= x - bx, bx = x;
447 if (by < y) bh -= y - by, by = y;
448 if (bx + bw > x + width) bw = x + width - bx;
449 if (by + bh > y + height) bh = y + height - by;
450 if (bw <= 0) continue;
451 if (bh <= 0) continue;
452
453 b->x1 = bx;
454 b->y1 = by;
455 b->x2 = bx + bw;
456 b->y2 = by + bh;
457 b++;
458 n++;
459 }
460
461 mmesa->sarea->nbox = n;
462
463 if (n && (retcode = drmCommandWrite( mmesa->driFd, DRM_MGA_BLIT,
464 &blit, sizeof(drmMGABlit)))) {
465 fprintf(stderr, "blit ioctl failed, retcode = %d\n", retcode);
466 UNLOCK_HARDWARE( mmesa );
467 exit(1);
468 }
469 }
470 #endif
471 }
472
473
474
475
476 static GLboolean
477 mgaTryDrawPixels( GLcontext *ctx,
478 GLint x, GLint y, GLsizei width, GLsizei height,
479 GLenum format, GLenum type,
480 const struct gl_pixelstore_attrib *unpack,
481 const GLvoid *pixels )
482 {
483 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
484 GLint size, skipPixels, skipRows;
485 GLint pitch = unpack->RowLength ? unpack->RowLength : width;
486 GLuint dest, planemask;
487 GLuint cpp = mmesa->mgaScreen->cpp;
488
489 if (!clip_pixelrect(ctx, ctx->DrawBuffer,
490 &x, &y, &width, &height,
491 &skipPixels, &skipRows, &size)) {
492 return GL_TRUE;
493 }
494
495
496 switch (format) {
497 #if defined(MESA_packed_depth_stencil)
498 case GL_DEPTH_STENCIL_MESA:
499 dest = mmesa->mgaScreen->depthOffset;
500 planemask = ~0;
501 if (!check_depth_stencil_24_8(ctx, type, unpack, pixels, size, pitch) ||
502 !check_depth_per_fragment_ops(ctx) ||
503 !check_stencil_per_fragment_ops(ctx))
504 return GL_FALSE;
505 break;
506 #endif
507
508 case GL_DEPTH_COMPONENT:
509 dest = mmesa->mgaScreen->depthOffset;
510
511 if (ctx->Visual.depthBits == 24)
512 planemask = ~0xff;
513 else
514 planemask = ~0;
515
516 if (!check_depth(ctx, type, unpack, pixels, size, pitch) ||
517 !check_depth_per_fragment_ops(ctx))
518 return GL_FALSE;
519 break;
520
521 case GL_RGB:
522 case GL_BGRA:
523 dest = (mmesa->draw_buffer == MGA_FRONT ?
524 mmesa->mgaScreen->frontOffset :
525 mmesa->mgaScreen->backOffset);
526
527 planemask = mgaPackColor(cpp,
528 ctx->Color.ColorMask[RCOMP],
529 ctx->Color.ColorMask[GCOMP],
530 ctx->Color.ColorMask[BCOMP],
531 ctx->Color.ColorMask[ACOMP]);
532
533 if (cpp == 2)
534 planemask |= planemask << 16;
535
536 if (!check_color(ctx, type, format, unpack, pixels, size, pitch)) {
537 return GL_FALSE;
538 }
539 if (!check_color_per_fragment_ops(ctx)) {
540 return GL_FALSE;
541 }
542 break;
543
544 default:
545 return GL_FALSE;
546 }
547
548 LOCK_HARDWARE_QUIESCENT( mmesa );
549
550 if (mmesa->dirty_cliprects & MGA_FRONT)
551 mgaUpdateRects( mmesa, MGA_FRONT );
552
553 if ( IS_AGP_MEM(mmesa, (char *)pixels) &&
554 IS_AGP_MEM(mmesa, (char *)pixels + size) )
555 {
556 do_draw_pix( ctx, x, y, width, height, pitch, pixels,
557 dest, planemask );
558 UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
559 }
560 else
561 {
562 /* Pixels is in regular memory -- get dma buffers and perform
563 * upload through them.
564 */
565 /* drmBufPtr buf = mgaGetBufferLocked(mmesa); */
566 GLuint bufferpitch = (width*cpp+31)&~31;
567
568 char *address = 0; /* mmesa->mgaScreen->agp.map; */
569
570 do {
571 /* GLuint rows = MIN2( height, MGA_DMA_BUF_SZ / bufferpitch ); */
572 GLuint rows = height;
573
574
575 if (0) fprintf(stderr, "trying to upload %d rows (pitch %d)\n",
576 rows, bufferpitch);
577
578 /* The texture conversion code is so slow that there is only
579 * negligble speedup when the buffers/images don't exactly
580 * match:
581 */
582 #if 0
583 if (cpp == 2) {
584 if (!_mesa_convert_texsubimage2d( MESA_FORMAT_RGB565,
585 0, 0, width, rows,
586 bufferpitch, format, type,
587 unpack, pixels, address )) {
588 /* mgaReleaseBufLocked( mmesa, buf ); */
589 UNLOCK_HARDWARE(mmesa);
590 return GL_FALSE;
591 }
592 } else {
593 if (!_mesa_convert_texsubimage2d( MESA_FORMAT_ARGB8888,
594 0, 0, width, rows,
595 bufferpitch, format, type,
596 unpack, pixels, address )) {
597 /* mgaReleaseBufLocked( mmesa, buf ); */
598 UNLOCK_HARDWARE(mmesa);
599 return GL_FALSE;
600 }
601 }
602 #else
603 MEMCPY( address, pixels, rows*bufferpitch );
604 #endif
605
606 do_draw_pix( ctx, x, y, width, rows,
607 bufferpitch/cpp, address, dest, planemask );
608
609 /* Fix me -- use multiple buffers to avoid flush.
610 */
611 UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
612
613 pixels = (void *)((char *) pixels + rows * pitch);
614 height -= rows;
615 y += rows;
616 } while (height);
617
618 /* mgaReleaseBufLocked( mmesa, buf ); */
619 }
620
621 UNLOCK_HARDWARE( mmesa );
622 mmesa->dirty |= MGA_UPLOAD_CLIPRECTS;
623
624 return GL_TRUE;
625 }
626
627 static void
628 mgaDDDrawPixels( GLcontext *ctx,
629 GLint x, GLint y, GLsizei width, GLsizei height,
630 GLenum format, GLenum type,
631 const struct gl_pixelstore_attrib *unpack,
632 const GLvoid *pixels )
633 {
634 if (!mgaTryDrawPixels( ctx, x, y, width, height, format, type,
635 unpack, pixels ))
636 _swrast_DrawPixels( ctx, x, y, width, height, format, type,
637 unpack, pixels );
638 }
639 #endif
640
641
642 /* Stub functions - not a real allocator, always returns pointer to
643 * the same block of agp space which isn't used for anything else at
644 * present.
645 */
646 void mgaDDInitPixelFuncs( GLcontext *ctx )
647 {
648 #if 0
649 /* evidently, these functions don't always work */
650 if (getenv("MGA_BLIT_PIXELS")) {
651 ctx->Driver.ReadPixels = mgaDDReadPixels; /* requires agp dest */
652 ctx->Driver.DrawPixels = mgaDDDrawPixels; /* works with agp/normal mem */
653 }
654 #endif
655 }