|
|
/******************************Module*Header**********************************\
* * ******************* * * D3D SAMPLE CODE * * ******************* * * Module Name: d3dtxman.c * * Content: D3D Texture cache manager * * Copyright (c) 1995-2003 Microsoft Corporation. All rights Reserved. \*****************************************************************************/ #include "glint.h"
#include "dma.h"
#if DX7_TEXMANAGEMENT
//-----------------------------------------------------------------------------
// The driver can optionally manage textures which have been marked as
// manageable. These DirectDraw surfaces are marked as manageable with the
// DDSCAPS2_TEXTUREMANAGE flag in the dwCaps2 field of the structure refered to
// by lpSurfMore->ddCapsEx.
//
// The driver makes explicit its support for driver-managed textures by setting
// DDCAPS2_CANMANAGETEXTURE in the dwCaps2 field of the DD_HALINFO structure.
// DD_HALINFO is returned in response to the initialization of the DirectDraw
// component of the driver, DrvGetDirectDrawInfo.
//
// The driver can then create the necessary surfaces in video or non-local
// memory in a lazy fashion. That is, the driver leaves them in system memory
// until it requires them, which is just before rasterizing a primitive that
// makes use of the texture.
//
// Surfaces should be evicted primarily by their priority assignment. The driver
// should respond to the D3DDP2OP_SETPRIORITY token in the D3dDrawPrimitives2
// command stream, which sets the priority for a given surface. As a secondary
// measure, it is expected that the driver will use a least recently used (LRU)
// scheme. This scheme should be used as a tie breaker, whenever the priority of
// two or more textures is identical in a particular scenario. Logically, any
// surface that is in use shouldn't be evicted at all.
//
// The driver must be cautious of honoring DdBlt calls and DdLock calls when
// managing textures. This is because any change to the system memory image of
// the surface must be propagated into the video memory copy of it before the
// texture is used again. The driver should determine if it is better to update
// just a portion of the surface or all of it.
//
// The driver is allowed to perform texture management in order to perform
// optimization transformations on the textures or to decide for itself where
// and when to transfer textures in memory.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
// Porting to your hardware/driver
//
// The following structures/functions/symbols are specific to this
// implementation. You should supply your own if needed:
//
// P3_SURF_INTERNAL
// P3_D3DCONTEXT
// DISPDBG
// HEAP_ALLOC ALLOC_TAG_DX
// _D3D_TM_HW_FreeVidmemSurface
// _D3D_TM_HW_AllocVidmemSurface
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Global texture management object and ref count
//-----------------------------------------------------------------------------
DWORD g_TMCount = 0; TextureCacheManager g_TextureManager;
//-----------------------------------------------------------------------------
// Macros and definitions
//-----------------------------------------------------------------------------
// Number of pointers to DX surfaces for first allocated heap
#define TM_INIT_HEAP_SIZE 1024
// Subsequent increments
#define TM_GROW_HEAP_SIZE(n) ((n)*2)
// Access to binary-tree structure in the heap. The heap is really just a
// linear array of pointers (to P3_SURF_INTERNAL structures) which is
// accessed as if it were a binary tree: m_data_p[1] is the root of the tree
// and the its immediate children are [2] and [3]. The children/parents of
// element are uniquely defined by the below macros.
#define parent(k) ((k) / 2)
#define lchild(k) ((k) * 2)
#define rchild(k) ((k) * 2 + 1)
//-----------------------------------------------------------------------------
//
// void __TM_TextureHeapFindSlot
//
// Starting at element k in the TM heap, search the heap (towards the root node)
// for an element whose parent is cheaper than llCost. Return the value of it.
//
// An important difference between __TM_TextureHeapFindSlot and
// __TM_TextureHeapHeapify is that the former searches upwards in the tree
// whereas the latter searches downwards through the tree.
//
//-----------------------------------------------------------------------------
DWORD __TM_TextureHeapFindSlot( PTextureHeap pTextureHeap, ULONGLONG llCost, DWORD k) { // Starting with element k, traverse the (binary-tree) heap upwards until
// you find an element whose parent is less expensive (cost-wise)
// than llCost . (Short circuited && expression below!)
while( (k > 1) && (llCost < TextureCost(pTextureHeap->m_data_p[parent(k)])) ) { // Slot k is assumed to be empty. So since we are looking for
// slot where to put things, we need to move downwards the
// parent slot before we go on in order for the new k to be
// available
pTextureHeap->m_data_p[k] = pTextureHeap->m_data_p[parent(k)]; pTextureHeap->m_data_p[k]->m_dwHeapIndex = k; // update surf reference
k = parent(k); // look now at parent
}
return k; } // __TM_TextureHeapFindSlot
//-----------------------------------------------------------------------------
//
// void __TM_TextureHeapHeapify
//
// Starting at element k in the TM heap, make sure the heap is well-ordered
// (parents are always lower cost than their children). This algorithm assumes
// the heap is well ordered with the possible exception of element k
//
//-----------------------------------------------------------------------------
void __TM_TextureHeapHeapify( PTextureHeap pTextureHeap, DWORD k) { while(TRUE) { DWORD smallest; DWORD l = lchild(k); DWORD r = rchild(k);
// Figure out who has the smallest TextureCost, either k,l or r.
if(l < pTextureHeap->m_next) { if(TextureCost(pTextureHeap->m_data_p[l]) < TextureCost(pTextureHeap->m_data_p[k])) { smallest = l; } else { smallest = k; } } else { smallest = k; } if(r < pTextureHeap->m_next) { if(TextureCost(pTextureHeap->m_data_p[r]) < TextureCost(pTextureHeap->m_data_p[smallest])) { smallest = r; } } if(smallest != k) { // it wasn't k. So now swap k with the smallest of the three
// and repeat the loop in order with the new position of k
// in order to keep the order right (parents are always lower
// cost than children)
P3_SURF_INTERNAL* ptempD3DSurf = pTextureHeap->m_data_p[k]; pTextureHeap->m_data_p[k] = pTextureHeap->m_data_p[smallest]; pTextureHeap->m_data_p[k]->m_dwHeapIndex = k; pTextureHeap->m_data_p[smallest] = ptempD3DSurf; ptempD3DSurf->m_dwHeapIndex = smallest; k = smallest; } else { // it was k, so the order is now all right, leave here
break; } } } // __TM_TextureHeapHeapify
//-----------------------------------------------------------------------------
//
// BOOL __TM_TextureHeapAddSurface
//
// Add a DX surface to a texture management heap.
// Returns success or failure status
//
//-----------------------------------------------------------------------------
BOOL __TM_TextureHeapAddSurface( PTextureHeap pTextureHeap, P3_SURF_INTERNAL* pD3DSurf) { P3_SURF_INTERNAL* *ppD3DSurf; ULONGLONG llCost; DWORD k;
if(pTextureHeap->m_next == pTextureHeap->m_size) { // Heap is full, we must grow it
DWORD dwNewSize = TM_GROW_HEAP_SIZE(pTextureHeap->m_size);
ppD3DSurf = (P3_SURF_INTERNAL* *) HEAP_ALLOC( FL_ZERO_MEMORY, sizeof(P3_SURF_INTERNAL*) * dwNewSize, ALLOC_TAG_DX(B)); if(ppD3DSurf == 0) { DISPDBG((ERRLVL,"Failed to allocate memory to grow heap.")); return FALSE; }
// Transfer data
memcpy(ppD3DSurf + 1, pTextureHeap->m_data_p + 1, sizeof(P3_SURF_INTERNAL*) * (pTextureHeap->m_next - 1));
// Free previous heap
HEAP_FREE( pTextureHeap->m_data_p); // Update texture heap structure
pTextureHeap->m_size = dwNewSize; pTextureHeap->m_data_p = ppD3DSurf; }
// Get the cost of the surface we are about to add
llCost = TextureCost(pD3DSurf);
// Starting at the last element in the heap (where we can theoretically
// place our new element) search upwards for an appropriate place to put
// it in. This will also maintain our tree/heap balanced
k = __TM_TextureHeapFindSlot(pTextureHeap, llCost, pTextureHeap->m_next);
// Add the new surface to the heap in the [k] place
pTextureHeap->m_data_p[k] = pD3DSurf; ++pTextureHeap->m_next;
//Update the surface's reference to its place on the heap
pD3DSurf->m_dwHeapIndex = k; return TRUE; } // __TM_TextureHeapAddSurface
//-----------------------------------------------------------------------------
//
// void __TM_TextureHeapDelSurface
//
// Delete the k element from the TM heap
//
//-----------------------------------------------------------------------------
void __TM_TextureHeapDelSurface(PTextureHeap pTextureHeap, DWORD k) { P3_SURF_INTERNAL* pD3DSurf = pTextureHeap->m_data_p[k]; ULONGLONG llCost;
// (virtually) delete the heaps last element and get its cost
--pTextureHeap->m_next; llCost = TextureCost(pTextureHeap->m_data_p[pTextureHeap->m_next]); if(llCost < TextureCost(pD3DSurf)) { // If the cost of the last element is less than that of the surface
// we are really trying to delete (k), look for a new place where
// to put the m_next element based on its cost.
// Starting at the k element in the heap (where we can theoretically
// place our new element) search upwards for an appropriate place to
// put it in
k = __TM_TextureHeapFindSlot(pTextureHeap, llCost, k);
// Overwrite the data of k with the data of the last element
pTextureHeap->m_data_p[k] = pTextureHeap->m_data_p[pTextureHeap->m_next]; pTextureHeap->m_data_p[k]->m_dwHeapIndex = k; } else { // If the cost of the last element is greather than that of the surface
// we are really trying to delete (k), replace (k) by (m_next)
pTextureHeap->m_data_p[k] = pTextureHeap->m_data_p[pTextureHeap->m_next]; pTextureHeap->m_data_p[k]->m_dwHeapIndex = k;
// Now make sure we keep the heap correctly ordered
__TM_TextureHeapHeapify(pTextureHeap,k); } pD3DSurf->m_dwHeapIndex = 0; } // __TM_TextureHeapDelSurface
//-----------------------------------------------------------------------------
//
// P3_SURF_INTERNAL* __TM_TextureHeapExtractMin
//
// Extract
//
//-----------------------------------------------------------------------------
P3_SURF_INTERNAL* __TM_TextureHeapExtractMin( PTextureHeap pTextureHeap) { // Obtaint pointer to surface we are extracting from the heap
// (the root node, which is the least expensive element of the
// whole heap because of the way we build the heap).
P3_SURF_INTERNAL* pD3DSurf = pTextureHeap->m_data_p[1];
// Update heap internal counter and move last
// element now to first position.
--pTextureHeap->m_next; pTextureHeap->m_data_p[1] = pTextureHeap->m_data_p[pTextureHeap->m_next]; pTextureHeap->m_data_p[1]->m_dwHeapIndex = 1;
// Now make sure we keep the heap correctly ordered
__TM_TextureHeapHeapify(pTextureHeap,1);
// Clear the deleted surface's reference to its place on the heap (deleted)
pD3DSurf->m_dwHeapIndex = 0; return pD3DSurf; } // __TM_TextureHeapExtractMin
//-----------------------------------------------------------------------------
//
// P3_SURF_INTERNAL* __TM_TextureHeapExtractMax
//
//-----------------------------------------------------------------------------
P3_SURF_INTERNAL* __TM_TextureHeapExtractMax( PTextureHeap pTextureHeap) { // When extracting the max element from the heap, we don't need to
// search the entire heap, but just the leafnodes. This is because
// it is guaranteed that parent nodes are cheaper than the leaf nodes
// so once you have looked through the leaves, you won't find anything
// cheaper.
// NOTE: (lchild(i) >= m_next) is true only for leaf nodes.
// ALSO NOTE: You cannot have a rchild without a lchild, so simply
// checking for lchild is sufficient.
unsigned max = pTextureHeap->m_next - 1; ULONGLONG llMaxCost = 0; ULONGLONG llCost; unsigned i; P3_SURF_INTERNAL* pD3DSurf;
// Search all the terminal nodes of the binary-tree (heap)
// for the most expensive element and store its index in max
for(i = max; lchild(i) >= pTextureHeap->m_next; --i) { llCost = TextureCost(pTextureHeap->m_data_p[i]); if(llMaxCost < llCost) { llMaxCost = llCost; max = i; } }
// Return the surface associated to this maximum cost heap element
pD3DSurf = pTextureHeap->m_data_p[max];
// Delete it from the heap ( will automatically maintain heap ordered)
__TM_TextureHeapDelSurface(pTextureHeap, max); return pD3DSurf; } // __TM_TextureHeapExtractMax
//-----------------------------------------------------------------------------
//
// void __TM_TextureHeapUpdate
//
// Updates the priority & number of of ticks of surface # k in the heap
//
//-----------------------------------------------------------------------------
void __TM_TextureHeapUpdate( PTextureHeap pTextureHeap, DWORD k, DWORD dwPriority, DWORD dwTicks) { P3_SURF_INTERNAL* pD3DSurf = pTextureHeap->m_data_p[k]; ULONGLONG llCost = 0; #ifdef _X86_
_asm { mov edx, 0; shl edx, 31; mov eax, dwPriority; mov ecx, eax; shr eax, 1; or edx, eax; mov DWORD PTR llCost + 4, edx; shl ecx, 31; mov eax, dwTicks; shr eax, 1; or eax, ecx; mov DWORD PTR llCost, eax; } #else
llCost = ((ULONGLONG)dwPriority << 31) + ((ULONGLONG)(dwTicks >> 1)); #endif
if(llCost < TextureCost(pD3DSurf)) { // Starting at the k element in the heap (where we can theoretically
// place our new element) search upwards for an appropriate place
// to move it to in order to keep the tree well ordered.
k = __TM_TextureHeapFindSlot(pTextureHeap, llCost, k); pD3DSurf->m_dwPriority = dwPriority; pD3DSurf->m_dwTicks = dwTicks; pD3DSurf->m_dwHeapIndex = k; pTextureHeap->m_data_p[k] = pD3DSurf; } else { pD3DSurf->m_dwPriority = dwPriority; pD3DSurf->m_dwTicks = dwTicks;
// Now make sure we keep the heap correctly ordered
__TM_TextureHeapHeapify(pTextureHeap,k); } }
//-----------------------------------------------------------------------------
//
// BOOL __TM_FreeTextures
//
// Free the LRU texture
//
//-----------------------------------------------------------------------------
BOOL __TM_FreeTextures( P3_D3DCONTEXT* pContext, DWORD dwBytes) { P3_SURF_INTERNAL* pD3DSurf; DWORD k;
PTextureCacheManager pTextureCacheManager = pContext->pTextureManager; P3_THUNKEDDATA* pThisDisplay = pContext->pThisDisplay; // No textures left to be freed
if(pTextureCacheManager->m_heap.m_next <= 1) return FALSE;
// Keep remove textures until we accunulate dwBytes of removed stuff
// or we have no more surfaces left to be removed.
for(k = 0; (pTextureCacheManager->m_heap.m_next > 1) && (k < dwBytes); k += pD3DSurf->m_dwBytes) { // Find the LRU texture (the one with lowest cost) and remove it.
pD3DSurf = __TM_TextureHeapExtractMin(&pTextureCacheManager->m_heap); _D3D_TM_RemoveTexture(pThisDisplay, pD3DSurf);
#if DX7_TEXMANAGEMENT_STATS
// Update stats
pTextureCacheManager->m_stats.dwLastPri = pD3DSurf->m_dwPriority; ++pTextureCacheManager->m_stats.dwNumEvicts; #endif
DISPDBG((WRNLVL, "Removed texture with timestamp %u,%u (current = %u).", pD3DSurf->m_dwPriority, pD3DSurf->m_dwTicks, pTextureCacheManager->tcm_ticks)); } return TRUE; } // __TM_FreeTextures
//-----------------------------------------------------------------------------
//
// HRESULT __TM_TextureHeapInitialize
//
// Allocate the heap and initialize
//
//-----------------------------------------------------------------------------
HRESULT __TM_TextureHeapInitialize( PTextureCacheManager pTextureCacheManager) { pTextureCacheManager->m_heap.m_next = 1; pTextureCacheManager->m_heap.m_size = TM_INIT_HEAP_SIZE; pTextureCacheManager->m_heap.m_data_p = (P3_SURF_INTERNAL* *) HEAP_ALLOC( FL_ZERO_MEMORY, sizeof(P3_SURF_INTERNAL*) * pTextureCacheManager->m_heap.m_size, ALLOC_TAG_DX(C)); if(pTextureCacheManager->m_heap.m_data_p == 0) { DISPDBG((ERRLVL,"Failed to allocate texture heap.")); return E_OUTOFMEMORY; } memset(pTextureCacheManager->m_heap.m_data_p, 0, sizeof(P3_SURF_INTERNAL*) * pTextureCacheManager->m_heap.m_size); return DD_OK; } // __TM_TextureHeapInitialize
//-----------------------------------------------------------------------------
//
// HRESULT _D3D_TM_Ctx_Initialize
//
// Initialize texture management for this context
// Should be called when the context is being created
//
//-----------------------------------------------------------------------------
HRESULT _D3D_TM_Ctx_Initialize( P3_D3DCONTEXT* pContext) {
HRESULT hr = DD_OK; if (0 == g_TMCount) { // first use - must initialize
hr = __TM_TextureHeapInitialize(&g_TextureManager);
// init ticks count for LRU policy
g_TextureManager.tcm_ticks = 0; }
if (SUCCEEDED(hr)) { // Initialization succesful or uneccesary.
// Increment the reference count and make the context
// remember where the texture management object is.
g_TMCount++; pContext->pTextureManager = &g_TextureManager; }
return hr; } // _D3D_TM_Ctx_Initialize
//-----------------------------------------------------------------------------
//
// void _D3D_TM_Ctx_Destroy
//
// Clean up texture management for this context
// Should be called when the context is being destroyed
//
//-----------------------------------------------------------------------------
void _D3D_TM_Ctx_Destroy( P3_D3DCONTEXT* pContext) { // clean up texture manager stuff if it
// is already allocated for this context
if (pContext->pTextureManager) { // Decrement reference count for the
// driver global texture manager object
g_TMCount--;
// If necessary, deallocate the texture manager heap;
if (0 == g_TMCount) { if (0 != g_TextureManager.m_heap.m_data_p) { _D3D_TM_EvictAllManagedTextures(pContext); HEAP_FREE(g_TextureManager.m_heap.m_data_p); g_TextureManager.m_heap.m_data_p = NULL; } }
pContext->pTextureManager = NULL; } } // _D3D_TM_Ctx_Destroy
//-----------------------------------------------------------------------------
//
// HRESULT _D3D_TM_AllocTexture
//
// add a new HW handle and create a surface (for a managed texture) in vidmem
//
//-----------------------------------------------------------------------------
HRESULT _D3D_TM_AllocTexture( P3_D3DCONTEXT* pContext, P3_SURF_INTERNAL* pTexture) { DWORD trycount = 0, bytecount = pTexture->m_dwBytes; PTextureCacheManager pTextureCacheManager = pContext->pTextureManager; P3_THUNKEDDATA* pThisDisplay = pContext->pThisDisplay; INT iLOD;
// Decide the largest level to allocate video memory based on what
// is specified through D3DDP2OP_SETTEXLOD token
iLOD = pTexture->m_dwTexLOD; if (iLOD > (pTexture->iMipLevels - 1)) { iLOD = pTexture->iMipLevels - 1; }
// Attempt to allocate a texture. (do until the texture is in vidmem)
while((FLATPTR)NULL == pTexture->MipLevels[iLOD].fpVidMemTM) { _D3D_TM_HW_AllocVidmemSurface(pContext, pTexture); ++trycount; DISPDBG((DBGLVL,"Got fpVidMemTM = %08lx", pTexture->MipLevels[0].fpVidMemTM));
// We were able to allocate the vidmem surface
if ((FLATPTR)NULL != pTexture->MipLevels[iLOD].fpVidMemTM) { // No problem, there is enough memory.
pTexture->m_dwTicks = pTextureCacheManager->tcm_ticks;
// Add texture to manager's heap to track it
if(!__TM_TextureHeapAddSurface(&pTextureCacheManager->m_heap, pTexture)) { // Failed - undo vidmem allocation
// This call will set all MipLevels[i].fpVidMemTM to NULL
_D3D_TM_HW_FreeVidmemSurface(pThisDisplay, pTexture); DISPDBG((ERRLVL,"Out of memory")); return DDERR_OUTOFMEMORY; }
// Mark surface as needing update from sysmem before use
pTexture->m_bTMNeedUpdate = TRUE; break; } else { // we weren't able to allocate the vidmem surface
// we will now try to free some managed surfaces to make space
if (!__TM_FreeTextures(pContext, bytecount)) { DISPDBG((ERRLVL,"all Freed no further video memory available")); return DDERR_OUTOFVIDEOMEMORY; //nothing left
} bytecount <<= 1; } }
if(trycount > 1) { DISPDBG((DBGLVL, "Allocated texture after %u tries.", trycount)); } __TM_STAT_Inc_TotSz(pTextureCacheManager, pTexture); __TM_STAT_Inc_WrkSet(pTextureCacheManager, pTexture);
#if DX7_TEXMANAGEMENT_STATS
++pTextureCacheManager->m_stats.dwNumVidCreates; #endif // DX7_TEXMANAGEMENT_STATS
return DD_OK; } // _D3D_TM_AllocTexture
//-----------------------------------------------------------------------------
//
// void _D3D_TM_RemoveTexture
//
// remove all HW handles and release the managed surface
// (usually done for every surface in vidmem when D3DDestroyDDLocal is called)
//
//-----------------------------------------------------------------------------
void _D3D_TM_RemoveTexture( P3_THUNKEDDATA *pThisDisplay, P3_SURF_INTERNAL* pTexture) { //@@BEGIN_DDKSPLIT
// azn - we should attach the g_TextureManager ptr to pThisDisplay,
// NOT to pContext !!!
//@@END_DDKSPLIT
PTextureCacheManager pTextureCacheManager = &g_TextureManager; int i; // Check if surface is currently in video memory
for (i = 0; i < pTexture->iMipLevels; i++) { if (pTexture->MipLevels[i].fpVidMemTM != (FLATPTR)NULL) { // Free (deallocate) the surface from video memory
// and mark the texture as not longer in vidmem
_D3D_TM_HW_FreeVidmemSurface(pThisDisplay, pTexture);
// Update statistics
__TM_STAT_Dec_TotSz(pTextureCacheManager, pTexture); __TM_STAT_Dec_WrkSet(pTextureCacheManager, pTexture);
// The job is done
break; } }
// Remove heap references to this surface
if (pTexture->m_dwHeapIndex && pTextureCacheManager->m_heap.m_data_p) { __TM_TextureHeapDelSurface(&pTextureCacheManager->m_heap, pTexture->m_dwHeapIndex); } } // _D3D_TM_RemoveTexture
//-----------------------------------------------------------------------------
//
// void _D3D_TM_RemoveDDSurface
//
// remove all HW handles and release the managed surface
// (usually done for every surface in vidmem when D3DDestroyDDLocal is called)
//
//-----------------------------------------------------------------------------
void _D3D_TM_RemoveDDSurface( P3_THUNKEDDATA *pThisDisplay, LPDDRAWI_DDRAWSURFACE_LCL pDDSLcl) { // We don't know which D3D context this is so we have to do a search
// through them (if there are any at all)
if (pThisDisplay->pDirectDrawLocalsHashTable != NULL) { DWORD dwSurfaceHandle = pDDSLcl->lpSurfMore->dwSurfaceHandle; PointerArray* pSurfaceArray; // Get a pointer to an array of surface pointers associated to this lpDD
// The PDD_DIRECTDRAW_LOCAL was stored at D3DCreateSurfaceEx call time
// in PDD_SURFACE_LOCAL->dwReserved1
pSurfaceArray = (PointerArray*) HT_GetEntry(pThisDisplay->pDirectDrawLocalsHashTable, pDDSLcl->dwReserved1);
if (pSurfaceArray) { // Found a surface array associated to this lpDD !
P3_SURF_INTERNAL* pSurfInternal;
// Check the surface in this array associated to this surface handle
pSurfInternal = PA_GetEntry(pSurfaceArray, dwSurfaceHandle);
if (pSurfInternal) { // Got it! remove it
_D3D_TM_RemoveTexture(pThisDisplay, pSurfInternal); } } }
} // _D3D_TM_RemoveDDSurface
//-----------------------------------------------------------------------------
//
// void _D3D_TM_EvictAllManagedTextures
//
// Remove all managed surfaces from video memory
//
//-----------------------------------------------------------------------------
void _D3D_TM_EvictAllManagedTextures( P3_D3DCONTEXT* pContext) { PTextureCacheManager pTextureCacheManager = pContext->pTextureManager; P3_THUNKEDDATA* pThisDisplay = pContext->pThisDisplay; P3_SURF_INTERNAL* pD3DSurf; while(pTextureCacheManager->m_heap.m_next > 1) { pD3DSurf = __TM_TextureHeapExtractMin(&pTextureCacheManager->m_heap); _D3D_TM_RemoveTexture(pThisDisplay, pD3DSurf); } pTextureCacheManager->tcm_ticks = 0; } // _D3D_TM_EvictAllManagedTextures
//-----------------------------------------------------------------------------
//
// void _DD_TM_EvictAllManagedTextures
//
// Remove all managed surfaces from video memory
//
//-----------------------------------------------------------------------------
void _DD_TM_EvictAllManagedTextures( P3_THUNKEDDATA* pThisDisplay) { PTextureCacheManager pTextureCacheManager = &g_TextureManager; P3_SURF_INTERNAL* pD3DSurf; while(pTextureCacheManager->m_heap.m_next > 1) { pD3DSurf = __TM_TextureHeapExtractMin(&pTextureCacheManager->m_heap); _D3D_TM_RemoveTexture(pThisDisplay, pD3DSurf); } pTextureCacheManager->tcm_ticks = 0; } // _D3D_TM_EvictAllManagedTextures
//-----------------------------------------------------------------------------
//
// void _D3D_TM_TimeStampTexture
//
//-----------------------------------------------------------------------------
void _D3D_TM_TimeStampTexture( PTextureCacheManager pTextureCacheManager, P3_SURF_INTERNAL* pD3DSurf) { __TM_TextureHeapUpdate(&pTextureCacheManager->m_heap, pD3DSurf->m_dwHeapIndex, pD3DSurf->m_dwPriority, pTextureCacheManager->tcm_ticks); pTextureCacheManager->tcm_ticks += 2; } // _D3D_TM_TimeStampTexture
//-----------------------------------------------------------------------------
//
// void _D3D_TM_HW_FreeVidmemSurface
//
// This is a hw/driver dependent function which takes care of evicting a
// managed texture that's living in videomemory out of it.
// After this all mipmaps fpVidMemTM should be NULL.
//
//-----------------------------------------------------------------------------
void _D3D_TM_HW_FreeVidmemSurface( P3_THUNKEDDATA* pThisDisplay, P3_SURF_INTERNAL* pD3DSurf) { INT i, iLimit;
if (pD3DSurf->bMipMap) { iLimit = pD3DSurf->iMipLevels; } else { iLimit = 1; }
for(i = 0; i < iLimit; i++) { if (pD3DSurf->MipLevels[i].fpVidMemTM != (FLATPTR)NULL) { // NOTE: if we weren't managing our own vidmem we would need to
// get the address of the VidMemFree callback using
// EngFindImageProcAddress and use this callback into Ddraw to
// do the video memory management. The declaration of
// VidMemFree is
//
// void VidMemFree(LPVMEMHEAP pvmh, FLATPTR ptr);
//
// You can find more information on this callback in the
// Graphics Drivers DDK documentation
_DX_LIN_FreeLinearMemory( &pThisDisplay->LocalVideoHeap0Info, (DWORD)(pD3DSurf->MipLevels[i].fpVidMemTM));
pD3DSurf->MipLevels[i].fpVidMemTM = (FLATPTR)NULL; } } } // _D3D_TM_HW_FreeVidmemSurface
//-----------------------------------------------------------------------------
//
// void _D3D_TM_HW_AllocVidmemSurface
//
// This is a hw/driver dependent function which takes care of allocating a
// managed texture that's living only in system memory into videomemory.
// After this fpVidMemTM should not be NULL. This is also the way to
// check if the call failed or was succesful (notice we don't return a
// function result)
//
//-----------------------------------------------------------------------------
void _D3D_TM_HW_AllocVidmemSurface( P3_D3DCONTEXT* pContext, P3_SURF_INTERNAL* pD3DSurf) { INT i, iLimit, iStart; P3_THUNKEDDATA* pThisDisplay; pThisDisplay = pContext->pThisDisplay;
if (pD3DSurf->bMipMap) { // Load only the necessary levels given any D3DDP2OP_SETTEXLOD command
iStart = pD3DSurf->m_dwTexLOD; if (iStart > (pD3DSurf->iMipLevels - 1)) { // we should at least load the smallest mipmap if we're loading
// the texture into vidmem (and make sure we never try to use any
// other than these levels), otherwise we won't be able to render
iStart = pD3DSurf->iMipLevels - 1; } iLimit = pD3DSurf->iMipLevels; } else { iStart = 0; iLimit = 1; }
for(i = iStart; i < iLimit; i++) { if (pD3DSurf->MipLevels[i].fpVidMemTM == (FLATPTR)NULL) { // NOTE: if we weren't managing our own vidmem we would need to
// get the address of the HeapVidMemAllocAligned callback
// using EngFindImageProcAddress and use this callback into
// Ddraw to do the video off-screen allocation. The
// declaration of HeapVidMemAllocAligned is
//
// FLATPTR HeapVidMemAllocAligned(LPVIDMEM lpVidMem,
// DWORD dwWidth,
// DWORD dwHeight,
// LPSURFACEALIGNEMENT lpAlign,
// LPLONG lpNewPitch);
//
// You can find more information on this callback in the
// Graphics Drivers DDK documentation
P3_MEMREQUEST mmrq; DWORD dwResult; memset(&mmrq, 0, sizeof(P3_MEMREQUEST)); mmrq.dwSize = sizeof(P3_MEMREQUEST); mmrq.dwBytes = pD3DSurf->MipLevels[i].lPitch * pD3DSurf->MipLevels[i].wHeight; mmrq.dwAlign = 8; mmrq.dwFlags = MEM3DL_FIRST_FIT; mmrq.dwFlags |= MEM3DL_FRONT;
dwResult = _DX_LIN_AllocateLinearMemory( &pThisDisplay->LocalVideoHeap0Info, &mmrq);
if (dwResult == GLDD_SUCCESS) { // Record the new vidmem addr for this managed mip level
pD3DSurf->MipLevels[i].fpVidMemTM = mmrq.pMem; } else { // Failure, we'll need to deallocate any mipmap
// allocated up to this point.
_D3D_TM_HW_FreeVidmemSurface(pThisDisplay, pD3DSurf); break; // don't do the loop anymore
} } }
} // _D3D_TM_HW_AllocVidmemSurface
//-----------------------------------------------------------------------------
//
// void _D3D_TM_Preload_Tex_IntoVidMem
//
// Transfer a texture from system memory into videomemory. If the texture
// still hasn't been allocated videomemory we try to do so (even evicting
// uneeded textures if necessary!).
//
//-----------------------------------------------------------------------------
BOOL _D3D_TM_Preload_Tex_IntoVidMem( P3_D3DCONTEXT* pContext, P3_SURF_INTERNAL* pD3DSurf) { P3_THUNKEDDATA* pThisDisplay = pContext->pThisDisplay; INT iLOD;
// Decide the largest level to load based on what
// is specified through D3DDP2OP_SETTEXLOD token
iLOD = pD3DSurf->m_dwTexLOD; if (iLOD > (pD3DSurf->iMipLevels - 1)) { iLOD = pD3DSurf->iMipLevels - 1; } if (!(pD3DSurf->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)) { DISPDBG((ERRLVL,"Must be a managed texture to do texture preload")); return FALSE; // INVALIDPARAMS
}
// Check if the largest required mipmap level has been allocated vidmem
// (only required mipmap levels are ever allocated vidmem)
if ((FLATPTR)NULL == pD3DSurf->MipLevels[iLOD].fpVidMemTM) { // add a new HW handle and create a surface
// (for a managed texture) in vidmem
if ((FAILED(_D3D_TM_AllocTexture(pContext, pD3DSurf))) || ((FLATPTR)NULL == pD3DSurf->MipLevels[iLOD].fpVidMemTM)) { DISPDBG((ERRLVL,"_D3D_OP_TextureBlt unable to " "allocate memory from heap")); return FALSE; // OUTOFVIDEOMEMORY
} pD3DSurf->m_bTMNeedUpdate = TRUE; } if (pD3DSurf->m_bTMNeedUpdate) { // texture download
DWORD iLimit, iCurrLOD;
if (pD3DSurf->bMipMap) { iLimit = pD3DSurf->iMipLevels; } else { iLimit = 1; }
// Switch to the DirectDraw context
DDRAW_OPERATION(pContext, pThisDisplay);
// Blt managed texture's required mipmap levels into vid mem
for (iCurrLOD = iLOD; iCurrLOD < iLimit ; iCurrLOD++) { RECTL rect; rect.left=rect.top = 0; rect.right = pD3DSurf->MipLevels[iCurrLOD].wWidth; rect.bottom = pD3DSurf->MipLevels[iCurrLOD].wHeight; _DD_P3Download(pThisDisplay, pD3DSurf->MipLevels[iCurrLOD].fpVidMem, pD3DSurf->MipLevels[iCurrLOD].fpVidMemTM, pD3DSurf->dwPatchMode, pD3DSurf->dwPatchMode, pD3DSurf->MipLevels[iCurrLOD].lPitch, pD3DSurf->MipLevels[iCurrLOD].lPitch, pD3DSurf->MipLevels[iCurrLOD].P3RXTextureMapWidth.Width, pD3DSurf->dwPixelSize, &rect, &rect); DISPDBG((DBGLVL, "Copy from %08lx to %08lx" " w=%08lx h=%08lx p=%08lx", pD3DSurf->MipLevels[iCurrLOD].fpVidMem, pD3DSurf->MipLevels[iCurrLOD].fpVidMemTM, pD3DSurf->MipLevels[iCurrLOD].wWidth, pD3DSurf->MipLevels[iCurrLOD].wHeight, pD3DSurf->MipLevels[iCurrLOD].lPitch)); }
// Switch back to the Direct3D context
D3D_OPERATION(pContext, pThisDisplay); // Texture updated in vidmem
pD3DSurf->m_bTMNeedUpdate = FALSE; }
return TRUE;
} // _D3D_TM_Preload_Tex_IntoVidMem
//-----------------------------------------------------------------------------
//
// void _D3D_TM_MarkDDSurfaceAsDirty
//
// Help mark a DD surface as dirty given that we need to search for it
// based on its lpSurfMore->dwSurfaceHandle and the lpDDLcl.
//
//-----------------------------------------------------------------------------
void _D3D_TM_MarkDDSurfaceAsDirty( P3_THUNKEDDATA* pThisDisplay, LPDDRAWI_DDRAWSURFACE_LCL pDDSLcl, BOOL bDirty) {
// We don't know which D3D context this is so we have to do a search
// through them (if there are any at all)
if (pThisDisplay->pDirectDrawLocalsHashTable != NULL) { DWORD dwSurfaceHandle = pDDSLcl->lpSurfMore->dwSurfaceHandle; PointerArray* pSurfaceArray; // Get a pointer to an array of surface pointers associated to this lpDD
// The PDD_DIRECTDRAW_LOCAL was stored at D3DCreateSurfaceEx call time
// in PDD_SURFACE_LOCAL->dwReserved1
pSurfaceArray = (PointerArray*) HT_GetEntry(pThisDisplay->pDirectDrawLocalsHashTable, pDDSLcl->dwReserved1);
if (pSurfaceArray) { // Found a surface array associated to this lpDD !
P3_SURF_INTERNAL* pSurfInternal;
// Check the surface in this array associated to this surface handle
pSurfInternal = PA_GetEntry(pSurfaceArray, dwSurfaceHandle);
if (pSurfInternal) { // Got it! Now update dirty TM value
pSurfInternal->m_bTMNeedUpdate = bDirty; } } }
} // _D3D_TM_MarkDDSurfaceAsDirty
#endif // DX7_TEXMANAGEMENT
|