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.

709 lines
21 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1994-1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: ddheapr.c
  6. * Content: Rectangular heap manager
  7. * History:
  8. * Date By Reason
  9. * ==== == ======
  10. * 30-mar-95 kylej initial implementation
  11. * 07-apr-95 kylej Added rectVidMemAmountFree
  12. * 15-may-95 craige made separate VMEM struct for rect & linear
  13. * 18-jun-95 craige specific pitch
  14. * 02-jul-95 craige have rectVidMemInit return a BOOL
  15. * 28-nov-95 colinmc new function to return amount of allocated memory
  16. * in a heap
  17. * 05-jul-96 colinmc Work Item: Removing the restriction on taking Win16
  18. * lock on VRAM surfaces (not including the primary)
  19. * 18-jan-97 colinmc Work Item: AGP support
  20. * 03-mar-97 jeffno Work item: Extended surface memory alignment
  21. * 03-Feb-98 DrewB Made portable between user and kernel.
  22. *
  23. ***************************************************************************/
  24. #include "precomp.hxx"
  25. /****************************************************************************
  26. This memory manager manages allocation of rectangular blocks of
  27. video memory. It has essentially the same interface as the linear
  28. video memory manager implemented in vmemmgr.c. Memory allocations
  29. are tracked by nodes on two circular, doubly-linked lists; the free
  30. list and the alloc list. Each list has a special node called the
  31. sentinel which contains a special memory size. The head of each
  32. list always points to the sentinel node and the first member of the
  33. list (if there is one) is pointed to by the sentinel node. Block
  34. adjacency information is kept in each node so that several free nodes
  35. can be coalesced into larger free nodes. This takes place every
  36. time a block of memory is freed.
  37. This memory manager is designed to have no impact on video memory usage.
  38. Global memory is used to maintain the allocation and free lists. Because
  39. of this choice, merging of free blocks is a more expensive operation.
  40. The assumption is that in general, the speed of creating/destroying these
  41. memory blocks is not a high usage item and so it is OK to be slower.
  42. ****************************************************************************/
  43. /*
  44. * IS_FREE and NOT_FREE are used to set the free flag in the flags
  45. * field of each VMEM node. The free flag is the lsb of this field.
  46. */
  47. #define IS_FREE 0x00000001
  48. #define NOT_FREE 0xfffffffe
  49. /*
  50. * SENTINEL is the value stuffed into the size field of a VMEM
  51. * node to identify it as the sentinel node. This value makes
  52. * the assumption that no rectangle sized 0x7fff by 0xffff will
  53. * ever be requested.
  54. */
  55. #define SENTINEL 0x7fffffff
  56. /*
  57. * MIN_DIMENSION_SIZE determines the smallest valid dimension for a
  58. * free memory block. If dividing a rectangle will result in a
  59. * rectangle with a dimension less then MIN_DIMENSION_SIZE, the
  60. * rectangle is not divided.
  61. */
  62. #define MIN_DIMENSION_SIZE 4
  63. /*
  64. * BLOCK_BOUNDARY must be a power of 2, and at least 4. This gives
  65. * us the alignment of memory blocks.
  66. */
  67. #define BLOCK_BOUNDARY 4
  68. // This macro results in the free list being maintained with a
  69. // cx-major, cy-minor sort:
  70. #define CXCY(cx, cy) (((cx) << 16) | (cy))
  71. /*
  72. * Debugging helpers
  73. */
  74. #define DPFVMEMR(str,p) VDPF((0,V,"%s: %d,%d (%dx%d) ptr:%08x, block:%08x",str,p->x,p->y,p->cx,p->cy,p->ptr,p))
  75. #define CHECK_HEAP(a,b) ;
  76. /*
  77. * insertIntoDoubleList - add an item to the a list. The list is
  78. * kept in order of increasing size and is doubly linked. The
  79. * list is circular with a sentinel node indicating the end
  80. * of the list. The sentinel node has its size field set
  81. * to SENTINEL.
  82. */
  83. void insertIntoDoubleList( LPVMEMR pnew, LPVMEMR listhead )
  84. {
  85. LPVMEMR pvmem = listhead;
  86. #ifdef DEBUG
  87. if( pnew->size == 0 )
  88. {
  89. VDPF(( 1, V, "block size = 0\n" ));
  90. }
  91. #endif
  92. /*
  93. * run through the list (sorted from smallest to largest) looking
  94. * for the first item bigger than the new item. If the sentinel
  95. * is encountered, insert the new item just before the sentinel.
  96. */
  97. while( pvmem->size != SENTINEL )
  98. {
  99. if( pnew->size < pvmem->size )
  100. {
  101. break;
  102. }
  103. pvmem = pvmem->next;
  104. }
  105. // insert the new item before the found one.
  106. pnew->prev = pvmem->prev;
  107. pnew->next = pvmem;
  108. pvmem->prev->next = pnew;
  109. pvmem->prev = pnew;
  110. } /* insertIntoDoubleList */
  111. /*
  112. * rectVidMemInit - initialize rectangular video memory manager
  113. */
  114. BOOL rectVidMemInit(
  115. LPVMEMHEAP pvmh,
  116. FLATPTR start,
  117. DWORD width,
  118. DWORD height,
  119. DWORD pitch )
  120. {
  121. LPVMEMR newNode;
  122. VDPF(( 2, V, "rectVidMemInit(start=%08lx,width=%ld,height=%ld,pitch=%ld)", start, width, height, pitch));
  123. pvmh->dwTotalSize = pitch * height;
  124. // Store the pitch for future address calculations.
  125. pvmh->stride = pitch;
  126. // Set up the Free list and the Alloc list by inserting the sentinel.
  127. pvmh->freeList = MemAlloc( sizeof(VMEMR) );
  128. if( pvmh->freeList == NULL )
  129. {
  130. return FALSE;
  131. }
  132. ((LPVMEMR)pvmh->freeList)->size = SENTINEL;
  133. ((LPVMEMR)pvmh->freeList)->cx = SENTINEL;
  134. ((LPVMEMR)pvmh->freeList)->cy = SENTINEL;
  135. ((LPVMEMR)pvmh->freeList)->next = (LPVMEMR)pvmh->freeList;
  136. ((LPVMEMR)pvmh->freeList)->prev = (LPVMEMR)pvmh->freeList;
  137. ((LPVMEMR)pvmh->freeList)->pLeft = NULL;
  138. ((LPVMEMR)pvmh->freeList)->pUp = NULL;
  139. ((LPVMEMR)pvmh->freeList)->pRight = NULL;
  140. ((LPVMEMR)pvmh->freeList)->pDown = NULL;
  141. pvmh->allocList = MemAlloc( sizeof(VMEMR) );
  142. if( pvmh->allocList == NULL )
  143. {
  144. MemFree(pvmh->freeList);
  145. return FALSE;
  146. }
  147. ((LPVMEMR)pvmh->allocList)->size = SENTINEL;
  148. ((LPVMEMR)pvmh->allocList)->next = (LPVMEMR)pvmh->allocList;
  149. ((LPVMEMR)pvmh->allocList)->prev = (LPVMEMR)pvmh->allocList;
  150. // Initialize the free list with the whole chunk of memory
  151. newNode = (LPVMEMR)MemAlloc( sizeof( VMEMR ) );
  152. if( newNode == NULL )
  153. {
  154. MemFree(pvmh->freeList);
  155. MemFree(pvmh->allocList);
  156. return FALSE;
  157. }
  158. newNode->ptr = start;
  159. newNode->size = CXCY(width, height);
  160. newNode->x = 0;
  161. newNode->y = 0;
  162. newNode->cx = width;
  163. newNode->cy = height;
  164. newNode->flags |= IS_FREE;
  165. newNode->pLeft = (LPVMEMR)pvmh->freeList;
  166. newNode->pUp = (LPVMEMR)pvmh->freeList;
  167. newNode->pRight = (LPVMEMR)pvmh->freeList;
  168. newNode->pDown = (LPVMEMR)pvmh->freeList;
  169. insertIntoDoubleList( newNode, ((LPVMEMR) pvmh->freeList)->next );
  170. return TRUE;
  171. } /* rectVidMemInit */
  172. /*
  173. * rectVidMemFini - done with rectangular video memory manager
  174. */
  175. void rectVidMemFini( LPVMEMHEAP pvmh )
  176. {
  177. LPVMEMR curr;
  178. LPVMEMR next;
  179. if( pvmh != NULL )
  180. {
  181. // free all memory allocated for the free list
  182. curr = ((LPVMEMR)pvmh->freeList)->next;
  183. while( curr->size != SENTINEL )
  184. {
  185. next = curr->next;
  186. MemFree( curr );
  187. curr = next;
  188. }
  189. MemFree( curr );
  190. pvmh->freeList = NULL;
  191. // free all memory allocated for the allocation list
  192. curr = ((LPVMEMR)pvmh->allocList)->next;
  193. while( curr->size != SENTINEL )
  194. {
  195. next = curr->next;
  196. MemFree( curr );
  197. curr = next;
  198. }
  199. MemFree( curr );
  200. pvmh->allocList = NULL;
  201. // free the heap data
  202. MemFree( pvmh );
  203. }
  204. } /* rectVidMemFini */
  205. /*
  206. * GetBeforeWastage.
  207. * Align the surface in the given block. Return the size of the holes
  208. * on the left side of the surface.
  209. * Fail if alignment would cause surface to spill out of block.
  210. * Works for horizontal and vertical alignment.
  211. * IN: dwBlockSize , dwBlockStart: Parameters of the block in which
  212. * the surface hopes to fit
  213. * dwSurfaceSize Width or height of the surface
  214. * dwAlignment Expected alignment. 0 means don't care
  215. * OUT: pdwBeforeWastage
  216. */
  217. BOOL GetBeforeWastage(
  218. DWORD dwBlockSize,
  219. DWORD dwBlockStart,
  220. DWORD dwSurfaceSize,
  221. LPDWORD pdwBeforeWastage,
  222. DWORD dwAlignment )
  223. {
  224. if (!dwAlignment)
  225. {
  226. *pdwBeforeWastage=0;
  227. /*
  228. * If no alignment requirement, then check if the surface fits
  229. */
  230. if (dwBlockSize >= dwSurfaceSize)
  231. {
  232. return TRUE;
  233. }
  234. return FALSE;
  235. }
  236. /*
  237. * There's an alignment.
  238. */
  239. *pdwBeforeWastage = (dwAlignment - (dwBlockStart % dwAlignment)) % dwAlignment;
  240. if ( *pdwBeforeWastage + dwSurfaceSize > dwBlockSize )
  241. {
  242. return FALSE;
  243. }
  244. DDASSERT( (dwBlockStart + *pdwBeforeWastage) % dwAlignment == 0 );
  245. return TRUE;
  246. }
  247. /*
  248. * rectVidMemAlloc - alloc some rectangular flat video memory
  249. */
  250. FLATPTR rectVidMemAlloc( LPVMEMHEAP pvmh, DWORD cxThis, DWORD cyThis,
  251. LPDWORD lpdwSize, LPSURFACEALIGNMENT lpAlignment )
  252. {
  253. LPVMEMR pvmem;
  254. DWORD cyRem;
  255. DWORD cxRem;
  256. DWORD cxBelow;
  257. DWORD cyBelow;
  258. DWORD cxBeside;
  259. DWORD cyBeside;
  260. LPVMEMR pnewBeside;
  261. LPVMEMR pnewBelow;
  262. DWORD dwXAlignment=0;
  263. DWORD dwYAlignment=0;
  264. DWORD dwLeftWastage=0;
  265. DWORD dwTopWastage=0;
  266. BOOL bDiscardable = FALSE;
  267. if((cxThis == 0) || (cyThis == 0) || (pvmh == NULL))
  268. return (FLATPTR) NULL;
  269. // Make sure the size of the block is a multiple of BLOCK_BOUNDARY
  270. // If every block allocated has a width which is a multiple of
  271. // BLOCK_BOUNDARY, it guarantees that all blocks will be allocated
  272. // on block boundaries.
  273. /*
  274. * Bump to new alignment
  275. */
  276. if( (cxThis >= (SENTINEL>>16) ) || (cyThis >= (SENTINEL&0xffff) ) )
  277. return (FLATPTR) NULL;
  278. if (lpAlignment)
  279. {
  280. dwXAlignment = lpAlignment->Rectangular.dwXAlignment;
  281. dwYAlignment = lpAlignment->Rectangular.dwYAlignment;
  282. if( lpAlignment->Rectangular.dwFlags & SURFACEALIGN_DISCARDABLE )
  283. {
  284. bDiscardable = TRUE;
  285. }
  286. }
  287. if (dwXAlignment < 4)
  288. {
  289. dwXAlignment = 4;
  290. }
  291. cxThis = (cxThis+(BLOCK_BOUNDARY-1)) & ~(BLOCK_BOUNDARY-1);
  292. /*
  293. * run through free list, looking for the closest matching block
  294. */
  295. pvmem = ((LPVMEMR)pvmh->freeList)->next;
  296. while (pvmem->size != SENTINEL)
  297. {
  298. if (!GetBeforeWastage( pvmem->cx, pvmem->x, cxThis, &dwLeftWastage, dwXAlignment ))
  299. {
  300. pvmem = pvmem->next;
  301. continue; //X size or alignment makes surface spill out of block
  302. }
  303. // Now see if size/alignment works for Y
  304. if (!GetBeforeWastage( pvmem->cy, pvmem->y, cyThis, &dwTopWastage, dwYAlignment ))
  305. {
  306. pvmem = pvmem->next;
  307. continue; //Y size alignment makes surface spill out of block
  308. }
  309. //success:
  310. break;
  311. }
  312. if(pvmem->size == SENTINEL)
  313. {
  314. // There was no rectangle large enough
  315. return (FLATPTR) NULL;
  316. }
  317. // pvmem now points to a rectangle that is the same size or larger
  318. // than the requested rectangle. We're going to use the upper-left
  319. // corner of the found rectangle and divide the unused remainder into
  320. // two rectangles which will go on the available list.
  321. // grow allocation by the wastage which makes the top-left aligned
  322. cxThis += dwLeftWastage;
  323. cyThis += dwTopWastage;
  324. // Compute the width of the unused rectangle to the right and the
  325. // height of the unused rectangle below:
  326. cyRem = pvmem->cy - cyThis;
  327. cxRem = pvmem->cx - cxThis;
  328. // Given finite area, we wish to find the two rectangles that are
  329. // most square -- i.e., the arrangement that gives two rectangles
  330. // with the least perimiter:
  331. cyBelow = cyRem;
  332. cxBeside = cxRem;
  333. if (cxRem <= cyRem)
  334. {
  335. cxBelow = cxThis + cxRem;
  336. cyBeside = cyThis;
  337. }
  338. else
  339. {
  340. cxBelow = cxThis;
  341. cyBeside = cyThis + cyRem;
  342. }
  343. // We only make new available rectangles of the unused right and
  344. // bottom portions if they're greater in dimension than MIN_DIMENSION_SIZE.
  345. // It hardly makes sense to do the book-work to keep around a
  346. // two pixel wide available space, for example.
  347. pnewBeside = NULL;
  348. if (cxBeside >= MIN_DIMENSION_SIZE)
  349. {
  350. pnewBeside = (LPVMEMR)MemAlloc( sizeof(VMEMR) );
  351. if( pnewBeside == NULL)
  352. return (FLATPTR) NULL;
  353. // Update the adjacency information along with the other required
  354. // information in this new node and then insert it into the free
  355. // list which is sorted in ascending cxcy.
  356. // size information
  357. pnewBeside->size = CXCY(cxBeside, cyBeside);
  358. pnewBeside->x = pvmem->x + cxThis;
  359. pnewBeside->y = pvmem->y;
  360. pnewBeside->ptr = pvmem->ptr + cxThis;
  361. pnewBeside->cx = cxBeside;
  362. pnewBeside->cy = cyBeside;
  363. pnewBeside->flags |= IS_FREE;
  364. // adjacency information
  365. pnewBeside->pLeft = pvmem;
  366. pnewBeside->pUp = pvmem->pUp;
  367. pnewBeside->pRight = pvmem->pRight;
  368. pnewBeside->pDown = pvmem->pDown;
  369. }
  370. pnewBelow = NULL;
  371. if (cyBelow >= MIN_DIMENSION_SIZE)
  372. {
  373. pnewBelow = (LPVMEMR) MemAlloc( sizeof(VMEMR) );
  374. if (pnewBelow == NULL)
  375. {
  376. if( pnewBeside != NULL )
  377. {
  378. MemFree( pnewBeside );
  379. }
  380. return (FLATPTR) NULL;
  381. }
  382. // Update the adjacency information along with the other required
  383. // information in this new node and then insert it into the free
  384. // list which is sorted in ascending cxcy.
  385. // size information
  386. pnewBelow->size = CXCY(cxBelow, cyBelow);
  387. pnewBelow->x = pvmem->x;
  388. pnewBelow->y = pvmem->y + cyThis;
  389. pnewBelow->ptr = pvmem->ptr + cyThis*pvmh->stride;
  390. pnewBelow->cx = cxBelow;
  391. pnewBelow->cy = cyBelow;
  392. pnewBelow->flags |= IS_FREE;
  393. // adjacency information
  394. pnewBelow->pLeft = pvmem->pLeft;
  395. pnewBelow->pUp = pvmem;
  396. pnewBelow->pRight = pvmem->pRight;
  397. pnewBelow->pDown = pvmem->pDown;
  398. }
  399. // Remove this node from the available list
  400. pvmem->next->prev = pvmem->prev;
  401. pvmem->prev->next = pvmem->next;
  402. // Update adjacency information for the current node
  403. if(pnewBelow != NULL)
  404. {
  405. insertIntoDoubleList( pnewBelow, ((LPVMEMR) pvmh->freeList)->next );
  406. // Modify the current node to reflect the changes we've made:
  407. pvmem->cy = cyThis;
  408. pvmem->pDown = pnewBelow;
  409. if((pnewBeside != NULL) && (cyBeside == pvmem->cy))
  410. pnewBeside->pDown = pnewBelow;
  411. }
  412. if(pnewBeside != NULL)
  413. {
  414. insertIntoDoubleList( pnewBeside, ((LPVMEMR) pvmh->freeList)->next);
  415. // Modify the current node to reflect the changes we've made:
  416. pvmem->cx = cxThis;
  417. pvmem->pRight = pnewBeside;
  418. if ((pnewBelow != NULL) && (cxBelow == pvmem->cx))
  419. pnewBelow->pRight = pnewBeside;
  420. }
  421. // set up new pointers (pBits is the value returned to the client, pvmem
  422. // points to the actual top-left of the block).
  423. pvmem->pBits = pvmem->ptr + dwLeftWastage + dwTopWastage*pvmh->stride;
  424. pvmem->flags &= NOT_FREE;
  425. pvmem->size = CXCY(pvmem->cx, pvmem->cy);
  426. pvmem->bDiscardable = bDiscardable;
  427. // Now insert it into the alloc list.
  428. insertIntoDoubleList( pvmem, ((LPVMEMR) pvmh->allocList)->next );
  429. if( NULL != lpdwSize )
  430. {
  431. /*
  432. * Note this is the total number of bytes needed for this surface
  433. * including the stuff off the left and right hand sides due to
  434. * pitch not being equal to width. This is different from the
  435. * size computed above which is simply the number of bytes within
  436. * the boundary of the surface itself.
  437. *
  438. * The formula below calculates the number of bytes from the first
  439. * byte in the rectangular surface to the first byte after it
  440. * taking the pitch into account. Complex I know but it works.
  441. */
  442. DDASSERT( 0UL != pvmem->cy );
  443. *lpdwSize = (pvmh->stride * (pvmem->cy - 1)) + pvmem->cx;
  444. }
  445. CHECK_HEAP("After rectVidMemAlloc",pvmh);
  446. return pvmem->pBits;
  447. } /* rectVidMemAlloc */
  448. /*
  449. * rectVidMemFree = free some rectangular flat video memory
  450. */
  451. void rectVidMemFree( LPVMEMHEAP pvmh, FLATPTR ptr )
  452. {
  453. LPVMEMR pvmem;
  454. LPVMEMR pBeside;
  455. // Find the node in the allocated list which matches ptr
  456. for(pvmem=((LPVMEMR)pvmh->allocList)->next; pvmem->size != SENTINEL;
  457. pvmem = pvmem->next)
  458. if(pvmem->pBits == ptr)
  459. break;
  460. if(pvmem->size == SENTINEL) // couldn't find allocated rectangle?
  461. {
  462. VDPF(( 1, V, "Couldn't find node requested freed!\n"));
  463. return;
  464. }
  465. // pvmem now points to the node which must be freed. Attempt to
  466. // coalesce rectangles around this node until no more action
  467. // is possible.
  468. while(1)
  469. {
  470. // Try merging with the right sibling:
  471. pBeside = pvmem->pRight;
  472. if ((pBeside->flags & IS_FREE) &&
  473. (pBeside->cy == pvmem->cy) &&
  474. (pBeside->pUp == pvmem->pUp) &&
  475. (pBeside->pDown == pvmem->pDown) &&
  476. (pBeside->pRight->pLeft != pBeside))
  477. {
  478. // Add the right rectangle to ours:
  479. pvmem->cx += pBeside->cx;
  480. pvmem->pRight = pBeside->pRight;
  481. // Remove pBeside from the list and free it.
  482. pBeside->next->prev = pBeside->prev;
  483. pBeside->prev->next = pBeside->next;
  484. MemFree(pBeside);
  485. continue; // go back and try again
  486. }
  487. // Try merging with the lower sibling:
  488. pBeside = pvmem->pDown;
  489. if ((pBeside->flags & IS_FREE) &&
  490. (pBeside->cx == pvmem->cx) &&
  491. (pBeside->pLeft == pvmem->pLeft) &&
  492. (pBeside->pRight == pvmem->pRight) &&
  493. (pBeside->pDown->pUp != pBeside))
  494. {
  495. pvmem->cy += pBeside->cy;
  496. pvmem->pDown = pBeside->pDown;
  497. // Remove pBeside from the list and free it.
  498. pBeside->next->prev = pBeside->prev;
  499. pBeside->prev->next = pBeside->next;
  500. MemFree(pBeside);
  501. continue; // go back and try again
  502. }
  503. // Try merging with the left sibling:
  504. pBeside = pvmem->pLeft;
  505. if ((pBeside->flags & IS_FREE) &&
  506. (pBeside->cy == pvmem->cy) &&
  507. (pBeside->pUp == pvmem->pUp) &&
  508. (pBeside->pDown == pvmem->pDown) &&
  509. (pBeside->pRight == pvmem) &&
  510. (pvmem->pRight->pLeft != pvmem))
  511. {
  512. // We add our rectangle to the one to the left:
  513. pBeside->cx += pvmem->cx;
  514. pBeside->pRight = pvmem->pRight;
  515. // Remove 'pvmem' from the list and free it:
  516. pvmem->next->prev = pvmem->prev;
  517. pvmem->prev->next = pvmem->next;
  518. MemFree(pvmem);
  519. pvmem = pBeside;
  520. continue;
  521. }
  522. // Try merging with the upper sibling:
  523. pBeside = pvmem->pUp;
  524. if ((pBeside->flags & IS_FREE) &&
  525. (pBeside->cx == pvmem->cx) &&
  526. (pBeside->pLeft == pvmem->pLeft) &&
  527. (pBeside->pRight == pvmem->pRight) &&
  528. (pBeside->pDown == pvmem) &&
  529. (pvmem->pDown->pUp != pvmem))
  530. {
  531. pBeside->cy += pvmem->cy;
  532. pBeside->pDown = pvmem->pDown;
  533. // Remove 'pvmem' from the list and free it:
  534. pvmem->next->prev = pvmem->prev;
  535. pvmem->prev->next = pvmem->next;
  536. MemFree(pvmem);
  537. pvmem = pBeside;
  538. continue;
  539. }
  540. // Remove the node from its current list.
  541. pvmem->next->prev = pvmem->prev;
  542. pvmem->prev->next = pvmem->next;
  543. pvmem->size = CXCY(pvmem->cx, pvmem->cy);
  544. pvmem->flags |= IS_FREE;
  545. // Insert the node into the free list:
  546. insertIntoDoubleList( pvmem, ((LPVMEMR) pvmh->freeList)->next );
  547. // No more area coalescing can be done, return.
  548. CHECK_HEAP("After rectVidMemFree",pvmh);
  549. return;
  550. }
  551. }
  552. /*
  553. * rectVidMemAmountAllocated
  554. */
  555. DWORD rectVidMemAmountAllocated( LPVMEMHEAP pvmh )
  556. {
  557. LPVMEMR pvmem;
  558. DWORD size;
  559. size = 0;
  560. // Traverse the alloc list and add up all the used space.
  561. for(pvmem=((LPVMEMR)pvmh->allocList)->next; pvmem->size != SENTINEL;
  562. pvmem = pvmem->next)
  563. {
  564. if( !( pvmem->bDiscardable ) )
  565. {
  566. size += pvmem->cx * pvmem->cy;
  567. }
  568. }
  569. return size;
  570. } /* rectVidMemAmountAllocated */
  571. /*
  572. * rectVidMemAmountFree
  573. */
  574. DWORD rectVidMemAmountFree( LPVMEMHEAP pvmh )
  575. {
  576. LPVMEMR pvmem;
  577. DWORD size;
  578. size = 0;
  579. // Traverse the free list and add up all the empty space.
  580. for(pvmem=((LPVMEMR)pvmh->freeList)->next; pvmem->size != SENTINEL;
  581. pvmem = pvmem->next)
  582. {
  583. size += pvmem->cx * pvmem->cy;
  584. }
  585. // Now traverse the alloced list and add in all of the memory
  586. // that's discardable
  587. for(pvmem=((LPVMEMR)pvmh->allocList)->next; pvmem->size != SENTINEL;
  588. pvmem = pvmem->next)
  589. {
  590. if( pvmem->bDiscardable )
  591. {
  592. size += pvmem->cx * pvmem->cy;
  593. }
  594. }
  595. return size;
  596. } /* rectVidMemAmountFree */