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.

575 lines
15 KiB

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