/* -*- mode: C; tab-width:8; -*- fxdd.c - 3Dfx VooDoo Mesa device driver functions */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * See the file fxapi.c for more informations about authors * */ #ifdef HAVE_CONFIG_H #include "conf.h" #endif #if defined(FX) #include "fxdrv.h" #include "enums.h" /**********************************************************************/ /***** Miscellaneous functions *****/ /**********************************************************************/ /* Enalbe/Disable dithering */ void fxDDDither(GLcontext *ctx, GLboolean enable) { if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDDither()\n"); } if(enable) grDitherMode(GR_DITHER_4x4); else grDitherMode(GR_DITHER_DISABLE); } /* Return buffer size information */ void fxDDBufferSize(GLcontext *ctx, GLuint *width, GLuint *height) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDBufferSize(...) Start\n"); } *width=fxMesa->width; *height=fxMesa->height; if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDBufferSize(...) End\n"); } } /* Set current drawing color */ static void fxDDSetColor(GLcontext *ctx, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha ) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; GLubyte col[4]; ASSIGN_4V( col, red, green, blue, alpha ); if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDSetColor(%d,%d,%d,%d)\n",red,green,blue,alpha); } fxMesa->color=FXCOLOR4(col); } /* Implements glClearColor() */ static void fxDDClearColor(GLcontext *ctx, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha ) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; GLubyte col[4]; ASSIGN_4V( col, red, green, blue, 255 ); if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDClearColor(%d,%d,%d,%d)\n",red,green,blue,alpha); } fxMesa->clearC=FXCOLOR4( col ); fxMesa->clearA=alpha; } /* Clear the color and/or depth buffers */ static GLbitfield fxDDClear(GLcontext *ctx, GLbitfield mask, GLboolean all, GLint x, GLint y, GLint width, GLint height ) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; GLbitfield newmask; if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDClear(%d,%d,%d,%d)\n",x,y,width,height); } switch(mask & (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)) { case (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT): /* clear color and depth buffer */ if (ctx->Color.DrawDestMask & BACK_LEFT_BIT) { grRenderBuffer(GR_BUFFER_BACKBUFFER); grBufferClear(fxMesa->clearC, fxMesa->clearA, (FxU16)(ctx->Depth.Clear*0xffff)); } if (ctx->Color.DrawDestMask & FRONT_LEFT_BIT) { grRenderBuffer(GR_BUFFER_FRONTBUFFER); grBufferClear(fxMesa->clearC, fxMesa->clearA, (FxU16)(ctx->Depth.Clear*0xffff)); } newmask=mask & (~(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)); break; case (GL_COLOR_BUFFER_BIT): /* clear color buffer */ if(ctx->Color.ColorMask) { grDepthMask(FXFALSE); if (ctx->Color.DrawDestMask & BACK_LEFT_BIT) { grRenderBuffer(GR_BUFFER_BACKBUFFER); grBufferClear(fxMesa->clearC, fxMesa->clearA, 0); } if (ctx->Color.DrawDestMask & FRONT_LEFT_BIT) { grRenderBuffer(GR_BUFFER_FRONTBUFFER); grBufferClear(fxMesa->clearC, fxMesa->clearA, 0); } if(ctx->Depth.Mask) grDepthMask(FXTRUE); } newmask=mask & (~(GL_COLOR_BUFFER_BIT)); break; case (GL_DEPTH_BUFFER_BIT): /* clear depth buffer */ if(ctx->Depth.Mask) { grColorMask(FXFALSE,FXFALSE); grBufferClear(fxMesa->clearC, fxMesa->clearA, (FxU16)(ctx->Depth.Clear*0xffff)); grColorMask(ctx->Color.ColorMask[RCOMP] || ctx->Color.ColorMask[GCOMP] || ctx->Color.ColorMask[BCOMP], ctx->Color.ColorMask[ACOMP] && fxMesa->haveAlphaBuffer); } newmask=mask & (~(GL_DEPTH_BUFFER_BIT)); break; default: newmask=mask; break; } return newmask; } /* Set the buffer used in double buffering */ static GLboolean fxDDSetBuffer(GLcontext *ctx, GLenum mode ) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxDDSetBuffer(%x)\n",mode); } if (mode == GL_FRONT_LEFT) { fxMesa->currentFB = GR_BUFFER_FRONTBUFFER; grRenderBuffer(fxMesa->currentFB); return GL_TRUE; } else if (mode == GL_BACK_LEFT) { fxMesa->currentFB = GR_BUFFER_BACKBUFFER; grRenderBuffer(fxMesa->currentFB); return GL_TRUE; } else { return GL_FALSE; } } static GLboolean fxDDDrawBitMap(GLcontext *ctx, GLint px, GLint py, GLsizei width, GLsizei height, const struct gl_pixelstore_attrib *unpack, const GLubyte *bitmap) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; FxU16 *p; GrLfbInfo_t info; const GLubyte *pb; int x,y; GLint r,g,b,a,scrwidth,scrheight,stride; FxU16 color; /* TODO: with a little work, these bitmap unpacking parameter restrictions * could be removed. */ if((unpack->Alignment!=1) || (unpack->RowLength!=0) || (unpack->SkipPixels!=0) || (unpack->SkipRows!=0) || (unpack->SwapBytes) || (unpack->LsbFirst)) return GL_FALSE; #define ISCLIPPED(rx) ( ((rx)<0) || ((rx)>=scrwidth) ) #define DRAWBIT(i) { \ if(!ISCLIPPED(x+px)) \ if( (*pb) & (1<<(i)) ) \ (*p)=color; \ p++; \ x++; \ if(x>=width) { \ pb++; \ break; \ } \ } scrwidth=fxMesa->width; scrheight=fxMesa->height; if((px>=scrwidth) || (px+width<=0) || (py>=scrheight) || (py+height<=0)) return GL_TRUE; pb=bitmap; if(py<0) { pb+=(height*(-py)) >> (3+1); height+=py; py=0; } if(py+height>=scrheight) height-=(py+height)-scrheight; info.size=sizeof(info); if(!grLfbLock(GR_LFB_WRITE_ONLY, fxMesa->currentFB, GR_LFBWRITEMODE_565, GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) { #ifndef FX_SILENT fprintf(stderr,"fx Driver: error locking the linear frame buffer\n"); #endif return GL_TRUE; } r=(GLint)(ctx->Current.RasterColor[0]*255.0f); g=(GLint)(ctx->Current.RasterColor[1]*255.0f); b=(GLint)(ctx->Current.RasterColor[2]*255.0f); a=(GLint)(ctx->Current.RasterColor[3]*255.0f); color=(FxU16) ( ((FxU16)0xf8 & b) <<(11-3)) | ( ((FxU16)0xfc & g) <<(5-3+1)) | ( ((FxU16)0xf8 & r) >> 3); stride=info.strideInBytes>>1; /* This code is a bit slow... */ for(y=0;ycurrentFB); #undef ISCLIPPED #undef DRAWBIT return GL_TRUE; } static void fxDDFinish(GLcontext *ctx) { FX_grFlush(); } static GLint fxDDGetParameteri(const GLcontext *ctx, GLint param) { switch(param) { case DD_HAVE_HARDWARE_FOG: return 1; default: fprintf(stderr,"fx Driver: internal error in fxDDGetParameteri(): %x\n",param); fxCloseHardware(); exit(-1); } } void fxDDSetNearFar(GLcontext *ctx, GLfloat n, GLfloat f) { FX_CONTEXT(ctx)->new_state |= FX_NEW_FOG; ctx->Driver.RenderStart = fxSetupFXUnits; } /* KW: Put the word Mesa in the render string because quakeworld * checks for this rather than doing a glGet(GL_MAX_TEXTURE_SIZE). * Why? */ static const GLubyte *fxDDGetString(GLcontext *ctx, GLenum name) { static char *extensions="GL_EXT_blend_color GL_EXT_blend_minmax GL_EXT_blend_logic_op GL_EXT_blend_subtract GL_EXT_paletted_texture GL_EXT_point_parameters GL_EXT_polygon_offset GL_EXT_vertex_array GL_EXT_texture_object GL_EXT_texture3D GL_MESA_window_pos GL_MESA_resize_buffers GL_EXT_shared_texture_palette GL_EXT_rescale_normal GL_EXT_abgr GL_SGIS_texture_edge_clamp 3DFX_set_global_palette GL_FXMESA_global_texture_lod_bias"; static char buf[MAX_NUM_SST][64]; fxQueryHardware(); switch (name) { case GL_RENDERER: if(glbHWConfig.SSTs[glbCurrentBoard].type==GR_SSTTYPE_VOODOO) { sprintf(buf[glbCurrentBoard],"Mesa Glide v0.30 Voodoo_Graphics %d CARD/%d FB/%d TM/%d TMU/%s", glbCurrentBoard, (glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.sliDetect ? (glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.fbRam*2) : glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.fbRam), glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.tmuConfig[GR_TMU0].tmuRam+ ((glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.nTexelfx>1) ? glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.tmuConfig[GR_TMU1].tmuRam : 0), glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.nTexelfx, (glbHWConfig.SSTs[glbCurrentBoard].sstBoard.VoodooConfig.sliDetect ? "SLI" : "NOSLI") ); } else { if(glbHWConfig.SSTs[glbCurrentBoard].type==GR_SSTTYPE_SST96) sprintf(buf[glbCurrentBoard],"Glide v0.30 Voodoo_Rush %d CARD/%d FB/%d TM/%d TMU/NOSLI", glbCurrentBoard, glbHWConfig.SSTs[glbCurrentBoard].sstBoard.SST96Config.fbRam, glbHWConfig.SSTs[glbCurrentBoard].sstBoard.SST96Config.tmuConfig.tmuRam, glbHWConfig.SSTs[glbCurrentBoard].sstBoard.SST96Config.nTexelfx ); else strcpy(buf[glbCurrentBoard],"Glide v0.30 UNKNOWN"); } return (GLubyte *) buf[glbCurrentBoard]; case GL_EXTENSIONS: return (GLubyte *) extensions; default: return NULL; } } void fxDDInitExtensions( GLcontext *ctx ) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; gl_extensions_add( ctx, DEFAULT_ON, "3DFX_set_global_palette", 0 ); gl_extensions_add( ctx, DEFAULT_ON, "GL_FXMESA_global_texture_lod_bias", 0); if (!fxMesa->emulateTwoTMUs) gl_extensions_disable( ctx, "GL_ARB_multitexture" ); } /************************************************************************/ /************************************************************************/ /************************************************************************/ /* This is a no-op, since the z-buffer is in hardware */ static void fxAllocDepthBuffer(GLcontext *ctx) { if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxAllocDepthBuffer()\n"); } } /************************************************************************/ /************************************************************************/ /************************************************************************/ /* Check if the hardware supports the current context * * Performs similar work to fxDDChooseRenderState() - should be merged. */ static GLboolean fxIsInHardware(GLcontext *ctx) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; if (!ctx->Hint.AllowDrawMem) return GL_TRUE; /* you'll take it and like it */ if((ctx->RasterMask & STENCIL_BIT) || ((ctx->Color.BlendEnabled) && (ctx->Color.BlendEquation!=GL_FUNC_ADD_EXT)) || ((ctx->Color.ColorLogicOpEnabled) && (ctx->Color.LogicOp!=GL_COPY)) || (ctx->Light.Model.ColorControl==GL_SEPARATE_SPECULAR_COLOR) || (!((ctx->Color.ColorMask[RCOMP]==ctx->Color.ColorMask[GCOMP]) && (ctx->Color.ColorMask[GCOMP]==ctx->Color.ColorMask[BCOMP]) && (ctx->Color.ColorMask[ACOMP]==ctx->Color.ColorMask[ACOMP]))) ) return GL_FALSE; /* Unsupported texture/multitexture cases */ if(fxMesa->emulateTwoTMUs) { if((ctx->Enabled & (TEXTURE0_3D | TEXTURE1_3D)) || /* Not very well written ... */ ((ctx->Enabled & (TEXTURE0_1D | TEXTURE1_1D)) && ((ctx->Enabled & (TEXTURE0_2D | TEXTURE1_2D))!=(TEXTURE0_2D | TEXTURE1_2D))) ) return GL_FALSE; if((ctx->Texture.ReallyEnabled & TEXTURE0_2D) && (ctx->Texture.Unit[0].EnvMode==GL_BLEND)) return GL_FALSE; if((ctx->Texture.ReallyEnabled & TEXTURE1_2D) && (ctx->Texture.Unit[1].EnvMode==GL_BLEND)) return GL_FALSE; if (MESA_VERBOSE & (VERBOSE_DRIVER|VERBOSE_TEXTURE)) fprintf(stderr, "fxMesa: fxIsInHardware, envmode is %s/%s\n", gl_lookup_enum_by_nr(ctx->Texture.Unit[0].EnvMode), gl_lookup_enum_by_nr(ctx->Texture.Unit[1].EnvMode)); /* KW: This was wrong (I think) and I changed it... which doesn't mean * it is now correct... */ if((ctx->Enabled & (TEXTURE0_1D | TEXTURE0_2D | TEXTURE0_3D)) && (ctx->Enabled & (TEXTURE1_1D | TEXTURE1_2D | TEXTURE1_3D))) { /* Can't use multipass to blend a multitextured triangle - fall * back to software. */ if (!fxMesa->haveTwoTMUs && ctx->Color.BlendEnabled) return GL_FALSE; if ((ctx->Texture.Unit[0].EnvMode!=ctx->Texture.Unit[1].EnvMode) && (ctx->Texture.Unit[0].EnvMode!=GL_MODULATE) && (ctx->Texture.Unit[0].EnvMode!=GL_REPLACE)) /* q2, seems ok... */ { if (MESA_VERBOSE&VERBOSE_DRIVER) fprintf(stderr, "fxMesa: unsupported multitex env mode\n"); return GL_FALSE; } } } else { if((ctx->Enabled & (TEXTURE1_1D | TEXTURE1_2D | TEXTURE1_3D)) || /* Not very well written ... */ ((ctx->Enabled & TEXTURE0_1D) && (!(ctx->Enabled & TEXTURE0_2D))) ) return GL_FALSE; if((ctx->Texture.ReallyEnabled & TEXTURE0_2D) && (ctx->Texture.Unit[0].EnvMode==GL_BLEND)) return GL_FALSE; } return GL_TRUE; } #define INTERESTED (~(NEW_MODELVIEW|NEW_PROJECTION|NEW_PROJECTION|NEW_TEXTURE_MATRIX|NEW_USER_CLIP|NEW_CLIENT_STATE|NEW_TEXTURE_ENABLE)) static void fxDDUpdateDDPointers(GLcontext *ctx) { fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx; GLuint new_state = ctx->NewState; if (MESA_VERBOSE&(VERBOSE_DRIVER|VERBOSE_STATE)) fprintf(stderr,"fxmesa: fxDDUpdateDDPointers(...)\n"); if (new_state & (NEW_RASTER_OPS|NEW_TEXTURING)) fxMesa->is_in_hardware = fxIsInHardware(ctx); if (fxMesa->is_in_hardware) { if (fxMesa->new_state) fxSetupFXUnits(ctx); if(new_state & INTERESTED) { fxDDChooseRenderState( ctx ); fxMesa->RenderVBTables=fxDDChooseRenderVBTables(ctx); ctx->Driver.RasterSetup=fxDDChooseSetupFunction(ctx); } ctx->Driver.PointsFunc=fxMesa->PointsFunc; ctx->Driver.LineFunc=fxMesa->LineFunc; ctx->Driver.TriangleFunc=fxMesa->TriangleFunc; ctx->Driver.QuadFunc=fxMesa->QuadFunc; ctx->Driver.RenderVBClippedTab=fxMesa->RenderVBTables[0]; ctx->Driver.RenderVBCulledTab=fxMesa->RenderVBTables[1]; ctx->Driver.RenderVBRawTab=fxMesa->RenderVBTables[2]; } ctx->Driver.AllocDepthBuffer=fxAllocDepthBuffer; ctx->Driver.DepthTestSpan=fxDDDepthTestSpanGeneric; ctx->Driver.DepthTestPixels=fxDDDepthTestPixelsGeneric; ctx->Driver.ReadDepthSpanFloat=fxDDReadDepthSpanFloat; ctx->Driver.ReadDepthSpanInt=fxDDReadDepthSpanInt; ctx->Driver.RenderStart = 0; } void fxSetupDDPointers(GLcontext *ctx) { if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxSetupDDPointers()\n"); } ctx->Driver.UpdateState=fxDDUpdateDDPointers; ctx->Driver.GetString=fxDDGetString; ctx->Driver.Dither=fxDDDither; ctx->Driver.NearFar=fxDDSetNearFar; ctx->Driver.GetParameteri=fxDDGetParameteri; ctx->Driver.ClearIndex=NULL; ctx->Driver.ClearColor=fxDDClearColor; ctx->Driver.Clear=fxDDClear; ctx->Driver.Index=NULL; ctx->Driver.Color=fxDDSetColor; ctx->Driver.SetBuffer=fxDDSetBuffer; ctx->Driver.GetBufferSize=fxDDBufferSize; ctx->Driver.Bitmap=fxDDDrawBitMap; ctx->Driver.DrawPixels=NULL; ctx->Driver.Finish=fxDDFinish; ctx->Driver.Flush=NULL; ctx->Driver.RenderStart=NULL; ctx->Driver.RenderFinish=NULL; ctx->Driver.TexEnv=fxDDTexEnv; ctx->Driver.TexImage=fxDDTexImg; ctx->Driver.TexSubImage=fxDDTexSubImg; ctx->Driver.TexParameter=fxDDTexParam; ctx->Driver.BindTexture=fxDDTexBind; ctx->Driver.DeleteTexture=fxDDTexDel; ctx->Driver.UpdateTexturePalette=fxDDTexPalette; ctx->Driver.UseGlobalTexturePalette=fxDDTexUseGlbPalette; ctx->Driver.RectFunc=NULL; ctx->Driver.AlphaFunc=fxDDAlphaFunc; ctx->Driver.BlendFunc=fxDDBlendFunc; ctx->Driver.DepthFunc=fxDDDepthFunc; ctx->Driver.DepthMask=fxDDDepthMask; ctx->Driver.ColorMask=fxDDColorMask; ctx->Driver.Fogfv=fxDDFogfv; ctx->Driver.Scissor=fxDDScissor; ctx->Driver.FrontFace=fxDDFrontFace; ctx->Driver.CullFace=fxDDCullFace; ctx->Driver.ShadeModel=fxDDShadeModel; ctx->Driver.Enable=fxDDEnable; ctx->Driver.RegisterVB=fxDDRegisterVB; ctx->Driver.UnregisterVB=fxDDUnregisterVB; ctx->Driver.RegisterPipelineStages = fxDDRegisterPipelineStages; ctx->Driver.OptimizeImmediatePipeline = 0; /* nothing done yet */ ctx->Driver.OptimizePrecalcPipeline = 0; /* if (getenv("MESA_USE_FAST") || getenv("FX_USE_FAST")) */ /* ctx->Driver.OptimizePrecalcPipeline = fxDDOptimizePrecalcPipeline; */ if (!getenv("FX_NO_FAST")) ctx->Driver.BuildPrecalcPipeline = fxDDBuildPrecalcPipeline; ctx->Driver.TriangleCaps = DD_TRI_CULL|DD_TRI_OFFSET|DD_TRI_LIGHT_TWOSIDE; fxSetupDDSpanPointers(ctx); FX_CONTEXT(ctx)->render_index = 1; /* force an update */ fxDDUpdateDDPointers(ctx); } #else /* * Need this to provide at least one external definition. */ int gl_fx_dummy_function_dd(void) { return 0; } #endif /* FX */