示例#1
0
文件: mgaioctl.c 项目: dikerex/theqvd
void mgaWaitAge( mgaContextPtr mmesa, int age  )
{
   if (GET_DISPATCH_AGE(mmesa) < age) {
      LOCK_HARDWARE(mmesa);
      if (GET_DISPATCH_AGE(mmesa) < age) {
	 UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
      }
      UNLOCK_HARDWARE(mmesa);
   }
}
static void mgaWaitForFrameCompletion( mgaContextPtr mmesa )
{
    if ( mgaWaitFence( mmesa, mmesa->last_frame_fence, NULL ) == ENOSYS ) {
	unsigned wait = 0;
	GLuint last_frame;
	GLuint last_wrap;


	LOCK_HARDWARE( mmesa );
	last_frame = mmesa->sarea->last_frame.head;
	last_wrap = mmesa->sarea->last_frame.wrap;

	/* The DMA routines in the kernel track a couple values in the SAREA
	 * that we use here.  The number of times that the primary DMA buffer
	 * has "wrapped" around is tracked in last_wrap.  In addition, the
	 * wrap count and the buffer position at the end of the last frame are
	 * stored in last_frame.wrap and last_frame.head.
	 * 
	 * By comparing the wrap counts and the current DMA pointer value
	 * (read directly from the hardware) to last_frame.head, we can
	 * determine when the graphics processor has processed all of the
	 * commands for the last frame.
	 * 
	 * In this case "last frame" means the frame of the *previous* swap-
	 * buffers call.  This is done to prevent queuing a second buffer swap
	 * before the previous swap is executed.
	 */
	while ( 1 ) {
	    if ( last_wrap < mmesa->sarea->last_wrap ||
		 ( last_wrap == mmesa->sarea->last_wrap &&
		   last_frame <= (MGA_READ( MGAREG_PRIMADDRESS ) -
				  mmesa->primary_offset) ) ) {
		break;
	    }
	    if ( 0 ) {
		wait++;
		fprintf( stderr, "   last: head=0x%06x wrap=%d\n",
			 last_frame, last_wrap );
		fprintf( stderr, "   head: head=0x%06lx wrap=%d\n",
			 (long)(MGA_READ( MGAREG_PRIMADDRESS ) - mmesa->primary_offset),
			 mmesa->sarea->last_wrap );
	    }
	    UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );

	    UNLOCK_HARDWARE( mmesa );
	    DO_USLEEP( 1 );
	    LOCK_HARDWARE( mmesa );
	}
	if ( wait )
	  fprintf( stderr, "\n" );

	UNLOCK_HARDWARE( mmesa );
    }
}
/**
 * Implement the hardware-specific portion of \c glFlush.
 *
 * \param ctx  Context to be flushed.
 *
 * \sa glFlush, mgaFinish, mgaFlushDMA
 */
static void mgaFlush( GLcontext *ctx )
{
    mgaContextPtr mmesa = MGA_CONTEXT( ctx );


    LOCK_HARDWARE( mmesa );
    if ( mmesa->vertex_dma_buffer != NULL ) {
	mgaFlushVerticesLocked( mmesa );
    }

    UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
    UNLOCK_HARDWARE( mmesa );
}
示例#4
0
文件: mgaioctl.c 项目: dikerex/theqvd
void mgaDDFlush( GLcontext *ctx )
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );


   FLUSH_BATCH( mmesa );

   /* This may be called redundantly - dispatch_age may trail what
    * has actually been sent and processed by the hardware.
    */
   if (1 || GET_DISPATCH_AGE( mmesa ) < mmesa->sarea->last_enqueue) {
      LOCK_HARDWARE( mmesa );
      UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
      UNLOCK_HARDWARE( mmesa );
   }
}
示例#5
0
文件: mgaioctl.c 项目: dikerex/theqvd
/* This is overkill
 */
void mgaDDFinish( GLcontext *ctx  )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   FLUSH_BATCH( mmesa );

   if (1/*mmesa->sarea->last_quiescent != mmesa->sarea->last_enqueue*/) {
      if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
	 fprintf(stderr, "mgaRegetLockQuiescent\n");

      LOCK_HARDWARE( mmesa );
      UPDATE_LOCK( mmesa, DRM_LOCK_QUIESCENT | DRM_LOCK_FLUSH );
      UNLOCK_HARDWARE( mmesa );

      mmesa->sarea->last_quiescent = mmesa->sarea->last_enqueue;
   }
}
/**
 * Implement the hardware-specific portion of \c glFinish.
 *
 * Flushes all pending commands to the hardware and wait for them to finish.
 * 
 * \param ctx  Context where the \c glFinish command was issued.
 *
 * \sa glFinish, mgaFlush, mgaFlushDMA
 */
static void mgaFinish( GLcontext *ctx  )
{
    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
    uint32_t  fence;


    LOCK_HARDWARE( mmesa );
    if ( mmesa->vertex_dma_buffer != NULL ) {
	mgaFlushVerticesLocked( mmesa );
    }

    if ( mgaSetFence( mmesa, & fence ) == 0 ) {
	UNLOCK_HARDWARE( mmesa );
	(void) mgaWaitFence( mmesa, fence, NULL );
    }
    else {
	if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL) {
	    fprintf(stderr, "mgaRegetLockQuiescent\n");
	}

	UPDATE_LOCK( mmesa, DRM_LOCK_QUIESCENT | DRM_LOCK_FLUSH );
	UNLOCK_HARDWARE( mmesa );
    }
}
示例#7
0
文件: mgaioctl.c 项目: dikerex/theqvd
drmBufPtr mga_get_buffer_ioctl( mgaContextPtr mmesa )
{
   int idx = 0;
   int size = 0;
   drmDMAReq dma;
   int retcode;
   drmBufPtr buf;

   if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
      fprintf(stderr,  "Getting dma buffer\n");

   dma.context = mmesa->hHWContext;
   dma.send_count = 0;
   dma.send_list = NULL;
   dma.send_sizes = NULL;
   dma.flags = 0;
   dma.request_count = 1;
   dma.request_size = MGA_BUFFER_SIZE;
   dma.request_list = &idx;
   dma.request_sizes = &size;
   dma.granted_count = 0;


   if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
      fprintf(stderr, "drmDMA (get) ctx %d count %d size 0x%x\n",
	   dma.context, dma.request_count,
	   dma.request_size);

   while (1) {
      retcode = drmDMA(mmesa->driFd, &dma);

      if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
	 fprintf(stderr, "retcode %d sz %d idx %d count %d\n",
		 retcode,
		 dma.request_sizes[0],
		 dma.request_list[0],
		 dma.granted_count);

      if (retcode == 0 &&
	  dma.request_sizes[0] &&
	  dma.granted_count)
	 break;

      if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
	 fprintf(stderr, "\n\nflush");

      UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
   }

   buf = &(mmesa->mgaScreen->bufs->list[idx]);
   buf->used = 0;

   if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
      fprintf(stderr,
	   "drmDMA (get) returns size[0] 0x%x idx[0] %d\n"
	   "dma_buffer now: buf idx: %d size: %d used: %d addr %p\n",
	   dma.request_sizes[0], dma.request_list[0],
	   buf->idx, buf->total,
	   buf->used, buf->address);

   if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
      fprintf(stderr, "finished getbuffer\n");

   return buf;
}
示例#8
0
文件: mgaioctl.c 项目: dikerex/theqvd
void mgaWaitAgeLocked( mgaContextPtr mmesa, int age  )
{
   if (GET_DISPATCH_AGE(mmesa) < age) {
      UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
   }
}
示例#9
0
文件: mgaioctl.c 项目: dikerex/theqvd
/*
 * Copy the back buffer to the front buffer.
 */
void mgaSwapBuffers(Display *dpy, void *drawablePrivate)
{
   __DRIdrawablePrivate *dPriv = (__DRIdrawablePrivate *) drawablePrivate;
   mgaContextPtr mmesa;
   XF86DRIClipRectPtr pbox;
   GLint nbox;
   GLint ret, wait = 0;
   GLint i;
   GLuint last_frame, last_wrap;

   assert(dPriv);
   assert(dPriv->driContextPriv);
   assert(dPriv->driContextPriv->driverPrivate);

   mmesa = (mgaContextPtr) dPriv->driContextPriv->driverPrivate;

   FLUSH_BATCH( mmesa );

   mgaWaitForVBlank( mmesa );

   LOCK_HARDWARE( mmesa );

   last_frame = mmesa->sarea->last_frame.head;
   last_wrap = mmesa->sarea->last_frame.wrap;

   /* FIXME: Add a timeout to this loop...
    */
   while ( 1 ) {
      if ( last_wrap < mmesa->sarea->last_wrap ||
	   ( last_wrap == mmesa->sarea->last_wrap &&
	     last_frame <= (MGA_READ( MGAREG_PRIMADDRESS ) -
			    mmesa->primary_offset) ) ) {
	 break;
      }
      if ( 0 ) {
	 wait++;
	 fprintf( stderr, "   last: head=0x%06x wrap=%d\n",
		  last_frame, last_wrap );
	 fprintf( stderr, "   head: head=0x%06lx wrap=%d\n",
		  (long)(MGA_READ( MGAREG_PRIMADDRESS ) - mmesa->primary_offset),
		  mmesa->sarea->last_wrap );
      }
      UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );

      for ( i = 0 ; i < 1024 ; i++ ) {
	 /* Don't just hammer the register... */
      }
   }
   if ( wait )
      fprintf( stderr, "\n" );

   /* Use the frontbuffer cliprects
    */
   if (mmesa->dirty_cliprects & MGA_FRONT)
      mgaUpdateRects( mmesa, MGA_FRONT );


   pbox = dPriv->pClipRects;
   nbox = dPriv->numClipRects;

   for (i = 0 ; i < nbox ; )
   {
      int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects);
      XF86DRIClipRectPtr b = mmesa->sarea->boxes;

      mmesa->sarea->nbox = nr - i;

      for ( ; i < nr ; i++)
	 *b++ = pbox[i];

      if (0)
	 fprintf(stderr, "DRM_IOCTL_MGA_SWAP\n");

      ret = drmCommandNone( mmesa->driFd, DRM_MGA_SWAP );
      if ( ret ) {
	 printf("send swap retcode = %d\n", ret);
	 exit(1);
      }
   }

   UNLOCK_HARDWARE( mmesa );

   mmesa->dirty |= MGA_UPLOAD_CLIPRECTS;
}
示例#10
0
文件: mgapixel.c 项目: dikerex/theqvd
static GLboolean
mgaTryDrawPixels( GLcontext *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) {
#if defined(MESA_packed_depth_stencil)
   case GL_DEPTH_STENCIL_MESA:
      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;
#endif

   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[RCOMP],
			       ctx->Color.ColorMask[GCOMP],
			       ctx->Color.ColorMask[BCOMP],
			       ctx->Color.ColorMask[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;
}
示例#11
0
文件: mgapixel.c 项目: dikerex/theqvd
static GLboolean
mgaTryReadPixels( GLcontext *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) {
#if defined(MESA_packed_depth_stencil)
   case GL_DEPTH_STENCIL_MESA:
      ok = check_depth_stencil_24_8(ctx, type, pack, pixels, size, pitch);
      planemask = ~0;
      source = mmesa->mgaScreen->depthOffset;
      break;
#endif

   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
   {
      __DRIdrawablePrivate *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);
	 XF86DRIClipRectRec *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;
}
示例#12
0
/**
 * Upload a texture image from system memory to either on-card or AGP
 * memory.  Uploads to on-card memory are performed using an ILOAD operation.
 * This is used for both initial loading of the entire image, and texSubImage
 * updates.
 *
 * Performed with the hardware lock held.
 * 
 * Even though this function is named "upload subimage," the entire image
 * is uploaded.
 * 
 * \param mmesa  Driver context.
 * \param t      Texture to be uploaded.
 * \param hwlevel  Mipmap level of the texture to be uploaded.
 * 
 * \bug As mentioned above, this fuction actually copies the entier mipmap
 *      level.  There should be a version of this function that performs
 *      sub-rectangle uploads.  This will perform quite a bit better if only
 *      a small portion of a larger texture has been updated.  Care would
 *      need to be take with such an implementation once glCopyTexImage has
 *      been hardware accelerated.
 */
static void mgaUploadSubImage( mgaContextPtr mmesa,
			       mgaTextureObjectPtr t, GLint hwlevel )
{
   struct gl_texture_image * texImage;
   unsigned     offset;
   unsigned     texelBytes;
   unsigned     length;
   const int level = hwlevel + t->base.firstLevel;


   if ( (hwlevel < 0) 
	|| (hwlevel >= (MGA_IS_G200(mmesa) 
		      ? G200_TEX_MAXLEVELS : G400_TEX_MAXLEVELS)) ) {
      fprintf( stderr, "[%s:%d] level = %d\n", __FILE__, __LINE__, level );
      return;
   }

   texImage = t->base.tObj->Image[0][level];
   if ( texImage == NULL ) {
      fprintf( stderr, "[%s:%d] Image[%d] = NULL\n", __FILE__, __LINE__,
	       level );
      return;
   }


   if (texImage->Data == NULL) {
      fprintf(stderr, "null texture image data tObj %p level %d\n",
	      (void *) t->base.tObj, level);
      return;
   }


   /* find the proper destination offset for this level */
   if ( MGA_IS_G200(mmesa) ) {
      offset = (t->base.memBlock->ofs + t->offsets[hwlevel]);
   }
   else {
      unsigned  i;

      offset = t->base.memBlock->ofs;
      for ( i = 0 ; i < hwlevel ; i++ ) {
	 offset += (t->offsets[1] >> (i * 2));
      }
   }


   /* Copy the texture from system memory to a memory space that can be
    * directly used by the hardware for texturing.
    */

   texelBytes = _mesa_get_format_bytes(texImage->TexFormat);
   length = texImage->Width * texImage->Height * texelBytes;
   if ( t->base.heap->heapId == MGA_CARD_HEAP ) {
      unsigned  tex_offset = 0;
      unsigned  to_copy;


      /* We may not be able to upload the entire texture in one batch due to
       * register limits or dma buffer limits.  Split the copy up into maximum
       * sized chunks.
       */

      offset += mmesa->mgaScreen->textureOffset[ t->base.heap->heapId ];
      while ( length != 0 ) {
	 mgaGetILoadBufferLocked( mmesa );

	 /* The kernel ILOAD ioctl requires that the lenght be an even multiple
	  * of MGA_ILOAD_ALIGN.
	  */
	 length = ((length) + MGA_ILOAD_MASK) & ~MGA_ILOAD_MASK;

	 to_copy = MIN2( length, MGA_BUFFER_SIZE );
	 (void) memcpy( mmesa->iload_buffer->address,
			(GLubyte *) texImage->Data + tex_offset, to_copy );

	 if ( MGA_DEBUG & DEBUG_VERBOSE_TEXTURE )
	     fprintf(stderr, "[%s:%d] address/size = 0x%08lx/%d\n",
		     __FILE__, __LINE__,
		     (long) (offset + tex_offset),
		     to_copy );

	 mgaFireILoadLocked( mmesa, offset + tex_offset, to_copy );
	 tex_offset += to_copy;
	 length -= to_copy;
      }
   } else {
      /* FIXME: the sync for direct copy reduces speed.. */
      /* This works, is slower for uploads to card space and needs
       * additional synchronization with the dma stream.
       */
       
      UPDATE_LOCK(mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT);

      memcpy( mmesa->mgaScreen->texVirtual[t->base.heap->heapId] + offset,
	      texImage->Data, length );

      if ( MGA_DEBUG & DEBUG_VERBOSE_TEXTURE )
	 fprintf(stderr, "[%s:%d] address/size = 0x%08lx/%d\n",
		 __FILE__, __LINE__,
		 (long) (mmesa->mgaScreen->texVirtual[t->base.heap->heapId] 
			 + offset),
		 length);
   }
}