/* * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2003 S3 Graphics, 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 * 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 * VIA, S3 GRAPHICS, 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. */ #include #include #include #include "main/mtypes.h" #include "main/macros.h" #include "main/dd.h" #include "main/context.h" #include "main/colormac.h" #include "main/mm.h" #include "swrast/swrast.h" #include "savagecontext.h" #include "savageioctl.h" #include "savagestate.h" #include "savagespan.h" #include "drm.h" #include #define DEPTH_SCALE_16 ((1<<16)-1) #define DEPTH_SCALE_24 ((1<<24)-1) void savageGetDMABuffer( savageContextPtr imesa ) { int idx = 0; int size = 0; drmDMAReq dma; int retcode; drmBufPtr buf; assert (imesa->savageScreen->bufs); if (SAVAGE_DEBUG & DEBUG_DMA) fprintf(stderr, "Getting dma buffer\n"); dma.context = imesa->hHWContext; dma.send_count = 0; dma.send_list = NULL; dma.send_sizes = NULL; dma.flags = 0; dma.request_count = 1; dma.request_size = imesa->bufferSize; dma.request_list = &idx; dma.request_sizes = &size; dma.granted_count = 0; if (SAVAGE_DEBUG & DEBUG_DMA) fprintf(stderr, "drmDMA (get) ctx %d count %d size 0x%x\n", dma.context, dma.request_count, dma.request_size); while (1) { retcode = drmDMA(imesa->driFd, &dma); if (SAVAGE_DEBUG & DEBUG_DMA) 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 (SAVAGE_DEBUG & DEBUG_DMA) fprintf(stderr, "\n\nflush"); } buf = &(imesa->savageScreen->bufs->list[idx]); if (SAVAGE_DEBUG & DEBUG_DMA) 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); imesa->dmaVtxBuf.total = buf->total / 4; imesa->dmaVtxBuf.used = 0; imesa->dmaVtxBuf.flushed = 0; imesa->dmaVtxBuf.idx = buf->idx; imesa->dmaVtxBuf.buf = (uint32_t *)buf->address; if (SAVAGE_DEBUG & DEBUG_DMA) fprintf(stderr, "finished getbuffer\n"); } #if 0 /* Still keeping this around because it demonstrates page flipping and * automatic z-clear. */ static void savage_BCI_clear(struct gl_context *ctx, drm_savage_clear_t *pclear) { savageContextPtr imesa = SAVAGE_CONTEXT(ctx); int nbox = imesa->sarea->nbox; drm_clip_rect_t *pbox = imesa->sarea->boxes; int i; if (nbox > SAVAGE_NR_SAREA_CLIPRECTS) nbox = SAVAGE_NR_SAREA_CLIPRECTS; for (i = 0 ; i < nbox ; i++, pbox++) { unsigned int x = pbox->x1; unsigned int y = pbox->y1; unsigned int width = pbox->x2 - x; unsigned int height = pbox->y2 - y; uint32_t *bciptr; if (pbox->x1 > pbox->x2 || pbox->y1 > pbox->y2 || pbox->x2 > imesa->savageScreen->width || pbox->y2 > imesa->savageScreen->height) continue; if ( pclear->flags & SAVAGE_FRONT ) { bciptr = savageDMAAlloc (imesa, 8); WRITE_CMD((bciptr) , 0x4BCC8C00,uint32_t); WRITE_CMD((bciptr) , imesa->savageScreen->frontOffset,uint32_t); WRITE_CMD((bciptr) , imesa->savageScreen->frontBitmapDesc,uint32_t); WRITE_CMD((bciptr) , pclear->clear_color,uint32_t); WRITE_CMD((bciptr) , (y <<16) | x,uint32_t); WRITE_CMD((bciptr) , (height << 16) | width,uint32_t); savageDMACommit (imesa, bciptr); } if ( pclear->flags & SAVAGE_BACK ) { bciptr = savageDMAAlloc (imesa, 8); WRITE_CMD((bciptr) , 0x4BCC8C00,uint32_t); WRITE_CMD((bciptr) , imesa->savageScreen->backOffset,uint32_t); WRITE_CMD((bciptr) , imesa->savageScreen->backBitmapDesc,uint32_t); WRITE_CMD((bciptr) , pclear->clear_color,uint32_t); WRITE_CMD((bciptr) , (y <<16) | x,uint32_t); WRITE_CMD((bciptr) , (height << 16) | width,uint32_t); savageDMACommit (imesa, bciptr); } if ( pclear->flags & (SAVAGE_DEPTH |SAVAGE_STENCIL) ) { uint32_t writeMask = 0x0; if(imesa->hw_stencil) { if(pclear->flags & SAVAGE_STENCIL) { writeMask |= 0xFF000000; } if(pclear->flags & SAVAGE_DEPTH) { writeMask |= 0x00FFFFFF; } } if(imesa->IsFullScreen && imesa->NotFirstFrame && imesa->savageScreen->chipset >= S3_SAVAGE4) { imesa->regs.s4.zBufCtrl.ni.autoZEnable = GL_TRUE; imesa->regs.s4.zBufCtrl.ni.frameID = ~imesa->regs.s4.zBufCtrl.ni.frameID; imesa->dirty |= SAVAGE_UPLOAD_GLOBAL; } else { if(imesa->IsFullScreen) imesa->NotFirstFrame = GL_TRUE; if(imesa->hw_stencil) { bciptr = savageDMAAlloc (imesa, 10); if(writeMask != 0xFFFFFFFF) { WRITE_CMD((bciptr) , 0x960100D7,uint32_t); WRITE_CMD((bciptr) , writeMask,uint32_t); } } else { bciptr = savageDMAAlloc (imesa, 6); } WRITE_CMD((bciptr) , 0x4BCC8C00,uint32_t); WRITE_CMD((bciptr) , imesa->savageScreen->depthOffset,uint32_t); WRITE_CMD((bciptr) , imesa->savageScreen->depthBitmapDesc,uint32_t); WRITE_CMD((bciptr) , pclear->clear_depth,uint32_t); WRITE_CMD((bciptr) , (y <<16) | x,uint32_t); WRITE_CMD((bciptr) , (height << 16) | width,uint32_t); if(imesa->hw_stencil) { if(writeMask != 0xFFFFFFFF) { WRITE_CMD((bciptr) , 0x960100D7,uint32_t); WRITE_CMD((bciptr) , 0xFFFFFFFF,uint32_t); } } savageDMACommit (imesa, bciptr); } } } /* FK: Make sure that the clear stuff is emitted. Otherwise a software fallback may get overwritten by a delayed clear. */ savageDMAFlush (imesa); } static void savage_BCI_swap(savageContextPtr imesa) { int nbox = imesa->sarea->nbox; drm_clip_rect_t *pbox = imesa->sarea->boxes; int i; volatile uint32_t *bciptr; if (nbox > SAVAGE_NR_SAREA_CLIPRECTS) nbox = SAVAGE_NR_SAREA_CLIPRECTS; savageDMAFlush (imesa); if(imesa->IsFullScreen) { /* full screen*/ unsigned int tmp0; tmp0 = imesa->savageScreen->frontOffset; imesa->savageScreen->frontOffset = imesa->savageScreen->backOffset; imesa->savageScreen->backOffset = tmp0; if(imesa->toggle == TARGET_BACK) imesa->toggle = TARGET_FRONT; else imesa->toggle = TARGET_BACK; driFlipRenderbuffers(imesa->glCtx->DrawBuffer, imesa->toggle != TARGET_FRONT); imesa->regs.s4.destCtrl.ni.offset = imesa->savageScreen->backOffset>>11; imesa->dirty |= SAVAGE_UPLOAD_GLOBAL; bciptr = SAVAGE_GET_BCI_POINTER(imesa,3); *(bciptr) = 0x960100B0; *(bciptr) = (imesa->savageScreen->frontOffset); *(bciptr) = 0xA0000000; } else { /* Use bitblt copy from back to front buffer*/ for (i = 0 ; i < nbox; i++, pbox++) { unsigned int w = pbox->x2 - pbox->x1; unsigned int h = pbox->y2 - pbox->y1; if (pbox->x1 > pbox->x2 || pbox->y1 > pbox->y2 || pbox->x2 > imesa->savageScreen->width || pbox->y2 > imesa->savageScreen->height) continue; bciptr = SAVAGE_GET_BCI_POINTER(imesa,6); *(bciptr) = 0x4BCC00C0; *(bciptr) = imesa->savageScreen->backOffset; *(bciptr) = imesa->savageScreen->backBitmapDesc; *(bciptr) = (pbox->y1 <<16) | pbox->x1; /*x0, y0*/ *(bciptr) = (pbox->y1 <<16) | pbox->x1; *(bciptr) = (h << 16) | w; } } } #endif static GLboolean intersect_rect( drm_clip_rect_t *out, const drm_clip_rect_t *a, const drm_clip_rect_t *b ) { *out = *a; if (b->x1 > out->x1) out->x1 = b->x1; if (b->y1 > out->y1) out->y1 = b->y1; if (b->x2 < out->x2) out->x2 = b->x2; if (b->y2 < out->y2) out->y2 = b->y2; return ((out->x1 < out->x2) && (out->y1 < out->y2)); } static GLuint savageIntersectClipRects(drm_clip_rect_t *dest, const drm_clip_rect_t *src, GLuint nsrc, const drm_clip_rect_t *clip) { GLuint i, ndest; for (i = 0, ndest = 0; i < nsrc; ++i, ++src) { if (intersect_rect(dest, src, clip)) { dest++; ndest++; } } return ndest; } static void savageDDClear( struct gl_context *ctx, GLbitfield mask ) { savageContextPtr imesa = SAVAGE_CONTEXT( ctx ); GLuint colorMask, depthMask, clearColor, clearDepth, flags; GLint cx = ctx->DrawBuffer->_Xmin; GLint cy = ctx->DrawBuffer->_Ymin; GLint cw = ctx->DrawBuffer->_Xmax - cx; GLint ch = ctx->DrawBuffer->_Ymax - cy; /* XXX FIX ME: the cx,cy,cw,ch vars are currently ignored! */ (void) ch; (void) cw; if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG) fprintf (stderr, "%s\n", __FUNCTION__); clearColor = imesa->ClearColor; if (imesa->float_depth) { if (imesa->savageScreen->zpp == 2) clearDepth = savageEncodeFloat16(1.0 - ctx->Depth.Clear); else clearDepth = savageEncodeFloat24(1.0 - ctx->Depth.Clear); } else { if (imesa->savageScreen->zpp == 2) clearDepth = (GLuint) ((1.0 - ctx->Depth.Clear) * DEPTH_SCALE_16); else clearDepth = (GLuint) ((1.0 - ctx->Depth.Clear) * DEPTH_SCALE_24); } colorMask = 0; depthMask = 0; switch (imesa->savageScreen->cpp) { case 2: colorMask = PACK_COLOR_565(ctx->Color.ColorMask[0][0], ctx->Color.ColorMask[0][1], ctx->Color.ColorMask[0][2]); break; case 4: colorMask = PACK_COLOR_8888(ctx->Color.ColorMask[0][3], ctx->Color.ColorMask[0][2], ctx->Color.ColorMask[0][1], ctx->Color.ColorMask[0][0]); break; } flags = 0; if (mask & BUFFER_BIT_FRONT_LEFT) { flags |= SAVAGE_FRONT; mask &= ~BUFFER_BIT_FRONT_LEFT; } if (mask & BUFFER_BIT_BACK_LEFT) { flags |= SAVAGE_BACK; mask &= ~BUFFER_BIT_BACK_LEFT; } if ((mask & BUFFER_BIT_DEPTH) && ctx->Depth.Mask) { flags |= SAVAGE_DEPTH; depthMask |= (imesa->savageScreen->zpp == 2) ? 0xffffffff : 0x00ffffff; mask &= ~BUFFER_BIT_DEPTH; } if((mask & BUFFER_BIT_STENCIL) && imesa->hw_stencil) { flags |= SAVAGE_DEPTH; depthMask |= 0xff000000; mask &= ~BUFFER_BIT_STENCIL; } savageFlushVertices(imesa); if (flags) { GLboolean depthCleared = GL_FALSE; if (flags & (SAVAGE_FRONT|SAVAGE_BACK)) { drm_savage_cmd_header_t *cmd; cmd = savageAllocCmdBuf(imesa, sizeof(drm_savage_cmd_header_t)); cmd[0].clear0.cmd = SAVAGE_CMD_CLEAR; if ((flags & SAVAGE_DEPTH) && clearDepth == clearColor && depthMask == colorMask) { cmd[0].clear0.flags = flags; depthCleared = GL_TRUE; } else cmd[0].clear0.flags = flags & (SAVAGE_FRONT|SAVAGE_BACK); cmd[1].clear1.mask = colorMask; cmd[1].clear1.value = clearColor; } if ((flags & SAVAGE_DEPTH) && !depthCleared) { drm_savage_cmd_header_t *cmd; cmd = savageAllocCmdBuf(imesa, sizeof(drm_savage_cmd_header_t)); cmd[0].clear0.cmd = SAVAGE_CMD_CLEAR; cmd[0].clear0.flags = SAVAGE_DEPTH; cmd[1].clear1.mask = depthMask; cmd[1].clear1.value = clearDepth; } } if (mask) _swrast_Clear( ctx, mask ); } /* * Copy the back buffer to the front buffer. */ void savageSwapBuffers( __DRIdrawable *dPriv ) { savageContextPtr imesa; if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG) fprintf (stderr, "%s\n================================\n", __FUNCTION__); assert(dPriv); assert(dPriv->driContextPriv); assert(dPriv->driContextPriv->driverPrivate); imesa = (savageContextPtr) dPriv->driContextPriv->driverPrivate; if (imesa->IsDouble) _mesa_notifySwapBuffers( imesa->glCtx ); FLUSH_BATCH(imesa); if (imesa->sync_frames) imesa->lastSwap = savageEmitEvent( imesa, 0 ); if (imesa->lastSwap != 0) savageWaitEvent( imesa, imesa->lastSwap ); { drm_savage_cmd_header_t *cmd = savageAllocCmdBuf(imesa, 0); cmd->cmd.cmd = SAVAGE_CMD_SWAP; imesa->inSwap = GL_TRUE; /* ignore scissors in savageFlushCmdBuf */ savageFlushCmdBuf(imesa, GL_FALSE); imesa->inSwap = GL_FALSE; } if (!imesa->sync_frames) /* don't sync, but limit the lag to one frame. */ imesa->lastSwap = savageEmitEvent( imesa, 0 ); } unsigned int savageEmitEventLocked( savageContextPtr imesa, unsigned int flags ) { drm_savage_event_emit_t event; int ret; event.count = 0; event.flags = flags; ret = drmCommandWriteRead( imesa->driFd, DRM_SAVAGE_BCI_EVENT_EMIT, &event, sizeof(event) ); if (ret) { fprintf (stderr, "emit event returned %d\n", ret); exit (1); } return event.count; } unsigned int savageEmitEvent( savageContextPtr imesa, unsigned int flags ) { unsigned int ret; LOCK_HARDWARE( imesa ); ret = savageEmitEventLocked( imesa, flags ); UNLOCK_HARDWARE( imesa ); return ret; } void savageWaitEvent( savageContextPtr imesa, unsigned int count ) { drm_savage_event_wait_t event; int ret; event.count = count; event.flags = 0; ret = drmCommandWriteRead( imesa->driFd, DRM_SAVAGE_BCI_EVENT_WAIT, &event, sizeof(event) ); if (ret) { fprintf (stderr, "wait event returned %d\n", ret); exit (1); } } void savageFlushVertices( savageContextPtr imesa ) { struct savage_vtxbuf_t *buffer = imesa->vtxBuf; if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG) fprintf (stderr, "%s\n", __FUNCTION__); if (!buffer->total) return; if (buffer->used > buffer->flushed) { drm_savage_cmd_header_t *cmd; /* State must be updated "per primitive" because hardware * culling must be disabled for unfilled primitives, points * and lines. */ savageEmitChangedState (imesa); cmd = savageAllocCmdBuf(imesa, 0); cmd->prim.cmd = buffer == &imesa->dmaVtxBuf ? SAVAGE_CMD_DMA_PRIM : SAVAGE_CMD_VB_PRIM; cmd->prim.prim = imesa->HwPrim; cmd->prim.skip = imesa->skip; cmd->prim.start = buffer->flushed / imesa->HwVertexSize; cmd->prim.count = buffer->used / imesa->HwVertexSize - cmd->prim.start; buffer->flushed = buffer->used; } } void savageFlushCmdBufLocked( savageContextPtr imesa, GLboolean discard ) { __DRIdrawable *dPriv = imesa->driDrawable; if (!imesa->dmaVtxBuf.total) discard = GL_FALSE; /* complete indexed drawing commands */ savageFlushElts(imesa); if (imesa->cmdBuf.write != imesa->cmdBuf.start || discard) { drm_savage_cmdbuf_t cmdbuf; drm_savage_cmd_header_t *start; int ret; /* If we lost the context we must restore the initial state (at * the start of the command buffer). */ if (imesa->lostContext) { start = imesa->cmdBuf.base; imesa->lostContext = GL_FALSE; } else start = imesa->cmdBuf.start; if ((SAVAGE_DEBUG & DEBUG_DMA) && discard) fprintf (stderr, "Discarding DMA buffer, used=%u\n", imesa->dmaVtxBuf.used); cmdbuf.dma_idx = imesa->dmaVtxBuf.idx; cmdbuf.discard = discard; cmdbuf.vb_addr = imesa->clientVtxBuf.buf; cmdbuf.vb_size = imesa->clientVtxBuf.total*4; cmdbuf.vb_stride = imesa->HwVertexSize; cmdbuf.cmd_addr = start; cmdbuf.size = (imesa->cmdBuf.write - start); if (!imesa->inSwap && imesa->scissor.enabled) { drm_clip_rect_t *box = dPriv->pClipRects, *ibox; drm_clip_rect_t scissor; GLuint nbox = dPriv->numClipRects, nibox; /* transform and clip scissor to viewport */ scissor.x1 = MAX2(imesa->scissor.x, 0) + dPriv->x; scissor.y1 = MAX2(dPriv->h - imesa->scissor.y - imesa->scissor.h, 0) + dPriv->y; scissor.x2 = MIN2(imesa->scissor.x + imesa->scissor.w, dPriv->w) + dPriv->x; scissor.y2 = MIN2(dPriv->h - imesa->scissor.y, dPriv->h) + dPriv->y; /* intersect cliprects with scissor */ ibox = malloc(dPriv->numClipRects*sizeof(drm_clip_rect_t)); if (!ibox) { fprintf(stderr, "Out of memory.\n"); exit(1); } nibox = savageIntersectClipRects(ibox, box, nbox, &scissor); cmdbuf.nbox = nibox; cmdbuf.box_addr = ibox; } else { cmdbuf.nbox = dPriv->numClipRects; cmdbuf.box_addr = dPriv->pClipRects; } ret = drmCommandWrite( imesa->driFd, DRM_SAVAGE_BCI_CMDBUF, &cmdbuf, sizeof(cmdbuf) ); if (ret) { fprintf (stderr, "cmdbuf ioctl returned %d\n", ret); exit(1); } if (cmdbuf.box_addr != dPriv->pClipRects) { free(cmdbuf.box_addr); } /* Save the current state at the start of the command buffer. That * state will only be emitted, if the context was lost since the * last command buffer. */ imesa->cmdBuf.write = imesa->cmdBuf.base; savageEmitOldState(imesa); imesa->cmdBuf.start = imesa->cmdBuf.write; } if (discard) { assert (!savageHaveIndexedVerts(imesa)); imesa->dmaVtxBuf.total = 0; imesa->dmaVtxBuf.used = 0; imesa->dmaVtxBuf.flushed = 0; } if (!savageHaveIndexedVerts(imesa)) { imesa->clientVtxBuf.used = 0; imesa->clientVtxBuf.flushed = 0; } } void savageFlushCmdBuf( savageContextPtr imesa, GLboolean discard ) { if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG) fprintf (stderr, "%s\n", __FUNCTION__); LOCK_HARDWARE(imesa); savageFlushCmdBufLocked (imesa, discard); UNLOCK_HARDWARE(imesa); } static void savageDDFlush( struct gl_context *ctx ) { savageContextPtr imesa = SAVAGE_CONTEXT(ctx); if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG) fprintf (stderr, "%s\n", __FUNCTION__); savageFlushVertices (imesa); savageFlushCmdBuf(imesa, GL_FALSE); } static void savageDDFinish( struct gl_context *ctx ) { savageContextPtr imesa = SAVAGE_CONTEXT(ctx); if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG) fprintf (stderr, "%s\n", __FUNCTION__); savageFlushVertices (imesa); savageFlushCmdBuf(imesa, GL_FALSE); WAIT_IDLE_EMPTY(imesa); } void savageDDInitIoctlFuncs( struct gl_context *ctx ) { ctx->Driver.Clear = savageDDClear; ctx->Driver.Flush = savageDDFlush; ctx->Driver.Finish = savageDDFinish; }