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