/* * Copyright 2000 Compaq Computer Inc. and VA Linux Systems, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * \file mgapixel.c * Implement framebuffer pixel operations for MGA. * * \todo * Someday the accelerated \c glReadPixels and \c glDrawPixels paths need to * be resurrected. They are currently ifdef'ed out because they don't seem * to work and they only get activated some very rare circumstances. * * \author Keith Whitwell * \author Gareth Hughes */ #include "main/mtypes.h" #include "main/macros.h" #include "mgadd.h" #include "mgacontext.h" #include "mgaioctl.h" #include "mgapixel.h" #include "mgastate.h" #include "swrast/swrast.h" #include "main/imports.h" #if 0 #define IS_AGP_MEM( mmesa, p ) \ ((unsigned long)mmesa->mgaScreen->buffers.map <= ((unsigned long)p) && \ (unsigned long)mmesa->mgaScreen->buffers.map + \ (unsigned long)mmesa->mgaScreen->buffers.size > ((unsigned long)p)) #define AGP_OFFSET( mmesa, p ) \ (((unsigned long)p) - (unsigned long)mmesa->mgaScreen->buffers.map) static GLboolean check_depth_stencil_24_8( const struct gl_context *ctx, GLenum type, const struct gl_pixelstore_attrib *packing, const void *pixels, GLint sz, GLint pitch ) { mgaContextPtr mmesa = MGA_CONTEXT(ctx); return ( type == GL_UNSIGNED_INT_24_8 && ctx->Visual->DepthBits == 24 && ctx->Visual->StencilBits == 8 && mmesa->mgaScreen->cpp == 4 && mmesa->hw_stencil && !ctx->Pixel.IndexShift && !ctx->Pixel.IndexOffset && !ctx->Pixel.MapStencilFlag && ctx->Pixel.DepthBias == 0.0 && ctx->Pixel.DepthScale == 1.0 && !packing->SwapBytes && pitch % 32 == 0 && pitch < 4096 ); } static GLboolean check_depth( const struct gl_context *ctx, GLenum type, const struct gl_pixelstore_attrib *packing, const void *pixels, GLint sz, GLint pitch ) { mgaContextPtr mmesa = MGA_CONTEXT(ctx); if ( IS_AGP_MEM( mmesa, pixels ) && !( ( type == GL_UNSIGNED_INT && mmesa->mgaScreen->cpp == 4 ) || ( type == GL_UNSIGNED_SHORT && mmesa->mgaScreen->cpp == 2 ) ) ) return GL_FALSE; return ( ctx->Pixel.DepthBias == 0.0 && ctx->Pixel.DepthScale == 1.0 && !packing->SwapBytes && pitch % 32 == 0 && pitch < 4096 ); } static GLboolean check_color( const struct gl_context *ctx, GLenum type, GLenum format, const struct gl_pixelstore_attrib *packing, const void *pixels, GLint sz, GLint pitch ) { mgaContextPtr mmesa = MGA_CONTEXT(ctx); GLuint cpp = mmesa->mgaScreen->cpp; /* Can't do conversions on agp reads/draws. */ if ( IS_AGP_MEM( mmesa, pixels ) && !( pitch % 32 == 0 && pitch < 4096 && ( ( type == GL_UNSIGNED_BYTE && cpp == 4 && format == GL_BGRA ) || ( type == GL_UNSIGNED_INT_8_8_8_8 && cpp == 4 && format == GL_BGRA ) || ( type == GL_UNSIGNED_SHORT_5_6_5_REV && cpp == 2 && format == GL_RGB ) ) ) ) return GL_FALSE; return (!ctx->_ImageTransferState && !packing->SwapBytes && !packing->LsbFirst); } static GLboolean check_color_per_fragment_ops( const struct gl_context *ctx ) { return (!( ctx->Color.AlphaEnabled || ctx->Depth.Test || ctx->Fog.Enabled || ctx->Scissor.Enabled || ctx->Stencil._Enabled || !ctx->Color.ColorMask[0][0] || !ctx->Color.ColorMask[0][1] || !ctx->Color.ColorMask[0][2] || !ctx->Color.ColorMask[0][3] || ctx->Color.ColorLogicOpEnabled || ctx->Texture._EnabledUnits ) && ctx->Current.RasterPosValid && ctx->Pixel.ZoomX == 1.0F && (ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F)); } static GLboolean check_depth_per_fragment_ops( const struct gl_context *ctx ) { return ( ctx->Current.RasterPosValid && ctx->Color.ColorMask[0][RCOMP] == 0 && ctx->Color.ColorMask[0][BCOMP] == 0 && ctx->Color.ColorMask[0][GCOMP] == 0 && ctx->Color.ColorMask[0][ACOMP] == 0 && ctx->Pixel.ZoomX == 1.0F && ( ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F ) ); } /* In addition to the requirements for depth: */ #if defined(MESA_packed_depth_stencil) static GLboolean check_stencil_per_fragment_ops( const struct gl_context *ctx ) { return ( !ctx->Pixel.IndexShift && !ctx->Pixel.IndexOffset ); } #endif static GLboolean clip_pixelrect( const struct gl_context *ctx, const struct gl_framebuffer *buffer, GLint *x, GLint *y, GLsizei *width, GLsizei *height, GLint *skipPixels, GLint *skipRows, GLint *size ) { mgaContextPtr mmesa = MGA_CONTEXT(ctx); *width = MIN2(*width, MAX_WIDTH); /* redundant? */ /* left clipping */ if (*x < buffer->_Xmin) { *skipPixels += (buffer->_Xmin - *x); *width -= (buffer->_Xmin - *x); *x = buffer->_Xmin; } /* right clipping */ if (*x + *width > buffer->_Xmax) *width -= (*x + *width - buffer->_Xmax - 1); if (*width <= 0) return GL_FALSE; /* bottom clipping */ if (*y < buffer->_Ymin) { *skipRows += (buffer->_Ymin - *y); *height -= (buffer->_Ymin - *y); *y = buffer->_Ymin; } /* top clipping */ if (*y + *height > buffer->_Ymax) *height -= (*y + *height - buffer->_Ymax - 1); if (*height <= 0) return GL_FALSE; *size = ((*y + *height - 1) * mmesa->mgaScreen->frontPitch + (*x + *width - 1) * mmesa->mgaScreen->cpp); return GL_TRUE; } static GLboolean mgaTryReadPixels( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *pack, GLvoid *pixels ) { mgaContextPtr mmesa = MGA_CONTEXT(ctx); GLint size, skipPixels, skipRows; GLint pitch = pack->RowLength ? pack->RowLength : width; GLboolean ok; GLuint planemask; GLuint source; #if 0 drmMGABlit blit; GLuint dest; GLint source_pitch, dest_pitch; GLint delta_sx, delta_sy; GLint delta_dx, delta_dy; GLint blit_height, ydir; #endif if (!clip_pixelrect(ctx, ctx->ReadBuffer, &x, &y, &width, &height, &skipPixels, &skipRows, &size)) { return GL_TRUE; } /* Only accelerate reading to agp buffers. */ if ( !IS_AGP_MEM(mmesa, (char *)pixels) || !IS_AGP_MEM(mmesa, (char *)pixels + size) ) return GL_FALSE; switch (format) { case GL_DEPTH_STENCIL: ok = check_depth_stencil_24_8(ctx, type, pack, pixels, size, pitch); planemask = ~0; source = mmesa->mgaScreen->depthOffset; break; case GL_DEPTH_COMPONENT: ok = check_depth(ctx, type, pack, pixels, size, pitch); /* Can't accelerate at this depth -- planemask does the wrong * thing; it doesn't clear the low order bits in the * destination, instead it leaves them untouched. * * Could get the acclerator to solid fill the destination with * zeros first... Or get the cpu to do it... */ if (ctx->Visual.depthBits == 24) return GL_FALSE; planemask = ~0; source = mmesa->mgaScreen->depthOffset; break; case GL_RGB: case GL_BGRA: ok = check_color(ctx, type, format, pack, pixels, size, pitch); planemask = ~0; source = (mmesa->draw_buffer == MGA_FRONT ? mmesa->mgaScreen->frontOffset : mmesa->mgaScreen->backOffset); break; default: return GL_FALSE; } if (!ok) { return GL_FALSE; } LOCK_HARDWARE( mmesa ); #if 0 { __DRIdrawable *dPriv = mmesa->driDrawable; int nbox, retcode, i; UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT ); if (mmesa->dirty_cliprects & MGA_FRONT) mgaUpdateRects( mmesa, MGA_FRONT ); nbox = dPriv->numClipRects; y = dPriv->h - y - height; x += mmesa->drawX; y += mmesa->drawY; dest = ((mmesa->mgaScreen->agp.handle + AGP_OFFSET(mmesa, pixels)) | DO_dstmap_sys | DO_dstacc_agp); source_pitch = mmesa->mgaScreen->frontPitch / mmesa->mgaScreen->cpp; dest_pitch = pitch; delta_sx = 0; delta_sy = 0; delta_dx = -x; delta_dy = -y; blit_height = 2*y + height; ydir = -1; if (0) fprintf(stderr, "XX doing readpixel blit src_pitch %d dst_pitch %d\n", source_pitch, dest_pitch); for (i = 0 ; i < nbox ; ) { int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects); drm_clip_rect_t *box = dPriv->pClipRects; drm_clip_rect_t *b = mmesa->sarea->boxes; int n = 0; for ( ; i < nr ; i++) { GLint bx = box[i].x1; GLint by = box[i].y1; GLint bw = box[i].x2 - bx; GLint bh = box[i].y2 - by; if (bx < x) bw -= x - bx, bx = x; if (by < y) bh -= y - by, by = y; if (bx + bw > x + width) bw = x + width - bx; if (by + bh > y + height) bh = y + height - by; if (bw <= 0) continue; if (bh <= 0) continue; b->x1 = bx; b->y1 = by; b->x2 = bx + bw; b->y2 = by + bh; b++; n++; } mmesa->sarea->nbox = n; if (n && (retcode = drmCommandWrite( mmesa->driFd, DRM_MGA_BLIT, &blit, sizeof(drmMGABlit)))) { fprintf(stderr, "blit ioctl failed, retcode = %d\n", retcode); UNLOCK_HARDWARE( mmesa ); exit(1); } } UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT ); } #endif UNLOCK_HARDWARE( mmesa ); return GL_TRUE; } static void mgaDDReadPixels( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *pack, GLvoid *pixels ) { if (!mgaTryReadPixels( ctx, x, y, width, height, format, type, pack, pixels)) _swrast_ReadPixels( ctx, x, y, width, height, format, type, pack, pixels); } static void do_draw_pix( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLint pitch, const void *pixels, GLuint dest, GLuint planemask) { #if 0 mgaContextPtr mmesa = MGA_CONTEXT(ctx); drmMGABlit blit; __DRIdrawable *dPriv = mmesa->driDrawable; drm_clip_rect_t pbox = dPriv->pClipRects; int nbox = dPriv->numClipRects; int retcode, i; y = dPriv->h - y - height; x += mmesa->drawX; y += mmesa->drawY; blit.dest = dest; blit.planemask = planemask; blit.source = ((mmesa->mgaScreen->agp.handle + AGP_OFFSET(mmesa, pixels)) | SO_srcmap_sys | SO_srcacc_agp); blit.dest_pitch = mmesa->mgaScreen->frontPitch / mmesa->mgaScreen->cpp; blit.source_pitch = pitch; blit.delta_sx = -x; blit.delta_sy = -y; blit.delta_dx = 0; blit.delta_dy = 0; if (ctx->Pixel.ZoomY == -1) { blit.height = height; blit.ydir = 1; } else { blit.height = height; blit.ydir = -1; } if (0) fprintf(stderr, "doing drawpixel blit src_pitch %d dst_pitch %d\n", blit.source_pitch, blit.dest_pitch); for (i = 0 ; i < nbox ; ) { int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects); drm_clip_rect_t *box = mmesa->pClipRects; drm_clip_rect_t *b = mmesa->sarea->boxes; int n = 0; for ( ; i < nr ; i++) { GLint bx = box[i].x1; GLint by = box[i].y1; GLint bw = box[i].x2 - bx; GLint bh = box[i].y2 - by; if (bx < x) bw -= x - bx, bx = x; if (by < y) bh -= y - by, by = y; if (bx + bw > x + width) bw = x + width - bx; if (by + bh > y + height) bh = y + height - by; if (bw <= 0) continue; if (bh <= 0) continue; b->x1 = bx; b->y1 = by; b->x2 = bx + bw; b->y2 = by + bh; b++; n++; } mmesa->sarea->nbox = n; if (n && (retcode = drmCommandWrite( mmesa->driFd, DRM_MGA_BLIT, &blit, sizeof(drmMGABlit)))) { fprintf(stderr, "blit ioctl failed, retcode = %d\n", retcode); UNLOCK_HARDWARE( mmesa ); exit(1); } } #endif } static GLboolean mgaTryDrawPixels( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid *pixels ) { mgaContextPtr mmesa = MGA_CONTEXT(ctx); GLint size, skipPixels, skipRows; GLint pitch = unpack->RowLength ? unpack->RowLength : width; GLuint dest, planemask; GLuint cpp = mmesa->mgaScreen->cpp; if (!clip_pixelrect(ctx, ctx->DrawBuffer, &x, &y, &width, &height, &skipPixels, &skipRows, &size)) { return GL_TRUE; } switch (format) { case GL_DEPTH_STENCIL: dest = mmesa->mgaScreen->depthOffset; planemask = ~0; if (!check_depth_stencil_24_8(ctx, type, unpack, pixels, size, pitch) || !check_depth_per_fragment_ops(ctx) || !check_stencil_per_fragment_ops(ctx)) return GL_FALSE; break; case GL_DEPTH_COMPONENT: dest = mmesa->mgaScreen->depthOffset; if (ctx->Visual.depthBits == 24) planemask = ~0xff; else planemask = ~0; if (!check_depth(ctx, type, unpack, pixels, size, pitch) || !check_depth_per_fragment_ops(ctx)) return GL_FALSE; break; case GL_RGB: case GL_BGRA: dest = (mmesa->draw_buffer == MGA_FRONT ? mmesa->mgaScreen->frontOffset : mmesa->mgaScreen->backOffset); planemask = mgaPackColor(cpp, ctx->Color.ColorMask[0][RCOMP], ctx->Color.ColorMask[0][GCOMP], ctx->Color.ColorMask[0][BCOMP], ctx->Color.ColorMask[0][ACOMP]); if (cpp == 2) planemask |= planemask << 16; if (!check_color(ctx, type, format, unpack, pixels, size, pitch)) { return GL_FALSE; } if (!check_color_per_fragment_ops(ctx)) { return GL_FALSE; } break; default: return GL_FALSE; } LOCK_HARDWARE_QUIESCENT( mmesa ); if (mmesa->dirty_cliprects & MGA_FRONT) mgaUpdateRects( mmesa, MGA_FRONT ); if ( IS_AGP_MEM(mmesa, (char *)pixels) && IS_AGP_MEM(mmesa, (char *)pixels + size) ) { do_draw_pix( ctx, x, y, width, height, pitch, pixels, dest, planemask ); UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT ); } else { /* Pixels is in regular memory -- get dma buffers and perform * upload through them. */ /* drmBufPtr buf = mgaGetBufferLocked(mmesa); */ GLuint bufferpitch = (width*cpp+31)&~31; char *address = 0; /* mmesa->mgaScreen->agp.map; */ do { /* GLuint rows = MIN2( height, MGA_DMA_BUF_SZ / bufferpitch ); */ GLuint rows = height; if (0) fprintf(stderr, "trying to upload %d rows (pitch %d)\n", rows, bufferpitch); /* The texture conversion code is so slow that there is only * negligble speedup when the buffers/images don't exactly * match: */ #if 0 if (cpp == 2) { if (!_mesa_convert_texsubimage2d( MESA_FORMAT_RGB565, 0, 0, width, rows, bufferpitch, format, type, unpack, pixels, address )) { /* mgaReleaseBufLocked( mmesa, buf ); */ UNLOCK_HARDWARE(mmesa); return GL_FALSE; } } else { if (!_mesa_convert_texsubimage2d( MESA_FORMAT_ARGB8888, 0, 0, width, rows, bufferpitch, format, type, unpack, pixels, address )) { /* mgaReleaseBufLocked( mmesa, buf ); */ UNLOCK_HARDWARE(mmesa); return GL_FALSE; } } #else memcpy( address, pixels, rows*bufferpitch ); #endif do_draw_pix( ctx, x, y, width, rows, bufferpitch/cpp, address, dest, planemask ); /* Fix me -- use multiple buffers to avoid flush. */ UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT ); pixels = (void *)((char *) pixels + rows * pitch); height -= rows; y += rows; } while (height); /* mgaReleaseBufLocked( mmesa, buf ); */ } UNLOCK_HARDWARE( mmesa ); mmesa->dirty |= MGA_UPLOAD_CLIPRECTS; return GL_TRUE; } static void mgaDDDrawPixels( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid *pixels ) { if (!mgaTryDrawPixels( ctx, x, y, width, height, format, type, unpack, pixels )) _swrast_DrawPixels( ctx, x, y, width, height, format, type, unpack, pixels ); } #endif /* Stub functions - not a real allocator, always returns pointer to * the same block of agp space which isn't used for anything else at * present. */ void mgaDDInitPixelFuncs( struct gl_context *ctx ) { #if 0 /* evidently, these functions don't always work */ if (getenv("MGA_BLIT_PIXELS")) { ctx->Driver.ReadPixels = mgaDDReadPixels; /* requires agp dest */ ctx->Driver.DrawPixels = mgaDDDrawPixels; /* works with agp/normal mem */ } #endif }