//---------------------------------------------------------------------------- // // rastctx.cpp // // Context functions + state functions. // // Copyright (C) Microsoft Corporation, 1997. // //---------------------------------------------------------------------------- #include "pch.cpp" #pragma hdrstop #include "rampif.h" #include "rampmat.hpp" // Unlock previous texture if necessary. It's called before the texture handle // is going to be changed. #define CHECK_AND_UNLOCK_TEXTURE \ { \ if (m_uFlags & D3DCONTEXT_TEXTURE_LOCKED) \ { \ RastUnlockSpanTexture(); \ } \ } inline void D3DContext::RampSetFogData(UINT32 uState, UINT32 uStateVal) { RLDDIRampLightingDriver *pRampdrv = (RLDDIRampLightingDriver*)m_RastCtx.pRampDrv; if (pRampdrv != NULL) { switch(uState) { case D3DRENDERSTATE_FOGENABLE : pRampdrv->fog_enable = uStateVal; break; case D3DRENDERSTATE_FOGCOLOR : pRampdrv->fog_color = uStateVal; break; case D3DRENDERSTATE_FOGTABLEMODE : pRampdrv->driver.fog_mode = (D3DFOGMODE)uStateVal; break; case D3DRENDERSTATE_FOGTABLESTART : pRampdrv->driver.fog_start = (FLOAT)uStateVal; break; case D3DRENDERSTATE_FOGTABLEEND : pRampdrv->driver.fog_end = (FLOAT)uStateVal; break; case D3DRENDERSTATE_FOGTABLEDENSITY: pRampdrv->driver.fog_density = (FLOAT)uStateVal; break; } } } //---------------------------------------------------------------------------- // // FillContext // // Fill the context with the info. from the surfaces. // //---------------------------------------------------------------------------- HRESULT D3DContext::FillContext(LPDIRECTDRAWSURFACE pDDS, LPDIRECTDRAWSURFACE pDDSZ) { HRESULT hr; LPDDRAWI_DDRAWSURFACE_LCL pLcl = ((LPDDRAWI_DDRAWSURFACE_INT)(pDDS))->lpLcl; m_RastCtx.iSurfaceStride = DDSurf_Pitch(pLcl); m_RastCtx.iSurfaceBitCount = DDSurf_BitDepth(pLcl); m_RastCtx.iSurfaceStep = m_RastCtx.iSurfaceBitCount/8; HR_RET(FindOutSurfFormat(&(DDSurf_PixFmt(pLcl)), (D3DI_SPANTEX_FORMAT *)&(m_RastCtx.iSurfaceType))); m_RastCtx.Clip.left = m_RastCtx.Clip.top = 0; m_RastCtx.Clip.bottom = DDSurf_Height(pLcl); m_RastCtx.Clip.right = DDSurf_Width(pLcl); if (pDDSZ != NULL) { pLcl = ((LPDDRAWI_DDRAWSURFACE_INT)(pDDSZ))->lpLcl; m_RastCtx.pZBits = (PUINT8)SURFACE_MEMORY(pDDSZ); m_RastCtx.iZStride = DDSurf_Pitch(pLcl); m_RastCtx.iZBitCount = DDSurf_BitDepth(pLcl); m_RastCtx.iZStep = m_RastCtx.iZBitCount/8; } else { m_RastCtx.pZBits = NULL; m_RastCtx.iZStride = 0; m_RastCtx.iZBitCount = 0; m_RastCtx.iZStep = 0; } m_RastCtx.pDDS = pDDS; m_RastCtx.pDDSZ = pDDSZ; m_RastCtx.dwSize = sizeof(D3DI_RASTCTX); // Make sure SpanInit is called at least once SetAllStatesDirtyBits(); // Check for MsGolf AppHack if (pLcl->lpSurfMore->lpDD_lcl->dwAppHackFlags & DDRAW_APPCOMPAT_FORCEMODULATED) { m_RastCtx.uFlags |= RASTCTXFLAGS_APPHACK_MSGOLF; } return D3D_OK; } HRESULT D3DContext::Initialize(LPDIRECTDRAWSURFACE pDDS, LPDIRECTDRAWSURFACE pDDSZ, DWORD BeadSet, DWORD devVer) { HRESULT hr; // Initialize the primitive processor. HR_RET(m_PrimProc.Initialize()); memset(&m_RastCtx, 0, sizeof(m_RastCtx)); m_uFlags = 0; HR_RET(FillContext(pDDS, pDDSZ)); m_PrimProc.SetCtx(&m_RastCtx); dwSize = sizeof(D3DContext); // Initialize bead table enum m_RastCtx.BeadSet = (D3DI_BEADSET)BeadSet; STATESET_INIT(m_renderstate_override); // Init FVF data as legacy TL vertex m_fvfData.preFVF = -1; CheckFVF(D3DFVF_TLVERTEX); m_RastCtx.uDevVer = devVer; // All render and texture stage state is initialized by // DIRECT3DDEVICEI::stateInitialize // Init prim function table. It will be updated for RAMP, or when FVF // control word changes or when fill mode changes. m_fnPrims.pfnTri = RGB_TriNoPackSolid; m_fnPrims.pfnPoint = RGB_PointNoPack; m_fnPrims.pfnLine = RGB_LineNoPack; // This one should be always the same. m_fnPrims.pfnStoreLastPixelState = RGBRAMP_StoreLastPixelState; m_fnPrims.pfnDp2SetRenderStates = RGBRAMP_Dp2SetRenderStates; m_fnPrims.pfnDp2TextureStageState = RGBRAMP_Dp2TextureStageState; m_fnPrims.pfnDp2SetViewport = RGBRAMP_Dp2SetViewport; m_fnPrims.pfnDp2SetWRange = RGBRAMP_Dp2SetWRange; // Enable MMX Fast Paths (Monolithics) if a registry key for it is not 0 m_RastCtx.dwMMXFPDisableMask[0] = 0x0; // enable MMX FP's by default HKEY hKey = (HKEY) NULL; if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, RESPATH_D3D, &hKey) ) { DWORD dwType; DWORD dwValue; DWORD dwDisableMask[MMX_FP_DISABLE_MASK_NUM] = {0x0}; DWORD dwSize = 4; // only code up looking at one mask, for now DDASSERT(MMX_FP_DISABLE_MASK_NUM == 1); if ( ERROR_SUCCESS == RegQueryValueEx( hKey, "MMXFPDisableMask0", NULL, &dwType, (LPBYTE) &dwValue, &dwSize) && dwType == REG_DWORD ) { dwDisableMask[0] = dwValue; } if ( ERROR_SUCCESS == RegQueryValueEx( hKey, "MMX Fast Path", NULL, &dwType, (LPBYTE) &dwValue, &dwSize) && dwType == REG_DWORD) { if (dwValue == 0) { // Override MMXFPDisableMask0 and disable all MMX Fast Paths m_RastCtx.dwMMXFPDisableMask[0] = 0xffffffff; } else { // Take all MMX paths not disabled by MMXFPDisableMask0 m_RastCtx.dwMMXFPDisableMask[0] = dwDisableMask[0]; } } RegCloseKey( hKey ); } return D3D_OK; } HRESULT D3DContext::SetViewport(LPD3DHAL_DP2VIEWPORTINFO pVpt) { m_RastCtx.Clip.left = pVpt->dwX; m_RastCtx.Clip.top = pVpt->dwY; m_RastCtx.Clip.bottom = pVpt->dwY + pVpt->dwHeight; m_RastCtx.Clip.right = pVpt->dwX + pVpt->dwWidth; return D3D_OK; } inline HRESULT D3DContext::CreateRampLightingDriver(void) { m_RastCtx.pRampDrv = RLDDIRampCreate(&m_RastCtx); if (m_RastCtx.pRampDrv == NULL) { return DDERR_OUTOFMEMORY; } else { return D3D_OK; } } inline void D3DContext::DestroyRampLightingDriver(void) { RLDDIRampDestroy((RLDDIRampLightingDriver*)m_RastCtx.pRampDrv); m_RastCtx.pRampDrv = NULL; } //---------------------------------------------------------------------------- // // RastContextCreateC // // Calls RastContextCreate with the C bead set. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextCreateC(LPD3DHAL_CONTEXTCREATEDATA pCtxData) { return RastContextCreate(pCtxData, (DWORD)D3DIBS_C); } //---------------------------------------------------------------------------- // // RastContextCreateCMMX // // Calls RastContextCreate with the CMMX bead set. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextCreateCMMX(LPD3DHAL_CONTEXTCREATEDATA pCtxData) { return RastContextCreate(pCtxData, (DWORD)D3DIBS_CMMX); } //---------------------------------------------------------------------------- // // RastContextCreateMMX // // Calls RastContextCreate with the MMX bead set. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextCreateMMX(LPD3DHAL_CONTEXTCREATEDATA pCtxData) { return RastContextCreate(pCtxData, (DWORD)D3DIBS_MMX); } //---------------------------------------------------------------------------- // // RastContextCreateMMXAsRGB // // Calls RastContextCreate with the MMX bead set, but remember that we // came from RGB. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextCreateMMXAsRGB(LPD3DHAL_CONTEXTCREATEDATA pCtxData) { return RastContextCreate(pCtxData, (DWORD)D3DIBS_MMXASRGB); } //---------------------------------------------------------------------------- // // RastContextCreateRamp // // Calls RastContextCreate with the ramp bead set. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextCreateRamp(LPD3DHAL_CONTEXTCREATEDATA pCtxData) { HRESULT hr; hr = RastContextCreate(pCtxData, (DWORD)D3DIBS_RAMP); if (pCtxData->ddrval != D3D_OK) { return hr; } // Create a RampLightingDriver D3DContext *pDCtx = (D3DContext *)pCtxData->dwhContext; pCtxData->ddrval = pDCtx->CreateRampLightingDriver(); // Init prim function table for RAMP pDCtx->InitRampFuncs(); return hr; } //---------------------------------------------------------------------------- // // RastContextCreate // // Creates a RASTCTX and initializes it with the info passed in. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextCreate(LPD3DHAL_CONTEXTCREATEDATA pCtxData, DWORD BeadSet) { DDASSERT(pCtxData != NULL); D3DContext *pDCtx = new D3DContext; if (pDCtx == NULL) { pCtxData->ddrval = DDERR_OUTOFMEMORY; return DDHAL_DRIVER_HANDLED; } pCtxData->ddrval = pDCtx->Initialize(pCtxData->lpDDS, pCtxData->lpDDSZ, BeadSet, (DWORD)pCtxData->dwhContext); pCtxData->dwhContext = (ULONG_PTR)pDCtx; PD3DI_RASTCTX pCtx = pDCtx->GetRastCtx(); if ((D3DI_SPTFMT_PALETTE8 == pCtx->iSurfaceType) && (D3DIBS_RAMP != BeadSet)) { // need a ramp lighting driver for 8 bit palettized RGB output pCtxData->ddrval = pDCtx->CreateRampLightingDriver(); if (pCtxData->ddrval == D3D_OK) { // initialize the RGB8 palette RLDDIRampMakePaletteRGB8((RLDDIRampLightingDriver*)pCtx->pRampDrv); // Make sure DD Palette is updated after it gets set by FindLightingRange RLDDIRampUpdateDDPalette(pCtx); } } return DDHAL_DRIVER_HANDLED; } //---------------------------------------------------------------------------- // // RastContextDestroy // // Destroy a rast context. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextDestroy(LPD3DHAL_CONTEXTDESTROYDATA pCtxDestroyData) { D3DContext *pDCtx; VALIDATE_D3DCONTEXT("RastContextDestroy", pCtxDestroyData); PD3DI_RASTCTX pCtx = pDCtx->GetRastCtx(); if (pCtx->pRampDrv) { // destroy the ramp lighting driver, if one was created pDCtx->DestroyRampLightingDriver(); } delete pDCtx; pCtxDestroyData->ddrval = D3D_OK; return DDHAL_DRIVER_HANDLED; } //---------------------------------------------------------------------------- // // ValidateTextureStageState // // Utility function that returns an appropriate D3DERR_ if the current // multi-texture setup can not be rendered, D3D_OK otherwise. // //---------------------------------------------------------------------------- HRESULT D3DContext::ValidateTextureStageState(void) { #if DBG if ((m_RastCtx.pTexture[0] == m_RastCtx.pTexture[1]) && (m_RastCtx.pTexture[0] != NULL) ) { // except under very special circumstances, this will not work in RGB/MMX // since we keep a lot of stage state in the D3DI_SPANTEX structure D3D_ERR("(Rast) ValidateTextureStageState Warning, pTexture[0] == pTexture[1]"); } #endif for (INT i = 0; i < 2; i++) { switch(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(i,D3DTSS_COLOROP)]) { default: return D3DERR_UNSUPPORTEDCOLOROPERATION; case D3DTOP_DISABLE: return D3D_OK; // don't have to validate further if the stage is disabled case D3DTOP_SELECTARG1: case D3DTOP_SELECTARG2: case D3DTOP_MODULATE: case D3DTOP_MODULATE2X: case D3DTOP_MODULATE4X: case D3DTOP_ADD: case D3DTOP_ADDSIGNED: case D3DTOP_BLENDDIFFUSEALPHA: case D3DTOP_BLENDTEXTUREALPHA: case D3DTOP_BLENDFACTORALPHA: case D3DTOP_BLENDTEXTUREALPHAPM: break; } switch(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(i,D3DTSS_COLORARG1)] & ~(D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT)) { default: return D3DERR_UNSUPPORTEDCOLORARG; case (D3DTA_TEXTURE): break; } switch(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(i,D3DTSS_COLORARG2)] & ~(D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT)) { default: return D3DERR_UNSUPPORTEDCOLORARG; case (D3DTA_TFACTOR): case (D3DTA_CURRENT): case (D3DTA_DIFFUSE): break; } switch(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(i,D3DTSS_ALPHAOP)]) { default: return D3DERR_UNSUPPORTEDALPHAOPERATION; case D3DTOP_DISABLE: break; case D3DTOP_SELECTARG1: case D3DTOP_SELECTARG2: case D3DTOP_MODULATE: case D3DTOP_MODULATE2X: case D3DTOP_MODULATE4X: case D3DTOP_ADD: case D3DTOP_ADDSIGNED: case D3DTOP_BLENDDIFFUSEALPHA: case D3DTOP_BLENDTEXTUREALPHA: case D3DTOP_BLENDFACTORALPHA: case D3DTOP_BLENDTEXTUREALPHAPM: // only validate alpha args if alpha op is not disable switch(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(i,D3DTSS_ALPHAARG1)] & ~(D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT)) { default: return D3DERR_UNSUPPORTEDALPHAARG; case (D3DTA_TEXTURE): break; } switch(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(i,D3DTSS_ALPHAARG2)] & ~(D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT)) { default: return D3DERR_UNSUPPORTEDALPHAARG; case (D3DTA_TFACTOR): case (D3DTA_CURRENT): case (D3DTA_DIFFUSE): break; } break; } } // allow unused state to be zero'ed since this is so common if ( !((m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(2,D3DTSS_COLOROP)] == D3DTOP_DISABLE) || (m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(2,D3DTSS_COLOROP)] == 0)) ) { return D3DERR_TOOMANYOPERATIONS; } return D3D_OK; } //---------------------------------------------------------------------------- // // RastValidateTextureStageState // // Returns whether the current multitexture setup can be rendered and, if // so, the number of passes required to render it. // //---------------------------------------------------------------------------- DWORD __stdcall RastValidateTextureStageState(LPD3DHAL_VALIDATETEXTURESTAGESTATEDATA pData) { D3DContext *pDCtx; VALIDATE_D3DCONTEXT("RastValidateTextureStageState", pData); pData->dwNumPasses = 1; pData->ddrval = pDCtx->ValidateTextureStageState(); return DDHAL_DRIVER_HANDLED; } //---------------------------------------------------------------------------- // // RastContextDestroyRamp // // Destroy a rast context. // //---------------------------------------------------------------------------- DWORD __stdcall RastContextDestroyRamp(LPD3DHAL_CONTEXTDESTROYDATA pCtxDestroyData) { D3DContext *pDCtx; VALIDATE_D3DCONTEXT("RastContextDestroy", pCtxDestroyData); pDCtx->DestroyRampLightingDriver(); delete pDCtx; pCtxDestroyData->ddrval = D3D_OK; return DDHAL_DRIVER_HANDLED; } //---------------------------------------------------------------------------- // // RastSetRenderTarget // // Update a rast context with the info from a new render target. // //---------------------------------------------------------------------------- DWORD __stdcall RastSetRenderTarget(LPD3DHAL_SETRENDERTARGETDATA pTgtData) { D3DContext *pDCtx; VALIDATE_D3DCONTEXT("RastSetRenderTarget", pTgtData); pTgtData->ddrval = pDCtx->FillContext(pTgtData->lpDDS, pTgtData->lpDDSZ); return DDHAL_DRIVER_HANDLED; } //---------------------------------------------------------------------------- // // SetRenderState // // Check to see if a state change requires an update to the D3DCTX. // //---------------------------------------------------------------------------- HRESULT D3DContext::SetRenderState(UINT32 uState, UINT32 uStateVal) { // Assume d3dim has filtered out unchanged states StateChanged(uState); m_RastCtx.pdwRenderState[uState] = uStateVal; switch(uState) { case D3DRENDERSTATE_FOGENABLE : case D3DRENDERSTATE_FOGCOLOR : case D3DRENDERSTATE_FOGTABLEMODE : case D3DRENDERSTATE_FOGTABLESTART : case D3DRENDERSTATE_FOGTABLEEND : case D3DRENDERSTATE_FOGTABLEDENSITY: RampSetFogData(uState, uStateVal); break; case D3DRENDERSTATE_CULLMODE: // Set face culling sign from state. switch(uStateVal) { case D3DCULL_CCW: m_RastCtx.uCullFaceSign = 1; break; case D3DCULL_CW: m_RastCtx.uCullFaceSign = 0; break; case D3DCULL_NONE: m_RastCtx.uCullFaceSign = 2; break; } break; case D3DRENDERSTATE_ZENABLE: if ( (D3DZB_FALSE != uStateVal) && (NULL == m_RastCtx.pDDSZ) ) { DPF(0, "(ERROR) (Rast) SetRenderState: Can't set D3DRENDERSTATE_ZENABLE to %d if there is no Z Buffer", uStateVal); m_RastCtx.pdwRenderState[uState] = D3DZB_FALSE; } break; case D3DRENDERSTATE_LASTPIXEL: // Set last-pixel flag from state. if (uStateVal) { m_PrimProc.SetFlags(PPF_DRAW_LAST_LINE_PIXEL); } else { m_PrimProc.ClrFlags(PPF_DRAW_LAST_LINE_PIXEL); } break; // map legacy modes with one-to-one mappings to texture stage 0 case D3DRENDERSTATE_TEXTUREADDRESS: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESS)] = m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSU)] = m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSV)] = uStateVal; MapTextureStage0State(); break; case D3DRENDERSTATE_TEXTUREADDRESSU: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSU)] = uStateVal; MapTextureStage0State(); break; case D3DRENDERSTATE_TEXTUREADDRESSV: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSV)] = uStateVal; MapTextureStage0State(); break; case D3DRENDERSTATE_MIPMAPLODBIAS: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPMAPLODBIAS)] = uStateVal; MapTextureStage0State(); break; case D3DRENDERSTATE_BORDERCOLOR: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_BORDERCOLOR)] = uStateVal; MapTextureStage0State(); break; case D3DRENDERSTATE_TEXTUREMAG: case D3DRENDERSTATE_TEXTUREMIN: // map legacy filtering/sampling state to texture stage 0 MapLegacyTextureFilter(); // assign to current texture MapTextureStage0State(); break; case D3DRENDERSTATE_TEXTUREMAPBLEND: // map legacy blending state to texture stage 0 MapLegacyTextureBlend(); break; // map legacy WRAPU/V to per-index controls case D3DRENDERSTATE_WRAPU: m_RastCtx.pdwRenderState[D3DRENDERSTATE_WRAP0] &= ~D3DWRAP_U; m_RastCtx.pdwRenderState[D3DRENDERSTATE_WRAP0] |= ((uStateVal) ? D3DWRAP_U : 0); StateChanged(D3DRENDERSTATE_WRAP0); break; case D3DRENDERSTATE_WRAPV: m_RastCtx.pdwRenderState[D3DRENDERSTATE_WRAP0] &= ~D3DWRAP_V; m_RastCtx.pdwRenderState[D3DRENDERSTATE_WRAP0] |= ((uStateVal) ? D3DWRAP_V : 0); StateChanged(D3DRENDERSTATE_WRAP0); break; // // NOTE - this compututation of cActTex does not account for blend-only stages // case D3DRENDERSTATE_TEXTUREHANDLE: CHECK_AND_UNLOCK_TEXTURE; // map handle thru to stage 0 m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_TEXTUREMAP)] = uStateVal; m_RastCtx.pTexture[1] = NULL; // set up for single stage if (uStateVal == 0) { m_RastCtx.pTexture[0] = NULL; } else { m_RastCtx.pTexture[0] = HANDLE_TO_SPANTEX(uStateVal); } // map stage 0 state to first texture MapTextureStage0State(); UpdateActiveTexStageCount(); break; case D3DHAL_TSS_TEXTUREMAP0: // Silently zero out legacy handle. They didn't mean it. if (m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREHANDLE] != 0 && uStateVal != 0) { m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREHANDLE] = 0; } CHECK_AND_UNLOCK_TEXTURE; if (uStateVal == 0) { if (m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREHANDLE] == 0) { m_RastCtx.pTexture[0] = NULL; } // don't set m_RastCtx.pTexture[1] = NULL (in case the handle is never // sent again). cActTex will keep it from being used until pTexture[0] is // set to something } else { #if DBG if (HANDLE_TO_SPANTEX(uStateVal) == m_RastCtx.pTexture[1]) { D3D_ERR( "Stage1 and 2 have same texture handle." ); return DDERR_INVALIDPARAMS; } #endif m_RastCtx.pTexture[0] = HANDLE_TO_SPANTEX(uStateVal); } // map stage 0 state to first texture MapTextureStage0State(); UpdateActiveTexStageCount(); break; case D3DHAL_TSS_TEXTUREMAP1: // Silently zero out legacy handle. They didn't mean it. if (m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREHANDLE] != 0 && uStateVal != 0) { m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREHANDLE] = 0; } CHECK_AND_UNLOCK_TEXTURE; if (uStateVal == 0) { m_RastCtx.pTexture[1] = NULL; } else { // 2nd texture can only be enabled if there's an active // 1st texture, but computation of cActTex will prevent this // from happening #if DBG if (HANDLE_TO_SPANTEX(uStateVal) == m_RastCtx.pTexture[0]) { D3D_ERR( "Stage1 and 2 have same texture handle." ); return DDERR_INVALIDPARAMS; } #endif m_RastCtx.pTexture[1] = HANDLE_TO_SPANTEX(uStateVal); } // map stage 1 state to second texture MapTextureStage1State(); UpdateActiveTexStageCount(); break; // map single set ADDRESS to both U and V controls for stages 0 & 1 case D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESS): m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSU)] = uStateVal; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSV)] = uStateVal; MapTextureStage0State(); break; case D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESS): m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESSU)] = uStateVal; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESSV)] = uStateVal; MapTextureStage1State(); break; case D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSU): case D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSV): case D3DHAL_TSS_OFFSET(0,D3DTSS_MIPMAPLODBIAS): case D3DHAL_TSS_OFFSET(0,D3DTSS_MAXMIPLEVEL): case D3DHAL_TSS_OFFSET(0,D3DTSS_BORDERCOLOR): case D3DHAL_TSS_OFFSET(0,D3DTSS_MAGFILTER): case D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER): case D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER): MapTextureStage0State(); break; case D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESSU): case D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESSV): case D3DHAL_TSS_OFFSET(1,D3DTSS_MIPMAPLODBIAS): case D3DHAL_TSS_OFFSET(1,D3DTSS_MAXMIPLEVEL): case D3DHAL_TSS_OFFSET(1,D3DTSS_BORDERCOLOR): case D3DHAL_TSS_OFFSET(1,D3DTSS_MAGFILTER): case D3DHAL_TSS_OFFSET(1,D3DTSS_MINFILTER): case D3DHAL_TSS_OFFSET(1,D3DTSS_MIPFILTER): MapTextureStage1State(); break; case D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP): case D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1): case D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2): case D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP): case D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1): case D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2): case D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP): case D3DHAL_TSS_OFFSET(1,D3DTSS_COLORARG1): case D3DHAL_TSS_OFFSET(1,D3DTSS_COLORARG2): case D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAOP): case D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAARG1): case D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAARG2): // anything that effects the validity of the texture blending // could change the number of active texture stages UpdateActiveTexStageCount(); break; } return D3D_OK; } //----------------------------------------------------------------------------- // // UpdateActiveTexStageCount - Steps through per-stage renderstate and computes // a count of currently active texture stages. For legacy texture, the count // is at most one. // //----------------------------------------------------------------------------- HRESULT D3DContext::UpdateActiveTexStageCount( void ) { HRESULT hr; UINT cNewActTex = 0; // conservative but correct if ((hr = ValidateTextureStageState()) == D3D_OK) { // always one active texture stage for legacy texture mode if ( NULL != m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREHANDLE] ) { cNewActTex = 1; } else { // count number of contiguous-from-zero active texture blend stages for ( INT iStage=0; iStage<2; iStage++ ) { // check for disabled stage (subsequent are thus inactive) // also conservatively checks for incorrectly enabled stage (might be legacy) if ( ( m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(iStage,D3DTSS_COLOROP)] == D3DTOP_DISABLE ) || ( m_RastCtx.pTexture[iStage] == NULL ) ) { break; } // stage is active cNewActTex ++; } } } if (m_RastCtx.cActTex != cNewActTex) { CHECK_AND_UNLOCK_TEXTURE; StateChanged(D3DRENDERSTATE_TEXTUREHANDLE); m_RastCtx.cActTex = cNewActTex; } return hr; } //---------------------------------------------------------------------------- // // UpdateRenderStates // // Update a list of render states and notify components of state change. // //---------------------------------------------------------------------------- HRESULT D3DContext:: UpdateRenderStates(LPDWORD puStateChange, UINT cStateChanges) { HRESULT hr; INT i; UINT32 State, StateVal; if (cStateChanges == 0) { return D3D_OK; } // Update the D3DCTX for (i = 0; i < (INT)cStateChanges; i++) { State = *puStateChange ++; StateVal = * puStateChange++; HR_RET(SetRenderState(State, StateVal)); } return D3D_OK; } //---------------------------------------------------------------------------- // // UpdateAllRenderStates // // Update all render states. // It is still kept here because we probably need it in the case of fail-over. // //---------------------------------------------------------------------------- HRESULT D3DContext::UpdateAllRenderStates(LPDWORD puStates) { HRESULT hr, hrSet; INT i; DDASSERT(puStates != NULL); // Update D3DCTX. // Attempt to set as many states as possible, even if there are // errors on some. This allows context initialization to work // even though some of the states fail due to dependencies on // other state, such as active texture handles. // SetRenderState failures are noted and returned eventually, // even if everything else succeeds. hrSet = D3D_OK; for (i = 0; i < D3DHAL_MAX_RSTATES_AND_STAGES; i++) { if ((hr = SetRenderState(i, puStates[i])) != D3D_OK) { hrSet = hr; } } return hrSet; } //---------------------------------------------------------------------------- // // Dp2SetRenderStates // // Called by Drawprim2 to set render states.. // //---------------------------------------------------------------------------- HRESULT D3DContext::Dp2SetRenderStates(LPD3DHAL_DP2COMMAND pCmd, LPDWORD lpdwRuntimeRStates) { WORD wStateCount = pCmd->wStateCount; INT i; HRESULT hr; D3DHAL_DP2RENDERSTATE *pRenderState = (D3DHAL_DP2RENDERSTATE *)(pCmd + 1); // Flush the prim proc before any state changs HR_RET(End(FALSE)); for (i = 0; i < (INT)wStateCount; i++, pRenderState++) { UINT32 type = (UINT32) pRenderState->RenderState; // Check for overrides if (IS_OVERRIDE(type)) { UINT32 override = GET_OVERRIDE(type); if (pRenderState->dwState) STATESET_SET(m_renderstate_override, override); else STATESET_CLEAR(m_renderstate_override, override); continue; } if (STATESET_ISSET(m_renderstate_override, type)) continue; // Set the runtime copy (if necessary) if (NULL != lpdwRuntimeRStates) { lpdwRuntimeRStates[pRenderState->RenderState] = pRenderState->dwState; } // Set the state HR_RET(SetRenderState(pRenderState->RenderState, pRenderState->dwState)); } hr = Begin(); return hr; } //---------------------------------------------------------------------------- // // Begin - Before rendering preparation // // //---------------------------------------------------------------------------- HRESULT D3DContext::Begin(void) { HRESULT hr; DDASSERT((m_uFlags & D3DCONTEXT_IN_BEGIN) == 0); // ATTENTION call this less often? UpdateColorKeyAndPalette(); // Check for state changes if (IsAnyStatesChanged()) { // Check for fillmode change if (IsStateChanged(D3DRENDERSTATE_FILLMODE)) { UpdatePrimFunctionTbl(); } BOOL bMaxMipLevelsDirty = FALSE; for (INT j = 0; j < (INT)m_RastCtx.cActTex; j++) { PD3DI_SPANTEX pSpanTex = m_RastCtx.pTexture[j]; if (pSpanTex) { bMaxMipLevelsDirty = bMaxMipLevelsDirty || (pSpanTex->uFlags & D3DI_SPANTEX_MAXMIPLEVELS_DIRTY); } } if (IsStateChanged(D3DRENDERSTATE_TEXTUREHANDLE) || IsStateChanged(D3DHAL_TSS_TEXTUREMAP0) || IsStateChanged(D3DHAL_TSS_TEXTUREMAP1) || bMaxMipLevelsDirty) { // Relock texture if texture handles have changed. // SetRenderState should have already unlocked the texture. if (m_uFlags & D3DCONTEXT_TEXTURE_LOCKED) { RastUnlockSpanTexture(); } HR_RET(RastLockSpanTexture()); } // Check for WRAP state change for (int iWrap=0; iWrap<8; iWrap++) { D3DRENDERSTATETYPE iWrapState = (D3DRENDERSTATETYPE)(D3DRENDERSTATE_WRAP0+iWrap); if (IsStateChanged(iWrapState)) { int i; for (i=0; i < 2; i++) { if (m_fvfData.TexIdx[i] == iWrap) { m_RastCtx.pdwWrap[i] = m_RastCtx.pdwRenderState[iWrapState]; } } } } // Notify primitive Processor of state change. m_PrimProc.StateChanged(); // Clear state dirtybits ClearAllStatesDirtyBits(); // Must call SpanInit AFTER texture is locked, since this // sets various flags and fields that are needed for bead choosing // Call SpanInit to setup the beads HR_RET(SpanInit(&m_RastCtx)); } // If texture is not locked yet, lock it if (!(m_uFlags & D3DCONTEXT_TEXTURE_LOCKED)) { HR_RET(RastLockSpanTexture()); } // Lock rendering target. if ((hr=LockSurface(m_RastCtx.pDDS, (LPVOID *)&(m_RastCtx.pSurfaceBits))) != D3D_OK) { RastUnlockSpanTexture(); return hr; } if (m_RastCtx.pDDSZ != NULL) { if ((hr=LockSurface(m_RastCtx.pDDSZ, (LPVOID *)&(m_RastCtx.pZBits))) != D3D_OK) { RastUnlockSpanTexture(); UnlockSurface(m_RastCtx.pDDS); return hr; } } else { m_RastCtx.pZBits = NULL; } // Prepare the primitive processor m_PrimProc.Begin(); m_uFlags |= D3DCONTEXT_IN_BEGIN; if (m_RastCtx.pRampDrv) { pTexRampmapSave = m_RastCtx.pTexRampMap; } return D3D_OK; } //----------------------------------------------------------------------------- // // MapTextureStage0/1State - Maps state0/stage1 texture state to spantex object // //----------------------------------------------------------------------------- void D3DContext::MapTextureStage0State( void ) { if (m_RastCtx.pTexture[0] == NULL) return; // // assign texture state from stage 0 // m_RastCtx.pTexture[0]->TexAddrU = (D3DTEXTUREADDRESS)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSU)]); m_RastCtx.pTexture[0]->TexAddrV = (D3DTEXTUREADDRESS)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ADDRESSV)]); m_RastCtx.pTexture[0]->BorderColor = (D3DCOLOR)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_BORDERCOLOR)]); m_RastCtx.pTexture[0]->uMagFilter = (D3DTEXTUREMAGFILTER)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MAGFILTER)]); m_RastCtx.pTexture[0]->uMinFilter = (D3DTEXTUREMINFILTER)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)]); m_RastCtx.pTexture[0]->uMipFilter = (D3DTEXTUREMIPFILTER)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)]); m_RastCtx.pTexture[0]->cLOD = (D3DTFP_NONE == m_RastCtx.pTexture[0]->uMipFilter) ? 0 : m_RastCtx.pTexture[0]->cLODTex; { m_RastCtx.pTexture[0]->fLODBias = m_RastCtx.pfRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPMAPLODBIAS)]; } if (m_RastCtx.pTexture[0]->iMaxMipLevel != (INT32)m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MAXMIPLEVEL)]) { m_RastCtx.pTexture[0]->iMaxMipLevel = (INT32)m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MAXMIPLEVEL)]; m_RastCtx.pTexture[0]->uFlags |= D3DI_SPANTEX_MAXMIPLEVELS_DIRTY; } } void D3DContext::MapTextureStage1State( void ) { if (m_RastCtx.pTexture[1] == NULL) return; // // assign texture state from stage 0 // m_RastCtx.pTexture[1]->TexAddrU = (D3DTEXTUREADDRESS)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESSU)]); m_RastCtx.pTexture[1]->TexAddrV = (D3DTEXTUREADDRESS)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ADDRESSV)]); m_RastCtx.pTexture[1]->BorderColor = (D3DCOLOR)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_BORDERCOLOR)]); m_RastCtx.pTexture[1]->uMagFilter = (D3DTEXTUREMAGFILTER)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_MAGFILTER)]); m_RastCtx.pTexture[1]->uMinFilter = (D3DTEXTUREMINFILTER)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_MINFILTER)]); m_RastCtx.pTexture[1]->uMipFilter = (D3DTEXTUREMIPFILTER)(m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_MIPFILTER)]); m_RastCtx.pTexture[1]->cLOD = (D3DTFP_NONE == m_RastCtx.pTexture[1]->uMipFilter) ? 0 : m_RastCtx.pTexture[1]->cLODTex; { m_RastCtx.pTexture[1]->fLODBias = m_RastCtx.pfRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_MIPMAPLODBIAS)]; } if (m_RastCtx.pTexture[1]->iMaxMipLevel != (INT32)m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_MAXMIPLEVEL)]) { m_RastCtx.pTexture[1]->iMaxMipLevel = (INT32)m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_MAXMIPLEVEL)]; m_RastCtx.pTexture[1]->uFlags |= D3DI_SPANTEX_MAXMIPLEVELS_DIRTY; } } //----------------------------------------------------------------------------- // // MapLegacyTextureFilter - // //----------------------------------------------------------------------------- void D3DContext::MapLegacyTextureFilter( void ) { // D3D legacy filter specifications are (XXXMIP)YYY where XXX is the // mip filter and YYY is the filter used within an LOD // map MAG filter - legacy support is point or linear (and maybe aniso) switch ( m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREMAG] ) { default: case D3DFILTER_NEAREST: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MAGFILTER)] = D3DTFG_POINT; break; case D3DFILTER_LINEAR: // select based on aniso enable m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MAGFILTER)] = D3DTFG_LINEAR; break; } // map MIN and MIP filter at the same time - legacy support // has them intermingled... switch ( m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREMIN] ) { case D3DFILTER_NEAREST: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)] = D3DTFN_POINT; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)] = D3DTFP_NONE; break; case D3DFILTER_MIPNEAREST: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)] = D3DTFN_POINT; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)] = D3DTFP_POINT; break; case D3DFILTER_LINEARMIPNEAREST: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)] = D3DTFN_POINT; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)] = D3DTFP_LINEAR; break; case D3DFILTER_LINEAR: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)] = D3DTFN_LINEAR; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)] = D3DTFP_NONE; break; case D3DFILTER_MIPLINEAR: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)] = D3DTFN_LINEAR; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)] = D3DTFP_POINT; break; case D3DFILTER_LINEARMIPLINEAR: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MINFILTER)] = D3DTFN_LINEAR; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_MIPFILTER)] = D3DTFP_LINEAR; break; } } //----------------------------------------------------------------------------- // // MapLegacyTextureBlend - Maps legacy (pre-DX6) texture blend modes to DX6 // texture blending controls. Uses per-stage program mode (first stage only). // This mapping is done whenever the legacy TBLEND renderstate is set, and // does overwrite any previously set DX6 texture blending controls. // //----------------------------------------------------------------------------- void D3DContext::MapLegacyTextureBlend( void ) { // disable texture blend processing stage 1 (this also disables subsequent stages) m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP)] = D3DTOP_DISABLE; // set texture blend processing stage 0 to match legacy mode switch ( m_RastCtx.pdwRenderState[D3DRENDERSTATE_TEXTUREMAPBLEND] ) { default: case D3DTBLEND_DECALMASK: // unsupported - do decal case D3DTBLEND_DECAL: case D3DTBLEND_COPY: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] = D3DTOP_SELECTARG1; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] = D3DTOP_SELECTARG1; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)] = D3DTA_TEXTURE; break; case D3DTBLEND_MODULATEMASK: // unsupported - do modulate case D3DTBLEND_MODULATE: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] = D3DTOP_MODULATE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)] = D3DTA_DIFFUSE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] = D3DTOP_SELECTARG1; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)] = D3DTA_DIFFUSE; break; case D3DTBLEND_MODULATEALPHA: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] = D3DTOP_MODULATE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)] = D3DTA_DIFFUSE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] = D3DTOP_MODULATE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)] = D3DTA_DIFFUSE; break; case D3DTBLEND_DECALALPHA: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] = D3DTOP_BLENDTEXTUREALPHA; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)] = D3DTA_DIFFUSE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] = D3DTOP_SELECTARG2; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)] = D3DTA_DIFFUSE; break; case D3DTBLEND_ADD: m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] = D3DTOP_ADD; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)] = D3DTA_DIFFUSE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] = D3DTOP_SELECTARG2; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)] = D3DTA_TEXTURE; m_RastCtx.pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)] = D3DTA_DIFFUSE; break; } // since we change [D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP), we can go from DISABLE to // something else, and we can need to update the TexStageCount UpdateActiveTexStageCount(); }