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 ); }
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 ); } }
/* 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 ); } }
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; }
void mgaWaitAgeLocked( mgaContextPtr mmesa, int age ) { if (GET_DISPATCH_AGE(mmesa) < age) { UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH ); } }
/* * 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; }
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; }
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; }
/** * 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); } }