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.
 
 
 
 
 
 

667 lines
24 KiB

/******************************Module*Header*******************************\
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! !!
* !! WARNING: NOT DDK SAMPLE CODE !!
* !! !!
* !! This source code is provided for completeness only and should not be !!
* !! used as sample code for display driver development. Only those sources !!
* !! marked as sample code for a given driver component should be used for !!
* !! development purposes. !!
* !! !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Module Name: linalloc.c
*
* Content: Videomemory linear allocator
*
* Copyright (c) 1995-2003 Microsoft Corporation
\**************************************************************************/
#include "glint.h"
//-----------------------------------------------------------------------------
//
// This module implements video memory allocation. It isn't a great
// allocator (though it IS robust), but mainly it shows how to hook
// up your own if you need/wish to.
//
//-----------------------------------------------------------------------------
// In linalloc.h we define MEMORY_MAP_SIZE and LinearAllocatorInfo
// which are key for our implementation
// This define allows allocations to search more efficiently for free
// memory. If you want to keep things simple you can turn it off
// and things will still work fine.
#define ALLOC_OPTIMIZE 1
// Total number of chunks per element of our memory map array
// (which is of DWORD type , therefore we use sizeof(DWORD) )
#define CHUNKS_PER_ELEM (sizeof(DWORD)*8)
// Memory to be managed will be subdivided in "memory chunks". Each memory
// chunk status will be signaled by a bit in the memory map by being turned
// on or off.
#define TOTAL_MEM_CHUNKS (MEMORY_MAP_SIZE * CHUNKS_PER_ELEM)
// Macros to set, clear and test the value of a given chunk bit without
// worrying about the structure internals.
#define CHUNKNUM_BIT(chunk_num) \
(1 << ((chunk_num) % CHUNKS_PER_ELEM))
#define CHUNKNUM_ELEM(mmap, chunk_num) \
mmap[ (chunk_num) / CHUNKS_PER_ELEM ]
#define SET_MEM_CHUNK(mmap, chunk_num) \
CHUNKNUM_ELEM(mmap, chunk_num) |= CHUNKNUM_BIT(chunk_num)
#define CLR_MEM_CHUNK(mmap, chunk_num) \
CHUNKNUM_ELEM(mmap, chunk_num) &= ~CHUNKNUM_BIT(chunk_num)
#define MEM_CHUNK_VAL(mmap, chunk_num) \
((CHUNKNUM_ELEM(mmap, chunk_num) & CHUNKNUM_BIT(chunk_num)) > 0 ? 1 : 0)
// Macros that do the mapping between real (heap) memory pointers and the
// chunking indices.
#define MEM_BYTES_TO_CHUNKS(pAlloc, dwBytes) \
( (dwBytes) / pAlloc->dwMemPerChunk + \
( ((dwBytes) % pAlloc->dwMemPerChunk)? 1 : 0 ) \
)
#define CHUNK_NUM_TO_PTR(pAlloc, num) \
( (num) * pAlloc->dwMemPerChunk + pAlloc->dwMemStart )
#define MEM_PTR_TO_CHUNK_NUM(pAlloc, ptr) \
MEM_BYTES_TO_CHUNKS(pAlloc, ((ptr) - pAlloc->dwMemStart) )
//-----------------------------------------------------------------------------
//
// __LIN_AlignPtr
//
// Return an aligned pointer
//
//-----------------------------------------------------------------------------
DWORD
__LIN_AlignPtr(DWORD pointer, DWORD alignment)
{
ULONG ulExtraBytes;
ulExtraBytes = pointer % alignment;
if (ulExtraBytes == 0)
{
ulExtraBytes = alignment;
}
// add enough to pointer so that its new value % alignment is == 0
return (pointer + alignment - ulExtraBytes);
} // __LIN_AlignPtr
//-----------------------------------------------------------------------------
//
// __LIN_CalcMaxChunks
//
// Calculate the number of chunks in the heap
//
//-----------------------------------------------------------------------------
void
__LIN_CalcMaxChunks(LinearAllocatorInfo* pAlloc)
{
DWORD n, dwSizeHeap;
// Compute how many chunks we'll need and what size of heap each
// chunk will control for this linear allocator.
dwSizeHeap = pAlloc->dwMemEnd - pAlloc->dwMemStart;
// We will need dwMemPerChunk * dwMaxChunks to be >= dwSizeHeap.
// We also want dwMaxChunks to be as close to TOTAL_MEM_CHUNKS and
// we would like (though its not necessary) dwMemPerChunk to be as 2^N.
// (and making them at least 16 bytes makes life easier for
// the alignment requirements we have in this driver).
for(n = 4; n < 32; n++)
{
// our current choice of heap size each chunk will control
pAlloc->dwMemPerChunk = 1 << n; // 2^N
// how many chunks do we need for such case?
pAlloc->dwMaxChunks = dwSizeHeap / pAlloc->dwMemPerChunk;
if (dwSizeHeap % pAlloc->dwMemPerChunk != 0)
{
pAlloc->dwMaxChunks++;
}
// can we accept this result to fit in our data structure?
if (pAlloc->dwMaxChunks <= TOTAL_MEM_CHUNKS)
{
// We have as finely grained chunks as we can without
// exceeding our self imposed limits.
break;
}
}
// 1 << n is the size of 1 chunk which is 1k on P3 with 256MB video memory
ASSERTDD((n < 32), "__LIN_CalcMaxChunks : Wrong heap size");
}
//-----------------------------------------------------------------------------
//
// __LIN_ReInitWhenNeeded
//
// Reinitialize heap allocater if needed. This is important only for
// the Win9x driver which can signal us from the 16bit side in a mode
// change that it needs the heap to be reinitialized completely. (It will
// do this by simple setting bResetLinAllocator to TRUE).
//
//-----------------------------------------------------------------------------
void
__LIN_ReInitWhenNeeded(LinearAllocatorInfo* pAlloc)
{
#ifdef W95_DDRAW
if (pAlloc)
{
if (pAlloc->bResetLinAllocator)
{
// Clean all previous allocation data in the memory map
if (pAlloc->pMMap)
{
memset(pAlloc->pMMap, 0, sizeof(MemoryMap));
}
// Clean all previous lenght data in the memory map
if (pAlloc->pLenMap)
{
memset(pAlloc->pLenMap, 0, sizeof(MemoryMap));
}
// Recalculate max chunks due to change of heap's size
__LIN_CalcMaxChunks(pAlloc);
}
// reinitialization completed
pAlloc->bResetLinAllocator = FALSE;
}
#endif // W95_DDRAW
} // __LIN_ReInitWhenNeeded
//-----------------------------------------------------------------------------
//
// _DX_LIN_InitialiseHeapManager
//
// Creates the heap manager. This code is fairly common to this
// sample app and the dd allocator as it will stand. The operations
// it performs will be in perm3dd and/or mini, though the shared heap
// memory can be allocated from 16 and 32 bit land.
//
//-----------------------------------------------------------------------------
BOOL
_DX_LIN_InitialiseHeapManager(LinearAllocatorInfo* pAlloc,
DWORD dwMemStart,
DWORD dwMemEnd)
{
DWORD n;
// Reinitialize heap allocater if needed
__LIN_ReInitWhenNeeded(pAlloc);
pAlloc->dwMemStart = dwMemStart;
pAlloc->dwMemEnd = dwMemEnd;
pAlloc->bResetLinAllocator = FALSE;
// Get memory for the allocator's memory map
pAlloc->pMMap = (MemoryMap*)HEAP_ALLOC(HEAP_ZERO_MEMORY,
sizeof(MemoryMap),
ALLOC_TAG_DX(G));
if(pAlloc->pMMap == NULL)
{
// Out of memory
return FALSE;
}
// Clear the memory map
memset(pAlloc->pMMap, 0, sizeof(MemoryMap));
// Calculate the maximum number of chunks
__LIN_CalcMaxChunks(pAlloc);
// Get memory for the allocator's lenght memory map. We'll keep here
// a map of 0's and 1's where 1 will indicate where the current
// allocated block ends. That way we won't need to keep any binding
// between the allocated addresses and the size of each one in order
// to do the right thing when we are asked to free the memory
pAlloc->pLenMap = (MemoryMap*)HEAP_ALLOC(HEAP_ZERO_MEMORY,
sizeof(MemoryMap),
ALLOC_TAG_DX(H));
if(pAlloc->pLenMap == NULL)
{
// Couln't allocate the lenght map, deallocate the memory map
HEAP_FREE(pAlloc->pMMap);
pAlloc->pMMap = NULL;
// Out of memory
return FALSE;
}
// Clear the lenghts memory map
memset(pAlloc->pLenMap, 0xFF, sizeof(MemoryMap));
return TRUE;
} // _DX_LIN_InitialiseHeapManager
//-----------------------------------------------------------------------------
//
// _DX_LIN_UnInitialiseHeapManager(pLinearAllocatorInfo pAlloc)
//
// Frees the heap manager. This code is fairly common to this
// sample app and the dd allocator as it will stand. The operations
// it performs will be in p3r3dx and/or mini, though the shared heap
// memory can be allocated from 16 and 32 bit land.
//
//-----------------------------------------------------------------------------
void _DX_LIN_UnInitialiseHeapManager(LinearAllocatorInfo* pAlloc)
{
__LIN_ReInitWhenNeeded(pAlloc);
// Destroy/Clean all previous allocation data
if (pAlloc)
{
if(pAlloc->pMMap)
{
HEAP_FREE(pAlloc->pMMap);
pAlloc->pMMap = NULL;
}
if(pAlloc->pLenMap)
{
HEAP_FREE(pAlloc->pLenMap);
pAlloc->pLenMap = NULL;
}
}
} // _DX_LIN_UnInitialiseHeapManager
//-----------------------------------------------------------------------------
//
// _DX_LIN_AllocateLinearMemory
//
// This is the allocation interface to the allocator. It gives an
// application the opportunity to allocate a linear chunk of memory
//
//-----------------------------------------------------------------------------
DWORD
_DX_LIN_AllocateLinearMemory(
pLinearAllocatorInfo pAlloc,
LPMEMREQUEST lpMemReq)
{
INT i;
DWORD dwBytes,
dwCurrStartChunk,
dwCurrEndChunk,
dwNumContChunksFound,
dwContChunksNeeded;
#if ALLOC_OPTIMIZE
// Each block is CHUNKS_PER_ELE chuncks
DWORD dwStartLastBlock;
#endif
// Reinitialize heap allocater if needed
__LIN_ReInitWhenNeeded(pAlloc);
// Validate the passed data
if ((lpMemReq == NULL) ||
(lpMemReq->dwSize != sizeof(P3_MEMREQUEST)))
{
DISPDBG((ERRLVL,"ERROR: NULL lpMemReq passed!"));
return GLDD_INVALIDARGS;
}
if ((!pAlloc) ||
(pAlloc->pMMap == NULL) ||
(pAlloc->pLenMap == NULL) )
{
DISPDBG((ERRLVL,"ERROR: invalid pAlloc passed!"));
return GLDD_INVALIDARGS;
}
// Always ensure that alignment is a DWORD (or DWORD multiple)
if (lpMemReq->dwAlign < 4)
{
lpMemReq->dwAlign = 4;
}
while ((lpMemReq->dwAlign % 4) != 0)
{
lpMemReq->dwAlign++;
}
// Always align memory requests to a minimum of a 4 byte boundary
dwBytes = __LIN_AlignPtr(lpMemReq->dwBytes, lpMemReq->dwAlign);
if (dwBytes == 0)
{
DISPDBG((WRNLVL,"ERROR: Requested 0 Bytes!"));
return GLDD_INVALIDARGS;
}
// Determine how many chunks of memory we'll need to allocate
dwContChunksNeeded = MEM_BYTES_TO_CHUNKS(pAlloc, dwBytes);
// We don't check if we were called with MEM3DL_FIRST_FIT since
// that's the only thing we know how to do right now. We decide
// whether we'll search from back to front or viceversa. We will
// scan the memory map in the chosen direction looking for a "hole"
// large enough for the current request.
if (lpMemReq->dwFlags & MEM3DL_BACK)
{
// We will examine the MemoryMap from the end towards the front
// looking out for a suitable space with the required number of
// chunks we need
dwCurrEndChunk = 0;
dwNumContChunksFound = 0;
for ( i = pAlloc->dwMaxChunks - 1; i >= 0 ; i--)
{
#if ALLOC_OPTIMIZE
// we are about to start testing a specific DWORD in
// the memory map (going from the end to the start)
if (( i % 32) == 31)
{
// If the whole DWORD is 0xFFFFFFFF (meaning all chunks are
// already allocated) then we can & should skip it altogheter
while ((i >= 0) &&
(CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0xFFFFFFFF))
{
// Search needs to be restarted
dwNumContChunksFound = 0;
i -= 32;
}
// If the whole DWORD is 0x00000000 (meaning none of the
// chunks is yet allocated) then we could grab all
while ((i >= 0) &&
(CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0x00000000) &&
!(dwNumContChunksFound >= dwContChunksNeeded))
{
if (dwNumContChunksFound == 0)
{
dwCurrEndChunk = i;
}
i -= 32;
dwNumContChunksFound += 32;
}
if (dwNumContChunksFound >= dwContChunksNeeded)
{
// We've found a suitable place! Figure out where it starts
dwCurrStartChunk = dwCurrEndChunk - dwContChunksNeeded + 1;
break;
}
else if(!(i >= 0))
{
break; // finished examining all memory, break loop here
}
}
#endif // ALLOC_OPTIMIZE
if (MEM_CHUNK_VAL((*pAlloc->pMMap), i ) == 0)
{
if (dwNumContChunksFound == 0)
{
// our count so far of contigous chunks is zero,
// meaning that were just starting to find free
// chunks. We need to remember where this block
// is ending
dwCurrEndChunk = i;
}
dwNumContChunksFound++;
}
else
{
// This chunk is being used and we haven't found a suitable
// set of chunks, so reset our count of contigous chunks
// found so far
dwNumContChunksFound = 0;
}
if (dwNumContChunksFound >= dwContChunksNeeded)
{
// We've found a suitable place! Figure out where it starts.
dwCurrStartChunk = dwCurrEndChunk - dwContChunksNeeded + 1;
break; // break loop here
}
}
}
else // even if no flags are set lets allocate at the heaps front
{
// We will examine the MemoryMap from the front towards the end
// looking out for a suitable space with the required number of
// chunks we need
dwCurrStartChunk = 0;
dwNumContChunksFound = 0;
#if ALLOC_OPTIMIZE
// At the end of the heap there might be a region smaller than
// CHUNKS_PER_ELEM(32) of chunks, and optimized search of 32
// chunk free blocks should be disabled in that region.
dwStartLastBlock = (pAlloc->dwMaxChunks / CHUNKS_PER_ELEM) *
CHUNKS_PER_ELEM;
#endif
for ( i = 0 ; i < (INT)pAlloc->dwMaxChunks ; i++)
{
#if ALLOC_OPTIMIZE
// we are about to start testing a specific
// DWORD in the memory map.
if (( i % 32) == 0)
{
// If the whole DWORD is 0xFFFFFFFF (meaning all chunks are
// already allocated) then we can & should skip it altogheter
while ((i < (INT)dwStartLastBlock) &&
(CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0xFFFFFFFF))
{
// Search needs to be restarted
dwNumContChunksFound = 0;
i += 32;
}
// If the whole DWORD is 0x00000000 (meaning none of the
// chunks is yet allocated) then we could grab all
while ((i < (INT)dwStartLastBlock) &&
(CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0x00000000) &&
!(dwNumContChunksFound >= dwContChunksNeeded))
{
if (dwNumContChunksFound == 0)
{
dwCurrStartChunk = i;
}
i += 32;
dwNumContChunksFound += 32;
}
if (dwNumContChunksFound >= dwContChunksNeeded)
{
break; // We've found a suitable place! Break loop here
}
else if(!(i < (INT)pAlloc->dwMaxChunks))
{
break; // finished examining all memory, break loop here
}
}
#endif // ALLOC_OPTIMIZE
if (MEM_CHUNK_VAL((*pAlloc->pMMap), i) == 0)
{
if (dwNumContChunksFound == 0)
{
// our count so far of contigous chunks is zero,
// meaning that were just starting to find free
// chunks. We need to remember where this block
// is starting
dwCurrStartChunk = i;
}
dwNumContChunksFound++;
}
else
{
// This chunk is being used and we haven't found a suitable
// set of chunks, so reset our count of contigous chunks
// found so far
dwNumContChunksFound = 0;
}
if (dwNumContChunksFound >= dwContChunksNeeded)
{
// We've found a suitable place!
break; // break loop here
}
}
}
// If we found a suitable place lets allocate in it
if (dwNumContChunksFound >= dwContChunksNeeded)
{
// Fill in the return pointer (properly aligned)
lpMemReq->pMem = __LIN_AlignPtr(CHUNK_NUM_TO_PTR(pAlloc,
dwCurrStartChunk),
lpMemReq->dwAlign);
for (i = dwCurrStartChunk ;
i < (INT)(dwCurrStartChunk + dwContChunksNeeded);
i++)
{
// Set up the bits in the memory map to indicate those
// addresses are being used.
SET_MEM_CHUNK((*pAlloc->pMMap), i);
// Clear the bits in the lenght memory map to indicate that
// the alloacted block doesn't end here.
CLR_MEM_CHUNK((*pAlloc->pLenMap), i);
}
// Now set the last bit of the lenght map in order to indicate
// end-of-allocated-block
SET_MEM_CHUNK((*pAlloc->pLenMap),
dwCurrStartChunk + dwContChunksNeeded - 1);
return GLDD_SUCCESS;
}
return GLDD_NOMEM;
} // _DX_LIN_AllocateLinearMemory
//-----------------------------------------------------------------------------
//
// _DX_LIN_FreeLinearMemory
//
// This is the interface to memory freeing.
//
//-----------------------------------------------------------------------------
DWORD
_DX_LIN_FreeLinearMemory(
pLinearAllocatorInfo pAlloc,
DWORD VidPointer)
{
// Reinitialize heap allocater if needed
__LIN_ReInitWhenNeeded(pAlloc);
if (pAlloc && pAlloc->pMMap && pAlloc->pLenMap)
{
DWORD i, dwFirstChunk;
BOOL bLast = FALSE;
// Now compute the starting chunk for this VidMem ptr
dwFirstChunk = MEM_PTR_TO_CHUNK_NUM(pAlloc, VidPointer);
// Clear the relevant bits in the memory map until the
// lenght map indicates we've reached the end of the allocated
// block
i = dwFirstChunk;
while ((!bLast) && (i <= pAlloc->dwMaxChunks))
{
// First check if this is the end of the block
bLast = MEM_CHUNK_VAL((*pAlloc->pLenMap), i );
// Now "delete" it (even if its the end of the block)
CLR_MEM_CHUNK((*pAlloc->pMMap), i);
// Set the bits in the lenght memory map for future
// allocations.
SET_MEM_CHUNK((*pAlloc->pLenMap), i);
i++;
}
return GLDD_SUCCESS;
}
return GLDD_NOMEM;
} // _DX_LIN_FreeLinearMemory
//-----------------------------------------------------------------------------
//
// _DX_LIN_GetFreeMemInHeap
//
// Scans the memory map and reports the memory that is available in it.
//
//-----------------------------------------------------------------------------
DWORD
_DX_LIN_GetFreeMemInHeap(
pLinearAllocatorInfo pAlloc)
{
DWORD dwTotalFreeMem = 0;
DWORD dwLargestBlock = 0;
DWORD dwTempSize = 0;
DWORD i;
// Reinitialize heap allocater if needed
__LIN_ReInitWhenNeeded(pAlloc);
// Make sure the linear allocator & memory map are valid
if (pAlloc && pAlloc->pMMap)
{
for (i = 0; i < pAlloc->dwMaxChunks ; i++)
{
// Check if chunk is free or in use
if (MEM_CHUNK_VAL((*pAlloc->pMMap), i) == 0)
{
// Keep track of total free memory
dwTotalFreeMem++;
// Keep track of largest single memory area
dwTempSize++;
if (dwTempSize > dwLargestBlock)
{
dwLargestBlock = dwTempSize;
}
}
else
{
dwTempSize = 0;
}
}
// There is a minimum amount for an allocation to succeed since we have
// to pad these/ surfaces out to 32x32, so a 32bpp surface requires at
// least 4K free.
//@@BEGIN_DDKSPLIT
// If we say that we have 1.5K free, then we'll fail TDDRAW WHQL test. Ouch!
//@@END_DDKSPLIT
if (dwLargestBlock * pAlloc->dwMemPerChunk >= 4096)
{
return dwTotalFreeMem * pAlloc->dwMemPerChunk;
}
}
return 0;
} // _DX_LIN_GetFreeMemInHeap