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.

550 lines
14 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1994-1998 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: ddheapl.c
  6. * Content: Linear heap manager
  7. * History:
  8. * Date By Reason
  9. * ==== == ======
  10. * 03-Feb-98 DrewB Split from old vmemmgr.c for user/kernel code.
  11. *
  12. ***************************************************************************/
  13. #include "ddrawpr.h"
  14. /****************************************************************************
  15. This memory manager is designed to have no impact on video memory usage.
  16. Global memory is used to maintain the allocation and free lists. Because
  17. of this choice, merging of free blocks is a more expensive operation.
  18. The assumption is that in general, the speed of creating/destroying these
  19. memory blocks is not a high usage item and so it is OK to be slower.
  20. ****************************************************************************/
  21. /*
  22. * MIN_SPLIT_SIZE determines the minimum size of a free block - if splitting
  23. * a block will result in less than MIN_SPLIT_SIZE bytes left, then
  24. * those bytes are just left as part of the new block.
  25. */
  26. #define MIN_SPLIT_SIZE 15
  27. /*
  28. * BLOCK_BOUNDARY must be a power of 2, and at least 4. This gives
  29. * us the alignment of memory blocks.
  30. */
  31. #define BLOCK_BOUNDARY 4
  32. /*
  33. * linVidMemInit - initialize video memory manager
  34. */
  35. BOOL linVidMemInit( LPVMEMHEAP pvmh, FLATPTR start, FLATPTR end )
  36. {
  37. DWORD size;
  38. VDPF((4,V, "linVidMemInit(%08lx,%08lx)", start, end ));
  39. /*
  40. * get the size of the heap (and verify its alignment for debug builds)
  41. */
  42. size = (DWORD)(end - start) + 1;
  43. #ifdef DEBUG
  44. if( (size & (BLOCK_BOUNDARY-1)) != 0 )
  45. {
  46. VDPF(( 0, V, "Invalid size: %08lx (%ld)\n", size, size ));
  47. }
  48. #endif
  49. pvmh->dwTotalSize = size;
  50. /*
  51. * set up a free list with the whole chunk of memory on the block
  52. */
  53. pvmh->freeList = MemAlloc( sizeof( VMEML ) );
  54. if( pvmh->freeList == NULL )
  55. {
  56. return FALSE;
  57. }
  58. ((LPVMEML)pvmh->freeList)->next = NULL;
  59. ((LPVMEML)pvmh->freeList)->ptr = start;
  60. ((LPVMEML)pvmh->freeList)->size = size;
  61. pvmh->allocList = NULL;
  62. return TRUE;
  63. } /* linVidMemInit */
  64. /*
  65. * linVidMemFini - done with video memory manager
  66. */
  67. void linVidMemFini( LPVMEMHEAP pvmh )
  68. {
  69. LPVMEML curr;
  70. LPVMEML next;
  71. if( pvmh != NULL )
  72. {
  73. /*
  74. * free all memory allocated for the free list
  75. */
  76. curr = (LPVMEML)pvmh->freeList;
  77. while( curr != NULL )
  78. {
  79. next = curr->next;
  80. MemFree( curr );
  81. curr = next;
  82. }
  83. pvmh->freeList = NULL;
  84. /*
  85. * free all memory allocated for the allocation list
  86. */
  87. curr = (LPVMEML)pvmh->allocList;
  88. while( curr != NULL )
  89. {
  90. next = curr->next;
  91. MemFree( curr );
  92. curr = next;
  93. }
  94. pvmh->allocList = NULL;
  95. /*
  96. * free the heap data
  97. */
  98. MemFree( pvmh );
  99. }
  100. } /* linVidMemFini */
  101. /*
  102. * insertIntoList - add an item to the allocation list. list is kept in
  103. * order of increasing size
  104. */
  105. void insertIntoList( LPVMEML pnew, LPLPVMEML listhead )
  106. {
  107. LPVMEML pvmem;
  108. LPVMEML prev;
  109. #ifdef DEBUG
  110. if( pnew->size == 0 )
  111. {
  112. VDPF(( 0, V, "block size = 0!!!\n" ));
  113. }
  114. #endif
  115. /*
  116. * run through the list (sorted from smallest to largest) looking
  117. * for the first item bigger than the new item
  118. */
  119. pvmem = *listhead;
  120. prev = NULL;
  121. while( pvmem != NULL )
  122. {
  123. if( pnew->size < pvmem->size )
  124. {
  125. break;
  126. }
  127. prev = pvmem;
  128. pvmem = pvmem->next;
  129. }
  130. /*
  131. * insert the new item item (before the found one)
  132. */
  133. if( prev != NULL )
  134. {
  135. pnew->next = pvmem;
  136. prev->next = pnew;
  137. }
  138. else
  139. {
  140. pnew->next = *listhead;
  141. *listhead = pnew;
  142. }
  143. } /* insertIntoList */
  144. /*
  145. * coalesceFreeBlocks - add a new item to the free list and coalesce
  146. */
  147. LPVMEML coalesceFreeBlocks( LPVMEMHEAP pvmh, LPVMEML pnew )
  148. {
  149. LPVMEML pvmem;
  150. LPVMEML prev;
  151. FLATPTR end;
  152. BOOL done;
  153. pvmem = (LPVMEML)pvmh->freeList;
  154. pnew->next = NULL;
  155. end = pnew->ptr + pnew->size;
  156. prev = NULL;
  157. done = FALSE;
  158. /*
  159. * try to merge the new block "pnew"
  160. */
  161. while( pvmem != NULL )
  162. {
  163. if( pnew->ptr == (pvmem->ptr + pvmem->size) )
  164. {
  165. /*
  166. * new block starts where another ended
  167. */
  168. pvmem->size += pnew->size;
  169. done = TRUE;
  170. }
  171. else if( end == pvmem->ptr )
  172. {
  173. /*
  174. * new block ends where another starts
  175. */
  176. pvmem->ptr = pnew->ptr;
  177. pvmem->size += pnew->size;
  178. done = TRUE;
  179. }
  180. /*
  181. * if we are joining 2 blocks, remove the merged on from the
  182. * list and return so that it can be re-tried (we don't recurse
  183. * since we could get very deep)
  184. */
  185. if( done )
  186. {
  187. if( prev != NULL )
  188. {
  189. prev->next = pvmem->next;
  190. }
  191. else
  192. {
  193. pvmh->freeList = pvmem->next;
  194. }
  195. MemFree( pnew );
  196. return pvmem;
  197. }
  198. prev = pvmem;
  199. pvmem = pvmem->next;
  200. }
  201. /*
  202. * couldn't merge, so just add to the free list
  203. */
  204. insertIntoList( pnew, (LPLPVMEML) &pvmh->freeList );
  205. return NULL;
  206. } /* coalesceFreeBlocks */
  207. /*
  208. * linVidMemFree = free some flat video memory
  209. */
  210. void linVidMemFree( LPVMEMHEAP pvmh, FLATPTR ptr )
  211. {
  212. LPVMEML pvmem;
  213. LPVMEML prev;
  214. if( ptr == (FLATPTR) NULL )
  215. {
  216. return;
  217. }
  218. #ifdef DEBUG
  219. if( pvmh == NULL )
  220. {
  221. VDPF(( 0, V, "VidMemAlloc: NULL heap handle!\n" ));
  222. return;
  223. }
  224. #endif
  225. pvmem = (LPVMEML)pvmh->allocList;
  226. prev = NULL;
  227. /*
  228. * run through the allocation list and look for this ptr
  229. * (O(N), bummer; that's what we get for not using video memory...)
  230. */
  231. while( pvmem != NULL )
  232. {
  233. if( pvmem->ptr == ptr )
  234. {
  235. /*
  236. * remove from allocation list
  237. */
  238. if( prev != NULL )
  239. {
  240. prev->next = pvmem->next;
  241. }
  242. else
  243. {
  244. pvmh->allocList = pvmem->next;
  245. }
  246. /*
  247. * keep coalescing until we can't coalesce anymore
  248. */
  249. while( pvmem != NULL )
  250. {
  251. pvmem = coalesceFreeBlocks( pvmh, pvmem );
  252. }
  253. return;
  254. }
  255. prev = pvmem;
  256. pvmem = pvmem->next;
  257. }
  258. } /* linVidMemFree */
  259. /*
  260. * linVidMemAlloc - alloc some flat video memory
  261. */
  262. FLATPTR linVidMemAlloc( LPVMEMHEAP pvmh, DWORD xsize, DWORD ysize,
  263. LPDWORD lpdwSize, LPSURFACEALIGNMENT lpAlignment,
  264. LPLONG lpNewPitch )
  265. {
  266. LPVMEML pvmem;
  267. LPVMEML prev;
  268. LPVMEML pnew_free;
  269. DWORD dwBeforeWastage;
  270. DWORD dwAfterWastage;
  271. FLATPTR pAligned;
  272. LONG lNewPitch;
  273. DWORD size;
  274. if( xsize == 0 || ysize == 0 || pvmh == NULL )
  275. {
  276. return (FLATPTR) NULL;
  277. }
  278. lNewPitch = (LONG) xsize;
  279. if (lpAlignment && lpAlignment->Linear.dwPitchAlignment )
  280. {
  281. if (lNewPitch % lpAlignment->Linear.dwPitchAlignment)
  282. {
  283. lNewPitch += lpAlignment->Linear.dwPitchAlignment - lNewPitch % lpAlignment->Linear.dwPitchAlignment;
  284. }
  285. }
  286. /*
  287. * This weird size calculation doesn't include the little bit on the 'bottom right' of the surface
  288. */
  289. size = (DWORD) lNewPitch * (ysize-1) + xsize;
  290. size = (size+(BLOCK_BOUNDARY-1)) & ~(BLOCK_BOUNDARY-1);
  291. /*
  292. * run through free list, looking for the closest matching block
  293. */
  294. prev = NULL;
  295. pvmem = (LPVMEML)pvmh->freeList;
  296. while( pvmem != NULL )
  297. {
  298. while( pvmem->size >= size ) //Using while as a try block
  299. {
  300. /*
  301. * Setup for no alignment changes..
  302. */
  303. pAligned = pvmem->ptr;
  304. dwBeforeWastage = 0;
  305. dwAfterWastage = pvmem->size - size;
  306. if( lpAlignment )
  307. {
  308. //get wastage if we put the new block at the beginning or at the end of the free block
  309. if( lpAlignment->Linear.dwStartAlignment )
  310. {
  311. /*
  312. * The before wastage is how much we'd have to skip at the beginning to align the surface
  313. */
  314. dwBeforeWastage = (lpAlignment->Linear.dwStartAlignment - ((DWORD)pvmem->ptr % lpAlignment->Linear.dwStartAlignment)) % lpAlignment->Linear.dwStartAlignment;
  315. //if ( dwBeforeWastage+size > pvmem->size )
  316. // break;
  317. /*
  318. * The after wastage is the bit between the end of the used surface and the end of the block
  319. * if we snuggle this surface as close to the end of the block as possible.
  320. */
  321. dwAfterWastage = ( (DWORD)pvmem->ptr + pvmem->size - size ) % lpAlignment->Linear.dwStartAlignment;
  322. //if ( dwAfterWastage + size > pvmem->size )
  323. // break;
  324. }
  325. /*
  326. * Reassign before/after wastage to meaningful values based on where the block will actually go.
  327. * Also check that aligning won't spill the surface off either end of the block.
  328. */
  329. if ( dwBeforeWastage <= dwAfterWastage )
  330. {
  331. if (pvmem->size < size + dwBeforeWastage)
  332. {
  333. /*
  334. * Alignment pushes end of surface off end of block
  335. */
  336. break;
  337. }
  338. dwAfterWastage = pvmem->size - (size + dwBeforeWastage);
  339. pAligned = pvmem->ptr + dwBeforeWastage;
  340. }
  341. else
  342. {
  343. if (pvmem->size < size + dwAfterWastage)
  344. {
  345. /*
  346. * Alignment pushes end of surface off beginning of block
  347. */
  348. break;
  349. }
  350. dwBeforeWastage = pvmem->size - (size + dwAfterWastage);
  351. pAligned = pvmem->ptr + dwBeforeWastage;
  352. }
  353. }
  354. DDASSERT(size + dwBeforeWastage + dwAfterWastage == pvmem->size );
  355. DDASSERT(pAligned >= pvmem->ptr );
  356. DDASSERT(pAligned + size <= pvmem->ptr + pvmem->size );
  357. /*
  358. * Remove the old free block from the free list.
  359. */
  360. if( prev != NULL )
  361. {
  362. prev->next = pvmem->next;
  363. }
  364. else
  365. {
  366. pvmh->freeList = pvmem->next;
  367. }
  368. /*
  369. * If the after wastage is less than a small amount, smush it into
  370. * this block.
  371. */
  372. if (dwAfterWastage <= MIN_SPLIT_SIZE)
  373. {
  374. size += dwAfterWastage;
  375. dwAfterWastage=0;
  376. }
  377. /*
  378. * Add the new block to the used list, using the old free block
  379. */
  380. pvmem->size = size;
  381. pvmem->ptr = pAligned;
  382. if( NULL != lpdwSize )
  383. *lpdwSize = size;
  384. if (NULL != lpNewPitch)
  385. *lpNewPitch = lNewPitch;
  386. insertIntoList( pvmem, (LPLPVMEML) &pvmh->allocList );
  387. /*
  388. * Add a new free block for before wastage
  389. */
  390. if (dwBeforeWastage)
  391. {
  392. pnew_free = (LPVMEML)MemAlloc( sizeof( VMEML ) );
  393. if( pnew_free == NULL )
  394. {
  395. return (FLATPTR) NULL;
  396. }
  397. pnew_free->size = dwBeforeWastage;
  398. pnew_free->ptr = pAligned-dwBeforeWastage;
  399. insertIntoList( pnew_free, (LPLPVMEML) &pvmh->freeList );
  400. }
  401. /*
  402. * Add a new free block for after wastage
  403. */
  404. if (dwAfterWastage)
  405. {
  406. pnew_free = (LPVMEML)MemAlloc( sizeof( VMEML ) );
  407. if( pnew_free == NULL )
  408. {
  409. return (FLATPTR) NULL;
  410. }
  411. pnew_free->size = dwAfterWastage;
  412. pnew_free->ptr = pAligned+size;
  413. insertIntoList( pnew_free, (LPLPVMEML) &pvmh->freeList );
  414. }
  415. #ifdef DEBUG
  416. if( lpAlignment )
  417. {
  418. if (lpAlignment->Linear.dwStartAlignment)
  419. {
  420. VDPF((5,V,"Alignment for start is %d",lpAlignment->Linear.dwStartAlignment));
  421. DDASSERT(pvmem->ptr % lpAlignment->Linear.dwStartAlignment == 0);
  422. }
  423. if (lpAlignment->Linear.dwPitchAlignment)
  424. {
  425. VDPF((5,V,"Alignment for pitch is %d",lpAlignment->Linear.dwPitchAlignment));
  426. DDASSERT(lNewPitch % lpAlignment->Linear.dwPitchAlignment == 0);
  427. }
  428. }
  429. #endif
  430. return pvmem->ptr;
  431. }
  432. prev = pvmem;
  433. pvmem = pvmem->next;
  434. }
  435. return (FLATPTR) NULL;
  436. } /* linVidMemAlloc */
  437. /*
  438. * linVidMemAmountAllocated
  439. */
  440. DWORD linVidMemAmountAllocated( LPVMEMHEAP pvmh )
  441. {
  442. LPVMEML pvmem;
  443. DWORD size;
  444. pvmem = (LPVMEML)pvmh->allocList;
  445. size = 0;
  446. while( pvmem != NULL )
  447. {
  448. size += pvmem->size;
  449. pvmem = pvmem->next;
  450. }
  451. return size;
  452. } /* linVidMemAmountAllocated */
  453. /*
  454. * linVidMemAmountFree
  455. */
  456. DWORD linVidMemAmountFree( LPVMEMHEAP pvmh )
  457. {
  458. LPVMEML pvmem;
  459. DWORD size;
  460. pvmem = (LPVMEML)pvmh->freeList;
  461. size = 0;
  462. while( pvmem != NULL )
  463. {
  464. size += pvmem->size;
  465. pvmem = pvmem->next;
  466. }
  467. return size;
  468. } /* linVidMemAmountFree */
  469. /*
  470. * linVidMemLargestFree - alloc some flat video memory
  471. */
  472. DWORD linVidMemLargestFree( LPVMEMHEAP pvmh )
  473. {
  474. LPVMEML pvmem;
  475. if( pvmh == NULL )
  476. {
  477. return 0;
  478. }
  479. pvmem = (LPVMEML)pvmh->freeList;
  480. if( pvmem == NULL )
  481. {
  482. return 0;
  483. }
  484. while( 1 )
  485. {
  486. if( pvmem->next == NULL )
  487. {
  488. return pvmem->size;
  489. }
  490. pvmem = pvmem->next;
  491. }
  492. } /* linVidMemLargestFree */