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

  1. /******************************Module*Header*******************************\
  2. *
  3. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  4. * !! !!
  5. * !! WARNING: NOT DDK SAMPLE CODE !!
  6. * !! !!
  7. * !! This source code is provided for completeness only and should not be !!
  8. * !! used as sample code for display driver development. Only those sources !!
  9. * !! marked as sample code for a given driver component should be used for !!
  10. * !! development purposes. !!
  11. * !! !!
  12. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  13. *
  14. * Module Name: linalloc.c
  15. *
  16. * Content: Videomemory linear allocator
  17. *
  18. * Copyright (c) 1995-2003 Microsoft Corporation
  19. \**************************************************************************/
  20. #include "glint.h"
  21. //-----------------------------------------------------------------------------
  22. //
  23. // This module implements video memory allocation. It isn't a great
  24. // allocator (though it IS robust), but mainly it shows how to hook
  25. // up your own if you need/wish to.
  26. //
  27. //-----------------------------------------------------------------------------
  28. // In linalloc.h we define MEMORY_MAP_SIZE and LinearAllocatorInfo
  29. // which are key for our implementation
  30. // This define allows allocations to search more efficiently for free
  31. // memory. If you want to keep things simple you can turn it off
  32. // and things will still work fine.
  33. #define ALLOC_OPTIMIZE 1
  34. // Total number of chunks per element of our memory map array
  35. // (which is of DWORD type , therefore we use sizeof(DWORD) )
  36. #define CHUNKS_PER_ELEM (sizeof(DWORD)*8)
  37. // Memory to be managed will be subdivided in "memory chunks". Each memory
  38. // chunk status will be signaled by a bit in the memory map by being turned
  39. // on or off.
  40. #define TOTAL_MEM_CHUNKS (MEMORY_MAP_SIZE * CHUNKS_PER_ELEM)
  41. // Macros to set, clear and test the value of a given chunk bit without
  42. // worrying about the structure internals.
  43. #define CHUNKNUM_BIT(chunk_num) \
  44. (1 << ((chunk_num) % CHUNKS_PER_ELEM))
  45. #define CHUNKNUM_ELEM(mmap, chunk_num) \
  46. mmap[ (chunk_num) / CHUNKS_PER_ELEM ]
  47. #define SET_MEM_CHUNK(mmap, chunk_num) \
  48. CHUNKNUM_ELEM(mmap, chunk_num) |= CHUNKNUM_BIT(chunk_num)
  49. #define CLR_MEM_CHUNK(mmap, chunk_num) \
  50. CHUNKNUM_ELEM(mmap, chunk_num) &= ~CHUNKNUM_BIT(chunk_num)
  51. #define MEM_CHUNK_VAL(mmap, chunk_num) \
  52. ((CHUNKNUM_ELEM(mmap, chunk_num) & CHUNKNUM_BIT(chunk_num)) > 0 ? 1 : 0)
  53. // Macros that do the mapping between real (heap) memory pointers and the
  54. // chunking indices.
  55. #define MEM_BYTES_TO_CHUNKS(pAlloc, dwBytes) \
  56. ( (dwBytes) / pAlloc->dwMemPerChunk + \
  57. ( ((dwBytes) % pAlloc->dwMemPerChunk)? 1 : 0 ) \
  58. )
  59. #define CHUNK_NUM_TO_PTR(pAlloc, num) \
  60. ( (num) * pAlloc->dwMemPerChunk + pAlloc->dwMemStart )
  61. #define MEM_PTR_TO_CHUNK_NUM(pAlloc, ptr) \
  62. MEM_BYTES_TO_CHUNKS(pAlloc, ((ptr) - pAlloc->dwMemStart) )
  63. //-----------------------------------------------------------------------------
  64. //
  65. // __LIN_AlignPtr
  66. //
  67. // Return an aligned pointer
  68. //
  69. //-----------------------------------------------------------------------------
  70. DWORD
  71. __LIN_AlignPtr(DWORD pointer, DWORD alignment)
  72. {
  73. ULONG ulExtraBytes;
  74. ulExtraBytes = pointer % alignment;
  75. if (ulExtraBytes == 0)
  76. {
  77. ulExtraBytes = alignment;
  78. }
  79. // add enough to pointer so that its new value % alignment is == 0
  80. return (pointer + alignment - ulExtraBytes);
  81. } // __LIN_AlignPtr
  82. //-----------------------------------------------------------------------------
  83. //
  84. // __LIN_CalcMaxChunks
  85. //
  86. // Calculate the number of chunks in the heap
  87. //
  88. //-----------------------------------------------------------------------------
  89. void
  90. __LIN_CalcMaxChunks(LinearAllocatorInfo* pAlloc)
  91. {
  92. DWORD n, dwSizeHeap;
  93. // Compute how many chunks we'll need and what size of heap each
  94. // chunk will control for this linear allocator.
  95. dwSizeHeap = pAlloc->dwMemEnd - pAlloc->dwMemStart;
  96. // We will need dwMemPerChunk * dwMaxChunks to be >= dwSizeHeap.
  97. // We also want dwMaxChunks to be as close to TOTAL_MEM_CHUNKS and
  98. // we would like (though its not necessary) dwMemPerChunk to be as 2^N.
  99. // (and making them at least 16 bytes makes life easier for
  100. // the alignment requirements we have in this driver).
  101. for(n = 4; n < 32; n++)
  102. {
  103. // our current choice of heap size each chunk will control
  104. pAlloc->dwMemPerChunk = 1 << n; // 2^N
  105. // how many chunks do we need for such case?
  106. pAlloc->dwMaxChunks = dwSizeHeap / pAlloc->dwMemPerChunk;
  107. if (dwSizeHeap % pAlloc->dwMemPerChunk != 0)
  108. {
  109. pAlloc->dwMaxChunks++;
  110. }
  111. // can we accept this result to fit in our data structure?
  112. if (pAlloc->dwMaxChunks <= TOTAL_MEM_CHUNKS)
  113. {
  114. // We have as finely grained chunks as we can without
  115. // exceeding our self imposed limits.
  116. break;
  117. }
  118. }
  119. // 1 << n is the size of 1 chunk which is 1k on P3 with 256MB video memory
  120. ASSERTDD((n < 32), "__LIN_CalcMaxChunks : Wrong heap size");
  121. }
  122. //-----------------------------------------------------------------------------
  123. //
  124. // __LIN_ReInitWhenNeeded
  125. //
  126. // Reinitialize heap allocater if needed. This is important only for
  127. // the Win9x driver which can signal us from the 16bit side in a mode
  128. // change that it needs the heap to be reinitialized completely. (It will
  129. // do this by simple setting bResetLinAllocator to TRUE).
  130. //
  131. //-----------------------------------------------------------------------------
  132. void
  133. __LIN_ReInitWhenNeeded(LinearAllocatorInfo* pAlloc)
  134. {
  135. #ifdef W95_DDRAW
  136. if (pAlloc)
  137. {
  138. if (pAlloc->bResetLinAllocator)
  139. {
  140. // Clean all previous allocation data in the memory map
  141. if (pAlloc->pMMap)
  142. {
  143. memset(pAlloc->pMMap, 0, sizeof(MemoryMap));
  144. }
  145. // Clean all previous lenght data in the memory map
  146. if (pAlloc->pLenMap)
  147. {
  148. memset(pAlloc->pLenMap, 0, sizeof(MemoryMap));
  149. }
  150. // Recalculate max chunks due to change of heap's size
  151. __LIN_CalcMaxChunks(pAlloc);
  152. }
  153. // reinitialization completed
  154. pAlloc->bResetLinAllocator = FALSE;
  155. }
  156. #endif // W95_DDRAW
  157. } // __LIN_ReInitWhenNeeded
  158. //-----------------------------------------------------------------------------
  159. //
  160. // _DX_LIN_InitialiseHeapManager
  161. //
  162. // Creates the heap manager. This code is fairly common to this
  163. // sample app and the dd allocator as it will stand. The operations
  164. // it performs will be in perm3dd and/or mini, though the shared heap
  165. // memory can be allocated from 16 and 32 bit land.
  166. //
  167. //-----------------------------------------------------------------------------
  168. BOOL
  169. _DX_LIN_InitialiseHeapManager(LinearAllocatorInfo* pAlloc,
  170. DWORD dwMemStart,
  171. DWORD dwMemEnd)
  172. {
  173. DWORD n;
  174. // Reinitialize heap allocater if needed
  175. __LIN_ReInitWhenNeeded(pAlloc);
  176. pAlloc->dwMemStart = dwMemStart;
  177. pAlloc->dwMemEnd = dwMemEnd;
  178. pAlloc->bResetLinAllocator = FALSE;
  179. // Get memory for the allocator's memory map
  180. pAlloc->pMMap = (MemoryMap*)HEAP_ALLOC(HEAP_ZERO_MEMORY,
  181. sizeof(MemoryMap),
  182. ALLOC_TAG_DX(G));
  183. if(pAlloc->pMMap == NULL)
  184. {
  185. // Out of memory
  186. return FALSE;
  187. }
  188. // Clear the memory map
  189. memset(pAlloc->pMMap, 0, sizeof(MemoryMap));
  190. // Calculate the maximum number of chunks
  191. __LIN_CalcMaxChunks(pAlloc);
  192. // Get memory for the allocator's lenght memory map. We'll keep here
  193. // a map of 0's and 1's where 1 will indicate where the current
  194. // allocated block ends. That way we won't need to keep any binding
  195. // between the allocated addresses and the size of each one in order
  196. // to do the right thing when we are asked to free the memory
  197. pAlloc->pLenMap = (MemoryMap*)HEAP_ALLOC(HEAP_ZERO_MEMORY,
  198. sizeof(MemoryMap),
  199. ALLOC_TAG_DX(H));
  200. if(pAlloc->pLenMap == NULL)
  201. {
  202. // Couln't allocate the lenght map, deallocate the memory map
  203. HEAP_FREE(pAlloc->pMMap);
  204. pAlloc->pMMap = NULL;
  205. // Out of memory
  206. return FALSE;
  207. }
  208. // Clear the lenghts memory map
  209. memset(pAlloc->pLenMap, 0xFF, sizeof(MemoryMap));
  210. return TRUE;
  211. } // _DX_LIN_InitialiseHeapManager
  212. //-----------------------------------------------------------------------------
  213. //
  214. // _DX_LIN_UnInitialiseHeapManager(pLinearAllocatorInfo pAlloc)
  215. //
  216. // Frees the heap manager. This code is fairly common to this
  217. // sample app and the dd allocator as it will stand. The operations
  218. // it performs will be in p3r3dx and/or mini, though the shared heap
  219. // memory can be allocated from 16 and 32 bit land.
  220. //
  221. //-----------------------------------------------------------------------------
  222. void _DX_LIN_UnInitialiseHeapManager(LinearAllocatorInfo* pAlloc)
  223. {
  224. __LIN_ReInitWhenNeeded(pAlloc);
  225. // Destroy/Clean all previous allocation data
  226. if (pAlloc)
  227. {
  228. if(pAlloc->pMMap)
  229. {
  230. HEAP_FREE(pAlloc->pMMap);
  231. pAlloc->pMMap = NULL;
  232. }
  233. if(pAlloc->pLenMap)
  234. {
  235. HEAP_FREE(pAlloc->pLenMap);
  236. pAlloc->pLenMap = NULL;
  237. }
  238. }
  239. } // _DX_LIN_UnInitialiseHeapManager
  240. //-----------------------------------------------------------------------------
  241. //
  242. // _DX_LIN_AllocateLinearMemory
  243. //
  244. // This is the allocation interface to the allocator. It gives an
  245. // application the opportunity to allocate a linear chunk of memory
  246. //
  247. //-----------------------------------------------------------------------------
  248. DWORD
  249. _DX_LIN_AllocateLinearMemory(
  250. pLinearAllocatorInfo pAlloc,
  251. LPMEMREQUEST lpMemReq)
  252. {
  253. INT i;
  254. DWORD dwBytes,
  255. dwCurrStartChunk,
  256. dwCurrEndChunk,
  257. dwNumContChunksFound,
  258. dwContChunksNeeded;
  259. #if ALLOC_OPTIMIZE
  260. // Each block is CHUNKS_PER_ELE chuncks
  261. DWORD dwStartLastBlock;
  262. #endif
  263. // Reinitialize heap allocater if needed
  264. __LIN_ReInitWhenNeeded(pAlloc);
  265. // Validate the passed data
  266. if ((lpMemReq == NULL) ||
  267. (lpMemReq->dwSize != sizeof(P3_MEMREQUEST)))
  268. {
  269. DISPDBG((ERRLVL,"ERROR: NULL lpMemReq passed!"));
  270. return GLDD_INVALIDARGS;
  271. }
  272. if ((!pAlloc) ||
  273. (pAlloc->pMMap == NULL) ||
  274. (pAlloc->pLenMap == NULL) )
  275. {
  276. DISPDBG((ERRLVL,"ERROR: invalid pAlloc passed!"));
  277. return GLDD_INVALIDARGS;
  278. }
  279. // Always ensure that alignment is a DWORD (or DWORD multiple)
  280. if (lpMemReq->dwAlign < 4)
  281. {
  282. lpMemReq->dwAlign = 4;
  283. }
  284. while ((lpMemReq->dwAlign % 4) != 0)
  285. {
  286. lpMemReq->dwAlign++;
  287. }
  288. // Always align memory requests to a minimum of a 4 byte boundary
  289. dwBytes = __LIN_AlignPtr(lpMemReq->dwBytes, lpMemReq->dwAlign);
  290. if (dwBytes == 0)
  291. {
  292. DISPDBG((WRNLVL,"ERROR: Requested 0 Bytes!"));
  293. return GLDD_INVALIDARGS;
  294. }
  295. // Determine how many chunks of memory we'll need to allocate
  296. dwContChunksNeeded = MEM_BYTES_TO_CHUNKS(pAlloc, dwBytes);
  297. // We don't check if we were called with MEM3DL_FIRST_FIT since
  298. // that's the only thing we know how to do right now. We decide
  299. // whether we'll search from back to front or viceversa. We will
  300. // scan the memory map in the chosen direction looking for a "hole"
  301. // large enough for the current request.
  302. if (lpMemReq->dwFlags & MEM3DL_BACK)
  303. {
  304. // We will examine the MemoryMap from the end towards the front
  305. // looking out for a suitable space with the required number of
  306. // chunks we need
  307. dwCurrEndChunk = 0;
  308. dwNumContChunksFound = 0;
  309. for ( i = pAlloc->dwMaxChunks - 1; i >= 0 ; i--)
  310. {
  311. #if ALLOC_OPTIMIZE
  312. // we are about to start testing a specific DWORD in
  313. // the memory map (going from the end to the start)
  314. if (( i % 32) == 31)
  315. {
  316. // If the whole DWORD is 0xFFFFFFFF (meaning all chunks are
  317. // already allocated) then we can & should skip it altogheter
  318. while ((i >= 0) &&
  319. (CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0xFFFFFFFF))
  320. {
  321. // Search needs to be restarted
  322. dwNumContChunksFound = 0;
  323. i -= 32;
  324. }
  325. // If the whole DWORD is 0x00000000 (meaning none of the
  326. // chunks is yet allocated) then we could grab all
  327. while ((i >= 0) &&
  328. (CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0x00000000) &&
  329. !(dwNumContChunksFound >= dwContChunksNeeded))
  330. {
  331. if (dwNumContChunksFound == 0)
  332. {
  333. dwCurrEndChunk = i;
  334. }
  335. i -= 32;
  336. dwNumContChunksFound += 32;
  337. }
  338. if (dwNumContChunksFound >= dwContChunksNeeded)
  339. {
  340. // We've found a suitable place! Figure out where it starts
  341. dwCurrStartChunk = dwCurrEndChunk - dwContChunksNeeded + 1;
  342. break;
  343. }
  344. else if(!(i >= 0))
  345. {
  346. break; // finished examining all memory, break loop here
  347. }
  348. }
  349. #endif // ALLOC_OPTIMIZE
  350. if (MEM_CHUNK_VAL((*pAlloc->pMMap), i ) == 0)
  351. {
  352. if (dwNumContChunksFound == 0)
  353. {
  354. // our count so far of contigous chunks is zero,
  355. // meaning that were just starting to find free
  356. // chunks. We need to remember where this block
  357. // is ending
  358. dwCurrEndChunk = i;
  359. }
  360. dwNumContChunksFound++;
  361. }
  362. else
  363. {
  364. // This chunk is being used and we haven't found a suitable
  365. // set of chunks, so reset our count of contigous chunks
  366. // found so far
  367. dwNumContChunksFound = 0;
  368. }
  369. if (dwNumContChunksFound >= dwContChunksNeeded)
  370. {
  371. // We've found a suitable place! Figure out where it starts.
  372. dwCurrStartChunk = dwCurrEndChunk - dwContChunksNeeded + 1;
  373. break; // break loop here
  374. }
  375. }
  376. }
  377. else // even if no flags are set lets allocate at the heaps front
  378. {
  379. // We will examine the MemoryMap from the front towards the end
  380. // looking out for a suitable space with the required number of
  381. // chunks we need
  382. dwCurrStartChunk = 0;
  383. dwNumContChunksFound = 0;
  384. #if ALLOC_OPTIMIZE
  385. // At the end of the heap there might be a region smaller than
  386. // CHUNKS_PER_ELEM(32) of chunks, and optimized search of 32
  387. // chunk free blocks should be disabled in that region.
  388. dwStartLastBlock = (pAlloc->dwMaxChunks / CHUNKS_PER_ELEM) *
  389. CHUNKS_PER_ELEM;
  390. #endif
  391. for ( i = 0 ; i < (INT)pAlloc->dwMaxChunks ; i++)
  392. {
  393. #if ALLOC_OPTIMIZE
  394. // we are about to start testing a specific
  395. // DWORD in the memory map.
  396. if (( i % 32) == 0)
  397. {
  398. // If the whole DWORD is 0xFFFFFFFF (meaning all chunks are
  399. // already allocated) then we can & should skip it altogheter
  400. while ((i < (INT)dwStartLastBlock) &&
  401. (CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0xFFFFFFFF))
  402. {
  403. // Search needs to be restarted
  404. dwNumContChunksFound = 0;
  405. i += 32;
  406. }
  407. // If the whole DWORD is 0x00000000 (meaning none of the
  408. // chunks is yet allocated) then we could grab all
  409. while ((i < (INT)dwStartLastBlock) &&
  410. (CHUNKNUM_ELEM((*pAlloc->pMMap), i) == 0x00000000) &&
  411. !(dwNumContChunksFound >= dwContChunksNeeded))
  412. {
  413. if (dwNumContChunksFound == 0)
  414. {
  415. dwCurrStartChunk = i;
  416. }
  417. i += 32;
  418. dwNumContChunksFound += 32;
  419. }
  420. if (dwNumContChunksFound >= dwContChunksNeeded)
  421. {
  422. break; // We've found a suitable place! Break loop here
  423. }
  424. else if(!(i < (INT)pAlloc->dwMaxChunks))
  425. {
  426. break; // finished examining all memory, break loop here
  427. }
  428. }
  429. #endif // ALLOC_OPTIMIZE
  430. if (MEM_CHUNK_VAL((*pAlloc->pMMap), i) == 0)
  431. {
  432. if (dwNumContChunksFound == 0)
  433. {
  434. // our count so far of contigous chunks is zero,
  435. // meaning that were just starting to find free
  436. // chunks. We need to remember where this block
  437. // is starting
  438. dwCurrStartChunk = i;
  439. }
  440. dwNumContChunksFound++;
  441. }
  442. else
  443. {
  444. // This chunk is being used and we haven't found a suitable
  445. // set of chunks, so reset our count of contigous chunks
  446. // found so far
  447. dwNumContChunksFound = 0;
  448. }
  449. if (dwNumContChunksFound >= dwContChunksNeeded)
  450. {
  451. // We've found a suitable place!
  452. break; // break loop here
  453. }
  454. }
  455. }
  456. // If we found a suitable place lets allocate in it
  457. if (dwNumContChunksFound >= dwContChunksNeeded)
  458. {
  459. // Fill in the return pointer (properly aligned)
  460. lpMemReq->pMem = __LIN_AlignPtr(CHUNK_NUM_TO_PTR(pAlloc,
  461. dwCurrStartChunk),
  462. lpMemReq->dwAlign);
  463. for (i = dwCurrStartChunk ;
  464. i < (INT)(dwCurrStartChunk + dwContChunksNeeded);
  465. i++)
  466. {
  467. // Set up the bits in the memory map to indicate those
  468. // addresses are being used.
  469. SET_MEM_CHUNK((*pAlloc->pMMap), i);
  470. // Clear the bits in the lenght memory map to indicate that
  471. // the alloacted block doesn't end here.
  472. CLR_MEM_CHUNK((*pAlloc->pLenMap), i);
  473. }
  474. // Now set the last bit of the lenght map in order to indicate
  475. // end-of-allocated-block
  476. SET_MEM_CHUNK((*pAlloc->pLenMap),
  477. dwCurrStartChunk + dwContChunksNeeded - 1);
  478. return GLDD_SUCCESS;
  479. }
  480. return GLDD_NOMEM;
  481. } // _DX_LIN_AllocateLinearMemory
  482. //-----------------------------------------------------------------------------
  483. //
  484. // _DX_LIN_FreeLinearMemory
  485. //
  486. // This is the interface to memory freeing.
  487. //
  488. //-----------------------------------------------------------------------------
  489. DWORD
  490. _DX_LIN_FreeLinearMemory(
  491. pLinearAllocatorInfo pAlloc,
  492. DWORD VidPointer)
  493. {
  494. // Reinitialize heap allocater if needed
  495. __LIN_ReInitWhenNeeded(pAlloc);
  496. if (pAlloc && pAlloc->pMMap && pAlloc->pLenMap)
  497. {
  498. DWORD i, dwFirstChunk;
  499. BOOL bLast = FALSE;
  500. // Now compute the starting chunk for this VidMem ptr
  501. dwFirstChunk = MEM_PTR_TO_CHUNK_NUM(pAlloc, VidPointer);
  502. // Clear the relevant bits in the memory map until the
  503. // lenght map indicates we've reached the end of the allocated
  504. // block
  505. i = dwFirstChunk;
  506. while ((!bLast) && (i <= pAlloc->dwMaxChunks))
  507. {
  508. // First check if this is the end of the block
  509. bLast = MEM_CHUNK_VAL((*pAlloc->pLenMap), i );
  510. // Now "delete" it (even if its the end of the block)
  511. CLR_MEM_CHUNK((*pAlloc->pMMap), i);
  512. // Set the bits in the lenght memory map for future
  513. // allocations.
  514. SET_MEM_CHUNK((*pAlloc->pLenMap), i);
  515. i++;
  516. }
  517. return GLDD_SUCCESS;
  518. }
  519. return GLDD_NOMEM;
  520. } // _DX_LIN_FreeLinearMemory
  521. //-----------------------------------------------------------------------------
  522. //
  523. // _DX_LIN_GetFreeMemInHeap
  524. //
  525. // Scans the memory map and reports the memory that is available in it.
  526. //
  527. //-----------------------------------------------------------------------------
  528. DWORD
  529. _DX_LIN_GetFreeMemInHeap(
  530. pLinearAllocatorInfo pAlloc)
  531. {
  532. DWORD dwTotalFreeMem = 0;
  533. DWORD dwLargestBlock = 0;
  534. DWORD dwTempSize = 0;
  535. DWORD i;
  536. // Reinitialize heap allocater if needed
  537. __LIN_ReInitWhenNeeded(pAlloc);
  538. // Make sure the linear allocator & memory map are valid
  539. if (pAlloc && pAlloc->pMMap)
  540. {
  541. for (i = 0; i < pAlloc->dwMaxChunks ; i++)
  542. {
  543. // Check if chunk is free or in use
  544. if (MEM_CHUNK_VAL((*pAlloc->pMMap), i) == 0)
  545. {
  546. // Keep track of total free memory
  547. dwTotalFreeMem++;
  548. // Keep track of largest single memory area
  549. dwTempSize++;
  550. if (dwTempSize > dwLargestBlock)
  551. {
  552. dwLargestBlock = dwTempSize;
  553. }
  554. }
  555. else
  556. {
  557. dwTempSize = 0;
  558. }
  559. }
  560. // There is a minimum amount for an allocation to succeed since we have
  561. // to pad these/ surfaces out to 32x32, so a 32bpp surface requires at
  562. // least 4K free.
  563. //@@BEGIN_DDKSPLIT
  564. // If we say that we have 1.5K free, then we'll fail TDDRAW WHQL test. Ouch!
  565. //@@END_DDKSPLIT
  566. if (dwLargestBlock * pAlloc->dwMemPerChunk >= 4096)
  567. {
  568. return dwTotalFreeMem * pAlloc->dwMemPerChunk;
  569. }
  570. }
  571. return 0;
  572. } // _DX_LIN_GetFreeMemInHeap