Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1137 lines
40 KiB

/******************************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