/******************************Module*Header**********************************\ * * ******************* * * SAMPLE CODE * * ******************* * * Module Name: p2ctxt.c * * Content: Context switching for Permedia 2. Used to create and swap * contexts in and out. * The GDI, DDraw and D3D part each have another context. * * Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved. * Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved. \*****************************************************************************/ #include "precomp.h" #include "p2ctxt.h" #include "gdi.h" #define ALLOC_TAG ALLOC_TAG_XC2P static DWORD readableRegistersP2[] = { __Permedia2TagStartXDom, __Permedia2TagdXDom, __Permedia2TagStartXSub, __Permedia2TagdXSub, __Permedia2TagStartY, __Permedia2TagdY, __Permedia2TagCount, __Permedia2TagRasterizerMode, __Permedia2TagYLimits, __Permedia2TagXLimits, __Permedia2TagScissorMode, __Permedia2TagScissorMinXY, __Permedia2TagScissorMaxXY, __Permedia2TagScreenSize, __Permedia2TagAreaStippleMode, __Permedia2TagWindowOrigin, __Permedia2TagAreaStipplePattern0, __Permedia2TagAreaStipplePattern1, __Permedia2TagAreaStipplePattern2, __Permedia2TagAreaStipplePattern3, __Permedia2TagAreaStipplePattern4, __Permedia2TagAreaStipplePattern5, __Permedia2TagAreaStipplePattern6, __Permedia2TagAreaStipplePattern7, __Permedia2TagTextureAddressMode, __Permedia2TagSStart, __Permedia2TagdSdx, __Permedia2TagdSdyDom, __Permedia2TagTStart, __Permedia2TagdTdx, __Permedia2TagdTdyDom, __Permedia2TagQStart, __Permedia2TagdQdx, __Permedia2TagdQdyDom, // texellutindex..transfer are treated seperately __Permedia2TagTextureBaseAddress, __Permedia2TagTextureMapFormat, __Permedia2TagTextureDataFormat, __Permedia2TagTexel0, __Permedia2TagTextureReadMode, __Permedia2TagTexelLUTMode, __Permedia2TagTextureColorMode, __Permedia2TagFogMode, __Permedia2TagFogColor, __Permedia2TagFStart, __Permedia2TagdFdx, __Permedia2TagdFdyDom, __Permedia2TagKsStart, __Permedia2TagdKsdx, __Permedia2TagdKsdyDom, __Permedia2TagKdStart, __Permedia2TagdKddx, __Permedia2TagdKddyDom, __Permedia2TagRStart, __Permedia2TagdRdx, __Permedia2TagdRdyDom, __Permedia2TagGStart, __Permedia2TagdGdx, __Permedia2TagdGdyDom, __Permedia2TagBStart, __Permedia2TagdBdx, __Permedia2TagdBdyDom, __Permedia2TagAStart, __Permedia2TagColorDDAMode, __Permedia2TagConstantColor, __Permedia2TagAlphaBlendMode, __Permedia2TagDitherMode, __Permedia2TagFBSoftwareWriteMask, __Permedia2TagLogicalOpMode, __Permedia2TagLBReadMode, __Permedia2TagLBReadFormat, __Permedia2TagLBSourceOffset, __Permedia2TagLBWindowBase, __Permedia2TagLBWriteMode, __Permedia2TagLBWriteFormat, __Permedia2TagTextureDownloadOffset, __Permedia2TagWindow, __Permedia2TagStencilMode, __Permedia2TagStencilData, __Permedia2TagStencil, __Permedia2TagDepthMode, __Permedia2TagDepth, __Permedia2TagZStartU, __Permedia2TagZStartL, __Permedia2TagdZdxU, __Permedia2TagdZdxL, __Permedia2TagdZdyDomU, __Permedia2TagdZdyDomL, __Permedia2TagFBReadMode, __Permedia2TagFBSourceOffset, __Permedia2TagFBPixelOffset, __Permedia2TagFBWindowBase, __Permedia2TagFBWriteMode, __Permedia2TagFBHardwareWriteMask, __Permedia2TagFBBlockColor, __Permedia2TagFBReadPixel, __Permedia2TagFilterMode, __Permedia2TagStatisticMode, __Permedia2TagMinRegion, __Permedia2TagMaxRegion, __Permedia2TagFBBlockColorU, __Permedia2TagFBBlockColorL, __Permedia2TagFBSourceBase, __Permedia2TagTexelLUT0, __Permedia2TagTexelLUT1, __Permedia2TagTexelLUT2, __Permedia2TagTexelLUT3, __Permedia2TagTexelLUT4, __Permedia2TagTexelLUT5, __Permedia2TagTexelLUT6, __Permedia2TagTexelLUT7, __Permedia2TagTexelLUT8, __Permedia2TagTexelLUT9, __Permedia2TagTexelLUT10, __Permedia2TagTexelLUT11, __Permedia2TagTexelLUT12, __Permedia2TagTexelLUT13, __Permedia2TagTexelLUT14, __Permedia2TagTexelLUT15, __Permedia2TagYUVMode, __Permedia2TagChromaUpperBound, __Permedia2TagChromaLowerBound, __Permedia2TagAlphaMapUpperBound, __Permedia2TagAlphaMapLowerBound, // delta tag values. must be at the end of this array // v0/1/2 fixed are not used and for that reason not in the context __Permedia2TagV0FloatS, __Permedia2TagV0FloatT, __Permedia2TagV0FloatQ, __Permedia2TagV0FloatKs, __Permedia2TagV0FloatKd, __Permedia2TagV0FloatR, __Permedia2TagV0FloatG, __Permedia2TagV0FloatB, __Permedia2TagV0FloatA, __Permedia2TagV0FloatF, __Permedia2TagV0FloatX, __Permedia2TagV0FloatY, __Permedia2TagV0FloatZ, __Permedia2TagV1FloatS, __Permedia2TagV1FloatT, __Permedia2TagV1FloatQ, __Permedia2TagV1FloatKs, __Permedia2TagV1FloatKd, __Permedia2TagV1FloatR, __Permedia2TagV1FloatG, __Permedia2TagV1FloatB, __Permedia2TagV1FloatA, __Permedia2TagV1FloatF, __Permedia2TagV1FloatX, __Permedia2TagV1FloatY, __Permedia2TagV1FloatZ, __Permedia2TagV2FloatS, __Permedia2TagV2FloatT, __Permedia2TagV2FloatQ, __Permedia2TagV2FloatKs, __Permedia2TagV2FloatKd, __Permedia2TagV2FloatR, __Permedia2TagV2FloatG, __Permedia2TagV2FloatB, __Permedia2TagV2FloatA, __Permedia2TagV2FloatF, __Permedia2TagV2FloatX, __Permedia2TagV2FloatY, __Permedia2TagV2FloatZ, __Permedia2TagDeltaMode }; #define N_READABLE_TAGSP2 (sizeof(readableRegistersP2) / sizeof(readableRegistersP2[0])) //----------------------------------------------------------------------------- // // P2AllocateNewContext: // // allocate a new context. If all registers are to be saved in the context then // pTag is passed as null. // ppdev--------ppdev // pTag---------user can supply list of registers to save and restore on // context switch. NULL defaults to all registers. // Holds pointer to user function if dwCtxtType==P2CtxtUserFunc // lTags--------number of tags in user supplied register list // dwCtxtType---P2CtxtReadWrite (default) // on a context switch, all Permedia 2 registers are // saved and restored. // P2CtxtWriteOnly // registers of context will be saved once at the first // context switch. After that they will always be restored // to the state ate the very beginning. This method avoids // readback of registers when switching away from context. // P2CtxtUserFunc // User can supply a user function to set context to a known // state, to avoid readback when switching away from context. // //----------------------------------------------------------------------------- P2CtxtPtr P2AllocateNewContext(PPDev ppdev, DWORD *pTag, LONG lTags, P2CtxtType dwCtxtType ) { P2CtxtTablePtr pCtxtTable, pNewCtxtTable; P2CtxtPtr pEntry; P2CtxtData *pData; LONG lEntries; LONG lExtraSize; LONG lSize; LONG lCtxtId; PERMEDIA_DECL; PERMEDIA_DEFS(ppdev); // first time round allocate the context table of pointers. We will // grow this table as required. // if (permediaInfo->ContextTable == NULL) { DISPDBG((7, "creating context table")); lSize = sizeof(P2CtxtTableRec); pCtxtTable = (P2CtxtTableRec *) ENGALLOCMEM( FL_ZERO_MEMORY, sizeof(P2CtxtTableRec), ALLOC_TAG); if (pCtxtTable == NULL) { DISPDBG((0, "failed to allocate Permedia2 context table. out of memory")); return(NULL); } pCtxtTable->lEntries = CTXT_CHUNK; pCtxtTable->lSize = lSize; permediaInfo->ContextTable = pCtxtTable; permediaInfo->pCurrentCtxt = NULL; } // find an empty entry in the table // I suppose if we have hundreds of contexts this could be a bit slow but then // allocating the context isn't time critical, swapping in and out is. // pCtxtTable = (P2CtxtTablePtr) permediaInfo->ContextTable; lEntries = pCtxtTable->lEntries; for (lCtxtId = 0; lCtxtId < lEntries; ++lCtxtId) if(pCtxtTable->pEntry[lCtxtId] == NULL) break; // if we found no free entries try to grow the table if (lCtxtId == lEntries) { DISPDBG((1, "context table full so enlarging")); lSize = pCtxtTable->lSize + (CTXT_CHUNK * sizeof(P2CtxtPtr)); pNewCtxtTable = (P2CtxtTablePtr) ENGALLOCMEM( FL_ZERO_MEMORY, sizeof(BYTE)*lSize, ALLOC_TAG); if (pNewCtxtTable == NULL) { DISPDBG((0, "failed to increase Permedia 2 context table. out of memory")); return(NULL); } // copy the old table to the new one RtlCopyMemory(pNewCtxtTable, pCtxtTable, pCtxtTable->lSize); pNewCtxtTable->lSize = lSize; pNewCtxtTable->lEntries = lEntries + CTXT_CHUNK; permediaInfo->ContextTable = (PVOID)pNewCtxtTable; // first of the newly allocated entries is next free one lCtxtId = lEntries; // free the old context table and reassign some variables ENGFREEMEM(pCtxtTable); pCtxtTable = pNewCtxtTable; lEntries = pCtxtTable->lEntries; } // if pTag is passed as null then we are to add all readable registers to the // context. lExtraSize = 0; if (dwCtxtType != P2CtxtUserFunc) { if (pTag == 0) { DISPDBG((7, "adding all readable registers to the context")); DISPDBG((7, "Using PERMEDIA 2 register set for other context switch")); pTag = readableRegistersP2; lTags = N_READABLE_TAGSP2; } } else { lTags = 1; } // now allocate space for the new entry. We are given the number of tags to save // when context switching. Allocate twice this much memory as we have to hold the // data values as well. DISPDBG((7, "Allocating space for context. lTags = %d", lTags)); lSize = sizeof(P2CtxtRec) + (lTags-1) * sizeof(P2CtxtData); pEntry = (P2CtxtPtr) ENGALLOCMEM( FL_ZERO_MEMORY, sizeof(BYTE)*(lSize+lExtraSize), ALLOC_TAG); if (pEntry == NULL) { DISPDBG((0, "out of memory trying to allocate space for new context")); return(NULL); } DISPDBG((7, "Got pEntry 0x%x", pEntry)); pCtxtTable->pEntry[lCtxtId] = pEntry; // allocate enough space for the Texel LUT: 256 entries pEntry->dwCtxtType=dwCtxtType; pEntry->bInitialized=FALSE; pEntry->pTexelLUTCtxt = (PULONG) ENGALLOCMEM( FL_ZERO_MEMORY, sizeof(ULONG)*256, ALLOC_TAG); if (pEntry->pTexelLUTCtxt!=0) { pEntry->ulTexelLUTEntries = 256; } else { pEntry->ulTexelLUTEntries = 0; } pEntry->lNumOfTags = lTags; pEntry->P2UserFunc = NULL; pData = pEntry->pData; if (dwCtxtType != P2CtxtUserFunc) { // we must initialize the new context to something reasonable. We choose to // initialize to the current state of the chip. We can't leave it uninitialized // since the first thing the caller will do when he wants to draw is validate // the new context which will load junk into the chip. At some point we // should define a reasonable starting context which would mean we wouldn't // have to do this readback. // copy the tags and read the data back from the chip. We don't sync since we are // only initialising the context to something reasonable. i.e. we don't care if // the FIFO is still draining while we do this. DISPDBG((7, "Reading current chip context back")); while (--lTags >= 0) { pData->dwTag = *pTag++; READ_PERMEDIA_FIFO_REG(pData->dwTag, pData->dwData); ++pData; } // save the texel LUT if(pEntry->ulTexelLUTEntries && pEntry->pTexelLUTCtxt!=NULL) { ULONG *pul; INT i=0; lEntries = pEntry->ulTexelLUTEntries; pul = pEntry->pTexelLUTCtxt; //special mechanism: reset readback index to 0 READ_PERMEDIA_FIFO_REG(__Permedia2TagTexelLUTIndex, i); for(i = 0; i < lEntries; ++i, ++pul) { READ_PERMEDIA_FIFO_REG(__Permedia2TagTexelLUTData, *pul); } } } else { pEntry->P2UserFunc = (PCtxtUserFunc) pTag; } DISPDBG((1, "Allocated context %lx", pEntry)); return(pEntry); } // P2AllocateNewContext //----------------------------------------------------------------------------- // // P2FreeContext: // // free a previously allocated context. // //----------------------------------------------------------------------------- VOID P2FreeContext( PPDev ppdev, P2CtxtPtr pEntry) { PERMEDIA_DECL; P2CtxtTablePtr pCtxtTable; ULONG lCtxtId; pCtxtTable = (P2CtxtTablePtr) permediaInfo->ContextTable; for (lCtxtId = 0; lCtxtId < pCtxtTable->lEntries; ++lCtxtId) if(pCtxtTable->pEntry[lCtxtId] == pEntry) break; ASSERTDD(lCtxtId != pCtxtTable->lEntries, "P2FreeContext: context not found"); // free LUT Table if(pEntry->pTexelLUTCtxt) { ENGFREEMEM( pEntry->pTexelLUTCtxt); } ENGFREEMEM( pEntry); pCtxtTable->pEntry[lCtxtId] = NULL; // if this was the current context, mark the current context as invalid so we // force a reload next time. if (permediaInfo->pCurrentCtxt == pEntry) { permediaInfo->pCurrentCtxt = NULL; } DISPDBG((1, "Released context %lx", pEntry)); } //----------------------------------------------------------------------------- // // VOID P2SwitchContext: // // load a new context into the hardware. We assume that this call is // protected by a test that the given context is not the current one - // hence the assertion. // The code would work but the driver should never try to load an already // loaded context so we trap it as an error. // //----------------------------------------------------------------------------- VOID P2SwitchContext( PPDev ppdev, P2CtxtPtr pEntry) { P2CtxtTablePtr pCtxtTable; P2CtxtData *pData; P2CtxtPtr pOldCtxt; LONG lTags; LONG i; ULONG *pul; LONG lEntries; PERMEDIA_DECL; PERMEDIA_DEFS(ppdev); //@@BEGIN_DDKSPLIT #if MULTITHREADED EngAcquireSemaphore(ppdev->hsemLock); ASSERTDD(ppdev->ulLockCount, "P2SwitchContext: ulLockCount = 0\n Context could change as caller is NOT protected!"); ppdev->ulLockCount++; #endif //@@END_DDKSPLIT pCtxtTable = (P2CtxtTablePtr)permediaInfo->ContextTable; ASSERTDD(pCtxtTable, "Can't perform context switch: no contexts have been created!"); pOldCtxt = permediaInfo->pCurrentCtxt; DISPDBG((3, "swapping from context %d to context %d", pOldCtxt, pEntry)); if(pOldCtxt == permediaInfo->pGDICtxt) { DISPDBG((6, "Switching from GDI context")); ASSERTDD(ppdev->bNeedSync || (ppdev->pulInFifoPtr == ppdev->pulInFifoStart), "P2SwitchContext: bNeedSync flag is wrong"); InputBufferSync(ppdev); ppdev->bGdiContext = FALSE; ppdev->pP2dma->bEnabled = TRUE; } // for each register in the old context, read it back if (pOldCtxt != NULL) { // // P2CtxtWriteOnly will only be readback once after context initialization // if ((pOldCtxt->dwCtxtType==P2CtxtReadWrite) || (pOldCtxt->dwCtxtType==P2CtxtWriteOnly && !pOldCtxt->bInitialized) ) { // sync with the chip before reading back the current state. The flag // is used to control context manipulation on lockup recovery. SYNC_WITH_PERMEDIA; pData = pOldCtxt->pData; lTags = pOldCtxt->lNumOfTags; while (--lTags >= 0) { READ_PERMEDIA_FIFO_REG(pData->dwTag, pData->dwData); ++pData; } // save the texel LUT if(pOldCtxt->ulTexelLUTEntries && pOldCtxt->pTexelLUTCtxt!=NULL) { lEntries = pOldCtxt->ulTexelLUTEntries; pul = pOldCtxt->pTexelLUTCtxt; //special mechanism: reset readback index to 0 READ_PERMEDIA_FIFO_REG(__Permedia2TagTexelLUTIndex, i); for(i = 0; i < lEntries; ++i, ++pul) { READ_PERMEDIA_FIFO_REG(__Permedia2TagTexelLUTData, *pul); } } pOldCtxt->bInitialized=TRUE; } } // load the new context. We allow -1 to be passed so that we can force a // save of the current context and force the current context to be // undefined. // if (pEntry != NULL) { if (pEntry->dwCtxtType==P2CtxtUserFunc) { ASSERTDD(pEntry->P2UserFunc!=NULL,"supplied user function not initialized"); (*pEntry->P2UserFunc)(ppdev); } else if (pEntry->dwCtxtType==P2CtxtWriteOnly || pEntry->dwCtxtType==P2CtxtReadWrite) { lTags = pEntry->lNumOfTags; pData = pEntry->pData; while (lTags > 0) { lEntries = MAX_P2_FIFO_ENTRIES; lTags -= lEntries; if (lTags < 0) lEntries += lTags; RESERVEDMAPTR(lEntries); while (--lEntries >= 0) { LD_INPUT_FIFO(pData->dwTag, pData->dwData); DISPDBG((20, "loading tag 0x%x, data 0x%x", pData->dwTag, pData->dwData)); ++pData; } COMMITDMAPTR(); } // restore the texel LUT if(pEntry->ulTexelLUTEntries && pEntry->pTexelLUTCtxt!=NULL ) { lEntries = pEntry->ulTexelLUTEntries; pul = pEntry->pTexelLUTCtxt; RESERVEDMAPTR(lEntries+1); LD_INPUT_FIFO(__Permedia2TagTexelLUTIndex, 0); for(i = 0; i < lEntries; ++i, ++pul) { LD_INPUT_FIFO(__Permedia2TagTexelLUTData, *pul); } COMMITDMAPTR(); FLUSHDMA(); } } else { ASSERTDD( FALSE, "undefined state for entry in context table"); } } if(pEntry == permediaInfo->pGDICtxt) { DISPDBG((6, "Switching to GDI context")); // // we have to do a full sync here, because GDI and DDraw // share the same DMA buffer. To make sure nothing will be // overridden, we do a complete sync // SYNC_WITH_PERMEDIA; // // Turn off permedia interrupt handler // WRITE_CTRL_REG( PREG_INTENABLE, 0); ppdev->bGdiContext = TRUE; ppdev->pP2dma->bEnabled = FALSE; // invalidate the mono brush cache entry // stipple unit was restored to default values ppdev->abeMono.prbVerify = NULL; } DISPDBG((6, "context %lx now current", pEntry)); permediaInfo->pCurrentCtxt = pEntry; //@@BEGIN_DDKSPLIT #if MULTITHREADED ppdev->ulLockCount--; EngReleaseSemaphore(ppdev->hsemLock); #endif //@@END_DDKSPLIT }